# CLAUDE.md - BuyerKiosk Live Flutter App

## Project Overview
Flutter mobile app (iOS/Android) migrated from Xamarin.Forms. Real-time retail store management for tracking buyer queues, completed transactions, buyer performance, workbook notes (announcements with reactions/comments), and daily KPI metrics.

## Tech Stack
- **Flutter 3.38.3** / Dart 3.10.1
- **State Management**: Riverpod 3.x (AsyncNotifier pattern)
- **Navigation**: GoRouter 17.x with auth redirects
- **Models**: Freezed 3.x (abstract classes required)
- **Entities**: Equatable (for domain layer entities)
- **HTTP**: Dio with interceptors
- **Storage**: flutter_secure_storage for JWT tokens
- **Charts**: fl_chart 1.x
- **Forms**: flutter_form_builder
- **Real-time**: Ably Flutter (realtime messaging + push notifications)
- **Push Notifications**: Firebase Cloud Messaging via Ably

## Architecture
Clean Architecture with Riverpod:
```
lib/
├── core/           # Constants, theme, network, utils, errors
├── data/           # Models, datasources, repository implementations, mappers
├── domain/         # Entities (Equatable), repository interfaces
├── presentation/   # Providers, screens, widgets
└── router/         # GoRouter configuration
```

## API Configuration
- **Dev**: `https://try.buyerkiosk.com/api/` (current)
- **Prod**: `https://buyerkiosk.com/api/`
- Change in: `lib/core/constants/api_constants.dart`

### Endpoints (JWT Authenticated)
All endpoints use POST method and JWT Bearer authentication.

| Endpoint | Purpose |
|----------|---------|
| `mobile/auth/login` | JWT login (email, password, device info) |
| `mobile/auth/refresh` | Refresh JWT access token |
| `mobile/auth/logout` | Invalidate tokens |
| `mobile/dashboard` | All stores with metrics |
| `mobile/storePage/:typeNum` | Single store details |
| `mobile/storeStats/:typeNum` | Comprehensive store statistics |
| `mobile/currentQueue/:typeNum` | Buy queue |
| `mobile/completedBuys/:typeNum` | Completed transactions |
| `mobile/buyerStats/:typeNum` | Buyer performance |
| `mobile/todayPerformance/:typeNum` | Daily KPI metrics |
| `mobile/notes/:typeNum` | Get paginated workbook notes |
| `mobile/notes/:typeNum/create` | Create a new note |
| `mobile/notes/:typeNum/:noteId/react` | Add reaction (like/heart) |
| `mobile/notes/:typeNum/:noteId/unreact` | Remove reaction |
| `mobile/notes/:typeNum/:noteId/comment` | Add comment |
| `mobile/notes/:typeNum/:noteId/comments` | Get comments for note |
| `mobile/notes/:typeNum/createByName` | Create note using author name |
| `mobile/notes/:typeNum/:noteId/update` | Update note fields |
| `mobile/notes/:typeNum/:noteId/delete` | Soft delete note |
| `mobile/tasks/:typeNum` | Get tasks (optional groupId filter) |
| `mobile/tasks/:typeNum/groups` | Get task groups |
| `mobile/tasks/:typeNum/create` | Create a task |
| `mobile/tasks/:typeNum/:taskId/update` | Update a task |
| `mobile/tasks/:typeNum/:taskId/delete` | Delete a task |
| `mobile/tasks/:typeNum/groups/create` | Create a task group |
| `mobile/tasks/:typeNum/groups/:groupId/update` | Update a task group |
| `mobile/tasks/:typeNum/groups/:groupId/delete` | Delete a task group |
| `mobile/workbook/:typeNum/lists` | Get today's task lists with completion status |
| `mobile/workbook/:typeNum/lists/active` | Get active list with carryover tasks |
| `mobile/workbook/:typeNum/completed` | Get completed tasks for a date |
| `mobile/workbook/:typeNum/tasks/:taskId/status` | Update task completion status |
| `mobile/workbook/:typeNum/tasks/:taskId/comment` | Add comment to task |
| `mobile/workbook/:typeNum/tasks/:taskId/comments` | Get task comments |

**Note**: All endpoints use JWT Bearer token in Authorization header. Auth interceptor handles token injection and refresh.

## Key Files

### Core
- `lib/core/constants/api_constants.dart` - API URLs and endpoints
- `lib/core/constants/ably_constants.dart` - Ably API key
- `lib/core/constants/push_notification_constants.dart` - Push notification types and channel naming
- `lib/core/network/api_client.dart` - Dio HTTP client
- `lib/core/network/api_interceptors.dart` - JWT auth interceptor (adds Bearer token, handles 401, token refresh)
- `lib/core/services/push_notification_service.dart` - Ably push notification service
- `lib/core/theme/app_theme.dart` - Material 3 theme (based on STYLE_GUIDE.md)
- `lib/core/theme/app_colors.dart` - App color palette (based on STYLE_GUIDE.md)
- `lib/core/constants/navigation_constants.dart` - Navigation categories and features (Spec 005)
- `lib/core/services/navigation_analytics.dart` - Navigation analytics event logging

### Data Models (Freezed 3.x - use `abstract class`)
- `lib/data/models/store_model.dart`
- `lib/data/models/store_detail_model.dart`
- `lib/data/models/queue_item_model.dart`
- `lib/data/models/completed_buy_model.dart`
- `lib/data/models/buyer_stats_model.dart`
- `lib/data/models/workbook_note_model.dart` - Workbook notes model
- `lib/data/models/workbook_comment_model.dart` - Workbook comments model
- `lib/data/models/verify_response_model.dart` - API key verification response (user, stores, employees)
- `lib/data/models/today_performance_model.dart` - KPI metrics model
- `lib/data/models/task_model.dart` - Task model
- `lib/data/models/task_group_model.dart` - Task group model
- `lib/data/models/workbook_task_list_model.dart` - Workbook task list/item models for daily completion
- `lib/data/models/scheduling/` - Scheduling Freezed models (auth, requests, schedule, labor)

### Domain Entities (Equatable - plain classes)
- `lib/domain/entities/store.dart`
- `lib/domain/entities/store_detail.dart`
- `lib/domain/entities/queue_item.dart`
- `lib/domain/entities/completed_buy.dart`
- `lib/domain/entities/buyer_stats.dart`
- `lib/domain/entities/workbook_note.dart` - Workbook note entity
- `lib/domain/entities/workbook_comment.dart` - Workbook comment entity
- `lib/domain/entities/today_performance.dart` - KPI metrics entity
- `lib/domain/entities/task.dart` - Task entity
- `lib/domain/entities/task_group.dart` - Task group entity
- `lib/domain/entities/workbook_task_list.dart` - Workbook task list/item entities for daily completion
- `lib/domain/entities/scheduling/` - Scheduling domain entities (store access, requests, schedule, labor)

### Mappers
- `lib/data/models/mappers/today_performance_mapper.dart` - Model to entity conversion
- `lib/data/models/mappers/workbook_note_mapper.dart` - Workbook note mapper
- `lib/data/models/mappers/workbook_comment_mapper.dart` - Workbook comment mapper
- `lib/data/models/mappers/task_mapper.dart` - Task mapper
- `lib/data/models/mappers/task_group_mapper.dart` - Task group mapper
- `lib/data/models/mappers/workbook_task_list_mapper.dart` - Workbook task list mapper
- `lib/data/models/mappers/scheduling/` - Scheduling mappers (auth, requests, schedule, labor)

### Providers (Riverpod 3.x - use `AsyncNotifier`)
- `lib/presentation/providers/providers.dart` - Base providers, exports
- `lib/presentation/providers/auth_provider.dart` - Auth state
- `lib/presentation/providers/dashboard_provider.dart` - Store list (with auth error caching)
- `lib/presentation/providers/store_detail_provider.dart` - Store details (family)
- `lib/presentation/providers/queue_provider.dart` - Queue items (family)
- `lib/presentation/providers/completed_buys_provider.dart` - Completed (family)
- `lib/presentation/providers/buyer_stats_provider.dart` - Stats (family)
- `lib/presentation/providers/workbook_notes_provider.dart` - Workbook notes & comments (family)
- `lib/presentation/providers/today_performance_provider.dart` - KPI metrics (family)
- `lib/presentation/providers/task_provider.dart` - Tasks & task groups (family)
- `lib/presentation/providers/today_tasks_provider.dart` - Today's tasks with completion tracking (family)
- `lib/presentation/providers/ably_provider.dart` - Real-time Ably subscriptions (family)
- `lib/presentation/providers/permission_provider.dart` - Role-based access control
- `lib/presentation/providers/push_notification_provider.dart` - Push notification state management
- `lib/presentation/providers/scheduling/` - Scheduling providers (context bridge, requests, schedule, labor)
- `lib/presentation/providers/navigation/` - Store navigation state (Spec 005)
  - `store_navigation_provider.dart` - Tab state, visible tabs/features, badges, store access history
  - `navigation_providers.dart` - Barrel export

### Screens
- `lib/presentation/screens/installation/installation_screen.dart` - API key entry
- `lib/presentation/screens/dashboard/dashboard_screen.dart` - Store list
- `lib/presentation/screens/settings/settings_screen.dart` - API key management
- `lib/presentation/screens/settings/permission_settings_screen.dart` - Permission configuration (Owner only)
- `lib/presentation/screens/store_detail/store_detail_screen.dart` - Store metrics & navigation
- `lib/presentation/screens/queue/buy_queue_screen.dart` - Queue list
- `lib/presentation/screens/completed/completed_buys_screen.dart` - Completed list
- `lib/presentation/screens/stats/buyer_stats_screen.dart` - Buyer performance
- `lib/presentation/screens/workbook_notes/workbook_notes_screen.dart` - Notes feed with infinite scroll, swipe actions
- `lib/presentation/screens/workbook_notes/workbook_note_detail_screen.dart` - Note detail with comments, edit/delete menu
- `lib/presentation/screens/workbook_notes/workbook_note_create_screen.dart` - Create note (uses createByName endpoint)
- `lib/presentation/screens/workbook_notes/workbook_note_edit_screen.dart` - Edit existing note
- `lib/presentation/screens/store_metrics/store_metrics_screen.dart` - Daily KPI dashboard
- `lib/presentation/screens/tasks/tasks_screen.dart` - Edit Task List - manage task definitions
- `lib/presentation/screens/tasks/task_create_screen.dart` - Create new task
- `lib/presentation/screens/tasks/task_edit_screen.dart` - Edit existing task
- `lib/presentation/screens/tasks/today_tasks_screen.dart` - Today's Tasks - view/complete daily tasks
- `lib/presentation/screens/scheduling/` - Scheduling screens (login, dashboard, requests, schedule, labor)
- `lib/presentation/screens/backstock/bin_search_screen.dart` - Bin search with filters, FAB create, scanner, hidden toggle
- `lib/presentation/screens/backstock/bin_detail_screen.dart` - Bin detail with CRUD actions, edit, hide/reactivate, delete
- `lib/presentation/screens/backstock/bin_create_screen.dart` - Create bin form with generate name, category/location pickers
- `lib/presentation/screens/backstock/bin_edit_screen.dart` - Edit bin form with dirty checking, generate name

### Widgets
- `lib/presentation/widgets/common/error_widget.dart` - Error display with `ErrorDisplay.fromError()` factory
- `lib/presentation/widgets/common/loading_indicator.dart`
- `lib/presentation/widgets/common/empty_state.dart`
- `lib/presentation/widgets/common/section_header.dart`
- `lib/presentation/widgets/common/metric_card.dart`
- `lib/presentation/widgets/common/store_logo.dart` - Franchise logo widget (storeType or typeNum based)
- `lib/presentation/widgets/cards/workbook_note_card.dart` - Note card with reactions
- `lib/presentation/widgets/scheduling/` - Scheduling widgets (request cards, schedule grid, labor summary)
- `lib/presentation/widgets/backstock/` - Backstock widgets (Spec 006)
  - `barcode_scanner_sheet.dart` - Barcode scanner bottom sheet with camera, flash, lookup
  - `category_picker_sheet.dart` - Searchable category picker sorted by bin count
  - `location_picker_sheet.dart` - Location picker with onsite/offsite grouping
  - `bin_action_sheet.dart` - Action type picker with category/location sub-pickers
  - `hidden_bins_sheet.dart` - Hidden bins list with reactivation
- `lib/presentation/widgets/navigation/` - Store navigation widgets (Spec 005)
  - `store_navigation_scaffold.dart` - Main scaffold with tab bar and sheets
  - `store_navigation_tab_bar.dart` - Bottom tab bar with badges
  - `category_bottom_sheet.dart` - Category feature sheet
  - `navigation_sheet_item.dart` - Individual feature item
  - `store_switcher_header.dart` - Header with store logo and dropdown
  - `store_switcher_sheet.dart` - Store selection sheet

### Router
- `lib/router/app_router.dart` - All routes, auth redirect logic

## Navigation Flow
```
/install → InstallationScreen (when no API key)
/ → DashboardScreen (store list)
/settings → SettingsScreen (API key management)
/store/:typeNum → StoreDetailScreen
/store/:typeNum/queue → BuyQueueScreen
/store/:typeNum/completed → CompletedBuysScreen
/store/:typeNum/stats → BuyerStatsScreen
/store/:typeNum/notes → WorkbookNotesScreen (announcements with reactions/comments)
/store/:typeNum/notes/create → WorkbookNoteCreateScreen
/store/:typeNum/metrics → StoreMetricsScreen (Today's Performance KPIs)
/store/:typeNum/tasks/today → TodayTasksScreen (view/complete daily tasks)
/store/:typeNum/tasks/edit → TasksScreen (Edit Task List - manage task definitions)
/store/:typeNum/tasks/edit/create → TaskCreateScreen
/store/:typeNum/tasks/edit/:taskId → TaskEditScreen
/store/:typeNum/backstock → BackstockDashboardScreen
/store/:typeNum/backstock/events → SeasonalEventsScreen
/store/:typeNum/backstock/events/create → EventFormScreen (create)
/store/:typeNum/backstock/events/:eventId → EventFormScreen (detail)
/store/:typeNum/backstock/events/:eventId/edit → EventFormScreen (edit)
/store/:typeNum/backstock/notes → BackstockNotesScreen
/store/:typeNum/backstock/reports → BackstockReportsScreen
/store/:typeNum/backstock/bins → BinSearchScreen (search, filter, create, scan)
/store/:typeNum/backstock/bins/create → BinCreateScreen (optional ?prefilledName query param)
/store/:typeNum/backstock/bins/:binId → BinDetailScreen (actions, edit, hide, delete)
/store/:typeNum/backstock/bins/:binId/edit → BinEditScreen (dirty checking)
/settings/permissions → PermissionSettingsScreen (Owner only)
/scheduling/dashboard → SchedulingDashboardScreen
/scheduling/requests → TimeOffRequestsScreen
/scheduling/requests/:id → RequestDetailScreen
/scheduling/schedule → ScheduleViewScreen
/scheduling/labor → LaborCostScreen
```

## Commands

```bash
# Run app
flutter run

# Run on specific device
flutter run -d iphone

# Regenerate Freezed/JSON code
dart run build_runner build --delete-conflicting-outputs

# Analyze for errors
flutter analyze

# Build APK
flutter build apk --debug

# Build iOS
flutter build ios --debug --no-codesign
```

## Riverpod 3.x Pattern
```dart
class MyNotifier extends AsyncNotifier<MyType> {
  @override
  Future<MyType> build() async {
    return await ref.read(repoProvider).getData();
  }

  Future<void> refresh() async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() => ref.read(repoProvider).getData());
  }
}

final myProvider = AsyncNotifierProvider<MyNotifier, MyType>(MyNotifier.new);

// Family pattern (with parameter)
class MyFamilyNotifier extends AsyncNotifier<MyType> {
  MyFamilyNotifier(this.id);
  final String id;
  // ...
}
final myFamilyProvider = AsyncNotifierProvider.family<...>(MyFamilyNotifier.new);
```

## Freezed 3.x Pattern (for Data Models)
```dart
@freezed
abstract class MyModel with _$MyModel {
  const factory MyModel({
    required String id,
    required String name,
  }) = _MyModel;

  factory MyModel.fromJson(Map<String, dynamic> json) => _$MyModelFromJson(json);
}
```

## Equatable Pattern (for Domain Entities)
```dart
class MyEntity extends Equatable {
  final String id;
  final String name;

  const MyEntity({required this.id, required this.name});

  @override
  List<Object?> get props => [id, name];
}
```

## API JSON Parsing Notes
- API may return numbers as strings (e.g., `"current": "1875.25"`)
- Use `@JsonKey(fromJson: _parseDouble)` with helper functions:
```dart
double _parseDouble(dynamic value) {
  if (value == null) return 0.0;
  if (value is num) return value.toDouble();
  if (value is String) return double.tryParse(value) ?? 0.0;
  return 0.0;
}
```

## Auth Flow (JWT Authentication)
1. App checks secure storage for valid JWT tokens
2. If no tokens or expired → redirect to `/auth/login`
3. User enters email/password → authenticates via `/mobile/auth/login`
4. On success → stores access token, refresh token, expiry, user info, and employee links
5. Auth interceptor automatically adds Bearer token to all API requests
6. On 401 response → attempts token refresh via `/mobile/auth/refresh`
7. If refresh fails → clears tokens and redirects to login
8. Token proactive refresh: interceptor refreshes token 5 minutes before expiry

## User Data Storage
On successful JWT login, the app stores:
- User info: `userId`, `userDisplayName`, `userEmail`
- Employee links: Maps store typeNum to employee ID for auto-attaching notes/comments
- Access via: `employeeIdForStoreProvider(typeNum)`, `userDisplayNameProvider`

## Error Handling
- Use `ErrorDisplay.fromError(error: error, onRetry: ...)` for consistent error messages
- Invalid API key errors show explicit "Invalid API key" message
- Dashboard provider caches auth errors to prevent API spam on invalid keys

## Workbook Notes Features
- **Infinite scroll**: Paginated notes feed with load more on scroll
- **Reactions**: Like/heart reactions with employee tracking
- **Comments**: Threaded comments on notes
- **Create note**: Uses `createByName` endpoint with author name (auto-matches to employee)
- **Edit note**: Update title, content, dates, pinned/manager-only flags
- **Delete note**: Soft delete with confirmation dialog
- **Swipe actions**: Swipe right for Edit, swipe left for Delete
- **Author pre-fill**: Author name pre-filled from stored user display name

## Task Management Features

### Edit Task List (task definitions)
- **Tasks organized by groups**: Tasks displayed in expandable sections by group name
- **Group filtering**: Filter chips to show tasks from specific groups
- **Priority levels**: High (red), Normal (purple), Low (grey) with visual indicators
- **Recurrence days**: Tasks can recur on specific days of week (MON, TUE, etc.)
- **Date range**: Optional start and end dates for seasonal/temporary tasks
- **Time of day**: Optional recommended time for task completion
- **Create task**: Form with task name, description, group, priority, recurrence, dates
- **Edit task**: Update any task field with dirty checking
- **Delete task**: Permanent delete with confirmation dialog
- **Swipe actions**: Swipe right for Edit, swipe left for Delete
- **Task groups**: CRUD operations for task groups (OPENING, CLOSING, etc.)

### Today's Tasks (daily completion tracking)
- **Progress summary**: Header with completion percentage and circular progress indicator
- **Task lists by time**: OPENING, CLOSING, etc. with individual progress bars
- **Carryover tasks**: Incomplete tasks from earlier lists highlighted in red
- **Status tracking**: Not Started, In Progress, Completed with visual indicators
- **Toggle completion**: Tap checkbox or use popup menu to change status
- **Who completed**: Shows employee name and timestamp for completed tasks
- **Task details**: Bottom sheet with full task info and action buttons
- **Add notes**: Add comments to tasks during completion
- **Employee auto-attach**: Uses stored employee ID for status updates and comments

## Manager Scheduling Features

Comprehensive scheduling module for managers to approve/deny time-off requests, view team schedules, and manage labor costs. Uses the **unified JWT auth system** — the scheduling module has no separate login. Authentication is handled by the main app's auth provider, and the `SchedulingContextProvider` bridges main auth state to scheduling-compatible formats.

### Scheduling Architecture
```
lib/
├── core/
│   ├── constants/scheduling/
│   │   ├── scheduling_api_constants.dart    # Scheduling API base URL, endpoints
│   │   └── scheduling_constants.dart        # Request types, status values, role mappings
│   ├── network/scheduling/
│   │   └── scheduling_api_client.dart       # Dedicated Dio client (uses UnifiedAuthInterceptor)
│   └── services/scheduling/
│       └── scheduling_services.dart         # Analytics, latency tracking
├── data/
│   ├── datasources/scheduling/
│   │   └── scheduling_remote_data_source.dart
│   ├── models/scheduling/                   # Freezed models for API responses
│   └── repositories/
│       └── scheduling_repository_impl.dart
├── domain/
│   ├── entities/scheduling/                 # Equatable domain entities
│   │   ├── scheduling_auth_state.dart       # StoreAccess entity (scheduling store info)
│   │   ├── time_off_request.dart            # Request entity with approval status
│   │   ├── schedule_entry.dart              # Individual shift entry
│   │   ├── labor_cost.dart                  # Weekly labor breakdown
│   │   └── scheduling_entities.dart         # Barrel export
│   └── repositories/
│       └── scheduling_repository.dart       # Repository interface
└── presentation/
    ├── providers/scheduling/
    │   ├── scheduling_auth_provider.dart    # Infrastructure only (API client, repository providers)
    │   ├── scheduling_context_provider.dart # Bridges main auth → scheduling StoreAccess
    │   ├── time_off_requests_provider.dart  # Pending/history with filters
    │   ├── schedule_provider.dart           # Weekly schedule grid
    │   └── labor_cost_provider.dart         # Weekly labor summary
    ├── screens/scheduling/
    │   ├── scheduling_dashboard_screen.dart # Manager overview
    │   ├── time_off_requests_screen.dart    # Pending requests list
    │   ├── request_detail_screen.dart       # Approve/deny with notes
    │   ├── schedule_view_screen.dart        # Weekly grid view
    │   └── labor_cost_screen.dart           # Weekly labor summary
    └── widgets/scheduling/
        ├── request_card.dart                # Time-off request card
        ├── schedule_grid.dart               # Weekly schedule table
        └── labor_summary_card.dart          # Cost summary widget
```

### Scheduling Routes
```
/scheduling/dashboard → SchedulingDashboardScreen
/scheduling/requests → TimeOffRequestsScreen
/scheduling/requests/:id → RequestDetailScreen
/scheduling/schedule → ScheduleViewScreen
/scheduling/labor → LaborCostScreen
```

### Scheduling Auth (Unified)
The scheduling module uses the main app's JWT tokens via `SchedulingContextProvider`:
1. User logs in to the main app (JWT auth)
2. `SchedulingContextProvider` watches `authProvider` and maps `AuthStore` → `StoreAccess`
3. `SchedulingApiClient` uses `UnifiedAuthInterceptor` which reads the same `AuthStorageKeys`
4. If user is a manager or higher → scheduling features are accessible
5. No separate login, no biometric auth, no separate token lifecycle

**IMPORTANT**: The scheduling API client's force-logout callback is a no-op. Scheduling API failures
(e.g., store doesn't have scheduling) must NOT trigger a force-logout that clears main auth tokens.

### Key Scheduling Providers
```dart
// Context bridge (maps main auth → scheduling StoreAccess)
final schedulingContextProvider = Provider<SchedulingContext>((ref) {...});

// Infrastructure (API client and repository)
final schedulingApiClientProvider = Provider<SchedulingApiClient>(...);
final schedulingRepositoryProvider = Provider<SchedulingRepository>(...);

// Time-off requests with filters
final timeOffRequestsProvider = AsyncNotifierProvider.family<TimeOffRequestsNotifier, List<TimeOffRequest>, String>(...);

// Schedule viewing
final weeklyScheduleProvider = AsyncNotifierProvider.family<WeeklyScheduleNotifier, List<ScheduleEntry>, ScheduleParams>(...);

// Labor cost analytics
final laborCostProvider = AsyncNotifierProvider.family<LaborCostNotifier, LaborCost, LaborCostParams>(...);
```

### Scheduling API Endpoints
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/scheduling/requests` | GET | List pending time-off requests |
| `/scheduling/requests/:id` | GET | Get request details |
| `/scheduling/requests/:id/approve` | POST | Approve with optional notes |
| `/scheduling/requests/:id/deny` | POST | Deny with required notes |
| `/scheduling/schedule` | GET | Get weekly schedule for store |
| `/scheduling/labor/cost` | GET | Get weekly labor cost breakdown |

### Request Status Values
```dart
enum RequestStatus {
  pending,   // Awaiting manager decision
  approved,  // Manager approved
  denied,    // Manager denied with notes
  cancelled, // Employee cancelled
}
```

### Usage in Screens
```dart
// Check scheduling access via context bridge
final context = ref.watch(schedulingContextProvider);

if (!context.isAuthenticated) {
  // Main auth handles login — no scheduling-specific login needed
}

// Get pending requests
final requests = ref.watch(timeOffRequestsProvider(storeId));

// Approve a request
await ref.read(schedulingRepositoryProvider).approveRequest(
  requestId: request.id,
  managerNotes: 'Approved - enjoy your time off!',
);
```

## Manager Schedule (Full Edit) — Spec 003 schedule-visibility

The "Full Schedule" screen extends the manager scheduling module with a multi-view editing surface (Day / Week / 2 Weeks / Month), inline edit/publish/unpublish/clear/copy, and real-time Ably reconciliation with last-write-wins (LWW) override toasts.

Normative design lives in `../docs/specs/003-schedule-visibility/` — PRD, SDD, and implementation-plan. This section is a navigation aid, not a substitute.

### Feature Flag and Routing

- **`liveFullScheduleEnabledProvider`** (default `false`) gates the new screen. When true, `/scheduling/schedule` resolves to `FullScheduleScreen`; when false, it falls back to the legacy `weekly_schedule_screen.dart`.
- Toggle in tests via `container.read(liveFullScheduleEnabledProvider.notifier).enable()`.

### View Model (4 modes, default 2 Weeks)

`scheduleViewModeProvider` (`LiveScheduleViewMode` enum) drives the body of `FullScheduleScreen`:
- **Day** — single-day timeline with `HorizontalHourStrip` + coverage panel.
- **Week** — 7-day stacked `DayCard` list with `StatusRow` + Publish CTA.
- **2 Weeks** (default) — 14-day grouped view; published/draft per week.
- **Month** — month grid with density indicators; no Publish CTA (per SDD).

### New Providers (`lib/presentation/providers/scheduling/`)

| Provider | Keyed by | Role |
|----------|----------|------|
| `liveFullScheduleEnabledProvider` | — | Feature flag |
| `scheduleViewModeProvider` | — | Selected view (Day/Week/2W/Month) |
| `selectedScheduleDateProvider` | — | Selected day (Day + Month views) |
| `weekStartProvider` | — | Monday of the active week |
| `monthStartProvider` | — | First-of-month for the Month view |
| `dayExpansionProvider` | dateKey | Per-day card collapse/expand |
| `weekCollapseProvider` | weekStart | Per-week collapse in 2W view |
| `weeklyScheduleProvider` | `WeeklyScheduleArgs(typeNum, weekStart)` | One-week shifts + reducer |
| `twoWeekScheduleProvider` | `WeeklyScheduleArgs` | 14-day shifts + reducer |
| `scheduleEventStreamProvider` | typeNum | Ably stream (`kiosk_{typeNum}` + `kiosk_{typeNum}_manager`) |
| `lwwOverrideDetectedProvider` | typeNum | Side-channel notifier for LWW toast |
| `unpublishScheduleProvider` | typeNum | Unpublish action + optimistic state |
| `clearWeekProvider` | typeNum | Bulk delete with partial-failure result |

### New Widgets (`lib/presentation/widgets/scheduling/`)

- `schedule_topbar.dart` — back + sync + overflow icons
- `schedule_view_segmented.dart` — 4-segment Day/Week/2W/Month pill
- `schedule_context_bar.dart` — range nav + status row container
- `status_row.dart` — published/draft pills + summary + Publish CTA
- `day_view.dart` / `week_view.dart` / `two_week_view.dart` / `month_view.dart` — per-mode bodies
- `day_card.dart` — collapsed/expanded day row used in Week/2W views
- `shift_card.dart` — single shift card with avatar + role pill
- `open_shift_card.dart` — "(Open shift)" card variant
- `horizontal_hour_strip.dart` — lane-packed timeline for Day view
- `coverage_histogram.dart` — per-half-hour coverage bars
- `edit_shift_sheet.dart` — bottom-sheet editor with inline validation
- `overflow_sheet.dart` — Copy / Clear / Unpublish (when published)
- `confirm_sheet.dart` — destructive-action confirmations (publish/unpublish/clear/copy)
- `conflict_warning_dialog.dart` — overlap (block) + availability (warn) alerts
- `edited_badge.dart` — "EDITED" badge on dirty published shifts
- `role_pill.dart` — short role label with neutral fallback

### New Repository Methods (`SchedulingRepository`)

- `unpublishSchedule(typeNum, weekStart)` — reverts a published week to draft.
- `getTwoWeekSchedule(typeNum, weekStart)` — fetches 14 contiguous days as a `TwoWeekSchedule(weekA, weekB, shifts)`.
- `clearWeek(typeNum, weekStart, shiftIds, reason)` — bulk delete with per-shift success/failure reporting.
- `copyWeekSchedule(typeNum, sourceWeekStart, targetWeekStart)` — clones shifts as drafts.

### LWW Override Surface

When the reducer (in `weekly_schedule_provider.dart` / `two_week_schedule_provider.dart`) detects an inbound `shift.updated` event from a foreign actor within 30s of a local edit (tracked in `ShiftNotifier.localEditTimestamps`), it sets `lwwOverrideDetectedProvider(typeNum)`. `FullScheduleScreen` listens and surfaces a `SnackBar`:
> "This shift was changed by [name] [N seconds] ago. Your edit was overridden."

The toast also emits `schedule_shift_edit_conflict_lwwloss` analytics.

### Analytics Events (Spec 003)

`SchedulingAnalyticsService` exposes — and the screen emits — all PRD §Tracking Requirements events:

`schedule_full_view_opened`, `schedule_week_navigated`, `schedule_view_mode_changed`, `schedule_shift_tapped`, `schedule_shift_edit_started`, `schedule_shift_edit_saved`, `schedule_shift_edit_blocked`, `schedule_shift_edit_conflict_lwwloss`, `schedule_published_from_mobile`, `schedule_unpublished_from_mobile`, `schedule_realtime_event_received`, `schedule_week_cleared` (and `_partial` variant), `schedule_copy_last_week`, `schedule_sync_tapped`.

The `schedulingAnalyticsProvider` Riverpod provider wraps the singleton so realtime-event analytics can be plumbed into notifiers (and overridden in tests).

### Pointer

See `../docs/specs/003-schedule-visibility/` for the full normative design (PRD F1–F6, SDD ADRs, Implementation Plan).

## Common Issues
- **White screen**: Check router imports point to actual screens, not placeholders
- **Freezed errors**: Ensure `abstract class` keyword (Freezed 3.x requirement)
- **Riverpod errors**: Use `AsyncNotifier` not `StateNotifier` (Riverpod 3.x)
- **Build errors**: Run `dart run build_runner build --delete-conflicting-outputs`
- **JSON parsing errors**: API may return numbers as strings - use custom `fromJson` converters
- **Entity vs Model**: Use Equatable for domain entities, Freezed for data models
- **Form data vs JSON**: Dio sends JSON by default; use `Options(contentType: Headers.formUrlEncodedContentType)` for form-encoded POST requests

## Assets
Store logos expected in `assets/images/franchises/`:
- `pc_square_alt2.png` - Plato's Closet
- `ouac_square.png` - Once Upon A Child
- `se_square.png` - Style Encore
- `cm_logo.png` - Clothes Mentor
- `pias_square.png` - Play It Again Sports
- `mgr_square.png` - Music Go Round

Priority icons in `assets/images/`:
- `priority0.png` through `priority3.png`

## Original Xamarin App
Located at `../buyerkiosk-live-app` - reference for API structure and business logic.

## Permission System

### Access Levels
Access levels are derived from the employee `role` field in the verify response:
- **Owner (1)**: Full access to all features including permission configuration
- **Manager (2)**: Access to management features like editing task lists
- **Shift Lead (3)**: Access to performance and notes features
- **Employee (4)**: Basic access to queues and today's tasks

### Key Files
- `lib/core/constants/permission_constants.dart` - AccessLevel enum, AppPage enum, default permissions
- `lib/presentation/providers/permission_provider.dart` - PermissionState and PermissionNotifier
- `lib/presentation/screens/settings/permission_settings_screen.dart` - Owner config screen

### How It Works
1. On API key validation, the highest access level across all stores is stored
2. `PermissionProvider` loads access level and custom permission config on init
3. `GoRouter` redirect checks `permissionState.canAccess(page)` for protected routes
4. UI elements (nav cards, settings button) conditionally render based on permissions
5. Owners can customize page access requirements via Permission Settings screen

### Default Page Permissions
| Page | Default Required Level |
|------|----------------------|
| Dashboard, Store Detail, Buy Queue, Completed, Today's Tasks | Employee |
| Buyer Stats, Store Metrics, Shift Notes | Shift Lead |
| Edit Task List | Manager |
| Settings, Permission Settings | Owner |

### Usage in Screens
```dart
// Check if user can access a page
final permissionState = ref.watch(permissionProvider);
if (permissionState.canAccess(AppPage.buyerStats)) {
  // Show buyer stats
}

// Convenience providers
final isOwner = ref.watch(isOwnerProvider);
final canAccess = ref.watch(canAccessPageProvider(AppPage.settings));
```

## Store Navigation System (Spec 005)

Bottom tab bar navigation with category sheets for store detail screens. Replaces the old "Quick Actions" chip-based navigation.

### Architecture

```
StoreNavigationScaffold
├── StoreSwitcherHeader (app bar)
│   └── StoreSwitcherSheet (modal)
├── Body content (scrollable metrics)
└── StoreNavigationTabBar (bottom)
    └── CategoryBottomSheet (modal per category)
        └── NavigationSheetItem (per feature)
```

### Navigation Categories

| Category | Icon | Features |
|----------|------|----------|
| Home | home_rounded | Dashboard metrics (inline, no sheet) |
| Buys | monetization_on_rounded | Buy Queue, Completed, Buyer Stats |
| Sales | trending_up_rounded | Store Metrics, Close Reports |
| Ops | settings_rounded | Today's Tasks, Shift Notes, Backstock |
| Schedule | calendar_today_rounded | Time-Off Requests, Schedule View, Labor Costs |

### Key Providers

```dart
// Selected tab (session-only, defaults to Home)
final selectedNavTabProvider = NotifierProvider<SelectedNavTabNotifier, NavigationCategory>(...);

// Tabs filtered by permission
final visibleTabsProvider = Provider<List<NavigationCategory>>(...);

// Features filtered by permission (per category)
final visibleFeaturesProvider = Provider.family<List<NavigationFeature>, NavigationCategory>(...);

// Badge counts for Buys (queue) and Schedule (pending requests)
final tabBadgeProvider = Provider.family<int?, NavigationCategory>(...);

// Store access history (persisted to secure storage)
final storeAccessHistoryProvider = NotifierProvider<StoreAccessHistoryNotifier, Map<String, DateTime>>(...);

// Stores sorted by last access
final sortedStoresProvider = Provider<List<Store>>(...);
```

### Business Rules

1. Only one sheet open at a time
2. Tapping active tab closes its sheet
3. Tapping Home closes any open sheet
4. Tab state preserved on navigation back
5. Permission-restricted features hidden (not disabled)
6. Rapid taps debounced (100ms, process last tap)

### Usage

```dart
// Wrap store detail content with navigation scaffold
StoreNavigationScaffold(
  typeNum: store.typeNum,
  storeName: store.storeName,
  child: YourStoreContent(),
)
```

## Push Notifications (Ably + FCM)

Push notifications are delivered via Firebase Cloud Messaging (FCM) through Ably. The backend triggers notifications to Ably, which routes them through FCM to devices.

### Key Files
- `lib/core/constants/push_notification_constants.dart` - Notification types and channel naming
- `lib/core/services/push_notification_service.dart` - Ably push service (singleton)
- `lib/presentation/providers/push_notification_provider.dart` - Riverpod state management
- `lib/main.dart` - Early event handler setup (before runApp)
- `android/app/google-services.json` - Firebase Android config
- `ios/Runner/GoogleService-Info.plist` - Firebase iOS config

### Notification Types
```dart
class PushNotificationTypes {
  static const String noteComment = 'note:comment';
  static const String noteMention = 'note:mention';
  static const String taskAssigned = 'task:assigned';
  static const String taskReminder = 'task:reminder';
  static const String queueUpdate = 'queue:update';
  static const String announcement = 'announcement';
}
```

### Channel Naming
```dart
class PushChannels {
  static String forStore(String typeNum) => typeNum.toLowerCase();  // e.g., "bk01"
  static String forUser(int userId) => 'user:$userId';              // e.g., "user:123"
}
```

### Activation Flow
1. Push event handlers set up in `main()` before `runApp()`
2. On dashboard load, `PushNotificationNotifier.activate()` is called
3. Service initializes Ably Realtime client with push support
4. Device activation requested (`push.activate()`)
5. On success, subscribes to all user's store channels + user-specific channel

### Usage
```dart
// Check push state
final pushState = ref.watch(pushNotificationProvider);
print('Activated: ${pushState.isActivated}');
print('Channels: ${pushState.subscribedChannels}');

// Subscribe to additional channel
await ref.read(pushNotificationProvider.notifier).subscribeToStore('bk01');

// Convenience providers
final isActivated = ref.watch(isPushActivatedProvider);
final channels = ref.watch(subscribedPushChannelsProvider);
```

### Backend Push Format (for PHP)
When sending push notifications via Ably from the backend:
```php
$channel->publish('notification', [
    'data' => [
        'type' => 'note:comment',
        'typeNum' => 'bk01',
        'noteId' => 123,
        'title' => 'New Comment',
        'body' => 'John commented on your note'
    ],
    'push' => [
        'notification' => [
            'title' => 'New Comment',
            'body' => 'John commented on your note'
        ],
        'data' => [
            'type' => 'note:comment',
            'typeNum' => 'bk01',
            'noteId' => '123'
        ]
    ]
]);
```

### iOS Configuration Required
1. Open `ios/Runner.xcworkspace` in Xcode
2. Select Runner target → Signing & Capabilities
3. Add "Push Notifications" capability
4. Add "Background Modes" → Enable "Remote notifications"
5. Configure APNs certificates in Ably dashboard

### Android Configuration
Already configured:
- `google-services.json` in `android/app/`
- Google Services plugin in `build.gradle`
- POST_NOTIFICATIONS permission in AndroidManifest.xml
- FCM receiver configuration

## API Documentation
- `mobile-api-verify.md` - Verify endpoint (API key validation, user info, employee links)
- `mobile-api-today-performance.md` - Today's Performance KPI endpoint documentation
- `mobile-api-workbook-notes.md` - Workbook Notes API documentation (pagination, reactions, comments)
- `mobile-api-task-management.md` - Task management CRUD endpoints (task definitions)
- `mobile-api-workbook-task-completion.md` - Daily task completion tracking endpoints

## Brand Style Guide

The app follows `STYLE_GUIDE.md` for consistent UI. Key design tokens:

### Colors (AppColors)
| Purpose | Color | Hex |
|---------|-------|-----|
| Primary (main) | Purple 600 | `#7c3aed` |
| Primary gradient | Indigo → Purple | `#667eea → #764ba2` |
| Secondary | Teal 500 | `#14b8a6` |
| Success | Green 500 | `#22c55e` |
| Warning | Amber 500 | `#f59e0b` |
| Error | Rose 500 | `#f43f5e` |
| Info | Blue 500 | `#3b82f6` |
| Page background | Neutral 50 | `#f8fafc` |
| Text primary | Neutral 900 | `#0f172a` |
| Text secondary | Neutral 600 | `#475569` |
| Borders | Neutral 200 | `#e2e8f0` |

### Typography
- **Font**: Inter (via Google Fonts)
- **Sizes**: XS(12), SM(14), Base(16), LG(18), XL(20), 2XL(24), 3XL(30), 4XL(36), 5XL(48), 6XL(60)
- **Weights**: Regular(400), Medium(500), Semibold(600), Bold(700), ExtraBold(800)

### Border Radius (AppTheme)
| Name | Size | Usage |
|------|------|-------|
| SM | 4px | Small elements |
| MD | 6px | Default |
| LG | 8px | Buttons, inputs |
| XL | 12px | Cards, modals |
| 2XL | 16px | Large cards |
| 3XL | 24px | Hero elements |

### Shadows (AppTheme)
```dart
AppTheme.shadowSm   // Subtle lift
AppTheme.shadowMd   // Cards
AppTheme.shadowLg   // Elevated cards, dropdowns
AppTheme.shadowXl   // Modals
AppTheme.shadow2xl  // Hero elements
AppTheme.primaryButtonShadow  // Gradient buttons
```

### Spacing (AppTheme)
```dart
AppTheme.space1  // 4px
AppTheme.space2  // 8px
AppTheme.space3  // 12px
AppTheme.space4  // 16px
AppTheme.space6  // 24px
AppTheme.space8  // 32px
// ... up to space24 (96px)
```

### Usage Examples
```dart
// Use theme constants for consistency
Container(
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(AppTheme.radiusXl),
    boxShadow: AppTheme.shadowMd,
  ),
  padding: EdgeInsets.all(AppTheme.space4),
)

// Access colors
color: AppColors.primary,
color: AppColors.success,
color: AppColors.neutral500,
```

## Inter-Agent Communication (Backend Coordination)

The backend codebase lives in two locations for parallel development:
- `../buyerkiosk-web` - Primary backend (try.buyerkiosk.com)
- `../buyerkiosk-web2` - Secondary backend (dev3.buyerkiosk.com)

### API Request Protocol

When you need API details, endpoint specifications, or backend changes:

1. **Check existing docs first**: `../buyerkiosk-web/docs/api/` or local `docs/api/`
2. **If not found, write a request** to: `../buyerkiosk-web/docs/api/live-agent-requests.md`

### Request Format

```markdown
### REQ-[YYYYMMDD]-[NN]: [Brief Title]

**Status**: `pending`
**Requested**: [Date]
**Live App Feature**: [Spec ID or feature name]
**Priority**: `high` | `medium` | `low`

#### Context
[What Live app feature needs this API]

#### Questions
1. [Specific question about endpoint, payload, response, etc.]

#### Backend Response
*Awaiting response*
```

### When to Use This

- Need endpoint details not in existing API docs
- Requesting new endpoints for app features
- Clarifying request/response payload formats
- Asking about error codes and edge cases
- Coordinating API changes for new features

### Key Files
- `../buyerkiosk-web/docs/api/live-agent-requests.md` - Send requests to backend
- `docs/backend-api-updates.md` - **CHECK THIS** for updates from backend

### Check for Backend Updates

**At session start**, check `docs/backend-api-updates.md` for:
- New endpoints you should implement
- Breaking changes requiring updates
- New response fields to consume
- Deprecation notices to address

Backend agents write here when they make Live app-affecting changes.
