# 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 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, 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): #C44D58 - Secondary (Slate Blue): #475C6C - Error: #BA1A1A **Typography:** - Primary Font: Montserrat (all weights 300-700) - Monospace Font: IBMPlexMono - Material Design 3 text styles **Theme Files:** - `lib/theme.dart` - Complete Material 3 theme configuration - Light and dark themes with high-contrast variants - All colors defined in ColorScheme ## 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 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 json) { return Delivery( id: json['id'] as int, name: json['name'] as String, ); } @override Map toJson() => { 'id': id, 'name': name, }; } ``` ### 3. Error Handling with Result **NEVER use try-catch for API calls. Use Result pattern:** ```dart final result = await apiClient.executeQuery( 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( 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((ref) { return AuthService(); }); final userProfileProvider = FutureProvider((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) { ... } ``` ## 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 - **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 ``` ## 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 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 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 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