Add GOTH stack CRM platform skeleton
- Go module with chi router, bcrypt - SQLite schema for 9 tables - Auth, Clients, Customers, Services, Scheduling, Payments, Q&A handlers - HTMX template layouts
This commit is contained in:
76
apps/go-crm/internal/templates/layout.go
Normal file
76
apps/go-crm/internal/templates/layout.go
Normal file
@@ -0,0 +1,76 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user