ionic-planb-logistic-app-fl.../CLAUDE.md
Mathias Beaulieu-Duncan edb106a7fd Refactor theme system and remove unused platforms
- Overhaul theme system with Svrnty design and WCAG AAA compliance
- Remove android, macos, and web platform files (iOS-only focus)
- Update components with improved dark mode map and UI refinements
- Enhance settings page with additional configuration options
- Add theme system documentation in lib/theme/README.md
- Update CLAUDE.md with comprehensive theme guidelines

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 14:47:51 -05:00

488 lines
13 KiB
Markdown

# CLAUDE.md - Plan B Logistics Flutter App
This file provides guidance to Claude Code when working with this Flutter/Dart project.
## Project Overview
Plan B Logistics Flutter is a complete refactor of the Ionic Angular delivery management app into Flutter/Dart, maintaining all functionality while applying Svrnty design system (colors, typography, and Material Design 3).
**Key Features:**
- OAuth2/OIDC authentication with Keycloak
- CQRS pattern for API integration with Result<T> error handling
- Delivery route and delivery management
- Photo upload for delivery proof
- i18n support (French/English)
- Native features: Camera, Phone calls, Maps
## Essential Commands
### Setup & Dependencies
```bash
flutter pub get
flutter pub upgrade
flutter pub run build_runner build --delete-conflicting-outputs
```
### Development
```bash
flutter run -d chrome # Web
flutter run -d macos # macOS
flutter run -d ios # iOS simulator
flutter run -d android # Android emulator
```
### Testing & Analysis
```bash
flutter test
flutter analyze
flutter test --coverage
```
### Build
```bash
flutter build web
flutter build ios
flutter build android
```
## Code Architecture
### Core Structure
```
lib/
├── api/ # CQRS API client and types
│ ├── types.dart # Result<T>, Serializable, ApiError
│ ├── client.dart # CqrsApiClient implementation
│ └── openapi_config.dart
├── models/ # Data models (strict typing)
│ ├── delivery.dart
│ ├── delivery_route.dart
│ ├── user_profile.dart
│ └── ...
├── services/ # Business logic
│ └── auth_service.dart
├── providers/ # Riverpod state management
│ └── providers.dart
├── pages/ # Screen widgets
│ ├── login_page.dart
│ ├── routes_page.dart
│ ├── deliveries_page.dart
│ └── settings_page.dart
├── components/ # Reusable UI components
├── l10n/ # i18n translations (*.arb files)
├── utils/ # Utility functions
├── theme.dart # Svrnty theme configuration
└── main.dart # App entry point
```
### Design System (Svrnty)
**Primary Colors:**
- Primary (Crimson): #C91F37 (light) / #FF5A6D (dark)
- Secondary (Slate Blue): #2D3843 (light) / #A5B6C8 (dark)
- Tertiary (Green): #16803D (light) / #5EE890 (dark)
- Error: #D32F2F (light) / #FF8A80 (dark)
**Typography:**
- Primary Font: Montserrat (all weights 300-700)
- Monospace Font: IBMPlexMono
- Material Design 3 text styles with explicit color assignments
**Theme System:**
- **2 Theme Variants:** Light and Dark (forest green background)
- **WCAG AAA Compliant:** All text meets 7:1 contrast minimum
- **Dark Theme Background:** Forest Green (#0C1410) - unique branding
- **No Hardcoded Colors:** All components use ColorScheme properties
- **Theme Files:**
- `lib/theme.dart` - Material 3 theme with 2 ColorScheme variants
- `lib/theme/color_system.dart` - Svrnty color constants
- `lib/theme/status_colors.dart` - Status color utilities
- `lib/theme/component_themes.dart` - Component-specific themes
- `lib/theme/README.md` - Complete theme documentation
## Core Patterns & Standards
### 1. Strict Typing (MANDATORY)
**NO `dynamic`, NO untyped `var`**
```dart
// FORBIDDEN:
var data = fetchData();
dynamic result = api.call();
// REQUIRED:
DeliveryRoute data = fetchData();
Result<DeliveryRoute> result = api.call();
```
### 2. Serializable Interface
All models must implement `Serializable`:
```dart
class Delivery implements Serializable {
final int id;
final String name;
const Delivery({required this.id, required this.name});
factory Delivery.fromJson(Map<String, dynamic> json) {
return Delivery(
id: json['id'] as int,
name: json['name'] as String,
);
}
@override
Map<String, Object?> toJson() => {
'id': id,
'name': name,
};
}
```
### 3. Error Handling with Result<T>
**NEVER use try-catch for API calls. Use Result<T> pattern:**
```dart
final result = await apiClient.executeQuery<DeliveryRoute>(
endpoint: 'deliveryRoutes',
query: GetRoutesQuery(),
fromJson: DeliveryRoute.fromJson,
);
result.when(
success: (route) => showRoute(route),
error: (error) {
switch (error.type) {
case ApiErrorType.network:
showSnackbar('No connection');
case ApiErrorType.timeout:
showSnackbar('Request timeout');
case ApiErrorType.validation:
showValidationErrors(error.details);
case ApiErrorType.http when error.statusCode == 401:
navigateToLogin();
default:
showSnackbar('Error: ${error.message}');
}
},
);
```
### 4. CQRS API Integration
**Query (Read Operations):**
```dart
final result = await client.executeQuery<Delivery>(
endpoint: 'simpleDeliveriesQueryItems',
query: GetDeliveriesQuery(routeFragmentId: 123),
fromJson: Delivery.fromJson,
);
```
**Command (Write Operations):**
```dart
final result = await client.executeCommand(
endpoint: 'completeDelivery',
command: CompleteDeliveryCommand(deliveryId: 123),
);
```
### 5. Riverpod State Management
**Providers Pattern:**
```dart
final authServiceProvider = Provider<AuthService>((ref) {
return AuthService();
});
final userProfileProvider = FutureProvider<UserProfile?>((ref) async {
final authService = ref.watch(authServiceProvider);
final token = await authService.getToken();
return token != null ? authService.decodeToken(token) : null;
});
// Usage in widget:
final userProfile = ref.watch(userProfileProvider);
userProfile.when(
data: (profile) => Text(profile?.fullName ?? ''),
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error: $error'),
);
```
### 6. Authentication Flow
1. **Login**: `AuthService.login()` triggers OAuth2/OIDC flow with Keycloak
2. **Token Storage**: Secure storage with `flutter_secure_storage`
3. **Token Validation**: Check expiration with `JwtDecoder.isExpired()`
4. **Auto Refresh**: Implement token refresh on 401 responses
5. **Logout**: Clear tokens from secure storage
**Keycloak Configuration:**
- Realm: planb-internal
- Client ID: delivery-mobile-app
- Discovery URL: https://auth.goutezplanb.com/realms/planb-internal/.well-known/openid-configuration
- Scopes: openid, profile, offline_access
### 7. No Emojis Rule
**MANDATORY: NO emojis in code, comments, or commit messages**
```dart
// FORBIDDEN:
// Bug fix for delivery issues
void completeDelivery(int id) { ... } // Done
// REQUIRED:
// Bug fix for delivery completion logic
void completeDelivery(int id) { ... }
```
### 8. Theme System (MANDATORY)
**Standard Color Access Pattern (use everywhere):**
```dart
final colorScheme = Theme.of(context).colorScheme;
// Primary UI
color: colorScheme.primary // Primary brand color
color: colorScheme.onPrimary // Text on primary
// Text colors
color: colorScheme.onSurface // Primary text
color: colorScheme.onSurfaceVariant // Secondary text
// Backgrounds
color: colorScheme.surface // Page background
color: colorScheme.surfaceContainer // Card background
// Shadows
color: colorScheme.scrim.withValues(alpha: 0.2)
```
**FORBIDDEN Patterns:**
```dart
// NEVER use these in component files:
color: Colors.white // FORBIDDEN
color: Colors.black // FORBIDDEN
color: Color(0xFFXXXXXX) // FORBIDDEN (except in theme files)
color: SvrntyColors.crimsonRed // FORBIDDEN - use colorScheme.primary
```
**Dark Theme:**
The app uses a **forest green dark theme** (#0C1410) for unique branding.
All text colors automatically adapt with WCAG AAA compliance (7:1 minimum contrast).
**Status Colors:**
```dart
import '../theme/status_colors.dart';
// Theme-aware status colors (preferred)
StatusColorScheme.getStatusColorFromTheme('completed', colorScheme)
// Hardcoded status colors (fallback)
StatusColorScheme.getStatusColor('completed')
```
**Modifying Theme:**
To change brand colors, edit `/lib/theme.dart`:
- `lightScheme()` - Light theme ColorScheme
- `darkScheme()` - Dark theme ColorScheme
**Documentation:**
See `/lib/theme/README.md` for complete theme system documentation.
## API Integration
### Base URLs
```dart
const String queryBaseUrl = 'https://api-route.goutezplanb.com/api/query';
const String commandBaseUrl = 'https://api-route.goutezplanb.com/api/command';
```
### Key Endpoints
- Query: `/api/query/simpleDeliveriesQueryItems`
- Query: `/api/query/simpleDeliveryRouteQueryItems`
- Command: `/api/command/completeDelivery`
- Command: `/api/command/markDeliveryAsUncompleted`
- Upload: `/api/delivery/uploadDeliveryPicture`
### Authorization
All requests to API base URL must include Bearer token:
```dart
final authClient = CqrsApiClient(
config: ApiClientConfig(
baseUrl: 'https://api-route.goutezplanb.com',
defaultHeaders: {'Authorization': 'Bearer $token'},
),
);
```
## Internationalization (i18n)
### File Structure
```
lib/l10n/
├── app_en.arb # English translations
└── app_fr.arb # French translations
```
### ARB Format
```json
{
"appTitle": "Plan B Logistics",
"loginButton": "Login with Keycloak",
"deliveryStatus": "Delivery #{id} is {status}",
"@deliveryStatus": {
"placeholders": {
"id": {"type": "int"},
"status": {"type": "String"}
}
}
}
```
### Usage in Code
```dart
AppLocalizations.of(context)!.appTitle
AppLocalizations.of(context)!.deliveryStatus(id: 123, status: 'completed')
```
## Native Features
### Camera Integration
- Package: `image_picker`
- Use: Photo capture for delivery proof
- Platforms: iOS, Android, Web
```dart
final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.camera);
if (pickedFile != null) {
// Upload to server
}
```
### Phone Calls
- Package: `url_launcher`
- Use: Call customer from delivery details
```dart
final Uri phoneUri = Uri(scheme: 'tel', path: phoneNumber);
if (await canLaunchUrl(phoneUri)) {
await launchUrl(phoneUri);
}
```
### Maps Integration
- Package: `url_launcher`
- Use: Open maps app to show delivery address
```dart
final Uri mapUri = Uri(
scheme: 'https',
host: 'maps.google.com',
queryParameters: {'q': '${address.latitude},${address.longitude}'},
);
if (await canLaunchUrl(mapUri)) {
await launchUrl(mapUri);
}
```
## File Naming Conventions
- **Files**: snake_case (e.g., `delivery_route.dart`)
- **Classes**: PascalCase (e.g., `DeliveryRoute`)
- **Variables/Functions**: camelCase (e.g., `deliveryId`, `completeDelivery()`)
- **Constants**: camelCase or UPPER_SNAKE_CASE (e.g., `kPrimaryColor` or `MAX_RETRIES`)
- **Private members**: Prefix with underscore (e.g., `_secureStorage`)
## Git Conventions
- **Author**: Svrnty
- **Co-Author**: Jean-Philippe Brule <jp@svrnty.io>
- **Commits**: Clear, concise messages describing the "why"
- **NO emojis in commits**
Example:
```
Implement OAuth2/OIDC authentication with Keycloak
Adds AuthService with flutter_appauth integration, JWT token
management with secure storage, and automatic token refresh.
Co-Authored-By: Claude <noreply@anthropic.com>
```
## Common Development Tasks
### Adding a New Page
1. Create widget file in `lib/pages/[name]_page.dart`
2. Extend `ConsumerWidget` for Riverpod access
3. Use strict typing for all parameters and variables
4. Apply Svrnty colors from theme
5. Handle loading/error states with `.when()`
### Adding a New Data Model
1. Create in `lib/models/[name].dart`
2. Implement `Serializable` interface
3. Add `fromJson` factory constructor
4. Implement `toJson()` method
5. Use explicit types (no `dynamic`)
### Implementing API Call
1. Create Query/Command class implementing `Serializable`
2. Use `CqrsApiClient.executeQuery()` or `.executeCommand()`
3. Handle Result<T> with `.when()` pattern
4. Never use try-catch for API calls
5. Provide proper error messages to user
### Adding i18n Support
1. Add key to `app_en.arb` and `app_fr.arb`
2. Use `AppLocalizations.of(context)!.keyName` in widgets
3. For parameterized strings, define placeholders in ARB
4. Test both English and French text
## Testing
- Unit tests: `test/` directory
- Widget tests: `test/` directory with widget_test suffix
- Use Riverpod's testing utilities for provider testing
- Mock API client for service tests
- Maintain >80% code coverage for business logic
## Deployment Checklist
- [ ] All strict typing rules followed
- [ ] No `dynamic` or untyped `var`
- [ ] All API calls use Result<T> pattern
- [ ] i18n translations complete for both languages
- [ ] Theme colors correctly applied
- [ ] No emojis in code or commits
- [ ] Tests passing (flutter test)
- [ ] Static analysis clean (flutter analyze)
- [ ] No secrets in code (tokens, keys, credentials)
- [ ] APK/IPA builds successful
## Key Dependencies
- `flutter_riverpod`: State management
- `flutter_appauth`: OAuth2/OIDC
- `flutter_secure_storage`: Token storage
- `jwt_decoder`: JWT token parsing
- `http`: HTTP client
- `image_picker`: Camera/photo access
- `url_launcher`: Phone calls and maps
- `animate_do`: Animations (from Svrnty)
- `lottie`: Loading animations
- `iconsax`: Icon set
- `intl`: Internationalization
## Support & Documentation
- **Theme**: See `lib/theme.dart` for complete Svrnty design system
- **API Types**: See `lib/api/types.dart` for Result<T> and error handling
- **Models**: See `lib/models/` for data structure examples
- **Providers**: See `lib/providers/providers.dart` for state management setup
- **Auth**: See `lib/services/auth_service.dart` for OAuth2/OIDC flow