The inline Paliadin chat surface — reachable from every authenticated
page, replacing the standalone /paliadin route as the primary entry
point. The standalone page survives as the dedicated full-screen mode
(the drawer's "↗ fullscreen" action links to it).
Components:
- frontend/src/components/PaliadinWidget.tsx — emits the floating
trigger button (bottom-right, lime ✨, owner-revealed by JS), a
scrim, and the right-edge slide-out drawer with header (reset /
fullscreen / close), context chip, message stream, empty-state
starter list, and textarea+send form. Loads /assets/paliadin-widget.js.
- frontend/src/client/paliadin-widget.ts — runtime. /api/me probe
reveals the trigger when caller matches PaliadinOwnerEmail (with
optional is_paliadin_owner flag fast-path); Cmd+J / Ctrl+J shortcut
toggles open/close (Cmd+K stays reserved for global search per
client/search.ts). Uses computePaliadinContext() (Slice B) per send
so route + entity + selection flow into every turn. SSE consumer
writes assistant bubbles; localStorage persists per-session history.
- frontend/src/client/paliadin-starters.ts — per-route starter prompt
registry. 14 routes covered (dashboard, projects.*, deadlines.*,
appointments.*, agenda, events, inbox, tools.*, glossary, courts) +
a _default fallback. Bilingual (DE/EN); prompts ending in `: ` seed
the textarea for the user to finish; fully-formed prompts auto-send.
- 39 authenticated TSX pages get a `<PaliadinWidget />` element after
`<Footer />` via a mechanical pass. paliadin.tsx (the standalone)
is intentionally excluded — its dedicated UI is the widget's
fullscreen escape hatch, not a place to overlay another widget.
- frontend/build.ts registers the new bundle.
- frontend/src/styles/global.css gains ~280 lines of widget CSS
(trigger / scrim / drawer / header / context-chip / messages /
bubbles / starters / form / send-btn) using only existing tokens.
Mobile (≤640px): drawer goes full-screen; trigger lifts above
bottom-nav slots.
- 11 new i18n keys × 2 langs = 22 entries under paliadin.widget.*.
Visibility predicate (paliadin-context.shouldSendContext) hides the
widget on /paliadin, /login, /onboarding. Owner-only gate stays on
PaliadinOwnerEmail.
Build clean: i18n 1955 → 1966 keys, IIFE-wrapped 218KB bundle, go test
green.
Refs: docs/design-paliadin-inline-2026-05-08.md §3, §5.
162 lines
8.2 KiB
TypeScript
162 lines
8.2 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";
|
|
|
|
// Template detail page. Shows template metadata + list of existing
|
|
// instances + CTA to create a new instance. Clicking an instance takes
|
|
// the user to /checklisten/instances/{id} where the interactive
|
|
// checkboxes live.
|
|
export function renderChecklistsDetail(): 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="checklisten.title">Checkliste — Paliad</title>
|
|
<link rel="stylesheet" href="/assets/global.css" />
|
|
</head>
|
|
<body className="has-sidebar">
|
|
<Sidebar currentPath="/checklists" />
|
|
<BottomNav currentPath="/checklists" />
|
|
|
|
<main>
|
|
<section className="tool-page">
|
|
<div className="container">
|
|
<a href="/checklists" className="checklist-back">
|
|
<span className="checklist-back-arrow">←</span>
|
|
<span data-i18n="checklisten.back">Zurück zur Übersicht</span>
|
|
</a>
|
|
|
|
<div className="tool-header checklist-detail-header">
|
|
<div className="checklist-detail-head-row">
|
|
<div>
|
|
<h1 id="checklist-title"> </h1>
|
|
<p className="tool-subtitle" id="checklist-subtitle"> </p>
|
|
<dl className="checklist-meta" id="checklist-meta" />
|
|
</div>
|
|
<div className="checklist-actions">
|
|
<button type="button" id="btn-new-instance" className="btn-primary btn-cta-lime" data-i18n="checklisten.newInstance">
|
|
Neue Instanz
|
|
</button>
|
|
<button type="button" id="btn-feedback" className="btn-cta-lime btn-outline">
|
|
<span data-i18n="checklisten.feedback.btn">Feedback</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<section className="checklist-instances-section">
|
|
<h2 data-i18n="checklisten.instances.heading">Instanzen</h2>
|
|
<p className="tool-subtitle" data-i18n="checklisten.instances.sub">
|
|
Jede Instanz hat ihren eigenen Fortschritt und kann optional an eine Akte gehängt werden.
|
|
</p>
|
|
|
|
<div id="instances-loading" className="entity-loading">
|
|
<p data-i18n="checklisten.instances.loading">Lädt…</p>
|
|
</div>
|
|
|
|
<div id="instances-empty" className="entity-events-empty" style="display:none">
|
|
<p data-i18n="checklisten.instances.empty">
|
|
Noch keine Instanzen. Klicken Sie auf „Neue Instanz“, um zu beginnen.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="entity-table-wrap" id="instances-tablewrap" style="display:none">
|
|
<table className="entity-table">
|
|
<thead>
|
|
<tr>
|
|
<th data-i18n="checklisten.instances.col.name">Name</th>
|
|
<th data-i18n="checklisten.instances.col.progress">Fortschritt</th>
|
|
<th data-i18n="checklisten.instances.col.akte">Akte</th>
|
|
<th data-i18n="checklisten.instances.col.created">Angelegt</th>
|
|
<th />
|
|
</tr>
|
|
</thead>
|
|
<tbody id="instances-body" />
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<div className="checklist-print-footer">
|
|
<p className="checklist-disclaimer" data-i18n="checklisten.disclaimer">
|
|
Hinweis: Diese Checklisten dienen als Gedächtnisstütze und ersetzen keine Prüfung im Einzelfall. Maßgeblich sind die jeweils geltenden Verfahrensregeln.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
{/* Neue Instanz modal */}
|
|
<div className="modal-overlay" id="new-instance-modal" style="display:none">
|
|
<div className="modal-card">
|
|
<div className="modal-header">
|
|
<h2 data-i18n="checklisten.newInstance.title">Neue Checklisten-Instanz</h2>
|
|
<button className="modal-close" id="new-instance-close" type="button">×</button>
|
|
</div>
|
|
<form id="new-instance-form" autocomplete="off">
|
|
<div className="form-field">
|
|
<label htmlFor="new-instance-name" data-i18n="checklisten.newInstance.name">Name</label>
|
|
<input type="text" id="new-instance-name" required maxLength={200} />
|
|
<p className="form-hint" data-i18n="checklisten.newInstance.name.hint">z.B. „Müller v. Schmidt — SoC“.</p>
|
|
</div>
|
|
<div className="form-field">
|
|
<label htmlFor="new-instance-project" data-i18n="checklisten.newInstance.akte">Akte (optional)</label>
|
|
<select id="new-instance-project">
|
|
<option value="" data-i18n="checklisten.newInstance.akte.none">— keine Akte —</option>
|
|
</select>
|
|
<p className="form-hint" data-i18n="checklisten.newInstance.akte.hint">Wenn verknüpft, sehen Bürokollegen die Instanz.</p>
|
|
</div>
|
|
<div className="form-actions">
|
|
<button type="button" className="btn-cancel" id="new-instance-cancel" data-i18n="checklisten.newInstance.cancel">Abbrechen</button>
|
|
<button type="submit" className="btn-primary btn-cta-lime" data-i18n="checklisten.newInstance.submit">Anlegen</button>
|
|
</div>
|
|
<p className="form-msg" id="new-instance-msg" />
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Feedback modal */}
|
|
<div className="modal-overlay" id="feedback-modal" style="display:none">
|
|
<div className="modal-card">
|
|
<div className="modal-header">
|
|
<h2 data-i18n="checklisten.feedback.title">Feedback zur Checkliste</h2>
|
|
<button className="modal-close" id="modal-close" type="button">×</button>
|
|
</div>
|
|
<form id="feedback-form">
|
|
<div className="form-field">
|
|
<label htmlFor="feedback-type" data-i18n="checklisten.feedback.type">Art</label>
|
|
<select id="feedback-type" required>
|
|
<option value="error" data-i18n="checklisten.feedback.error">Fehler gefunden</option>
|
|
<option value="missing" data-i18n="checklisten.feedback.missing">Fehlender Punkt</option>
|
|
<option value="suggestion" data-i18n="checklisten.feedback.suggestion">Verbesserungsvorschlag</option>
|
|
<option value="other" data-i18n="checklisten.feedback.other">Sonstiges</option>
|
|
</select>
|
|
</div>
|
|
<div className="form-field">
|
|
<label htmlFor="feedback-message" data-i18n="checklisten.feedback.message">Nachricht</label>
|
|
<textarea id="feedback-message" rows={4} required />
|
|
</div>
|
|
<div className="form-actions">
|
|
<button type="button" className="btn-cancel" id="modal-cancel" data-i18n="checklisten.feedback.cancel">Abbrechen</button>
|
|
<button type="submit" className="btn-submit" data-i18n="checklisten.feedback.submit">Absenden</button>
|
|
</div>
|
|
<p className="form-msg" id="feedback-msg" />
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<Footer />
|
|
<PaliadinWidget />
|
|
<script src="/assets/checklists-detail.js"></script>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|