전역 구조 리팩터링 및 테스트 확장

This commit is contained in:
JiWoong Sul
2025-09-29 01:51:47 +09:00
parent c00c0c9ab2
commit fef7108479
70 changed files with 7709 additions and 3185 deletions

View File

@@ -7,6 +7,7 @@ import '../../../../../core/config/environment.dart';
import '../../../../../core/constants/app_sections.dart';
import '../../../../../widgets/app_layout.dart';
import '../../../../../widgets/components/filter_bar.dart';
import '../../../../../widgets/components/superport_dialog.dart';
import '../../../../../widgets/spec_page.dart';
import '../controllers/approval_step_controller.dart';
import '../../domain/entities/approval_step_input.dart';
@@ -528,73 +529,50 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
if (!mounted) return;
Navigator.of(context, rootNavigator: true).pop();
if (detail == null) return;
await showDialog<void>(
final step = detail.step;
await SuperportDialog.show<void>(
context: context,
builder: (dialogContext) {
final step = detail.step;
final theme = ShadTheme.of(dialogContext);
return Dialog(
insetPadding: const EdgeInsets.all(24),
clipBehavior: Clip.antiAlias,
child: ShadCard(
title: Text('결재 단계 상세', style: theme.textTheme.h3),
description: Text(
'결재번호 ${detail.approvalNo}',
style: theme.textTheme.muted,
dialog: SuperportDialog(
title: '결재 단계 상세',
description: '결재번호 ${detail.approvalNo}',
constraints: const BoxConstraints(maxWidth: 560),
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 18,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
_DetailRow(label: '단계 순서', value: '${step.stepOrder}'),
_DetailRow(label: '승인자', value: step.approver.name),
_DetailRow(label: '상태', value: step.status.name),
_DetailRow(label: '배정일시', value: _formatDate(step.assignedAt)),
_DetailRow(
label: '결정일시',
value: step.decidedAt == null
? '-'
: _formatDate(step.decidedAt!),
),
footer: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ShadButton.ghost(
onPressed: () => Navigator.of(dialogContext).pop(),
child: const Text('닫기'),
),
],
_DetailRow(label: '템플릿', value: detail.templateName ?? '-'),
_DetailRow(label: '트랜잭션번호', value: detail.transactionNo ?? '-'),
const SizedBox(height: 12),
Text(
'비고',
style: ShadTheme.of(
context,
).textTheme.small.copyWith(fontWeight: FontWeight.w600),
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_DetailRow(label: '단계 순서', value: '${step.stepOrder}'),
_DetailRow(label: '승인자', value: step.approver.name),
_DetailRow(label: '상태', value: step.status.name),
_DetailRow(
label: '배정일시',
value: _formatDate(step.assignedAt),
),
_DetailRow(
label: '결정일시',
value: step.decidedAt == null
? '-'
: _formatDate(step.decidedAt!),
),
_DetailRow(label: '템플릿', value: detail.templateName ?? '-'),
_DetailRow(
label: '트랜잭션번호',
value: detail.transactionNo ?? '-',
),
const SizedBox(height: 12),
Text(
'비고',
style: theme.textTheme.small.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
ShadTextarea(
initialValue: step.note ?? '',
readOnly: true,
minHeight: 80,
maxHeight: 200,
),
],
),
const SizedBox(height: 8),
ShadTextarea(
initialValue: step.note ?? '',
readOnly: true,
minHeight: 80,
maxHeight: 200,
),
),
);
},
],
),
),
);
}
@@ -724,102 +702,93 @@ class _StepFormDialogState extends State<_StepFormDialog> {
final theme = ShadTheme.of(context);
final materialTheme = Theme.of(context);
return Dialog(
insetPadding: const EdgeInsets.all(24),
clipBehavior: Clip.antiAlias,
child: ShadCard(
title: Text(widget.title, style: theme.textTheme.h3),
footer: Row(
mainAxisAlignment: MainAxisAlignment.end,
return SuperportDialog(
title: widget.title,
constraints: const BoxConstraints(maxWidth: 560),
primaryAction: ShadButton(
key: const ValueKey('step_form_submit'),
onPressed: _handleSubmit,
child: Text(widget.submitLabel),
),
secondaryAction: ShadButton.ghost(
onPressed: () => Navigator.of(context).pop(),
child: const Text('취소'),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ShadButton.ghost(
onPressed: () => Navigator.of(context).pop(),
child: const Text('취소'),
),
const SizedBox(width: 12),
ShadButton(
key: const ValueKey('step_form_submit'),
onPressed: _handleSubmit,
child: Text(widget.submitLabel),
),
],
),
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (!widget.isEditing)
_FormFieldBlock(
label: '결재 ID',
errorText: _errors['approvalId'],
child: ShadInput(
key: const ValueKey('step_form_approval_id'),
controller: _approvalIdController,
onChanged: (_) => _clearError('approvalId'),
),
)
else ...[
_FormFieldBlock(
label: '결재 ID',
child: ShadInput(
controller: _approvalIdController,
readOnly: true,
),
),
const SizedBox(height: 16),
_FormFieldBlock(
label: '결재번호',
child: ShadInput(
controller: _approvalNoController,
readOnly: true,
),
),
],
if (!widget.isEditing) const SizedBox(height: 16),
if (!widget.isEditing)
_FormFieldBlock(
label: '단계 순서',
errorText: _errors['stepOrder'],
label: '결재 ID',
errorText: _errors['approvalId'],
child: ShadInput(
key: const ValueKey('step_form_step_order'),
controller: _stepOrderController,
onChanged: (_) => _clearError('stepOrder'),
key: const ValueKey('step_form_approval_id'),
controller: _approvalIdController,
onChanged: (_) => _clearError('approvalId'),
),
)
else ...[
_FormFieldBlock(
label: '결재 ID',
child: ShadInput(
controller: _approvalIdController,
readOnly: true,
),
),
const SizedBox(height: 16),
_FormFieldBlock(
label: '승인자 ID',
errorText: _errors['approverId'],
label: '결재번호',
child: ShadInput(
key: const ValueKey('step_form_approver_id'),
controller: _approverIdController,
onChanged: (_) => _clearError('approverId'),
controller: _approvalNoController,
readOnly: true,
),
),
const SizedBox(height: 16),
_FormFieldBlock(
label: '비고',
helperText: '필요 시 단계에 대한 참고 내용을 남길 수 있습니다.',
child: ShadTextarea(
key: const ValueKey('step_form_note'),
controller: _noteController,
minHeight: 100,
maxHeight: 200,
),
),
if (_errors['form'] != null)
Padding(
padding: const EdgeInsets.only(top: 12),
child: Text(
_errors['form']!,
style: theme.textTheme.small.copyWith(
color: materialTheme.colorScheme.error,
),
),
),
],
),
if (!widget.isEditing) const SizedBox(height: 16),
_FormFieldBlock(
label: '단계 순서',
errorText: _errors['stepOrder'],
child: ShadInput(
key: const ValueKey('step_form_step_order'),
controller: _stepOrderController,
onChanged: (_) => _clearError('stepOrder'),
),
),
const SizedBox(height: 16),
_FormFieldBlock(
label: '승인자 ID',
errorText: _errors['approverId'],
child: ShadInput(
key: const ValueKey('step_form_approver_id'),
controller: _approverIdController,
onChanged: (_) => _clearError('approverId'),
),
),
const SizedBox(height: 16),
_FormFieldBlock(
label: '비고',
helperText: '필요 시 단계에 대한 참고 내용을 남길 수 있습니다.',
child: ShadTextarea(
key: const ValueKey('step_form_note'),
controller: _noteController,
minHeight: 100,
maxHeight: 200,
),
),
if (_errors['form'] != null)
Padding(
padding: const EdgeInsets.only(top: 12),
child: Text(
_errors['form']!,
style: theme.textTheme.small.copyWith(
color: materialTheme.colorScheme.error,
),
),
),
],
),
),
);