Commit Graph

5 Commits

Author SHA1 Message Date
mAi
669764e86f mAi: #108 - t-paliad-276 submission generator language selector (DE/EN)
Per-draft `language` column drives the .docx output language for the
submission generator. The lawyer picks DE or EN on the draft editor's
sidebar; the generator selects the language-matched template variant
(falling back through {code}.{lang} → {code} → _skeleton.{lang} →
_skeleton → letterhead) and resolves language-aware variables
({{procedural_event.name}} → name_de vs name_en).

Schema (mig 130 — bumped from 129 to deconflict with atlas's #96):
- paliad.submission_drafts.language text NOT NULL DEFAULT 'de'
  CHECK IN ('de','en'). Existing rows inherit 'de' via the default,
  preserving every legacy draft's behaviour byte-for-byte.

Backend (Go):
- SubmissionVarsContext.Lang overrides the user's UI lang. Build()
  uses it when set; falls back to user.Lang otherwise — Slice 1's
  format-only /generate path keeps working unchanged.
- SubmissionDraftService.BuildRenderBag now threads draft.Language
  through. Create/EnsureLatest seed from the UI lang (DE default).
- DraftPatch.Language landed; Update validates and rejects values
  outside {de,en}. Project-scoped + global PATCH endpoints both
  surface the field.
- resolveSubmissionTemplate(ctx, code, lang) replaces the lang-less
  predecessor. Returns the matched tier (per_code_lang / per_code /
  skeleton_lang / skeleton / letterhead) so the editor knows whether
  to surface the "Fallback: universelles Skelett" notice.
- fileRegistry registers the EN skeleton sibling (`_skeleton.en.docx`)
  alongside the DE one; per-code EN variants land in a parallel
  submissionTemplateENRegistry (empty for now — EN templates land per
  HLC authoring). 404s from Gitea fall through silently.
- /api/projects/{id}/submissions/{code}/generate accepts
  `?language=de|en` query override (one-shot path, no draft row to
  pull the column from); defaults to the user's UI lang.

Frontend (TS/JSX):
- DE/EN radio above the variables list in the draft editor sidebar.
  Switching the radio PATCHes `language` and the server returns the
  freshly-resolved bag + preview HTML so the lawyer sees EN values
  immediately.
- Fallback notice ("Fallback: universelles Skelett (keine
  sprachspezifische Vorlage)") shows when the resolved tier doesn't
  match the requested language.
- 4 new i18n keys (DE + EN) + CSS for the toggle.

Tests:
- normalizeDraftLanguage covers DE/EN/case/whitespace/unknown.
- addRuleVars language-pick test pins procedural_event.name and the
  rule.name alias to the language-matched value.
- languageFallback truth table covers all 10 (lang × tier) combos.

Build hygiene: go build/vet/test clean; bun run build clean.
2026-05-25 16:39:29 +02:00
mAi
5df87f4129 fix(submissions): t-paliad-253 — /generate runs the merge engine
The "Generieren" button on the project Schriftsätze tab posts to
/api/projects/{id}/submissions/{code}/generate. Pre-fix that handler
called `fetchHLPatentsStyleBytes` unconditionally and streamed the
result after a format-only .dotm→.docx convert — it never touched
`submissionTemplateRegistry` (added in t-paliad-241 for the draft
editor) and never ran the SubmissionRenderer merge. m's report on
m/paliad#84 ("the document generator still has no variables in the
template") was the lawyer-facing manifestation: HL Patents Style has
no {{…}} placeholders, so the downloaded .docx had nothing to
substitute and looked like a generic firm-style fixture.

The "Bearbeiten" path (/projects/{id}/submissions/{code}/draft) was
unaffected — it uses `resolveSubmissionTemplate` + the renderer
already, which is why the editor preview shows the 48 placeholders
resolved correctly. Only the one-click /generate side missed the
wire-up.

Fix:

- `internal/services/submission_draft_service.go` — add
  `RenderProjectSubmission(ctx, userID, projectID, submissionCode,
  templateBytes)` that wraps `vars.Build` + `renderer.Render` for the
  no-saved-draft path. Returns the merged bytes plus the resolved
  SubmissionVarsResult (rule, project, user, lang) so the handler can
  derive filename + audit metadata without a second DB round-trip.

- `internal/handlers/submissions.go` — rewrite
  `handleGenerateProjectSubmission` to resolve the template via
  `resolveSubmissionTemplate` (per-firm slug → HL Patents Style
  fallback, same as the editor draft) and run the new service method.
  Visibility / rule-not-found semantics route through
  `SubmissionVarsService` errors so the gate behavior matches every
  other project endpoint. Removed `loadPublishedRuleByCode` and
  `errRuleNotFound` — both were only used by the old handler.

- `scripts/gen-demo-submission-template/main.go` + the regenerated
  `de.inf.lg.erwidg.docx` on mWorkRepo (HL/mWorkRepo @ 3e3e828f) now
  exercise the bare `{{today}}` alias too. The demo template covers
  every one of the 48 keys SubmissionVarsService can resolve (firm 2,
  today 4, user 3, project 18, parties 6, rule 8, deadline 7).

The renderer is a no-op on placeholder substitution when the
fallback HL Patents Style is fetched (it has none) — but it still
runs the .dotm→.docx pre-pass via `ConvertDotmToDocx`, so the
non-per-firm code path streams a byte-for-byte equivalent download.

Build + vet + tests clean (go test ./internal/...; bun run build).
2026-05-25 13:51:45 +02:00
mAi
a911a2d0ee feat(submissions): t-paliad-243 — global Schriftsätze drafts without project
Adds an end-to-end project-optional path for Schriftsatz drafts:

- Migration 120 drops NOT NULL on paliad.submission_drafts.project_id
  and rewrites the four RLS policies to gate purely on user_id when
  project_id IS NULL, otherwise on paliad.can_see_project. Down
  refuses to run if project-less rows exist (safer than silent
  data corruption).

- SubmissionDraft.ProjectID becomes *uuid.UUID end-to-end. Service
  layer skips project/parties/deadline lookups when nil and exposes
  DraftPatch.ProjectID for the "Projekt zuweisen" affordance.
  ListAllForUser LEFT JOINs paliad.projects so project-less drafts
  surface in the global index next to project-scoped ones.

- New HTTP surface:
    GET  /submissions/new                 (picker page)
    GET  /submissions/draft/{draft_id}    (editor for any draft)
    GET  /api/submissions/catalog         (catalog without project)
    POST /api/submission-drafts           (project-less or attached)
    GET/PATCH/DELETE /api/submission-drafts/{draft_id}
    POST /api/submission-drafts/{draft_id}/export
  Existing /api/projects/{id}/submissions/... routes remain bit-
  identical so the project-scoped flow keeps working unchanged.

- Frontend: /submissions/new lists the full cross-proceeding catalog
  grouped by proceeding, filterable by text + chip. Each row offers
  "Ohne Projekt" (instant draft) or "Mit Projekt…" (modal picker
  with autocomplete over visible projects). /submissions index gains
  a prominent "Neuer Entwurf" CTA and an empty-state CTA pointing at
  the picker. The editor renders a banner + "Projekt zuweisen"
  action when project_id is null; assigning persists project_id and
  redirects to the project-scoped URL.

Audit + project-event writes detect d.ProjectID == nil; the audit
row's scope flips to 'user' (scope_root = user_id) and the
project_events row is skipped entirely.
2026-05-23 02:19:55 +02:00
mAi
436c1b41bb feat(submissions): t-paliad-240 — Schriftsätze sidebar + global drafts index
Add a top-level Schriftsätze entry under the Werkzeuge sidebar group
plus a new /submissions page that lists every draft the caller owns
across visible projects. Each row links to the per-project editor at
/projects/{id}/submissions/{code}/draft/{draft_id}.

Backend: SubmissionDraftService.ListAllForUser joins paliad.submission_drafts
with paliad.projects, gated by paliad.can_see_project for visibility. New
GET /api/user/submission-drafts endpoint exposes the rows; the page route
GET /submissions is gateOnboarded'd alongside the other project surfaces.

Frontend: submissions-index.tsx renders an entity-table; submissions-index.ts
hydrates from /api/user/submission-drafts and wires the row-click contract
(skip clicks on inner a/button). DE primary, EN secondary i18n.
2026-05-23 01:29:56 +02:00
mAi
d3aade5aac feat(submissions): t-paliad-238 Slice A — dedicated draft editor page
Adds the dedicated Submissions/Schriftsätze editor at
/projects/{id}/submissions/{code}/draft (and …/draft/{draft_id}) per
docs/design-submission-page-2026-05-22.md.

Lawyer picks (or creates) a named draft, edits placeholder variables
in a sticky sidebar, sees a read-only HTML preview of the merged
document body, and exports a .docx with project state + lawyer
overrides resolved. Drafts persist in paliad.submission_drafts
keyed on (project_id, submission_code, user_id, name) with RLS via
can_see_project; updates and deletes additionally gated on owner-only
(Q-E4 owner-scoped pick, m-confirmed).

Resurrected from git history per the design's "no rewrite" plan:
  SubmissionVarsService    ← commit 1765d5e (Slice 2 with patent_number_upc)
  SubmissionRenderer       ← commit 8ea3509 (in-house merge engine — the
                             lukasjarosch/go-docx library refuses sibling
                             placeholders in one run, which patent submissions
                             use routinely)
  ConvertDotmToDocx        ← existing format-only convert (kept; reused as
                             pre-pass so .dotm inputs strip macros before
                             merge)

New code:
  paliad.submission_drafts  migration 119 (idempotent — DROP POLICY IF EXISTS
                            + CREATE; CREATE OR REPLACE for the shared trigger
                            function). Applied to live DB.
  SubmissionDraftService    CRUD + autosave-friendly Update + Export/RenderPreview
                            entry points
  RenderHTML method         new on the renderer; walks the same merged
                            document.xml as Render but emits HTML for the
                            preview pane (Q-E3 server-side pick)
  7 API handlers            list / create / get / patch / delete / preview / export
  2 page routes             /draft and /draft/{draft_id}
  submission-draft.tsx      stand-alone editor page (header / sidebar /
                            preview / export button); served via
                            dist/submission-draft.html
  submission-draft.ts       client bundle — autosave (500ms debounce),
                            draft switcher, rename, delete, export with
                            blob download

Tab integration: existing /projects/{id}/#tab-submissions rows get
[Bearbeiten] alongside the existing [Generieren] one-click format-only
path — additive, no removal.

Slice A template: universal HL Patents Style .dotm (same path
t-paliad-230 uses). resolveSubmissionTemplate carries the
submission_code parameter so Slice B's TemplateRegistry wiring (per-
code .docx fallback chain) is a one-function swap.

Audit trail: paliad.system_audit_log row per export
(event_type='submission.exported') + paliad.project_events row
(event_type='submission_exported', timeline_kind='custom_milestone')
so the export surfaces on the project's Verlauf / SmartTimeline. No
paliad.documents write (Q-E2 inventor pick, head-ratified).

Tests: TestRender_* / TestPlaceholderRegex_* / TestRenderHTML_* +
TestLegalSourcePretty / TestOurSide* / TestPatentNumberUPC — all
green. go build / go vet / go test ./internal/... / bun run build all
clean.

Migration slot taken: 119.
2026-05-23 00:06:08 +02:00