refactor: Projektstruktur in app/ und backend/ aufgeteilt
This commit is contained in:
429
app/AGENTS.md
Normal file
429
app/AGENTS.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# Votian LT App - Agent Guidelines
|
||||
|
||||
## Project Overview
|
||||
|
||||
**Votian LT** is a Flutter-based mobile application for logistics and transport management. The app enables drivers to manage transport jobs, complete tasks (photos, signatures, barcodes), communicate via real-time chat, and navigate to pickup/delivery locations.
|
||||
|
||||
### Key Features
|
||||
- **Job Management**: View and manage assigned transport jobs with cargo items
|
||||
- **Task System**: Complete various task types (confirmation, photo capture, signature, barcode scanning, todo lists, comments)
|
||||
- **Real-time Chat**: Job-specific and general chat via WebSocket
|
||||
- **Offline Support**: Full offline functionality with local ObjectBox database
|
||||
- **Navigation**: Integration with external maps and embedded WebView routing
|
||||
- **Push Notifications**: Local notifications for new jobs and messages
|
||||
- **Implementation**: When making changes, be careful not to damage existing functionalities.
|
||||
|
||||
---
|
||||
|
||||
## Technology Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-------|------------|
|
||||
| Framework | Flutter 3.7+ with Dart |
|
||||
| State Management | Singleton pattern + Custom DartMQ pub/sub |
|
||||
| Local Database | ObjectBox (NoSQL) |
|
||||
| Real-time Communication | WebSocket (STOMP-style protocol) |
|
||||
| Backend Integration | WebSocket to `ws://localhost:8082/ws/messaging` |
|
||||
| UI Design | Material Design 3 |
|
||||
|
||||
### Key Dependencies (pubspec.yaml)
|
||||
- `web_socket_channel: ^3.0.0` - WebSocket client
|
||||
- `objectbox: ^4.0.3` + `objectbox_flutter_libs` - Local database
|
||||
- `camera: ^0.10.5+9` - Photo capture
|
||||
- `mobile_scanner: ^5.0.0` - Barcode scanning
|
||||
- `signature: ^5.5.0` - Signature capture canvas
|
||||
- `flutter_local_notifications: ^18.0.0` - Local notifications
|
||||
- `url_launcher: ^6.3.0` - External navigation
|
||||
- `webview_flutter: ^4.8.0` - Embedded maps
|
||||
- `image: ^4.2.0` - Image processing
|
||||
- `package_info_plus: ^8.0.0` - App version info
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
lib/
|
||||
├── main.dart # App entry point, MaterialApp setup
|
||||
├── app_state.dart # Global app state singleton (jobs, login)
|
||||
├── navigation_observer.dart # Route observer for analytics
|
||||
├── routing_view.dart # Embedded navigation WebView
|
||||
│
|
||||
├── models/ # Domain models (JSON serializable)
|
||||
│ ├── job.dart # Transport job with cargo items and tasks
|
||||
│ ├── cargo_item.dart # Cargo/load items
|
||||
│ ├── task.dart # Abstract task base class
|
||||
│ ├── tasks/ # Concrete task implementations
|
||||
│ │ ├── confirmation_task.dart
|
||||
│ │ ├── photo_task.dart
|
||||
│ │ ├── signature_task.dart
|
||||
│ │ ├── barcode_task.dart
|
||||
│ │ ├── todolist_task.dart
|
||||
│ │ ├── comment_task.dart
|
||||
│ │ └── generic_task.dart
|
||||
│ ├── chat.dart # Chat conversation
|
||||
│ ├── chat_message.dart # Individual message
|
||||
│ ├── message_envelope.dart # WebSocket message wrapper
|
||||
│ ├── acknowledgment_message.dart
|
||||
│ └── queued_message.dart # Offline message queue
|
||||
│
|
||||
├── entities/ # ObjectBox database entities
|
||||
│ ├── job_entity.dart
|
||||
│ ├── task_status_entity.dart
|
||||
│ ├── user_data_entity.dart
|
||||
│ ├── photo_entity.dart
|
||||
│ ├── queued_message_entity.dart
|
||||
│ └── chat_message_entity.dart
|
||||
│
|
||||
├── services/ # Business logic services (singletons)
|
||||
│ ├── websocket_service.dart # WebSocket connection & messaging
|
||||
│ ├── database_service.dart # ObjectBox database operations
|
||||
│ ├── chat_service.dart # Chat management
|
||||
│ ├── dart_mq.dart # In-app pub/sub message bus
|
||||
│ ├── notification_service.dart # Local notifications
|
||||
│ ├── message_handler.dart # Incoming message processing
|
||||
│ ├── ack_tracker.dart # Message acknowledgment tracking
|
||||
│ └── developer.dart # Developer utilities/logging
|
||||
│
|
||||
├── views/ # Main UI screens
|
||||
│ ├── login_view.dart # Authentication screen
|
||||
│ ├── jobs_view.dart # Job list screen
|
||||
│ ├── cargo_items_view.dart # Cargo details for a job
|
||||
│ ├── chats_view.dart # Chat list screen
|
||||
│ ├── chat_details_view.dart # Individual chat conversation
|
||||
│ └── task_view.dart # Task completion screen
|
||||
│
|
||||
├── tasks/ # Task-specific UI screens
|
||||
│ ├── photo_capture_screen.dart
|
||||
│ ├── signature_capture_screen.dart
|
||||
│ └── barcode_capture_screen.dart
|
||||
│
|
||||
└── widgets/ # Reusable UI components
|
||||
├── chat_photo_dialog.dart
|
||||
└── offline_banner.dart
|
||||
|
||||
test/ # Unit and widget tests
|
||||
├── models/
|
||||
│ ├── job_parsing_test.dart
|
||||
│ ├── message_envelope_test.dart
|
||||
│ └── acknowledgment_message_test.dart
|
||||
└── services/
|
||||
├── ack_tracker_test.dart
|
||||
├── message_handler_test.dart
|
||||
└── mqtt_integration_test.dart
|
||||
|
||||
android/, ios/, macos/, linux/, windows/ # Platform-specific code
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
### Setup
|
||||
```bash
|
||||
# Install dependencies
|
||||
flutter pub get
|
||||
|
||||
# Generate ObjectBox code (after entity changes)
|
||||
flutter pub run build_runner build
|
||||
```
|
||||
|
||||
### Development
|
||||
```bash
|
||||
# Run static analysis
|
||||
flutter analyze
|
||||
|
||||
# Format code (CI expects formatted code)
|
||||
dart format lib/ test/
|
||||
|
||||
# Run the app
|
||||
flutter run
|
||||
|
||||
# Run on specific device
|
||||
flutter run -d <device_id>
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Run all tests
|
||||
flutter test
|
||||
|
||||
# Run with coverage
|
||||
flutter test --coverage
|
||||
|
||||
# Run specific test file
|
||||
flutter test test/models/job_parsing_test.dart
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Singleton Services
|
||||
All major services use the singleton pattern for app-wide state:
|
||||
|
||||
```dart
|
||||
// Accessing services
|
||||
final appState = AppState();
|
||||
final chatService = ChatService();
|
||||
final wsService = WebSocketService();
|
||||
final dbService = DatabaseService();
|
||||
```
|
||||
|
||||
### DartMQ Pub/Sub
|
||||
Custom lightweight message bus for decoupled communication:
|
||||
|
||||
```dart
|
||||
// Subscribe to topics
|
||||
final sub = DartMQ().subscribe<Map<String, dynamic>>(
|
||||
MQTopics.authResponse,
|
||||
(data) => handleAuth(data),
|
||||
);
|
||||
|
||||
// Publish messages
|
||||
DartMQ().publish<bool>(MQTopics.connectionStatus, true);
|
||||
|
||||
// Cleanup
|
||||
sub.cancel();
|
||||
```
|
||||
|
||||
**Common Topics** (`lib/services/dart_mq.dart`):
|
||||
- `connection/status` - WebSocket connection state (bool)
|
||||
- `auth/response` - Authentication responses (Map)
|
||||
- `jobs/response` - Job list updates (List)
|
||||
- `jobsUpdated` - Job data changed notification (void)
|
||||
- `job/deleted` - Job deletion event (Map)
|
||||
- `job/created` - New job created (Map)
|
||||
- `chat/incoming` - New chat message (ChatMessage)
|
||||
|
||||
### Task System
|
||||
Tasks are polymorphic based on `taskType` field:
|
||||
|
||||
| Task Type | Description |
|
||||
|-----------|-------------|
|
||||
| `CONFIRMATION` | Button tap confirmation |
|
||||
| `PHOTO` | Capture photos (min/max count) |
|
||||
| `SIGNATURE` | Capture signature as SVG |
|
||||
| `BARCODE` | Scan barcodes/QR codes |
|
||||
| `TODOLIST` | Checklist of items |
|
||||
| `COMMENT` | Text input field |
|
||||
| `GENERIC` | Fallback type |
|
||||
|
||||
---
|
||||
|
||||
## Coding Style Guidelines
|
||||
|
||||
### Dart/Flutter Conventions
|
||||
- **Indentation**: 2 spaces
|
||||
- **Trailing Commas**: Use trailing commas to encourage proper auto-formatting
|
||||
- **Naming**:
|
||||
- Classes: `UpperCamelCase`
|
||||
- Methods/variables: `lowerCamelCase`
|
||||
- Private members: `_leadingUnderscore`
|
||||
- Constants: `camelCase` or `UPPER_SNAKE_CASE` for static const
|
||||
|
||||
### Code Style Examples
|
||||
```dart
|
||||
// Good: trailing commas for multi-line
|
||||
final job = Job(
|
||||
id: '123',
|
||||
jobNumber: 'JOB-001',
|
||||
status: 'ASSIGNED',
|
||||
// ...
|
||||
);
|
||||
|
||||
// Good: private helper methods
|
||||
String _formatAddress(String street, String city) {
|
||||
return '$street, $city';
|
||||
}
|
||||
|
||||
// Good: type annotations for public APIs
|
||||
Future<List<Job>> loadJobs() async {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Imports
|
||||
- Order: Dart SDK → Flutter → Third-party → Project (alphabetical within groups)
|
||||
- Use `package:votianlt_app/` prefix for project imports
|
||||
|
||||
---
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
### Test Structure
|
||||
```dart
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:votianlt_app/models/job.dart';
|
||||
|
||||
void main() {
|
||||
group('Job Parsing', () {
|
||||
test('parses basic fields correctly', () {
|
||||
// Arrange
|
||||
final json = {'job': {'id': '123', ...}};
|
||||
|
||||
// Act
|
||||
final job = Job.fromJson(json);
|
||||
|
||||
// Assert
|
||||
expect(job.id, '123');
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Testing Patterns
|
||||
- Mirror the `lib/` directory structure in `test/`
|
||||
- Name tests after the unit: `job_parsing_test.dart`
|
||||
- Use `group()` for related assertions
|
||||
- Test both success and edge cases
|
||||
- Test round-trip serialization (`fromJson` → `toJson` → `fromJson`)
|
||||
|
||||
### Mocking
|
||||
Use `mocktail` for mocking dependencies in service tests.
|
||||
|
||||
---
|
||||
|
||||
## Database (ObjectBox)
|
||||
|
||||
### Entity Definition Example
|
||||
```dart
|
||||
@Entity()
|
||||
class JobEntity {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
@Unique()
|
||||
String jobId;
|
||||
|
||||
String jobData; // JSON-encoded
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime updatedAt;
|
||||
|
||||
JobEntity({...});
|
||||
}
|
||||
```
|
||||
|
||||
### Regenerating Code
|
||||
After modifying entities in `lib/entities/`:
|
||||
```bash
|
||||
flutter pub run build_runner build
|
||||
```
|
||||
|
||||
This generates `lib/objectbox.g.dart`.
|
||||
|
||||
---
|
||||
|
||||
## WebSocket Protocol
|
||||
|
||||
### Connection
|
||||
- URL: `ws://localhost:8082/ws/messaging` (desktop)
|
||||
- Android Emulator: `ws://10.0.2.2:8082/ws/messaging`
|
||||
|
||||
### Message Format
|
||||
```json
|
||||
{
|
||||
"topic": "/client/auth",
|
||||
"payload": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### Client → Server Topics
|
||||
- `/server/login` - Authentication
|
||||
- `/server/jobs/assigned` - Request job list
|
||||
- `/server/message` - Send chat message
|
||||
- `/server/task_completed` - Mark task complete
|
||||
|
||||
### Server → Client Topics
|
||||
- `/client/{userId}/auth` - Auth response
|
||||
- `/client/{userId}/jobs` - Job list
|
||||
- `/client/{userId}/message` - Incoming chat
|
||||
- `/client/{userId}/job_deleted` - Job deleted
|
||||
- `/client/{userId}/job_created` - New job
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Credentials
|
||||
- Email/password stored in ObjectBox (encrypted at rest by OS)
|
||||
- Credentials cleared on logout
|
||||
- Auto-login with saved credentials
|
||||
|
||||
### WebSocket
|
||||
- Reconnection with 15-second interval
|
||||
- Message buffering when offline
|
||||
- Unique App ID per installation for client identification
|
||||
|
||||
### Secrets
|
||||
- Do not commit API keys or credentials
|
||||
- Server endpoint configurable in `WebSocketService._buildWebSocketUrl()`
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific Notes
|
||||
|
||||
### Android
|
||||
- Minimum SDK: Defined in `android/app/build.gradle`
|
||||
- Permissions in `AndroidManifest.xml`:
|
||||
- `INTERNET` - WebSocket communication
|
||||
- `CAMERA` - Photo/barcode capture
|
||||
- `WRITE_EXTERNAL_STORAGE` - Photo storage
|
||||
- `POST_NOTIFICATIONS` - Push notifications
|
||||
- `VIBRATE` - Notification vibration
|
||||
|
||||
### iOS
|
||||
- Camera and photo permissions in `ios/Runner/Info.plist`
|
||||
- Notification permissions configured
|
||||
|
||||
---
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding a New Task Type
|
||||
1. Create model in `lib/models/tasks/new_task_type.dart`
|
||||
2. Add to `Task.fromJson()` factory in `lib/models/task.dart`
|
||||
3. Add UI screen in `lib/tasks/` if needed
|
||||
4. Update `task_view.dart` to handle the new type
|
||||
5. Add tests in `test/models/`
|
||||
|
||||
### Adding a New Database Entity
|
||||
1. Create entity class in `lib/entities/`
|
||||
2. Run `flutter pub run build_runner build`
|
||||
3. Add CRUD operations in `DatabaseService`
|
||||
|
||||
### Modifying WebSocket Messages
|
||||
1. Update message handler in `WebSocketService._handleMessage()`
|
||||
2. Add topic constant to `MQTopics` if needed
|
||||
3. Update `MessageHandler` for processing logic
|
||||
|
||||
---
|
||||
|
||||
## Localization
|
||||
|
||||
The app uses German for UI text:
|
||||
- Job status: "Erstellt", "Zugewiesen", "In Bearbeitung", "Abgeschlossen"
|
||||
- Notifications: "Neue Jobs", "Neue Nachricht"
|
||||
- Chat: "Allgemeine Nachrichten"
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Logging
|
||||
Use the developer log utility:
|
||||
```dart
|
||||
import 'package:votianlt_app/services/developer.dart' as developer;
|
||||
|
||||
developer.log('Debug message', name: 'ComponentName');
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
1. **WebSocket not connecting**: Check server is running on port 8082
|
||||
2. **Database errors**: Run `flutter clean` and `flutter pub get`
|
||||
3. **ObjectBox issues**: Regenerate with `build_runner`
|
||||
4. **Camera not working**: Check platform permissions
|
||||
Reference in New Issue
Block a user