Slice 3 step 5 (optional). The back-link on the chart page now points
explicitly at /projects/{id}/history (Verlauf sub-path) instead of
the bare /projects/{id}. Today's projects-detail.ts treats both the
same — bare and /history land on the Verlauf tab — but /history is
the explicit form, so the link keeps working if Verlauf ever stops
being the default tab.
Label flips from "Zurück zum Projekt" → "Zurück zum Verlauf" so
users see exactly where they're heading. Pairs naturally with the
Slice 1 "Als Chart anzeigen ↗" affordance: the trip is round.
Design ref: docs/design-project-chart-2026-05-09.md §8.1.
173 lines
8.7 KiB
TypeScript
173 lines
8.7 KiB
TypeScript
import { h } from "./jsx";
|
|
import { Sidebar } from "./components/Sidebar";
|
|
import { PaliadinWidget } from "./components/PaliadinWidget";
|
|
import { BottomNav } from "./components/BottomNav";
|
|
import { Footer } from "./components/Footer";
|
|
import { PWAHead } from "./components/PWAHead";
|
|
|
|
// t-paliad-177 Slice 1 — Project Timeline / Chart standalone page.
|
|
//
|
|
// Pure shell: header / controls scaffold (inert chips for the
|
|
// vertical-toggle, density and palette pickers, which Slice 3 wires
|
|
// live) + a chart host that client/projects-chart.ts mounts the SVG
|
|
// renderer into. Project metadata is loaded client-side so the same
|
|
// dist/projects-chart.html serves every {id}.
|
|
//
|
|
// Design ref: docs/design-project-chart-2026-05-09.md §8.2 + §12.
|
|
export function renderProjectsChart(): string {
|
|
return "<!DOCTYPE html>" + (
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
<meta name="theme-color" content="#BFF355" />
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
|
<PWAHead />
|
|
<title data-i18n="projects.chart.title">Projekt-Chart — Paliad</title>
|
|
<link rel="stylesheet" href="/assets/global.css" />
|
|
</head>
|
|
<body className="has-sidebar">
|
|
<Sidebar currentPath="/projects" />
|
|
<BottomNav currentPath="/projects" />
|
|
|
|
<main>
|
|
<section className="tool-page smart-timeline-chart-page">
|
|
<div className="container">
|
|
<a
|
|
id="projects-chart-back-link"
|
|
href="/projects"
|
|
className="back-link"
|
|
data-i18n="projects.chart.back"
|
|
>
|
|
← Zurück zum Verlauf
|
|
</a>
|
|
|
|
<div id="projects-chart-loading" className="entity-loading">
|
|
<p data-i18n="projects.chart.loading">Lädt…</p>
|
|
</div>
|
|
|
|
<div id="projects-chart-notfound" className="entity-empty" style="display:none">
|
|
<p data-i18n="projects.chart.notfound">Projekt nicht gefunden oder keine Berechtigung.</p>
|
|
</div>
|
|
|
|
<div id="projects-chart-body" style="display:none">
|
|
<header className="smart-timeline-chart-header">
|
|
<h1 id="projects-chart-title" />
|
|
<span id="projects-chart-meta" className="smart-timeline-chart-meta" />
|
|
</header>
|
|
|
|
<div className="smart-timeline-chart-controls" id="projects-chart-controls">
|
|
{/* Slice 1: chips render inert. Slice 3 wires them to
|
|
density / palette / zoom state. The presence keeps
|
|
the surface visually stable when controls light up. */}
|
|
<span className="chip-inert" data-i18n="projects.chart.control.layout.horizontal" title="Slice 3">
|
|
Layout: Horizontal
|
|
</span>
|
|
<span className="smart-timeline-chart-picker">
|
|
<label htmlFor="projects-chart-range" data-i18n="projects.chart.control.range.label">
|
|
Zeitraum:
|
|
</label>
|
|
<select id="projects-chart-range">
|
|
<option value="1y" data-i18n="projects.chart.range.1y">1 Jahr</option>
|
|
<option value="2y" data-i18n="projects.chart.range.2y">2 Jahre</option>
|
|
<option value="all" data-i18n="projects.chart.range.all">Alles anzeigen</option>
|
|
<option value="custom" data-i18n="projects.chart.range.custom">Eigener Bereich…</option>
|
|
</select>
|
|
</span>
|
|
<span className="smart-timeline-chart-picker smart-timeline-chart-range-custom" id="projects-chart-range-custom" style="display:none">
|
|
<label htmlFor="projects-chart-range-from" data-i18n="projects.chart.range.from">Von:</label>
|
|
<input type="date" id="projects-chart-range-from" />
|
|
<label htmlFor="projects-chart-range-to" data-i18n="projects.chart.range.to">Bis:</label>
|
|
<input type="date" id="projects-chart-range-to" />
|
|
</span>
|
|
<span className="smart-timeline-chart-picker">
|
|
<label htmlFor="projects-chart-density" data-i18n="projects.chart.control.density.label">
|
|
Dichte:
|
|
</label>
|
|
<select id="projects-chart-density">
|
|
<option value="compact" data-i18n="projects.chart.density.compact">Kompakt</option>
|
|
<option value="standard" data-i18n="projects.chart.density.standard">Standard</option>
|
|
<option value="spacious" data-i18n="projects.chart.density.spacious">Großzügig</option>
|
|
</select>
|
|
</span>
|
|
<span className="smart-timeline-chart-picker">
|
|
<label htmlFor="projects-chart-palette" data-i18n="projects.chart.control.palette.label">
|
|
Palette:
|
|
</label>
|
|
<select id="projects-chart-palette">
|
|
<option value="default" data-i18n="projects.chart.palette.default">Standard</option>
|
|
<option value="kind-coded" data-i18n="projects.chart.palette.kind_coded">Nach Ereignistyp</option>
|
|
<option value="track-coded" data-i18n="projects.chart.palette.track_coded">Nach Spur</option>
|
|
<option value="high-contrast" data-i18n="projects.chart.palette.high_contrast">Hoher Kontrast</option>
|
|
<option value="print" data-i18n="projects.chart.palette.print">Druck (S/W)</option>
|
|
</select>
|
|
</span>
|
|
<button
|
|
type="button"
|
|
id="projects-chart-copylink"
|
|
className="smart-timeline-chart-copylink"
|
|
data-i18n="projects.chart.permalink.copy"
|
|
data-i18n-title="projects.chart.permalink.title"
|
|
title="URL mit allen Filtern in die Zwischenablage kopieren"
|
|
>
|
|
🔗 Link kopieren
|
|
</button>
|
|
<details className="smart-timeline-chart-export">
|
|
<summary data-i18n="projects.chart.export.menu">
|
|
⇓ Export
|
|
</summary>
|
|
<menu className="smart-timeline-chart-export-menu">
|
|
<li>
|
|
<button type="button" id="projects-chart-export-svg" data-i18n="projects.chart.export.svg">
|
|
SVG (Vektorgrafik)
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<button type="button" id="projects-chart-export-png" data-i18n="projects.chart.export.png">
|
|
PNG (Bild, 2× HiDPI)
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<button type="button" id="projects-chart-export-print" data-i18n="projects.chart.export.print">
|
|
PDF (Drucken)
|
|
</button>
|
|
</li>
|
|
<li className="smart-timeline-chart-export-divider" />
|
|
<li>
|
|
<button type="button" id="projects-chart-export-csv" data-i18n="projects.chart.export.csv">
|
|
CSV (Excel-Tabelle)
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<button type="button" id="projects-chart-export-json" data-i18n="projects.chart.export.json">
|
|
JSON (Rohdaten)
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<button type="button" id="projects-chart-export-ics" data-i18n="projects.chart.export.ics">
|
|
iCal (.ics — Outlook / Apple)
|
|
</button>
|
|
</li>
|
|
</menu>
|
|
</details>
|
|
</div>
|
|
|
|
<div id="projects-chart-lanes-filter" className="smart-timeline-chart-lanes-filter" style="display:none" />
|
|
|
|
<div id="projects-chart-host" className="smart-timeline-chart-host" />
|
|
|
|
<p id="projects-chart-undated" className="smart-timeline-chart-undated-hint" style="display:none" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<Footer />
|
|
<PaliadinWidget />
|
|
<script src="/assets/projects-chart.js"></script>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|