414 lines
17 KiB
HTML
414 lines
17 KiB
HTML
{{ define "demo_doc_start" }}
|
||
<!doctype html>
|
||
<html lang="en" data-vapor-shell="miami-sunset">
|
||
<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>
|
||
<svg class="icon-sprite" aria-hidden="true" focusable="false" width="0" height="0">
|
||
<symbol id="ico-select-visible" viewBox="0 0 16 16">
|
||
<rect x="2.5" y="2.5" width="11" height="11" rx="2"></rect>
|
||
<path d="M5 8.2l2 2.1 4-4.3"></path>
|
||
</symbol>
|
||
<symbol id="ico-select-filtered" viewBox="0 0 16 16">
|
||
<path d="M2.5 3h11l-4.2 4.6v3.1l-2.6 1.5V7.6L2.5 3z"></path>
|
||
<path d="M11.4 10.2h3M12.9 8.7v3"></path>
|
||
</symbol>
|
||
<symbol id="ico-clear-visible" viewBox="0 0 16 16">
|
||
<rect x="2.5" y="2.5" width="11" height="11" rx="2"></rect>
|
||
<path d="M5 8h6"></path>
|
||
</symbol>
|
||
<symbol id="ico-clear-filtered" viewBox="0 0 16 16">
|
||
<path d="M2.5 3h11L9.3 7.6v3.1l-2.6 1.5V7.6L2.5 3z"></path>
|
||
<path d="M10.5 10.2l3 3M13.5 10.2l-3 3"></path>
|
||
</symbol>
|
||
<symbol id="ico-clear-selection" viewBox="0 0 16 16">
|
||
<rect x="2.5" y="2.5" width="11" height="11" rx="2"></rect>
|
||
<path d="M5 5l6 6M11 5l-6 6"></path>
|
||
</symbol>
|
||
<symbol id="ico-run" viewBox="0 0 16 16">
|
||
<path d="M5 3.5l7 4.5-7 4.5z"></path>
|
||
</symbol>
|
||
<symbol id="ico-edit" viewBox="0 0 16 16">
|
||
<path d="M3 13l1.2-3.6L10 3.6l2.4 2.4-5.8 5.8L3 13z"></path>
|
||
<path d="M8.8 4.8l2.4 2.4"></path>
|
||
</symbol>
|
||
<symbol id="ico-remove" viewBox="0 0 16 16">
|
||
<path d="M4.2 4.2l7.6 7.6M11.8 4.2l-7.6 7.6"></path>
|
||
</symbol>
|
||
<symbol id="ico-cancel" viewBox="0 0 16 16">
|
||
<circle cx="8" cy="8" r="5.5"></circle>
|
||
<path d="M5.5 5.5l5 5M10.5 5.5l-5 5"></path>
|
||
</symbol>
|
||
<symbol id="ico-archive" viewBox="0 0 16 16">
|
||
<rect x="2.5" y="3" width="11" height="2.8" rx="1"></rect>
|
||
<path d="M3.5 5.8V13h9V5.8"></path>
|
||
<path d="M6.2 8h3.6"></path>
|
||
</symbol>
|
||
<symbol id="ico-import" viewBox="0 0 16 16">
|
||
<path d="M3 4.2v8.6h10V4.2"></path>
|
||
<path d="M8 1.8v6.2"></path>
|
||
<path d="M5.8 5.8L8 8l2.2-2.2"></path>
|
||
<path d="M5 10.2h6"></path>
|
||
</symbol>
|
||
<symbol id="ico-export" viewBox="0 0 16 16">
|
||
<path d="M3 4.2v8.6h10V4.2"></path>
|
||
<path d="M8 8V1.8"></path>
|
||
<path d="M5.8 4L8 1.8 10.2 4"></path>
|
||
<path d="M5 10.2h6"></path>
|
||
</symbol>
|
||
<symbol id="ico-export-filtered" viewBox="0 0 16 16">
|
||
<path d="M2 4.2v8.6h10.8"></path>
|
||
<path d="M7 8V1.8"></path>
|
||
<path d="M4.8 4L7 1.8 9.2 4"></path>
|
||
<path d="M10 5.1h5.2L13.1 7.5v3.1l-1.3.7V7.5z"></path>
|
||
</symbol>
|
||
<symbol id="ico-export-selected" viewBox="0 0 16 16">
|
||
<path d="M2 4.2v8.6h10.8"></path>
|
||
<path d="M7 8V1.8"></path>
|
||
<path d="M4.8 4L7 1.8 9.2 4"></path>
|
||
<rect x="10.1" y="5.2" width="5" height="5" rx="0.6"></rect>
|
||
<path d="M11.2 6.3l2.8 2.8M14 6.3l-2.8 2.8"></path>
|
||
</symbol>
|
||
<symbol id="ico-retry" viewBox="0 0 16 16">
|
||
<path d="M12.2 6A4.8 4.8 0 0 0 4.8 4.4"></path>
|
||
<path d="M5.6 2.9L4.1 4.6 5.9 5.8"></path>
|
||
<path d="M3.8 10A4.8 4.8 0 0 0 11.2 11.6"></path>
|
||
<path d="M10.4 13.1L11.9 11.4 10.1 10.2"></path>
|
||
</symbol>
|
||
<symbol id="ico-review" viewBox="0 0 16 16">
|
||
<circle cx="8" cy="8" r="5.5"></circle>
|
||
<path d="M8 7.2v3.6"></path>
|
||
<path d="M8 5.1h.01"></path>
|
||
</symbol>
|
||
<symbol id="ico-mark-review" viewBox="0 0 16 16">
|
||
<path d="M8 2.6l5.2 2v3.7c0 2.3-1.5 4.4-5.2 5.7-3.7-1.3-5.2-3.4-5.2-5.7V4.6l5.2-2z"></path>
|
||
<path d="M8 6.1v3.2"></path>
|
||
<path d="M8 11.1h.01"></path>
|
||
</symbol>
|
||
<symbol id="ico-confirm" viewBox="0 0 16 16">
|
||
<path d="M8 2.5l5 2v3.8c0 2.2-1.3 4.1-5 5.3-3.7-1.2-5-3.1-5-5.3V4.5l5-2z"></path>
|
||
<path d="M5.7 8.3l1.6 1.7 3-3.1"></path>
|
||
</symbol>
|
||
<symbol id="ico-inspect" viewBox="0 0 16 16">
|
||
<circle cx="7" cy="7" r="3.8"></circle>
|
||
<path d="M9.8 9.8l3.2 3.2"></path>
|
||
</symbol>
|
||
<symbol id="ico-scope" viewBox="0 0 16 16">
|
||
<rect x="2.5" y="2.5" width="4.2" height="4.2"></rect>
|
||
<rect x="9.3" y="2.5" width="4.2" height="4.2"></rect>
|
||
<rect x="2.5" y="9.3" width="4.2" height="4.2"></rect>
|
||
<rect x="9.3" y="9.3" width="4.2" height="4.2"></rect>
|
||
</symbol>
|
||
<symbol id="ico-queue" viewBox="0 0 16 16">
|
||
<path d="M5.4 4h8.1M5.4 8h8.1M5.4 12h8.1"></path>
|
||
<circle cx="3.2" cy="4" r="0.7"></circle>
|
||
<circle cx="3.2" cy="8" r="0.7"></circle>
|
||
<circle cx="3.2" cy="12" r="0.7"></circle>
|
||
</symbol>
|
||
</svg>
|
||
{{ 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 }}
|