Merge: t-paliad-165 — Regel ↔ Typ collapse via auto-link on the deadline create form
Two slices on mai/noether/collapse-regel-typ-on:0c12644feat(deadline-rules): expose concept's canonical event_type per rule1e97eccfeat(deadlines/new): auto-link Typ to Regel's concept What ships: - New junction paliad.deadline_concept_event_types maps every paliad.deadline_concepts row to its canonical paliad.event_types row(s). Many-to-many for concepts with multiple legitimate variants (statement-of-defence ↔ base + with_ccr + no_ccr; opposition across EPO + DPMA). Exactly one row per concept marked is_default = true by a partial unique index — that is the row the deadline form auto-fills with. - Backend: paliad.deadline_rules_with_concept_event_type view + the deadline-rules read path now expose the rule's default concept event_type so the form has the auto-fill target without an extra round-trip. - Frontend deadline create / edit form: when the user picks a Regel, the Typ chip auto-fills with the rule's concept's default event_type. A small "vorgegeben durch Regel — überschreiben?" hint sits next to the chip so the auto-fill is visible. The user can override (free- text or pick a different type); the override is explicit, no blocking validation. - Free-text Typ stays available — manual deadlines without a matching rule (e.g. "Call me" reminders) keep working as today. Migration housekeeping ====================== noether authored her migration as 072 on her branch but main had already taken 072 via minkowski's t-paliad-164 (paliad.projects.our_side). Renumbered to 073 during merge resolution to resolve the same-number collision. Added IF NOT EXISTS guards on CREATE TABLE / CREATE INDEX for re-run safety (the seed INSERT already had ON CONFLICT DO NOTHING). Live tracker bumped 72 → 73 in the same operation: both effects (our_side column AND deadline_concept_event_types table) were applied to live during dev (each worker against the same DB), so the tracker advance reflects schema reality. Next deploy sees tracker=73 with file 073 present and has nothing to apply. Refs m/paliad#18.
This commit is contained in:
@@ -32,6 +32,9 @@ const proceedingTypeColumns = `id, code, name, name_en, description, jurisdictio
|
||||
category, default_color, sort_order, is_active`
|
||||
|
||||
// List returns active rules, optionally filtered by proceeding type.
|
||||
// Each row has ConceptDefaultEventTypeID hydrated from
|
||||
// paliad.deadline_concept_event_types so the deadline-create form can
|
||||
// auto-populate the Typ chip when the user picks a Regel.
|
||||
func (s *DeadlineRuleService) List(ctx context.Context, proceedingTypeID *int) ([]models.DeadlineRule, error) {
|
||||
var rules []models.DeadlineRule
|
||||
var err error
|
||||
@@ -52,9 +55,62 @@ func (s *DeadlineRuleService) List(ctx context.Context, proceedingTypeID *int) (
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list deadline rules: %w", err)
|
||||
}
|
||||
if err := s.hydrateConceptDefaultEventTypes(ctx, rules); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
// hydrateConceptDefaultEventTypes resolves rule.ConceptID →
|
||||
// paliad.deadline_concept_event_types.event_type_id (where is_default)
|
||||
// for every rule with a non-nil ConceptID, and assigns the result.
|
||||
// One round-trip; rules whose concept has no default mapping stay NULL.
|
||||
func (s *DeadlineRuleService) hydrateConceptDefaultEventTypes(ctx context.Context, rules []models.DeadlineRule) error {
|
||||
conceptIDs := make([]uuid.UUID, 0, len(rules))
|
||||
seen := make(map[uuid.UUID]bool, len(rules))
|
||||
for _, r := range rules {
|
||||
if r.ConceptID == nil || seen[*r.ConceptID] {
|
||||
continue
|
||||
}
|
||||
seen[*r.ConceptID] = true
|
||||
conceptIDs = append(conceptIDs, *r.ConceptID)
|
||||
}
|
||||
if len(conceptIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
query, args, err := sqlx.In(
|
||||
`SELECT concept_id, event_type_id
|
||||
FROM paliad.deadline_concept_event_types
|
||||
WHERE is_default = true AND concept_id IN (?)`, conceptIDs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build concept→event_type IN query: %w", err)
|
||||
}
|
||||
query = s.db.Rebind(query)
|
||||
|
||||
type row struct {
|
||||
ConceptID uuid.UUID `db:"concept_id"`
|
||||
EventTypeID uuid.UUID `db:"event_type_id"`
|
||||
}
|
||||
var rows []row
|
||||
if err := s.db.SelectContext(ctx, &rows, query, args...); err != nil {
|
||||
return fmt.Errorf("load concept→event_type defaults: %w", err)
|
||||
}
|
||||
defaultByConcept := make(map[uuid.UUID]uuid.UUID, len(rows))
|
||||
for _, r := range rows {
|
||||
defaultByConcept[r.ConceptID] = r.EventTypeID
|
||||
}
|
||||
for i := range rules {
|
||||
if rules[i].ConceptID == nil {
|
||||
continue
|
||||
}
|
||||
if et, ok := defaultByConcept[*rules[i].ConceptID]; ok {
|
||||
etCopy := et
|
||||
rules[i].ConceptDefaultEventTypeID = &etCopy
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RuleTreeNode pairs a rule with its child rules in a parent_id hierarchy.
|
||||
type RuleTreeNode struct {
|
||||
models.DeadlineRule
|
||||
|
||||
Reference in New Issue
Block a user