high priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

Widget renders two calendar or date-input controls: one for start date and one for end date
When the user selects an end date earlier than the start date, an inline error message is displayed below the end date picker
The error message is cleared automatically when a valid date ordering is restored
onChange(DateTimeRange) callback is invoked only when both dates are selected and end >= start
Selected date cells and navigation arrows meet WCAG 2.2 AA contrast ratio (4.5:1 minimum)
All interactive elements have a minimum touch target of 48 × 48 dp
Screen readers announce the selected start and end dates when focus changes using Semantics
The widget supports keyboard navigation: arrow keys move between days, Enter selects, Tab moves between start/end pickers
The widget accepts optional initialStartDate and initialEndDate values to pre-populate selections
All design token colours are used for backgrounds, selected states, today indicator, and text — no hardcoded colour values

Technical Requirements

frameworks
Flutter
data models
DateTimeRange
performance requirements
Calendar rendering must complete within one frame (16 ms) — avoid heavy computation in the build method
Month navigation must be smooth without visible lag
security requirements
Selected dates must be sanitised to valid DateTime values before being passed to the onChange callback
ui components
CustomDateRangePicker (stateful outer widget)
SingleMonthCalendarView (reusable month grid)
DateRangeErrorMessage (inline error feedback)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Build as a StatefulWidget that internally manages _startDate and _endDate state. Consider wrapping Flutter's built-in CalendarDatePicker or showDateRangePicker as a base — if the built-in widget does not meet accessibility or design requirements, implement a custom grid using a Table widget with GestureDetector cells. Implement date ordering validation in a private _validateAndEmit() method called after every selection change. Use ExcludeSemantics judiciously — never suppress screen reader access to interactive elements.

Apply the project design token system for all colours: use token references such as AppColors.primary for selected state, AppColors.error for the error indicator. Ensure keyboard focus is managed with FocusNode and FocusTraversalGroup so Tab moves logically from start to end picker. Announce selection changes using SemanticsService.announce() or by updating a live Semantics node.

Testing Requirements

Widget tests using flutter_test. Test cases: (1) initial values pre-populate both pickers correctly, (2) selecting end < start shows inline error and does not invoke onChange, (3) correcting the order clears the error and invokes onChange with a valid DateTimeRange, (4) onChange fires with the correct DateTimeRange when both dates are valid, (5) Semantics labels are present on selected date cells, (6) keyboard navigation moves focus between days (simulate key events with tester.sendKeyEvent), (7) touch target size >= 48 dp for day cells and navigation arrows. Include a golden test for default, selected-range, and error states.

Component
Custom Date Range Picker
ui low
Epic Risks (3)
high impact medium prob security

Supabase RLS policies for period preset configuration may be missing or incorrectly scoped, causing one organisation's presets to leak to another or write operations to fail silently.

Mitigation & Contingency

Mitigation: Define and review RLS policies for the bufdir_period_presets table in the migration file before any repository code is written. Include an integration test that verifies cross-organisation isolation using two distinct org credentials.

Contingency: If RLS is misconfigured in production, immediately disable the period preset fetch endpoint and fall back to hardcoded global presets until the policy is corrected and redeployed.

medium impact medium prob technical

The activities table may lack a composite index on (organisation_id, activity_date), causing the range count query in BufdirAggregationRepository to perform a full table scan and exceed acceptable response time for large organisations.

Mitigation & Contingency

Mitigation: Add a migration that creates a composite index on (organisation_id, activity_date) as part of this epic. Benchmark the count query against a representative dataset (10 000+ rows) before marking the epic complete.

Contingency: If query latency is unacceptable after indexing, move the count query to a Supabase RPC function that leverages a materialised view or partial index, accepting a slight staleness window.

medium impact medium prob technical

Flutter's native date picker widgets have known accessibility gaps (missing semantic labels, non-standard focus traversal) that may prevent WCAG 2.2 AA compliance out of the box, requiring a custom implementation.

Mitigation & Contingency

Mitigation: Evaluate third-party accessible date picker packages (e.g., table_calendar with custom semantics) against WCAG 2.2 AA criteria before beginning implementation. Document the chosen approach in the epic kick-off.

Contingency: If no package meets accessibility requirements, implement a simple text-field-based date entry with explicit semantic labels and format hints as an accessible fallback, deferring a fully visual calendar to a later iteration.