medium priority low complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

The last-updated timestamp of the active organization's terminology map is displayed in the TerminologyAdminPreviewScreen
Timestamp is formatted as 'dd.MM.yyyy HH:mm' using Norwegian locale (nb_NO)
When the TerminologyRepository returns a null or missing timestamp, the screen displays 'Never synced' in the timestamp position
Timestamp is shown in the app bar subtitle or a dedicated info card — placement is consistent with the rest of the admin screen design
Timestamp updates reactively when the active organization changes
Timestamp text uses a de-emphasized design token style (text-secondary or text-tertiary equivalent)
Accessibility: timestamp element has a Semantics label such as 'Terminology last updated: [date]' or 'Terminology never synced'
Widget test covers both the populated timestamp case and the null/never-synced case

Technical Requirements

frameworks
Flutter
Riverpod
apis
TerminologyRepository.getLastUpdatedTimestamp(orgId)
data models
OrganizationLabelMap (lastUpdatedAt: DateTime?)
performance requirements
Timestamp retrieval must be part of the existing OrganizationLabelMap load — no additional network call
security requirements
Timestamp must not expose internal system identifiers or server paths
ui components
AppBar subtitle Text widget OR InfoCard widget
Semantics wrapper with label override

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Use the intl package (already likely a transitive dependency via Flutter) with DateFormat('dd.MM.yyyy HH:mm', 'nb_NO') for formatting. Ensure 'nb' locale is initialized via initializeDateFormatting() at app startup — if not already done, add it to the app's locale initialization block, not inside this widget. The lastUpdatedAt field should already exist on OrganizationLabelMap if the data model is complete; if not, coordinate with the backend team to add it to the Supabase response. Prefer placing the timestamp in the app bar subtitle (via AppBar(title: ..., bottom: PreferredSize(...))) rather than a separate card to keep the screen uncluttered.

Use a ternary on the nullable DateTime: timestamp != null ? DateFormat(...).format(timestamp) : 'Never synced'.

Testing Requirements

Write flutter_test widget tests for: (1) populated DateTime renders formatted as 'dd.MM.yyyy HH:mm', (2) null timestamp renders 'Never synced' string, (3) Semantics node contains correct label text for both cases. Unit-test the timestamp formatting function in isolation with known DateTime inputs including edge cases (midnight, year boundary). Verify Norwegian locale formatting by asserting the exact formatted string for a known DateTime value.

Epic Risks (3)
high impact medium prob integration

WcagSemanticsLabelResolver's Semantics wrappers may conflict with Semantics nodes already defined by existing accessible widgets (e.g., accessible-bottom-navigation, activity-wizard-semantics), causing duplicate or contradictory screen reader announcements that fail WCAG 2.2 AA criteria.

Mitigation & Contingency

Mitigation: Audit all existing Semantics-annotated widgets in the accessibility feature before implementing WcagSemanticsLabelResolver. Define a clear hierarchy rule: WcagSemanticsLabelResolver always merges with, never replaces, existing Semantics nodes. Use Flutter's debugSemantics output in CI to detect conflicts automatically.

Contingency: If conflicts are discovered in testing, introduce a resolverMode parameter to WcagSemanticsLabelResolver allowing it to operate in 'override' or 'merge' mode per call site; coordinate with the Screen Reader Support feature team to align Semantics strategies.

medium impact medium prob technical

If TerminologyAwareTextWidget subscribes to the full terminology map provider rather than the per-key labelProvider family, a single label update will trigger a full widget-tree rebuild across all screens simultaneously, causing jank on devices used by older peer mentors.

Mitigation & Contingency

Mitigation: Implement TerminologyAwareTextWidget using ref.watch(labelProvider(key)) on the per-key family provider from TerminologyRiverpodProviders so that only widgets bound to the changed key rebuild. Verify with Flutter DevTools 'rebuild tracking' in widget tests.

Contingency: If full-map subscriptions slip through code review, add a Riverpod lint rule that flags direct organizationLabelsNotifierProvider subscriptions inside widget build methods and enforces the per-key family pattern.

medium impact low prob security

The TerminologyAdminPreviewScreen requires coordinator-level access, but if role checks rely solely on client-side guard logic without matching Supabase RLS policies, a peer mentor could potentially access the admin preview by manipulating navigation state.

Mitigation & Contingency

Mitigation: Protect the admin preview route with a server-validated role guard that re-fetches the user's role from Supabase on screen initialization, not just from local state. Add a Supabase RLS policy that restricts label map read access for the admin preview endpoint to coordinator and admin roles only.

Contingency: If unauthorized access is discovered in testing, immediately add a middleware role assertion that redirects non-coordinators to the no-access screen and logs the unauthorized navigation attempt for audit.