(DB/FEAT): Recipe tags have been implemented! #16
@ -275,16 +275,16 @@ found in **OTHER** section.
|
||||
- [ ] RecipeId (FK: Recipe.Id, Required) Serial
|
||||
- [ ] Created (Required) date/time stamp
|
||||
|
||||
- [ ] Tags: Represents a single tag that can be had by many recipes.
|
||||
- [ ] ID (PK) Serial
|
||||
- [ ] Name (Unique, Required) string(32)
|
||||
- [ ] Created (Required) date/time stamp
|
||||
- [x] Tags: Represents a single tag that can be had by many recipes.
|
||||
- [x] ID (PK) Serial
|
||||
- [x] Name (Unique, Required) string(32)
|
||||
- [x] Created (Required) date/time stamp
|
||||
|
||||
- [ ] RecipeTags: **Many-to-many** table to represent a list of tags on a recipe.
|
||||
- [ ] ID (PK) Serial
|
||||
- [ ] RecipeId (FK: Recipe.Id, Required) Serial
|
||||
- [ ] TagId (FK: Tag.Id, Required) Serial
|
||||
- [ ] Created (Required) date/time stamp
|
||||
- [x] RecipeTags: **Many-to-many** table to represent a list of tags on a recipe.
|
||||
- [x] ID (PK) Serial
|
||||
- [x] RecipeId (FK: Recipe.Id, Required) Serial
|
||||
- [x] TagId (FK: Tag.Id, Required) Serial
|
||||
- [x] Created (Required) date/time stamp
|
||||
|
||||
- [ ] Lists: Represents a single users shopping list.
|
||||
- [ ] ID (PK) Serial
|
||||
|
||||
@ -112,6 +112,9 @@ func (s *RecipeService) CreateRecipe(ctx *gin.Context) (*domain.Recipe, error) {
|
||||
|
||||
// TODO: Create the tags in the database
|
||||
if len(tags) > 0 {
|
||||
if err := s.recipeRepository.CreateRecipeTags(recipe, tags); err != nil {
|
||||
return &recipe, fmt.Errorf("Failed to attach/create tags. %s\n", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return &recipe, nil
|
||||
|
||||
@ -54,7 +54,8 @@ type RecipeIngredient struct {
|
||||
}
|
||||
|
||||
// Recipe is the database model of a recipe. There is no need to map to a different model so
|
||||
// this will remain in the domain.
|
||||
// this will remain in the domain. The Tags field should be loaded from the external Tags table,
|
||||
// but is still attached to this domain object.
|
||||
type Recipe struct {
|
||||
Id int
|
||||
Title string
|
||||
@ -68,6 +69,7 @@ type Recipe struct {
|
||||
UserId int
|
||||
Modified *time.Time // Pointer to allow null
|
||||
Created time.Time
|
||||
Tags []Tag
|
||||
}
|
||||
|
||||
// SearchFilters is a model which represents the required filters to complete a recipe search.
|
||||
@ -80,3 +82,20 @@ type SearchFilters struct {
|
||||
Difficulty int
|
||||
ServingSize int
|
||||
}
|
||||
|
||||
// Tag is a model which represents a single tag in the Tags table. A tag is mapped to a recipe
|
||||
// using the RecipeTag data model and can be accessed via their ID from the DB.
|
||||
type Tag struct {
|
||||
Id int
|
||||
Name string
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
// RecipeTag is a model which represents a single mapping in the RecipeTags table. This model
|
||||
// is a many-to-many mapping for tags to recipes.
|
||||
type RecipeTag struct {
|
||||
Id int
|
||||
RecipeId int
|
||||
TagId int
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
@ -4,4 +4,5 @@ type RecipeRepository interface {
|
||||
CreateRecipe(recipe *Recipe) error
|
||||
GetRecipe(id int) (*Recipe, error)
|
||||
SearchRecipes(filters SearchFilters) ([]Recipe, error)
|
||||
CreateRecipeTags(recipe Recipe, tags []string) error
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
-- Author: Hayden Hargreaves (hhargreaves2006@gmail.com)
|
||||
-- Desc: Create tables for the recipe tags and mapping tags to recipes.
|
||||
-- Date: 07/12/2025
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Create the tags table itself
|
||||
CREATE TABLE IF NOT EXISTS Tags (
|
||||
Id SERIAL PRIMARY KEY NOT NULL,
|
||||
Name VARCHAR(32) UNIQUE NOT NULL,
|
||||
Created TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create the recipe tag table to map tags to a recipe. This DB will be dynamically
|
||||
-- cleared when recipes or tags are deleted. This will prevent issues when deleting
|
||||
-- tags or recipes.
|
||||
CREATE TABLE IF NOT EXISTS RecipeTags (
|
||||
Id SERIAL PRIMARY KEY NOT NULL,
|
||||
RecipeId INT NOT NULL,
|
||||
TagId INT NOT NULL,
|
||||
Created TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT fk_recipe_id FOREIGN KEY (RecipeId) REFERENCES Recipes (Id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_tag_id FOREIGN KEY (TagId) REFERENCES Tags (Id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
@ -128,6 +128,29 @@ 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
|
||||
@ -374,3 +397,62 @@ func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters) ([]domain
|
||||
|
||||
return recipes, nil
|
||||
}
|
||||
|
||||
// CreateRecipeTags accepts a list of tags (names) and a recipe (already created by the DB) and
|
||||
// creates the tags that do not exists, and adds those that do exist to the mapping table for the
|
||||
// recipe. The result is records in the RecipeTags mapping table that represent all of the new
|
||||
// and existing tags provided to this function. The recipe object must only contain an ID to call
|
||||
// this function successfully, therefore, it must be an existing recipe. Any errors will be bubbled
|
||||
// to the caller.
|
||||
func (r *RecipeRepository) CreateRecipeTags(recipe domain.Recipe, tags []string) error {
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
// Normalize the tag names (lower case with trimmed space)
|
||||
normalized := make(map[string]struct{}) // Use map to disallow duplicates
|
||||
for _, tag := range tags {
|
||||
normalized[strings.ToLower(strings.TrimSpace(tag))] = struct{}{}
|
||||
}
|
||||
|
||||
// Insert the tags into the DB and return their IDS into the tag ID list
|
||||
var tagIds []int
|
||||
for tag := range normalized {
|
||||
var tagId int
|
||||
|
||||
query := `
|
||||
INSERT INTO tags (name) VALUES ($1)
|
||||
ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name
|
||||
RETURNING id;
|
||||
`
|
||||
|
||||
err := tx.QueryRow(query, tag).Scan(&tagId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to retrieve or create tag. %s\n", err.Error())
|
||||
}
|
||||
|
||||
tagIds = append(tagIds, tagId)
|
||||
}
|
||||
|
||||
// Using a prepared statement, execute the mapping insertions one-by-one
|
||||
for _, id := range tagIds {
|
||||
stmt, err := tx.Prepare("INSERT INTO RecipeTags (RecipeId, TagId) VALUES ($1, $2);")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create statement for recipe tag mapping. %s\n", err.Error())
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
if _, err := stmt.Exec(recipe.Id, id); err != nil {
|
||||
return fmt.Errorf("Failed to insert tag-recipe-mapping. %s\n", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ templ RecipePage(recipe domain.Recipe, user domainUser.User) {
|
||||
</div>
|
||||
@ingredientList(recipe.Ingredients)
|
||||
@instructionList(recipe.Instructions)
|
||||
@tagList(recipe.Created, recipe.Modified)
|
||||
@tagList(recipe.Tags, recipe.Created, recipe.Modified)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@ -150,14 +150,17 @@ templ instructionList(instructions []string) {
|
||||
</div>
|
||||
}
|
||||
|
||||
templ tagList(created time.Time, modified *time.Time) {
|
||||
templ tagList(tags []domain.Tag, 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>
|
||||
if len(tags) > 0 {
|
||||
<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">
|
||||
for _, tag := range tags {
|
||||
@tagListItem(tag.Name)
|
||||
}
|
||||
</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 {
|
||||
@ -190,11 +193,13 @@ templ ingredientListItem(name, quantity string, odd bool) {
|
||||
}
|
||||
|
||||
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"
|
||||
}>
|
||||
<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>
|
||||
|
||||
@ -206,7 +206,7 @@ func RecipePage(recipe domain.Recipe, user domainUser.User) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = tagList(recipe.Created, recipe.Modified).Render(ctx, templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = tagList(recipe.Tags, recipe.Created, recipe.Modified).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -427,7 +427,7 @@ func instructionList(instructions []string) templ.Component {
|
||||
})
|
||||
}
|
||||
|
||||
func tagList(created time.Time, modified *time.Time) templ.Component {
|
||||
func tagList(tags []domain.Tag, created time.Time, modified *time.Time) 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 {
|
||||
@ -448,55 +448,63 @@ func tagList(created time.Time, modified *time.Time) templ.Component {
|
||||
templ_7745c5c3_Var15 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<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\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<div class=\"px-4 py-4 md:px-8\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = tagListItem("healthy").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
if len(tags) > 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<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\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, tag := range tags {
|
||||
templ_7745c5c3_Err = tagListItem(tag.Name).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</ul>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = tagListItem("high-protein").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</ul><hr class=\"text-gray-300\"><p class=\"my-4 mb-1.5 text-sm text-gray-700\">Created: ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<hr class=\"text-gray-300\"><p class=\"my-4 mb-1.5 text-sm text-gray-700\">Created: ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(created.Format("January 2, 2006"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 162, Col: 91}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 165, Col: 91}
|
||||
}
|
||||
_, 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, 31, "</p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if modified != nil {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<p class=\"mb-4 text-sm text-gray-700\">Last Modified: ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<p class=\"mb-4 text-sm text-gray-700\">Last Modified: ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(modified.Format("January 2, 2006"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 164, Col: 92}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 167, Col: 92}
|
||||
}
|
||||
_, 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, 33, "</p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -525,48 +533,48 @@ func ingredientListItem(name, quantity string, odd bool) templ.Component {
|
||||
templ_7745c5c3_Var18 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<li")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<li")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if odd {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, " class=\"p-2 hover:bg-gray-100 transition-all duration-300 rounded-sm flex items-center justify-start bg-[#f8f8f8]\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, " class=\"p-2 hover:bg-gray-100 transition-all duration-300 rounded-sm flex items-center justify-start bg-[#f8f8f8]\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, " class=\"p-2 hover:bg-gray-100 transition-all duration-300 rounded-sm flex items-center justify-start\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, " class=\"p-2 hover:bg-gray-100 transition-all duration-300 rounded-sm flex items-center justify-start\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "><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\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "><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\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(quantity)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 188, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 191, Col: 45}
|
||||
}
|
||||
_, 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, 39, ": </span> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, ": </span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 188, Col: 63}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 191, Col: 63}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</li>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -595,48 +603,48 @@ func instructionListItem(num int, content string) templ.Component {
|
||||
templ_7745c5c3_Var21 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<li")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<li")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if num%2 == 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, " class=\"p-4 flex items-start gap-x-4 bg-[#f8f8f8]\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, " class=\"p-4 flex items-start gap-x-4 bg-[#f8f8f8]\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, " class=\"p-4 flex items-start gap-x-4\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, " class=\"p-4 flex items-start gap-x-4\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "><div class=\"size-10 bg-blue-50 rounded-full flex items-center justify-center flex-shrink-0\"><h3 class=\"text-blue-600 font-semibold\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "><div class=\"size-10 bg-blue-50 rounded-full flex items-center justify-center flex-shrink-0\"><h3 class=\"text-blue-600 font-semibold\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(num)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 199, Col: 48}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 204, Col: 48}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</h3></div><p class=\"\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</h3></div><p class=\"\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(content)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 201, Col: 23}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 206, Col: 23}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</p></li>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</p></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -665,20 +673,20 @@ func tagListItem(content string) templ.Component {
|
||||
templ_7745c5c3_Var24 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<li class=\"text-sm items-center bg-blue-100 text-blue-700 w-fit px-3 py-1.5 rounded-full\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<li class=\"text-sm items-center bg-blue-100 text-blue-700 w-fit px-3 py-1.5 rounded-full\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(content)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 207, Col: 11}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/recipe.templ`, Line: 212, Col: 11}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</li>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user