Skip to content

Commit

Permalink
Mass update 2
Browse files Browse the repository at this point in the history
  • Loading branch information
imahassle committed Nov 8, 2023
1 parent dc97cbf commit 367203b
Show file tree
Hide file tree
Showing 12 changed files with 328 additions and 17 deletions.
8 changes: 5 additions & 3 deletions app/models/recipe.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function getRecipe({

export function getRecipeListItems({userId}: UserId) {
return prisma.recipe.findMany({
select: {title: true, tags: true},
select: {title: true, tags: true, id: true},
where: {userId}
})
}
Expand Down Expand Up @@ -46,13 +46,15 @@ export function updateRecipe({
title,
source,
steps,
ingredients
ingredients,
userId
}: Pick<Recipe, ("id" | "title" | "source")>
& {steps?: Step[]}
& {ingredients?: Ingredient[]}
& UserId
) {
return prisma.recipe.update({
where: {id},
where: {id_userId: { id, userId}},
data: {
title,
source,
Expand Down
17 changes: 4 additions & 13 deletions app/routes/notes.$noteId.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json, } from "@remix-run/node";
import {
Form,
isRouteErrorResponse,
Expand All @@ -8,7 +8,7 @@ import {
} from "@remix-run/react";
import invariant from "tiny-invariant";

import { deleteNote, getNote } from "~/models/note.server";
import { getNote } from "~/models/note.server";
import { requireUserId } from "~/session.server";

export const loader = async ({ params, request }: LoaderFunctionArgs) => {
Expand All @@ -22,15 +22,6 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => {
return json({ note });
};

export const action = async ({ params, request }: ActionFunctionArgs) => {
const userId = await requireUserId(request);
invariant(params.noteId, "noteId not found");

await deleteNote({ id: params.noteId, userId });

return redirect("/notes");
};

export default function NoteDetailsPage() {
const data = useLoaderData<typeof loader>();

Expand All @@ -39,7 +30,7 @@ export default function NoteDetailsPage() {
<h3 className="text-2xl font-bold">{data.note.title}</h3>
<p className="py-6">{data.note.body}</p>
<hr className="my-4" />
<Form method="post">
<Form action="delete" method="post">
<button
type="submit"
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:bg-blue-400"
Expand Down
14 changes: 14 additions & 0 deletions app/routes/notes.delete.$noteId.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ActionFunctionArgs, redirect } from "@remix-run/node";
import invariant from "tiny-invariant";

import { deleteNote } from "~/models/note.server";
import { requireUserId } from "~/session.server";

export const action = async ({ params, request }: ActionFunctionArgs) => {
const userId = await requireUserId(request);
invariant(params.noteId, "noteId not found");

await deleteNote({ id: params.noteId, userId });

return redirect("/notes");
};
4 changes: 3 additions & 1 deletion app/routes/notes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { requireUserId } from "~/session.server";
import { useUser } from "~/utils";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const userId = await requireUserId(request);
const userId = await requireUserId(request, "/notes");
const noteListItems = await getNoteListItems({ userId });
return json({ noteListItems });
};
Expand All @@ -21,7 +21,9 @@ export default function NotesPage() {
<header className="flex items-center justify-between bg-slate-800 p-4 text-white">
<h1 className="text-3xl font-bold">
<Link to=".">Notes</Link>

</h1>
<p><Link to="/recipes">Recipes</Link></p>
<p>{user.email}</p>
<Form action="/logout" method="post">
<button
Expand Down
156 changes: 156 additions & 0 deletions app/routes/recipes.$recipeId.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import {
Form,
isRouteErrorResponse,
useLoaderData,
useRouteError,
useSearchParams,
} from "@remix-run/react";
import invariant from "tiny-invariant";

import { getRecipe, updateRecipe } from "~/models/recipe.server";
import { requireUserId } from "~/session.server";
import { getRecipeFromForm } from "~/utils";

export const loader = async ({ params, request }: LoaderFunctionArgs) => {
const userId = await requireUserId(request);
invariant(params.recipeId, "recipeId not found");

const recipe = await getRecipe({ id: params.recipeId, userId });
if (!recipe) {
throw new Response("Not Found", { status: 404 });
}
return json({ recipe });
};

export const action = async ({ params, request }: ActionFunctionArgs) => {
const userId = await requireUserId(request);
invariant(params.recipeId, "recipeId not found");

const formData = await request.formData();
const recipe = getRecipeFromForm(formData);

if (typeof recipe.title !== "string" || recipe.title.length === 0) {
return json({ errors: { title: "Title is required" } }, { status: 400 });
}

if (typeof recipe.source !== "string") {
return json({ errors: { source: "Invalid source type" } }, { status: 400 });
}

await updateRecipe({
...recipe,
id: params.recipeId,
userId,
});

return redirect("/recipes");
};

export default function RecipeDetailsPage() {
const data = useLoaderData<typeof loader>();
const [params] = useSearchParams();
const isEdit = params.get("edit") === "true";

return (
<div>
{!isEdit ? (
// Display the recipe
<>
<h3 className="text-2xl font-bold">{data.recipe.title}</h3>
<p className="py-6">{data.recipe.source}</p>
<p>
Tags:{" "}
{data.recipe.tags?.map((tag) => {
return <p key={tag.name}>{tag.displayName}</p>;
})}
</p>
<p>Ingredients</p>
<ul>
{data.recipe.ingredients
?.sort((i) => i.index)
.map((ingredient) => {
return (
<li key={ingredient.id}>
{ingredient.amount} {ingredient.item}
</li>
);
})}
</ul>
<p>Steps</p>
<ol>
{data.recipe.steps
?.sort((s) => s.index)
.map((step) => {
return <li key={step.id}>{step.text}</li>;
})}
</ol>
</>
) : (
<>
{/* Recipe edit form */}
<Form method="post">
<input
name="title"
type="text"
defaultValue={data.recipe.source ?? ""}
/>
<input
name="source"
type="text"
defaultValue={data.recipe.source ?? ""}
/>
{/* <input name="tags" type="text" defaultValue={data.recipe.tags} multiple/> */}
</Form>
</>
)}

<hr className="my-4" />
<Form action="delete" method="post">
<button
type="submit"
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:bg-blue-400"
>
Delete
</button>
</Form>

{!isEdit ? (
<button
name="edit"
value="true"
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:bg-blue-400"
>
Edit
</button>
) : (
<button
name="edit"
value="false"
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:bg-blue-400"
>
Close edit
</button>
)}
</div>
);
}

export function ErrorBoundary() {
const error = useRouteError();

if (error instanceof Error) {
return <div>An unexpected error occurred: {error.message}</div>;
}

if (!isRouteErrorResponse(error)) {
return <h1>Unknown Error</h1>;
}

if (error.status === 404) {
return <div>Note not found</div>;
}

return <div>An unexpected error occurred: {error.statusText}</div>;
}
14 changes: 14 additions & 0 deletions app/routes/recipes._index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Form } from "@remix-run/react";

export default function RecipesIndexPage() {
return (
<p>
No recipe selected. Select a recipe on the left, or{" "}
<Form action="new" method="post">
<button type="submit" className="text-blue-500 underline">
create a new recipe.
</button>
</Form>
</p>
);
}
14 changes: 14 additions & 0 deletions app/routes/recipes.delete.$recipeId.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ActionFunctionArgs, redirect } from "@remix-run/node";
import invariant from "tiny-invariant";

import { deleteRecipe } from "~/models/recipe.server";
import { requireUserId } from "~/session.server";

export const action = async ({ params, request }: ActionFunctionArgs) => {
const userId = await requireUserId(request);
invariant(params.recipeId, "recipeId not found");

await deleteRecipe({ id: params.recipeId, userId });

return redirect("/notes");
};
26 changes: 26 additions & 0 deletions app/routes/recipes.new.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { LoaderFunctionArgs, redirect } from "@remix-run/node";

import { createRecipe } from "~/models/recipe.server";
import { requireUserId } from "~/session.server";

export const action = async ({ request }: LoaderFunctionArgs) => {
const userId = await requireUserId(request);
// invariant(params.recipeId, "recipeId not found");

// const formData = await request.formData();
// const title = formData.get("title");

// if (typeof recipe.title !== "string" || recipe.title.length === 0) {
// return json(
// { errors: { title: "Title is required" } },
// { status: 400 },
// );
// }

const recipe = await createRecipe({ userId });
return redirect(`/recipes/${recipe.id}?edit=true`);
};

// export default function NewRecipe() {

// }
73 changes: 73 additions & 0 deletions app/routes/recipes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Form, Link, NavLink, Outlet, useLoaderData } from "@remix-run/react";

import { getRecipeListItems } from "~/models/recipe.server";
import { requireUserId } from "~/session.server";
import { useUser } from "~/utils";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const userId = await requireUserId(request);
const recipeListItems = await getRecipeListItems({ userId });
return json({ recipeListItems: recipeListItems });
};

export default function RecipesPage() {
const data = useLoaderData<typeof loader>();
const user = useUser();

return (
<div className="flex h-full min-h-screen flex-col">
<header className="flex items-center justify-between bg-slate-800 p-4 text-white">
<h1 className="text-3xl font-bold">
<Link to=".">Recipes</Link>
</h1>
<Link to="/notes">Notes</Link>
<p>{user.email}</p>
<Form action="/logout" method="post">
<button
type="submit"
className="rounded bg-slate-600 px-4 py-2 text-blue-100 hover:bg-blue-500 active:bg-blue-600"
>
Logout
</button>
</Form>
</header>

<main className="flex h-full bg-white">
<div className="h-full w-80 border-r bg-gray-50">
<Form action="new" method="post">
<button type="submit" className="block p-4 text-xl text-blue-500">
+ New Recipe
</button>
</Form>

<hr />

{data.recipeListItems.length === 0 ? (
<p className="p-4">No recipes yet</p>
) : (
<ol>
{data.recipeListItems.map((recipe) => (
<li key={recipe.id}>
<NavLink
className={({ isActive }) =>
`block border-b p-4 text-xl ${isActive ? "bg-white" : ""}`
}
to={recipe.id}
>
📝 {recipe.title}
</NavLink>
</li>
))}
</ol>
)}
</div>

<div className="flex-1 p-6">
<Outlet />
</div>
</main>
</div>
);
}
Loading

0 comments on commit 367203b

Please sign in to comment.