-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
328 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() { | ||
|
||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
Oops, something went wrong.