dealroom/templates/layout.templ

160 lines
6.8 KiB
Plaintext

package templates
import "dealroom/internal/model"
import "dealroom/internal/rbac"
templ Layout(profile *model.Profile, activePage string) {
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Dealspace AI</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<link rel="stylesheet" href="/static/styles.css"/>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
atlas: '#14B8A6',
'atlas-surface': 'rgba(20,184,166,0.1)',
}
}
}
}
</script>
</head>
<body class="bg-gray-950 text-gray-100 min-h-screen flex">
<!-- Sidebar -->
<aside class="w-60 bg-gray-900 border-r border-gray-800 flex flex-col fixed h-full">
<!-- Brand -->
<div class="p-4 border-b border-gray-800">
<div class="flex items-center gap-2">
<div class="w-8 h-8 rounded-lg bg-teal-500 flex items-center justify-center">
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path></svg>
</div>
<span class="font-bold text-lg">Dealspace AI</span>
</div>
<div class="mt-3 flex items-center gap-2">
<div class="w-6 h-6 rounded-full bg-teal-500/20 flex items-center justify-center text-xs font-bold text-teal-400">A</div>
<span class="text-xs text-gray-400">Apex Capital Partners</span>
</div>
</div>
<!-- Navigation -->
<nav class="flex-1 p-3 space-y-1">
@sidebarLink("/", "Dashboard", activePage == "dashboard", svgDashboard())
if rbac.IsSeller(profile.Role) {
@sidebarLink("/deals", "Deal Rooms", activePage == "deals", svgFolder())
@sidebarLink("/requests", "Request Lists", activePage == "requests", svgClipboard())
@sidebarLink("/analytics", "Analytics", activePage == "analytics", svgChart())
@sidebarLink("/contacts", "Contacts", activePage == "contacts", svgUsers())
@sidebarLink("/audit", "Audit Log", activePage == "audit", svgShield())
}
if rbac.IsBuyer(profile.Role) {
@sidebarLink("/deals", "Deal Rooms", activePage == "deals", svgFolder())
@sidebarLink("/requests", "Request Lists", activePage == "requests", svgClipboard())
}
</nav>
<!-- Atlas AI -->
<div class="p-3">
<div class="rounded-lg border border-teal-500/30 bg-teal-500/5 p-3">
<div class="flex items-center gap-2 mb-2">
<div class="w-5 h-5 rounded bg-teal-500/20 flex items-center justify-center">
<svg class="w-3 h-3 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg>
</div>
<span class="text-xs font-semibold text-teal-400">Atlas AI</span>
</div>
<p class="text-xs text-gray-500">Ask anything about your deal room documents.</p>
</div>
</div>
<!-- User -->
<div class="p-3 border-t border-gray-800">
<div class="flex items-center gap-2">
<div class="w-8 h-8 rounded-full bg-teal-500/20 flex items-center justify-center text-xs font-bold text-teal-400">
{ initials(profile.FullName) }
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium truncate">{ profile.FullName }</p>
<p class="text-xs text-gray-500 truncate">
if rbac.IsSeller(profile.Role) {
Seller
} else {
Buyer
}
</p>
</div>
<a href="/auth/logout" class="text-gray-500 hover:text-gray-300">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path></svg>
</a>
</div>
</div>
</aside>
<!-- Main Content -->
<main class="ml-60 flex-1 min-h-screen">
<div class="p-6">
{ children... }
</div>
</main>
</body>
</html>
}
func initials(name string) string {
if len(name) == 0 {
return "?"
}
result := ""
prev := ' '
for i, c := range name {
if i == 0 || prev == ' ' {
result += string(c)
}
prev = c
}
if len(result) > 2 {
result = result[:2]
}
return result
}
templ sidebarLink(href string, label string, active bool, icon templ.Component) {
<a href={ templ.SafeURL(href) }
class={ "flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition",
templ.KV("bg-teal-500/10 text-teal-400 font-medium", active),
templ.KV("text-gray-400 hover:text-gray-200 hover:bg-gray-800", !active) }>
@icon
{ label }
</a>
}
templ svgDashboard() {
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path></svg>
}
templ svgFolder() {
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"></path></svg>
}
templ svgClipboard() {
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg>
}
templ svgChart() {
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
}
templ svgUsers() {
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path></svg>
}
templ svgShield() {
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path></svg>
}