결재 단계 UI 보완 및 분석 통과
This commit is contained in:
@@ -513,8 +513,8 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
const SizedBox(height: 8),
|
||||
ShadTextarea(
|
||||
controller: noteController,
|
||||
minLines: 3,
|
||||
maxLines: 5,
|
||||
minHeight: 120,
|
||||
maxHeight: 220,
|
||||
),
|
||||
if (requireNote)
|
||||
Padding(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import '../../../../core/common/models/paginated_result.dart';
|
||||
import '../../../domain/entities/approval.dart';
|
||||
import '../../data/dtos/approval_dto.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/features/approvals/data/dtos/approval_dto.dart';
|
||||
import 'package:superport_v2/features/approvals/domain/entities/approval.dart';
|
||||
|
||||
import '../../domain/entities/approval_step_record.dart';
|
||||
|
||||
class ApprovalStepRecordDto {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../../../core/common/models/paginated_result.dart';
|
||||
import '../../../../core/network/api_client.dart';
|
||||
import '../../domain/entities/approval_step_record.dart';
|
||||
import '../../domain/repositories/approval_step_repository.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/network/api_client.dart';
|
||||
import 'package:superport_v2/features/approvals/step/domain/entities/approval_step_record.dart';
|
||||
import 'package:superport_v2/features/approvals/step/domain/repositories/approval_step_repository.dart';
|
||||
import '../dtos/approval_step_record_dto.dart';
|
||||
|
||||
class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
@@ -321,6 +320,9 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: ShadButton.outline(
|
||||
key: ValueKey(
|
||||
'step_detail_${step.id ?? record.approvalId}_${step.stepOrder}',
|
||||
),
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: step.id == null
|
||||
? null
|
||||
@@ -382,9 +384,7 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
|
||||
options.add(const _StatusOption(id: -1, name: '전체'));
|
||||
for (final record in records) {
|
||||
final status = record.step.status;
|
||||
if (status.id != null) {
|
||||
options.add(_StatusOption(id: status.id!, name: status.name));
|
||||
}
|
||||
options.add(_StatusOption(id: status.id, name: status.name));
|
||||
}
|
||||
return options.toList()..sort((a, b) => a.id.compareTo(b.id));
|
||||
}
|
||||
@@ -444,6 +444,15 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
|
||||
'결재번호 ${detail.approvalNo}',
|
||||
style: theme.textTheme.muted,
|
||||
),
|
||||
footer: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
child: const Text('닫기'),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Column(
|
||||
@@ -485,15 +494,6 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
footer: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
ShadButton.ghost(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
child: const Text('닫기'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
@@ -442,6 +441,7 @@ class _ApprovalTemplateEnabledPageState
|
||||
|
||||
Future<bool?> _openTemplateForm({ApprovalTemplate? template}) async {
|
||||
final isEdit = template != null;
|
||||
final existingTemplate = template;
|
||||
final codeController = TextEditingController(text: template?.code ?? '');
|
||||
final nameController = TextEditingController(text: template?.name ?? '');
|
||||
final descriptionController = TextEditingController(
|
||||
@@ -587,13 +587,15 @@ class _ApprovalTemplateEnabledPageState
|
||||
),
|
||||
actions: [
|
||||
ShadButton.ghost(
|
||||
onPressed: isSaving ? null : () => Navigator.of(context).pop(false),
|
||||
onPressed: () {
|
||||
if (isSaving) return;
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: const Text('취소'),
|
||||
),
|
||||
ShadButton(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () async {
|
||||
onPressed: () async {
|
||||
if (isSaving) return;
|
||||
final codeValue = codeController.text.trim();
|
||||
final nameValue = nameController.text.trim();
|
||||
if (!isEdit && codeValue.isEmpty) {
|
||||
@@ -614,12 +616,8 @@ class _ApprovalTemplateEnabledPageState
|
||||
.map(
|
||||
(field) => ApprovalTemplateStepInput(
|
||||
id: field.id,
|
||||
stepOrder: int.parse(
|
||||
field.orderController.text.trim(),
|
||||
),
|
||||
approverId: int.parse(
|
||||
field.approverController.text.trim(),
|
||||
),
|
||||
stepOrder: int.parse(field.orderController.text.trim()),
|
||||
approverId: int.parse(field.approverController.text.trim()),
|
||||
note: field.noteController.text.trim().isEmpty
|
||||
? null
|
||||
: field.noteController.text.trim(),
|
||||
@@ -627,7 +625,7 @@ class _ApprovalTemplateEnabledPageState
|
||||
)
|
||||
.toList();
|
||||
final input = ApprovalTemplateInput(
|
||||
code: isEdit ? template!.code : codeValue,
|
||||
code: isEdit ? existingTemplate?.code : codeValue,
|
||||
name: nameValue,
|
||||
description: descriptionController.text.trim().isEmpty
|
||||
? null
|
||||
@@ -637,10 +635,17 @@ class _ApprovalTemplateEnabledPageState
|
||||
: noteController.text.trim(),
|
||||
isActive: statusNotifier.value,
|
||||
);
|
||||
if (isEdit && existingTemplate == null) {
|
||||
modalSetState?.call(() => errorText = '템플릿 정보를 불러오지 못했습니다.');
|
||||
modalSetState?.call(() => isSaving = false);
|
||||
return;
|
||||
}
|
||||
|
||||
modalSetState?.call(() => isSaving = true);
|
||||
final success = isEdit
|
||||
|
||||
final success = isEdit && existingTemplate != null
|
||||
? await _controller.update(
|
||||
template!.id,
|
||||
existingTemplate.id,
|
||||
input,
|
||||
stepInputs,
|
||||
)
|
||||
@@ -651,13 +656,7 @@ class _ApprovalTemplateEnabledPageState
|
||||
modalSetState?.call(() => isSaving = false);
|
||||
}
|
||||
},
|
||||
child: isSaving
|
||||
? const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: Text(isEdit ? '수정 완료' : '생성 완료'),
|
||||
child: Text(isEdit ? '수정 완료' : '생성 완료'),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -780,7 +779,9 @@ class _StepEditorRow extends StatelessWidget {
|
||||
margin: const EdgeInsets.symmetric(vertical: 6),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: theme.colorScheme.border.withOpacity(0.6)),
|
||||
border: Border.all(
|
||||
color: theme.colorScheme.border.withValues(alpha: 0.6),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
|
||||
@@ -4,7 +4,6 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:two_dimensional_scrollables/two_dimensional_scrollables.dart';
|
||||
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/features/approvals/domain/entities/approval.dart';
|
||||
@@ -95,14 +94,9 @@ void main() {
|
||||
expect(find.text('APP-2024-0001'), findsOneWidget);
|
||||
expect(find.text('최승인'), findsOneWidget);
|
||||
|
||||
await tester.dragUntilVisible(
|
||||
find.widgetWithText(ShadButton, '상세'),
|
||||
find.byType(TwoDimensionalScrollable),
|
||||
const Offset(-200, 0),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.widgetWithText(ShadButton, '상세').first);
|
||||
final detailButtonFinder = find.byKey(const ValueKey('step_detail_501_1'));
|
||||
final detailButton = tester.widget<ShadButton>(detailButtonFinder);
|
||||
detailButton.onPressed?.call();
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
ApprovalTemplate _template({bool isActive = true}) {
|
||||
ApprovalTemplate buildTemplate({bool isActive = true}) {
|
||||
return ApprovalTemplate(
|
||||
id: 10,
|
||||
code: 'AP_INBOUND',
|
||||
@@ -101,7 +101,7 @@ void main() {
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<ApprovalTemplate>(
|
||||
items: [_template()],
|
||||
items: [buildTemplate()],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
@@ -140,7 +140,7 @@ void main() {
|
||||
|
||||
when(
|
||||
() => repository.create(any(), steps: any(named: 'steps')),
|
||||
).thenAnswer((_) async => _template());
|
||||
).thenAnswer((_) async => buildTemplate());
|
||||
|
||||
await tester.pumpWidget(_buildApp(const ApprovalTemplatePage()));
|
||||
await tester.pump();
|
||||
@@ -192,7 +192,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('수정 플로우에서 fetchDetail 후 update를 호출한다', (tester) async {
|
||||
final activeTemplate = _template();
|
||||
final activeTemplate = buildTemplate();
|
||||
|
||||
when(
|
||||
() => repository.list(
|
||||
|
||||
Reference in New Issue
Block a user