fill
This commit is contained in:
526
APP_COMPLETE_SETUP_GUIDE.md
Normal file
526
APP_COMPLETE_SETUP_GUIDE.md
Normal file
@@ -0,0 +1,526 @@
|
||||
# Complete App Setup Guide - Warehouse Management App
|
||||
|
||||
## Overview
|
||||
This guide provides a complete overview of the rewritten warehouse management app following clean architecture principles as specified in CLAUDE.md.
|
||||
|
||||
## App Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Presentation Layer │
|
||||
│ (UI, Widgets, State Management with Riverpod) │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Domain Layer │
|
||||
│ (Business Logic, Use Cases, Entities, Interfaces) │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Data Layer │
|
||||
│ (API Client, Models, Data Sources, Repositories) │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Core Layer │
|
||||
│ (Network, Storage, Theme, Constants, Utilities) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
lib/
|
||||
├── core/ # Core infrastructure
|
||||
│ ├── constants/
|
||||
│ │ ├── api_endpoints.dart # API endpoint constants
|
||||
│ │ └── app_constants.dart # App-wide constants
|
||||
│ ├── di/
|
||||
│ │ └── providers.dart # Riverpod dependency injection
|
||||
│ ├── errors/
|
||||
│ │ ├── exceptions.dart # Exception classes
|
||||
│ │ └── failures.dart # Failure classes for Either
|
||||
│ ├── network/
|
||||
│ │ ├── api_client.dart # Dio HTTP client with interceptors
|
||||
│ │ └── api_response.dart # Generic API response wrapper
|
||||
│ ├── router/
|
||||
│ │ └── app_router.dart # GoRouter configuration
|
||||
│ ├── storage/
|
||||
│ │ └── secure_storage.dart # Secure token storage
|
||||
│ ├── theme/
|
||||
│ │ └── app_theme.dart # Material 3 theme
|
||||
│ └── widgets/
|
||||
│ ├── custom_button.dart # Reusable button widgets
|
||||
│ └── loading_indicator.dart # Loading indicators
|
||||
│
|
||||
├── features/ # Feature-first organization
|
||||
│ ├── auth/ # Authentication feature
|
||||
│ │ ├── data/
|
||||
│ │ │ ├── datasources/
|
||||
│ │ │ │ └── auth_remote_datasource.dart
|
||||
│ │ │ ├── models/
|
||||
│ │ │ │ ├── login_request_model.dart
|
||||
│ │ │ │ └── user_model.dart
|
||||
│ │ │ └── repositories/
|
||||
│ │ │ └── auth_repository_impl.dart
|
||||
│ │ ├── domain/
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ │ └── user_entity.dart
|
||||
│ │ │ ├── repositories/
|
||||
│ │ │ │ └── auth_repository.dart
|
||||
│ │ │ └── usecases/
|
||||
│ │ │ └── login_usecase.dart
|
||||
│ │ └── presentation/
|
||||
│ │ ├── pages/
|
||||
│ │ │ └── login_page.dart
|
||||
│ │ ├── providers/
|
||||
│ │ │ └── auth_provider.dart
|
||||
│ │ └── widgets/
|
||||
│ │ └── login_form.dart
|
||||
│ │
|
||||
│ ├── warehouse/ # Warehouse feature
|
||||
│ │ ├── data/
|
||||
│ │ │ ├── datasources/
|
||||
│ │ │ │ └── warehouse_remote_datasource.dart
|
||||
│ │ │ ├── models/
|
||||
│ │ │ │ └── warehouse_model.dart
|
||||
│ │ │ └── repositories/
|
||||
│ │ │ └── warehouse_repository_impl.dart
|
||||
│ │ ├── domain/
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ │ └── warehouse_entity.dart
|
||||
│ │ │ ├── repositories/
|
||||
│ │ │ │ └── warehouse_repository.dart
|
||||
│ │ │ └── usecases/
|
||||
│ │ │ └── get_warehouses_usecase.dart
|
||||
│ │ └── presentation/
|
||||
│ │ ├── pages/
|
||||
│ │ │ └── warehouse_selection_page.dart
|
||||
│ │ ├── providers/
|
||||
│ │ │ └── warehouse_provider.dart
|
||||
│ │ └── widgets/
|
||||
│ │ └── warehouse_card.dart
|
||||
│ │
|
||||
│ ├── operation/ # Operation selection feature
|
||||
│ │ └── presentation/
|
||||
│ │ ├── pages/
|
||||
│ │ │ └── operation_selection_page.dart
|
||||
│ │ └── widgets/
|
||||
│ │ └── operation_card.dart
|
||||
│ │
|
||||
│ └── products/ # Products feature
|
||||
│ ├── data/
|
||||
│ │ ├── datasources/
|
||||
│ │ │ └── products_remote_datasource.dart
|
||||
│ │ ├── models/
|
||||
│ │ │ └── product_model.dart
|
||||
│ │ └── repositories/
|
||||
│ │ └── products_repository_impl.dart
|
||||
│ ├── domain/
|
||||
│ │ ├── entities/
|
||||
│ │ │ └── product_entity.dart
|
||||
│ │ ├── repositories/
|
||||
│ │ │ └── products_repository.dart
|
||||
│ │ └── usecases/
|
||||
│ │ └── get_products_usecase.dart
|
||||
│ └── presentation/
|
||||
│ ├── pages/
|
||||
│ │ └── products_page.dart
|
||||
│ ├── providers/
|
||||
│ │ └── products_provider.dart
|
||||
│ └── widgets/
|
||||
│ └── product_list_item.dart
|
||||
│
|
||||
└── main.dart # App entry point
|
||||
```
|
||||
|
||||
## App Flow
|
||||
|
||||
```
|
||||
1. App Start
|
||||
↓
|
||||
2. Check Authentication (via SecureStorage)
|
||||
↓
|
||||
├── Not Authenticated → Login Screen
|
||||
│ ↓
|
||||
│ Enter credentials
|
||||
│ ↓
|
||||
│ API: POST /auth/login
|
||||
│ ↓
|
||||
│ Store access token
|
||||
│ ↓
|
||||
└── Authenticated → Warehouse Selection Screen
|
||||
↓
|
||||
API: GET /warehouses
|
||||
↓
|
||||
Select warehouse
|
||||
↓
|
||||
Operation Selection Screen
|
||||
↓
|
||||
Choose Import or Export
|
||||
↓
|
||||
Products List Screen
|
||||
↓
|
||||
API: GET /products?warehouseId={id}&type={type}
|
||||
↓
|
||||
Display products
|
||||
```
|
||||
|
||||
## Key Technologies
|
||||
|
||||
- **Flutter SDK**: >=3.0.0 <4.0.0
|
||||
- **State Management**: Riverpod (flutter_riverpod ^2.4.9)
|
||||
- **Navigation**: GoRouter (go_router ^13.2.0)
|
||||
- **HTTP Client**: Dio (dio ^5.3.2)
|
||||
- **Secure Storage**: FlutterSecureStorage (flutter_secure_storage ^9.0.0)
|
||||
- **Functional Programming**: Dartz (dartz ^0.10.1)
|
||||
- **Value Equality**: Equatable (equatable ^2.0.5)
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Install Dependencies
|
||||
|
||||
```bash
|
||||
cd /Users/phuocnguyen/Projects/minhthu
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
### 2. Configure API Base URL
|
||||
|
||||
Edit `/Users/phuocnguyen/Projects/minhthu/lib/core/constants/app_constants.dart`:
|
||||
|
||||
```dart
|
||||
static const String apiBaseUrl = 'https://your-api-domain.com';
|
||||
```
|
||||
|
||||
### 3. Configure API Endpoints (if needed)
|
||||
|
||||
Edit `/Users/phuocnguyen/Projects/minhthu/lib/core/constants/api_endpoints.dart` to match your backend API paths.
|
||||
|
||||
### 4. Run the App
|
||||
|
||||
```bash
|
||||
flutter run
|
||||
```
|
||||
|
||||
## API Integration
|
||||
|
||||
### API Response Format
|
||||
|
||||
All APIs follow this response format:
|
||||
|
||||
```json
|
||||
{
|
||||
"Value": <data>,
|
||||
"IsSuccess": true,
|
||||
"IsFailure": false,
|
||||
"Errors": [],
|
||||
"ErrorCodes": []
|
||||
}
|
||||
```
|
||||
|
||||
### Available APIs
|
||||
|
||||
#### 1. Login
|
||||
```bash
|
||||
POST /auth/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "string",
|
||||
"password": "string"
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"Value": {
|
||||
"userId": "string",
|
||||
"username": "string",
|
||||
"accessToken": "string",
|
||||
"refreshToken": "string"
|
||||
},
|
||||
"IsSuccess": true,
|
||||
"IsFailure": false,
|
||||
"Errors": [],
|
||||
"ErrorCodes": []
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Get Warehouses
|
||||
```bash
|
||||
GET /warehouses
|
||||
Authorization: Bearer {access_token}
|
||||
|
||||
Response:
|
||||
{
|
||||
"Value": [
|
||||
{
|
||||
"Id": 1,
|
||||
"Name": "Kho nguyên vật liệu",
|
||||
"Code": "001",
|
||||
"Description": null,
|
||||
"IsNGWareHouse": false,
|
||||
"TotalCount": 8
|
||||
}
|
||||
],
|
||||
"IsSuccess": true,
|
||||
"IsFailure": false,
|
||||
"Errors": [],
|
||||
"ErrorCodes": []
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Get Products
|
||||
```bash
|
||||
GET /products?warehouseId={id}&type={import/export}
|
||||
Authorization: Bearer {access_token}
|
||||
|
||||
Response:
|
||||
{
|
||||
"Value": [
|
||||
{
|
||||
"Id": 11,
|
||||
"Name": "Thép 435",
|
||||
"Code": "SCM435",
|
||||
"FullName": "SCM435 | Thép 435",
|
||||
"Weight": 120.00,
|
||||
"Pieces": 1320,
|
||||
"ConversionRate": 11.00,
|
||||
... (43 total fields)
|
||||
}
|
||||
],
|
||||
"IsSuccess": true,
|
||||
"IsFailure": false,
|
||||
"Errors": [],
|
||||
"ErrorCodes": []
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Using Auth Provider
|
||||
|
||||
```dart
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:minhthu/core/di/providers.dart';
|
||||
|
||||
class MyWidget extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Watch auth state
|
||||
final isAuthenticated = ref.watch(isAuthenticatedProvider);
|
||||
final currentUser = ref.watch(currentUserProvider);
|
||||
final isLoading = ref.watch(authProvider.select((s) => s.isLoading));
|
||||
|
||||
// Login
|
||||
onLoginPressed() async {
|
||||
await ref.read(authProvider.notifier).login(username, password);
|
||||
}
|
||||
|
||||
// Logout
|
||||
onLogoutPressed() async {
|
||||
await ref.read(authProvider.notifier).logout();
|
||||
}
|
||||
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using Warehouse Provider
|
||||
|
||||
```dart
|
||||
class WarehousePage extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Watch warehouses
|
||||
final warehouses = ref.watch(warehousesListProvider);
|
||||
final selectedWarehouse = ref.watch(selectedWarehouseProvider);
|
||||
final isLoading = ref.watch(warehouseProvider.select((s) => s.isLoading));
|
||||
|
||||
// Load warehouses
|
||||
useEffect(() {
|
||||
Future.microtask(() =>
|
||||
ref.read(warehouseProvider.notifier).loadWarehouses()
|
||||
);
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
// Select warehouse
|
||||
onWarehouseTap(warehouse) {
|
||||
ref.read(warehouseProvider.notifier).selectWarehouse(warehouse);
|
||||
context.go('/operations', extra: warehouse);
|
||||
}
|
||||
|
||||
return ListView.builder(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using Products Provider
|
||||
|
||||
```dart
|
||||
class ProductsPage extends ConsumerWidget {
|
||||
final int warehouseId;
|
||||
final String warehouseName;
|
||||
final String operationType;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Watch products
|
||||
final products = ref.watch(productsListProvider);
|
||||
final isLoading = ref.watch(productsProvider.select((s) => s.isLoading));
|
||||
|
||||
// Load products
|
||||
useEffect(() {
|
||||
Future.microtask(() =>
|
||||
ref.read(productsProvider.notifier)
|
||||
.loadProducts(warehouseId, warehouseName, operationType)
|
||||
);
|
||||
return null;
|
||||
}, [warehouseId, operationType]);
|
||||
|
||||
return ListView.builder(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Navigation
|
||||
|
||||
```dart
|
||||
// Navigate to login
|
||||
context.go('/login');
|
||||
|
||||
// Navigate to warehouses
|
||||
context.go('/warehouses');
|
||||
|
||||
// Navigate to operations
|
||||
context.go('/operations', extra: warehouseEntity);
|
||||
|
||||
// Navigate to products
|
||||
context.go('/products', extra: {
|
||||
'warehouse': warehouseEntity,
|
||||
'warehouseName': 'Kho nguyên vật liệu',
|
||||
'operationType': 'import', // or 'export'
|
||||
});
|
||||
|
||||
// Or use extension methods
|
||||
context.goToOperations(warehouse);
|
||||
context.goToProducts(warehouse: warehouse, operationType: 'import');
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Using Either for Error Handling
|
||||
|
||||
```dart
|
||||
final result = await ref.read(authProvider.notifier).login(username, password);
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
// Handle error
|
||||
print('Error: ${failure.message}');
|
||||
},
|
||||
(user) {
|
||||
// Handle success
|
||||
print('Logged in as: ${user.username}');
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
### Error Types
|
||||
|
||||
- **ServerFailure**: API returned an error
|
||||
- **NetworkFailure**: Network connectivity issues
|
||||
- **AuthenticationFailure**: Authentication/authorization errors
|
||||
|
||||
## Testing
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
flutter test
|
||||
```
|
||||
|
||||
### Run Analysis
|
||||
|
||||
```bash
|
||||
flutter analyze
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
|
||||
```bash
|
||||
flutter test --coverage
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Token Storage**: Access tokens are stored securely using `flutter_secure_storage` with platform-specific encryption:
|
||||
- Android: EncryptedSharedPreferences
|
||||
- iOS: Keychain
|
||||
|
||||
2. **API Security**:
|
||||
- All authenticated requests include Bearer token
|
||||
- 401 errors automatically clear tokens and redirect to login
|
||||
- Sensitive data redacted in logs
|
||||
|
||||
3. **HTTPS**: All API calls should use HTTPS in production
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
1. **Riverpod**: Minimal rebuilds with fine-grained reactivity
|
||||
2. **Lazy Loading**: Providers are created only when needed
|
||||
3. **Caching**: SecureStorage caches auth tokens
|
||||
4. **Error Boundaries**: Proper error handling prevents crashes
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Login fails with 401
|
||||
- Check API base URL in `app_constants.dart`
|
||||
- Verify credentials
|
||||
- Check network connectivity
|
||||
|
||||
### Issue: White screen after login
|
||||
- Check if router redirect logic is working
|
||||
- Verify `SecureStorage.isAuthenticated()` returns true
|
||||
- Check console for errors
|
||||
|
||||
### Issue: Products not loading
|
||||
- Verify warehouse is selected
|
||||
- Check API endpoint configuration
|
||||
- Verify access token is valid
|
||||
|
||||
### Issue: Build errors
|
||||
```bash
|
||||
flutter clean
|
||||
flutter pub get
|
||||
flutter run
|
||||
```
|
||||
|
||||
## Documentation References
|
||||
|
||||
- **Core Architecture**: `/lib/core/di/README.md`
|
||||
- **Auth Feature**: `/lib/features/auth/README.md`
|
||||
- **Warehouse Feature**: `/lib/features/warehouse/README.md`
|
||||
- **Products Feature**: Inline documentation in code
|
||||
- **API Client**: `/lib/core/network/README.md`
|
||||
- **Router**: `/lib/core/router/README.md`
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ Core architecture set up
|
||||
2. ✅ Auth feature implemented
|
||||
3. ✅ Warehouse feature implemented
|
||||
4. ✅ Operation selection implemented
|
||||
5. ✅ Products feature implemented
|
||||
6. ✅ Routing configured
|
||||
7. ✅ Dependency injection set up
|
||||
8. ⏳ Configure production API URL
|
||||
9. ⏳ Test with real API
|
||||
10. ⏳ Add additional features as needed
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check inline documentation in code files
|
||||
2. Review README files in each module
|
||||
3. Check CLAUDE.md for specifications
|
||||
|
||||
## License
|
||||
|
||||
This project follows the specifications in CLAUDE.md and is built with clean architecture principles.
|
||||
Reference in New Issue
Block a user