Files
workspace/apps/go-crm/internal/templates/layout.go
gabspereira 744868caa1 feat(go-crm): full auth, routing, middleware, and supporting infra
- Add auth handlers (signup, login, logout, account management) with bcrypt
- Add client, customer, service, scheduling, payment, question, answer handlers
- Add dashboard, monthly report, and lead pipeline pages
- Add UTF-8 middleware to force charset on HTML responses
- Add config package with env-based overrides for DB path, secrets, endpoints
- Add parser package for WhatsApp message ingestion
- Add clean-arch layers: pkg/domain, pkg/repo, pkg/usecase for leads
- Add cmd/migrate utility for DB migrations
- Add Makefile, README, run-tests.sh, and dev scripts
- Update docker-compose.yml with memory limits
- Update .air.toml to exclude DB files and stop on errors
- Update whatsapp-sync dependencies and add src/index.js entrypoint
- Add whatsme standalone WhatsApp reader app (source only)
- Untrack .opencode-sandbox/data/go-crm.db from git history
- Expand root .gitignore: ngrok, tmp dirs, sandbox DBs, compiled binaries
2026-05-23 16:55:55 -03:00

78 lines
2.8 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="/leads">Leads</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>
<li><a href="/answers">Answers</a></li>
</ul>
</nav>
<div class="container">
{{ .Content }}
</div>
</body>
</html>`))
return tmpl
}
func Get(name string) *template.Template {
mu.RLock()
defer mu.RUnlock()
return templates.Lookup(name)
}