# 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` - 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().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( () => MyFeatureRemoteDataSourceImpl(sl()), ); // 2) Repository sl.registerLazySingleton( () => MyFeatureRepositoryImpl(sl()), ); // 3) UseCase sl.registerLazySingleton(() => GetMyFeatureUseCase(sl())); // 4) Controller sl.registerFactory(() => MyFeatureController(sl())); ``` ## 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 _items = []; ExampleController(this._getItems); Future 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> getItems() async { final res = await _api.get(ApiEndpoints.items); final list = (res.data['data'] as List).cast>(); 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().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 사용" - 예) "우편번호 팝업 테이블 컬럼 폭 축소로 수평 스크롤 제거"