1
0
Fork 0
forked from goffee/cup

Merge pull request 'develop' (#15) from develop into main

Reviewed-on: goffee/cup#15
This commit is contained in:
Zeni Kim 2026-05-06 22:26:05 -04:00
commit 06c4d5765a
12 changed files with 2 additions and 1857 deletions

View file

@ -1,52 +0,0 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package controllers
import (
"encoding/json"
"log"
"time"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/cup/workers"
"github.com/hibiken/asynq"
)
// Make samples queues
func Queuesample(c *core.Context) *core.Response {
// Get client queue asynq
client := c.GetQueueClient()
// Create a task with typename and payload.
payload, err := json.Marshal(workers.EmailTaskPayload{UserID: 42})
if err != nil {
log.Fatal(err)
}
t1 := asynq.NewTask(workers.TypeWelcomeEmail, payload)
t2 := asynq.NewTask(workers.TypeReminderEmail, payload)
// Process the task immediately.
info, err := client.Enqueue(t1)
if err != nil {
log.Fatal(err)
}
log.Printf(" [*] Successfully enqueued task: %+v", info)
// Process 2 task 1 min later.
for i := 1; i < 3; i++ {
info, err = client.Enqueue(t2, asynq.ProcessIn(1*time.Minute))
if err != nil {
log.Fatal(err)
}
log.Printf(" [*] Successfully enqueued task: %+v", info)
}
message := "{\"message\": \"Task queued\"}"
return c.Response.Json(message)
}

View file

@ -1,149 +0,0 @@
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package controllers
import (
"fmt"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/core/template/components"
"git.smarteching.com/goffee/cup/utils"
)
// Show basic template
func Sample(c *core.Context) *core.Response {
// first, include all compoments
type templateData struct {
PageCard components.PageCard
}
// now fill data of the components
tmplData := templateData{
PageCard: components.PageCard{
CardTitle: "Framework Goffee",
CardBody: "Powered by Golang",
},
}
return c.Response.Template("basic.html", tmplData)
}
// Show basic app login
func AppLogin(c *core.Context) *core.Response {
// first, include all compoments
// first, include all compoments
type templateData struct {
PageCard components.PageCard
}
// now fill data of the components
tmplData := templateData{
PageCard: components.PageCard{
CardTitle: "Card title",
CardBody: "Loerm ipsum at deim",
},
}
return c.Response.Template("login.html", tmplData)
}
// Show basic app login
func AppSession(c *core.Context) *core.Response {
var session = new(utils.SessionUser)
// true if session is active
hassession := session.Init(c)
//session.Set("numberdos", 66)
type templateData struct {
PageCard components.PageCard
}
// now fill data of the components
tmplData := templateData{}
if hassession {
sesiondata := ""
cardtitle := fmt.Sprintf("Session user id: %v", session.GetUserID())
numberdos, ok := session.Get("numberdos")
if numberdos != nil {
numberdos = numberdos.(float64) + 10
} else {
numberdos = 10
}
session.Set("numberdos", numberdos)
if ok {
sesiondata = fmt.Sprintf("OK, Session numberdos has %v", numberdos)
} else {
sesiondata = fmt.Sprintf("No ok, session numberdos has %v", numberdos)
}
// delete single
//session.Delete("numberdos")
// delete all data
//session.Flush()
tmplData = templateData{
PageCard: components.PageCard{
CardTitle: cardtitle,
CardBody: sesiondata,
},
}
return c.Response.Template("appsession.html", tmplData)
} else {
return c.Response.Template("login.html", tmplData)
}
}
// Show basic app sample
func AppSample(c *core.Context) *core.Response {
// first, include all compoments
type templateData struct {
PageCard components.PageCard
ContentDropdown components.ContentDropdown
}
// now fill data of the components
tmplData := templateData{
PageCard: components.PageCard{
CardTitle: "Protected page",
CardBody: "If you can see this page, your are loggedin",
},
ContentDropdown: components.ContentDropdown{
Label: "dropdown",
Items: []components.ContentDropdownItem{
{
Text: "Signout",
Link: "#",
ID: "signout",
},
{
Text: "item disabled",
Link: "#",
IsDisabled: true,
},
},
},
}
//fmt.Printf("Outside cookie user is: %s", user.Email)
return c.Response.Template("app.html", tmplData)
}

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,6 @@ package main
import ( import (
"git.smarteching.com/goffee/core" "git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/cup/controllers" "git.smarteching.com/goffee/cup/controllers"
"git.smarteching.com/goffee/cup/hooks"
) )
// Register the app controllers // Register the app controllers
@ -20,15 +19,8 @@ func registerRoutes() {
// Define your routes here... // Define your routes here...
controller.Get("/", controllers.WelcomeHome) controller.Get("/", controllers.WelcomeHome)
// Uncomment the lines below to enable theme demo
controller.Get("/themebase", controllers.Themedemo)
controller.Get("/themeform", controllers.Themeform)
controller.Get("/themecontent", controllers.Themecontent)
controller.Get("/themepanel", controllers.Themedemo)
controller.Get("/themeelements", controllers.ThemeElements)
controller.Get("/queuesample", controllers.Queuesample)
// Uncomment the lines below to enable authentication // Uncomment the lines below to enable authentication API
controller.Post("/signup", controllers.Signup) controller.Post("/signup", controllers.Signup)
controller.Post("/signin", controllers.Signin) controller.Post("/signin", controllers.Signin)
controller.Post("/signout", controllers.Signout) controller.Post("/signout", controllers.Signout)
@ -44,22 +36,6 @@ func registerRoutes() {
controller.Post("/admin/users/edit/:id", controllers.AdminUsersEdit) controller.Post("/admin/users/edit/:id", controllers.AdminUsersEdit)
controller.Post("/admin/users/delete", controllers.AdminUsersDelete) controller.Post("/admin/users/delete", controllers.AdminUsersDelete)
controller.Post("/admin/users/deleteconfirm", controllers.AdminUsersDelConfirm) controller.Post("/admin/users/deleteconfirm", controllers.AdminUsersDelConfirm)
//controller.Get("/admin/users/roles", controllers.Signout)
//controller.Get("/admin/users/permissions", controllers.ResetPasswordRequest)
controller.Get("/dashboard", controllers.WelcomeToDashboard, hooks.AuthCheck)
// templates demos
controller.Get("/signout", controllers.Signout)
controller.Get("/appsample", controllers.AppSample, hooks.AuthCheck)
controller.Post("/appsample", controllers.AppSample, hooks.AuthCheck)
controller.Get("/applogin", controllers.AppLogin, hooks.CheckSessionCookie)
controller.Post("/applogin", controllers.AppLogin, hooks.CheckSessionCookie)
controller.Get("/appsession", controllers.AppSession)
controller.Post("/appsession", controllers.AppSession)
controller.Get("/templatesfunc", controllers.TemplatesFunctions)
} }

Binary file not shown.

View file

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en">
{{template "page_head" "Sample page"}}
<body>
<div class="container">
<div class="row">
{{template "content_dropdown" .ContentDropdown}}
{{template "page_card" .PageCard}}
{{ define "page_card_content" }}
<img class="goffeelogo"src="/public/img/goffee.png" alt="Goffee logo" />
{{ end }}
</div>
</div>
{{template "page_footer"}}
</body>
</html>

View file

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
{{template "page_head" "Sample page test session vars"}}
<body>
<div class="container">
<div class="row">
{{template "page_card" .PageCard}}
</div>
</div>
{{template "page_footer"}}
</body>
</html>

View file

@ -1,471 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{.SiteTitle}} — template lab</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=JetBrains+Mono:wght@400;500&family=DM+Sans:wght@300;400;500&display=swap" rel="stylesheet" />
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #0d0d0f;
--surface: #141416;
--border: #222226;
--muted: #3a3a40;
--subtle: #6b6b75;
--body: #c8c8d0;
--heading: #f0f0f4;
--accent: #e8c547;
--accent2: #5b8cff;
--danger: #ff6b6b;
--mono: 'JetBrains Mono', monospace;
--serif: 'Playfair Display', Georgia, serif;
--sans: 'DM Sans', sans-serif;
}
html { background: var(--bg); color: var(--body); font-family: var(--sans); font-size: 16px; line-height: 1.6; }
body { max-width: 1100px; margin: 0 auto; padding: 0 2rem 6rem; }
/* ── Header ── */
header {
border-bottom: 1px solid var(--border);
padding: 3rem 0 2rem;
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 1rem;
}
.site-title {
font-family: var(--mono);
font-size: .75rem;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--accent);
}
.site-headline {
font-family: var(--serif);
font-size: clamp(2rem, 5vw, 3.5rem);
color: var(--heading);
line-height: 1.1;
margin-top: .4rem;
}
.site-headline em { color: var(--accent); font-style: italic; }
.helper-count {
font-family: var(--mono);
font-size: .7rem;
color: var(--subtle);
text-align: right;
white-space: nowrap;
}
/* ── Section label ── */
.section-label {
display: flex;
align-items: center;
gap: 1rem;
margin: 4rem 0 1.5rem;
}
.section-label span {
font-family: var(--mono);
font-size: .65rem;
letter-spacing: .15em;
text-transform: uppercase;
color: var(--accent);
white-space: nowrap;
}
.section-label::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}
/* ── Helper badge ── */
.badge {
display: inline-block;
font-family: var(--mono);
font-size: .6rem;
letter-spacing: .08em;
padding: .15em .5em;
border-radius: 3px;
border: 1px solid var(--muted);
color: var(--subtle);
vertical-align: middle;
margin-left: .4rem;
}
.badge-accent { border-color: var(--accent); color: var(--accent); }
.badge-blue { border-color: var(--accent2); color: var(--accent2); }
/* ── Demo block ── */
.demo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1px;
background: var(--border);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
}
.demo-cell {
background: var(--surface);
padding: 1.5rem;
}
.demo-cell h3 {
font-family: var(--mono);
font-size: .7rem;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: .75rem;
}
.demo-cell .result {
font-size: 1rem;
color: var(--heading);
}
.demo-cell .note {
margin-top: .5rem;
font-size: .75rem;
color: var(--subtle);
font-family: var(--mono);
}
/* ── Article cards ── */
.article-list { display: flex; flex-direction: column; gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.article-card { background: var(--surface); padding: 2rem; display: grid; grid-template-columns: 1fr auto; gap: 1rem; align-items: start; transition: background .15s; }
.article-card:hover { background: #18181c; }
.card-meta { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-bottom: .75rem; }
.tag {
font-family: var(--mono);
font-size: .6rem;
letter-spacing: .08em;
text-transform: uppercase;
padding: .2em .55em;
background: var(--muted);
border-radius: 3px;
color: var(--body);
}
.tag-featured { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); }
.card-title { font-family: var(--serif); font-size: 1.35rem; color: var(--heading); line-height: 1.25; margin-bottom: .5rem; }
.card-subtitle { font-size: .85rem; color: var(--accent2); margin-bottom: .6rem; font-style: italic; }
.card-excerpt { font-size: .875rem; color: var(--body); line-height: 1.65; }
.card-author { display: flex; align-items: center; gap: .6rem; margin-top: 1.25rem; }
.avatar {
width: 32px; height: 32px; border-radius: 50%;
background: var(--muted);
display: flex; align-items: center; justify-content: center;
font-family: var(--mono); font-size: .55rem; color: var(--accent);
flex-shrink: 0;
}
.author-name { font-size: .8rem; color: var(--heading); }
.author-date { font-size: .72rem; color: var(--subtle); font-family: var(--mono); }
.card-aside { text-align: right; }
.views { font-family: var(--mono); font-size: .75rem; color: var(--subtle); white-space: nowrap; }
.views strong { color: var(--body); display: block; font-size: 1rem; }
.price-badge {
display: inline-block; margin-top: .75rem;
font-family: var(--mono); font-size: .7rem;
padding: .3em .7em; border-radius: 4px;
background: color-mix(in srgb, var(--accent2) 15%, transparent);
color: var(--accent2); border: 1px solid var(--accent2);
}
.price-free {
background: color-mix(in srgb, #4caf50 12%, transparent);
color: #6fcf97; border-color: #4caf50;
}
/* ── Logic demo table ── */
.logic-table { width: 100%; border-collapse: collapse; font-size: .85rem; }
.logic-table th {
font-family: var(--mono); font-size: .65rem; letter-spacing: .1em; text-transform: uppercase;
color: var(--subtle); text-align: left; padding: .6rem 1rem;
border-bottom: 1px solid var(--border);
}
.logic-table td { padding: .75rem 1rem; border-bottom: 1px solid var(--border); color: var(--body); }
.logic-table tr:last-child td { border-bottom: none; }
.logic-table tr:hover td { background: var(--surface); }
.val { font-family: var(--mono); font-size: .8rem; color: var(--accent); }
/* ── Footer ── */
footer {
margin-top: 5rem; padding-top: 2rem; border-top: 1px solid var(--border);
font-family: var(--mono); font-size: .65rem; color: var(--muted);
display: flex; justify-content: space-between; flex-wrap: wrap; gap: .5rem;
}
</style>
</head>
<body>
<!-- ════════════════════════════════════════════
HEADER
════════════════════════════════════════════ -->
<header>
<div>
<p class="site-title">{{.SiteTitle}}</p>
<h1 class="site-headline">Template <em>helpers</em><br>in action</h1>
</div>
<p class="helper-count">22 helpers registered<br>across 6 groups</p>
</header>
<!-- ════════════════════════════════════════════
STRING HELPERS
════════════════════════════════════════════ -->
<div class="section-label"><span>String helpers</span></div>
{{$first := first .Articles}}
<div class="demo-grid">
<div class="demo-cell">
<h3>capitalize <span class="badge">capitalize</span></h3>
<p class="result">{{capitalize $first.Title}}</p>
<p class="note">input: "{{$first.Title}}"</p>
</div>
<div class="demo-cell">
<h3>truncate <span class="badge">truncate</span></h3>
<p class="result">{{$first.Excerpt | truncate 80}}</p>
<p class="note">capped at 80 chars</p>
</div>
<div class="demo-cell">
<h3>prepend <span class="badge">prepend</span></h3>
<p class="result">{{prepend $first.Slug "/articles/"}}</p>
<p class="note">prepended "/articles/" to slug</p>
</div>
<div class="demo-cell">
<h3>strAppend <span class="badge">strAppend</span></h3>
<p class="result">{{strAppend $first.Slug ".html"}}</p>
<p class="note">appended ".html" to slug</p>
</div>
<div class="demo-cell">
<h3>split → join <span class="badge">split</span> <span class="badge">join</span></h3>
{{$parts := split "go,templates,funcmap" ","}}
<p class="result">{{join $parts " · "}}</p>
<p class="note">split on "," then joined with " · "</p>
</div>
</div>
<!-- ════════════════════════════════════════════
NUMBER & DATE HELPERS
════════════════════════════════════════════ -->
<div class="section-label"><span>Number &amp; Date helpers</span></div>
<div class="demo-grid">
<div class="demo-cell">
<h3>fmtNumber — int <span class="badge">fmtNumber</span></h3>
<p class="result">{{fmtNumber $first.Views}}</p>
<p class="note">raw value: {{$first.Views}}</p>
</div>
{{$paid := index .Articles 2}}
<div class="demo-cell">
<h3>fmtNumber — float <span class="badge">fmtNumber</span></h3>
<p class="result">$ {{fmtNumber $paid.Price}}</p>
<p class="note">raw value: {{$paid.Price}}</p>
</div>
<div class="demo-cell">
<h3>fmtDate "short" <span class="badge">fmtDate</span></h3>
<p class="result">{{fmtDate $first.PublishedAt "short"}}</p>
<p class="note">layout: "02 Jan 2006"</p>
</div>
<div class="demo-cell">
<h3>fmtDate "long" <span class="badge">fmtDate</span></h3>
<p class="result">{{fmtDate $first.PublishedAt "long"}}</p>
<p class="note">layout: "02 January 2006"</p>
</div>
<div class="demo-cell">
<h3>fmtDate "iso" <span class="badge">fmtDate</span></h3>
<p class="result">{{fmtDate $first.PublishedAt "iso"}}</p>
<p class="note">layout: "2006-01-02"</p>
</div>
<div class="demo-cell">
<h3>timeAgo <span class="badge">timeAgo</span></h3>
{{range .Articles}}
<p class="result" style="margin-bottom:.3rem">{{timeAgo .PublishedAt}} <span class="note" style="display:inline">— {{fmtDate .PublishedAt "short"}}</span></p>
{{end}}
</div>
</div>
<!-- ════════════════════════════════════════════
COLLECTION HELPERS
════════════════════════════════════════════ -->
<div class="section-label"><span>Collection helpers</span></div>
<div class="demo-grid">
<div class="demo-cell">
<h3>first <span class="badge">first</span></h3>
{{with first .Articles}}
<p class="result">{{capitalize .Title}}</p>
<p class="note">first article in the list</p>
{{end}}
</div>
<div class="demo-cell">
<h3>last <span class="badge">last</span></h3>
{{with last .Articles}}
<p class="result">{{capitalize .Title}}</p>
<p class="note">last article in the list</p>
{{end}}
</div>
<div class="demo-cell">
<h3>sliceOf 02 <span class="badge">sliceOf</span></h3>
{{range sliceOf .Articles 0 2}}
<p class="result" style="margin-bottom:.25rem">— {{capitalize .Title}}</p>
{{end}}
<p class="note">only first 2 of {{len .Articles}} articles</p>
</div>
<div class="demo-cell">
<h3>contains — slice <span class="badge">contains</span></h3>
{{if contains $first.Tags "go"}}
<p class="result" style="color:var(--accent)">✓ has tag "go"</p>
{{else}}
<p class="result" style="color:var(--danger)">✗ missing tag "go"</p>
{{end}}
<p class="note">tags: {{join $first.Tags ", "}}</p>
</div>
<div class="demo-cell">
<h3>contains — string <span class="badge">contains</span></h3>
{{if contains $first.Excerpt "FuncMap"}}
<p class="result" style="color:var(--accent)">✓ excerpt mentions "FuncMap"</p>
{{else}}
<p class="result" style="color:var(--danger)">✗ not found</p>
{{end}}
<p class="note">substring search on .Excerpt</p>
</div>
<div class="demo-cell">
<h3>join <span class="badge">join</span></h3>
<p class="result">{{join $first.Tags " / "}}</p>
<p class="note">tags joined with " / "</p>
</div>
</div>
<!-- ════════════════════════════════════════════
LOGIC HELPERS
════════════════════════════════════════════ -->
<div class="section-label"><span>Logic helpers</span></div>
<table class="logic-table">
<thead>
<tr>
<th>Helper</th>
<th>Article</th>
<th>Input</th>
<th>Output</th>
</tr>
</thead>
<tbody>
{{range .Articles}}
<tr>
<td><span class="badge badge-accent">defaultVal</span></td>
<td>{{.Title | capitalize | truncate 30}}</td>
<td class="val">.Subtitle ({{if .Subtitle}}"{{.Subtitle}}"{{else}}empty{{end}})</td>
<td class="val">{{defaultVal "No subtitle" .Subtitle}}</td>
</tr>
<tr>
<td><span class="badge badge-blue">ternary</span></td>
<td>{{capitalize .Title | truncate 30}}</td>
<td class="val">.Featured = {{.Featured}}</td>
<td class="val">{{ternary "⭐ featured" "regular" .Featured}}</td>
</tr>
<tr>
<td><span class="badge badge-accent">coalesce</span></td>
<td>{{capitalize .Title | truncate 30}}</td>
<td class="val">.Subtitle → .Title</td>
<td class="val">{{coalesce .Subtitle .Title}}</td>
</tr>
{{end}}
</tbody>
</table>
<!-- ════════════════════════════════════════════
FULL ARTICLE CARDS (all helpers combined)
════════════════════════════════════════════ -->
<div class="section-label"><span>Full article cards — all helpers combined</span></div>
<div class="article-list">
{{range .Articles}}
<div class="article-card">
<div>
<div class="card-meta">
{{if .Featured}}<span class="tag tag-featured">featured</span>{{end}}
{{range .Tags}}<span class="tag">{{.}}</span>{{end}}
</div>
<h2 class="card-title">{{capitalize .Title}}</h2>
{{with coalesce .Subtitle ""}}
<p class="card-subtitle">{{.}}</p>
{{end}}
<p class="card-excerpt">{{.Excerpt | truncate 160}}</p>
<a style="font-family:var(--mono);font-size:.7rem;color:var(--accent2);text-decoration:none;margin-top:.75rem;display:inline-block;"
href="{{prepend .Slug "/articles/"}}">
{{prepend .Slug "/articles/"}} →
</a>
<div class="card-author">
<div class="avatar">{{.Author.Avatar}}</div>
<div>
<div class="author-name">{{capitalize .Author.Name}}</div>
<div class="author-date">
{{fmtDate .PublishedAt "short"}} · {{timeAgo .PublishedAt}}
</div>
</div>
</div>
</div>
<div class="card-aside">
<div class="views">
<strong>{{fmtNumber .Views}}</strong>
views
</div>
{{if gt .Price 0.0}}
<span class="price-badge">$ {{fmtNumber .Price}}</span>
{{else}}
<span class="price-badge price-free">free</span>
{{end}}
</div>
</div>
{{end}}
</div>
<!-- ════════════════════════════════════════════
FOOTER
════════════════════════════════════════════ -->
<footer>
<span>{{.SiteTitle}} / template sandbox</span>
<span>{{len .Articles}} articles · {{fmtDate (first .Articles).PublishedAt "iso"}} → {{fmtDate (last .Articles).PublishedAt "iso"}}</span>
</footer>
</body>
</html>

View file

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
{{template "page_head" "Goffee"}}
<body>
<div class="container">
{{template "page_card" .PageCard}}
</div>
{{template "page_footer"}}
</body>
</html>

View file

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html lang="en">
{{template "page_head" "Goffee"}}
<body>
<div class="container">
<fieldset>
<legend>Content demos</legend>
<div class="row">
{{template "content_table" .ContentTable}}
{{if .ShouldShowPagination}}
{{template "content_pagination" .Pagination}}
{{end}}
</div>
<div class="row">
<div class="col-lg-6">
<h2>Pie chart</h2>
{{template "content_graph" .ContentGraph}}
<hr>
{{template "form_input" .FieldText}}
{{template "form_select" .FormSelectCityM}}
</div>
<div class="col-lg-6">
{{template "content_tabledetail" .ContentTabledetail}}
</div>
</div>
</fieldset>
</div>
{{template "page_footer"}}
</body>
</html>

View file

@ -1,66 +0,0 @@
<!DOCTYPE html>
<html lang="en">
{{template "page_head" "Goffee"}}
<body>
<div class="container">
<fieldset class="mb-3">
<legend>Demos buttons</legend>
<div class="container border rounded-3 p-2">
{{range .Buttons}}
{{template "form_button" .}}
{{end}}
</div>
</fieldset>
<fieldset class="mb-3">
<legend>Demos href</legend>
<div class="container d-flex justify-content-between border rounded-3 p-2">
{{range .Hrefs}}
{{template "content_href" .}}
{{end}}
</div>
</fieldset>
<fieldset class="mb-3">
<legend>Demos Badges</legend>
<div class="container d-flex justify-content-between border rounded-3 p-2">
{{range .Badges}}
{{template "content_badge" .}}
{{end}}
</div>
</fieldset>
<fieldset class="mb-3">
<legend>Demos dropdown</legend>
<div class="container d-flex justify-content-between border rounded-3 p-2">
{{range .Dropdowns}}
{{template "content_dropdown" .}}
{{end}}
</div>
</fieldset>
<fieldset class="mb-3">
<legend>Demos List</legend>
<div class="container d-flex justify-content-between border rounded-3 p-2">
{{range .Lists}}
{{template "content_list" .}}
{{end}}
</div>
</fieldset>
<fieldset class="mb-3">
<legend>Demos nav</legend>
<div class="container border rounded-3 p-2 ">
{{range .Menus}}
<div class="container border rounded-3 p-2 mb-2">
{{template "page_nav" .}}
</div>
{{end}}
</div>
</fieldset>
</div>
{{template "page_footer"}}
</body>
</html>

View file

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html lang="en">
{{template "page_head" "Goffee"}}
<body>
<div class="container">
<form>
<fieldset>
<legend>form demos</legend>
<div class="row">
{{template "form_input" .FormText}}
{{template "form_input" .FormEmail}}
{{template "form_select" .FormSelectCity}}
{{template "form_textarea" .FormTextarea}}
{{template "form_radio" .FormRadio}}
{{template "form_checkbox" .FormCheckbox}}
</div>
</fieldset>
{{template "form_button" .FormButton}}
</form>
</div>
{{template "page_footer"}}
</body>
</html>