Skip to content

Commit 8867f71

Browse files
committed
update
1 parent 4c97d78 commit 8867f71

File tree

3 files changed

+199
-26
lines changed

3 files changed

+199
-26
lines changed

app/models/note.server.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ export function getNote({
99
userId: User["id"];
1010
}) {
1111
return prisma.note.findFirst({
12-
select: { id: true, body: true, title: true },
12+
select: { id: true, body: true, title: true, userId: true },
1313
where: { id, userId },
1414
});
1515
}
1616

1717
export function getNoteListItems({ userId }: { userId: User["id"] }) {
1818
return prisma.note.findMany({
1919
where: { userId },
20-
select: { id: true, title: true },
20+
select: { id: true, title: true, updatedAt: true },
2121
orderBy: { updatedAt: "desc" },
2222
});
2323
}
@@ -33,11 +33,9 @@ export function createNote({
3333
data: {
3434
title,
3535
body,
36-
user: {
37-
connect: {
38-
id: userId,
39-
},
40-
},
36+
userId, // Directly set userId instead of using connect for simplicity
37+
createdAt: new Date(),
38+
updatedAt: new Date(),
4139
},
4240
});
4341
}
@@ -46,7 +44,31 @@ export function deleteNote({
4644
id,
4745
userId,
4846
}: Pick<Note, "id"> & { userId: User["id"] }) {
49-
return prisma.note.deleteMany({
50-
where: { id, userId },
47+
console.log("Attempting to delete note with id:", id, "for user:", userId);
48+
return prisma.note.delete({
49+
where: { id, userId }, // Changed to delete for single record
50+
}).catch((error) => {
51+
console.error("Error deleting note:", error);
52+
throw new Error("Failed to delete note: " + error.message);
5153
});
5254
}
55+
56+
export function updateNote({
57+
id,
58+
title,
59+
body,
60+
userId,
61+
}: Pick<Note, "id" | "title" | "body"> & { userId: User["id"] }) {
62+
console.log("Attempting to update note with id:", id, "for user:", userId, "title:", title, "body:", body);
63+
return prisma.note.update({
64+
where: { id, userId }, // Ensure user owns the note
65+
data: {
66+
title,
67+
body,
68+
updatedAt: new Date(),
69+
},
70+
}).catch((error) => {
71+
console.error("Error updating note:", error);
72+
throw new Error("Failed to update note: " + error.message);
73+
});
74+
}

app/routes/notes.$noteId.edit.tsx

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
2+
import { json, redirect } from "@remix-run/node";
3+
import { Form, useActionData, useLoaderData, useNavigate } from "@remix-run/react";
4+
import invariant from "tiny-invariant";
5+
6+
import { updateNote, getNote } from "~/models/note.server";
7+
import { requireUserId } from "~/session.server";
8+
9+
export const loader = async ({ params, request }: LoaderFunctionArgs) => {
10+
const userId = await requireUserId(request);
11+
invariant(params.noteId, "noteId not found");
12+
13+
console.log('Loading note - noteId:', params.noteId, 'userId:', userId);
14+
15+
const note = await getNote({ id: params.noteId, userId });
16+
if (!note) {
17+
throw new Response("Not Found", { status: 404 });
18+
}
19+
return json({ note });
20+
};
21+
22+
export const action = async ({ params, request }: ActionFunctionArgs) => {
23+
const userId = await requireUserId(request);
24+
invariant(params.noteId, "noteId not found");
25+
invariant(typeof params.noteId === "string", "noteId must be a string");
26+
27+
const formData = await request.formData();
28+
const title = formData.get("title") as string;
29+
const body = formData.get("body") as string;
30+
31+
if (!title || title.length === 0) {
32+
return json({ errors: { title: "Title is required" } }, { status: 400 });
33+
}
34+
35+
if (!body || body.length === 0) {
36+
return json({ errors: { body: "Body is required" } }, { status: 400 });
37+
}
38+
39+
console.log("Action: Updating note with id:", params.noteId, "title:", title, "body:", body, "userId:", userId);
40+
41+
try {
42+
await updateNote({
43+
id: params.noteId,
44+
title,
45+
body,
46+
userId,
47+
});
48+
return redirect(`/notes/${params.noteId}`);
49+
} catch (error) {
50+
console.error("Action Error: Failed to update note:", error);
51+
return json(
52+
{ errors: { server: "Failed to update note. Please try again." } },
53+
{ status: 500 }
54+
);
55+
}
56+
};
57+
58+
export default function NoteEditPage() {
59+
const { note } = useLoaderData<typeof loader>();
60+
const actionData = useActionData<typeof action>();
61+
const navigate = useNavigate();
62+
63+
console.log("Rendering NoteEditPage with note:", note);
64+
65+
return (
66+
<div className="flex-1 p-6 bg-gray-100 min-h-screen">
67+
<h1 className="text-3xl font-bold mb-6 text-gray-800">Edit Note</h1>
68+
{actionData?.errors && (
69+
<div className="mb-4 p-4 bg-red-100 text-red-700 rounded-lg">
70+
{Object.entries(actionData.errors).map(([key, value]) => (
71+
<p key={key}>{value}</p>
72+
))}
73+
</div>
74+
)}
75+
<Form method="post" onSubmit={(e) => {
76+
console.log("Form submitted with data:", new FormData(e.currentTarget));
77+
}} className="space-y-6 max-w-2xl bg-white p-6 rounded-lg shadow-md">
78+
<div>
79+
<label htmlFor="title" className="block text-lg font-medium text-gray-700 mb-2">
80+
Title
81+
</label>
82+
<input
83+
type="text"
84+
id="title"
85+
name="title"
86+
defaultValue={note.title}
87+
required
88+
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 p-2"
89+
/>
90+
</div>
91+
92+
<div>
93+
<label htmlFor="body" className="block text-lg font-medium text-gray-700 mb-2">
94+
Body
95+
</label>
96+
<textarea
97+
id="body"
98+
name="body"
99+
defaultValue={note.body}
100+
required
101+
rows={6}
102+
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 p-2"
103+
/>
104+
</div>
105+
106+
<div className="flex space-x-4">
107+
<button
108+
type="submit"
109+
className="w-full py-3 px-6 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition duration-300"
110+
>
111+
Update Note
112+
</button>
113+
<button
114+
type="button"
115+
onClick={() => navigate(-1)}
116+
className="w-full py-3 px-6 bg-gray-500 text-white rounded-md hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transition duration-300"
117+
>
118+
Cancel
119+
</button>
120+
</div>
121+
</Form>
122+
</div>
123+
);
124+
}

app/routes/notes.$noteId.tsx

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import { Link } from "@remix-run/react";
2+
13
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
24
import { json, redirect } from "@remix-run/node";
35
import {
46
Form,
57
isRouteErrorResponse,
8+
useParams,
69
useLoaderData,
710
useRouteError,
811
} from "@remix-run/react";
@@ -15,8 +18,11 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => {
1518
const userId = await requireUserId(request);
1619
invariant(params.noteId, "noteId not found");
1720

21+
console.log('Loader: Fetching note with id:', params.noteId, 'for user:', userId);
22+
1823
const note = await getNote({ id: params.noteId, userId });
1924
if (!note) {
25+
console.log('Note not found or unauthorized for user:', userId);
2026
throw new Response("Not Found", { status: 404 });
2127
}
2228
return json({ note });
@@ -26,27 +32,48 @@ export const action = async ({ params, request }: ActionFunctionArgs) => {
2632
const userId = await requireUserId(request);
2733
invariant(params.noteId, "noteId not found");
2834

29-
await deleteNote({ id: params.noteId, userId });
35+
console.log('Action: Deleting note with id:', params.noteId, 'for user:', userId);
3036

31-
return redirect("/notes");
37+
try {
38+
await deleteNote({ id: params.noteId, userId });
39+
return redirect("/notes");
40+
} catch (error) {
41+
console.error('Error deleting note:', error);
42+
throw new Response("Failed to delete note", { status: 500 });
43+
}
3244
};
3345

3446
export default function NoteDetailsPage() {
47+
const params = useParams();
3548
const data = useLoaderData<typeof loader>();
3649

3750
return (
38-
<div>
39-
<h3 className="text-2xl font-bold">{data.note.title}</h3>
51+
<div className="p-6">
52+
<h3 className="text-3xl font-bold mb-4">{data.note.title}</h3>
4053
<p className="py-6">{data.note.body}</p>
4154
<hr className="my-4" />
42-
<Form method="post">
43-
<button
44-
type="submit"
45-
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:bg-blue-400"
46-
>
47-
Delete
48-
</button>
49-
</Form>
55+
<div className="space-y-4">
56+
<Form method="post" onSubmit={(e) => {
57+
if (!confirm("Are you sure you want to delete this note?")) {
58+
e.preventDefault();
59+
}
60+
}}>
61+
<button
62+
type="submit"
63+
className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600 focus:bg-red-400"
64+
>
65+
Delete
66+
</button>
67+
</Form>
68+
<Link to={`/notes/${params.noteId}/edit`} className="inline-block">
69+
<button
70+
type="button"
71+
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:bg-blue-400"
72+
>
73+
Edit
74+
</button>
75+
</Link>
76+
</div>
5077
</div>
5178
);
5279
}
@@ -55,16 +82,16 @@ export function ErrorBoundary() {
5582
const error = useRouteError();
5683

5784
if (error instanceof Error) {
58-
return <div>An unexpected error occurred: {error.message}</div>;
85+
return <div className="p-6 text-red-600">An unexpected error occurred: {error.message}</div>;
5986
}
6087

6188
if (!isRouteErrorResponse(error)) {
62-
return <h1>Unknown Error</h1>;
89+
return <h1 className="p-6 text-red-600">Unknown Error</h1>;
6390
}
6491

6592
if (error.status === 404) {
66-
return <div>Note not found</div>;
93+
return <div className="p-6 text-red-600">Note not found</div>;
6794
}
6895

69-
return <div>An unexpected error occurred: {error.statusText}</div>;
70-
}
96+
return <div className="p-6 text-red-600">An unexpected error occurred: {error.statusText}</div>;
97+
}

0 commit comments

Comments
 (0)