Merge pull request 'Search page work: Still not complete.' (#11) from feature/search into master
Reviewed-on: #11
This commit is contained in:
commit
1b41478143
@ -78,19 +78,19 @@ well as view lists of recently made recipes, recently viewed and trending recipe
|
||||
|
||||
##### API Requirements
|
||||
|
||||
- [ ] Message & Pills Banner
|
||||
- [ ] Search all recipes of a specific meal type
|
||||
- [ ] ~Message & Pills Banner~
|
||||
- [ ] ~Search all recipes of a specific meal type~
|
||||
|
||||
- [ ] Search bar
|
||||
- [ ] Text search on titles based on search query
|
||||
- [ ] Text search on tags based on search query
|
||||
- [ ] Text search on **meal** based on search query
|
||||
- [x] Search bar
|
||||
- [x] Text search on titles based on search query
|
||||
- [x] Text search on tags based on search query
|
||||
- [x] Text search on **meal** based on search query
|
||||
|
||||
- [ ] Filter drop down
|
||||
- [ ] Update search to only contain meals from selected filter
|
||||
- [ ] Update search to only contain means that meet the time requirement of the selected filter
|
||||
- [ ] Update search to only contain meals that meet the difficulty level of the selected filter
|
||||
- [ ] Update search to only contain meals that meet the serving size of the selected filter
|
||||
- [x] Filter drop down
|
||||
- [x] Update search to only contain meals from selected filter
|
||||
- [x] Update search to only contain means that meet the time requirement of the selected filter
|
||||
- [x] Update search to only contain meals that meet the difficulty level of the selected filter
|
||||
- [x] Update search to only contain meals that meet the serving size of the selected filter
|
||||
|
||||
- [ ] Recipe of The Week
|
||||
- [ ] Fetch the most performing recipe of the last 7 days and all of the required meta data
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ func GoogleCallback(ctx *gin.Context) {
|
||||
jwt,
|
||||
int(time.Now().Add(7*24*time.Hour).Sub(time.Now()).Seconds()),
|
||||
"/",
|
||||
"localhost",
|
||||
"", // TODO: Real live domain
|
||||
false, // TODO: True in prod
|
||||
true,
|
||||
)
|
||||
@ -60,6 +60,6 @@ func GoogleCallback(ctx *gin.Context) {
|
||||
// This route will direct the user back to the home page.
|
||||
func Logout(ctx *gin.Context) {
|
||||
// TODO: Use same values as the GoogleCallback function
|
||||
ctx.SetCookie("jwt_token", "", -1, "/", "localhost", false, true)
|
||||
ctx.SetCookie("jwt_token", "", -1, "/", "", false, true) // TODO: Update settings
|
||||
ctx.Redirect(http.StatusSeeOther, domain.WEB_HOME)
|
||||
}
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/gin-gonic/gin"
|
||||
domain "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
layouts "github.com/haydenhargreaves/Potion/internal/templates/layouts"
|
||||
pages "github.com/haydenhargreaves/Potion/internal/templates/pages"
|
||||
)
|
||||
@ -15,64 +18,64 @@ func LoginPage(ctx *gin.Context) {
|
||||
title := "Potion - Login"
|
||||
page := pages.LoginPage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func HomePage(ctx *gin.Context) {
|
||||
title := "Potion - Home"
|
||||
page := pages.HomePage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func FavoritesPage(ctx *gin.Context) {
|
||||
title := "Potion - Favorites"
|
||||
page := pages.FavoritesPage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func CreatePage(ctx *gin.Context) {
|
||||
// If not logged in, direct to the login page
|
||||
if !domain.IsLoggedIn(ctx) {
|
||||
ctx.Redirect(http.StatusSeeOther, domain.WEB_LOGIN)
|
||||
if !domainServer.IsLoggedIn(ctx) {
|
||||
ctx.Redirect(http.StatusSeeOther, domainServer.WEB_LOGIN)
|
||||
return
|
||||
}
|
||||
|
||||
title := "Potion - Create"
|
||||
page := pages.CreatePage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func ProfilePage(ctx *gin.Context) {
|
||||
// If not logged in, direct to the login page
|
||||
if !domain.IsLoggedIn(ctx) {
|
||||
ctx.Redirect(http.StatusSeeOther, domain.WEB_LOGIN)
|
||||
if !domainServer.IsLoggedIn(ctx) {
|
||||
ctx.Redirect(http.StatusSeeOther, domainServer.WEB_LOGIN)
|
||||
return
|
||||
}
|
||||
|
||||
// Else, get the user data
|
||||
deps := ctx.MustGet("deps").(*domain.InjectedDependencies)
|
||||
deps := ctx.MustGet("deps").(*domainServer.InjectedDependencies)
|
||||
user := deps.UserService.GetAuthenicatedUser(ctx)
|
||||
|
||||
title := "Potion - Profile"
|
||||
page := pages.ProfilePage(user)
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func ListPage(ctx *gin.Context) {
|
||||
title := "Potion - Shopping List"
|
||||
page := pages.ListPage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
// TODO: Figure out how to handle errors, think we just need a simple display.
|
||||
func RecipePage(ctx *gin.Context) {
|
||||
// Call recipe service to get via ID
|
||||
deps := ctx.MustGet("deps").(*domain.InjectedDependencies)
|
||||
deps := ctx.MustGet("deps").(*domainServer.InjectedDependencies)
|
||||
id := ctx.Param("id")
|
||||
|
||||
// Parse ID
|
||||
@ -102,5 +105,26 @@ func RecipePage(ctx *gin.Context) {
|
||||
title := "Potion - View Recipe"
|
||||
page := pages.RecipePage(*recipe, *user)
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func SearchPage(ctx *gin.Context) {
|
||||
var page templ.Component
|
||||
// Get filters from cookies
|
||||
if bytes, err := ctx.Cookie("search-filters"); err != nil {
|
||||
fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error())
|
||||
page = pages.SearchPage(domainRecipe.SearchFilters{}, false)
|
||||
} else {
|
||||
var filters domainRecipe.SearchFilters
|
||||
if err := json.Unmarshal([]byte(bytes), &filters); err != nil {
|
||||
fmt.Printf("ERROR: Failed to unmarshal search-filter cookie. %s\n", err.Error())
|
||||
page = pages.SearchPage(domainRecipe.SearchFilters{}, false)
|
||||
} else {
|
||||
page = pages.SearchPage(filters, true)
|
||||
}
|
||||
}
|
||||
|
||||
title := "Potion - Recipe Search"
|
||||
|
||||
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
domain "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
templates "github.com/haydenhargreaves/Potion/internal/templates/pages"
|
||||
)
|
||||
|
||||
const CREATE_SUCCESS_HTML = `
|
||||
<p id="response" class="text-sm text-green-600 px-4 py-1 bg-green-100 rounded-full w-fit">
|
||||
Success! Your new masterpiece was created!
|
||||
</p>
|
||||
`
|
||||
|
||||
const CREATE_ERROR_HTML = `
|
||||
<p id="response" class="text-sm text-red-500 px-4 py-1 bg-red-100 rounded-full w-fit">
|
||||
Uh oh! Something went wrong when creating your recipe. Please try again. %s
|
||||
@ -34,3 +33,56 @@ func CreateRecipe(ctx *gin.Context) {
|
||||
ctx.Header("HX-Redirect", url)
|
||||
ctx.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// TODO: I don't love doing all of this here, but it seems to be the only way to get it to work...
|
||||
func SearchRecipes(ctx *gin.Context) {
|
||||
deps := ctx.MustGet("deps").(*domain.InjectedDependencies)
|
||||
|
||||
|
||||
// create filters
|
||||
filters := domainRecipe.SearchFilters{
|
||||
Search: ctx.PostForm("search"), // string, search query for titles
|
||||
MealType: toBits(ctx.PostFormArray("meal")),
|
||||
Time: toBits(ctx.PostFormArray("time")),
|
||||
Difficulty: toBits(ctx.PostFormArray("difficulty")),
|
||||
ServingSize: toBits(ctx.PostFormArray("serving")),
|
||||
}
|
||||
|
||||
// Set the filters into the cookies, so they can be reloaded
|
||||
if bytes, err := json.Marshal(filters); err == nil {
|
||||
ctx.SetCookie(
|
||||
"search-filters",
|
||||
string(bytes),
|
||||
int(time.Now().Add(24 * time.Hour).Sub(time.Now()).Seconds()),
|
||||
"/",
|
||||
"", // TODO: Need an actual domain
|
||||
false, // TODO: True in prod
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
redirect := ctx.PostForm("redirect")
|
||||
if redirect == "true" {
|
||||
ctx.Header("HX-Redirect", domain.WEB_SEARCH)
|
||||
ctx.Status(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
recipes, err := deps.RecipeService.SearchRecipes(filters)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, gin.H{"error": err.Error()})
|
||||
}
|
||||
|
||||
// Render content as the response
|
||||
ctx.Status(200)
|
||||
templates.ResultList(recipes).Render(ctx.Request.Context(), ctx.Writer)
|
||||
}
|
||||
|
||||
@ -6,11 +6,12 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
domain "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
)
|
||||
|
||||
const TAG_HTML = `
|
||||
<li
|
||||
hx-post="/v1/web/state/tags/delete"
|
||||
hx-post="%s"
|
||||
hx-trigger="click"
|
||||
hx-target="#tag-list"
|
||||
hx-swap="innerHTML"
|
||||
@ -41,7 +42,7 @@ func NewTag(ctx *gin.Context) {
|
||||
var cleaned_tags []string
|
||||
for _, tag := range tags {
|
||||
if tag != "" {
|
||||
html += fmt.Sprintf(TAG_HTML, tag, tag)
|
||||
html += fmt.Sprintf(TAG_HTML, domain.STATE_TAGS_DELETE, tag, tag)
|
||||
|
||||
// Ensure that the list provided does not contain blank spaces.
|
||||
// This is another measure to ensure this state is bulletproof.
|
||||
@ -63,7 +64,7 @@ func DeleteTag(ctx *gin.Context) {
|
||||
var new_tags []string
|
||||
for _, tag := range tags {
|
||||
if tag != target && tag != "" {
|
||||
html += fmt.Sprintf(TAG_HTML, tag, tag)
|
||||
html += fmt.Sprintf(TAG_HTML, domain.STATE_TAGS_DELETE ,tag, tag)
|
||||
new_tags = append(new_tags, tag)
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,6 +165,7 @@ func (s *Server) Setup() *Server {
|
||||
router_web.GET("/profile", handlers.ProfilePage)
|
||||
router_web.GET("/list", handlers.ListPage)
|
||||
router_web.GET("/recipe/:id", handlers.RecipePage)
|
||||
router_web.GET("/search", handlers.SearchPage)
|
||||
|
||||
// WEB state endpoints
|
||||
router_state.POST("/tags", handlers.NewTag)
|
||||
@ -176,8 +177,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
|
||||
}
|
||||
|
||||
@ -98,9 +98,11 @@ func (s *AuthService) GoogleAuthSuccess(state, code string) (string, domain.User
|
||||
|
||||
jwt, err := generateJwt(newUser.Id, newUser.Email, s.jwtSecret)
|
||||
return jwt, newUser, googleUserInfo, err
|
||||
|
||||
}
|
||||
|
||||
// generateJwt requires user data and returns a JSON web token which can be stored in the browsers
|
||||
// cookies. This token is used to log a user into the application and allow access to protected
|
||||
// routes.
|
||||
func generateJwt(userId int, email string, jwtSecret []byte) (string, error) {
|
||||
expiration := time.Now().Add(7 * 24 * time.Hour)
|
||||
|
||||
|
||||
@ -129,3 +129,21 @@ func (s *RecipeService) GetRecipe(id int) (*domain.Recipe, error) {
|
||||
|
||||
return recipe, err
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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 `isBitActive` in the recipe repository provides an example of
|
||||
// testing of testing the filter parsing.
|
||||
func (s *RecipeService) SearchRecipes(filters domain.SearchFilters) ([]domain.Recipe, error) {
|
||||
return s.recipeRepository.SearchRecipes(filters)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -3,4 +3,5 @@ package domain
|
||||
type RecipeRepository interface {
|
||||
CreateRecipe(recipe *Recipe) error
|
||||
GetRecipe(id int) (*Recipe, error)
|
||||
SearchRecipes(filters SearchFilters) ([]Recipe, error)
|
||||
}
|
||||
|
||||
@ -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(filters SearchFilters) ([]Recipe, error)
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ package domain
|
||||
const VERSION = "/v1"
|
||||
const WEB = "/web"
|
||||
const API = "/api"
|
||||
const STATE = "/state"
|
||||
|
||||
// Web prefixed routes
|
||||
const WEB_LOGIN = VERSION + WEB + "/login"
|
||||
@ -14,8 +15,15 @@ const WEB_CREATE = VERSION + WEB + "/create"
|
||||
const WEB_PROFIlE = VERSION + WEB + "/profile"
|
||||
const WEB_LIST = VERSION + WEB + "/list"
|
||||
const WEB_RECIPE = VERSION + WEB + "/recipe/%d"
|
||||
const WEB_SEARCH = VERSION + WEB + "/search"
|
||||
|
||||
// API prefixed routes
|
||||
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"
|
||||
|
||||
// State prefixed routes
|
||||
const STATE_TAGS_CREATE = VERSION + WEB + STATE + "/tags"
|
||||
const STATE_TAGS_DELETE = VERSION + WEB + STATE + "/tags/delete"
|
||||
|
||||
@ -8,12 +8,16 @@ import (
|
||||
domainUser "github.com/haydenhargreaves/Potion/internal/domain/user"
|
||||
)
|
||||
|
||||
// InjectedDependencies is a collection of dependencies that are injected into the application. They
|
||||
// are stored in the context and can be accessed by handlers via the context.
|
||||
type InjectedDependencies struct {
|
||||
UserService domainUser.UserService
|
||||
AuthService domainAuth.AuthService
|
||||
RecipeService domainRecipe.RecipeService
|
||||
}
|
||||
|
||||
// JwtClaims is the data stored in the JSON web token. All that is needed is the users ID and their
|
||||
// Google email provided.
|
||||
type JwtClaims struct {
|
||||
UserId int `json:"id"`
|
||||
Email string `json:"email"`
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
-- Author: Hayden Hargreaves (hhargreaves2006@gmail.com)
|
||||
-- Desc: Create a full text index on the recipes table.
|
||||
-- Date: 07/10/2025
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Update recipes table with the search vector column
|
||||
ALTER TABLE Recipes
|
||||
ADD search_vector tsvector GENERATED ALWAYS AS (
|
||||
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
|
||||
setweight(to_tsvector('english', coalesce(description, '')), 'B')
|
||||
) STORED;
|
||||
|
||||
-- Create the search index
|
||||
CREATE INDEX idx_recipe_search_vector ON recipes USING GIN (search_vector);
|
||||
|
||||
COMMIT;
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
domain "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
"github.com/lib/pq"
|
||||
@ -98,7 +99,13 @@ func (r *RecipeRepository) GetRecipe(id int) (*domain.Recipe, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := "SELECT * FROM recipes WHERE id = $1"
|
||||
query := `
|
||||
SELECT
|
||||
id, title, description, instructions, serves, difficulty, duration, category, ingredients,
|
||||
userid, modified, created
|
||||
FROM recipes
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
var durationBytes []byte
|
||||
var ingredientBytes []byte
|
||||
@ -134,6 +141,8 @@ func (r *RecipeRepository) GetRecipe(id int) (*domain.Recipe, error) {
|
||||
}
|
||||
|
||||
recipe.Duration = duration
|
||||
} else {
|
||||
recipe.Duration = domain.RecipeDuration{}
|
||||
}
|
||||
|
||||
// Parse ingredient
|
||||
@ -144,7 +153,224 @@ 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
|
||||
}
|
||||
|
||||
// SearchRecipes will search the recipe table using the provided filters and return an unbound list
|
||||
// of recipes. The filters are fairly complex, they are stored as bit masks. A more details
|
||||
// description can be found in the recipe service implementation. Any errors will be bubbled to the
|
||||
// caller.
|
||||
//
|
||||
// TODO: Pagination is required, to provide infinite scroll.
|
||||
func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters) ([]domain.Recipe, error) {
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Define columns to select. More fields can be added if the full text search is required
|
||||
columns := []string{
|
||||
"id",
|
||||
"title",
|
||||
"description",
|
||||
"instructions",
|
||||
"serves",
|
||||
"difficulty",
|
||||
"duration",
|
||||
"category",
|
||||
"ingredients",
|
||||
"userid",
|
||||
"modified",
|
||||
"created",
|
||||
}
|
||||
|
||||
// Create search vector query
|
||||
var orderBy string = ""
|
||||
if filters.Search != "" {
|
||||
vector_query := strings.ReplaceAll(filters.Search, " ", " | ")
|
||||
|
||||
conditions = append(
|
||||
conditions,
|
||||
fmt.Sprintf("search_vector @@ to_tsquery('english', '%s')", vector_query),
|
||||
)
|
||||
|
||||
template := `
|
||||
ORDER BY
|
||||
ts_rank(search_vector, to_tsquery('english', '%s')) DESC,
|
||||
ts_rank_cd(search_vector, to_tsquery('english', '%s')) DESC
|
||||
`
|
||||
orderBy = fmt.Sprintf(template, vector_query, vector_query)
|
||||
}
|
||||
|
||||
// Generate the query
|
||||
query := fmt.Sprintf("SELECT %s FROM recipes", strings.Join(columns, ","))
|
||||
|
||||
// Convert and append conditions if provided
|
||||
if len(conditions) > 0 {
|
||||
conditionsString := fmt.Sprintf("WHERE %s", strings.Join(conditions, " AND "))
|
||||
query = fmt.Sprintf("%s %s", query, conditionsString)
|
||||
}
|
||||
|
||||
// Append sorting order if exists
|
||||
if len(orderBy) > 0 {
|
||||
query = fmt.Sprintf("%s %s", query, orderBy)
|
||||
}
|
||||
|
||||
// Finish it off with a colon!
|
||||
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
|
||||
}
|
||||
|
||||
@ -1,16 +1,27 @@
|
||||
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>
|
||||
import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
|
||||
// isBitActive returns true when the bit at pos (0 indexed) is true.
|
||||
func isBitActive(bits, pos int) bool {
|
||||
return (bits>>pos)&1 == 1
|
||||
}
|
||||
|
||||
templ FilterDropdown() {
|
||||
<script>
|
||||
templ dropdownButton(content, name, value string, selected bool) {
|
||||
<label class="inline-block cursor-pointer select-none">
|
||||
<input type="checkbox" name={ name } value={ value } class="sr-only peer"
|
||||
if selected {
|
||||
checked
|
||||
} />
|
||||
<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(filters domainRecipe.SearchFilters) {
|
||||
<script>
|
||||
function toggleDropdown() {
|
||||
const menu = document.getElementById("filter-dropdown-menu");
|
||||
const button = document.getElementById("filter-dropdown-button");
|
||||
@ -24,56 +35,62 @@ templ FilterDropdown() {
|
||||
}
|
||||
}
|
||||
</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">
|
||||
@dropdownButton("Breakfast")
|
||||
@dropdownButton("Lunch")
|
||||
@dropdownButton("Dinner")
|
||||
@dropdownButton("Desert")
|
||||
@dropdownButton("Snack")
|
||||
@dropdownButton("Side")
|
||||
@dropdownButton("Other")
|
||||
</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">
|
||||
@dropdownButton("< 15 min")
|
||||
@dropdownButton("15 to 30 min")
|
||||
@dropdownButton("30 to 60 min")
|
||||
@dropdownButton("60 to 120 min")
|
||||
@dropdownButton("+120 min")
|
||||
</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">
|
||||
@dropdownButton("Beginner")
|
||||
@dropdownButton("Easy")
|
||||
@dropdownButton("Intermediate")
|
||||
@dropdownButton("Challegening")
|
||||
@dropdownButton("Extreme")
|
||||
</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">
|
||||
@dropdownButton("1 to 2")
|
||||
@dropdownButton("1 to 4")
|
||||
@dropdownButton("4 to 6")
|
||||
@dropdownButton("6 to 8")
|
||||
@dropdownButton("8+")
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<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-2">
|
||||
Meal
|
||||
</h3>
|
||||
<div class="flex text-xs flex-wrap gap-1 gap-y-3">
|
||||
@dropdownButton("Breakfast", "meal", "1", isBitActive(filters.MealType, 0))
|
||||
@dropdownButton("Lunch", "meal", "2", isBitActive(filters.MealType, 1))
|
||||
@dropdownButton("Dinner", "meal", "4", isBitActive(filters.MealType, 2))
|
||||
@dropdownButton("Desert", "meal", "8", isBitActive(filters.MealType, 3))
|
||||
@dropdownButton("Snack", "meal", "16", isBitActive(filters.MealType, 4))
|
||||
@dropdownButton("Side", "meal", "32", isBitActive(filters.MealType, 5))
|
||||
@dropdownButton("Other", "meal", "64", isBitActive(filters.MealType, 6))
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full border-b border-gray-300 py-2">
|
||||
<h3 class="mb-2">
|
||||
Cook Time
|
||||
</h3>
|
||||
<div class="flex text-xs flex-wrap gap-1 gap-y-3">
|
||||
@dropdownButton("< 15 min", "time" , "1" , isBitActive(filters.Time, 0))
|
||||
@dropdownButton("15 to 30 min", "time", "2" , isBitActive(filters.Time, 1))
|
||||
@dropdownButton("30 to 60 min", "time" , "4" , isBitActive(filters.Time, 2))
|
||||
@dropdownButton("60 to 120 min", "time" , "8" , isBitActive(filters.Time, 3))
|
||||
@dropdownButton("+120 min", "time" , "16" , isBitActive(filters.Time, 4))
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full border-b border-gray-300 py-2">
|
||||
<h3 class="mb-2">
|
||||
Difficulty
|
||||
</h3>
|
||||
<div class="flex text-xs flex-wrap gap-1 gap-y-3">
|
||||
@dropdownButton("Beginner", "difficulty", "1", isBitActive(filters.Difficulty, 0))
|
||||
@dropdownButton("Easy", "difficulty", "2", isBitActive(filters.Difficulty, 1))
|
||||
@dropdownButton("Intermediate", "difficulty", "4", isBitActive(filters.Difficulty, 2))
|
||||
@dropdownButton("Challenging", "difficulty", "8", isBitActive(filters.Difficulty, 3))
|
||||
@dropdownButton("Extreme", "difficulty", "16", isBitActive(filters.Difficulty, 4))
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full border-b border-gray-300 py-2">
|
||||
<h3 class="mb-2">
|
||||
Serving Size
|
||||
</h3>
|
||||
<div class="flex text-xs flex-wrap gap-1 gap-y-3">
|
||||
@dropdownButton("1 to 2", "serving", "1", isBitActive(filters.ServingSize, 0))
|
||||
@dropdownButton("2 to 4", "serving", "2", isBitActive(filters.ServingSize, 1))
|
||||
@dropdownButton("4 to 6", "serving", "4", isBitActive(filters.ServingSize, 2))
|
||||
@dropdownButton("6 to 8", "serving", "8", isBitActive(filters.ServingSize, 3))
|
||||
@dropdownButton("8+", "serving", "16", isBitActive(filters.ServingSize, 4))
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full pt-2 flex justify-end items-end">
|
||||
<button type="submit"
|
||||
class="w-full text-sm md:text-base text-white rounded-lg py-1.5 md:py-2 bg-blue-600 hover:bg-blue-700 duration-300">
|
||||
Apply Filters
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -8,7 +8,14 @@ package components
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
func dropdownButton(name string) templ.Component {
|
||||
import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
|
||||
// isBitActive returns true when the bit at pos (0 indexed) is true.
|
||||
func isBitActive(bits, pos int) bool {
|
||||
return (bits>>pos)&1 == 1
|
||||
}
|
||||
|
||||
func dropdownButton(content, name, value string, selected bool) 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 +36,56 @@ 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 select-none\"><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: 12, 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: 12, 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\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if selected {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " checked")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "> <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: 18, Col: 13}
|
||||
}
|
||||
_, 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, 6, "</span></label>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -50,7 +93,7 @@ func dropdownButton(name string) templ.Component {
|
||||
})
|
||||
}
|
||||
|
||||
func FilterDropdown() templ.Component {
|
||||
func FilterDropdown(filters domainRecipe.SearchFilters) 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 {
|
||||
@ -66,116 +109,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, 7, "<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-2\">Meal</h3><div class=\"flex text-xs flex-wrap gap-1 gap-y-3\">")
|
||||
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", isBitActive(filters.MealType, 0)).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", isBitActive(filters.MealType, 1)).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", isBitActive(filters.MealType, 2)).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", isBitActive(filters.MealType, 3)).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", isBitActive(filters.MealType, 4)).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", isBitActive(filters.MealType, 5)).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", isBitActive(filters.MealType, 6)).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, 8, "</div></div><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-2\">Cook Time</h3><div class=\"flex text-xs flex-wrap gap-1 gap-y-3\">")
|
||||
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", isBitActive(filters.Time, 0)).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", isBitActive(filters.Time, 1)).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", isBitActive(filters.Time, 2)).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", isBitActive(filters.Time, 3)).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", isBitActive(filters.Time, 4)).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, 9, "</div></div><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-2\">Difficulty</h3><div class=\"flex text-xs flex-wrap gap-1 gap-y-3\">")
|
||||
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", isBitActive(filters.Difficulty, 0)).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", isBitActive(filters.Difficulty, 1)).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", isBitActive(filters.Difficulty, 2)).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", isBitActive(filters.Difficulty, 3)).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", isBitActive(filters.Difficulty, 4)).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, 10, "</div></div><div class=\"w-full border-b border-gray-300 py-2\"><h3 class=\"mb-2\">Serving Size</h3><div class=\"flex text-xs flex-wrap gap-1 gap-y-3\">")
|
||||
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", isBitActive(filters.ServingSize, 0)).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", isBitActive(filters.ServingSize, 1)).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", isBitActive(filters.ServingSize, 2)).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", isBitActive(filters.ServingSize, 3)).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", isBitActive(filters.ServingSize, 4)).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, 11, "</div></div><div class=\"w-full pt-2 flex justify-end items-end\"><button type=\"submit\" class=\"w-full text-sm md:text-base text-white rounded-lg py-1.5 md:py-2 bg-blue-600 hover:bg-blue-700 duration-300\">Apply Filters</button></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
80
internal/templates/components/search_bar.templ
Normal file
80
internal/templates/components/search_bar.templ
Normal file
@ -0,0 +1,80 @@
|
||||
package components
|
||||
|
||||
import domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
|
||||
templ SearchBar(filters domainRecipe.SearchFilters, redirect bool, searchOnLoad bool) {
|
||||
<form
|
||||
hx-post={ domainServer.API_SEARCH_RECIPES }
|
||||
hx-swap="innerHTML"
|
||||
hx-target="#result-list"
|
||||
if searchOnLoad {
|
||||
hx-trigger="submit, load"
|
||||
} else {
|
||||
hx-trigger="submit"
|
||||
}
|
||||
hx-encoding="multipart/form-data"
|
||||
class="w-full px-4 my-8"
|
||||
>
|
||||
<div class="flex w-full gap-x-2">
|
||||
<div class="relative w-full">
|
||||
<input type="hidden" name="redirect" value={ redirect }/>
|
||||
<input
|
||||
type="search"
|
||||
name="search"
|
||||
placeholder="Search recipes, ingredients..."
|
||||
value={ filters.Search }
|
||||
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>
|
||||
@filterButton()
|
||||
</div>
|
||||
@FilterDropdown(filters)
|
||||
</form>
|
||||
}
|
||||
|
||||
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"
|
||||
>
|
||||
<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>
|
||||
}
|
||||
142
internal/templates/components/search_bar_templ.go
Normal file
142
internal/templates/components/search_bar_templ.go
Normal file
@ -0,0 +1,142 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
package components
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
|
||||
func SearchBar(filters domainRecipe.SearchFilters, redirect bool, searchOnLoad bool) 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<form hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(domainServer.API_SEARCH_RECIPES)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/search_bar.templ`, Line: 8, Col: 43}
|
||||
}
|
||||
_, 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, "\" hx-swap=\"innerHTML\" hx-target=\"#result-list\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if searchOnLoad {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " hx-trigger=\"submit, load\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " hx-trigger=\"submit\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " hx-encoding=\"multipart/form-data\" class=\"w-full px-4 my-8\"><div class=\"flex w-full gap-x-2\"><div class=\"relative w-full\"><input type=\"hidden\" name=\"redirect\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(redirect)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/search_bar.templ`, Line: 21, Col: 57}
|
||||
}
|
||||
_, 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, 6, "\"> <input type=\"search\" name=\"search\" placeholder=\"Search recipes, ingredients...\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(filters.Search)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/search_bar.templ`, Line: 26, Col: 27}
|
||||
}
|
||||
_, 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, 7, "\" 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
|
||||
}
|
||||
templ_7745c5c3_Err = filterButton().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = FilterDropdown(filters).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func filterButton() 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, 10, "<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
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -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"
|
||||
@ -77,7 +78,7 @@ templ Page() {
|
||||
<input
|
||||
onkeydown="return event.key != 'Enter';"
|
||||
class="border border-gray-300 px-4 py-2 rounded-lg focus:outline-none focus:ring-blue-500 focus:ring-2 duration-200 ease-in-out transition-all shadow-sm"
|
||||
hx-post="/v1/web/state/tags"
|
||||
hx-post={domain.STATE_TAGS_CREATE}
|
||||
maxlength="32"
|
||||
hx-trigger="keyup[keyCode==13]"
|
||||
hx-on::after-request="this.value=''"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -2,6 +2,7 @@ package templates
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
import "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
|
||||
templ introSection() {
|
||||
<section class="w-full h-fit mb-16">
|
||||
@ -25,69 +26,11 @@ templ introSection() {
|
||||
</section>
|
||||
}
|
||||
|
||||
templ searchBar() {
|
||||
<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>
|
||||
@filterButton()
|
||||
</div>
|
||||
}
|
||||
|
||||
templ filterButton() {
|
||||
<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 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">
|
||||
@searchBar()
|
||||
@components.FilterDropdown()
|
||||
</div>
|
||||
@components.SearchBar(domainRecipe.SearchFilters{}, true, false)
|
||||
<div class="hidden" id="result-list"></div>
|
||||
</section>
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
import "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
|
||||
func introSection() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
@ -40,7 +41,7 @@ func introSection() templ.Component {
|
||||
})
|
||||
}
|
||||
|
||||
func searchBar() templ.Component {
|
||||
func searchSection() 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 {
|
||||
@ -61,73 +62,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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = filterButton().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func filterButton() 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_Var3 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var3 == nil {
|
||||
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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func searchSection() 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_Var4 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var4 == nil {
|
||||
templ_7745c5c3_Var4 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<section class=\"w-full flex flex-col items-center justify-center my-8 py-4\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<section class=\"w-full flex flex-col items-center justify-center my-8 py-4\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -135,19 +70,11 @@ 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 = components.SearchBar(domainRecipe.SearchFilters{}, true, false).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = searchBar().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.FilterDropdown().Render(ctx, templ_7745c5c3_Buffer)
|
||||
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, 3, "<div class=\"hidden\" id=\"result-list\"></div></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -171,12 +98,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_Var3 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var3 == nil {
|
||||
templ_7745c5c3_Var3 = 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, 4, "<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 +111,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, 5, "<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 +119,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, 6, "</div></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -216,12 +143,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_Var4 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var4 == nil {
|
||||
templ_7745c5c3_Var4 = 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, 7, "<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 +156,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, 8, "<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 +184,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, 9, "</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 +212,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, 10, "</div></div></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -309,21 +236,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_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, 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, 11, "<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_Var6 templ.SafeURL = domain.WEB_CREATE
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
|
||||
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, 12, "\" 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 +274,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_Var7 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var7 == nil {
|
||||
templ_7745c5c3_Var7 = 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, 13, "<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 +307,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, 14, "</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
130
internal/templates/pages/search.templ
Normal file
130
internal/templates/pages/search.templ
Normal file
@ -0,0 +1,130 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
"github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
)
|
||||
|
||||
templ SearchPage(filters domainRecipe.SearchFilters, searchOnLoad bool) {
|
||||
@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 min-h-screen border-l border-r border-gray-300
|
||||
bg-white flex flex-col items-center"
|
||||
>
|
||||
@components.BannerText("Recipe Search")
|
||||
@components.SearchBar(filters, false, searchOnLoad)
|
||||
<hr class="text-gray-300 w-full"/>
|
||||
@ResultList(nil)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ ResultList(recipes []domain.Recipe) {
|
||||
<div id="result-list" class="flex flex-col w-full p-4 items-center">
|
||||
for i, recipe := range recipes {
|
||||
@searchResult(recipe, i%2 == 1)
|
||||
}
|
||||
if len(recipes) == 0 || recipes == nil {
|
||||
<p class="text-gray-700 text-sm py-4">No results</p>
|
||||
} else {
|
||||
<p class="text-gray-700 text-sm py-4">End of results</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ searchResult(recipe domain.Recipe, odd bool) {
|
||||
<a
|
||||
href={ templ.SafeURL(fmt.Sprintf(domainServer.WEB_RECIPE, recipe.Id)) }
|
||||
if odd {
|
||||
class="w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row bg-[#f8f8f8]"
|
||||
} else {
|
||||
class="w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row"
|
||||
}
|
||||
>
|
||||
<img class="bg-gray-50 size-56 md:size-40 rounded-md border-0" src=""/>
|
||||
<div class="text-gray-700 p-4 flex flex-col items-center md:items-start">
|
||||
<h3 class="text-xl font-semibold text-black pb-1">
|
||||
{ recipe.Title } <span class="text-sm font-normal">{ recipe.Category }</span>
|
||||
</h3>
|
||||
<div class="text-sm flex gap-x-3 gap-y-1 items-center flex-wrap">
|
||||
<span class="flex gap-x-1 align-center">
|
||||
@timeIconSm()
|
||||
{ recipe.Duration.Total } min
|
||||
</span>
|
||||
<span class="flex gap-x-1 align-center">
|
||||
for _ = range(recipe.Difficulty) {
|
||||
@starIconSm(true)
|
||||
}
|
||||
for _ = range(5 - recipe.Difficulty) {
|
||||
@starIconSm(false)
|
||||
}
|
||||
</span>
|
||||
<span class="flex gap-x-1 align-center">
|
||||
@servingIconSm()
|
||||
Serves { recipe.Serves }
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm py-2 text-center md:text-left">{ recipe.Description }</p>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
|
||||
templ servingIconSm() {
|
||||
<svg
|
||||
class="h-5 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 timeIconSm() {
|
||||
<svg class="h-5 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 starIconSm(filled bool) {
|
||||
if filled {
|
||||
<svg class="h-4 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-4 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>
|
||||
}
|
||||
}
|
||||
362
internal/templates/pages/search_templ.go
Normal file
362
internal/templates/pages/search_templ.go
Normal file
@ -0,0 +1,362 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
"github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
)
|
||||
|
||||
func SearchPage(filters domainRecipe.SearchFilters, searchOnLoad bool) 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = components.Navbar("").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"w-full flex justify-center\"><div class=\"mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 min-h-screen border-l border-r border-gray-300 \n bg-white flex flex-col items-center\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.BannerText("Recipe Search").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.SearchBar(filters, false, searchOnLoad).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<hr class=\"text-gray-300 w-full\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = ResultList(nil).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func ResultList(recipes []domain.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_Var2 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var2 == nil {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div id=\"result-list\" class=\"flex flex-col w-full p-4 items-center\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for i, recipe := range recipes {
|
||||
templ_7745c5c3_Err = searchResult(recipe, i%2 == 1).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if len(recipes) == 0 || recipes == nil {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p class=\"text-gray-700 text-sm py-4\">No results</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<p class=\"text-gray-700 text-sm py-4\">End of results</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func searchResult(recipe domain.Recipe, odd bool) 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_Var3 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var3 == nil {
|
||||
templ_7745c5c3_Var3 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 templ.SafeURL = templ.SafeURL(fmt.Sprintf(domainServer.WEB_RECIPE, recipe.Id))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if odd {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " class=\"w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row bg-[#f8f8f8]\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " class=\"w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "><img class=\"bg-gray-50 size-56 md:size-40 rounded-md border-0\" src=\"\"><div class=\"text-gray-700 p-4 flex flex-col items-center md:items-start\"><h3 class=\"text-xl font-semibold text-black pb-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/search.templ`, Line: 51, Col: 18}
|
||||
}
|
||||
_, 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, 13, " <span class=\"text-sm font-normal\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/search.templ`, Line: 51, 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, 14, "</span></h3><div class=\"text-sm flex gap-x-3 gap-y-1 items-center flex-wrap\"><span class=\"flex gap-x-1 align-center\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = timeIconSm().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/search.templ`, Line: 56, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " min</span> <span class=\"flex gap-x-1 align-center\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _ = range recipe.Difficulty {
|
||||
templ_7745c5c3_Err = starIconSm(true).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
for _ = range 5 - recipe.Difficulty {
|
||||
templ_7745c5c3_Err = starIconSm(false).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span> <span class=\"flex gap-x-1 align-center\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = servingIconSm().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "Serves ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Serves)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/search.templ`, Line: 68, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</span></div><p class=\"text-sm py-2 text-center md:text-left\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Description)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/search.templ`, Line: 71, Col: 72}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</p></div></a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func servingIconSm() 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, 20, "<svg class=\"h-5 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\n\t\tS15.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\n\t\tc0-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\n\t\ts1.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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func timeIconSm() 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_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, 21, "<svg class=\"h-5 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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func starIconSm(filled bool) 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_Var12 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var12 == nil {
|
||||
templ_7745c5c3_Var12 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
if filled {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<svg class=\"h-4 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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<svg class=\"h-4 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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -1,10 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -30,9 +30,9 @@
|
||||
--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-black: #000;
|
||||
--color-white: #fff;
|
||||
--spacing: 0.25rem;
|
||||
--container-xl: 36rem;
|
||||
--container-2xl: 42rem;
|
||||
--text-xs: 0.75rem;
|
||||
--text-xs--line-height: calc(1 / 0.75);
|
||||
@ -50,6 +50,7 @@
|
||||
--text-3xl--line-height: calc(2.25 / 1.875);
|
||||
--text-4xl: 2.25rem;
|
||||
--text-4xl--line-height: calc(2.5 / 2.25);
|
||||
--font-weight-normal: 400;
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
@ -57,6 +58,7 @@
|
||||
--tracking-wide: 0.025em;
|
||||
--leading-relaxed: 1.625;
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--radius-xl: 0.75rem;
|
||||
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
@ -212,6 +214,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;
|
||||
}
|
||||
@ -251,6 +264,9 @@
|
||||
.mx-auto {
|
||||
margin-inline: auto;
|
||||
}
|
||||
.my-0 {
|
||||
margin-block: calc(var(--spacing) * 0);
|
||||
}
|
||||
.my-1 {
|
||||
margin-block: calc(var(--spacing) * 1);
|
||||
}
|
||||
@ -323,6 +339,9 @@
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
.inline-flex {
|
||||
display: inline-flex;
|
||||
}
|
||||
@ -341,6 +360,10 @@
|
||||
width: calc(var(--spacing) * 32);
|
||||
height: calc(var(--spacing) * 32);
|
||||
}
|
||||
.size-56 {
|
||||
width: calc(var(--spacing) * 56);
|
||||
height: calc(var(--spacing) * 56);
|
||||
}
|
||||
.size-80 {
|
||||
width: calc(var(--spacing) * 80);
|
||||
height: calc(var(--spacing) * 80);
|
||||
@ -375,6 +398,9 @@
|
||||
.h-screen {
|
||||
height: 100vh;
|
||||
}
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
.w-1\/3 {
|
||||
width: calc(1/3 * 100%);
|
||||
}
|
||||
@ -411,9 +437,6 @@
|
||||
.max-w-2xl {
|
||||
max-width: var(--container-2xl);
|
||||
}
|
||||
.max-w-xl {
|
||||
max-width: var(--container-xl);
|
||||
}
|
||||
.flex-shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@ -461,6 +484,9 @@
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.justify-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
@ -476,12 +502,21 @@
|
||||
.gap-x-2 {
|
||||
column-gap: calc(var(--spacing) * 2);
|
||||
}
|
||||
.gap-x-3 {
|
||||
column-gap: calc(var(--spacing) * 3);
|
||||
}
|
||||
.gap-x-4 {
|
||||
column-gap: calc(var(--spacing) * 4);
|
||||
}
|
||||
.gap-x-8 {
|
||||
column-gap: calc(var(--spacing) * 8);
|
||||
}
|
||||
.gap-y-1 {
|
||||
row-gap: calc(var(--spacing) * 1);
|
||||
}
|
||||
.gap-y-3 {
|
||||
row-gap: calc(var(--spacing) * 3);
|
||||
}
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
@ -494,6 +529,9 @@
|
||||
.rounded-lg {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
.rounded-md {
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
.rounded-sm {
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
@ -504,6 +542,10 @@
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 1px;
|
||||
}
|
||||
.border-0 {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 0px;
|
||||
}
|
||||
.border-2 {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 2px;
|
||||
@ -562,15 +604,18 @@
|
||||
.bg-blue-500 {
|
||||
background-color: var(--color-blue-500);
|
||||
}
|
||||
.bg-blue-600 {
|
||||
background-color: var(--color-blue-600);
|
||||
}
|
||||
.bg-gray-50 {
|
||||
background-color: var(--color-gray-50);
|
||||
}
|
||||
.bg-gray-100 {
|
||||
background-color: var(--color-gray-100);
|
||||
}
|
||||
.bg-gray-200 {
|
||||
background-color: var(--color-gray-200);
|
||||
}
|
||||
.bg-green-100 {
|
||||
background-color: var(--color-green-100);
|
||||
}
|
||||
.bg-red-100 {
|
||||
background-color: var(--color-red-100);
|
||||
}
|
||||
@ -660,9 +705,15 @@
|
||||
.py-8 {
|
||||
padding-block: calc(var(--spacing) * 8);
|
||||
}
|
||||
.pt-2 {
|
||||
padding-top: calc(var(--spacing) * 2);
|
||||
}
|
||||
.pr-4 {
|
||||
padding-right: calc(var(--spacing) * 4);
|
||||
}
|
||||
.pb-1 {
|
||||
padding-bottom: calc(var(--spacing) * 1);
|
||||
}
|
||||
.pl-10 {
|
||||
padding-left: calc(var(--spacing) * 10);
|
||||
}
|
||||
@ -713,6 +764,10 @@
|
||||
--tw-font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.font-normal {
|
||||
--tw-font-weight: var(--font-weight-normal);
|
||||
font-weight: var(--font-weight-normal);
|
||||
}
|
||||
.font-semibold {
|
||||
--tw-font-weight: var(--font-weight-semibold);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
@ -727,6 +782,9 @@
|
||||
.whitespace-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.text-black {
|
||||
color: var(--color-black);
|
||||
}
|
||||
.text-blue-500 {
|
||||
color: var(--color-blue-500);
|
||||
}
|
||||
@ -754,9 +812,6 @@
|
||||
.text-gray-800 {
|
||||
color: var(--color-gray-800);
|
||||
}
|
||||
.text-green-600 {
|
||||
color: var(--color-green-600);
|
||||
}
|
||||
.text-red-500 {
|
||||
color: var(--color-red-500);
|
||||
}
|
||||
@ -844,6 +899,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;
|
||||
@ -927,6 +997,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-blue-700 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--color-blue-700);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-gray-50 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
@ -978,11 +1055,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.focus\:bg-blue-200 {
|
||||
&:focus {
|
||||
background-color: var(--color-blue-200);
|
||||
}
|
||||
}
|
||||
.focus\:bg-gray-50 {
|
||||
&:focus {
|
||||
background-color: var(--color-gray-50);
|
||||
@ -1009,17 +1081,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.focus\:outline-2 {
|
||||
&:focus {
|
||||
outline-style: var(--tw-outline-style);
|
||||
outline-width: 2px;
|
||||
}
|
||||
}
|
||||
.focus\:outline-blue-500 {
|
||||
&:focus {
|
||||
outline-color: var(--color-blue-500);
|
||||
}
|
||||
}
|
||||
.focus\:outline-none {
|
||||
&:focus {
|
||||
--tw-outline-style: none;
|
||||
@ -1071,6 +1132,11 @@
|
||||
margin-block: calc(var(--spacing) * 0);
|
||||
}
|
||||
}
|
||||
.md\:my-2 {
|
||||
@media (width >= 48rem) {
|
||||
margin-block: calc(var(--spacing) * 2);
|
||||
}
|
||||
}
|
||||
.md\:flex {
|
||||
@media (width >= 48rem) {
|
||||
display: flex;
|
||||
@ -1087,6 +1153,12 @@
|
||||
height: calc(var(--spacing) * 32);
|
||||
}
|
||||
}
|
||||
.md\:size-40 {
|
||||
@media (width >= 48rem) {
|
||||
width: calc(var(--spacing) * 40);
|
||||
height: calc(var(--spacing) * 40);
|
||||
}
|
||||
}
|
||||
.md\:size-64 {
|
||||
@media (width >= 48rem) {
|
||||
width: calc(var(--spacing) * 64);
|
||||
@ -1108,11 +1180,6 @@
|
||||
width: calc(1/4 * 100%);
|
||||
}
|
||||
}
|
||||
.md\:w-2\/3 {
|
||||
@media (width >= 48rem) {
|
||||
width: calc(2/3 * 100%);
|
||||
}
|
||||
}
|
||||
.md\:w-2\/5 {
|
||||
@media (width >= 48rem) {
|
||||
width: calc(2/5 * 100%);
|
||||
@ -1138,6 +1205,11 @@
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
.md\:items-start {
|
||||
@media (width >= 48rem) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
.md\:border-x {
|
||||
@media (width >= 48rem) {
|
||||
border-inline-style: var(--tw-border-style);
|
||||
@ -1170,16 +1242,31 @@
|
||||
padding-block: calc(var(--spacing) * 0);
|
||||
}
|
||||
}
|
||||
.md\:py-2 {
|
||||
@media (width >= 48rem) {
|
||||
padding-block: calc(var(--spacing) * 2);
|
||||
}
|
||||
}
|
||||
.md\:py-12 {
|
||||
@media (width >= 48rem) {
|
||||
padding-block: calc(var(--spacing) * 12);
|
||||
}
|
||||
}
|
||||
.md\:pt-2 {
|
||||
@media (width >= 48rem) {
|
||||
padding-top: calc(var(--spacing) * 2);
|
||||
}
|
||||
}
|
||||
.md\:pt-14 {
|
||||
@media (width >= 48rem) {
|
||||
padding-top: calc(var(--spacing) * 14);
|
||||
}
|
||||
}
|
||||
.md\:text-left {
|
||||
@media (width >= 48rem) {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.md\:text-2xl {
|
||||
@media (width >= 48rem) {
|
||||
font-size: var(--text-2xl);
|
||||
@ -1198,6 +1285,12 @@
|
||||
line-height: var(--tw-leading, var(--text-4xl--line-height));
|
||||
}
|
||||
}
|
||||
.md\:text-base {
|
||||
@media (width >= 48rem) {
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--tw-leading, var(--text-base--line-height));
|
||||
}
|
||||
}
|
||||
.md\:text-lg {
|
||||
@media (width >= 48rem) {
|
||||
font-size: var(--text-lg);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user