The /views/{slug} runner now mounts the same FilterBar primitive that
/events and /inbox use. The saved view's filter_spec becomes the bar's
baseline, axes are picked client-side per the view's data sources so a
deadline-only view exposes deadline_status, an approval-driven view
exposes approval_viewer_role + approval_status + approval_entity_type,
etc. Universal axes (time, personal_only, sort) always render.
Per-session tweaks overlay the saved baseline without mutating the
stored row; the URL round-trips state through the bar's existing codec
so deep-links share the active narrow. "Speichern als Sicht" stays
available on user-owned views so a tweaked narrow can be forked into a
new saved view.
Shape axis is intentionally excluded from the bar — the existing
top-of-page shape chip cluster (list / cards / calendar / timeline)
already plays that role and switching now mutates the cached render
spec without re-hitting the substrate.
Empty-state hint reuses the saved filter summary as before; the bar's
onResult handler hides all shape hosts when the rows array is empty.
134 lines
7.1 KiB
TypeScript
134 lines
7.1 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";
|
|
|
|
// Custom Views shell (t-paliad-144 Phase A2). One TSX powers /views (the
|
|
// landing) and /views/{slug} (a specific view). The client bundle reads
|
|
// window.location.pathname to decide which mode to render.
|
|
//
|
|
// Hydration: client/views.ts loads the saved or system view via /api/views
|
|
// and dispatches to the matching render-shape component (list / cards /
|
|
// calendar — Q4 lock-in 2026-05-07: 3 shapes, no separate "activity").
|
|
|
|
export function renderViews(): 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" />
|
|
<PWAHead />
|
|
<title data-i18n="views.title">Ansichten — Paliad</title>
|
|
<link rel="stylesheet" href="/assets/global.css" />
|
|
</head>
|
|
<body className="has-sidebar">
|
|
<Sidebar currentPath="/views" />
|
|
<BottomNav currentPath="/views" />
|
|
|
|
<main>
|
|
<section className="tool-page">
|
|
<div className="container">
|
|
{/* Header — populated by client/views.ts from the loaded view name. */}
|
|
<div className="tool-header" id="views-header">
|
|
<div className="entity-header-row">
|
|
<div>
|
|
<h1 id="views-heading" data-i18n="views.heading">Ansichten</h1>
|
|
<p className="tool-subtitle" id="views-subtitle" data-i18n="views.subtitle">
|
|
Eigene Ansichten über Ihre Daten — Filter und Darstellung speicherbar.
|
|
</p>
|
|
</div>
|
|
<div className="views-header-actions" id="views-header-actions">
|
|
{/* Edit + delete buttons inserted by client/views.ts when on a custom view. */}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Toolbar — shape switcher (3 shapes per Q4 lock-in). */}
|
|
<div className="views-toolbar" id="views-toolbar" hidden>
|
|
<div className="agenda-chip-row" role="tablist" id="views-shape-chips" aria-label="Form">
|
|
<button type="button" className="agenda-chip" data-shape="list" role="tab" data-i18n="views.shape.list">Liste</button>
|
|
<button type="button" className="agenda-chip" data-shape="cards" role="tab" data-i18n="views.shape.cards">Karten</button>
|
|
<button type="button" className="agenda-chip" data-shape="calendar" role="tab" data-i18n="views.shape.calendar">Kalender</button>
|
|
<button type="button" className="agenda-chip" data-shape="timeline" role="tab" data-i18n="views.shape.timeline">Timeline</button>
|
|
</div>
|
|
<div className="views-toolbar-spacer" />
|
|
<a href="#" className="btn-secondary btn-small" id="views-save-as" data-i18n="views.save_as" hidden>
|
|
Als Ansicht speichern
|
|
</a>
|
|
</div>
|
|
|
|
{/* Filter bar host — t-paliad-211. mountFilterBar appends its
|
|
own toolbar element here; the saved view's filter_spec
|
|
becomes the bar's baseline, axes are chosen client-side
|
|
per the view's data sources. */}
|
|
<div className="views-filter-bar" id="views-filter-bar" hidden />
|
|
|
|
|
|
{/* Empty / onboarding state — shown on bare /views with no saved views. */}
|
|
<div className="views-onboarding" id="views-onboarding" hidden>
|
|
<h2 data-i18n="views.onboarding.title">Eigene Ansichten — was ist das?</h2>
|
|
<p data-i18n="views.onboarding.body">
|
|
Eine Ansicht ist eine gespeicherte Filterkombination — z. B. „Fristen meiner Projekte in den nächsten 14 Tagen“.
|
|
Ansichten erscheinen als eigene Buttons in der Sidebar.
|
|
</p>
|
|
<div className="views-onboarding-actions">
|
|
<a href="/views/new" className="btn-primary btn-cta-lime" data-i18n="views.onboarding.create">
|
|
Beispiel-Ansicht erstellen
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Inaccessible-projects toast (Q17 attribution). */}
|
|
<div className="views-toast" id="views-toast" hidden>
|
|
<span className="views-toast-text" id="views-toast-text" />
|
|
<button type="button" className="views-toast-close" id="views-toast-close" aria-label="Close">×</button>
|
|
</div>
|
|
|
|
{/* Loading + error + empty states (mutually exclusive). */}
|
|
<div className="views-loading" id="views-loading" data-i18n="views.loading">Lädt …</div>
|
|
<div className="views-error" id="views-error" hidden>
|
|
<p id="views-error-message" />
|
|
<a href="/views" className="btn-secondary btn-small" data-i18n="views.error.back">Zurück zur Ansichten-Übersicht</a>
|
|
</div>
|
|
<div className="views-empty" id="views-empty" hidden>
|
|
<p data-i18n="views.empty.title">Keine Einträge gefunden.</p>
|
|
<p className="views-empty-hint" id="views-empty-hint" />
|
|
</div>
|
|
|
|
{/* Render targets — only the active shape is visible. */}
|
|
<div className="views-shape-host views-shape-list" id="views-shape-list" hidden />
|
|
<div className="views-shape-host views-shape-cards" id="views-shape-cards" hidden />
|
|
<div className="views-shape-host views-shape-calendar" id="views-shape-calendar" hidden />
|
|
<div className="views-shape-host views-shape-timeline" id="views-shape-timeline" hidden>
|
|
{/* CV-chart caveat banner — design §13.4: ViewService
|
|
doesn't run the fristenrechner calculator, so Custom
|
|
Views show actual events only. One-time-per-session
|
|
dismissible (sessionStorage). */}
|
|
<div className="views-timeline-caveat" id="views-timeline-caveat" hidden>
|
|
<span data-i18n="views.timeline.caveat.body">
|
|
Custom Views zeigen nur eingetretene Ereignisse. Für prognostizierte Fristen das Projekt-Chart öffnen.
|
|
</span>
|
|
<button
|
|
type="button"
|
|
className="views-timeline-caveat-close"
|
|
id="views-timeline-caveat-close"
|
|
aria-label="Schließen"
|
|
>×</button>
|
|
</div>
|
|
<div id="views-timeline-chart-host" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<Footer />
|
|
<PaliadinWidget />
|
|
</main>
|
|
|
|
<script src="/assets/views.js" defer />
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|