Files
paliad/frontend/src/components/ProjectFormFields.tsx
mAi ebb5ff0caa feat(projects): add 'other' as a real type; drop synthetic Empty filter
m/paliad#51 (t-paliad-221) — the type chip filter on /projects used to
treat unclassified projects as a synthetic "Empty" bucket. Make 'other'
a first-class projects.type value so every row carries a meaningful
label and the filter UI stops needing a NULL/Empty shim.

- mig 110: extend projects.type CHECK to include 'other'; backfill any
  NULL rows defensively (production query confirmed zero, but the
  NOT NULL constraint isn't load-bearing once the IN-list changes).
- Go: add ProjectTypeOther constant; isValidProjectType + humanProjectType
  recognise it; handler doc lists 'other' in the ?type whitelist.
- Frontend: new chip in the projects.tsx type filter, new option in the
  Create-Project form, DE "Sonstiges" / EN "Other" labels for the
  projects.type and projects.chip.type i18n families.

Also drops a stray data-i18n-text attribute on the existing 'project'
chip checkbox (it had no consumer in i18n.ts and the surrounding markup
was nesting a <span> inside an <input>).
2026-05-20 14:42:02 +02:00

187 lines
8.4 KiB
TypeScript

import { h } from "../jsx";
import { FIRM } from "../branding";
// Reusable Project form body. Renders the field grid only — the surrounding
// <form>, submit/cancel buttons and the form-msg paragraph belong to the
// caller because /projects/new and the edit modal want different button
// labels and submit behaviour.
//
// Field IDs are intentionally identical to the ones used historically on
// /projects/new so the shared client module client/project-form.ts can read
// them via getElementById on either page (the two forms never coexist on a
// single page).
export function ProjectFormFields(): string {
return (
<div className="project-form-fields">
<div className="form-field">
<label htmlFor="project-type" data-i18n="projects.field.type">Typ</label>
<select id="project-type" required>
<option value="" disabled selected data-i18n="projects.field.type.choose">Bitte w&auml;hlen&hellip;</option>
<option value="client" data-i18n="projects.type.client">Mandant (Wurzel)</option>
<option value="litigation" data-i18n="projects.type.litigation">Streitsache</option>
<option value="patent" data-i18n="projects.type.patent">Patent</option>
<option value="case" data-i18n="projects.type.case">Verfahren</option>
<option value="project" data-i18n="projects.type.project">Projekt (generisch)</option>
<option value="other" data-i18n="projects.type.other">Sonstiges</option>
</select>
</div>
<div className="form-field" id="projekt-parent-wrap" style="display:none">
<label htmlFor="projekt-parent-input" data-i18n="projects.field.parent">&Uuml;bergeordnetes Projekt</label>
<input
type="text"
id="projekt-parent-input"
placeholder="Titel eingeben, um ein &Uuml;berprojekt zu suchen..."
data-i18n-placeholder="projects.field.parent.placeholder"
autocomplete="off"
/>
<input type="hidden" id="projekt-parent-id" />
<div id="projekt-parent-suggestions" className="collab-suggestions" />
<p className="form-hint" data-i18n="projects.field.parent.hint">
Leer lassen f&uuml;r ein Wurzel-Projekt (typisch: Mandant).
</p>
</div>
<div className="form-field">
<label htmlFor="project-title" data-i18n="projects.field.title">Titel</label>
<input
type="text"
id="project-title"
required
placeholder="z.B. Siemens AG | Siemens v. Huawei | EP 1 234 567"
data-i18n-placeholder="projects.field.title.placeholder"
/>
</div>
<div className="form-field">
<label htmlFor="project-ref" data-i18n="projects.field.reference">Interne Referenz (optional)</label>
<input
type="text"
id="project-ref"
placeholder={`z.B. ${FIRM}-2026-0042`}
data-i18n-placeholder="projects.field.reference.placeholder"
/>
</div>
<div className="form-field-row">
<div className="form-field">
<label htmlFor="project-client-number" data-i18n="projects.field.client_number">Client-Nr. (6 Ziffern)</label>
<input
type="text"
id="project-client-number"
pattern="[0-9]{6}"
maxLength={6}
placeholder="001234"
/>
</div>
<div className="form-field">
<label htmlFor="project-matter-number" data-i18n="projects.field.matter_number">Matter-Nr. (6 Ziffern)</label>
<input
type="text"
id="project-matter-number"
pattern="[0-9]{6}"
maxLength={6}
placeholder="000567"
/>
</div>
</div>
<p className="form-hint" data-i18n="projects.field.clientmatter.hint">
{`${FIRM}-Billing-Nummern. Format CCCCCC.MMMMMM. Client-Nr. wird an Unterprojekte vererbt
(überschreibbar).`}
</p>
<div className="form-field">
<label htmlFor="project-billing-ref" data-i18n="projects.field.billing_reference">Billing-Referenz (optional)</label>
<input
type="text"
id="project-billing-ref"
placeholder="z.B. PO-2026-0815"
/>
</div>
<div className="form-field">
<label htmlFor="project-netdocs" data-i18n="projects.field.netdocuments_url">netDocuments-URL (optional)</label>
<input
type="url"
id="project-netdocs"
placeholder="https://netdocs.example.com/..."
/>
</div>
{/* Client-specific */}
<div className="projekt-fields projekt-fields-client" id="fields-client" style="display:none">
<div className="form-field-row">
<div className="form-field">
<label htmlFor="project-industry" data-i18n="projects.field.industry">Branche</label>
<input type="text" id="project-industry" placeholder="z.B. industrial" />
</div>
<div className="form-field">
<label htmlFor="project-country" data-i18n="projects.field.country">Land (ISO-2)</label>
<input type="text" id="project-country" maxLength={2} placeholder="DE" />
</div>
</div>
</div>
{/* Patent-specific */}
<div className="projekt-fields projekt-fields-patent" id="fields-patent" style="display:none">
<div className="form-field">
<label htmlFor="project-patent-number" data-i18n="projects.field.patent_number">Patentnummer</label>
<input type="text" id="project-patent-number" placeholder="EP 1 234 567" />
</div>
<div className="form-field-row">
<div className="form-field">
<label htmlFor="project-filing-date" data-i18n="projects.field.filing_date">Anmeldetag</label>
<input type="date" id="project-filing-date" />
</div>
<div className="form-field">
<label htmlFor="project-grant-date" data-i18n="projects.field.grant_date">Erteilungstag</label>
<input type="date" id="project-grant-date" />
</div>
</div>
</div>
{/* Case-specific */}
<div className="projekt-fields projekt-fields-case" id="fields-case" style="display:none">
<div className="form-field-row">
<div className="form-field">
<label htmlFor="project-court" data-i18n="projects.field.court">Gericht</label>
<input type="text" id="project-court" placeholder="UPC_CFI_Munich" />
</div>
<div className="form-field">
<label htmlFor="project-case-number" data-i18n="projects.field.case_number">Aktenzeichen (Gericht)</label>
<input type="text" id="project-case-number" placeholder="UPC_CFI_123/2026" />
</div>
</div>
</div>
<div className="form-field">
<label htmlFor="project-our-side" data-i18n="projects.field.our_side">Wir vertreten</label>
<select id="project-our-side">
<option value="" data-i18n="projects.field.our_side.unset">Unbekannt / nicht gesetzt</option>
<option value="claimant" data-i18n="projects.field.our_side.claimant">Kl&auml;gerseite</option>
<option value="defendant" data-i18n="projects.field.our_side.defendant">Beklagtenseite</option>
<option value="court" data-i18n="projects.field.our_side.court">Gericht / Tribunal</option>
<option value="both" data-i18n="projects.field.our_side.both">Beide Seiten</option>
</select>
<p className="form-hint" data-i18n="projects.field.our_side.hint">
Bestimmt die Voreinstellung der Perspektive im Fristenrechner-Determinator. L&auml;sst sich dort jederzeit &uuml;berschreiben.
</p>
</div>
<div className="form-field">
<label htmlFor="project-description" data-i18n="projects.field.description">Notizen</label>
<textarea id="project-description" rows={4} placeholder="Kurznotizen zum Projekt (optional)..." data-i18n-placeholder="projects.field.description.placeholder" />
</div>
<div className="form-field">
<label htmlFor="project-status" data-i18n="projects.field.status">Status</label>
<select id="project-status">
<option value="active" data-i18n="projects.filter.status.active">Aktiv</option>
<option value="closed" data-i18n="projects.filter.status.closed">Abgeschlossen</option>
<option value="archived" data-i18n="projects.filter.status.archived">Archiviert</option>
</select>
</div>
</div>
);
}