Merge pull request '(CI/CD): Workflow and Docker implementation.' (#26) from dev into master
Some checks failed
Deploy application with Docker / build_and_deploy (push) Failing after 1m34s
Some checks failed
Deploy application with Docker / build_and_deploy (push) Failing after 1m34s
Reviewed-on: #26
This commit is contained in:
commit
13596f7cb6
28
.github/workflows/deploy.yml
vendored
Normal file
28
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
name: Deploy application with Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: azpect3120/option.gophernest:latest
|
||||
53
Dockerfile
53
Dockerfile
@ -1,15 +1,54 @@
|
||||
# TEMPORARY SOLUTION
|
||||
# Need a real way to build tailwind and build the templ files
|
||||
FROM golang:1.24
|
||||
# Fetch stage
|
||||
FROM golang:latest AS fetch-stage
|
||||
|
||||
COPY go.mod go.sum /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN go mod download
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/app /app/cmd/web/main.go
|
||||
|
||||
# Generate stage
|
||||
FROM ghcr.io/a-h/templ:latest AS generate-stage
|
||||
|
||||
COPY --chown=65532:65532 . /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN ["templ", "generate"]
|
||||
|
||||
# Generate stage two
|
||||
|
||||
FROM node:lts-alpine AS tailwind-build-stage
|
||||
|
||||
COPY --from=generate-stage /app /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install tailwindcss @tailwindcss/cli && npx @tailwindcss/cli -i ./web/static/css/main.css -o ./web/static/css/tailwind.css --minify -c ./tailwind.config.js
|
||||
|
||||
|
||||
# Build stage
|
||||
FROM golang:1.24 AS build-stage
|
||||
|
||||
COPY --from=tailwind-build-stage /app /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /entrypoint /app/cmd/web/main.go
|
||||
|
||||
# Deploy.
|
||||
FROM gcr.io/distroless/static-debian11 AS release-stage
|
||||
|
||||
WORKDIR /
|
||||
|
||||
COPY --from=build-stage /entrypoint /entrypoint
|
||||
|
||||
COPY --from=build-stage /app/web/static /web/static
|
||||
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD [ "/app/app" ]
|
||||
USER nonroot:nonroot
|
||||
|
||||
ENTRYPOINT ["/entrypoint"]
|
||||
|
||||
@ -56,7 +56,7 @@ func Init(port int) *Server {
|
||||
func (s *Server) ConfigureAuth() *Server {
|
||||
err := godotenv.Load(".env")
|
||||
if err != nil {
|
||||
panic("Could not load env file")
|
||||
fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err)
|
||||
}
|
||||
|
||||
redirect_domain := os.Getenv("DOMAIN")
|
||||
@ -80,7 +80,7 @@ func (s *Server) ConfigureAuth() *Server {
|
||||
func (s *Server) ConnectDatabase() *Server {
|
||||
err := godotenv.Load(".env")
|
||||
if err != nil {
|
||||
panic("Could not load env file")
|
||||
fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err)
|
||||
}
|
||||
|
||||
var connUrl string = os.Getenv("DATABASE_URL")
|
||||
@ -107,7 +107,7 @@ func (s *Server) Start() {
|
||||
func (s *Server) Setup() *Server {
|
||||
err := godotenv.Load(".env")
|
||||
if err != nil {
|
||||
panic("Could not load env file")
|
||||
fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err)
|
||||
}
|
||||
|
||||
jwtSecret := []byte(os.Getenv("JWT_SECRET"))
|
||||
|
||||
@ -4,75 +4,89 @@ import "strings"
|
||||
import "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
|
||||
templ navLink(current, name, url string) {
|
||||
<a href={ templ.SafeURL(url) } if strings.ToLower(current)==strings.ToLower(name) {
|
||||
class="text-gray-700 border-b-2 border-blue-500 px-1 cursor-pointer" } else {
|
||||
class="text-gray-700 border-b-2 hover:border-blue-400 px-1 cursor-pointer border-white duration-150" }>
|
||||
{ name }
|
||||
</a>
|
||||
<a
|
||||
href={ templ.SafeURL(url) }
|
||||
if strings.ToLower(current)==strings.ToLower(name) {
|
||||
class="text-gray-700 border-b-2 border-blue-500 px-1 cursor-pointer"
|
||||
} else {
|
||||
class="text-gray-700 border-b-2 hover:border-blue-400 px-1 cursor-pointer border-white duration-150"
|
||||
}
|
||||
>
|
||||
{ name }
|
||||
</a>
|
||||
}
|
||||
|
||||
templ dropdownLink(name, url string) {
|
||||
<a class="py-2" href={ templ.SafeURL(url) }>
|
||||
{ name }
|
||||
</a>
|
||||
<a class="py-2" href={ templ.SafeURL(url) }>
|
||||
{ name }
|
||||
</a>
|
||||
}
|
||||
|
||||
templ listIcon(current, name, url string) {
|
||||
<a href={ templ.SafeURL(url) }>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"
|
||||
if strings.ToLower(current) == strings.ToLower(name) {
|
||||
class="h-4 text-blue-500"
|
||||
} else {
|
||||
class="h-4 text-gray-700 hover:text-blue-400 duration-150"
|
||||
}
|
||||
>
|
||||
<path fill="currentColor"
|
||||
d="M0 24C0 10.7 10.7 0 24 0L69.5 0c22 0 41.5 12.8 50.6 32l411 0c26.3 0 45.5 25 38.6 50.4l-41 152.3c-8.5 31.4-37 53.3-69.5 53.3l-288.5 0 5.4 28.5c2.2 11.3 12.1 19.5 23.6 19.5L488 336c13.3 0 24 10.7 24 24s-10.7 24-24 24l-288.3 0c-34.6 0-64.3-24.6-70.7-58.5L77.4 54.5c-.7-3.8-4-6.5-7.9-6.5L24 48C10.7 48 0 37.3 0 24zM128 464a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zm336-48a48 48 0 1 1 0 96 48 48 0 1 1 0-96z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a href={ templ.SafeURL(url) }>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 576 512"
|
||||
if strings.ToLower(current)==strings.ToLower(name) {
|
||||
class="h-4 text-blue-500"
|
||||
} else {
|
||||
class="h-4 text-gray-700 hover:text-blue-400 duration-150"
|
||||
}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M0 24C0 10.7 10.7 0 24 0L69.5 0c22 0 41.5 12.8 50.6 32l411 0c26.3 0 45.5 25 38.6 50.4l-41 152.3c-8.5 31.4-37 53.3-69.5 53.3l-288.5 0 5.4 28.5c2.2 11.3 12.1 19.5 23.6 19.5L488 336c13.3 0 24 10.7 24 24s-10.7 24-24 24l-288.3 0c-34.6 0-64.3-24.6-70.7-58.5L77.4 54.5c-.7-3.8-4-6.5-7.9-6.5L24 48C10.7 48 0 37.3 0 24zM128 464a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zm336-48a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
}
|
||||
|
||||
templ Navbar(current string) {
|
||||
<nav class="block md:fixed w-full z-10">
|
||||
<div
|
||||
class="relative w-full px-8 md:px-44 p-4 border-b border-gray-300 shadow-sm shadow-gray-300 bg-white flex justify-between items-center">
|
||||
<div>
|
||||
<a href={ domain.WEB_HOME }>
|
||||
<p class="select-none">Potion</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="hidden md:flex lg:flex items-center gap-8 select-none">
|
||||
@navLink(current, "Home", domain.WEB_HOME)
|
||||
@navLink(current, "Favorites", domain.WEB_FAVORITES)
|
||||
@navLink(current, "Create", domain.WEB_CREATE)
|
||||
@navLink(current, "Profile", domain.WEB_PROFIlE)
|
||||
@listIcon(current, "List", domain.WEB_LIST)
|
||||
</div>
|
||||
<div class="md:hidden grid place-content-center">
|
||||
<button onclick="toggleMenu()" class="p-2">
|
||||
// carot
|
||||
<svg id="mobile-menu-button-carot" class="hidden size-5" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 320 512">
|
||||
<path
|
||||
d="M182.6 137.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l256 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z">
|
||||
</path>
|
||||
</svg>
|
||||
// bars
|
||||
<svg id="mobile-menu-button-bars" class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
||||
<path fill="currentColor"
|
||||
d="M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@hamburgerMenu()
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="block md:fixed w-full z-10">
|
||||
<div
|
||||
class="relative w-full px-8 md:px-44 p-4 border-b border-gray-300 shadow-sm shadow-gray-300 bg-white flex justify-between items-center"
|
||||
>
|
||||
<div>
|
||||
<a href={ domain.WEB_HOME }>
|
||||
<p class="select-none">Potion</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="hidden md:flex lg:flex items-center gap-8 select-none">
|
||||
@navLink(current, "Home", domain.WEB_HOME)
|
||||
@navLink(current, "Favorites", domain.WEB_FAVORITES)
|
||||
@navLink(current, "Create", domain.WEB_CREATE)
|
||||
@navLink(current, "Profile", domain.WEB_PROFIlE)
|
||||
@listIcon(current, "List", domain.WEB_LIST)
|
||||
</div>
|
||||
<div class="md:hidden grid place-content-center">
|
||||
<button onclick="toggleMenu()" class="p-2">
|
||||
// carot
|
||||
<svg
|
||||
id="mobile-menu-button-carot"
|
||||
class="hidden size-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 320 512"
|
||||
>
|
||||
<path
|
||||
d="M182.6 137.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l256 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z"
|
||||
></path>
|
||||
</svg>
|
||||
// bars
|
||||
<svg id="mobile-menu-button-bars" class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@hamburgerMenu()
|
||||
</div>
|
||||
</nav>
|
||||
}
|
||||
|
||||
templ hamburgerMenu() {
|
||||
<script>
|
||||
<script>
|
||||
function toggleMenu() {
|
||||
const menu = document.getElementById("mobile-menu-content");
|
||||
const carotButton = document.getElementById("mobile-menu-button-carot");
|
||||
@ -93,13 +107,14 @@ templ hamburgerMenu() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div id="mobile-menu-content"
|
||||
class="hidden w-full flex-col items-center absolute top-[100%] left-0 py-2 bg-white border-b border-gray-300 shadow-sm shadow-gray-300 z-20">
|
||||
@dropdownLink("Home", domain.WEB_HOME)
|
||||
@dropdownLink("Favorites", domain.WEB_FAVORITES)
|
||||
@dropdownLink("Create", domain.WEB_CREATE)
|
||||
@dropdownLink("Profile", domain.WEB_PROFIlE)
|
||||
@dropdownLink("Shopping List", domain.WEB_LIST)
|
||||
</div>
|
||||
<div
|
||||
id="mobile-menu-content"
|
||||
class="hidden w-full flex-col items-center absolute top-[100%] left-0 py-2 bg-white border-b border-gray-300 shadow-sm shadow-gray-300 z-20"
|
||||
>
|
||||
@dropdownLink("Home", domain.WEB_HOME)
|
||||
@dropdownLink("Favorites", domain.WEB_FAVORITES)
|
||||
@dropdownLink("Create", domain.WEB_CREATE)
|
||||
@dropdownLink("Profile", domain.WEB_PROFIlE)
|
||||
@dropdownLink("Shopping List", domain.WEB_LIST)
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ func navLink(current, name, url string) templ.Component {
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/navbar.templ`, Line: 10, Col: 8}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/navbar.templ`, Line: 15, Col: 8}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -114,7 +114,7 @@ func dropdownLink(name, url string) templ.Component {
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/navbar.templ`, Line: 16, Col: 8}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/navbar.templ`, Line: 21, Col: 8}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -211,7 +211,7 @@ func Navbar(current string) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"><p class=\"select-none\">Potion</p></a></div><div class=\"hidden md:flex lg:flex items-center gap-8 select-none\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"><p class=\"select-none text-red-800\">Potion</p></a></div><div class=\"hidden md:flex lg:flex items-center gap-8 select-none\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@ -5,118 +5,124 @@ 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">
|
||||
<div class="relative">
|
||||
<video class="" autoplay loop muted playsinline>
|
||||
<source src="/v1/web/static/img/salmon_video.mp4" type="video/mp4" />
|
||||
</video>
|
||||
<h1 class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-center
|
||||
text-white text-3xl w-4/5 font-bold z-10">
|
||||
Discover Your Next Favorite Meal
|
||||
</h1>
|
||||
</div>
|
||||
<p class="leading-relaxed p-4 my-8">
|
||||
Welcome to your ultimate recipe hub! Whether you're a seasoned chef or just starting your culinary adventure,
|
||||
we're here to inspire. Explore thousands of delicious recipes, from quick weeknight dinners to gourmet delights,
|
||||
all at your fingertips. Find exactly what you're craving with our powerful search and intuitive filters, or
|
||||
browse our trending dishes for fresh ideas.
|
||||
</p>
|
||||
</section>
|
||||
<section class="w-full h-fit mb-16">
|
||||
<div class="relative">
|
||||
<video class="" autoplay loop muted playsinline>
|
||||
<source src="/v1/web/static/img/salmon_video.mp4" type="video/mp4"/>
|
||||
</video>
|
||||
<h1
|
||||
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-center
|
||||
text-white text-3xl w-4/5 font-bold z-10"
|
||||
>
|
||||
Discover Your Next Favorite Meal
|
||||
</h1>
|
||||
</div>
|
||||
<p class="leading-relaxed p-4 my-8">
|
||||
Welcome to your ultimate recipe hub! Whether you're a seasoned chef or just starting your culinary adventure,
|
||||
we're here to inspire. Explore thousands of delicious recipes, from quick weeknight dinners to gourmet delights,
|
||||
all at your fingertips. Find exactly what you're craving with our powerful search and intuitive filters, or
|
||||
browse our trending dishes for fresh ideas.
|
||||
</p>
|
||||
</section>
|
||||
}
|
||||
|
||||
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-3/4">
|
||||
@components.SearchBar(domainRecipe.SearchFilters{}, true, false, false)
|
||||
</div>
|
||||
<div class="hidden" id="result-list"></div>
|
||||
</section>
|
||||
<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-3/4">
|
||||
@components.SearchBar(domainRecipe.SearchFilters{}, true, false, false)
|
||||
</div>
|
||||
<div class="hidden" id="result-list"></div>
|
||||
</section>
|
||||
}
|
||||
|
||||
templ highlightSection(liked bool) {
|
||||
<section class="w-full flex flex-col items-center justify-center my-8 py-4">
|
||||
@components.BannerText("Recipe of the Week!")
|
||||
<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">
|
||||
@components.RecipeCardLarge(false)
|
||||
</div>
|
||||
</section>
|
||||
<section class="w-full flex flex-col items-center justify-center my-8 py-4">
|
||||
@components.BannerText("Recipe of the Week!")
|
||||
<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">
|
||||
@components.RecipeCardLarge(false)
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
templ listsSection(loggedIn bool, viewed, made []domainRecipe.Recipe) {
|
||||
<section class="w-full flex flex-col items-center justify-center my-8 py-4">
|
||||
@components.BannerText("Take Another Look.")
|
||||
<div class="w-full">
|
||||
<h3 class="text-lg mt-8 mx-4">Recently viewed</h3>
|
||||
if loggedIn {
|
||||
<div class="flex overflow-x-auto gap-x-4 mx-4 my-4">
|
||||
for _, recipe := range viewed {
|
||||
@components.RecipeCardSmall(recipe)
|
||||
}
|
||||
@components.ContentCardSmall("View full history...", "/v1/web/history")
|
||||
</div>
|
||||
} else {
|
||||
<div class="my-2 mx-4 text-gray-800">
|
||||
<a class="underline" href={ domain.WEB_LOGIN }>
|
||||
<p class="text-sm">Log in to view metrics.</p>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
<h3 class="text-lg mt-8 mx-4">Make again</h3>
|
||||
if loggedIn {
|
||||
<div class="flex overflow-x-auto gap-x-4 mx-4 my-4">
|
||||
for _, recipe := range made {
|
||||
@components.RecipeCardSmall(recipe)
|
||||
}
|
||||
@components.ContentCardSmall("View full history...", "/v1/web/history")
|
||||
</div>
|
||||
} else {
|
||||
<div class="my-2 mx-4 text-gray-800">
|
||||
<a class="underline" href={ domain.WEB_LOGIN }>
|
||||
<p class="text-sm">Log in to view metrics.</p>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
<section class="w-full flex flex-col items-center justify-center my-8 py-4">
|
||||
@components.BannerText("Take Another Look.")
|
||||
<div class="w-full">
|
||||
<h3 class="text-lg mt-8 mx-4">Recently viewed</h3>
|
||||
if loggedIn {
|
||||
<div class="flex overflow-x-auto gap-x-4 mx-4 my-4">
|
||||
for _, recipe := range viewed {
|
||||
@components.RecipeCardSmall(recipe)
|
||||
}
|
||||
@components.ContentCardSmall("View full history...", "/v1/web/history")
|
||||
</div>
|
||||
} else {
|
||||
<div class="my-2 mx-4 text-gray-800">
|
||||
<a class="underline" href={ domain.WEB_LOGIN }>
|
||||
<p class="text-sm">Log in to view metrics.</p>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
<h3 class="text-lg mt-8 mx-4">Make again</h3>
|
||||
if loggedIn {
|
||||
<div class="flex overflow-x-auto gap-x-4 mx-4 my-4">
|
||||
for _, recipe := range made {
|
||||
@components.RecipeCardSmall(recipe)
|
||||
}
|
||||
@components.ContentCardSmall("View full history...", "/v1/web/history")
|
||||
</div>
|
||||
} else {
|
||||
<div class="my-2 mx-4 text-gray-800">
|
||||
<a class="underline" href={ domain.WEB_LOGIN }>
|
||||
<p class="text-sm">Log in to view metrics.</p>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
templ ctaSection() {
|
||||
<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={ domain.WEB_CREATE } class="flex items-center justify-center
|
||||
<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={ domain.WEB_CREATE }
|
||||
class="flex items-center justify-center
|
||||
bg-gradient-to-r from-blue-400 to-blue-600 text-white
|
||||
px-12 py-5 rounded-full shadow-sm hover:shadow-md
|
||||
transition-all duration-300 ease-in-out shadow-blue-700
|
||||
text-lg md:text-2xl font-bold uppercase tracking-wide">
|
||||
Create Your Recipe!
|
||||
</a>
|
||||
</section>
|
||||
text-lg md:text-2xl font-bold uppercase tracking-wide"
|
||||
>
|
||||
Create Your Recipe!
|
||||
</a>
|
||||
</section>
|
||||
}
|
||||
|
||||
templ HomePage(loggedIn bool, viewed, made []domainRecipe.Recipe) {
|
||||
@components.Navbar("home")
|
||||
<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">
|
||||
@introSection()
|
||||
@searchSection()
|
||||
@highlightSection(false)
|
||||
@listsSection(loggedIn, viewed, made)
|
||||
@ctaSection()
|
||||
</div>
|
||||
</div>
|
||||
@components.Navbar("home")
|
||||
<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">
|
||||
@introSection()
|
||||
@searchSection()
|
||||
@highlightSection(false)
|
||||
@listsSection(loggedIn, viewed, made)
|
||||
@ctaSection()
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
monospace;
|
||||
--color-red-100: oklch(93.6% 0.032 17.717);
|
||||
--color-red-500: oklch(63.7% 0.237 25.331);
|
||||
--color-red-800: oklch(44.4% 0.177 26.899);
|
||||
--color-green-500: oklch(72.3% 0.219 149.579);
|
||||
--color-blue-50: oklch(97% 0.014 254.604);
|
||||
--color-blue-100: oklch(93.2% 0.032 255.585);
|
||||
@ -259,18 +260,12 @@
|
||||
.z-20 {
|
||||
z-index: 20;
|
||||
}
|
||||
.m-4 {
|
||||
margin: calc(var(--spacing) * 4);
|
||||
}
|
||||
.mx-2 {
|
||||
margin-inline: calc(var(--spacing) * 2);
|
||||
}
|
||||
.mx-4 {
|
||||
margin-inline: calc(var(--spacing) * 4);
|
||||
}
|
||||
.mx-8 {
|
||||
margin-inline: calc(var(--spacing) * 8);
|
||||
}
|
||||
.mx-auto {
|
||||
margin-inline: auto;
|
||||
}
|
||||
@ -662,9 +657,6 @@
|
||||
.bg-red-100 {
|
||||
background-color: var(--color-red-100);
|
||||
}
|
||||
.bg-red-500 {
|
||||
background-color: var(--color-red-500);
|
||||
}
|
||||
.bg-white {
|
||||
background-color: var(--color-white);
|
||||
}
|
||||
@ -881,6 +873,9 @@
|
||||
.text-red-500 {
|
||||
color: var(--color-red-500);
|
||||
}
|
||||
.text-red-800 {
|
||||
color: var(--color-red-800);
|
||||
}
|
||||
.text-white {
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user