high priority medium complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

ScenarioPromptNotificationCard is a StatelessWidget that accepts a ScenarioPromptNotification model as its only required parameter
Card renders: scenario title (card-title style), activity summary snippet truncated to 2 lines, relative timestamp badge (e.g. '2 hours ago'), and a primary CTA button labeled per scenario configuration
Card uses only design token classes from styles.css — zero inline style properties on any widget
Card background uses the dark-mode card surface color token consistent with other notification card types in the app
Timestamp badge uses badge-story or equivalent Design System v3 badge class with sky color (#38bdf8) for story/notification entity type
Activity summary snippet text is HTML-escaped before rendering to prevent injection
CTA button is an AppButton widget (existing shared component) — not a custom button
Card renders correctly at all three Flutter text scale factors: 1.0, 1.3, and 1.5 (no overflow or clipping)
Card is accessible: semantic label on CTA button includes scenario title for screen reader context; timestamp uses Semantics widget with full absolute date as label
Widget test: golden snapshot matches Design System v3 dark-mode reference at 390px width
Widget test: renders with minimum data (title + timestamp, empty snippet) without throwing

Technical Requirements

frameworks
Flutter
Design System v3 (styles.css / design tokens)
data models
activity
performance requirements
Widget must be const-constructible where possible to minimize rebuild cost in list scrolling
Avoid any FutureBuilder or async work inside the card widget — data must be pre-fetched by the parent
security requirements
All string content from ScenarioPromptNotification model must be rendered via Text() — never via HtmlWidget or WebView
Activity snippet must be sanitized: strip any HTML tags before truncation
Do not render peer mentor PII (full name, contact details) in the notification card — use first name only or anonymized reference
ui components
AppButton (existing shared CTA button)
Timestamp badge widget (relative time display)
card / card-clickable container
card-title text style
card-desc text style (2-line truncation)
card-meta text style (timestamp)

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Define the widget in lib/features/notifications/widgets/scenario_prompt_notification_card.dart. Accept ScenarioPromptNotification data class with fields: scenarioTitle (String), activitySnippet (String), dispatchedAt (DateTime), ctaLabel (String), scenarioId (String), activityId (String). Use design token constants from your token system (AppColors, AppTextStyles, AppSpacing) rather than hardcoded hex values. For the 2-line snippet truncation, use Text(snippet, maxLines: 2, overflow: TextOverflow.ellipsis).

For relative timestamp, compute in a helper: if < 60min show 'X minutes ago', if < 24h show 'X hours ago', else show formatted date. Wrap the CTA tap handler with an empty onTap: () {} stub — the actual deep-link implementation is task-007. Do not implement navigation logic in this widget.

Testing Requirements

Widget tests with flutter_test. Test cases: (1) widget renders all four elements (title, snippet, timestamp, CTA) given a fully populated ScenarioPromptNotification; (2) widget renders without error when activity snippet is empty string; (3) text scale factor 1.5 does not cause RenderFlex overflow — use MediaQuery override in test; (4) Semantics tree contains a label on the CTA button that includes the scenario title; (5) HTML tags in snippet string are stripped before render — verify rendered text contains no '<' characters; (6) golden test at 390px width in dark mode. Run goldens with: flutter test --update-goldens on first generation.

Component
Scenario Prompt Notification Card
ui medium
Epic Risks (2)
high impact medium prob technical

If the scheduler runs concurrently (e.g., two overlapping cron invocations due to edge function retry), duplicate prompts could be dispatched before the first run's history records are committed, breaking the deduplication guarantee.

Mitigation & Contingency

Mitigation: Use a Postgres advisory lock or unique constraint on (user_id, scenario_id, activity_ref) in the prompt history table to make concurrent writes idempotent; design the scheduler to check history inside a transaction.

Contingency: If concurrency issues persist in production, add a distributed lock via Supabase Edge Function concurrency limit (max_instances=1) for the evaluation function as a hard guard.

medium impact medium prob scope

Coordinators may find scenario configuration unclear if trigger conditions are expressed as raw JSON or technical terminology, leading to misconfiguration and irrelevant prompts being sent to peer mentors.

Mitigation & Contingency

Mitigation: Design the ScenarioConfigurationScreen to display human-readable descriptions of each template's trigger condition (e.g., 'Send 3 days after first contact if wellbeing concern was flagged') rather than raw rule properties; validate with an HLF coordinator in a design review before implementation.

Contingency: If coordinators still misconfigure rules after launch, add a preview mode that shows a simulated prompt based on a test activity before the rule is enabled.