# 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): #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<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) { ... }
```

## 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
