diff --git a/web/src/components/Spinner.tsx b/web/src/components/Spinner.tsx
new file mode 100644
index 0000000..14b509b
--- /dev/null
+++ b/web/src/components/Spinner.tsx
@@ -0,0 +1,12 @@
+interface SpinnerProps {
+ content: string;
+}
+
+export default function Spinner({ content }: SpinnerProps) {
+ return (
+ <>
+
+ { content }
+ >
+ );
+}
diff --git a/web/src/components/buttons/MadeButton.tsx b/web/src/components/buttons/MadeButton.tsx
index 29fcf49..41a10c5 100644
--- a/web/src/components/buttons/MadeButton.tsx
+++ b/web/src/components/buttons/MadeButton.tsx
@@ -2,14 +2,14 @@ import { useState } from "react";
interface MadeButtonProps {
- id: number | undefined;
+ id: number;
}
export default function MadeButton({ id }: MadeButtonProps) {
const [clicked, setClicked] = useState(false);
const clickHandler = () => {
- if (!id || clicked) return;
+ if (clicked) return;
// TODO: Implement actions
diff --git a/web/src/components/buttons/ShareButton.tsx b/web/src/components/buttons/ShareButton.tsx
index 18759ee..ced9381 100644
--- a/web/src/components/buttons/ShareButton.tsx
+++ b/web/src/components/buttons/ShareButton.tsx
@@ -2,14 +2,14 @@ import { useState } from "react";
interface ShareButtonProps {
- id: number | undefined;
+ id: number;
}
export default function ShareButton({ id }: ShareButtonProps) {
const [clicked, setClicked] = useState(false);
const clickHandler = () => {
- if (!id || clicked) return;
+ if (clicked) return;
// TODO: Implement action here
diff --git a/web/src/components/items/IngredientList.tsx b/web/src/components/items/IngredientList.tsx
new file mode 100644
index 0000000..6e9c337
--- /dev/null
+++ b/web/src/components/items/IngredientList.tsx
@@ -0,0 +1,34 @@
+import type { RecipeIngredient } from "../../types/recipe";
+
+interface IngredientListProps {
+ ingredients: RecipeIngredient[];
+}
+
+export default function IngredientList({ ingredients }: IngredientListProps) {
+ return (
+ <>
+
+
Ingredients
+
+
+ {ingredients?.map(ingredient => (
+ -
+
+
+
+ {ingredient.Quantity}: {ingredient.Name}
+
+ ))}
+
+
+ >
+ );
+}
diff --git a/web/src/components/items/InstructionList.tsx b/web/src/components/items/InstructionList.tsx
new file mode 100644
index 0000000..9aef668
--- /dev/null
+++ b/web/src/components/items/InstructionList.tsx
@@ -0,0 +1,24 @@
+interface InstructionListProps {
+ instructions: string[];
+}
+export default function InstructionList({ instructions }: InstructionListProps) {
+ return (
+ <>
+
+
Instructions
+
+
+ {instructions?.map((instruction, i) => (
+ -
+
+
{ i + 1}
+
+ { instruction }
+
+ ))}
+
+
+
+ >
+ );
+}
diff --git a/web/src/components/items/TagList.tsx b/web/src/components/items/TagList.tsx
new file mode 100644
index 0000000..51c0060
--- /dev/null
+++ b/web/src/components/items/TagList.tsx
@@ -0,0 +1,43 @@
+import type { Tag } from "../../types/recipe";
+
+interface TagListProps {
+ tags: Tag[]
+ created: Date
+ modified: Date | null;
+}
+
+function FormatDate(date: Date): string {
+ return new Intl.DateTimeFormat("en-US", {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit"
+ }).format(date);
+}
+
+export default function TagList({ tags, created, modified }: TagListProps) {
+
+
+ return (
+
+ {tags && (
+ <>
+
Tags
+
+
+ {tags.map(tag => (
+ -
+ {tag.Name}
+
+
+ ))}
+
+ >
+ )}
+
+
Created: {FormatDate(new Date(created))}
+ {modified && (
+
Last Modified: {FormatDate(new Date(modified))}
+ )}
+
+ );
+}
diff --git a/web/src/pages/Recipe.tsx b/web/src/pages/Recipe.tsx
index db6461a..97089d6 100644
--- a/web/src/pages/Recipe.tsx
+++ b/web/src/pages/Recipe.tsx
@@ -1,22 +1,20 @@
-import { use, useEffect, useState } from "react";
+import { useEffect, useState } from "react";
import { isApiError, type ApiError } from "../types/api/error";
import { GetRecipe } from "../services/RecipeService";
import type { Recipe } from "../types/recipe";
-import { AuthContext } from "../context/AuthContext";
import { useParams } from "react-router-dom";
import RecipePlaceholder from "../assets/images/recipe_placeholder_wide.jpg"
-import TimeIcon from "../components/icons/TimeIcon";
-import StarIcon from "../components/icons/StarIcon";
import RecipeMetaData from "../components/display/RecipeMetaData";
import MadeButton from "../components/buttons/MadeButton";
import ShareButton from "../components/buttons/ShareButton";
import FavoriteButton from "../components/buttons/FavoriteButton";
+import TagList from "../components/items/TagList";
+import IngredientList from "../components/items/IngredientList";
+import InstructionList from "../components/items/InstructionList";
+import Spinner from "../components/Spinner";
export default function RecipePage() {
- // Context
- const { isLoggedIn } = use(AuthContext);
-
// Url params
const { id } = useParams();
@@ -42,28 +40,32 @@ export default function RecipePage() {
console.error(error);
}, [error]);
- return (
+ return recipe ? (
<>
-
{recipe?.Title ?? "Loading..."}
+
{recipe.Title}
Author: loading...
-
Category: {recipe?.Category ?? "loading..."}
+
Category: {recipe.Category}
About this recipe
-
{recipe?.Description ?? "loading..."}
+
{recipe.Description}
- @ingredientList(recipe.Ingredients)
- @instructionList(recipe.Instructions)
- @tagList(recipe.Tags, recipe.Created, recipe.Modified)
-
+
+
+
>
- )
+ ) : (
+ /* TODO: Implement a loading page! */
+
+
+
+ );
}