(FEAT): Search is returning recipes, next just need a UI and wire job.

Furthermore, not sure how we are going to handle the searching. Maybe a
full-text search index? For now, it has been ignored, but the filters
seem to be working properly.
This commit is contained in:
Hayden Hargreaves 2025-07-06 22:40:15 -07:00
parent 6744a0144a
commit 7ad710f880
16 changed files with 520 additions and 100 deletions

View File

@ -194,7 +194,7 @@ creation process will take place here
- [x] Recipe Creation Wizard
- [x] Create a new recipe object in the database
- [x] Recipe should be attached to a user (logged in)
- [ ] User should be directed to the view recipe page on a successful creation
- [x] User should be directed to the view recipe page on a successful creation

View File

@ -34,3 +34,13 @@ func CreateRecipe(ctx *gin.Context) {
ctx.Header("HX-Redirect", url)
ctx.Status(http.StatusCreated)
}
func SearchRecipes(ctx *gin.Context) {
deps := ctx.MustGet("deps").(*domain.InjectedDependencies)
recipes, err := deps.RecipeService.SearchRecipes(ctx)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"error": err.Error()})
}
ctx.JSON(http.StatusOK, gin.H{"recipes": recipes})
}

View File

@ -176,8 +176,8 @@ func (s *Server) Setup() *Server {
router_api.GET("/auth/logout", handlers.Logout)
// Recipe endpoints
// TODO: This should be post. Temp!
router_api.POST("/recipe", handlers.CreateRecipe)
router_api.POST("/recipe/search", handlers.SearchRecipes)
return s
}

View File

@ -129,3 +129,47 @@ func (s *RecipeService) GetRecipe(id int) (*domain.Recipe, error) {
return recipe, err
}
// toBits converts an array of stringified numbers into a single summed value
func toBits(arr []string) (bits int) {
for _, x := range arr {
num, _ := strconv.Atoi(x)
bits += num
}
return
}
// isBitActive returns true when the bit at pos (0 indexed) is true.
func isBitActive(bits, pos int) bool {
return (bits>>pos)&1 == 1
}
func (s *RecipeService) SearchRecipes(ctx *gin.Context) ([]domain.Recipe, error) {
// NOTE: How are the filters handled?
// 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
// and will result in a integer which represents bit values. These bits
// can then be passed to the repository and are then parsed to determine
// which filters should be applied.
// Parsing these is simple, for each filter option, use the bitwise and (&)
// operator with the value we expect for the filter. When 1, we can ensure
// the filter is provided.
// A function above (isBitActive) provides an example of testing of testing
// the filter parsing.
search := ctx.PostForm("search") // string, search query for titles
meal := toBits(ctx.PostFormArray("meal"))
time := toBits(ctx.PostFormArray("time"))
difficulty := toBits(ctx.PostFormArray("difficulty"))
serving := toBits(ctx.PostFormArray("serving"))
filters := domain.SearchFilters{
Search: search,
MealType: meal,
Time: time,
Difficulty: difficulty,
ServingSize: serving,
}
return s.recipeRepository.SearchRecipes(filters)
}

View File

@ -18,12 +18,34 @@ const (
MealBreakfast RecipeMeal = "breakfast"
MealLunch RecipeMeal = "lunch"
MealDinner RecipeMeal = "dinner"
MealDessert RecipeMeal = "dessert"
MealDesert RecipeMeal = "dessert"
MealSnack RecipeMeal = "snack"
MealSide RecipeMeal = "side"
MealOther RecipeMeal = "other"
)
// ParseMeal converts an integer value into a meal type (string). Values are 0-indexed.
func ParseMeal(meal int) RecipeMeal {
switch meal {
case 0:
return MealBreakfast
case 1:
return MealLunch
case 2:
return MealDinner
case 3:
return MealDesert
case 4:
return MealSnack
case 5:
return MealSide
case 6:
return MealOther
default:
return MealOther
}
}
// RecipeIngredient is a single ingredients in a recipe. These have JSON tags which allow them
// to be marshaled into a JSON array and stored in the database (JSONB).
type RecipeIngredient struct {
@ -47,3 +69,14 @@ type Recipe struct {
Modified *time.Time // Pointer to allow null
Created time.Time
}
// SearchFilters is a model which represents the required filters to complete a recipe search.
// The integer values should be provided as bits and used to parse out individual flags. More
// details can be found in the SearchRecipes service function.
type SearchFilters struct {
Search string
MealType int
Time int
Difficulty int
ServingSize int
}

View File

@ -3,4 +3,5 @@ package domain
type RecipeRepository interface {
CreateRecipe(recipe *Recipe) error
GetRecipe(id int) (*Recipe, error)
SearchRecipes(filters SearchFilters) ([]Recipe, error)
}

View File

@ -5,4 +5,5 @@ import "github.com/gin-gonic/gin"
type RecipeService interface {
CreateRecipe(ctx *gin.Context) (*Recipe, error)
GetRecipe(id int) (*Recipe, error)
SearchRecipes(ctx *gin.Context) ([]Recipe, error)
}

View File

@ -19,3 +19,5 @@ const WEB_RECIPE = VERSION + WEB + "/recipe/%d"
const API_AUTH_LOGIN = VERSION + API + "/auth/login"
const API_AUTH_CALLBACK = VERSION + API + "/auth/callback"
const API_AUTH_LOGOUT = VERSION + API + "/auth/logout"
const API_CREATE_RECIPE = VERSION + API + "/recipe"
const API_SEARCH_RECIPES = VERSION + API + "/recipe/search"

View File

@ -4,6 +4,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"strings"
domain "github.com/haydenhargreaves/Potion/internal/domain/recipe"
"github.com/lib/pq"
@ -134,6 +135,8 @@ func (r *RecipeRepository) GetRecipe(id int) (*domain.Recipe, error) {
}
recipe.Duration = duration
} else {
recipe.Duration = domain.RecipeDuration{}
}
// Parse ingredient
@ -144,7 +147,178 @@ func (r *RecipeRepository) GetRecipe(id int) (*domain.Recipe, error) {
}
recipe.Ingredients = ingredients
} else {
recipe.Ingredients = []domain.RecipeIngredient{}
}
return &recipe, nil
}
// isBitActive returns true when the bit at pos (0 indexed) is true.
func isBitActive(bits, pos int) bool {
return (bits>>pos)&1 == 1
}
func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters) ([]domain.Recipe, error) {
tx, err := r.db.Begin()
if err != nil {
tx.Rollback()
return nil, err
}
// Generate the query
query := "SELECT * FROM recipes"
// Compute meals type filters (there are 7 bits)
var mealConditions []string
for i := range 7 {
if isBitActive(filters.MealType, i) {
mealConditions = append(mealConditions, fmt.Sprintf("category = '%s'", domain.ParseMeal(i)))
}
}
// Compute time filters (there are 5 bits)
var timeConditions []string
for i := range 5 {
var cond string
if isBitActive(filters.Time, i) {
switch i {
case 0:
cond = "(duration->>'total')::int < 15"
case 1:
cond = "(duration->>'total')::int BETWEEN 15 AND 30"
case 2:
cond = "(duration->>'total')::int BETWEEN 30 AND 60"
case 3:
cond = "(duration->>'total')::int BETWEEN 60 AND 120"
case 4:
cond = "(duration->>'total')::int > 120"
}
timeConditions = append(timeConditions, cond)
}
}
// Compute difficulty filters (there are 5 bits)
var difficultyConditions []string
for i := range 5 {
if isBitActive(filters.Difficulty, i) {
cond := fmt.Sprintf("difficulty = '%d'", i+1)
difficultyConditions = append(difficultyConditions, cond)
}
}
// Compute serving size filters (there are 5 bits)
var servingConditions []string
for i := range 5 {
var cond string
if isBitActive(filters.ServingSize, i) {
switch i {
case 0:
cond = "serves BETWEEN 1 AND 2"
case 1:
cond = "serves BETWEEN 2 AND 4"
case 2:
cond = "serves BETWEEN 4 AND 6"
case 3:
cond = "serves BETWEEN 6 AND 8"
case 4:
cond = "serves > 8"
}
servingConditions = append(servingConditions, cond)
}
}
// TODO: Title search somehow...
// Merge condition strings
mealString := fmt.Sprintf("(%s)", strings.Join(mealConditions, " OR "))
timeString := fmt.Sprintf("(%s)", strings.Join(timeConditions, " OR "))
difficultyString := fmt.Sprintf("(%s)", strings.Join(difficultyConditions, " OR "))
servingString := fmt.Sprintf("(%s)", strings.Join(servingConditions, " OR "))
// Combine condition strings
var conditions []string
if len(mealConditions) > 0 {
conditions = append(conditions, mealString)
}
if len(timeConditions) > 0 {
conditions = append(conditions, timeString)
}
if len(difficultyConditions) > 0 {
conditions = append(conditions, difficultyString)
}
if len(servingConditions) > 0 {
conditions = append(conditions, servingString)
}
// Convert and append conditions
if len(conditions) > 0 {
conditionsString := fmt.Sprintf("WHERE %s", strings.Join(conditions, " AND "))
query = fmt.Sprintf("%s %s;", query, conditionsString)
} else {
query += ";"
}
// Execute the query
rows, err := tx.Query(query)
if err != nil {
return nil, fmt.Errorf("failed to query recipes: %w", err)
}
defer rows.Close()
var recipes []domain.Recipe
for rows.Next() {
// Parsed values location
var recipe domain.Recipe
var durationBytes []byte
var ingredientBytes []byte
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 recipe row: %w", err)
}
// Parse duration from bytes
if len(durationBytes) > 0 {
var duration domain.RecipeDuration
if err := json.Unmarshal(durationBytes, &duration); err != nil {
return nil, fmt.Errorf("failed to parse duration for recipe ID %d: %w", recipe.Id, err)
}
recipe.Duration = duration
} else {
recipe.Duration = domain.RecipeDuration{}
}
// Parse ingredients from bytes
if len(ingredientBytes) > 0 {
var ingredients []domain.RecipeIngredient
if err := json.Unmarshal(ingredientBytes, &ingredients); err != nil {
return nil, fmt.Errorf("failed to parse ingredients for recipe ID %d: %w", recipe.Id, err)
}
recipe.Ingredients = ingredients
} else {
recipe.Ingredients = []domain.RecipeIngredient{}
}
recipes = append(recipes, recipe)
}
if err := tx.Commit(); err != nil {
tx.Rollback()
return nil, err
}
return recipes, nil
}

View File

@ -1,12 +1,15 @@
package components
templ dropdownButton(name string) {
<button
class="px-2 py-1 border border-gray-300 rounded-lg focus:outline-2
focus:outline-blue-500 focus:bg-blue-200"
>
{ name }
</button>
templ dropdownButton(content, name, value string) {
<label class="inline-block cursor-pointer">
<input type="checkbox" name={ name } value={ value } class="sr-only peer"/>
<span
class="peer-checked:bg-blue-600 peer-checked:text-white peer-checked:border-blue-600
px-2 py-1 border border-gray-300 rounded-lg"
>
{ content }
</span>
</label>
}
templ FilterDropdown() {
@ -30,13 +33,13 @@ templ FilterDropdown() {
Meal
</h3>
<div class="flex text-xs flex-wrap gap-1">
@dropdownButton("Breakfast")
@dropdownButton("Lunch")
@dropdownButton("Dinner")
@dropdownButton("Desert")
@dropdownButton("Snack")
@dropdownButton("Side")
@dropdownButton("Other")
@dropdownButton("Breakfast", "meal", "1")
@dropdownButton("Lunch", "meal", "2")
@dropdownButton("Dinner", "meal", "4")
@dropdownButton("Desert", "meal", "8")
@dropdownButton("Snack", "meal", "16")
@dropdownButton("Side", "meal", "32")
@dropdownButton("Other", "meal", "64")
</div>
</div>
<div class="w-full border-b border-gray-300 py-2">
@ -44,11 +47,11 @@ templ FilterDropdown() {
Cook Time
</h3>
<div class="flex text-xs flex-wrap gap-1">
@dropdownButton("< 15 min")
@dropdownButton("15 to 30 min")
@dropdownButton("30 to 60 min")
@dropdownButton("60 to 120 min")
@dropdownButton("+120 min")
@dropdownButton("< 15 min", "time", "1")
@dropdownButton("15 to 30 min", "time", "2")
@dropdownButton("30 to 60 min", "time", "4")
@dropdownButton("60 to 120 min", "time", "8")
@dropdownButton("+120 min", "time", "16")
</div>
</div>
<div class="w-full border-b border-gray-300 py-2">
@ -56,11 +59,11 @@ templ FilterDropdown() {
Difficulty
</h3>
<div class="flex text-xs flex-wrap gap-1">
@dropdownButton("Beginner")
@dropdownButton("Easy")
@dropdownButton("Intermediate")
@dropdownButton("Challegening")
@dropdownButton("Extreme")
@dropdownButton("Beginner", "difficulty", "1")
@dropdownButton("Easy", "difficulty", "2")
@dropdownButton("Intermediate", "difficulty", "4")
@dropdownButton("Challenging", "difficulty", "8")
@dropdownButton("Extreme", "difficulty", "16")
</div>
</div>
<div class="w-full border-b border-gray-300 py-2">
@ -68,11 +71,11 @@ templ FilterDropdown() {
Serving Size
</h3>
<div class="flex text-xs flex-wrap gap-1">
@dropdownButton("1 to 2")
@dropdownButton("1 to 4")
@dropdownButton("4 to 6")
@dropdownButton("6 to 8")
@dropdownButton("8+")
@dropdownButton("1 to 2", "serving", "1")
@dropdownButton("2 to 4", "serving", "2")
@dropdownButton("4 to 6", "serving", "4")
@dropdownButton("6 to 8", "serving", "8")
@dropdownButton("8+", "serving", "16")
</div>
</div>
</div>

View File

@ -8,7 +8,7 @@ package components
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func dropdownButton(name string) templ.Component {
func dropdownButton(content, name, value string) 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 {
@ -29,20 +29,46 @@ func dropdownButton(name string) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<button class=\"px-2 py-1 border border-gray-300 rounded-lg focus:outline-2\n focus:outline-blue-500 focus:bg-blue-200\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<label class=\"inline-block cursor-pointer\"><input type=\"checkbox\" name=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/dropdowns.templ`, Line: 8, Col: 8}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/dropdowns.templ`, Line: 5, Col: 36}
}
_, 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, "</button>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/dropdowns.templ`, Line: 5, Col: 52}
}
_, 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, "\" class=\"sr-only peer\"> <span class=\"peer-checked:bg-blue-600 peer-checked:text-white peer-checked:border-blue-600\n px-2 py-1 border border-gray-300 rounded-lg\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(content)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/dropdowns.templ`, Line: 10, Col: 12}
}
_, 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, "</span></label>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -66,116 +92,116 @@ func FilterDropdown() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
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, 3, "<script>\n function toggleDropdown() {\n const menu = document.getElementById(\"filter-dropdown-menu\");\n const button = document.getElementById(\"filter-dropdown-button\");\n\n if (menu.classList.contains(\"block\")) {\n menu.classList.remove(\"block\");\n menu.classList.add(\"hidden\");\n } else {\n menu.classList.remove(\"hidden\");\n menu.classList.add(\"block\");\n }\n }\n</script><div id=\"filter-dropdown-menu\" class=\"hidden w-full p-2 border border-gray-300 my-2 rounded-lg\"><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-1\">Meal</h3><div class=\"flex text-xs flex-wrap gap-1\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<script>\n function toggleDropdown() {\n const menu = document.getElementById(\"filter-dropdown-menu\");\n const button = document.getElementById(\"filter-dropdown-button\");\n\n if (menu.classList.contains(\"block\")) {\n menu.classList.remove(\"block\");\n menu.classList.add(\"hidden\");\n } else {\n menu.classList.remove(\"hidden\");\n menu.classList.add(\"block\");\n }\n }\n</script><div id=\"filter-dropdown-menu\" class=\"hidden w-full p-2 border border-gray-300 my-2 rounded-lg\"><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-1\">Meal</h3><div class=\"flex text-xs flex-wrap gap-1\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Breakfast").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Breakfast", "meal", "1").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Lunch").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Lunch", "meal", "2").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Dinner").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Dinner", "meal", "4").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Desert").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Desert", "meal", "8").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Snack").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Snack", "meal", "16").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Side").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Side", "meal", "32").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Other").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Other", "meal", "64").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div></div><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-1\">Cook Time</h3><div class=\"flex text-xs flex-wrap gap-1\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div></div><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-1\">Cook Time</h3><div class=\"flex text-xs flex-wrap gap-1\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("< 15 min").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("< 15 min", "time", "1").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("15 to 30 min").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("15 to 30 min", "time", "2").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("30 to 60 min").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("30 to 60 min", "time", "4").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("60 to 120 min").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("60 to 120 min", "time", "8").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("+120 min").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("+120 min", "time", "16").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div></div><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-1\">Difficulty</h3><div class=\"flex text-xs flex-wrap gap-1\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div></div><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-1\">Difficulty</h3><div class=\"flex text-xs flex-wrap gap-1\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Beginner").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Beginner", "difficulty", "1").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Easy").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Easy", "difficulty", "2").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Intermediate").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Intermediate", "difficulty", "4").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Challegening").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Challenging", "difficulty", "8").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("Extreme").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("Extreme", "difficulty", "16").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div></div><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-1\">Serving Size</h3><div class=\"flex text-xs flex-wrap gap-1\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div></div><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-1\">Serving Size</h3><div class=\"flex text-xs flex-wrap gap-1\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("1 to 2").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("1 to 2", "serving", "1").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("1 to 4").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("2 to 4", "serving", "2").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("4 to 6").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("4 to 6", "serving", "4").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("6 to 8").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("6 to 8", "serving", "8").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dropdownButton("8+").Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dropdownButton("8+", "serving", "16").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div></div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@ -1,6 +1,7 @@
package templates
import "github.com/haydenhargreaves/Potion/internal/templates/components"
import "github.com/haydenhargreaves/Potion/internal/domain/server"
templ CreatePage() {
@components.Navbar("create")
@ -24,7 +25,7 @@ templ Page() {
share your masterpiece!
</p>
<form
hx-post="/v1/api/recipe"
hx-post={domain.API_CREATE_RECIPE}
hx-swap="outerHTML"
hx-target="#response"
hx-trigger="submit"

File diff suppressed because one or more lines are too long

View File

@ -26,15 +26,18 @@ templ introSection() {
}
templ searchBar() {
<p id="result"></p>
<div class="flex w-full gap-x-2">
<div class="relative w-full max-w-xl">
<input
type="search"
name="search"
placeholder="Search recipes, ingredients..."
class="w-full pr-4 pl-10 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button type="submit" class="absolute left-3 top-1/2 -translate-y-1/2">
<svg
class="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400"
class="h-5 w-5 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@ -47,6 +50,7 @@ templ searchBar() {
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
></path>
</svg>
</button>
</div>
@filterButton()
</div>
@ -54,6 +58,7 @@ templ searchBar() {
templ filterButton() {
<button
type="button"
id="filter-dropdown-button"
onclick="toggleDropdown()"
class="text-gray-400 border border-gray-300 rounded-lg p-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
@ -84,10 +89,17 @@ templ filterButton() {
templ searchSection() {
<section class="w-full flex flex-col items-center justify-center my-8 py-4">
@components.BannerText("Craving Something Specific?")
<div class="w-full md:w-2/3 px-4 my-8">
<form
hx-post={domain.API_SEARCH_RECIPES}
hx-swap="innerHTML"
hx-target="#result"
hx-trigger="submit"
hx-encoding="multipart/form-data"
class="w-full md:w-2/3 px-4 my-8"
>
@searchBar()
@components.FilterDropdown()
</div>
</form>
</section>
}

View File

@ -61,7 +61,7 @@ func searchBar() templ.Component {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex w-full gap-x-2\"><div class=\"relative w-full max-w-xl\"><input type=\"search\" placeholder=\"Search recipes, ingredients...\" class=\"w-full pr-4 pl-10 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500\"> <svg class=\"absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"></path></svg></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<p id=\"result\"></p><div class=\"flex w-full gap-x-2\"><div class=\"relative w-full max-w-xl\"><input type=\"search\" name=\"search\" placeholder=\"Search recipes, ingredients...\" class=\"w-full pr-4 pl-10 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500\"> <button type=\"submit\" class=\"absolute left-3 top-1/2 -translate-y-1/2\"><svg class=\"h-5 w-5 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"></path></svg></button></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -98,7 +98,7 @@ func filterButton() templ.Component {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<button id=\"filter-dropdown-button\" onclick=\"toggleDropdown()\" class=\"text-gray-400 border border-gray-300 rounded-lg p-2 focus:outline-none focus:ring-2 focus:ring-blue-500\"><svg class=\"h-6\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M6 11.1707L6 4C6 3.44771 5.55228 3 5 3C4.44771 3 4 3.44771 4 4L4 11.1707C2.83481 11.5825 2 12.6938 2 14C2 15.3062 2.83481 16.4175 4 16.8293L4 20C4 20.5523 4.44772 21 5 21C5.55228 21 6 20.5523 6 20L6 16.8293C7.16519 16.4175 8 15.3062 8 14C8 12.6938 7.16519 11.5825 6 11.1707ZM5 13C4.44772 13 4 13.4477 4 14C4 14.5523 4.44772 15 5 15C5.55228 15 6 14.5523 6 14C6 13.4477 5.55228 13 5 13Z\" fill=\"currentColor\"></path> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M19 21C18.4477 21 18 20.5523 18 20L18 18C18 17.9435 18.0047 17.8881 18.0137 17.8341C16.8414 17.4262 16 16.3113 16 15C16 13.6887 16.8414 12.5738 18.0137 12.1659C18.0047 12.1119 18 12.0565 18 12L18 4C18 3.44771 18.4477 3 19 3C19.5523 3 20 3.44771 20 4L20 12C20 12.0565 19.9953 12.1119 19.9863 12.1659C21.1586 12.5738 22 13.6887 22 15C22 16.3113 21.1586 17.4262 19.9863 17.8341C19.9953 17.8881 20 17.9435 20 18V20C20 20.5523 19.5523 21 19 21ZM18 15C18 14.4477 18.4477 14 19 14C19.5523 14 20 14.4477 20 15C20 15.5523 19.5523 16 19 16C18.4477 16 18 15.5523 18 15Z\" fill=\"currentColor\"></path> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M9 9C9 7.69378 9.83481 6.58254 11 6.17071V4C11 3.44772 11.4477 3 12 3C12.5523 3 13 3.44772 13 4V6.17071C14.1652 6.58254 15 7.69378 15 9C15 10.3113 14.1586 11.4262 12.9863 11.8341C12.9953 11.8881 13 11.9435 13 12L13 20C13 20.5523 12.5523 21 12 21C11.4477 21 11 20.5523 11 20L11 12C11 11.9435 11.0047 11.8881 11.0137 11.8341C9.84135 11.4262 9 10.3113 9 9ZM11 9C11 8.44772 11.4477 8 12 8C12.5523 8 13 8.44772 13 9C13 9.55229 12.5523 10 12 10C11.4477 10 11 9.55229 11 9Z\" fill=\"currentColor\"></path></svg></button>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<button type=\"button\" id=\"filter-dropdown-button\" onclick=\"toggleDropdown()\" class=\"text-gray-400 border border-gray-300 rounded-lg p-2 focus:outline-none focus:ring-2 focus:ring-blue-500\"><svg class=\"h-6\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M6 11.1707L6 4C6 3.44771 5.55228 3 5 3C4.44771 3 4 3.44771 4 4L4 11.1707C2.83481 11.5825 2 12.6938 2 14C2 15.3062 2.83481 16.4175 4 16.8293L4 20C4 20.5523 4.44772 21 5 21C5.55228 21 6 20.5523 6 20L6 16.8293C7.16519 16.4175 8 15.3062 8 14C8 12.6938 7.16519 11.5825 6 11.1707ZM5 13C4.44772 13 4 13.4477 4 14C4 14.5523 4.44772 15 5 15C5.55228 15 6 14.5523 6 14C6 13.4477 5.55228 13 5 13Z\" fill=\"currentColor\"></path> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M19 21C18.4477 21 18 20.5523 18 20L18 18C18 17.9435 18.0047 17.8881 18.0137 17.8341C16.8414 17.4262 16 16.3113 16 15C16 13.6887 16.8414 12.5738 18.0137 12.1659C18.0047 12.1119 18 12.0565 18 12L18 4C18 3.44771 18.4477 3 19 3C19.5523 3 20 3.44771 20 4L20 12C20 12.0565 19.9953 12.1119 19.9863 12.1659C21.1586 12.5738 22 13.6887 22 15C22 16.3113 21.1586 17.4262 19.9863 17.8341C19.9953 17.8881 20 17.9435 20 18V20C20 20.5523 19.5523 21 19 21ZM18 15C18 14.4477 18.4477 14 19 14C19.5523 14 20 14.4477 20 15C20 15.5523 19.5523 16 19 16C18.4477 16 18 15.5523 18 15Z\" fill=\"currentColor\"></path> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M9 9C9 7.69378 9.83481 6.58254 11 6.17071V4C11 3.44772 11.4477 3 12 3C12.5523 3 13 3.44772 13 4V6.17071C14.1652 6.58254 15 7.69378 15 9C15 10.3113 14.1586 11.4262 12.9863 11.8341C12.9953 11.8881 13 11.9435 13 12L13 20C13 20.5523 12.5523 21 12 21C11.4477 21 11 20.5523 11 20L11 12C11 11.9435 11.0047 11.8881 11.0137 11.8341C9.84135 11.4262 9 10.3113 9 9ZM11 9C11 8.44772 11.4477 8 12 8C12.5523 8 13 8.44772 13 9C13 9.55229 12.5523 10 12 10C11.4477 10 11 9.55229 11 9Z\" fill=\"currentColor\"></path></svg></button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -135,7 +135,20 @@ func searchSection() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"w-full md:w-2/3 px-4 my-8\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<form hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(domain.API_SEARCH_RECIPES)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/home.templ`, Line: 93, Col: 40}
}
_, 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, 7, "\" hx-swap=\"innerHTML\" hx-target=\"#result\" hx-trigger=\"submit\" hx-encoding=\"multipart/form-data\" class=\"w-full md:w-2/3 px-4 my-8\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -147,7 +160,7 @@ func searchSection() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div></section>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</form></section>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -171,12 +184,12 @@ func highlightSection(liked bool) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
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, 8, "<section class=\"w-full flex flex-col items-center justify-center my-8 py-4\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<section class=\"w-full flex flex-col items-center justify-center my-8 py-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -184,7 +197,7 @@ func highlightSection(liked bool) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<p class=\"leading-relaxed p-4 my-8\">Our 'Recipe of the Week' is the cream of the crop! We handpick it by looking at what recipes our community loves most. This isn't just about how many people view a recipe; it's also about how many times it's been made, liked, reviewed, and its average rating, all combined to find the true fan favorite of the week. It's our way of highlighting the best recipes that truly resonate with our users!</p><div class=\"flex items-center justify-center w-full\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<p class=\"leading-relaxed p-4 my-8\">Our 'Recipe of the Week' is the cream of the crop! We handpick it by looking at what recipes our community loves most. This isn't just about how many people view a recipe; it's also about how many times it's been made, liked, reviewed, and its average rating, all combined to find the true fan favorite of the week. It's our way of highlighting the best recipes that truly resonate with our users!</p><div class=\"flex items-center justify-center w-full\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -192,7 +205,7 @@ func highlightSection(liked bool) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div></section>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</div></section>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -216,12 +229,12 @@ func listsSection() 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, 11, "<section class=\"w-full flex flex-col items-center justify-center my-8 py-4\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<section class=\"w-full flex flex-col items-center justify-center my-8 py-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -229,7 +242,7 @@ func listsSection() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"w-full\"><h3 class=\"text-lg mt-8 mx-4\">Recently viewed</h3><div class=\"flex overflow-x-auto gap-x-4 mx-4 my-4\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<div class=\"w-full\"><h3 class=\"text-lg mt-8 mx-4\">Recently viewed</h3><div class=\"flex overflow-x-auto gap-x-4 mx-4 my-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -257,7 +270,7 @@ func listsSection() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div><h3 class=\"text-lg mt-8 mx-4\">Make again</h3><div class=\"flex overflow-x-auto gap-x-4 mx-4 my-4\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div><h3 class=\"text-lg mt-8 mx-4\">Make again</h3><div class=\"flex overflow-x-auto gap-x-4 mx-4 my-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -285,7 +298,7 @@ func listsSection() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div></div></section>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div></div></section>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -309,21 +322,21 @@ func ctaSection() 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, 15, "<section class=\"w-full flex flex-col items-center justify-center mt-16 py-8 md:py-12 bg-gradient-to-br from-blue-100 to-purple-100 text-center\"><h2 class=\"text-2xl md:text-3xl font-extrabold text-gray-800 mb-6 px-4\">Unleash Your Inner Chef!</h2><p class=\"text-md md:text-lg text-gray-700 max-w-2xl mb-10 px-4 leading-relaxed\">Have a unique recipe idea? Want to share your culinary masterpiece with the world? It's time to bring your creations to life!</p><a href=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<section class=\"w-full flex flex-col items-center justify-center mt-16 py-8 md:py-12 bg-gradient-to-br from-blue-100 to-purple-100 text-center\"><h2 class=\"text-2xl md:text-3xl font-extrabold text-gray-800 mb-6 px-4\">Unleash Your Inner Chef!</h2><p class=\"text-md md:text-lg text-gray-700 max-w-2xl mb-10 px-4 leading-relaxed\">Have a unique recipe idea? Want to share your culinary masterpiece with the world? It's time to bring your creations to life!</p><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 templ.SafeURL = domain.WEB_CREATE
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var8)))
var templ_7745c5c3_Var9 templ.SafeURL = domain.WEB_CREATE
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var9)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" class=\"flex items-center justify-center\n bg-gradient-to-r from-blue-400 to-blue-600 text-white\n px-12 py-5 rounded-full shadow-sm hover:shadow-md\n transition-all duration-300 ease-in-out shadow-blue-700\n text-lg md:text-2xl font-bold uppercase tracking-wide\">Create Your Recipe!</a></section>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" class=\"flex items-center justify-center\n bg-gradient-to-r from-blue-400 to-blue-600 text-white\n px-12 py-5 rounded-full shadow-sm hover:shadow-md\n transition-all duration-300 ease-in-out shadow-blue-700\n text-lg md:text-2xl font-bold uppercase tracking-wide\">Create Your Recipe!</a></section>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -347,16 +360,16 @@ func HomePage() 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 = components.Navbar("home").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"w-full h-fit 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\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<div class=\"w-full h-fit 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\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -380,7 +393,7 @@ func HomePage() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@ -212,6 +212,17 @@
}
}
@layer utilities {
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.absolute {
position: absolute;
}
@ -221,6 +232,9 @@
.static {
position: static;
}
.top-1 {
top: calc(var(--spacing) * 1);
}
.top-1\/2 {
top: calc(1/2 * 100%);
}
@ -230,6 +244,9 @@
.left-0 {
left: calc(var(--spacing) * 0);
}
.left-1 {
left: calc(var(--spacing) * 1);
}
.left-1\/2 {
left: calc(1/2 * 100%);
}
@ -323,6 +340,9 @@
.hidden {
display: none;
}
.inline-block {
display: inline-block;
}
.inline-flex {
display: inline-flex;
}
@ -375,12 +395,18 @@
.h-screen {
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%);
}
@ -393,6 +419,9 @@
.w-5 {
width: calc(var(--spacing) * 5);
}
.w-9 {
width: calc(var(--spacing) * 9);
}
.w-9\/10 {
width: calc(9/10 * 100%);
}
@ -414,16 +443,30 @@
.max-w-xl {
max-width: var(--container-xl);
}
.flex-shrink {
flex-shrink: 1;
}
.flex-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);
@ -431,6 +474,9 @@
.cursor-pointer {
cursor: pointer;
}
.resize {
resize: both;
}
.resize-none {
resize: none;
}
@ -766,6 +812,9 @@
.uppercase {
text-transform: uppercase;
}
.underline {
text-decoration-line: underline;
}
.shadow {
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@ -844,6 +893,21 @@
.\[-webkit-line-clamp\:4\] {
-webkit-line-clamp: 4;
}
.peer-checked\:border-blue-600 {
&:is(:where(.peer):checked ~ *) {
border-color: var(--color-blue-600);
}
}
.peer-checked\:bg-blue-600 {
&:is(:where(.peer):checked ~ *) {
background-color: var(--color-blue-600);
}
}
.peer-checked\:text-white {
&:is(:where(.peer):checked ~ *) {
color: var(--color-white);
}
}
.peer-invalid\:block {
&:is(:where(.peer):invalid ~ *) {
display: block;
@ -920,6 +984,13 @@
}
}
}
.hover\:border-gray-400 {
&:hover {
@media (hover: hover) {
border-color: var(--color-gray-400);
}
}
}
.hover\:bg-blue-200 {
&:hover {
@media (hover: hover) {
@ -978,6 +1049,15 @@
}
}
}
.checked\:hover\:bg-blue-700 {
&:checked {
&:hover {
@media (hover: hover) {
background-color: var(--color-blue-700);
}
}
}
}
.focus\:bg-blue-200 {
&:focus {
background-color: var(--color-blue-200);
@ -999,6 +1079,12 @@
--tw-ring-color: var(--color-blue-500);
}
}
.focus\:ring-offset-2 {
&:focus {
--tw-ring-offset-width: 2px;
--tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
}
}
.focus\:outline-hidden {
&:focus {
--tw-outline-style: none;