diff --git a/frontend/src/client/builder.ts b/frontend/src/client/builder.ts index c92f49a..ffb56df 100644 --- a/frontend/src/client/builder.ts +++ b/frontend/src/client/builder.ts @@ -815,6 +815,13 @@ async function loadScenario(id: string): Promise { setSaveState("error"); return; } + // Defensive: Go's encoding/json serialises a nil slice as `null`, not + // `[]`. The server initialises these arrays today, but normalising on + // the client too means a future regression (or an older deployed + // build) can't crash renderCanvas with `null.filter(...)`. + if (!Array.isArray(deep.proceedings)) deep.proceedings = []; + if (!Array.isArray(deep.events)) deep.events = []; + if (!Array.isArray(deep.shares)) deep.shares = []; state.active = deep; state.pending = {}; writeScenarioToUrl(id); diff --git a/internal/services/deadline_service.go b/internal/services/deadline_service.go index 70327e1..8d6319c 100644 --- a/internal/services/deadline_service.go +++ b/internal/services/deadline_service.go @@ -265,7 +265,7 @@ func (s *DeadlineService) ListVisibleForUser(ctx context.Context, userID uuid.UU query := ` SELECT f.id, f.project_id, f.title, f.description, f.due_date, f.original_due_date, - f.warning_date, f.source, f.rule_id, f.rule_code, f.custom_rule_text, f.status, f.completed_at, + f.warning_date, f.source, f.sequencing_rule_id, f.rule_code, f.custom_rule_text, f.status, f.completed_at, f.caldav_uid, f.caldav_etag, f.notes, f.created_by, f.created_at, f.updated_at, f.approval_status, f.pending_request_id, f.approved_by, f.approved_at, diff --git a/internal/services/projection_service.go b/internal/services/projection_service.go index a51f383..fe0150e 100644 --- a/internal/services/projection_service.go +++ b/internal/services/projection_service.go @@ -1247,7 +1247,7 @@ func (s *ProjectionService) collectActualsForOverrides( } var dRows []drow scopeFilter := scopeProjectIDFilter("d", "project_id", projectID, directOnly) - q := `SELECT d.rule_id, d.rule_code, d.due_date, d.completed_at, d.status + q := `SELECT d.sequencing_rule_id AS rule_id, d.rule_code, d.due_date, d.completed_at, d.status FROM paliad.deadlines d WHERE ` + scopeFilter if err := s.db.SelectContext(ctx, &dRows, q, projectID); err != nil { diff --git a/internal/services/scenario_builder_service.go b/internal/services/scenario_builder_service.go index df3c6da..86986a2 100644 --- a/internal/services/scenario_builder_service.go +++ b/internal/services/scenario_builder_service.go @@ -204,7 +204,15 @@ func (s *ScenarioBuilderService) GetScenarioDeep(ctx context.Context, userID, sc return nil, ErrScenarioBuilderNotVisible } - deep := &BuilderScenarioDeep{BuilderScenario: *sc} + deep := &BuilderScenarioDeep{ + BuilderScenario: *sc, + // Initialise to empty so the JSON response always carries arrays, + // not null — the builder frontend's renderCanvas calls .filter on + // proceedings/events unconditionally once state.active is set. + Proceedings: []BuilderProceeding{}, + Events: []BuilderEvent{}, + Shares: []BuilderShare{}, + } if err := s.db.SelectContext(ctx, &deep.Proceedings, ` SELECT id, scenario_id, proceeding_type_id, primary_party, scenario_flags,