web: migrate health notifications to js_interop; add browser hook
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled

- Replace dart:js with package:js in health_check_service_web.dart\n- Implement showHealthCheckNotification in web/index.html\n- Pin js dependency to ^0.6.7 for flutter_secure_storage_web compatibility

auth: harden AuthInterceptor + tests

- Allow overrideAuthRepository injection for testing\n- Normalize imports to package: paths\n- Add unit test covering token attach, 401→refresh→retry, and failure path\n- Add integration test skeleton gated by env vars

ui/data: map User.companyName to list column

- Add companyName to domain User\n- Map UserDto.company?.name\n- Render companyName in user_list

cleanup: remove legacy equipment table + unused code; minor warnings

- Remove _buildFlexibleTable and unused helpers\n- Remove unused zipcode details and cache retry constant\n- Fix null-aware and non-null assertions\n- Address child-last warnings in administrator dialog

docs: update AGENTS.md session context
This commit is contained in:
JiWoong Sul
2025-09-08 17:39:00 +09:00
parent 519e1883a3
commit 655d473413
55 changed files with 2729 additions and 4968 deletions

303
AGENTS.md Normal file
View File

@@ -0,0 +1,303 @@
# 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.