결재 템플릿 단계 적용 구현
- ApprovalTemplate 엔티티·DTO·원격 리포지토리 추가 - ApprovalController에 템플릿 로딩/적용 상태와 assignSteps 호출 연동 - ApprovalPage 단계 탭에 템플릿 선택 UI 및 적용 확인 다이얼로그 구현 - 템플릿 적용 단위 테스트와 IMPLEMENTATION_TASKS 현황 갱신
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import 'package:superport_v2/core/constants/app_sections.dart';
|
||||
import 'package:superport_v2/widgets/app_layout.dart';
|
||||
import 'package:superport_v2/widgets/components/filter_bar.dart';
|
||||
|
||||
class OutboundPage extends StatefulWidget {
|
||||
const OutboundPage({super.key});
|
||||
|
||||
@@ -50,95 +54,67 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
final theme = ShadTheme.of(context);
|
||||
final filtered = _filteredRecords;
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
return AppLayout(
|
||||
title: '출고 관리',
|
||||
subtitle: '출고 처리, 고객사 연결, 품목 라인을 실시간으로 확인합니다.',
|
||||
breadcrumbs: const [
|
||||
AppBreadcrumbItem(label: '대시보드', path: dashboardRoutePath),
|
||||
AppBreadcrumbItem(label: '입·출고', path: '/inventory/outbound'),
|
||||
AppBreadcrumbItem(label: '출고'),
|
||||
],
|
||||
actions: [
|
||||
ShadButton(
|
||||
leading: const Icon(LucideIcons.plus, size: 16),
|
||||
onPressed: _handleCreate,
|
||||
child: const Text('출고 등록'),
|
||||
),
|
||||
ShadButton.outline(
|
||||
leading: const Icon(LucideIcons.pencil, size: 16),
|
||||
onPressed:
|
||||
_selectedRecord == null ? null : () => _handleEdit(_selectedRecord!),
|
||||
child: const Text('선택 항목 수정'),
|
||||
),
|
||||
],
|
||||
toolbar: FilterBar(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('출고 관리', style: theme.textTheme.h2),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'출고 처리, 고객사 연결, 품목 라인을 실시간으로 확인합니다.',
|
||||
style: theme.textTheme.muted,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
SizedBox(
|
||||
width: 260,
|
||||
child: ShadInput(
|
||||
controller: _searchController,
|
||||
placeholder: const Text('트랜잭션번호, 작성자, 제품, 고객사 검색'),
|
||||
leading: const Icon(LucideIcons.search, size: 16),
|
||||
onChanged: (_) => setState(() {}),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 220,
|
||||
child: ShadButton.outline(
|
||||
onPressed: _pickDateRange,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ShadButton(
|
||||
leading: const Icon(LucideIcons.plus, size: 16),
|
||||
onPressed: _handleCreate,
|
||||
child: const Text('출고 등록'),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
ShadButton.outline(
|
||||
leading: const Icon(LucideIcons.pencil, size: 16),
|
||||
onPressed: _selectedRecord == null
|
||||
? null
|
||||
: () => _handleEdit(_selectedRecord!),
|
||||
child: const Text('선택 항목 수정'),
|
||||
const Icon(LucideIcons.calendar, size: 16),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
_dateRange == null
|
||||
? '기간 선택'
|
||||
: '${_dateFormatter.format(_dateRange!.start)} ~ ${_dateFormatter.format(_dateRange!.end)}',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ShadCard(
|
||||
title: Text('검색 필터', style: theme.textTheme.h3),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 260,
|
||||
child: ShadInput(
|
||||
controller: _searchController,
|
||||
placeholder: const Text('트랜잭션번호, 작성자, 제품, 고객사 검색'),
|
||||
leading: const Icon(LucideIcons.search, size: 16),
|
||||
onChanged: (_) => setState(() {}),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 220,
|
||||
child: ShadButton.outline(
|
||||
onPressed: _pickDateRange,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(LucideIcons.calendar, size: 16),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
_dateRange == null
|
||||
? '기간 선택'
|
||||
: '${_dateFormatter.format(_dateRange!.start)} ~ ${_dateFormatter.format(_dateRange!.end)}',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_dateRange != null)
|
||||
ShadButton.ghost(
|
||||
onPressed: () => setState(() => _dateRange = null),
|
||||
child: const Text('기간 초기화'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
if (_dateRange != null)
|
||||
ShadButton.ghost(
|
||||
onPressed: () => setState(() => _dateRange = null),
|
||||
child: const Text('기간 초기화'),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ShadCard(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
||||
Reference in New Issue
Block a user