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.
151 lines
8.0 KiB
TypeScript
151 lines
8.0 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";
|
|
|
|
export function renderDeadlinesDetail(): 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="deadlines.detail.title">Frist — Paliad</title>
|
|
<link rel="stylesheet" href="/assets/global.css" />
|
|
</head>
|
|
<body className="has-sidebar">
|
|
<Sidebar currentPath="/events?type=deadline" />
|
|
<BottomNav currentPath="/events?type=deadline" />
|
|
|
|
<main>
|
|
<section className="tool-page">
|
|
<div className="container">
|
|
<a href="/events?type=deadline" className="back-link" data-i18n="deadlines.detail.back">← Zurück zur Fristenübersicht</a>
|
|
|
|
<div id="deadline-loading" className="entity-loading">
|
|
<p data-i18n="deadlines.detail.loading">Lädt…</p>
|
|
</div>
|
|
|
|
<div id="deadline-notfound" className="entity-empty" style="display:none">
|
|
<p data-i18n="deadlines.detail.notfound">Frist nicht gefunden oder keine Berechtigung.</p>
|
|
</div>
|
|
|
|
<div id="deadline-body" style="display:none">
|
|
<header className="entity-detail-header">
|
|
<div className="entity-detail-title-row">
|
|
<div className="entity-detail-title-col">
|
|
<h1 id="deadline-title-display" />
|
|
<input type="text" id="deadline-title-edit" className="entity-title-input" style="display:none" />
|
|
<div className="entity-detail-meta">
|
|
<span id="deadline-due-chip" className="frist-due-chip" />
|
|
<span id="deadline-status-chip" className="entity-status-chip" />
|
|
<span id="deadline-pending-approval-badge" className="approval-pending-badge" style="display:none" data-i18n="approvals.pending.badge" title="">
|
|
Wartet auf Genehmigung
|
|
</span>
|
|
<a id="deadline-project-link" className="entity-ref" href="#" />
|
|
<select id="deadline-project-edit" className="entity-ref-select" style="display:none" />
|
|
</div>
|
|
</div>
|
|
<div className="entity-detail-actions">
|
|
<button id="deadline-complete-btn" type="button" className="btn-primary btn-cta-lime btn-small" data-i18n="deadlines.detail.complete">
|
|
Als erledigt markieren
|
|
</button>
|
|
<button id="deadline-reopen-btn" type="button" className="btn-primary btn-cta-lime btn-small" style="display:none" data-i18n="deadlines.detail.reopen">
|
|
Wieder öffnen
|
|
</button>
|
|
<button id="deadline-withdraw-btn" type="button" className="btn-secondary btn-small" style="display:none" data-i18n="approvals.withdraw.cta">
|
|
Genehmigungsanfrage zurückziehen
|
|
</button>
|
|
<button id="deadline-edit-btn" className="btn-icon" type="button" aria-label="Bearbeiten" data-i18n-title="deadlines.detail.edit" title="Bearbeiten">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
|
</svg>
|
|
</button>
|
|
<button id="deadline-save-btn" className="btn-primary btn-cta-lime" type="button" style="display:none" data-i18n="deadlines.detail.save">
|
|
Speichern
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<section className="entity-tab-panel frist-detail-panel">
|
|
<dl className="frist-detail-list">
|
|
<dt data-i18n="deadlines.detail.due">Fälligkeitsdatum</dt>
|
|
<dd>
|
|
<span id="deadline-due-display" />
|
|
<input type="date" id="deadline-due-edit" style="display:none" />
|
|
</dd>
|
|
|
|
<dt data-i18n="deadlines.detail.rule">Regel</dt>
|
|
<dd id="deadline-rule-display">—</dd>
|
|
|
|
<dt data-i18n="deadlines.field.event_type">Typ (optional)</dt>
|
|
<dd>
|
|
<span id="deadline-event-types-display">—</span>
|
|
<div id="deadline-event-types-edit" className="event-type-picker-host" style="display:none" />
|
|
</dd>
|
|
|
|
<dt data-i18n="deadlines.detail.source">Quelle</dt>
|
|
<dd id="deadline-source-display" />
|
|
|
|
<dt data-i18n="deadlines.detail.notes">Notizen</dt>
|
|
<dd>
|
|
<span id="deadline-notes-display" />
|
|
<textarea id="deadline-notes-edit" rows={3} style="display:none" />
|
|
</dd>
|
|
|
|
<dt data-i18n="deadlines.detail.created">Angelegt</dt>
|
|
<dd id="deadline-created-display" />
|
|
|
|
<dt id="deadline-completed-row-label" data-i18n="deadlines.detail.completed" style="display:none">
|
|
Erledigt am
|
|
</dt>
|
|
<dd id="deadline-completed-display" style="display:none" />
|
|
</dl>
|
|
</section>
|
|
|
|
<section className="frist-notes-section">
|
|
<h2 className="frist-section-heading" data-i18n="notes.section.title">Notizen</h2>
|
|
<div id="notes-container" className="notiz-container" data-parent-type="deadline" />
|
|
</section>
|
|
|
|
<div className="entity-detail-footer" id="deadline-delete-wrap" style="display:none">
|
|
<button id="deadline-delete-btn" className="btn-danger" type="button" data-i18n="deadlines.detail.delete">
|
|
Frist löschen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="modal-overlay" id="deadline-delete-modal" style="display:none">
|
|
<div className="modal-card">
|
|
<div className="modal-header">
|
|
<h2 data-i18n="deadlines.detail.delete.confirm.title">Frist wirklich löschen?</h2>
|
|
<button className="modal-close" id="deadline-delete-modal-close" type="button">×</button>
|
|
</div>
|
|
<p data-i18n="deadlines.detail.delete.confirm.body">
|
|
Die Frist wird endgültig entfernt. Der Eintrag im Verlauf der Akte bleibt erhalten.
|
|
</p>
|
|
<div className="form-actions">
|
|
<button type="button" className="btn-cancel" id="deadline-delete-modal-cancel" data-i18n="deadlines.detail.delete.confirm.cancel">Abbrechen</button>
|
|
<button type="button" className="btn-danger" id="deadline-delete-modal-confirm" data-i18n="deadlines.detail.delete.confirm.ok">Löschen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<Footer />
|
|
<PaliadinWidget />
|
|
<script src="/assets/deadlines-detail.js"></script>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|