- Go module with chi router, bcrypt - SQLite schema for 9 tables - Auth, Clients, Customers, Services, Scheduling, Payments, Q&A handlers - HTMX template layouts
76 lines
2.7 KiB
Go
76 lines
2.7 KiB
Go
package templates
|
|
|
|
import (
|
|
"html/template"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
templates *template.Template
|
|
mu sync.RWMutex
|
|
)
|
|
|
|
func Init() {
|
|
templates = template.Must(template.New("").ParseGlob("internal/templates/*.html"))
|
|
}
|
|
|
|
func Layout(title, content string) *template.Template {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
|
|
tmpl := template.Must(template.New("layout.html").Parse(`<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{ .Title }}</title>
|
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
|
<style>
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; }
|
|
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
nav { background: white; border-bottom: 1px solid #e0e0e0; padding: 1rem 0; margin-bottom: 2rem; }
|
|
nav ul { list-style: none; display: flex; gap: 1.5rem; max-width: 1200px; margin: 0 auto; padding: 0 20px; }
|
|
nav a { color: #333; text-decoration: none; }
|
|
nav a:hover { color: #007bff; }
|
|
.card { background: white; border-radius: 8px; padding: 1.5rem; margin-bottom: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
.btn { display: inline-block; padding: 0.5rem 1rem; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; }
|
|
.btn:hover { background: #0056b3; }
|
|
.btn-secondary { background: #6c757d; }
|
|
.btn-danger { background: #dc3545; }
|
|
table { width: 100%; border-collapse: collapse; }
|
|
th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #e0e0e0; }
|
|
th { background: #f8f9fa; font-weight: 600; }
|
|
form { display: flex; flex-direction: column; gap: 1rem; max-width: 500px; }
|
|
input, textarea, select { padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px; width: 100%; }
|
|
textarea { min-height: 100px; }
|
|
.error { color: #dc3545; }
|
|
.success { color: #28a745; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav>
|
|
<ul>
|
|
<li><a href="/">Home</a></li>
|
|
<li><a href="/clients">Clients</a></li>
|
|
<li><a href="/customers">Customers</a></li>
|
|
<li><a href="/services">Services</a></li>
|
|
<li><a href="/scheduling">Scheduling</a></li>
|
|
<li><a href="/payments">Payments</a></li>
|
|
<li><a href="/questions">Questions</a></li>
|
|
</ul>
|
|
</nav>
|
|
<div class="container">
|
|
{{ template "content" . }}
|
|
</div>
|
|
</body>
|
|
</html>`))
|
|
|
|
return tmpl
|
|
}
|
|
|
|
func Get(name string) *template.Template {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return templates.Lookup(name)
|
|
} |