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

@@ -4,7 +4,7 @@ import 'package:shadcn_ui/shadcn_ui.dart';
import '../../../../../widgets/components/feedback.dart';
import '../../../../../widgets/components/superport_dialog.dart';
import '../../../shared/approver_catalog.dart';
import '../../../shared/domain/entities/approval_approver_candidate.dart';
import '../../../shared/widgets/approver_autocomplete_field.dart';
import '../controllers/approval_request_controller.dart';
import 'approval_step_row.dart';
@@ -435,7 +435,7 @@ class _ConfiguratorDialogBodyState extends State<_ConfiguratorDialogBody> {
}
Future<void> _openAddStepDialog() async {
ApprovalApproverCatalogItem? selected;
ApprovalApproverCandidate? selected;
final idController = TextEditingController();
final result = await SuperportDialog.show<bool>(
@@ -483,13 +483,19 @@ class _ConfiguratorDialogBodyState extends State<_ConfiguratorDialogBody> {
return;
}
final participant = _resolveParticipant(selected, idController.text.trim());
if (participant == null) {
final selection = selected;
if (selection == null) {
SuperportToast.warning(context, '유효한 승인자를 선택해주세요.');
idController.dispose();
return;
}
final participant = ApprovalRequestParticipant(
id: selection.id,
name: selection.name,
employeeNo: selection.employeeNo,
);
final added = widget.controller.addStep(approver: participant);
if (!added) {
final message = widget.controller.errorMessage ?? '결재 단계를 추가하지 못했습니다.';
@@ -503,31 +509,6 @@ class _ConfiguratorDialogBodyState extends State<_ConfiguratorDialogBody> {
idController.dispose();
}
ApprovalRequestParticipant? _resolveParticipant(
ApprovalApproverCatalogItem? selected,
String manualInput,
) {
if (selected != null) {
return ApprovalRequestParticipant(
id: selected.id,
name: selected.name,
employeeNo: selected.employeeNo,
);
}
final manualId = int.tryParse(manualInput);
if (manualId == null) {
return null;
}
final match = ApprovalApproverCatalog.byId(manualId);
if (match == null) {
return null;
}
return ApprovalRequestParticipant(
id: match.id,
name: match.name,
employeeNo: match.employeeNo,
);
}
}
class _InfoBadge extends StatelessWidget {

View File

@@ -3,7 +3,7 @@ import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../../../../widgets/components/feedback.dart';
import '../../../shared/approver_catalog.dart';
import '../../../shared/domain/entities/approval_approver_candidate.dart';
import '../../../shared/widgets/approver_autocomplete_field.dart';
import '../controllers/approval_request_controller.dart';
@@ -96,37 +96,23 @@ class _ApprovalStepRowState extends State<ApprovalStepRow> {
Future<void> _handleApproverSelected(
BuildContext context,
ApprovalApproverCatalogItem? item,
ApprovalApproverCandidate? candidate,
) async {
if (widget.readOnly) {
return;
}
ApprovalRequestParticipant? nextParticipant;
if (item != null) {
if (candidate != null) {
nextParticipant = ApprovalRequestParticipant(
id: item.id,
name: item.name,
employeeNo: item.employeeNo,
id: candidate.id,
name: candidate.name,
employeeNo: candidate.employeeNo,
);
} else {
final manualId = int.tryParse(_approverIdController.text.trim());
if (manualId == null) {
SuperportToast.warning(context, '승인자를 다시 선택해주세요.');
_restorePreviousApprover();
return;
}
final catalogMatch = ApprovalApproverCatalog.byId(manualId);
if (catalogMatch == null) {
SuperportToast.warning(context, '등록되지 않은 승인자입니다.');
_restorePreviousApprover();
return;
}
nextParticipant = ApprovalRequestParticipant(
id: catalogMatch.id,
name: catalogMatch.name,
employeeNo: catalogMatch.employeeNo,
);
SuperportToast.warning(context, '승인자를 다시 선택해주세요.');
_restorePreviousApprover();
return;
}
final updated = widget.controller.updateStep(