From c9be9876e3cbde62503f2964c1ab2851f2736e6f Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Thu, 13 Nov 2025 19:57:10 -0700 Subject: [PATCH] (FEAT): Profile page is complete, minus functionality --- .../components/results/ActivityListItem.tsx | 19 ++ web/src/components/results/RecipeListItem.tsx | 56 +++++ web/src/pages/Profile.tsx | 199 +++++++++++++++++- web/src/types/engagement.ts | 11 + web/src/types/user.ts | 19 ++ 5 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 web/src/components/results/ActivityListItem.tsx create mode 100644 web/src/components/results/RecipeListItem.tsx create mode 100644 web/src/types/engagement.ts create mode 100644 web/src/types/user.ts diff --git a/web/src/components/results/ActivityListItem.tsx b/web/src/components/results/ActivityListItem.tsx new file mode 100644 index 0000000..de262ba --- /dev/null +++ b/web/src/components/results/ActivityListItem.tsx @@ -0,0 +1,19 @@ +import type { Engagement } from "../../types/engagement"; + + +interface ActivityListItemProps { + engagement: Engagement; +} + +export default function ActivityListItem({ engagement }: ActivityListItemProps) { + return <> +
  • +

    + {engagement.Message} +

    +

    + {engagement.Created.toLocaleDateString()} +

    +
  • + ; +} diff --git a/web/src/components/results/RecipeListItem.tsx b/web/src/components/results/RecipeListItem.tsx new file mode 100644 index 0000000..ff321e6 --- /dev/null +++ b/web/src/components/results/RecipeListItem.tsx @@ -0,0 +1,56 @@ +import type { Recipe, Tag } from "../../types/recipe" + +interface RecipeListItemProps { + recipe: Recipe; +}; + +function displayDifficulty(diff: number): string { + switch (diff) { + case 1: + return "Beginner" + case 2: + return "Easy" + case 3: + return "Intermediate" + case 4: + return "Challenging" + case 5: + return "Extreme" + default: + return "" + } +} + +function displayTags(tags: Tag[]): string { + return tags.map(tag => tag.Name).join(", "); +} + +export default function RecipeListItem({ recipe }: RecipeListItemProps) { + // TODO: Click event + return <> +
  • +

    + {recipe.Title} +

    +

    + Difficulty: {displayDifficulty(recipe.Difficulty)} + {" "} | Duration: {recipe.Duration.Total} min + {" "} | Category: {recipe.Category} +

    +

    + Difficulty: {displayDifficulty(recipe.Difficulty)} +

    +

    + Duration: {recipe.Duration.Total} min +

    +

    + Category: {recipe.Category} +

    + {recipe.Tags && ( +

    + Tags: {displayTags(recipe.Tags)} +

    + )} +
  • + +} diff --git a/web/src/pages/Profile.tsx b/web/src/pages/Profile.tsx index 355a3d1..a4d27c6 100644 --- a/web/src/pages/Profile.tsx +++ b/web/src/pages/Profile.tsx @@ -1,7 +1,204 @@ +import { useEffect, useState } from "react"; +import type { User } from "../types/user"; +import type { Recipe } from "../types/recipe"; +import RecipeListItem from "../components/results/RecipeListItem"; +import type { Engagement } from "../types/engagement"; +import ActivityListItem from "../components/results/ActivityListItem"; + export default function Profile() { + const [user, setUser] = useState(null); + const [recipes, setRecipes] = useState([]); + const [favorites, setFavorites] = useState([]); + const [activity, setActivity] = useState([]); + + useEffect(() => { + const recipe: Recipe = { + Id: 1, + Title: "Classic Pancakes", + Description: "Fluffy and delicious pancakes perfect for breakfast.", + Instructions: [ + "In a bowl, mix all the dry ingredients.", + "In another bowl, whisk the wet ingredients.", + "Combine both mixes until smooth.", + "Heat a non-stick skillet and pour batter.", + "Cook until bubbles form, flip and cook the other side.", + "Serve warm with syrup." + ], + Serves: 4, + Difficulty: 2, // scale 1-5 (example) + Duration: { + Total: 20, + Prep: 5, + Cook: 15 + }, + Category: "breakfast", + Ingredients: [ + { Name: "Flour", Quantity: "2 cups" }, + { Name: "Milk", Quantity: "1.5 cups" }, + { Name: "Egg", Quantity: "1 large" }, + { Name: "Baking Powder", Quantity: "2 teaspoons" }, + { Name: "Salt", Quantity: "0.5 teaspoon" }, + { Name: "Sugar", Quantity: "1 tablespoon" } + ], + UserId: 101, + Modified: new Date("2025-10-30T09:00:00"), + Created: new Date("2025-10-01T08:30:00"), + Tags: [ + { Id: 1, Name: "easy", Created: new Date("2025-01-01T12:00:00") }, + { Id: 2, Name: "quick", Created: new Date("2025-01-02T12:00:00") }, + { Id: 3, Name: "breakfast", Created: new Date("2025-01-03T12:00:00") } + ], + Favorite: true + }; + + const recipe2: Recipe = { + Id: 2, + Title: "Classic Pancakes", + Description: "Fluffy and delicious pancakes perfect for breakfast.", + Instructions: [ + "In a bowl, mix all the dry ingredients.", + "In another bowl, whisk the wet ingredients.", + "Combine both mixes until smooth.", + "Heat a non-stick skillet and pour batter.", + "Cook until bubbles form, flip and cook the other side.", + "Serve warm with syrup." + ], + Serves: 4, + Difficulty: 2, // scale 1-5 (example) + Duration: { + Total: 20, + Prep: 5, + Cook: 15 + }, + Category: "breakfast", + Ingredients: [ + { Name: "Flour", Quantity: "2 cups" }, + { Name: "Milk", Quantity: "1.5 cups" }, + { Name: "Egg", Quantity: "1 large" }, + { Name: "Baking Powder", Quantity: "2 teaspoons" }, + { Name: "Salt", Quantity: "0.5 teaspoon" }, + { Name: "Sugar", Quantity: "1 tablespoon" } + ], + UserId: 101, + Modified: new Date("2025-10-30T09:00:00"), + Created: new Date("2025-10-01T08:30:00"), + Tags: [ + { Id: 1, Name: "easy", Created: new Date("2025-01-01T12:00:00") }, + { Id: 2, Name: "quick", Created: new Date("2025-01-02T12:00:00") }, + { Id: 3, Name: "breakfast", Created: new Date("2025-01-03T12:00:00") } + ], + Favorite: true + }; + + const eng: Engagement = { + Id: 1, + Type: "made", + Message: "Created some shit", + Entity: 1, + UserId: 1, + Created: new Date(), + }; + + const user: User = { + Id: 1, + GoogleId: "a", + Name: "Hayden Hargreaves", + Email: "hhargreaves2006@gmail.com", + ImageUrl: "https://lh3.googleusercontent.com/a/ACg8ocLeT6ltjQIkiBy1MgMJDbQxtBfMVfn8sP4e1t7d0bCJeHFdpcea=s96-c", + GoogleRefreshToken: "a", + Created: new Date(), + }; + + setUser(user); + + setRecipes([recipe, recipe2, recipe, recipe, recipe, recipe, recipe]); + setFavorites([recipe, recipe2]); + setActivity([eng]); + }, []); + return ( <> -

    Profile

    + {/* User Details Section */} +
    +
    + {user?.ImageUrl != "" ? ( + + ) : ( + + )} +
    +
    +

    {user?.Name}

    +

    {user?.Email}

    +
    +
    +

    {recipes.length} recipes

    +

    {favorites.length} favorites

    +
    +
    +
    +
    + + {/* Recipe Section */} +
    +

    My Recipes

    +
      + {recipes.length <= 4 ? ( + recipes.map(recipe => ) + ) : ( + recipes.slice(0, 4).map(recipe => ) + )} + +
    • + See all... +
    • +
      +
    +
    + + {/* Favorites Section */} +
    +

    My Favorites

    +
      + {favorites.length <= 4 ? ( + favorites.map(recipe => ) + ) : ( + favorites.slice(0, 4).map(recipe => ) + )} + +
    • + See all... +
    • +
      +
    +
    + + {/* Activity Section */} +
    +

    Recent Activity

    + +
    + + + {/* Logout Section TODO: Click event*/} +
    + + Logout + +
    ); } diff --git a/web/src/types/engagement.ts b/web/src/types/engagement.ts new file mode 100644 index 0000000..233e6c9 --- /dev/null +++ b/web/src/types/engagement.ts @@ -0,0 +1,11 @@ + +export type EngagementType = "made" | "liked" | "viewed" | "shared" | "reviewed" | "rated"; + +export interface Engagement { + Id: number; + Type: EngagementType; + Message: string; + Entity: number; + UserId: number; + Created: Date; +}; diff --git a/web/src/types/user.ts b/web/src/types/user.ts new file mode 100644 index 0000000..60d0d04 --- /dev/null +++ b/web/src/types/user.ts @@ -0,0 +1,19 @@ +export interface GoogleUserInfo { + Id: string; + Email: string; + Verified: boolean; + Name: string; + GivenName: string; + FamilyName: string; + Picture: string; +} + +export interface User { + Id: number; + GoogleId: string; + Name: string; + Email: string; + ImageUrl: string; + GoogleRefreshToken: string; + Created: Date; +}