결재 단계 필터 개선

This commit is contained in:
JiWoong Sul
2025-09-25 16:53:43 +09:00
parent 35b9002688
commit c31f6217ef
3 changed files with 68 additions and 2 deletions

View File

@@ -14,6 +14,7 @@ class ApprovalStepController extends ChangeNotifier {
bool _isLoading = false; bool _isLoading = false;
String _query = ''; String _query = '';
int? _statusId; int? _statusId;
int? _approverId;
String? _errorMessage; String? _errorMessage;
bool _isLoadingDetail = false; bool _isLoadingDetail = false;
ApprovalStepRecord? _selected; ApprovalStepRecord? _selected;
@@ -22,6 +23,7 @@ class ApprovalStepController extends ChangeNotifier {
bool get isLoading => _isLoading; bool get isLoading => _isLoading;
String get query => _query; String get query => _query;
int? get statusId => _statusId; int? get statusId => _statusId;
int? get approverId => _approverId;
String? get errorMessage => _errorMessage; String? get errorMessage => _errorMessage;
bool get isLoadingDetail => _isLoadingDetail; bool get isLoadingDetail => _isLoadingDetail;
ApprovalStepRecord? get selected => _selected; ApprovalStepRecord? get selected => _selected;
@@ -37,6 +39,7 @@ class ApprovalStepController extends ChangeNotifier {
pageSize: _result?.pageSize ?? 20, pageSize: _result?.pageSize ?? 20,
query: sanitizedQuery.isEmpty ? null : sanitizedQuery, query: sanitizedQuery.isEmpty ? null : sanitizedQuery,
statusId: _statusId, statusId: _statusId,
approverId: _approverId,
); );
_result = response; _result = response;
} catch (e) { } catch (e) {
@@ -57,6 +60,11 @@ class ApprovalStepController extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void updateApproverId(int? value) {
_approverId = value;
notifyListeners();
}
Future<ApprovalStepRecord?> fetchDetail(int id) async { Future<ApprovalStepRecord?> fetchDetail(int id) async {
_isLoadingDetail = true; _isLoadingDetail = true;
_errorMessage = null; _errorMessage = null;
@@ -84,11 +92,13 @@ class ApprovalStepController extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
bool get hasActiveFilters => _query.trim().isNotEmpty || _statusId != null; bool get hasActiveFilters =>
_query.trim().isNotEmpty || _statusId != null || _approverId != null;
void resetFilters() { void resetFilters() {
_query = ''; _query = '';
_statusId = null; _statusId = null;
_approverId = null;
notifyListeners(); notifyListeners();
} }
} }

View File

@@ -140,6 +140,8 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
: (result.page * result.pageSize) < result.total; : (result.page * result.pageSize) < result.total;
final statusOptions = _buildStatusOptions(records); final statusOptions = _buildStatusOptions(records);
final selectedStatus = _controller.statusId ?? -1; final selectedStatus = _controller.statusId ?? -1;
final approverOptions = _buildApproverOptions(records);
final selectedApprover = _controller.approverId ?? -1;
return AppLayout( return AppLayout(
title: '결재 단계 관리', title: '결재 단계 관리',
@@ -197,6 +199,32 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
.toList(), .toList(),
), ),
), ),
if (approverOptions.length > 1)
SizedBox(
width: 220,
child: ShadSelect<int>(
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<int>(
value: opt.id,
child: Text(opt.name),
),
)
.toList(),
),
),
ShadButton.outline( ShadButton.outline(
onPressed: _controller.isLoading ? null : _applyFilters, onPressed: _controller.isLoading ? null : _applyFilters,
child: const Text('검색 적용'), child: const Text('검색 적용'),
@@ -361,6 +389,18 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
return options.toList()..sort((a, b) => a.id.compareTo(b.id)); return options.toList()..sort((a, b) => a.id.compareTo(b.id));
} }
List<_ApproverOption> _buildApproverOptions(
List<ApprovalStepRecord> 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<void> _applyFilters() async { Future<void> _applyFilters() async {
_controller.updateQuery(_searchController.text.trim()); _controller.updateQuery(_searchController.text.trim());
await _controller.fetch(page: 1); await _controller.fetch(page: 1);
@@ -480,6 +520,21 @@ class _StatusOption {
int get hashCode => id.hashCode; 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 { class _DetailRow extends StatelessWidget {
const _DetailRow({required this.label, required this.value}); const _DetailRow({required this.label, required this.value});

View File

@@ -106,6 +106,7 @@ void main() {
controller.updateQuery('APP-2024'); controller.updateQuery('APP-2024');
controller.updateStatusId(2); controller.updateStatusId(2);
controller.updateApproverId(21);
await controller.fetch(page: 3); await controller.fetch(page: 3);
@@ -115,7 +116,7 @@ void main() {
pageSize: 20, pageSize: 20,
query: 'APP-2024', query: 'APP-2024',
statusId: 2, statusId: 2,
approverId: null, approverId: 21,
approvalId: null, approvalId: null,
), ),
).called(1); ).called(1);