Establish the shared frontend editor package and make the Go resolvers the
single source of truth for variable labels.
Go — catalogue SSOT:
- VariableResolver gains Keys() []VariableKey; ResolverSet gains
Catalogue(). The 7 submission resolvers implement Keys() with the
bilingual labels ported from the TS VARIABLE_LABELS table (incl. the
legacy rule.* aliases). Keys() is entity-independent, so
SubmissionVariableCatalogue() builds a metadata-only ResolverSet.
- GET /api/docforge/variables serves the catalogue (auth-gated, static).
- Tests: docforge ResolverSet (BuildBag merge + Catalogue order) and the
submission catalogue integrity (no dupes, labels present, spot-checks).
Frontend — frontend/src/lib/docforge-editor/ (new shared package):
- dom.ts: escapeHtml + cssEscape (pure), with bun tests. Dedupes the two
identical escapeHtml/escapeHTML copies + the cssEscape copy that lived
in the submission editor.
- catalogue.ts: fetchVariableCatalogue() + labelMap() — the client for
the Go catalogue.
- submission-draft.ts now imports escapeHtml/cssEscape from the lib and
fetches the catalogue on boot into state.varLabels (labelFor reads it,
falling back to the raw key if the fetch fails — graceful degrade). The
hardcoded VARIABLE_LABELS table is removed; VARIABLE_GROUPS stays
(presentation: which keys to show + how to section them, legitimately
frontend).
Scope note: the DOM-coupled editor plumbing (wireDraftVars/focus
preservation/autosave debounce) is extracted in slice 6 alongside its first
reuse — the authoring page — rather than speculatively now (extract with the
consumer; same principle as slices 2-3). Slice 5 lands the pure utilities +
the catalogue, which the slice-6 authoring palette consumes.
Verification: go build/vet/test green (Go files gofmt-clean; handlers.go
pre-existing drift, added region clean); bun run build.ts clean;
bun test 274/274 (incl. 5 new docforge-editor tests).
m/paliad#157
201 lines
9.7 KiB
Go
201 lines
9.7 KiB
Go
package services
|
|
|
|
// Variable resolvers — the paliad-side implementations of
|
|
// docforge.VariableResolver (t-paliad-349 slice 3). Each wraps one of the
|
|
// addXxxVars push-builders, capturing the entity it needs, so the proven
|
|
// builder bodies stay byte-for-byte unchanged while the composition moves
|
|
// behind the docforge.ResolverSet seam. SubmissionVarsService.Build wires
|
|
// the applicable resolvers and calls ResolverSet.BuildBag().
|
|
//
|
|
// These live in paliad (not docforge) because they read paliad's domain
|
|
// model — branding, user, project, parties, deadline_rules, deadlines. A
|
|
// second docforge consumer implements its own resolvers against its own
|
|
// data and plugs them into a ResolverSet the same way.
|
|
|
|
import (
|
|
"time"
|
|
|
|
"mgit.msbls.de/m/paliad/internal/models"
|
|
"mgit.msbls.de/m/paliad/pkg/docforge"
|
|
)
|
|
|
|
// Compile-time conformance: each resolver satisfies docforge.VariableResolver.
|
|
var (
|
|
_ docforge.VariableResolver = firmResolver{}
|
|
_ docforge.VariableResolver = todayResolver{}
|
|
_ docforge.VariableResolver = userResolver{}
|
|
_ docforge.VariableResolver = proceduralEventResolver{}
|
|
_ docforge.VariableResolver = projectResolver{}
|
|
_ docforge.VariableResolver = partiesResolver{}
|
|
_ docforge.VariableResolver = deadlineResolver{}
|
|
)
|
|
|
|
// vk is a terse constructor for a catalogue entry in the given group.
|
|
func vk(group, key, de, en string) docforge.VariableKey {
|
|
return docforge.VariableKey{Key: key, LabelDE: de, LabelEN: en, Group: group}
|
|
}
|
|
|
|
// SubmissionVariableCatalogue returns the full variable catalogue for the
|
|
// submission resolvers — every (key, DE/EN label, namespace) the sidebar
|
|
// form and the authoring palette can offer. Built from the resolvers'
|
|
// Keys() with no entity state, so it needs no DB call. This is the single
|
|
// source of truth for variable labels, replacing the duplicated TS
|
|
// VARIABLE_LABELS table (t-paliad-349 slice 5).
|
|
func SubmissionVariableCatalogue() []docforge.VariableKey {
|
|
return docforge.NewResolverSet(
|
|
firmResolver{},
|
|
todayResolver{},
|
|
userResolver{},
|
|
proceduralEventResolver{},
|
|
projectResolver{},
|
|
partiesResolver{},
|
|
deadlineResolver{},
|
|
).Catalogue()
|
|
}
|
|
|
|
// firmResolver populates firm.* from process-wide branding.
|
|
type firmResolver struct{}
|
|
|
|
func (firmResolver) Namespace() string { return "firm" }
|
|
func (firmResolver) Populate(bag PlaceholderMap) { addFirmVars(bag) }
|
|
func (firmResolver) Keys() []docforge.VariableKey {
|
|
return []docforge.VariableKey{
|
|
vk("firm", "firm.name", "Kanzlei", "Firm"),
|
|
vk("firm", "firm.signature_block", "Signatur-Block", "Signature block"),
|
|
}
|
|
}
|
|
|
|
// todayResolver populates today.* from the build-time clock.
|
|
type todayResolver struct{ now time.Time }
|
|
|
|
func (todayResolver) Namespace() string { return "today" }
|
|
func (r todayResolver) Populate(bag PlaceholderMap) { addTodayVars(bag, r.now) }
|
|
func (todayResolver) Keys() []docforge.VariableKey {
|
|
return []docforge.VariableKey{
|
|
vk("today", "today", "Heute", "Today"),
|
|
vk("today", "today.iso", "Heute (ISO)", "Today (ISO)"),
|
|
vk("today", "today.long_de", "Heute (DE lang)", "Today (DE long)"),
|
|
vk("today", "today.long_en", "Heute (EN lang)", "Today (EN long)"),
|
|
}
|
|
}
|
|
|
|
// userResolver populates user.* from the caller's row.
|
|
type userResolver struct{ user *models.User }
|
|
|
|
func (userResolver) Namespace() string { return "user" }
|
|
func (r userResolver) Populate(bag PlaceholderMap) { addUserVars(bag, r.user) }
|
|
func (userResolver) Keys() []docforge.VariableKey {
|
|
return []docforge.VariableKey{
|
|
vk("user", "user.display_name", "Bearbeiter", "Author"),
|
|
vk("user", "user.email", "E-Mail", "Email"),
|
|
vk("user", "user.office", "Büro", "Office"),
|
|
}
|
|
}
|
|
|
|
// proceduralEventResolver populates procedural_event.* and the legacy
|
|
// rule.* alias from the published deadline_rule.
|
|
type proceduralEventResolver struct {
|
|
rule *models.DeadlineRule
|
|
lang string
|
|
}
|
|
|
|
func (proceduralEventResolver) Namespace() string { return "procedural_event" }
|
|
func (r proceduralEventResolver) Populate(bag PlaceholderMap) { addRuleVars(bag, r.rule, r.lang) }
|
|
func (proceduralEventResolver) Keys() []docforge.VariableKey {
|
|
return []docforge.VariableKey{
|
|
vk("procedural_event", "procedural_event.code", "Code (Verfahrensschritt)", "Code (procedural event)"),
|
|
vk("procedural_event", "procedural_event.name", "Verfahrensschritt", "Procedural event"),
|
|
vk("procedural_event", "procedural_event.name_de", "Verfahrensschritt (DE)", "Procedural event (DE)"),
|
|
vk("procedural_event", "procedural_event.name_en", "Verfahrensschritt (EN)", "Procedural event (EN)"),
|
|
vk("procedural_event", "procedural_event.legal_source", "Rechtsgrundlage (Code)", "Legal source (code)"),
|
|
vk("procedural_event", "procedural_event.legal_source_pretty", "Rechtsgrundlage", "Legal source"),
|
|
vk("procedural_event", "procedural_event.primary_party", "Partei (typisch)", "Primary party"),
|
|
vk("procedural_event", "procedural_event.event_kind", "Art des Verfahrensschritts", "Procedural-event kind"),
|
|
// Legacy rule.* aliases — @deprecated, kept forever (m/paliad#93 Q7).
|
|
vk("procedural_event", "rule.submission_code", "Schriftsatz-Code (legacy)", "Submission code (legacy)"),
|
|
vk("procedural_event", "rule.name", "Schriftsatz (legacy)", "Submission (legacy)"),
|
|
vk("procedural_event", "rule.name_de", "Schriftsatz (DE, legacy)", "Submission (DE, legacy)"),
|
|
vk("procedural_event", "rule.name_en", "Schriftsatz (EN, legacy)", "Submission (EN, legacy)"),
|
|
vk("procedural_event", "rule.legal_source", "Rechtsgrundlage (Code, legacy)", "Legal source (code, legacy)"),
|
|
vk("procedural_event", "rule.legal_source_pretty", "Rechtsgrundlage (legacy)", "Legal source (legacy)"),
|
|
vk("procedural_event", "rule.primary_party", "Partei (typisch, legacy)", "Primary party (legacy)"),
|
|
vk("procedural_event", "rule.event_type", "Schriftsatz-Typ (legacy)", "Event type (legacy)"),
|
|
}
|
|
}
|
|
|
|
// projectResolver populates project.* from the project + its proceeding type.
|
|
type projectResolver struct {
|
|
project *models.Project
|
|
pt *models.ProceedingType
|
|
lang string
|
|
}
|
|
|
|
func (projectResolver) Namespace() string { return "project" }
|
|
func (r projectResolver) Populate(bag PlaceholderMap) { addProjectVars(bag, r.project, r.pt, r.lang) }
|
|
func (projectResolver) Keys() []docforge.VariableKey {
|
|
return []docforge.VariableKey{
|
|
vk("project", "project.title", "Projekttitel", "Project title"),
|
|
vk("project", "project.reference", "Aktenzeichen (intern)", "Internal reference"),
|
|
vk("project", "project.case_number", "Aktenzeichen (Gericht)", "Court case number"),
|
|
vk("project", "project.court", "Gericht", "Court"),
|
|
vk("project", "project.patent_number", "Patentnummer", "Patent number"),
|
|
vk("project", "project.patent_number_upc", "Patentnummer (UPC-Format)", "Patent number (UPC format)"),
|
|
vk("project", "project.filing_date", "Anmeldedatum", "Filing date"),
|
|
vk("project", "project.grant_date", "Erteilungsdatum", "Grant date"),
|
|
vk("project", "project.our_side", "Unsere Seite", "Our side"),
|
|
vk("project", "project.our_side_de", "Unsere Seite (DE)", "Our side (DE)"),
|
|
vk("project", "project.our_side_en", "Unsere Seite (EN)", "Our side (EN)"),
|
|
vk("project", "project.instance_level", "Instanz", "Instance"),
|
|
vk("project", "project.client_number", "Mandantennummer", "Client number"),
|
|
vk("project", "project.matter_number", "Matter-Nummer", "Matter number"),
|
|
vk("project", "project.proceeding.code", "Verfahrenstyp (Code)", "Proceeding type (code)"),
|
|
vk("project", "project.proceeding.name", "Verfahrenstyp", "Proceeding type"),
|
|
vk("project", "project.proceeding.name_de", "Verfahrenstyp (DE)", "Proceeding type (DE)"),
|
|
vk("project", "project.proceeding.name_en", "Verfahrenstyp (EN)", "Proceeding type (EN)"),
|
|
}
|
|
}
|
|
|
|
// partiesResolver populates parties.* from the (already filtered) party list.
|
|
type partiesResolver struct{ parties []models.Party }
|
|
|
|
func (partiesResolver) Namespace() string { return "parties" }
|
|
func (r partiesResolver) Populate(bag PlaceholderMap) { addPartyVars(bag, r.parties) }
|
|
|
|
// Keys returns the flat, user-facing party forms (the power-user override
|
|
// rows the sidebar shows). The indexed (parties.claimant.0.name) and
|
|
// joined (parties.claimants) forms Populate also emits are not catalogue
|
|
// entries — they're resolved into the bag but not offered in the palette.
|
|
func (partiesResolver) Keys() []docforge.VariableKey {
|
|
return []docforge.VariableKey{
|
|
vk("parties", "parties.claimant.name", "Klägerin", "Claimant"),
|
|
vk("parties", "parties.claimant.representative", "Klägerin-Vertreter", "Claimant representative"),
|
|
vk("parties", "parties.defendant.name", "Beklagte", "Defendant"),
|
|
vk("parties", "parties.defendant.representative", "Beklagten-Vertreter", "Defendant representative"),
|
|
vk("parties", "parties.other.name", "Weitere Partei", "Other party"),
|
|
vk("parties", "parties.other.representative", "Weitere-Partei-Vertreter", "Other party representative"),
|
|
}
|
|
}
|
|
|
|
// deadlineResolver populates deadline.* from the next pending deadline.
|
|
type deadlineResolver struct {
|
|
deadline *models.Deadline
|
|
project *models.Project
|
|
lang string
|
|
}
|
|
|
|
func (deadlineResolver) Namespace() string { return "deadline" }
|
|
func (r deadlineResolver) Populate(bag PlaceholderMap) {
|
|
addDeadlineVars(bag, r.deadline, r.project, r.lang)
|
|
}
|
|
func (deadlineResolver) Keys() []docforge.VariableKey {
|
|
return []docforge.VariableKey{
|
|
vk("deadline", "deadline.due_date", "Frist (ISO)", "Deadline (ISO)"),
|
|
vk("deadline", "deadline.due_date_long_de", "Frist (DE lang)", "Deadline (DE long)"),
|
|
vk("deadline", "deadline.due_date_long_en", "Frist (EN lang)", "Deadline (EN long)"),
|
|
vk("deadline", "deadline.original_due_date", "Ursprüngliche Frist", "Original deadline"),
|
|
vk("deadline", "deadline.computed_from", "Frist berechnet aus", "Deadline computed from"),
|
|
vk("deadline", "deadline.title", "Frist-Titel", "Deadline title"),
|
|
vk("deadline", "deadline.source", "Frist-Quelle", "Deadline source"),
|
|
}
|
|
}
|