From c31f6217ef7b343642b8f27c105d3d7052120c2a Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Thu, 25 Sep 2025 16:53:43 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B2=B0=EC=9E=AC=20=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/approval_step_controller.dart | 12 +++- .../pages/approval_step_page.dart | 55 +++++++++++++++++++ .../approval_step_controller_test.dart | 3 +- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/lib/features/approvals/step/presentation/controllers/approval_step_controller.dart b/lib/features/approvals/step/presentation/controllers/approval_step_controller.dart index 4396ea8..f442a06 100644 --- a/lib/features/approvals/step/presentation/controllers/approval_step_controller.dart +++ b/lib/features/approvals/step/presentation/controllers/approval_step_controller.dart @@ -14,6 +14,7 @@ class ApprovalStepController extends ChangeNotifier { bool _isLoading = false; String _query = ''; int? _statusId; + int? _approverId; String? _errorMessage; bool _isLoadingDetail = false; ApprovalStepRecord? _selected; @@ -22,6 +23,7 @@ class ApprovalStepController extends ChangeNotifier { bool get isLoading => _isLoading; String get query => _query; int? get statusId => _statusId; + int? get approverId => _approverId; String? get errorMessage => _errorMessage; bool get isLoadingDetail => _isLoadingDetail; ApprovalStepRecord? get selected => _selected; @@ -37,6 +39,7 @@ class ApprovalStepController extends ChangeNotifier { pageSize: _result?.pageSize ?? 20, query: sanitizedQuery.isEmpty ? null : sanitizedQuery, statusId: _statusId, + approverId: _approverId, ); _result = response; } catch (e) { @@ -57,6 +60,11 @@ class ApprovalStepController extends ChangeNotifier { notifyListeners(); } + void updateApproverId(int? value) { + _approverId = value; + notifyListeners(); + } + Future fetchDetail(int id) async { _isLoadingDetail = true; _errorMessage = null; @@ -84,11 +92,13 @@ class ApprovalStepController extends ChangeNotifier { notifyListeners(); } - bool get hasActiveFilters => _query.trim().isNotEmpty || _statusId != null; + bool get hasActiveFilters => + _query.trim().isNotEmpty || _statusId != null || _approverId != null; void resetFilters() { _query = ''; _statusId = null; + _approverId = null; notifyListeners(); } } diff --git a/lib/features/approvals/step/presentation/pages/approval_step_page.dart b/lib/features/approvals/step/presentation/pages/approval_step_page.dart index 4e3bd93..2e75591 100644 --- a/lib/features/approvals/step/presentation/pages/approval_step_page.dart +++ b/lib/features/approvals/step/presentation/pages/approval_step_page.dart @@ -140,6 +140,8 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> { : (result.page * result.pageSize) < result.total; final statusOptions = _buildStatusOptions(records); final selectedStatus = _controller.statusId ?? -1; + final approverOptions = _buildApproverOptions(records); + final selectedApprover = _controller.approverId ?? -1; return AppLayout( title: '결재 단계 관리', @@ -197,6 +199,32 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> { .toList(), ), ), + if (approverOptions.length > 1) + SizedBox( + width: 220, + child: ShadSelect( + key: ValueKey(selectedApprover), + initialValue: selectedApprover, + onChanged: (value) { + _controller.updateApproverId(value == -1 ? null : value); + }, + selectedOptionBuilder: (context, value) { + final option = approverOptions.firstWhere( + (element) => element.id == value, + orElse: () => const _ApproverOption(id: -1, name: '전체'), + ); + return Text(option.name); + }, + options: approverOptions + .map( + (opt) => ShadOption( + value: opt.id, + child: Text(opt.name), + ), + ) + .toList(), + ), + ), ShadButton.outline( onPressed: _controller.isLoading ? null : _applyFilters, child: const Text('검색 적용'), @@ -361,6 +389,18 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> { return options.toList()..sort((a, b) => a.id.compareTo(b.id)); } + List<_ApproverOption> _buildApproverOptions( + List records, + ) { + final options = <_ApproverOption>{}; + options.add(const _ApproverOption(id: -1, name: '전체')); + for (final record in records) { + final approver = record.step.approver; + options.add(_ApproverOption(id: approver.id, name: approver.name)); + } + return options.toList()..sort((a, b) => a.name.compareTo(b.name)); + } + Future _applyFilters() async { _controller.updateQuery(_searchController.text.trim()); await _controller.fetch(page: 1); @@ -480,6 +520,21 @@ class _StatusOption { int get hashCode => id.hashCode; } +class _ApproverOption { + const _ApproverOption({required this.id, required this.name}); + + final int id; + final String name; + + @override + bool operator ==(Object other) { + return other is _ApproverOption && other.id == id; + } + + @override + int get hashCode => id.hashCode; +} + class _DetailRow extends StatelessWidget { const _DetailRow({required this.label, required this.value}); diff --git a/test/features/approvals/step/presentation/controllers/approval_step_controller_test.dart b/test/features/approvals/step/presentation/controllers/approval_step_controller_test.dart index ec9e793..74dd349 100644 --- a/test/features/approvals/step/presentation/controllers/approval_step_controller_test.dart +++ b/test/features/approvals/step/presentation/controllers/approval_step_controller_test.dart @@ -106,6 +106,7 @@ void main() { controller.updateQuery('APP-2024'); controller.updateStatusId(2); + controller.updateApproverId(21); await controller.fetch(page: 3); @@ -115,7 +116,7 @@ void main() { pageSize: 20, query: 'APP-2024', statusId: 2, - approverId: null, + approverId: 21, approvalId: null, ), ).called(1);