From ae1f4f8e99008693a7d4d92f6df230ce4463824f Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Sat, 12 Jul 2025 14:22:23 -0700 Subject: [PATCH 1/3] (UI/STYLE): Implemented the first rendition of the profile page. Needs a backend wire job, but it looks pretty good. I can't really test this page on a real mobile device either, since it requires the user to be logged in and the testing doesn't work with google. --- doc/TechnicalSpecification.md | 29 +++ internal/templates/pages/profile.templ | 126 +++++++++- internal/templates/pages/profile_templ.go | 266 ++++++++++++++++++++-- web/static/css/tailwind.css | 136 ++++++++++- 4 files changed, 528 insertions(+), 29 deletions(-) diff --git a/doc/TechnicalSpecification.md b/doc/TechnicalSpecification.md index 886ed13..dce8da4 100644 --- a/doc/TechnicalSpecification.md +++ b/doc/TechnicalSpecification.md @@ -213,6 +213,35 @@ creation process will take place here +##### UI Requirements + +- [ ] User details section + - [ ] Google details: profile picture, full name and email + +- [ ] User recipe section + - [ ] List of all the recipes created by the user + - [ ] Paginated? **Maybe a max size and a button for seeing them all*** + - [ ] Short and sweet, not much data displayed, no image and no description + - [ ] Different layout then used elsewhere, spanned across the entire page + +- [ ] User saved recipe section + - [ ] List of all the recipes liked/saved by the user + - [ ] Max size of **x*** amount, a see all button will take the user to their favorites page + - [ ] Should look the like section above + +- [ ] User activity section + - [ ] List of all recent "engagement" the user has created + - [ ] Just a message and a date + + +'*': Not sure yet, still under consideration + + +##### API Requirements + + + + ## Database Requirements This section outlines the specific technical requirements for the database store for diff --git a/internal/templates/pages/profile.templ b/internal/templates/pages/profile.templ index 2480b9b..51a406b 100644 --- a/internal/templates/pages/profile.templ +++ b/internal/templates/pages/profile.templ @@ -2,7 +2,7 @@ package templates import "github.com/haydenhargreaves/Potion/internal/templates/components" import domain "github.com/haydenhargreaves/Potion/internal/domain/server" -import domain_user"github.com/haydenhargreaves/Potion/internal/domain/user" +import domain_user "github.com/haydenhargreaves/Potion/internal/domain/user" templ userDetailsSection(user domain_user.User) {
@@ -11,18 +11,123 @@ templ userDetailsSection(user domain_user.User) { class="w-24 md:w-32 border-2 border-blue-500 rounded-full shadow-blue-500 shadow select-none" src={ user.ImageUrl } /> -
-

{ user.Name }

-

{ user.Email }

+
+
+

{ user.Name }

+

{ user.Email }

+
+
+

10 recipes

+

14 favorites

+
} +templ recipesSection() { +
+

My Recipes

+ +
+} + +templ favoritesSection() { +
+

My Favorites

+ +
+} + +templ activitySection() { +
+

My Favorites

+ +
+} + +templ recipeListItem() { +
  • +

    + + My Awesome Chili Recipe + +

    + +

    + Difficulty: Medium +

    +

    + Duration: 60 min +

    +

    + Category: Dinner +

    +

    + Tags: comfort food, spicy, beef +

    +
  • +} + +templ activityListItem() { +
  • +

    + Rated "Spicy Chicken Wings" +

    +

    + 2 days ago +

    +
  • +} + templ logoutSection() { -
    +
    Logout @@ -32,11 +137,12 @@ templ logoutSection() { templ ProfilePage(user domain_user.User) { @components.Navbar(" profile") -
    -
    +
    +
    @userDetailsSection(user) + @recipesSection() + @favoritesSection() + @activitySection() @logoutSection()
    diff --git a/internal/templates/pages/profile_templ.go b/internal/templates/pages/profile_templ.go index 845c66e..a116777 100644 --- a/internal/templates/pages/profile_templ.go +++ b/internal/templates/pages/profile_templ.go @@ -46,14 +46,14 @@ func userDetailsSection(user domain_user.User) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 15, Col: 61} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 16, Col: 62} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -66,13 +66,235 @@ func userDetailsSection(user domain_user.User) templ.Component { var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 16, Col: 46} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 17, Col: 47} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "

    10 recipes

    14 favorites

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func recipesSection() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var5 := templ.GetChildren(ctx) + if templ_7745c5c3_Var5 == nil { + templ_7745c5c3_Var5 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "

    My Recipes

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func favoritesSection() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var6 := templ.GetChildren(ctx) + if templ_7745c5c3_Var6 == nil { + templ_7745c5c3_Var6 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "

    My Favorites

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func activitySection() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var8 := templ.GetChildren(ctx) + if templ_7745c5c3_Var8 == nil { + templ_7745c5c3_Var8 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "

    My Favorites

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func recipeListItem() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var9 := templ.GetChildren(ctx) + if templ_7745c5c3_Var9 == nil { + templ_7745c5c3_Var9 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
  • My Awesome Chili Recipe

    Difficulty: Medium | Duration: 60 min | Category: Dinner

    Difficulty: Medium

    Duration: 60 min

    Category: Dinner

    Tags: comfort food, spicy, beef

  • ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func activityListItem() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var10 := templ.GetChildren(ctx) + if templ_7745c5c3_Var10 == nil { + templ_7745c5c3_Var10 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
  • Rated \"Spicy Chicken Wings\"

    2 days ago

  • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -96,21 +318,21 @@ func logoutSection() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var5 := templ.GetChildren(ctx) - if templ_7745c5c3_Var5 == nil { - templ_7745c5c3_Var5 = templ.NopComponent + templ_7745c5c3_Var11 := templ.GetChildren(ctx) + if templ_7745c5c3_Var11 == nil { + templ_7745c5c3_Var11 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
    Logout
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" class=\"text-center border border-red-500 text-red-500 w-9/10 md:w-1/3 py-2 rounded-lg hover:cursor-pointer hover:bg-red-100 duration-300\">Logout") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -134,16 +356,16 @@ func ProfilePage(user domain_user.User) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var7 := templ.GetChildren(ctx) - if templ_7745c5c3_Var7 == nil { - templ_7745c5c3_Var7 = templ.NopComponent + templ_7745c5c3_Var13 := templ.GetChildren(ctx) + if templ_7745c5c3_Var13 == nil { + templ_7745c5c3_Var13 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = components.Navbar(" profile").Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -151,11 +373,23 @@ func ProfilePage(user domain_user.User) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + templ_7745c5c3_Err = recipesSection().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = favoritesSection().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = activitySection().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } templ_7745c5c3_Err = logoutSection().Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/web/static/css/tailwind.css b/web/static/css/tailwind.css index eb2fa10..d64a267 100644 --- a/web/static/css/tailwind.css +++ b/web/static/css/tailwind.css @@ -17,7 +17,6 @@ --color-blue-500: oklch(62.3% 0.214 259.815); --color-blue-600: oklch(54.6% 0.245 262.881); --color-blue-700: oklch(48.8% 0.243 264.376); - --color-blue-800: oklch(42.4% 0.199 265.638); --color-purple-100: oklch(94.6% 0.033 307.174); --color-purple-200: oklch(90.2% 0.063 306.703); --color-gray-50: oklch(98.5% 0.002 247.839); @@ -29,6 +28,7 @@ --color-gray-600: oklch(44.6% 0.03 256.802); --color-gray-700: oklch(37.3% 0.034 259.733); --color-gray-800: oklch(27.8% 0.033 256.848); + --color-gray-900: oklch(21% 0.034 264.665); --color-black: #000; --color-white: #fff; --spacing: 0.25rem; @@ -49,8 +49,6 @@ --text-3xl--line-height: calc(2.25 / 1.875); --text-4xl: 2.25rem; --text-4xl--line-height: calc(2.5 / 2.25); - --text-5xl: 3rem; - --text-5xl--line-height: 1; --text-6xl: 3.75rem; --text-6xl--line-height: 1; --text-8xl: 6rem; @@ -240,6 +238,9 @@ .static { position: static; } + .top-1 { + top: calc(var(--spacing) * 1); + } .top-1\/2 { top: calc(1/2 * 100%); } @@ -249,6 +250,9 @@ .left-0 { left: calc(var(--spacing) * 0); } + .left-1 { + left: calc(var(--spacing) * 1); + } .left-1\/2 { left: calc(1/2 * 100%); } @@ -273,6 +277,9 @@ .my-1 { margin-block: calc(var(--spacing) * 1); } + .my-1\.5 { + margin-block: calc(var(--spacing) * 1.5); + } .my-2 { margin-block: calc(var(--spacing) * 2); } @@ -300,6 +307,9 @@ .mt-16 { margin-top: calc(var(--spacing) * 16); } + .mt-auto { + margin-top: auto; + } .mr-2 { margin-right: calc(var(--spacing) * 2); } @@ -410,12 +420,18 @@ .min-h-screen { min-height: 100vh; } + .w-1 { + width: calc(var(--spacing) * 1); + } .w-1\/3 { width: calc(1/3 * 100%); } .w-1\/4 { width: calc(1/4 * 100%); } + .w-3 { + width: calc(var(--spacing) * 3); + } .w-3\/4 { width: calc(3/4 * 100%); } @@ -428,6 +444,9 @@ .w-5 { width: calc(var(--spacing) * 5); } + .w-9 { + width: calc(var(--spacing) * 9); + } .w-9\/10 { width: calc(9/10 * 100%); } @@ -446,16 +465,33 @@ .max-w-2xl { max-width: var(--container-2xl); } + .flex-shrink { + flex-shrink: 1; + } .flex-shrink-0 { flex-shrink: 0; } + .shrink-0 { + flex-shrink: 0; + } .flex-grow { flex-grow: 1; } + .border-collapse { + border-collapse: collapse; + } + .-translate-x-1 { + --tw-translate-x: calc(var(--spacing) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } .-translate-x-1\/2 { --tw-translate-x: calc(calc(1/2 * 100%) * -1); translate: var(--tw-translate-x) var(--tw-translate-y); } + .-translate-y-1 { + --tw-translate-y: calc(var(--spacing) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } .-translate-y-1\/2 { --tw-translate-y: calc(calc(1/2 * 100%) * -1); translate: var(--tw-translate-x) var(--tw-translate-y); @@ -468,9 +504,15 @@ --tw-scale-y: 50%; scale: var(--tw-scale-x) var(--tw-scale-y); } + .transform { + transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); + } .cursor-pointer { cursor: pointer; } + .resize { + resize: both; + } .resize-none { resize: none; } @@ -495,6 +537,9 @@ .items-start { align-items: flex-start; } + .justify-around { + justify-content: space-around; + } .justify-between { justify-content: space-between; } @@ -504,6 +549,9 @@ .justify-end { justify-content: flex-end; } + .justify-evenly { + justify-content: space-evenly; + } .justify-start { justify-content: flex-start; } @@ -645,6 +693,9 @@ .bg-red-100 { background-color: var(--color-red-100); } + .bg-red-500 { + background-color: var(--color-red-500); + } .bg-white { background-color: var(--color-white); } @@ -686,6 +737,9 @@ .p-4 { padding: calc(var(--spacing) * 4); } + .p-8 { + padding: calc(var(--spacing) * 8); + } .px-1 { padding-inline: calc(var(--spacing) * 1); } @@ -849,6 +903,9 @@ .text-gray-800 { color: var(--color-gray-800); } + .text-gray-900 { + color: var(--color-gray-900); + } .text-red-500 { color: var(--color-red-500); } @@ -858,6 +915,9 @@ .uppercase { text-transform: uppercase; } + .italic { + font-style: italic; + } .underline { text-decoration-line: underline; } @@ -920,6 +980,15 @@ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); transition-duration: var(--tw-duration, var(--default-transition-duration)); } + .transition-colors { + transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + .duration-100 { + --tw-duration: 100ms; + transition-duration: 100ms; + } .duration-150 { --tw-duration: 150ms; transition-duration: 150ms; @@ -1008,6 +1077,16 @@ color: var(--color-blue-700); } } + .odd\:bg-gray-50 { + &:nth-child(odd) { + background-color: var(--color-gray-50); + } + } + .even\:bg-gray-50 { + &:nth-child(even) { + background-color: var(--color-gray-50); + } + } .valid\:my-2 { &:valid { margin-block: calc(var(--spacing) * 2); @@ -1079,6 +1158,27 @@ } } } + .hover\:text-blue-500 { + &:hover { + @media (hover: hover) { + color: var(--color-blue-500); + } + } + } + .hover\:text-blue-600 { + &:hover { + @media (hover: hover) { + color: var(--color-blue-600); + } + } + } + .hover\:text-blue-700 { + &:hover { + @media (hover: hover) { + color: var(--color-blue-700); + } + } + } .hover\:shadow { &:hover { @media (hover: hover) { @@ -1189,6 +1289,11 @@ margin-top: calc(var(--spacing) * -2); } } + .md\:block { + @media (width >= 48rem) { + display: block; + } + } .md\:flex { @media (width >= 48rem) { display: flex; @@ -1408,6 +1513,26 @@ inherits: false; initial-value: 1; } +@property --tw-rotate-x { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-y { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-z { + syntax: "*"; + inherits: false; +} +@property --tw-skew-x { + syntax: "*"; + inherits: false; +} +@property --tw-skew-y { + syntax: "*"; + inherits: false; +} @property --tw-border-style { syntax: "*"; inherits: false; @@ -1617,6 +1742,11 @@ --tw-scale-x: 1; --tw-scale-y: 1; --tw-scale-z: 1; + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; --tw-border-style: solid; --tw-gradient-position: initial; --tw-gradient-from: #0000; From 3b6ebd0dae40f6bf21241e74ebe75aa105e33694 Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Sun, 13 Jul 2025 14:01:05 -0700 Subject: [PATCH 2/3] (DB/UI): Created user list API and wired to UI. The profile list will now properly display the users recipes! The favorites list does not exist yet, since there is no backend support for favoriting/saving recipes. So the list displays the same content as the user recipe list. Same goes for the activity list, not yet implemented. --- internal/app/handlers/page_handler.go | 11 +- internal/app/handlers/user_handler.go | 48 +++ internal/app/server/server.go | 1 + internal/app/service/recipe_service.go | 8 +- internal/domain/recipe/repository.go | 2 + internal/domain/recipe/service.go | 1 + .../database/repository/recipe_repository.go | 171 ++++++-- internal/templates/pages/profile.templ | 102 +++-- internal/templates/pages/profile_templ.go | 364 +++++++++++++----- 9 files changed, 545 insertions(+), 163 deletions(-) diff --git a/internal/app/handlers/page_handler.go b/internal/app/handlers/page_handler.go index b8b2885..d375b78 100644 --- a/internal/app/handlers/page_handler.go +++ b/internal/app/handlers/page_handler.go @@ -58,9 +58,18 @@ func ProfilePage(ctx *gin.Context) { // Else, get the user data deps := ctx.MustGet("deps").(*domainServer.InjectedDependencies) user := deps.UserService.GetAuthenicatedUser(ctx) + recipes, err := deps.RecipeService.GetUserRecipes(user.Id) + if err != nil { + fmt.Printf("Error getting recipes. %s\n", err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "status": http.StatusInternalServerError, + "message": fmt.Sprintf("Error getting recipes. %s\n", err.Error()), + }) + return + } title := "Potion - Profile" - page := pages.ProfilePage(user) + page := pages.ProfilePage(user, recipes) ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page)) } diff --git a/internal/app/handlers/user_handler.go b/internal/app/handlers/user_handler.go index 5ac8282..261e199 100644 --- a/internal/app/handlers/user_handler.go +++ b/internal/app/handlers/user_handler.go @@ -1 +1,49 @@ package handlers + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + domain "github.com/haydenhargreaves/Potion/internal/domain/server" +) + +func GetUserRecipes(ctx *gin.Context) { + deps := ctx.MustGet("deps").(*domain.InjectedDependencies) + + // Ensure logged in + if !domain.IsLoggedIn(ctx) { + ctx.JSON(http.StatusUnauthorized, gin.H{ + "status": http.StatusUnauthorized, + "message": "User is not authorized to access this endpoint. Please login to continue.", + "recipes": nil, + }) + return + } + + userId, ok := ctx.MustGet("userId").(int) + if !ok { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "status": http.StatusInternalServerError, + "message": "Unable to access user id from store.", + "recipes": nil, + }) + return + } + + recipes, err := deps.RecipeService.GetUserRecipes(userId) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("Could not get user recipes. %s", err.Error()), + "recipes": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "User recipes successfully retrieved.", + "recipes": recipes, + }) +} diff --git a/internal/app/server/server.go b/internal/app/server/server.go index f46e8bd..406169b 100644 --- a/internal/app/server/server.go +++ b/internal/app/server/server.go @@ -181,6 +181,7 @@ func (s *Server) Setup() *Server { // Recipe endpoints router_api.POST("/recipe", handlers.CreateRecipe) router_api.POST("/recipe/search", handlers.SearchRecipes) + router_api.GET("/user/recipes", handlers.GetUserRecipes) // Catch un-routed URLS s.Router.NoRoute(func(ctx *gin.Context) { diff --git a/internal/app/service/recipe_service.go b/internal/app/service/recipe_service.go index 82e9696..870f053 100644 --- a/internal/app/service/recipe_service.go +++ b/internal/app/service/recipe_service.go @@ -134,8 +134,8 @@ func (s *RecipeService) GetRecipe(id int) (*domain.Recipe, error) { } // SearchRecipes will search the database using the filters provided. The recipes can be passed into -// a template and displayed in the UI as the search result. A more detailed definition of the -// filters is provided below. +// a template and displayed in the UI as the search result. A more detailed definition of the +// filters is provided below. // // Each input is given a bit value (e.g., 00001 for 1) and will be passed // back to this handler as an array. The values are then added together @@ -150,3 +150,7 @@ func (s *RecipeService) GetRecipe(id int) (*domain.Recipe, error) { func (s *RecipeService) SearchRecipes(filters domain.SearchFilters) ([]domain.Recipe, error) { return s.recipeRepository.SearchRecipes(filters) } + +func (s *RecipeService) GetUserRecipes(id int) ([]domain.Recipe, error) { + return s.recipeRepository.GetUserRecipes(id) +} diff --git a/internal/domain/recipe/repository.go b/internal/domain/recipe/repository.go index b47ccd9..a5c57d9 100644 --- a/internal/domain/recipe/repository.go +++ b/internal/domain/recipe/repository.go @@ -5,4 +5,6 @@ type RecipeRepository interface { GetRecipe(id int) (*Recipe, error) SearchRecipes(filters SearchFilters) ([]Recipe, error) CreateRecipeTags(recipe Recipe, tags []string) error + GetUserRecipes(id int) ([]Recipe, error) + GetRecipeTags(recipe *Recipe) error } diff --git a/internal/domain/recipe/service.go b/internal/domain/recipe/service.go index cf6723e..b3466f4 100644 --- a/internal/domain/recipe/service.go +++ b/internal/domain/recipe/service.go @@ -6,4 +6,5 @@ type RecipeService interface { CreateRecipe(ctx *gin.Context) (*Recipe, error) GetRecipe(id int) (*Recipe, error) SearchRecipes(filters SearchFilters) ([]Recipe, error) + GetUserRecipes(id int) ([]Recipe, error) } diff --git a/internal/infrastructure/database/repository/recipe_repository.go b/internal/infrastructure/database/repository/recipe_repository.go index 293f9c8..e92739a 100644 --- a/internal/infrastructure/database/repository/recipe_repository.go +++ b/internal/infrastructure/database/repository/recipe_repository.go @@ -128,34 +128,6 @@ func (r *RecipeRepository) GetRecipe(id int) (*domain.Recipe, error) { return nil, fmt.Errorf("Failed to location recipe in database: %s", err.Error()) } - // Get tags from external tables - query = ` - SELECT t.* FROM tags t - JOIN recipetags rt ON rt.tagid = t.id - WHERE rt.recipeid = $1; - ` - rows, err := tx.Query(query, recipe.Id) - if err != nil { - return nil, fmt.Errorf("Failed to get tags for recipe. %s\n", err.Error()) - } - defer rows.Close() - - for rows.Next() { - var tag domain.Tag - - err := rows.Scan(&tag.Id, &tag.Name, &tag.Created) - if err != nil { - return nil, fmt.Errorf("Failed to scan tag onto domain model. %s\n", err.Error()) - } - - recipe.Tags = append(recipe.Tags, tag) - } - - if err := tx.Commit(); err != nil { - tx.Rollback() - return nil, err - } - // Parse duration if len(durationBytes) > 0 { var duration domain.RecipeDuration @@ -180,6 +152,14 @@ func (r *RecipeRepository) GetRecipe(id int) (*domain.Recipe, error) { recipe.Ingredients = []domain.RecipeIngredient{} } + // Add tags + r.GetRecipeTags(&recipe) + + if err := tx.Commit(); err != nil { + tx.Rollback() + return nil, err + } + return &recipe, nil } @@ -387,6 +367,9 @@ func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters) ([]domain recipe.Ingredients = []domain.RecipeIngredient{} } + // Add tags + r.GetRecipeTags(&recipe) + recipes = append(recipes, recipe) } @@ -456,3 +439,135 @@ func (r *RecipeRepository) CreateRecipeTags(recipe domain.Recipe, tags []string) return nil } + +// GetUserRecipes gets a list of a users owned recipes. This function does not ensure the user is +// authenticated or exists. If nothing is found, a blank slice will be returned. The resulting list +// is sorted by the created dates, newest first. Any errors will be bubbled to the caller. +func (r *RecipeRepository) GetUserRecipes(id int) ([]domain.Recipe, error) { + tx, err := r.db.Begin() + if err != nil { + tx.Rollback() + return nil, err + } + + query := ` + SELECT id, title, description, instructions, serves, difficulty, duration, category, ingredients, + userid, modified, created + FROM recipes + WHERE userid = $1 + ORDER BY created DESC; + ` + + rows, err := tx.Query(query, id) + if err != nil { + return nil, fmt.Errorf("Failed to query DB for user recipes. %s\n", err.Error()) + } + defer rows.Close() + + // Prepare statement for tag query + // tagQuery := ` + // ` + + var recipes []domain.Recipe + for rows.Next() { + var recipe domain.Recipe + var durationBytes []byte + var ingredientBytes []byte + + // Scan results from recipe query onto recipe object + if err := rows.Scan( + &recipe.Id, + &recipe.Title, + &recipe.Description, + pq.Array(&recipe.Instructions), + &recipe.Serves, + &recipe.Difficulty, + &durationBytes, + &recipe.Category, + &ingredientBytes, + &recipe.UserId, + &recipe.Modified, + &recipe.Created, + ); err != nil { + return nil, fmt.Errorf("Failed to scan row onto recipe object. %s\n", err.Error()) + } + + // Parse duration + if len(durationBytes) > 0 { + var duration domain.RecipeDuration + if err := json.Unmarshal(durationBytes, &duration); err != nil { + return nil, fmt.Errorf("Failed to parse duration from database: %s", err.Error()) + } + + recipe.Duration = duration + } else { + recipe.Duration = domain.RecipeDuration{} + } + + // Parse ingredient + if len(ingredientBytes) > 0 { + var ingredients []domain.RecipeIngredient + if err := json.Unmarshal(ingredientBytes, &ingredients); err != nil { + return nil, fmt.Errorf("Failed to parse ingredients from database: %s", err.Error()) + } + + recipe.Ingredients = ingredients + } else { + recipe.Ingredients = []domain.RecipeIngredient{} + } + + // Add tags + r.GetRecipeTags(&recipe) + + recipes = append(recipes, recipe) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + return nil, err + } + + return recipes, nil +} + +// GetRecipeTags requires a recipe to be filled with at least an ID. This function will use the ID +// defined in the provided recipe to fill the Tags array with the recipe's tags from the database. +// The recipe is modified in place and is not returned. Any errors will be bubbled to the caller. +func (r *RecipeRepository) GetRecipeTags(recipe *domain.Recipe) error { + tx, err := r.db.Begin() + if err != nil { + tx.Rollback() + return err + } + + recipe.Tags = []domain.Tag{} + + query := ` + SELECT t.* FROM tags t + JOIN recipetags rt ON rt.tagid = t.id + WHERE rt.recipeid = $1; + ` + rows, err := tx.Query(query, recipe.Id) + if err != nil { + return fmt.Errorf("Failed to get tags for recipe. %s\n", err.Error()) + } + defer rows.Close() + + for rows.Next() { + var tag domain.Tag + + err := rows.Scan(&tag.Id, &tag.Name, &tag.Created) + if err != nil { + return fmt.Errorf("Failed to scan tag onto domain model. %s\n", err.Error()) + } + + recipe.Tags = append(recipe.Tags, tag) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + return err + } + + return nil +} diff --git a/internal/templates/pages/profile.templ b/internal/templates/pages/profile.templ index 51a406b..c631efa 100644 --- a/internal/templates/pages/profile.templ +++ b/internal/templates/pages/profile.templ @@ -1,10 +1,38 @@ package templates import "github.com/haydenhargreaves/Potion/internal/templates/components" +import "fmt" +import "strings" import domain "github.com/haydenhargreaves/Potion/internal/domain/server" -import domain_user "github.com/haydenhargreaves/Potion/internal/domain/user" +import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe" +import domainUser "github.com/haydenhargreaves/Potion/internal/domain/user" -templ userDetailsSection(user domain_user.User) { +func displayDifficulty(diff int) 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 "" + } +} + +func displayTags(tags []domainRecipe.Tag) string { + names := make([]string, 0, len(tags)) + for _, tag := range tags { + names = append(names, tag.Name) + } + return strings.Join(names, ", ") +} + +templ userDetailsSection(user domainUser.User, recipeCount int) {
    { user.Email }

    -

    10 recipes

    -

    14 favorites

    +

    { recipeCount } recipes

    +

    0 favorites

    } -templ recipesSection() { +templ recipesSection(recipes []domainRecipe.Recipe) {

    My Recipes

    } -templ ProfilePage(user domain_user.User) { +templ ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe) { @components.Navbar(" profile")
    - @userDetailsSection(user) - @recipesSection() - @favoritesSection() + @userDetailsSection(user, len(recipes)) + @recipesSection(recipes) + @favoritesSection(recipes) @activitySection() @logoutSection()
    diff --git a/internal/templates/pages/profile_templ.go b/internal/templates/pages/profile_templ.go index a116777..6ea849b 100644 --- a/internal/templates/pages/profile_templ.go +++ b/internal/templates/pages/profile_templ.go @@ -9,10 +9,38 @@ import "github.com/a-h/templ" import templruntime "github.com/a-h/templ/runtime" import "github.com/haydenhargreaves/Potion/internal/templates/components" +import "fmt" +import "strings" import domain "github.com/haydenhargreaves/Potion/internal/domain/server" -import domain_user "github.com/haydenhargreaves/Potion/internal/domain/user" +import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe" +import domainUser "github.com/haydenhargreaves/Potion/internal/domain/user" -func userDetailsSection(user domain_user.User) templ.Component { +func displayDifficulty(diff int) 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 "" + } +} + +func displayTags(tags []domainRecipe.Tag) string { + names := make([]string, 0, len(tags)) + for _, tag := range tags { + names = append(names, tag.Name) + } + return strings.Join(names, ", ") +} + +func userDetailsSection(user domainUser.User, recipeCount int) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -40,7 +68,7 @@ func userDetailsSection(user domain_user.User) templ.Component { var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(user.ImageUrl) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 12, Col: 23} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 40, Col: 23} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { @@ -53,7 +81,7 @@ func userDetailsSection(user domain_user.User) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 16, Col: 62} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 44, Col: 62} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -66,13 +94,26 @@ func userDetailsSection(user domain_user.User) templ.Component { var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 17, Col: 47} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 45, Col: 47} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "

    10 recipes

    14 favorites

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(recipeCount) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 48, Col: 72} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " recipes

    0 favorites

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -80,56 +121,7 @@ func userDetailsSection(user domain_user.User) templ.Component { }) } -func recipesSection() templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var5 := templ.GetChildren(ctx) - if templ_7745c5c3_Var5 == nil { - templ_7745c5c3_Var5 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "

    My Recipes

      ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = recipeListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = recipeListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = recipeListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = recipeListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
    • See all...
    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -func favoritesSection() templ.Component { +func recipesSection(recipes []domainRecipe.Recipe) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -150,36 +142,83 @@ func favoritesSection() templ.Component { templ_7745c5c3_Var6 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "

    My Favorites

      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "

      My Recipes

        ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = recipeListItem().Render(ctx, templ_7745c5c3_Buffer) + if len(recipes) <= 4 { + for _, recipe := range recipes { + templ_7745c5c3_Err = recipeListItem(recipe).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + } else { + for _, recipe := range recipes[:4] { + templ_7745c5c3_Err = recipeListItem(recipe).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
      • See all...
      ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = recipeListItem().Render(ctx, templ_7745c5c3_Buffer) + return nil + }) +} + +func favoritesSection(recipes []domainRecipe.Recipe) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var7 := templ.GetChildren(ctx) + if templ_7745c5c3_Var7 == nil { + templ_7745c5c3_Var7 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "

      My Favorites

        ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = recipeListItem().Render(ctx, templ_7745c5c3_Buffer) + if len(recipes) <= 4 { + for _, recipe := range recipes { + templ_7745c5c3_Err = recipeListItem(recipe).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + } else { + for _, recipe := range recipes[:4] { + templ_7745c5c3_Err = recipeListItem(recipe).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
      • See all...
      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\" class=\"bg-red-500\">
    • See all...
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -203,12 +242,12 @@ func activitySection() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var8 := templ.GetChildren(ctx) - if templ_7745c5c3_Var8 == nil { - templ_7745c5c3_Var8 = templ.NopComponent + templ_7745c5c3_Var9 := templ.GetChildren(ctx) + if templ_7745c5c3_Var9 == nil { + templ_7745c5c3_Var9 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "

    My Favorites

      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "

      Recent Activity

        ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -236,7 +275,7 @@ func activitySection() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
      • See all...
      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
    • See all...
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -244,7 +283,7 @@ func activitySection() templ.Component { }) } -func recipeListItem() templ.Component { +func recipeListItem(recipe domainRecipe.Recipe) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -260,12 +299,135 @@ func recipeListItem() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var9 := templ.GetChildren(ctx) - if templ_7745c5c3_Var9 == nil { - templ_7745c5c3_Var9 = templ.NopComponent + templ_7745c5c3_Var10 := templ.GetChildren(ctx) + if templ_7745c5c3_Var10 == nil { + templ_7745c5c3_Var10 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
  • My Awesome Chili Recipe

    Difficulty: Medium | Duration: 60 min | Category: Dinner

    Difficulty: Medium

    Duration: 60 min

    Category: Dinner

    Tags: comfort food, spicy, beef

  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
  • ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var12 string + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Title) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 129, Col: 18} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "

    Difficulty: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(displayDifficulty(recipe.Difficulty)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 133, Col: 81} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " | Duration: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 134, Col: 66} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " min | Category: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var15 string + templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 135, Col: 60} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "

    Difficulty: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var16 string + templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(displayDifficulty(recipe.Difficulty)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 138, Col: 81} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "

    Duration: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var17 string + templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 141, Col: 64} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " min

    Category: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var18 string + templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 144, Col: 58} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(recipe.Tags) > 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "

    Tags: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var19 string + templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(displayTags(recipe.Tags)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 148, Col: 36} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "
  • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -289,12 +451,12 @@ func activityListItem() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var10 := templ.GetChildren(ctx) - if templ_7745c5c3_Var10 == nil { - templ_7745c5c3_Var10 = templ.NopComponent + templ_7745c5c3_Var20 := templ.GetChildren(ctx) + if templ_7745c5c3_Var20 == nil { + templ_7745c5c3_Var20 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
  • Rated \"Spicy Chicken Wings\"

    2 days ago

  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
  • Rated \"Spicy Chicken Wings\"

    2 days ago

  • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -318,21 +480,21 @@ func logoutSection() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var11 := templ.GetChildren(ctx) - if templ_7745c5c3_Var11 == nil { - templ_7745c5c3_Var11 = templ.NopComponent + templ_7745c5c3_Var21 := templ.GetChildren(ctx) + if templ_7745c5c3_Var21 == nil { + templ_7745c5c3_Var21 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
    Logout
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" class=\"text-center border border-red-500 text-red-500 w-9/10 md:w-1/3 py-2 rounded-lg hover:cursor-pointer hover:bg-red-100 duration-300\">Logout") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -340,7 +502,7 @@ func logoutSection() templ.Component { }) } -func ProfilePage(user domain_user.User) templ.Component { +func ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -356,28 +518,28 @@ func ProfilePage(user domain_user.User) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var13 := templ.GetChildren(ctx) - if templ_7745c5c3_Var13 == nil { - templ_7745c5c3_Var13 = templ.NopComponent + templ_7745c5c3_Var23 := templ.GetChildren(ctx) + if templ_7745c5c3_Var23 == nil { + templ_7745c5c3_Var23 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = components.Navbar(" profile").Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = userDetailsSection(user).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = userDetailsSection(user, len(recipes)).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = recipesSection().Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = recipesSection(recipes).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = favoritesSection().Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = favoritesSection(recipes).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -389,7 +551,7 @@ func ProfilePage(user domain_user.User) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } From 9be423cea002006faf174d4ccd06a0190f3d502a Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Sun, 13 Jul 2025 19:58:47 -0700 Subject: [PATCH 3/3] (UI): Finished up the profile implementation for now. The other sections not working yet were marked as under construction. There still needs to be a page for "see all..." for the recipes, but that will be a task for another time. --- internal/templates/pages/profile.templ | 32 ++-- internal/templates/pages/profile_templ.go | 212 ++++++++++------------ web/static/css/tailwind.css | 36 +--- 3 files changed, 108 insertions(+), 172 deletions(-) diff --git a/internal/templates/pages/profile.templ b/internal/templates/pages/profile.templ index c631efa..00e0a76 100644 --- a/internal/templates/pages/profile.templ +++ b/internal/templates/pages/profile.templ @@ -35,10 +35,18 @@ func displayTags(tags []domainRecipe.Tag) string { templ userDetailsSection(user domainUser.User, recipeCount int) {
    + if user.ImageUrl != "" { + } else { + + + }

    { user.Name }

    @@ -58,7 +66,7 @@ templ recipesSection(recipes []domainRecipe.Recipe) {

    My Recipes

      if len(recipes) <= 4 { - for _, recipe := range recipes { + for _, recipe :=range recipes { @recipeListItem(recipe) } } else { @@ -80,30 +88,15 @@ templ recipesSection(recipes []domainRecipe.Recipe) { templ favoritesSection(recipes []domainRecipe.Recipe) {

      My Favorites

      -
        - if len(recipes) <= 4 { - for _, recipe := range recipes { - @recipeListItem(recipe) - } - } else { - for _, recipe := range recipes[:4] { - @recipeListItem(recipe) - } - } - -
      • - See all... -
      • -
        -
      +

      Favorites section is under construction!

      } templ activitySection() {

      Recent Activity

      +

      Activity section is under construction!

      +
      } diff --git a/internal/templates/pages/profile_templ.go b/internal/templates/pages/profile_templ.go index 6ea849b..b035f02 100644 --- a/internal/templates/pages/profile_templ.go +++ b/internal/templates/pages/profile_templ.go @@ -61,59 +61,87 @@ func userDetailsSection(user domainUser.User, recipeCount int) templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
      ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var2 string - templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(user.ImageUrl) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 40, Col: 23} + if user.ImageUrl != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">

      ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var3 string - templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 44, Col: 62} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "

      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "

      ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var4 string - templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email) + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 45, Col: 47} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 52, Col: 62} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "

      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "

      ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var5 string - templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(recipeCount) + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 48, Col: 72} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 53, Col: 47} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " recipes

      0 favorites

      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(recipeCount) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 56, Col: 72} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " recipes

    0 favorites

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -137,12 +165,12 @@ func recipesSection(recipes []domainRecipe.Recipe) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var6 := templ.GetChildren(ctx) - if templ_7745c5c3_Var6 == nil { - templ_7745c5c3_Var6 = templ.NopComponent + templ_7745c5c3_Var7 := templ.GetChildren(ctx) + if templ_7745c5c3_Var7 == nil { + templ_7745c5c3_Var7 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "

    My Recipes

      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "

      My Recipes

        ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -161,7 +189,7 @@ func recipesSection(recipes []domainRecipe.Recipe) templ.Component { } } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
      • See all...
      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
    • See all...
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -185,40 +213,12 @@ func favoritesSection(recipes []domainRecipe.Recipe) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var7 := templ.GetChildren(ctx) - if templ_7745c5c3_Var7 == nil { - templ_7745c5c3_Var7 = templ.NopComponent + templ_7745c5c3_Var8 := templ.GetChildren(ctx) + if templ_7745c5c3_Var8 == nil { + templ_7745c5c3_Var8 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "

    My Favorites

      ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if len(recipes) <= 4 { - for _, recipe := range recipes { - templ_7745c5c3_Err = recipeListItem(recipe).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - } else { - for _, recipe := range recipes[:4] { - templ_7745c5c3_Err = recipeListItem(recipe).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
    • See all...
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "

    My Favorites

    Favorites section is under construction!

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -247,35 +247,7 @@ func activitySection() templ.Component { templ_7745c5c3_Var9 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "

    Recent Activity

      ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = activityListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = activityListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = activityListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = activityListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = activityListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = activityListItem().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
    • See all...
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "

    Recent Activity

    Activity section is under construction!

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -304,7 +276,7 @@ func recipeListItem(recipe domainRecipe.Recipe) templ.Component { templ_7745c5c3_Var10 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" class=\"hover:text-blue-600 duration-100\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var12 string templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 129, Col: 18} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 123, Col: 18} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "

    Difficulty: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "

    Difficulty: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var13 string templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(displayDifficulty(recipe.Difficulty)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 133, Col: 81} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 127, Col: 81} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " | Duration: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " | Duration: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 134, Col: 66} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 128, Col: 66} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " min | Category: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " min | Category: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var15 string templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 135, Col: 60} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 129, Col: 60} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "

    Difficulty: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "

    Difficulty: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var16 string templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(displayDifficulty(recipe.Difficulty)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 138, Col: 81} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 132, Col: 81} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "

    Duration: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "

    Duration: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var17 string templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 141, Col: 64} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 135, Col: 64} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " min

    Category: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " min

    Category: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var18 string templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 144, Col: 58} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 138, Col: 58} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if len(recipe.Tags) > 0 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "

    Tags: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "

    Tags: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var19 string templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(displayTags(recipe.Tags)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 148, Col: 36} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 142, Col: 36} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "
  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -456,7 +428,7 @@ func activityListItem() templ.Component { templ_7745c5c3_Var20 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
  • Rated \"Spicy Chicken Wings\"

    2 days ago

  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "
  • Rated \"Spicy Chicken Wings\"

    2 days ago

  • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -485,7 +457,7 @@ func logoutSection() templ.Component { templ_7745c5c3_Var21 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "
    Logout
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\" class=\"text-center border border-red-500 text-red-500 w-9/10 md:w-1/3 py-2 rounded-lg hover:cursor-pointer hover:bg-red-100 duration-300\">Logout") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -527,7 +499,7 @@ func ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe) templ.Comp if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -551,7 +523,7 @@ func ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe) templ.Comp if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/web/static/css/tailwind.css b/web/static/css/tailwind.css index d64a267..8bd1054 100644 --- a/web/static/css/tailwind.css +++ b/web/static/css/tailwind.css @@ -537,9 +537,6 @@ .items-start { align-items: flex-start; } - .justify-around { - justify-content: space-around; - } .justify-between { justify-content: space-between; } @@ -549,9 +546,6 @@ .justify-end { justify-content: flex-end; } - .justify-evenly { - justify-content: space-evenly; - } .justify-start { justify-content: flex-start; } @@ -707,6 +701,9 @@ --tw-gradient-position: to right in oklab; background-image: linear-gradient(var(--tw-gradient-stops)); } + .bg-none { + background-image: none; + } .from-blue-100 { --tw-gradient-from: var(--color-blue-100); --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); @@ -903,9 +900,6 @@ .text-gray-800 { color: var(--color-gray-800); } - .text-gray-900 { - color: var(--color-gray-900); - } .text-red-500 { color: var(--color-red-500); } @@ -980,11 +974,6 @@ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); transition-duration: var(--tw-duration, var(--default-transition-duration)); } - .transition-colors { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } .duration-100 { --tw-duration: 100ms; transition-duration: 100ms; @@ -1077,11 +1066,6 @@ color: var(--color-blue-700); } } - .odd\:bg-gray-50 { - &:nth-child(odd) { - background-color: var(--color-gray-50); - } - } .even\:bg-gray-50 { &:nth-child(even) { background-color: var(--color-gray-50); @@ -1158,13 +1142,6 @@ } } } - .hover\:text-blue-500 { - &:hover { - @media (hover: hover) { - color: var(--color-blue-500); - } - } - } .hover\:text-blue-600 { &:hover { @media (hover: hover) { @@ -1172,13 +1149,6 @@ } } } - .hover\:text-blue-700 { - &:hover { - @media (hover: hover) { - color: var(--color-blue-700); - } - } - } .hover\:shadow { &:hover { @media (hover: hover) {