feat(verfahrensablauf): re-surface hidden optional events — show-hidden toggle + un-hide chip (t-paliad-290)
m/paliad#122. atlas's #96 Slice A added per-card 'Überspringen' but no un-skip path — hidden cards just disappeared from the timeline. This adds the missing return path: - CalcOptions.IncludeHidden (default false) tells the calculator to re-surface skipRules entries as faded rows instead of dropping them. When true, the rule renders with UIDeadline.IsHidden=true and the descendant-suppression cascade is bypassed so children compute their dates off the un-suppressed parent. - UIResponse.HiddenCount always reflects the projection's hide count (gate-passed rules whose submission_code is in skipRules) so the "Ausgeblendete (N)" badge stays accurate regardless of toggle state. - /tools/verfahrensablauf gets a "Ausgeblendete anzeigen" checkbox next to the perspective + appellant selectors. URL-driven (?show_hidden=1) so the state is shareable and survives reload. The row hides itself on projections with zero hidden cards. - Hidden cards render via .timeline-item--hidden / .fr-col-item--hidden (opacity 0.55 + dotted border, mirroring the existing --skipped fade) and carry an inline "Wieder einblenden" chip. Clicking the chip removes the skip choice via the page's existing attachEventCardChoices remove callback (URL state + recalc included) and runs through a new delegated handler in event-card-choices.ts. - 3 new i18n keys (DE+EN): choices.show_hidden.label, choices.show_hidden.count, choices.unhide.chip. The skip-choice storage shape (paliad.project_event_choices, atlas's table) is unchanged — un-hide is just a delete of the skip row. Tests: 3 new bun-test cases pin the chip contract (emits on isHidden= true with submission_code, suppressed otherwise); go test ./internal/... + bun run build clean.
This commit is contained in:
@@ -102,6 +102,12 @@ type UIDeadline struct {
|
||||
// Frontend bucketer prefers this over the page-level appellant when
|
||||
// non-empty. (t-paliad-265)
|
||||
AppellantContext string `json:"appellantContext,omitempty"`
|
||||
// IsHidden marks a card the user has previously hidden via a
|
||||
// skip choice. Only ever true when CalcOptions.IncludeHidden is
|
||||
// set — the toggle re-surfaces these rows so the user can either
|
||||
// keep them faded for context or un-hide them via the inline
|
||||
// "Wieder einblenden" chip. (t-paliad-290 / m/paliad#122)
|
||||
IsHidden bool `json:"isHidden,omitempty"`
|
||||
}
|
||||
|
||||
// UIResponse matches the frontend's DeadlineResponse TypeScript interface.
|
||||
@@ -137,6 +143,14 @@ type UIResponse struct {
|
||||
// is the appealable first-instance decision (m/paliad#81).
|
||||
TriggerEventLabel string `json:"triggerEventLabel,omitempty"`
|
||||
TriggerEventLabelEN string `json:"triggerEventLabelEN,omitempty"`
|
||||
// HiddenCount is the number of rules whose submission_code is in
|
||||
// CalcOptions.SkipRules AND whose condition_expr gate passes —
|
||||
// i.e. how many rows the user has hidden in this projection
|
||||
// regardless of the IncludeHidden toggle state. The frontend uses
|
||||
// this to render the "Ausgeblendete (N)" badge on the toggle even
|
||||
// when the toggle is OFF (so users know there's something to
|
||||
// re-surface). (t-paliad-290 / m/paliad#122)
|
||||
HiddenCount int `json:"hiddenCount"`
|
||||
}
|
||||
|
||||
// ErrUnknownProceedingType is returned when the UI sends an unrecognised code.
|
||||
@@ -214,6 +228,19 @@ type CalcOptions struct {
|
||||
PerCardAppellant map[string]string
|
||||
SkipRules map[string]struct{}
|
||||
IncludeCCRFor map[string]struct{}
|
||||
|
||||
// IncludeHidden re-surfaces rules whose submission_code is in
|
||||
// SkipRules (t-paliad-290 / m/paliad#122). When true:
|
||||
// - Skipped rules are NOT dropped from the result; they render
|
||||
// with UIDeadline.IsHidden=true so the frontend can fade them.
|
||||
// - Descendant suppression is bypassed (the skipped parent is
|
||||
// present in the result, so children compute their dates off
|
||||
// it as if the user had never hidden it).
|
||||
// Default false preserves the original skip semantic (drop rule +
|
||||
// suppress descendants). HiddenCount on UIResponse is independent
|
||||
// of this flag — it always reflects the number of hide-eligible
|
||||
// rows so the toggle's count badge stays accurate.
|
||||
IncludeHidden bool
|
||||
}
|
||||
|
||||
// Calculate renders the full UI timeline for a proceeding type + trigger date.
|
||||
@@ -381,6 +408,13 @@ func (s *FristenrechnerService) Calculate(ctx context.Context, proceedingCode, t
|
||||
// child rule's parent has already been classified — so descendant
|
||||
// suppression is a one-pass parent_id lookup.
|
||||
skippedIDs := make(map[uuid.UUID]struct{}, len(skipRules))
|
||||
// hiddenCount counts rows whose submission_code is in skipRules
|
||||
// AND that pass the condition_expr gate — i.e. rows the user has
|
||||
// hidden in this projection. Surfaced on UIResponse.HiddenCount so
|
||||
// the frontend's "Ausgeblendete (N)" badge stays accurate even when
|
||||
// IncludeHidden is off and the rows aren't in the result list.
|
||||
// (t-paliad-290 / m/paliad#122)
|
||||
hiddenCount := 0
|
||||
// appellantContext maps a rule UUID to the appellant value that
|
||||
// applies to its descendants. A rule that has its own PerCardAppellant
|
||||
// pick stamps itself with that value; a rule whose parent has a
|
||||
@@ -403,10 +437,22 @@ func (s *FristenrechnerService) Calculate(ctx context.Context, proceedingCode, t
|
||||
// this rule (or one of its ancestors) as "don't consider for
|
||||
// this case". Drop the row entirely AND record the rule ID so
|
||||
// descendants suppress too.
|
||||
//
|
||||
// t-paliad-290 (m/paliad#122): when opts.IncludeHidden is set,
|
||||
// we re-surface the directly-skipped row (faded via IsHidden)
|
||||
// instead of dropping it. Descendants are NOT cascade-suppressed
|
||||
// in that mode either — the un-suppressed parent computes its
|
||||
// date normally, so children compute off it as usual. Either
|
||||
// way we count the hide for the toggle's badge.
|
||||
var isHidden bool
|
||||
if r.SubmissionCode != nil {
|
||||
if _, skipped := skipRules[*r.SubmissionCode]; skipped {
|
||||
skippedIDs[r.ID] = struct{}{}
|
||||
continue
|
||||
hiddenCount++
|
||||
if !opts.IncludeHidden {
|
||||
skippedIDs[r.ID] = struct{}{}
|
||||
continue
|
||||
}
|
||||
isHidden = true
|
||||
}
|
||||
}
|
||||
if r.ParentID != nil {
|
||||
@@ -442,6 +488,7 @@ func (s *FristenrechnerService) Calculate(ctx context.Context, proceedingCode, t
|
||||
ConditionExpr: json.RawMessage(r.ConditionExpr),
|
||||
AppellantContext: ctxVal,
|
||||
ChoicesOffered: json.RawMessage(r.ChoicesOffered),
|
||||
IsHidden: isHidden,
|
||||
}
|
||||
if r.SubmissionCode != nil {
|
||||
d.Code = *r.SubmissionCode
|
||||
@@ -712,6 +759,7 @@ func (s *FristenrechnerService) Calculate(ctx context.Context, proceedingCode, t
|
||||
ProceedingNameEN: pickedProceeding.NameEN,
|
||||
TriggerDate: triggerDateStr,
|
||||
Deadlines: deadlines,
|
||||
HiddenCount: hiddenCount,
|
||||
}
|
||||
// Sub-track routing keeps the user-picked proceeding's identity,
|
||||
// so the trigger-event label rides on `pickedProceeding` (e.g.
|
||||
|
||||
Reference in New Issue
Block a user