Skip to content

Commit c3515eb

Browse files
Copilotlimonte
andcommitted
Add edit route for todo items with browser tests
Co-authored-by: limonte <[email protected]>
1 parent a0a250c commit c3515eb

File tree

4 files changed

+177
-20
lines changed

4 files changed

+177
-20
lines changed

resources/js/Pages/Todo/Edit.jsx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { useForm, Link } from '@inertiajs/react'
2+
3+
export default function TodoEdit({ todo }) {
4+
const { data, setData, patch, processing } = useForm({
5+
title: todo.title,
6+
})
7+
8+
const handleSubmit = (e) => {
9+
e.preventDefault()
10+
patch(`/todos/${todo.id}`)
11+
}
12+
13+
return (
14+
<div className="min-h-screen bg-neutral-50 py-12 px-4 sm:px-6 lg:px-8">
15+
<div className="max-w-2xl mx-auto">
16+
{/* Header */}
17+
<header className="mb-12">
18+
<Link
19+
href="/"
20+
className="inline-flex items-center text-sm font-medium text-neutral-600 hover:text-neutral-900 mb-4"
21+
>
22+
<svg
23+
className="w-4 h-4 mr-2"
24+
fill="none"
25+
stroke="currentColor"
26+
viewBox="0 0 24 24"
27+
aria-hidden="true"
28+
>
29+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
30+
</svg>
31+
Back to Tasks
32+
</Link>
33+
<h1 className="text-4xl font-bold tracking-tight text-neutral-900 mb-2">Edit Task</h1>
34+
<div className="h-1 w-16 bg-neutral-900" aria-hidden="true"></div>
35+
</header>
36+
37+
{/* Edit Todo Form */}
38+
<section aria-labelledby="edit-todo-heading">
39+
<h2 id="edit-todo-heading" className="sr-only">
40+
Edit task
41+
</h2>
42+
<form onSubmit={handleSubmit} className="space-y-4">
43+
<div>
44+
<label htmlFor="todo-input" className="block text-sm font-medium text-neutral-700 mb-2">
45+
Task Title
46+
</label>
47+
<div className="flex gap-3">
48+
<input
49+
id="todo-input"
50+
type="text"
51+
value={data.title}
52+
onChange={(e) => setData('title', e.target.value)}
53+
placeholder="What needs to be done?"
54+
disabled={processing}
55+
required
56+
className="flex-1 px-4 py-3 border-2 border-neutral-300 bg-white text-neutral-900 placeholder-neutral-400 focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:border-transparent disabled:bg-neutral-100 disabled:cursor-not-allowed transition-colors"
57+
aria-describedby="todo-input-description"
58+
/>
59+
<button
60+
type="submit"
61+
disabled={processing || !data.title.trim()}
62+
className="px-6 py-3 bg-neutral-900 text-white font-medium hover:bg-neutral-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900 disabled:bg-neutral-400 disabled:cursor-not-allowed transition-colors"
63+
aria-label="Save task"
64+
>
65+
{processing ? 'Saving...' : 'Save'}
66+
</button>
67+
</div>
68+
<p id="todo-input-description" className="mt-2 text-sm text-neutral-500">
69+
Press Enter or click Save to update the task
70+
</p>
71+
</div>
72+
</form>
73+
</section>
74+
</div>
75+
</div>
76+
)
77+
}

resources/js/Pages/Todo/List.jsx

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useForm } from '@inertiajs/react'
1+
import { useForm, Link } from '@inertiajs/react'
22

33
export default function TodoList({ todos }) {
44
const { data, setData, post, processing } = useForm({
@@ -92,26 +92,28 @@ export default function TodoList({ todos }) {
9292
) : (
9393
<ul className="space-y-3" role="list">
9494
{todos.map((todo) => (
95-
<li
96-
key={todo.id}
97-
className="bg-white border-l-4 border-neutral-900 p-4 shadow-sm hover:shadow-md transition-shadow"
98-
>
99-
<div className="flex items-start">
100-
<div className="flex-1 min-w-0">
101-
<p
102-
className={`text-base font-medium ${
103-
todo.completed ? 'text-neutral-500 line-through' : 'text-neutral-900'
104-
}`}
105-
>
106-
{todo.title}
107-
</p>
108-
{todo.completed && (
109-
<span className="inline-flex items-center mt-2 px-2 py-1 text-xs font-medium bg-neutral-100 text-neutral-600">
110-
Completed
111-
</span>
112-
)}
95+
<li key={todo.id}>
96+
<Link
97+
href={`/todos/${todo.id}/edit`}
98+
className="block bg-white border-l-4 border-neutral-900 p-4 shadow-sm hover:shadow-md transition-shadow"
99+
>
100+
<div className="flex items-start">
101+
<div className="flex-1 min-w-0">
102+
<p
103+
className={`text-base font-medium ${
104+
todo.completed ? 'text-neutral-500 line-through' : 'text-neutral-900'
105+
}`}
106+
>
107+
{todo.title}
108+
</p>
109+
{todo.completed && (
110+
<span className="inline-flex items-center mt-2 px-2 py-1 text-xs font-medium bg-neutral-100 text-neutral-600">
111+
Completed
112+
</span>
113+
)}
114+
</div>
113115
</div>
114-
</div>
116+
</Link>
115117
</li>
116118
))}
117119
</ul>

routes/web.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,19 @@
2121

2222
return redirect('/');
2323
});
24+
25+
Route::get('/todos/{todo}/edit', function (Todo $todo): Response {
26+
return Inertia::render('Todo/Edit', [
27+
'todo' => $todo,
28+
]);
29+
});
30+
31+
Route::patch('/todos/{todo}', function (Todo $todo): RedirectResponse {
32+
$validated = request()->validate([
33+
'title' => 'required|string|max:255',
34+
]);
35+
36+
$todo->update($validated);
37+
38+
return redirect('/');
39+
});

tests/Feature/ExampleTest.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,65 @@
4343
$page->assertSee('First todo');
4444
$page->assertSee('Second todo');
4545
});
46+
47+
test('todo items are clickable links to edit page', function () {
48+
$page = visit('/');
49+
50+
// Add a todo
51+
$page->type('input[type="text"]', 'My clickable todo');
52+
$page->click('button[type="submit"]');
53+
$page->waitForText('My clickable todo');
54+
55+
// Click on the todo item
56+
$page->click('text=My clickable todo');
57+
$page->waitForText('Edit Task');
58+
59+
// Verify we're on the edit page
60+
$page->assertSee('Edit Task');
61+
$page->assertSee('Task Title');
62+
$page->assertSee('Back to Tasks');
63+
});
64+
65+
test('can edit todo from edit page', function () {
66+
$page = visit('/');
67+
68+
// Add a todo
69+
$page->type('input[type="text"]', 'Original todo');
70+
$page->click('button[type="submit"]');
71+
$page->waitForText('Original todo');
72+
73+
// Click on the todo to edit
74+
$page->click('text=Original todo');
75+
$page->waitForText('Edit Task');
76+
77+
// Update the todo
78+
$page->type('input[type="text"]', 'Updated todo');
79+
$page->click('button[type="submit"]');
80+
$page->waitForText('Updated todo');
81+
82+
// Verify we're back on the list page with the updated todo
83+
$page->assertSee('All Tasks');
84+
$page->assertSee('Updated todo');
85+
$page->assertDontSee('Original todo');
86+
});
87+
88+
test('can navigate back from edit page to list page', function () {
89+
$page = visit('/');
90+
91+
// Add a todo
92+
$page->type('input[type="text"]', 'Test navigation');
93+
$page->click('button[type="submit"]');
94+
$page->waitForText('Test navigation');
95+
96+
// Click on the todo to go to edit page
97+
$page->click('text=Test navigation');
98+
$page->waitForText('Edit Task');
99+
100+
// Click back to tasks link
101+
$page->click('text=Back to Tasks');
102+
$page->waitForText('All Tasks');
103+
104+
// Verify we're back on the list page
105+
$page->assertSee('All Tasks');
106+
$page->assertSee('Test navigation');
107+
});

0 commit comments

Comments
 (0)