critical priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

PeriodicSummaryCardBloc defined with sealed state classes: PeriodicSummaryCardInitial, PeriodicSummaryCardLoading, PeriodicSummaryCardLoaded, PeriodicSummaryCardError, PeriodicSummaryCardDismissed
PeriodicSummaryCardEvent sealed with: LoadPeriodicSummaryCard, RefreshPeriodicSummaryCard, DismissPeriodicSummaryCard events
On LoadPeriodicSummaryCard: BLoC emits Loading then fetches from summary cache repository; emits Loaded on success with PeriodicSummary payload
On RefreshPeriodicSummaryCard: re-fetches data, shows Loading briefly (300ms min), then emits Loaded or Error
On DismissPeriodicSummaryCard: persists dismissed state to cache repository and emits Dismissed state
On network error when cache is available: emits Loaded with cached data and a `isFromCache: true` flag
On network error with no cache: emits Error state with a localised error message
BLoC is registered via BlocProvider at the appropriate widget tree level (not globally)
BLoC closes and cancels subscriptions on close() without memory leaks
Dismissed state persists across hot restarts (stored in cache repository, not in-memory)

Technical Requirements

frameworks
Flutter
BLoC
apis
Summary Cache Repository (internal)
Supabase (via repository abstraction)
data models
PeriodicSummary
SummaryDismissalRecord
performance requirements
Initial load should complete within 500ms on cached data
BLoC must not block the UI thread — repository calls must be async
security requirements
Dismissed state stored with mentor ID key to prevent cross-user contamination on shared devices
Repository must not cache raw Supabase auth tokens

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use the `bloc` and `flutter_bloc` packages (already in stack). Define states as a sealed class hierarchy with Equatable for value equality in tests. The BLoC should depend on an abstract `SummaryCardRepository` interface injected via constructor — never instantiate Supabase directly in the BLoC. For the offline-with-cache flow, catch `SocketException` / `TimeoutException` in the repository and return a `CachedResult` wrapper.

The dismissed state key should be `dismissed_summary_card_{mentorId}_{periodId}` stored via the cache repository. Ensure `on` handlers use `emit.isDone` checks if the handler is async and the BLoC might be closed mid-flight.

Testing Requirements

Bloc_test package unit tests covering: (1) LoadPeriodicSummaryCard emits [Loading, Loaded] on success, (2) LoadPeriodicSummaryCard emits [Loading, Loaded(isFromCache: true)] on offline with cache, (3) LoadPeriodicSummaryCard emits [Loading, Error] on offline with no cache, (4) RefreshPeriodicSummaryCard re-fetches and updates state, (5) DismissPeriodicSummaryCard emits Dismissed and calls repository persist. Mock the repository with mocktail. Aim for 100% branch coverage on the BLoC handlers.

Component
Periodic Summary Digest Card
ui medium
Epic Risks (3)
medium impact medium prob technical

If the cached summary is from a prior period (e.g., previous quarter's card is still cached), the digest card could appear on the home screen with outdated numbers after a new period begins, confusing users who have already seen that data.

Mitigation & Contingency

Mitigation: In the SummaryCacheRepository, store the period_start and period_end alongside cached data. In the PeriodicSummaryCard BLoC, compare the cached record's period against the current date using PeriodCalculatorService. Only render the card if the cached summary belongs to the active period.

Contingency: If stale cards appear in production, push a hotfix that adds the period validity check to the BLoC and clears all cached summaries older than the current period boundary via a forced cache flush on next app launch.

medium impact medium prob technical

Using colour alone to distinguish underactive from overloaded mentors in the coordinator view would fail WCAG 1.4.1 (use of colour). This is especially critical for organisations serving users with colour vision deficiency.

Mitigation & Contingency

Mitigation: Always pair colour indicators with a text label or icon (e.g., a downward arrow + 'Underactive' text alongside the amber colour). Use the contrast-safe-color-palette design tokens throughout. Run the contrast-ratio-validator on all new colour combinations in CI.

Contingency: If an accessibility audit flags colour-only indicators post-launch, introduce icon-based indicators as a patch release and update the design token definitions to enforce the paired-indicator pattern going forward.

low impact medium prob integration

Inserting a new card into the role-based home screen requires changes to shared home screen widget composition, which may conflict with parallel feature branches also modifying the home screen layout, causing merge conflicts and integration delays.

Mitigation & Contingency

Mitigation: Implement the PeriodicSummaryCard as a fully self-contained widget that the home screen conditionally renders based on a BLoC state flag. Minimise changes to the home screen itself to a single conditional insertion point, reducing the surface area for merge conflicts.

Contingency: If integration conflicts block the PR, isolate the card behind the organisation-scoped feature flag so it can be toggled independently and merged without affecting other home screen changes.