315 lines
13 KiB
Markdown
315 lines
13 KiB
Markdown
# Superport AGENTS.md — Working Guide and Current State
|
|
|
|
This document is the project-wide guide for agents/developers. It describes the Flutter frontend structure, rules, run/test workflows, backend integration principles, technical debt, and improvement items.
|
|
|
|
- Scope: applies to the entire repo (lib, test, docs, etc.).
|
|
- Backend repo: `/Users/maximilian.j.sul/Documents/flutter/superport_api/` (Rust/Actix, SeaORM)
|
|
- Agent doc precedence: `./.claude/agents/` → `~/.claude/agents/` (global)
|
|
- Project communication/comments can be in Korean, but this file stays in English unless requested otherwise.
|
|
|
|
|
|
## 1) System Overview
|
|
|
|
- Stack: Flutter 3.x, Dart 3.x, Provider, get_it, dio, freezed, json_serializable, shadcn_ui
|
|
- Architecture: Clean Architecture (recommended) with some legacy Service coexistence
|
|
- Data (API/DTO/RemoteDataSource) → Domain (Repository/UseCase) → Presentation (Controller/UI)
|
|
- Some features still use legacy Service-based logic
|
|
- Data flow: Backend owns business logic; frontend focuses on DTO mapping and presentation
|
|
- UI: shadcn_ui components as standard; tables migrating to ShadTable
|
|
|
|
|
|
## 2) Current Status (Important)
|
|
|
|
- DI: `lib/injection_container.dart` via get_it
|
|
- Both ApiClient (env-driven Dio) and a raw Dio existed; now standardized on ApiClient.dio
|
|
- Avoid raw Dio; use `ApiClient` only
|
|
- Env/config: `.env.development`, `.env.production`, `lib/core/config/environment.dart`
|
|
- `main.dart` must call `Environment.initialize()` to load .env before DI
|
|
- Previously, baseUrl was hardcoded in DI; now use Environment + ApiClient consistently
|
|
- UI standard: per root `CLAUDE.md`, use ShadTable; avoid Flutter base widgets for tables
|
|
- Tests: multiple unit/integration suites under `test/`, including `test/integration/automated`
|
|
- Docs: `docs/backend.md` (backend issues/requests), `docs/superportPRD.md` (requirements/UI)
|
|
|
|
|
|
## 3) Directory Map (Essentials)
|
|
|
|
```
|
|
lib/
|
|
core/ # constants/utils/env/storage/theme/errors
|
|
data/ # DTOs, RemoteDataSources, Repo impl, ApiClient/Interceptors
|
|
domain/ # Entities, Repository interfaces, UseCases
|
|
screens/ # UI screens and Controllers (ChangeNotifier)
|
|
services/ # legacy/transitional services
|
|
models/ # app-side models (Freezed/Json)
|
|
utils/ # UI/validators (some moving to core)
|
|
```
|
|
|
|
- API endpoints: `lib/core/constants/api_endpoints.dart`
|
|
- Env/Config: `lib/core/config/environment.dart`
|
|
- API client: `lib/data/datasources/remote/api_client.dart`
|
|
- DI container: `lib/injection_container.dart`
|
|
|
|
|
|
## 4) Coding Rules (Mandatory)
|
|
|
|
- Follow Clean Architecture flow:
|
|
- API ← Repository (impl in data) ← UseCase ← Controller ← UI
|
|
- DTO/field names must match backend exactly
|
|
- DTO field mapping (critical)
|
|
- Example
|
|
```dart
|
|
@JsonSerializable()
|
|
class EquipmentDto {
|
|
@JsonKey(name: 'companies_id') final int? companiesId; // NOT company_id
|
|
@JsonKey(name: 'models_id') final int? modelsId; // NOT model_id
|
|
const EquipmentDto({this.companiesId, this.modelsId});
|
|
}
|
|
```
|
|
- Error handling
|
|
- Domain returns `Either<Failure, T>`
|
|
- Show user-friendly messages in UI (see `AppConstants`)
|
|
- State management
|
|
- Controllers are ChangeNotifier; inject via Provider
|
|
- Wrap dialogs/routes with Provider when needed
|
|
- UI components
|
|
- Prefer shadcn_ui; tables must use `ShadTable`
|
|
- Theme/color/spacing via `ShadcnTheme` and shared constants
|
|
- Lint/logging
|
|
- `analysis_options.yaml` relaxes some warnings for dev speed
|
|
- Use debug logging in dev; minimize in production
|
|
|
|
|
|
## 5) API/Networking
|
|
|
|
- Define endpoints in `lib/core/constants/api_endpoints.dart`
|
|
- Use ApiClient (recommended)
|
|
- Env-driven Base URL/Timeout/Logging via `.env`
|
|
- Interceptors: Auth, Logging (dev), Response normalization, Error handling
|
|
- Tokens read from secure storage/AuthService
|
|
- Avoid raw Dio
|
|
- For transitional code requiring `Dio`, obtain it from `sl<ApiClient>().dio`
|
|
- 401 handling
|
|
- Refresh token flow implemented in `AuthInterceptor` using `AuthService.refreshToken()`
|
|
- On success, original request is retried; on failure, session is cleared
|
|
|
|
|
|
## 6) Dependency Injection (DI)
|
|
|
|
- Container: `lib/injection_container.dart`
|
|
- Order: External → Core → ApiClient/Dio → DataSource → Repository → UseCase → Controller → Services
|
|
- Lifecycle: Repository/UseCase as `registerLazySingleton`, Controller as `registerFactory`
|
|
- Example
|
|
```dart
|
|
// 1) RemoteDataSource
|
|
sl.registerLazySingleton<MyFeatureRemoteDataSource>(
|
|
() => MyFeatureRemoteDataSourceImpl(sl<ApiClient>()),
|
|
);
|
|
// 2) Repository
|
|
sl.registerLazySingleton<MyFeatureRepository>(
|
|
() => MyFeatureRepositoryImpl(sl<MyFeatureRemoteDataSource>()),
|
|
);
|
|
// 3) UseCase
|
|
sl.registerLazySingleton(() => GetMyFeatureUseCase(sl<MyFeatureRepository>()));
|
|
// 4) Controller
|
|
sl.registerFactory(() => MyFeatureController(sl<GetMyFeatureUseCase>()));
|
|
```
|
|
|
|
|
|
## 7) Environment & Running
|
|
|
|
- Env files: `.env.development`, `.env.production` (sample: `.env.example`)
|
|
- Environment class: `lib/core/config/environment.dart`
|
|
- Use `Environment.apiBaseUrl`, `apiTimeout`, `enableLogging`
|
|
- Initialization at startup
|
|
```dart
|
|
void main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
await Environment.initialize();
|
|
await di.init();
|
|
runApp(const SuperportApp());
|
|
}
|
|
```
|
|
- Web dev with CORS disabled: `./run_web_with_proxy.sh` (keep API URL consistent with .env)
|
|
|
|
|
|
## 8) Backend Integration
|
|
|
|
- Backend repo: `/Users/maximilian.j.sul/Documents/flutter/superport_api/`
|
|
- Rust (Actix), SeaORM, PostgreSQL, JWT
|
|
- See backend README for run/migrations/env
|
|
- Frontend policy
|
|
- Backend schema/fields = source of truth
|
|
- Keep business logic on the backend
|
|
- Known issues
|
|
- See `docs/backend.md`: `GET /companies` with `search` + `is_active` returns empty (Critical)
|
|
- When backend responses change, update DTO → RemoteDataSource → Repository in order
|
|
|
|
|
|
## 9) Build/Test/Quality
|
|
|
|
- Install deps: `flutter pub get`
|
|
- Static analysis: `flutter analyze` (keep ERROR: 0)
|
|
- Tests: `flutter test` or use scripts under `test/integration/automated`
|
|
- Quick API check: `./test_api_integration.sh`
|
|
- Quality script: `./scripts/fix_code_quality.sh`
|
|
|
|
|
|
## 10) Feature Workflow (Checklist)
|
|
|
|
1. Confirm requirements/backend spec (`docs/superportPRD.md`, backend README, `docs/backend.md`)
|
|
2. Add/verify endpoint in `core/constants/api_endpoints.dart`
|
|
3. Define DTO with exact `JsonKey` mapping
|
|
4. Implement RemoteDataSource (API call/error mapping)
|
|
5. Add Repository interface/impl (delegate to DataSource)
|
|
6. Write UseCase (single responsibility; compose in Controller)
|
|
7. Register in DI (`injection_container.dart`)
|
|
8. Add Controller and wire UI (Provider)
|
|
9. Write tests (errors/edge cases)
|
|
10. Use shadcn_ui in UI (ShadTable for tables)
|
|
|
|
|
|
## 11) Legacy Coexistence (Transition)
|
|
|
|
- Service-based calls remain for some features (e.g., parts of Company/Auth)
|
|
- Prefer migrating to Repository/UseCase; gradually remove Service-bound UseCases
|
|
- Avoid duplicate implementations; standardize on `ApiClient`
|
|
|
|
|
|
## 12) Improvement Roadmap
|
|
|
|
- Env/Networking
|
|
- Remove raw Dio from DI → standardized on `ApiClient`
|
|
- Ensure `Environment.initialize()` runs before DI
|
|
- Stabilize refresh-token retry flow
|
|
- UI
|
|
- Continue ShadTable migration; follow `CLAUDE.md` checklist
|
|
- Use only design system constants for color/typography/spacing
|
|
- Architecture
|
|
- Migrate remaining Service-based paths (Company/Auth) to Repos
|
|
- Remove interceptor duplication
|
|
- Tests
|
|
- Strengthen real-API integration suite and wire into CI
|
|
|
|
|
|
## 13) Frequently Used Paths
|
|
|
|
- Env: `lib/core/config/environment.dart`, `.env.*`
|
|
- Endpoints: `lib/core/constants/api_endpoints.dart`
|
|
- API: `lib/data/datasources/remote/api_client.dart`
|
|
- DI: `lib/injection_container.dart`
|
|
- Controllers: `lib/screens/*/controllers/*_controller.dart`
|
|
- Docs: `docs/backend.md`, `docs/superportPRD.md`, root `CLAUDE.md`
|
|
- Global agent docs: `~/.claude/CLAUDE.md`, `~/.claude/agents/*.md`
|
|
|
|
|
|
## 14) Patterns
|
|
|
|
```dart
|
|
// Controller — standard pattern with loading/error states
|
|
class ExampleController extends ChangeNotifier {
|
|
final GetItemsUseCase _getItems;
|
|
bool _loading = false; String? _error; List<Item> _items = [];
|
|
ExampleController(this._getItems);
|
|
|
|
Future<void> fetch() async {
|
|
_loading = true; _error = null; notifyListeners();
|
|
final result = await _getItems();
|
|
result.fold(
|
|
(f) { _error = f.message; },
|
|
(data) { _items = data; },
|
|
);
|
|
_loading = false; notifyListeners();
|
|
}
|
|
}
|
|
```
|
|
|
|
```dart
|
|
// RemoteDataSource — using ApiClient
|
|
class ItemsRemoteDataSource {
|
|
final ApiClient _api;
|
|
ItemsRemoteDataSource(this._api);
|
|
|
|
Future<List<ItemDto>> getItems() async {
|
|
final res = await _api.get(ApiEndpoints.items);
|
|
final list = (res.data['data'] as List).cast<Map<String, dynamic>>();
|
|
return list.map(ItemDto.fromJson).toList();
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
Before merging, always verify:
|
|
- Patterns in `./.claude/agents/` → `~/.claude/agents/`
|
|
- .env setup and API Base URL consistency
|
|
- New features use ApiClient + Repository/UseCase
|
|
- UI uses shadcn_ui (ShadTable for tables)
|
|
- Tests and analysis pass (0 errors)
|
|
|
|
|
|
## 15) Session Continuation Context (2025-09-08)
|
|
|
|
This section captures the current in-progress state so another session can resume seamlessly.
|
|
|
|
- Environment/DI
|
|
- `lib/main.dart`: calls `await Environment.initialize()` before DI init.
|
|
- `lib/injection_container.dart`: unified network stack to `sl<ApiClient>().dio`; removed raw `Dio` config to avoid divergence.
|
|
|
|
- Auth flow (migrated to Repository)
|
|
- UseCases moved to `AuthRepository`:
|
|
- `LogoutUseCase`, `GetCurrentUserUseCase` (returns `AuthUser`), `RefreshTokenUseCase`.
|
|
- `AuthRepository` extended with:
|
|
- `getStoredAccessToken()`, `getStoredRefreshToken()`, `clearLocalSession()`.
|
|
- Interceptor now repository-based:
|
|
- `lib/data/datasources/remote/interceptors/auth_interceptor.dart` reads tokens via `AuthRepository`, handles 401 → refresh → retry → clear+navigate.
|
|
- Global navigation: `lib/core/navigation/app_navigator.dart` (key used in `ShadApp`), enables navigation on 401.
|
|
|
|
- UI: ShadTable migration (columns preserved as-is)
|
|
- Equipment list: `lib/screens/equipment/equipment_list.dart` → `_buildShadTable` used. Legacy `_buildFlexibleTable` is deprecated (kept temporarily).
|
|
- Vendor list: `lib/screens/vendor/vendor_list_screen.dart` → `ShadTable.list`.
|
|
- Model list: `lib/screens/model/model_list_screen.dart` → `ShadTable.list`.
|
|
- User list: `lib/screens/user/user_list.dart` → `ShadTable.list` (Company column shows `-` until backend supplies it).
|
|
- Inventory history: `lib/screens/inventory/inventory_history_screen.dart` → `ShadTable.list`.
|
|
- Company list: `lib/screens/company/company_list.dart` → `ShadTable.list`.
|
|
|
|
- Company UseCases (Repository-based)
|
|
- `GetCompanyDetailUseCase`, `CreateCompanyUseCase`, `UpdateCompanyUseCase`, `DeleteCompanyUseCase`, `ToggleCompanyStatusUseCase`, `GetCompanyHierarchyUseCase`, `UpdateParentCompanyUseCase`, `ValidateCompanyDeletionUseCase` now use `CompanyRepository`.
|
|
|
|
- Analysis status
|
|
- `flutter analyze` shows 0 errors (non-blocking warnings remain; functionality unaffected).
|
|
|
|
- Known remaining warnings (non-blocking)
|
|
- `lib/screens/equipment/equipment_in_form.dart`: minor validator consistency; most null-aware warnings resolved.
|
|
- Other general warnings may persist but do not affect functionality.
|
|
|
|
- How to continue
|
|
- Optional cleanups (most done):
|
|
- Deprecated `_buildFlexibleTable` removed; ShadTable only.
|
|
- Unused `_showAddressDetails` and `_maxRetryCount` removed.
|
|
- Web health notification uses JS interop; ensure global function exists (added to `web/index.html`).
|
|
- User list: map Company name into the “Company” column when backend/DTO supplies it (update DTO + repository mapping).
|
|
- Add/extend integration tests for Auth (login → 401 → refresh → retry) and core flows.
|
|
- Run `flutter pub get` after dependency adjustment (`js` pinned to `^0.6.7`).
|
|
|
|
- Quick verification commands
|
|
- Static analysis: `flutter analyze` (expect 0 errors).
|
|
- Sample unit test: `flutter test test/utils/currency_formatter_test.dart`.
|
|
- Dev run (web, with CORS disabled): `./run_web_with_proxy.sh`.
|
|
|
|
- Conventions to uphold when resuming
|
|
- Use `ApiClient` (env-driven) for all network calls; do not instantiate raw `Dio`.
|
|
- Keep Clean Architecture boundaries; DTO names match backend exactly (`JsonKey(name: ...)`).
|
|
- UI tables use `ShadTable.list`; avoid Flutter default tables.
|
|
- For navigation from non-UI layers (e.g., interceptor), use `appNavigatorKey`.
|
|
- DI: register Repositories/UseCases as lazy singletons, Controllers as factories.
|
|
|
|
|
|
## 16) Git / Commit Policy
|
|
|
|
- Commit messages must be written in Korean. (필수)
|
|
- 기술 용어/식별자는 영어 사용 가능하나, 메시지의 기본 문장은 한국어로 작성합니다.
|
|
- PR 제목/설명도 동일한 원칙을 따릅니다.
|
|
- 권장 형식
|
|
- 한 줄 요약(명령형, 50자 내외) + 필요 시 본문(이유/영향/추가 작업)
|
|
- 예) "장비 목록 페이지네이션 메타 적용; 서버 total/totalPages 사용"
|
|
- 예) "우편번호 팝업 테이블 컬럼 폭 축소로 수평 스크롤 제거"
|