high priority low complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

Pull-to-refresh gesture on the home screen triggers RefreshPeriodicSummaryCard BLoC event
During refresh, card shows a subtle loading indicator (e.g., LinearProgressIndicator at card top) without hiding existing data
On successful refresh, card content updates in place with no full rebuild flash
Dismissal via the × button triggers DismissPeriodicSummaryCard BLoC event
Card animates out smoothly on dismissal (slide-up or fade-out, duration 250ms) before returning SizedBox.shrink()
Dismissed state is persisted to cache repository with the period ID as part of the key
After app restart during the same summary period, dismissed card does not reappear
After the summary period ends and a new period begins, the dismissal is cleared and the card reappears
Pull-to-refresh does not re-show a dismissed card
Animation respects system disableAnimations accessibility setting

Technical Requirements

frameworks
Flutter
BLoC
apis
Summary Cache Repository (internal)
data models
SummaryDismissalRecord
PeriodicSummary
performance requirements
Dismissal animation completes in ≤300ms
Refresh indicator visible within one frame of pull gesture
security requirements
Dismissal key must be scoped per mentor ID and period ID to prevent cross-user state leakage
ui components
AnimatedSize or AnimatedOpacity for card exit animation
RefreshIndicator (Flutter built-in) wrapping the home screen scroll view

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

The `RefreshIndicator` should wrap the home screen `ScrollView`, not just the card — pull-to-refresh is a page-level gesture. The BLoC event should be dispatched from the `onRefresh` callback. For the card exit animation, use `AnimatedSize` wrapping the card content or `AnimatedOpacity` + `SizedBox` combination. Avoid `AnimatedCrossFade` here as it introduces layout overhead for a simple hide.

For dismissal persistence, store `{mentorId}_{periodId}` as the key in the cache repository. The repository should expose a `isCardDismissedForPeriod(mentorId, periodId)` method checked at BLoC load time. Period expiry logic: when loading, compare stored dismissal's period ID to current active period ID — clear if different.

Testing Requirements

Widget tests: (1) simulating a drag gesture on the RefreshIndicator calls the correct BLoC event, (2) BLoC Dismissed state causes card to animate to SizedBox.shrink() (verify final widget tree), (3) repository persist method called with correct dismissal key on dismiss. Integration test: dismiss card, restart app (pump new widget tree with mocked repository returning dismissed state), verify card absent. Unit test on dismissal key generation logic.

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.