312 lines
12 KiB
HTML
312 lines
12 KiB
HTML
{{ define "demo_doc_start" }}
|
||
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>{{ .Title }}</title>
|
||
<link rel="stylesheet" href="/static/css/app.css">
|
||
</head>
|
||
<body>
|
||
{{ template "demo_nav" . }}
|
||
{{ template "demo_app_shell" . }}
|
||
<main class="page">
|
||
{{ end }}
|
||
|
||
{{ define "demo_doc_end" }}
|
||
</main>
|
||
<script src="/static/js/app.js" defer></script>
|
||
</body>
|
||
</html>
|
||
{{ end }}
|
||
|
||
{{ define "demo_masthead" }}
|
||
<header class="masthead">
|
||
<p class="label">{{ index . "label" }}</p>
|
||
<h1>{{ index . "title" }}</h1>
|
||
<p class="lead">{{ index . "lead" }}</p>
|
||
{{ if index . "back_url" }}
|
||
<p class="meta" style="margin-top:10px;"><a class="text-link" href="{{ index . "back_url" }}">{{ index . "back_text" }}</a></p>
|
||
{{ end }}
|
||
{{ if index . "links_html" }}
|
||
{{ index . "links_html" }}
|
||
{{ end }}
|
||
</header>
|
||
{{ end }}
|
||
|
||
{{ define "demo_app_shell" }}
|
||
<header class="app-shell" id="app-shell">
|
||
<div class="app-shell-inner">
|
||
<div>
|
||
<p class="app-shell-kicker">Demo Application Shell</p>
|
||
<h1 class="app-shell-title">Universal Operations Console</h1>
|
||
<p class="app-shell-subtitle">Reference shell for server-rendered Go web apps using shared design-code contracts.</p>
|
||
</div>
|
||
<div class="app-shell-statuses" aria-label="Application status">
|
||
<span class="shell-pill shell-pill-env">ENV: demo</span>
|
||
<span class="shell-pill shell-pill-db">DB: connected</span>
|
||
<span class="shell-pill shell-pill-user">operator@demo · admin</span>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
{{ end }}
|
||
|
||
{{ define "base.html" }}
|
||
{{ template "demo_doc_start" . }}
|
||
<header class="masthead" id="home-overview">
|
||
<p class="label">Submodule-First Design Kit</p>
|
||
<h1>{{ .Title }}</h1>
|
||
<p class="lead">A universal design-code workspace for Go web applications: reusable rules, demo-first UI patterns, and canonical development contracts for AI-assisted implementation.</p>
|
||
<div class="button-demo-row" style="margin-top:12px;">
|
||
{{ range .Patterns }}
|
||
{{ if .Link }}
|
||
<a class="chip-link" href="{{ .Link }}">{{ .Name }}</a>
|
||
{{ end }}
|
||
{{ end }}
|
||
</div>
|
||
</header>
|
||
|
||
<section class="panel" id="home-design-approach">
|
||
<div class="panel-head"><h2>Design Approach</h2></div>
|
||
<div class="grid">
|
||
<article class="card">
|
||
<h3>Contract First</h3>
|
||
<p>UI behavior is defined as explicit contracts (filters, pagination, modal steps, timeline grouping) before implementation details. Demo pages act as executable specs.</p>
|
||
</article>
|
||
<article class="card">
|
||
<h3>Server-Rendered by Default</h3>
|
||
<p>Patterns target Go server-rendered apps first (net/http or Gin with templates). Interactivity is additive and must preserve deterministic URL/query contracts.</p>
|
||
</article>
|
||
<article class="card">
|
||
<h3>Reusable, Not Branded</h3>
|
||
<p>Shared patterns standardize behavior and structure while leaving visual branding, domain terminology, and business logic to each host project.</p>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" id="home-workflow">
|
||
<div class="panel-head"><h2>Development Workflow Standard</h2></div>
|
||
<div class="timeline-cards">
|
||
<article class="timeline-card">
|
||
<div class="timeline-card-top"><h3>1. Describe the contract</h3></div>
|
||
<p class="meta">Update Bible + pattern contract first: routes, query params, states, edge cases, and UI semantics.</p>
|
||
</article>
|
||
<article class="timeline-card">
|
||
<div class="timeline-card-top"><h3>2. Implement in demo</h3></div>
|
||
<p class="meta">Build the pattern in the demo app as a live reference page with realistic state transitions and test coverage.</p>
|
||
</article>
|
||
<article class="timeline-card">
|
||
<div class="timeline-card-top"><h3>3. Publish as bundle</h3></div>
|
||
<p class="meta">Encode reusable docs and templates in the design kit and expose them as bundles for host repositories.</p>
|
||
</article>
|
||
<article class="timeline-card">
|
||
<div class="timeline-card-top"><h3>4. Apply in host repos</h3></div>
|
||
<p class="meta">Use the sync workflow to plan and apply changes, then adapt domain-specific rules without breaking canonical UI contracts.</p>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" id="home-standardizes">
|
||
<div class="panel-head"><h2>What the Demo Standardizes</h2></div>
|
||
<div class="chip-row">
|
||
<span class="chip">URL-driven filters</span>
|
||
<span class="chip">Server-side pagination</span>
|
||
<span class="chip">Bulk selection semantics</span>
|
||
<span class="chip">Modal state machines</span>
|
||
<span class="chip">Import preview / confirm</span>
|
||
<span class="chip">CSV export behavior</span>
|
||
<span class="chip">Operator tooling dashboards</span>
|
||
<span class="chip">Timeline card grouping</span>
|
||
<span class="chip">Drilldown UX</span>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" id="home-anti-patterns">
|
||
<div class="panel-head"><h2>Anti-Patterns (Do Not Implement)</h2></div>
|
||
<div class="grid">
|
||
<article class="card">
|
||
<h3>Page-local filters on paginated tables</h3>
|
||
<p>Do not filter only the currently rendered page slice. Filters must apply to the full dataset/query scope before pagination.</p>
|
||
</article>
|
||
<article class="card">
|
||
<h3>Nested modals</h3>
|
||
<p>Do not open one modal from another modal. Use a single modal state machine with explicit stages (edit, confirm, done).</p>
|
||
</article>
|
||
<article class="card">
|
||
<h3>Implicit export scope</h3>
|
||
<p>Do not export without clear scope selection when ambiguity exists (selected, filtered, all). Make the scope explicit in UI and request.</p>
|
||
</article>
|
||
<article class="card">
|
||
<h3>Undocumented UI contracts</h3>
|
||
<p>Do not implement new interaction behavior without updating the design code (Bible, pattern contract, and demo reference page).</p>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" id="home-bundles">
|
||
<div class="panel-head">
|
||
<h2>Bundles</h2>
|
||
<a href="/healthz" class="pill">healthz</a>
|
||
</div>
|
||
<div class="grid">
|
||
{{ range .Bundles }}
|
||
<article class="card">
|
||
<div class="row">
|
||
<h3>{{ .Name }}</h3>
|
||
<span class="status status-{{ .Status }}">{{ .Status }}</span>
|
||
</div>
|
||
<p>{{ .Summary }}</p>
|
||
<p class="meta"><code>{{ .Bundle }}</code></p>
|
||
{{ if .Link }}
|
||
<p class="meta"><a class="text-link" href="{{ .Link }}">Open demo</a></p>
|
||
{{ end }}
|
||
</article>
|
||
{{ end }}
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" id="home-roadmap">
|
||
<div class="panel-head">
|
||
<h2>Pattern Roadmap</h2>
|
||
</div>
|
||
<div class="grid">
|
||
{{ range .Patterns }}
|
||
<article class="card">
|
||
<div class="row">
|
||
<h3>{{ .Name }}</h3>
|
||
<span class="status status-{{ .Status }}">{{ .Status }}</span>
|
||
</div>
|
||
<p>{{ .Summary }}</p>
|
||
<p class="meta"><code>{{ .Bundle }}</code></p>
|
||
</article>
|
||
{{ end }}
|
||
</div>
|
||
</section>
|
||
{{ template "demo_doc_end" . }}
|
||
{{ end }}
|
||
|
||
{{ define "demo_nav" }}
|
||
<nav class="demo-topnav" aria-label="Demo navigation">
|
||
<div class="demo-topnav-inner">
|
||
<a class="demo-topnav-brand {{ if eq .CurrentPath "/" }}active{{ end }}" href="/">Demo Catalog</a>
|
||
<a class="demo-topnav-link {{ if eq .CurrentPath "/patterns/table" }}active{{ end }}" href="/patterns/table">Table</a>
|
||
<a class="demo-topnav-link {{ if eq .CurrentPath "/patterns/controls" }}active{{ end }}" href="/patterns/controls">Controls</a>
|
||
<a class="demo-topnav-link {{ if eq .CurrentPath "/patterns/modals" }}active{{ end }}" href="/patterns/modals">Modals</a>
|
||
<a class="demo-topnav-link {{ if eq .CurrentPath "/patterns/io" }}active{{ end }}" href="/patterns/io">Import/Export</a>
|
||
<a class="demo-topnav-link {{ if eq .CurrentPath "/patterns/forms" }}active{{ end }}" href="/patterns/forms">Forms</a>
|
||
<a class="demo-topnav-link {{ if eq .CurrentPath "/patterns/operator-tools" }}active{{ end }}" href="/patterns/operator-tools">Operator Tools</a>
|
||
<a class="demo-topnav-link {{ if eq .CurrentPath "/patterns/timeline" }}active{{ end }}" href="/patterns/timeline">Timeline</a>
|
||
</div>
|
||
</nav>
|
||
{{ end }}
|
||
|
||
{{ define "table_pattern.html" }}
|
||
{{ template "demo_doc_start" . }}
|
||
{{ template "demo_masthead" (dict "label" "Pattern Demo" "title" .Title "lead" "Server-side filtering and pagination. Filters apply to the full dataset before pagination." "back_url" "/" "back_text" "← Back to catalog") }}
|
||
|
||
<section class="panel panel-composite" id="table-module">
|
||
<div class="panel-subsection" id="table-filters">
|
||
<div class="panel-head">
|
||
<h2>Filters</h2>
|
||
<div class="button-demo-row" style="margin-top:0;">
|
||
<a href="/patterns/table#table-filters" class="btn btn-ghost btn-pair">Reset</a>
|
||
<button class="btn btn-primary btn-pair" form="table-filters-form" type="submit">Apply</button>
|
||
</div>
|
||
</div>
|
||
<form id="table-filters-form" class="filters" method="get" action="/patterns/table#table-filters">
|
||
<label>
|
||
Search
|
||
<input type="text" name="q" value="{{ .Filters.Query }}" placeholder="name / owner / status" />
|
||
</label>
|
||
<label>
|
||
Category
|
||
<select name="category">
|
||
<option value="">All</option>
|
||
{{ range .Categories }}
|
||
<option value="{{ . }}" {{ if eq $.Filters.Category . }}selected{{ end }}>{{ . }}</option>
|
||
{{ end }}
|
||
</select>
|
||
</label>
|
||
<label>
|
||
Status
|
||
<select name="status">
|
||
<option value="">All</option>
|
||
{{ range .Statuses }}
|
||
<option value="{{ . }}" {{ if eq $.Filters.Status . }}selected{{ end }}>{{ . }}</option>
|
||
{{ end }}
|
||
</select>
|
||
</label>
|
||
<label>
|
||
Rows per page
|
||
<select name="per_page">
|
||
{{ range .PerPageOpts }}
|
||
<option value="{{ . }}" {{ if eq $.PerPage . }}selected{{ end }}>{{ . }}</option>
|
||
{{ end }}
|
||
</select>
|
||
</label>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="panel-subsection panel-subsection-divider" id="table-list">
|
||
<div class="panel-head">
|
||
<h2>Canonical List Page</h2>
|
||
<div class="meta">
|
||
{{ if gt .Pager.TotalItems 0 }}
|
||
Showing {{ .Pager.From }}–{{ .Pager.To }} of {{ .Pager.TotalItems }}
|
||
{{ else }}
|
||
Showing 0 of 0
|
||
{{ end }}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-wrap">
|
||
<table class="ui-table">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Name</th>
|
||
<th>Category</th>
|
||
<th class="status-col">Status</th>
|
||
<th>Owner</th>
|
||
<th>Updated</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{{ if .Rows }}
|
||
{{ range .Rows }}
|
||
<tr>
|
||
<td>{{ .ID }}</td>
|
||
<td>{{ .Name }}</td>
|
||
<td>{{ .Category }}</td>
|
||
<td class="status-col"><span class="status status-{{ .Status }}">{{ .Status }}</span></td>
|
||
<td>{{ .Owner }}</td>
|
||
<td>{{ .Updated }}</td>
|
||
</tr>
|
||
{{ end }}
|
||
{{ else }}
|
||
<tr>
|
||
<td colspan="6" class="empty-cell">No rows match current filters.</td>
|
||
</tr>
|
||
{{ end }}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
{{ if gt .Pager.TotalPages 1 }}
|
||
<nav class="pager pager-dots" aria-label="Pagination">
|
||
{{ range .Pager.Links }}
|
||
{{ if .Ellipsis }}
|
||
<span class="ellipsis" aria-hidden="true">…</span>
|
||
{{ else if .Current }}
|
||
<a class="current" aria-current="page" href="{{ .URL }}#table-list" aria-label="Page {{ .Label }}, current">{{ .Label }}</a>
|
||
{{ else }}
|
||
<a href="{{ .URL }}#table-list" aria-label="Go to page {{ .Label }}">{{ .Label }}</a>
|
||
{{ end }}
|
||
{{ end }}
|
||
</nav>
|
||
{{ end }}
|
||
</div>
|
||
</section>
|
||
{{ template "demo_doc_end" . }}
|
||
{{ end }}
|