feat(frontend): 승인 템플릿 API 통합 및 디버그 로그인 확장

- docs 폴더 문서를 최신 API 계약으로 갱신하고 가이드를 다듬었다\n- approvals data/presentation 레이어를 API v4 스펙에 맞춰 리팩터링했다\n- approver 자동완성 위젯을 신규 공유 레포지토리에 맞춰 교체하고 UX를 보강했다\n- inventory/rental 페이지 테이블 초기화 시 승인 기준 연동을 정비했다\n- 로그인 페이지 디버그 버튼을 tera/exa 계정으로 분리해 QA 로그인을 단순화했다\n- get_it 등록과 테스트 케이스를 신규 공유 리포지토리에 맞춰 업데이트했다
This commit is contained in:
JiWoong Sul
2025-11-05 17:05:38 +09:00
parent 3e83408aa7
commit fa0bda5ea4
28 changed files with 1102 additions and 545 deletions

View File

@@ -12,6 +12,11 @@ import 'package:superport_v2/features/approvals/domain/repositories/approval_tem
import 'package:superport_v2/features/approvals/domain/usecases/apply_approval_template_use_case.dart';
import 'package:superport_v2/features/approvals/domain/usecases/save_approval_template_use_case.dart';
import 'package:superport_v2/features/approvals/template/presentation/pages/approval_template_page.dart';
import 'package:superport_v2/features/approvals/shared/domain/entities/approval_approver_candidate.dart';
import 'package:superport_v2/features/approvals/shared/domain/repositories/approval_approver_repository.dart';
import 'package:superport_v2/features/auth/application/auth_service.dart';
import 'package:superport_v2/features/auth/domain/entities/auth_session.dart';
import 'package:superport_v2/features/auth/domain/entities/authenticated_user.dart';
class _MockApprovalTemplateRepository extends Mock
implements ApprovalTemplateRepository {}
@@ -23,6 +28,8 @@ class _FakeTemplateInput extends Fake implements ApprovalTemplateInput {}
class _FakeTemplateStepInput extends Fake
implements ApprovalTemplateStepInput {}
class _MockAuthService extends Mock implements AuthService {}
Widget _buildApp(Widget child) {
return MaterialApp(
home: ShadTheme(
@@ -82,6 +89,37 @@ void main() {
approvalRepository: approvalRepository,
),
);
GetIt.I.registerLazySingleton<ApprovalApproverRepository>(
() => _FakeApproverRepository({
33: ApprovalApproverCandidate(
id: 33,
employeeNo: 'EMP033',
name: '테스트 승인자',
team: 'QA팀',
),
21: ApprovalApproverCandidate(
id: 21,
employeeNo: 'E001',
name: '최승인',
team: '결재팀',
),
}),
);
final authService = _MockAuthService();
when(() => authService.session).thenReturn(
AuthSession(
accessToken: 'test-access',
refreshToken: 'test-refresh',
expiresAt: DateTime(2099, 1, 1),
user: const AuthenticatedUser(
id: 99,
name: '테스트 사용자',
employeeNo: 'E999',
email: 'test@example.com',
),
),
);
GetIt.I.registerSingleton<AuthService>(authService);
});
ApprovalTemplate buildTemplate({bool isActive = true}) {
@@ -333,3 +371,38 @@ void main() {
});
});
}
class _FakeApproverRepository implements ApprovalApproverRepository {
_FakeApproverRepository(this._candidates);
final Map<int, ApprovalApproverCandidate> _candidates;
@override
Future<ApprovalApproverCandidate?> fetchById(int id) async {
return _candidates[id];
}
@override
Future<List<ApprovalApproverCandidate>> search({
required String keyword,
int limit = 20,
}) async {
final lower = keyword.trim().toLowerCase();
if (lower.isEmpty) {
return _candidates.values.toList(growable: false);
}
return _candidates.values
.where(
(candidate) =>
candidate.name.toLowerCase().contains(lower) ||
candidate.employeeNo.toLowerCase().contains(lower) ||
candidate.id.toString().contains(lower),
)
.toList(growable: false);
}
@override
Future<List<ApprovalApproverCandidate>> listInitial({int limit = 20}) async {
return _candidates.values.take(limit).toList(growable: false);
}
}