feat(submissions): t-paliad-370 S2 — editor draft-meta lifted into a header toolbar
docforge UX slice S2 (PRD docs/plans/prd-docforge-ux-2026-06-01.md §3). De-densifies the draft editor (G1-d): draft management + template controls move out of the variable sidebar into a horizontal, wrap-friendly header strip above the working grid. The sidebar then holds only the fill-in work — 'Aus Projekt importieren', parties, and the variable fields. - submission-draft.tsx: new .submission-draft-toolbar strip carrying the switcher (+ '+ Neuer Entwurf'), name (+ Löschen), Stichwort (+ hint), Vorlagenbasis picker, language toggle, fallback notice + savestatus. The 'Als .docx exportieren' button stays top-right in the header. - New 👁 Vorschau button next to the base picker (id submission-draft-preview-base-btn), disabled 'Bald verfügbar' stub — S3 wires it to the truthful base-preview modal (PRD §2). - global.css: .submission-draft-toolbar* layout + .submission-draft-base- controls (select + 👁 on one row); single-column collapse < 900px. - i18n: submissions.draft.base.preview(.soon) (DE/EN). Pure relayout — NOT a behavior change. Every control keeps its id, so the existing wiring is untouched (switcher, name save, keyword→composer_meta, base picker PATCH base_id, language autosave, savestatus). Grounded in the 2-panel reality (§1.4): the conditional Abschnitte section panel is unchanged (still display:none when a draft has no sections; S5 addresses its discoverability). TS/CSS only, no backend. bun build (i18n scan clean) + go vet ./... + go test ./... (15 ok, 0 fail).
This commit is contained in:
@@ -1771,6 +1771,8 @@ const translations: Record<Lang, Record<string, string>> = {
|
||||
// t-paliad-313 (m/paliad#141) Composer Slice A — base picker + section list.
|
||||
"submissions.draft.base.label": "Vorlagenbasis",
|
||||
"submissions.draft.base.hint": "Steuert Schriftarten, Briefkopf und Abschnitts-Defaults.",
|
||||
"submissions.draft.base.preview": "Vorschau Vorlagenbasis",
|
||||
"submissions.draft.base.preview.soon": "Bald verfügbar",
|
||||
"submissions.draft.sections.title": "Abschnitte",
|
||||
"submissions.draft.sections.hint": "Inhalt pro Abschnitt — Autosave nach 500 ms. Letztes Layout in Word.",
|
||||
// t-paliad-349 (m/paliad#157) docforge slice 6 — template authoring page.
|
||||
@@ -5124,6 +5126,8 @@ const translations: Record<Lang, Record<string, string>> = {
|
||||
// t-paliad-313 (m/paliad#141) Composer Slice A — base picker + section list.
|
||||
"submissions.draft.base.label": "Template base",
|
||||
"submissions.draft.base.hint": "Drives fonts, letterhead, and section defaults.",
|
||||
"submissions.draft.base.preview": "Preview template base",
|
||||
"submissions.draft.base.preview.soon": "Coming soon",
|
||||
"submissions.draft.sections.title": "Sections",
|
||||
"submissions.draft.sections.hint": "Edit per section — autosaves after 500ms. Final layout in Word.",
|
||||
// t-paliad-349 (m/paliad#157) docforge slice 6 — template authoring page.
|
||||
|
||||
@@ -2860,6 +2860,8 @@ export type I18nKey =
|
||||
| "submissions.draft.back"
|
||||
| "submissions.draft.base.hint"
|
||||
| "submissions.draft.base.label"
|
||||
| "submissions.draft.base.preview"
|
||||
| "submissions.draft.base.preview.soon"
|
||||
| "submissions.draft.import.button"
|
||||
| "submissions.draft.keyword.hint"
|
||||
| "submissions.draft.keyword.label"
|
||||
|
||||
@@ -6218,6 +6218,85 @@ dialog.modal::backdrop {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
/* t-paliad-370 S2 — meta toolbar strip. Draft management + template
|
||||
controls are lifted out of the sidebar into a horizontal, wrap-friendly
|
||||
header strip above the working grid; the sidebar then holds only the
|
||||
fill-in work (parties + variables). */
|
||||
.submission-draft-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
gap: 0.75rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 0.9rem 1rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
background: var(--color-surface, #fff);
|
||||
}
|
||||
|
||||
/* Zero the stacked sidebar margins so the moved blocks align on the
|
||||
toolbar baseline, and size them as flexible cells. */
|
||||
.submission-draft-toolbar > .submission-draft-switcher,
|
||||
.submission-draft-toolbar > .submission-draft-name-row,
|
||||
.submission-draft-toolbar > .submission-draft-keyword-row,
|
||||
.submission-draft-toolbar > .submission-draft-base-row,
|
||||
.submission-draft-toolbar > .submission-draft-language-row {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.submission-draft-toolbar > .submission-draft-switcher { flex: 1 1 240px; }
|
||||
.submission-draft-toolbar > .submission-draft-name-row { flex: 1 1 200px; }
|
||||
.submission-draft-toolbar > .submission-draft-keyword-row {
|
||||
flex: 1 1 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
.submission-draft-toolbar > .submission-draft-base-row { flex: 1 1 240px; }
|
||||
.submission-draft-toolbar > .submission-draft-language-row { flex: 0 0 auto; }
|
||||
|
||||
/* savestatus + fallback notice break onto their own full-width line at the
|
||||
bottom of the toolbar. */
|
||||
.submission-draft-toolbar > .submission-draft-savestatus,
|
||||
.submission-draft-toolbar > .submission-draft-language-fallback {
|
||||
flex-basis: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Base picker + 👁 Vorschau button share one row (disabled stub until S3). */
|
||||
.submission-draft-base-controls {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.submission-draft-base-controls select {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.submission-draft-preview-base-btn {
|
||||
flex: 0 0 auto;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
padding: 0;
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.submission-draft-preview-base-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.submission-draft-toolbar > .submission-draft-switcher,
|
||||
.submission-draft-toolbar > .submission-draft-name-row,
|
||||
.submission-draft-toolbar > .submission-draft-keyword-row,
|
||||
.submission-draft-toolbar > .submission-draft-base-row {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.submission-draft-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 360px) 1fr;
|
||||
|
||||
@@ -75,132 +75,151 @@ export function renderSubmissionDraft(): string {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="submission-draft-grid">
|
||||
{/* Sidebar — draft switcher + variable groups. */}
|
||||
<aside className="submission-draft-sidebar" id="submission-draft-sidebar">
|
||||
<div className="submission-draft-switcher">
|
||||
<label htmlFor="submission-draft-pick" data-i18n="submissions.draft.switcher.label">
|
||||
Entwurf
|
||||
</label>
|
||||
<select id="submission-draft-pick" />
|
||||
<button
|
||||
type="button"
|
||||
id="submission-draft-new-btn"
|
||||
className="btn-small btn-secondary"
|
||||
data-i18n="submissions.draft.action.new">
|
||||
+ Neuer Entwurf
|
||||
</button>
|
||||
</div>
|
||||
{/* t-paliad-370 S2 — meta toolbar. Draft management +
|
||||
template controls lifted out of the sidebar into a header
|
||||
strip so the sidebar holds only the fill-in work (parties
|
||||
+ variables). Pure relayout: every control keeps its id +
|
||||
wiring (switcher, name, keyword→composer_meta, base picker
|
||||
PATCH base_id, language autosave). */}
|
||||
<div className="submission-draft-toolbar" id="submission-draft-toolbar">
|
||||
<div className="submission-draft-switcher">
|
||||
<label htmlFor="submission-draft-pick" data-i18n="submissions.draft.switcher.label">
|
||||
Entwurf
|
||||
</label>
|
||||
<select id="submission-draft-pick" />
|
||||
<button
|
||||
type="button"
|
||||
id="submission-draft-new-btn"
|
||||
className="btn-small btn-secondary"
|
||||
data-i18n="submissions.draft.action.new">
|
||||
+ Neuer Entwurf
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="submission-draft-name-row">
|
||||
<input
|
||||
type="text"
|
||||
id="submission-draft-name"
|
||||
className="entity-form-input"
|
||||
data-i18n-placeholder="submissions.draft.name.placeholder"
|
||||
placeholder="Name dieses Entwurfs"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
id="submission-draft-delete-btn"
|
||||
className="btn-small btn-link-danger"
|
||||
data-i18n="submissions.draft.action.delete">
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
<div className="submission-draft-name-row">
|
||||
<input
|
||||
type="text"
|
||||
id="submission-draft-name"
|
||||
className="entity-form-input"
|
||||
data-i18n-placeholder="submissions.draft.name.placeholder"
|
||||
placeholder="Name dieses Entwurfs"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
id="submission-draft-delete-btn"
|
||||
className="btn-small btn-link-danger"
|
||||
data-i18n="submissions.draft.action.delete">
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* t-paliad-354 — keyword that leads the exported
|
||||
document name "<date> <keyword> (<case>)". Empty
|
||||
falls back to the auto-derived rule name; the
|
||||
placeholder shows that default. Persisted to
|
||||
composer_meta.filename_keyword via the draft-save
|
||||
path on change. Grouped with the draft-name row
|
||||
(naming controls) ahead of the template controls
|
||||
(base + language) per t-paliad-359. */}
|
||||
<div className="submission-draft-keyword-row">
|
||||
<label
|
||||
htmlFor="submission-draft-keyword"
|
||||
data-i18n="submissions.draft.keyword.label">
|
||||
Stichwort (Dateiname)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="submission-draft-keyword"
|
||||
className="entity-form-input"
|
||||
data-i18n-placeholder="submissions.draft.keyword.placeholder"
|
||||
placeholder="Automatisch aus dem Schriftsatztyp"
|
||||
/>
|
||||
<p
|
||||
className="submission-draft-keyword-hint"
|
||||
id="submission-draft-keyword-hint"
|
||||
data-i18n="submissions.draft.keyword.hint">
|
||||
Führt den Dateinamen an: <Datum> <Stichwort> (<Aktenzeichen>).
|
||||
</p>
|
||||
</div>
|
||||
{/* t-paliad-354 — keyword that leads the exported document
|
||||
name "<date> <keyword> (<case>)". Empty falls back to the
|
||||
auto-derived rule name. Persisted to
|
||||
composer_meta.filename_keyword on change. */}
|
||||
<div className="submission-draft-keyword-row">
|
||||
<label
|
||||
htmlFor="submission-draft-keyword"
|
||||
data-i18n="submissions.draft.keyword.label">
|
||||
Stichwort (Dateiname)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="submission-draft-keyword"
|
||||
className="entity-form-input"
|
||||
data-i18n-placeholder="submissions.draft.keyword.placeholder"
|
||||
placeholder="Automatisch aus dem Schriftsatztyp"
|
||||
/>
|
||||
<p
|
||||
className="submission-draft-keyword-hint"
|
||||
id="submission-draft-keyword-hint"
|
||||
data-i18n="submissions.draft.keyword.hint">
|
||||
Führt den Dateinamen an: <Datum> <Stichwort> (<Aktenzeichen>).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* t-paliad-313 (m/paliad#141) Composer Slice A —
|
||||
base picker. Hydrated by client/submission-draft.ts
|
||||
once /api/submission-bases returns. Disabled
|
||||
for pre-Composer drafts (base_id NULL); switching
|
||||
autosaves the draft. */}
|
||||
<div
|
||||
className="submission-draft-base-row"
|
||||
id="submission-draft-base-row"
|
||||
style="display:none">
|
||||
<label htmlFor="submission-draft-base" data-i18n="submissions.draft.base.label">
|
||||
Vorlagenbasis
|
||||
</label>
|
||||
{/* t-paliad-313 (m/paliad#141) Composer Slice A — base
|
||||
picker. Hydrated by client/submission-draft.ts once
|
||||
/api/submission-bases returns; switching autosaves
|
||||
base_id. t-paliad-370 S2 adds the 👁 Vorschau button
|
||||
(disabled stub; S3 wires it to the truthful base-preview
|
||||
modal, PRD §2). */}
|
||||
<div
|
||||
className="submission-draft-base-row"
|
||||
id="submission-draft-base-row"
|
||||
style="display:none">
|
||||
<label htmlFor="submission-draft-base" data-i18n="submissions.draft.base.label">
|
||||
Vorlagenbasis
|
||||
</label>
|
||||
<div className="submission-draft-base-controls">
|
||||
<select id="submission-draft-base" />
|
||||
<p
|
||||
className="submission-draft-base-hint"
|
||||
id="submission-draft-base-hint"
|
||||
data-i18n="submissions.draft.base.hint">
|
||||
Steuert Schriftarten, Briefkopf und Abschnitts-Defaults.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* t-paliad-276 — output language toggle (DE/EN).
|
||||
Hydrated by client/submission-draft.ts; switching
|
||||
autosaves the draft and re-renders the preview. */}
|
||||
<div
|
||||
className="submission-draft-language-row"
|
||||
id="submission-draft-language-row"
|
||||
role="radiogroup"
|
||||
aria-labelledby="submission-draft-language-label">
|
||||
<span
|
||||
id="submission-draft-language-label"
|
||||
className="submission-draft-language-label"
|
||||
data-i18n="submissions.draft.language">
|
||||
Sprache
|
||||
</span>
|
||||
<label className="submission-draft-language-option">
|
||||
<input
|
||||
type="radio"
|
||||
name="submission-draft-language"
|
||||
value="de"
|
||||
id="submission-draft-language-de"
|
||||
/>
|
||||
<span data-i18n="submissions.draft.language.de">DE</span>
|
||||
</label>
|
||||
<label className="submission-draft-language-option">
|
||||
<input
|
||||
type="radio"
|
||||
name="submission-draft-language"
|
||||
value="en"
|
||||
id="submission-draft-language-en"
|
||||
/>
|
||||
<span data-i18n="submissions.draft.language.en">EN</span>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
id="submission-draft-preview-base-btn"
|
||||
className="btn-icon submission-draft-preview-base-btn"
|
||||
disabled
|
||||
aria-label="Vorschau Vorlagenbasis"
|
||||
data-i18n-title="submissions.draft.base.preview.soon"
|
||||
title="Bald verfügbar">
|
||||
👁
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
className="submission-draft-language-fallback"
|
||||
id="submission-draft-language-fallback"
|
||||
style="display:none"
|
||||
data-i18n="submissions.draft.language.fallback_notice">
|
||||
Fallback: universelles Skelett (keine sprachspezifische Vorlage).
|
||||
className="submission-draft-base-hint"
|
||||
id="submission-draft-base-hint"
|
||||
data-i18n="submissions.draft.base.hint">
|
||||
Steuert Schriftarten, Briefkopf und Abschnitts-Defaults.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="submission-draft-savestatus" id="submission-draft-savestatus" />
|
||||
{/* t-paliad-276 — output language toggle (DE/EN). Hydrated
|
||||
by client/submission-draft.ts; switching autosaves the
|
||||
draft and re-renders the preview. */}
|
||||
<div
|
||||
className="submission-draft-language-row"
|
||||
id="submission-draft-language-row"
|
||||
role="radiogroup"
|
||||
aria-labelledby="submission-draft-language-label">
|
||||
<span
|
||||
id="submission-draft-language-label"
|
||||
className="submission-draft-language-label"
|
||||
data-i18n="submissions.draft.language">
|
||||
Sprache
|
||||
</span>
|
||||
<label className="submission-draft-language-option">
|
||||
<input
|
||||
type="radio"
|
||||
name="submission-draft-language"
|
||||
value="de"
|
||||
id="submission-draft-language-de"
|
||||
/>
|
||||
<span data-i18n="submissions.draft.language.de">DE</span>
|
||||
</label>
|
||||
<label className="submission-draft-language-option">
|
||||
<input
|
||||
type="radio"
|
||||
name="submission-draft-language"
|
||||
value="en"
|
||||
id="submission-draft-language-en"
|
||||
/>
|
||||
<span data-i18n="submissions.draft.language.en">EN</span>
|
||||
</label>
|
||||
</div>
|
||||
<p
|
||||
className="submission-draft-language-fallback"
|
||||
id="submission-draft-language-fallback"
|
||||
style="display:none"
|
||||
data-i18n="submissions.draft.language.fallback_notice">
|
||||
Fallback: universelles Skelett (keine sprachspezifische Vorlage).
|
||||
</p>
|
||||
|
||||
<p className="submission-draft-savestatus" id="submission-draft-savestatus" />
|
||||
</div>
|
||||
|
||||
<div className="submission-draft-grid">
|
||||
{/* Sidebar — parties + variable groups (the fill-in work).
|
||||
Draft/template meta lives in the toolbar above. */}
|
||||
<aside className="submission-draft-sidebar" id="submission-draft-sidebar">
|
||||
|
||||
{/* t-paliad-277: "Aus Projekt importieren" + last-
|
||||
imported-at timestamp. Only visible when the
|
||||
|
||||
Reference in New Issue
Block a user