Files
2026-03-01 22:26:50 +03:00

414 lines
17 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{ 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 }}