high priority low complexity frontend pending frontend specialist Tier 8

Acceptance Criteria

Card displays display_name as the primary title using the card-title design token typography
org_label_override is shown as subtle subtext below the title only when the field is non-null and non-empty
Status badge shows 'Active' in green (design token colorSuccess or badge-active) for status=active
Status badge shows 'Archived' in grey (design token colorSurface/textTertiary) for status=archived
Three compact icon badges are shown for: travel_eligible (car icon), report_required (document icon), reimbursement_trigger (coin/receipt icon); badge is filled/colored when flag is true, outlined/muted when false
Bufdir category is shown as a small violet badge (design token badge-tech color) with the human-readable label (not the enum key)
Tapping anywhere on the card navigates to ActivityTypeFormScreen in edit mode with the ActivityType entity passed as argument
Card has a hover/press ripple effect (InkWell) consistent with card-clickable design token convention
All badge labels and icon meanings are announced by screen readers (Semantics tooltips on icon-only badges)
Widget is stateless and accepts a single ActivityType entity plus an onTap callback as constructor parameters

Technical Requirements

frameworks
Flutter
data models
ActivityType
BufdirCategory (for label resolution)
performance requirements
Widget must be const-constructible when ActivityType is immutable — enables Flutter widget diffing optimisation
No async calls inside the widget — all data resolved before passing to constructor
security requirements
org_label_override displayed as plain text — escape any HTML-like characters to prevent XSS-equivalent rendering issues in web builds
ui components
Card with InkWell (card-clickable pattern)
StatusBadge widget (reusable, accepts label + color)
IconBadge widget (icon + active/inactive state)
TechBadge widget (violet, small text label)
Design token Typography: titleMedium for card title, bodySmall/textTertiary for subtext

Execution Context

Execution Tier
Tier 8

Tier 8 - 48 tasks

Can start after Tier 7 completes

Implementation Notes

Make `ActivityTypeListItem` a `StatelessWidget` accepting `({required ActivityType activityType, required VoidCallback onTap})`. Resolve the Bufdir label by calling `BufdirCategories.labelForKey(activityType.bufdirCategory)` — this must be a pure synchronous lookup. For icon badges, create a small private `_MetadataIconBadge` widget accepting `(IconData icon, bool active, String semanticLabel)` that renders a filled icon in `colorPrimary` when active and an outlined icon in `colorOutline` when inactive. Wrap each icon badge with `Tooltip(message: semanticLabel)` for pointer devices and `Semantics(label: semanticLabel)` for screen readers.

Use `Card` with `InkWell` child and `borderRadius` matching design tokens. Keep all badge colors sourced from the design token system — no hardcoded hex values in this widget.

Testing Requirements

Write widget tests covering: (1) org_label_override visible when set, hidden when null/empty; (2) active status badge rendered green, archived rendered grey; (3) each metadata flag badge renders correctly in true and false state; (4) Bufdir badge shows human-readable label, not enum key; (5) onTap callback fires when card is tapped; (6) Semantics tree contains meaningful labels for all icon badges. Add golden tests for the card in four states: active with all flags true, active with all flags false, archived, and a card with org_label_override set. These goldens should be checked into version control.

Component
Activity Type Admin Screen
ui medium
Epic Risks (3)
high impact medium prob dependency

The Bufdir reporting category list is defined externally by Bufdir and may change between reporting years. If the dropdown in ActivityTypeFormScreen is hardcoded, existing activity type mappings could become invalid after a Bufdir schema update, breaking export validation for all organisations.

Mitigation & Contingency

Mitigation: Store the valid Bufdir category list in a Supabase configuration table (bufdir_categories) rather than as a Dart constant, so it can be updated by an admin without a mobile app release. Load the list in the form screen via a lightweight repository call cached locally.

Contingency: If the Bufdir category list cannot be externalised before the admin screen ships, expose a manual override field that allows coordinators to enter a raw Bufdir category code as a fallback, and schedule the configuration table migration as a follow-up task.

medium impact medium prob technical

Reusing ActivityTypeFormScreen for both creation and editing requires careful Riverpod provider scoping. If the form provider is not properly reset between navigation events, stale values from a previously edited type may pre-populate a new creation form, leading to incorrect data being saved.

Mitigation & Contingency

Mitigation: Scope the form state provider to the route using Riverpod's autoDispose modifier, ensuring the state is torn down when the screen is popped. Write a widget test that navigates to edit type A, pops, navigates to create new, and asserts all fields are empty.

Contingency: If provider scoping proves complex with the current router setup, fall back to separate widget implementations for create and edit that share a common form widget but maintain independent provider instances.

high impact low prob integration

Archiving an activity type must not break historical Bufdir export queries that filter activities by type. If the export pipeline performs an INNER JOIN against only active activity types, archived types will cause historical activities to be silently excluded from exports, producing incorrect reporting data.

Mitigation & Contingency

Mitigation: Audit all downstream query builders (Bufdir export, stats aggregation) before shipping the archive feature to confirm they join against all activity types regardless of is_active status. Add an integration test that archives a type, then asserts historical activity records for that type still appear in export queries.

Contingency: If a downstream query is discovered to filter on is_active post-launch, apply a targeted Supabase view fix that unions active and archived types for export contexts without requiring a mobile app update.