결재 및 마스터 모듈을 v4 API 계약에 맞게 조정

This commit is contained in:
JiWoong Sul
2025-10-16 17:27:20 +09:00
parent d5c99627db
commit 9e2244f260
34 changed files with 1394 additions and 330 deletions

View File

@@ -11,7 +11,6 @@ import '../../../../core/permissions/permission_resources.dart';
import '../../../../widgets/app_layout.dart';
import '../../../../widgets/components/feedback.dart';
import '../../../../widgets/components/filter_bar.dart';
import '../../../../widgets/components/superport_date_picker.dart';
import '../../../../widgets/components/superport_dialog.dart';
import '../../../../widgets/components/superport_table.dart';
import '../../../../widgets/components/feature_disabled_placeholder.dart';
@@ -20,6 +19,7 @@ import '../../domain/entities/approval_template.dart';
import '../../domain/repositories/approval_repository.dart';
import '../../domain/repositories/approval_template_repository.dart';
import '../../../inventory/lookups/domain/repositories/inventory_lookup_repository.dart';
import '../../../inventory/shared/widgets/employee_autocomplete_field.dart';
import '../controllers/approval_controller.dart';
const _approvalsResourcePath = PermissionResources.approvals;
@@ -73,11 +73,11 @@ class _ApprovalEnabledPage extends StatefulWidget {
class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
late final ApprovalController _controller;
final TextEditingController _searchController = TextEditingController();
final FocusNode _searchFocus = FocusNode();
final intl.DateFormat _dateFormat = intl.DateFormat('yyyy-MM-dd');
final TextEditingController _transactionController = TextEditingController();
final TextEditingController _requesterController = TextEditingController();
final FocusNode _transactionFocus = FocusNode();
InventoryEmployeeSuggestion? _selectedRequester;
final intl.DateFormat _dateTimeFormat = intl.DateFormat('yyyy-MM-dd HH:mm');
DateTimeRange? _dateRange;
String? _lastError;
int? _selectedTemplateId;
@@ -114,8 +114,9 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
void dispose() {
_controller.removeListener(_handleControllerUpdate);
_controller.dispose();
_searchController.dispose();
_searchFocus.dispose();
_transactionController.dispose();
_requesterController.dispose();
_transactionFocus.dispose();
super.dispose();
}
@@ -197,16 +198,39 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
),
children: [
SizedBox(
width: 260,
width: 180,
child: ShadInput(
controller: _searchController,
focusNode: _searchFocus,
placeholder: const Text('결재번호, 트랜잭션번호, 상신자 검색'),
leading: const Icon(lucide.LucideIcons.search, size: 16),
controller: _transactionController,
focusNode: _transactionFocus,
enabled: !_controller.isLoadingList,
keyboardType: TextInputType.number,
placeholder: const Text('트랜잭션 ID 입력'),
leading: const Icon(lucide.LucideIcons.hash, size: 16),
onChanged: (_) => setState(() {}),
onSubmitted: (_) => _applyFilters(),
),
),
SizedBox(
width: 280,
child: InventoryEmployeeAutocompleteField(
controller: _requesterController,
initialSuggestion: _selectedRequester,
enabled: !_controller.isLoadingList,
onSuggestionSelected: (suggestion) {
setState(() => _selectedRequester = suggestion);
},
onChanged: () {
setState(() {
final selectedLabel = _selectedRequester == null
? ''
: '${_selectedRequester!.name} (${_selectedRequester!.employeeNo})';
if (_requesterController.text.trim() != selectedLabel) {
_selectedRequester = null;
}
});
},
),
),
SizedBox(
width: 200,
child: ShadSelect<ApprovalStatusFilter>(
@@ -229,37 +253,6 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
.toList(),
),
),
SizedBox(
width: 220,
child: SuperportDateRangePickerButton(
value: _dateRange,
dateFormat: _dateFormat,
enabled: !_controller.isLoadingList,
firstDate: DateTime(DateTime.now().year - 5),
lastDate: DateTime(DateTime.now().year + 1),
initialDateRange:
_dateRange ??
DateTimeRange(
start: DateTime.now().subtract(const Duration(days: 7)),
end: DateTime.now(),
),
onChanged: (range) {
if (range == null) return;
setState(() => _dateRange = range);
_controller.updateDateRange(range.start, range.end);
_controller.fetch(page: 1);
},
),
),
if (_dateRange != null)
ShadButton.ghost(
onPressed: () {
setState(() => _dateRange = null);
_controller.updateDateRange(null, null);
_controller.fetch(page: 1);
},
child: const Text('기간 초기화'),
),
],
),
child: Column(
@@ -476,25 +469,48 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
}
void _applyFilters() {
_controller.updateQuery(_searchController.text.trim());
if (_dateRange != null) {
_controller.updateDateRange(_dateRange!.start, _dateRange!.end);
final transactionText = _transactionController.text.trim();
if (transactionText.isNotEmpty) {
final transactionId = int.tryParse(transactionText);
if (transactionId == null) {
SuperportToast.error(context, '트랜잭션 ID는 숫자만 입력해야 합니다.');
return;
}
_controller.updateTransactionFilter(transactionId);
} else {
_controller.updateTransactionFilter(null);
}
final requester = _selectedRequester;
if (requester != null) {
_controller.updateRequestedByFilter(
id: requester.id,
name: requester.name,
employeeNo: requester.employeeNo,
);
} else if (_requesterController.text.trim().isEmpty) {
_controller.updateRequestedByFilter(id: null);
}
_controller.fetch(page: 1);
}
void _resetFilters() {
_searchController.clear();
_searchFocus.requestFocus();
_dateRange = null;
_transactionController.clear();
_requesterController.clear();
setState(() => _selectedRequester = null);
_transactionFocus.requestFocus();
_controller.clearFilters();
_controller.fetch(page: 1);
}
bool _hasFilters() {
return _searchController.text.isNotEmpty ||
_controller.statusFilter != ApprovalStatusFilter.all ||
_dateRange != null;
return _transactionController.text.trim().isNotEmpty ||
_requesterController.text.trim().isNotEmpty ||
_selectedRequester != null ||
_controller.transactionIdFilter != null ||
_controller.requestedById != null ||
_controller.statusFilter != ApprovalStatusFilter.all;
}
Future<void> _handleStepAction(