Hayden Hargreaves 55c6a99bb1 (FEAT/DOC): Wired the backend to the UI for the recipe page!
The route will now display real, live data from the DB! Errors are not
handled very well, just returned as JSON for now. Need to implement an
error page for states when errors occur.

This commit also includes lots of documentation for the various
service/repository methods. I am trying to not let docs fall through the
cracks, but I am not perfect lol.
2025-07-02 22:53:15 -07:00

199 lines
7.0 KiB
Plaintext

package templates
import (
domain "github.com/haydenhargreaves/Potion/internal/domain/recipe"
domainUser "github.com/haydenhargreaves/Potion/internal/domain/user"
"github.com/haydenhargreaves/Potion/internal/templates/components"
"time"
)
templ servingIcon() {
<svg
class="h-8 text-blue-600"
fill="currentColor"
version="1.1"
id="Icons"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 32 32"
xml:space="preserve"
>
<g>
<circle cx="12" cy="16" r="5"></circle>
<path
d="M12,6C6.5,6,2,10.5,2,16s4.5,10,10,10s10-4.5,10-10S17.5,6,12,6z M12,23c-3.9,0-7-3.1-7-7s3.1-7,7-7s7,3.1,7,7
S15.9,23,12,23z"
></path>
<path
d="M30,10.5V5c0-0.6-0.4-1-1-1s-1,0.4-1,1v5.5c0,0.2,0,0.4,0,0.5h-1V5c0-0.6-0.4-1-1-1s-1,0.4-1,1v6h-1c0-0.2,0-0.4,0-0.5V5
c0-0.6-0.4-1-1-1s-1,0.4-1,1v5.5c0,1.9,0.5,3.4,1.4,4.3c0.7,0.8,1,1.8,0.9,2.7l-1,7.3c-0.1,0.8,0.1,1.6,0.6,2.2S25.2,28,26,28
s1.5-0.3,2.1-0.9s0.8-1.4,0.6-2.2l-1-7.3c-0.1-1,0.2-2,0.9-2.8C29.5,13.8,30,12.3,30,10.5z"
></path>
</g>
</svg>
}
templ timeIcon() {
<svg class="h-7 text-blue-600" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12 7V12L14.5 13.5M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
></path>
</svg>
}
templ starIcon(filled bool) {
if filled {
<svg class="h-6 text-blue-600" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
d="M23.632 9.201a.628.628 0 0 1-.22.678l-5.726 4.96 1.727 7.394a.606.606 0 0 1-.935.676l-6.503-3.953-6.503 3.953a.713.713 0 0 1-.374.112.57.57 0 0 1-.34-.109.629.629 0 0 1-.222-.679l1.729-7.393L.539 9.879A.607.607 0 0 1 .897 8.78l7.536-.635 2.965-7.083a.62.62 0 0 1 1.155.001l2.965 7.082 7.536.635a.63.63 0 0 1 .578.42z"
></path>
<path fill="none" d="M0 0h24v24H0z"></path>
</svg>
} else {
<svg class="h-6 text-gray-500" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
d="M23.054 8.781l-7.536-.635-2.965-7.082a.619.619 0 0 0-1.155 0L8.433 8.145.896 8.78a.607.607 0 0 0-.357 1.1l5.726 4.96-1.729 7.395a.63.63 0 0 0 .223.679.573.573 0 0 0 .339.108.717.717 0 0 0 .374-.111l6.503-3.954 6.503 3.953a.606.606 0 0 0 .935-.677l-1.727-7.392 5.725-4.96a.607.607 0 0 0-.357-1.099zm-6.48 5.698l1.662 7.113-6.261-3.806-6.262 3.807 1.663-7.114-5.513-4.776 7.257-.611 2.855-6.817 2.855 6.817 7.257.611z"
></path>
<path fill="none" d="M0 0h24v24H0z"></path>
</svg>
}
}
templ RecipePage(recipe domain.Recipe, user domainUser.User) {
@components.Navbar("")
<div class="w-full flex justify-center">
<div class="mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 h-full border-l border-r border-gray-300 bg-white">
<img class="bg-gray-100 w-full h-96 mx-auto mb-8" src="" alt=""/>
<div class="px-4 py-8 md:px-8">
<h1 class="text-3xl md:text-4xl font-bold text-gray-800">{ recipe.Title }</h1>
<p class="text-sm mt-2 mb-1 text-gray-700">Author: { user.Name }</p>
<p class="text-sm mb-2 text-gray-700">Category: { recipe.Category }</p>
</div>
@metadataSection(recipe)
<div class="px-4 py-8 md:px-8">
<h3 class="text-xl text-gray-800 font-semibold mb-2">About this recipe</h3>
<p class="text-gray-700">{ recipe.Description }</p>
</div>
@ingredientList(recipe.Ingredients)
@instructionList(recipe.Instructions)
@tagList(recipe.Created, recipe.Modified)
</div>
</div>
}
templ metadataSection(recipe domain.Recipe) {
<div
class="border border-blue-300 bg-blue-50 text-gray-700 mx-4 md:mx-8 rounded-lg flex flex-col
md:flex-row justify-center items-center py-8"
>
<div class="flex flex-col items-center justify-center text-sm my-4 md:my-0 mx-4 w-full md:w-1/4">
@timeIcon()
<p>Prep: { recipe.Duration.Prep } min</p>
<p>Cook: { recipe.Duration.Cook } min</p>
</div>
<div
class="flex flex-col items-center justify-center text-sm my-4 md:my-0 mx-4 border-y md:border-y-0 md:border-x border-blue-300 py-8 w-9/10 md:w-fit md:py-0 px-8"
>
<div class="flex gap-x-1 my-2">
for _ = range recipe.Difficulty {
@starIcon(true)
}
for _ = range (5 - recipe.Difficulty) {
@starIcon(false)
}
</div>
<p>{ recipe.Difficulty }</p>
</div>
<div class="flex flex-col items-center justify-center text-sm my-4 md:my-0 mx-4 w-1/4">
@servingIcon()
<p>Serves { recipe.Serves }</p>
</div>
</div>
}
templ ingredientList(ingredients []domain.RecipeIngredient) {
<div class="px-4 py-8 md:px-8">
<h2 class="text-2xl text-gray-800 font-semibold mb-2">Ingredients</h2>
<hr class="text-gray-300"/>
<ul class="text-lg my-4 text-gray-700">
for i, ingredient := range ingredients {
@ingredientListItem(ingredient.Name, ingredient.Quantity, i%2 == 1)
}
</ul>
</div>
}
templ instructionList(instructions []string) {
<div class="px-4 py-8 md:px-8">
<h2 class="text-2xl text-gray-800 font-semibold mb-2">Instructions</h2>
<hr class="text-gray-300"/>
<ul class="text-lg my-4 text-gray-700">
for i, instruction := range instructions {
@instructionListItem(i+1, instruction)
}
</ul>
</div>
}
templ tagList(created time.Time, modified *time.Time) {
<div class="px-4 py-4 md:px-8">
<h2 class="text-2xl text-gray-800 font-semibold mb-2">Tags</h2>
<hr class="text-gray-300"/>
<ul id="tag-list" class="my-4 flex gap-1 flex-wrap">
@tagListItem("healthy")
@tagListItem("high-protein")
</ul>
<hr class="text-gray-300"/>
<p class="my-4 mb-1.5 text-sm text-gray-700">Created: { created.Format("January 2, 2006") }</p>
if modified != nil {
<p class="mb-4 text-sm text-gray-700">Last Modified: { modified.Format("January 2, 2006") }</p>
}
</div>
}
templ ingredientListItem(name, quantity string, odd bool) {
<li
if odd {
class="p-2 hover:bg-gray-100 transition-all duration-300 rounded-sm flex items-center justify-start bg-[#f8f8f8]"
} else {
class="p-2 hover:bg-gray-100 transition-all duration-300 rounded-sm flex items-center justify-start"
}
>
<span class="mr-4">
<svg class="h-4 text-gray-400" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
></path>
</svg>
</span>
<span class="font-semibold mr-2">{ quantity }: </span> { name }
</li>
}
templ instructionListItem(num int, content string) {
<li if num % 2==0 {
class="p-4 flex items-start gap-x-4 bg-[#f8f8f8]"
} else {
class="p-4 flex items-start gap-x-4"
}>
<div class="size-10 bg-blue-50 rounded-full flex items-center justify-center flex-shrink-0">
<h3 class="text-blue-600 font-semibold">{ num }</h3>
</div>
<p class="">{ content }</p>
</li>
}
templ tagListItem(content string) {
<li class="text-sm items-center bg-blue-100 text-blue-700 w-fit px-3 py-1.5 rounded-full">
{ content }
</li>
}