환경 초기화 및 벤더 리포지토리 스켈레톤 도입
This commit is contained in:
175
lib/features/masters/vendor/presentation/pages/vendor_page.dart
vendored
Normal file
175
lib/features/masters/vendor/presentation/pages/vendor_page.dart
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import '../../../../../core/config/environment.dart';
|
||||
import '../../../../../widgets/spec_page.dart';
|
||||
import '../../../vendor/domain/entities/vendor.dart';
|
||||
import '../../../vendor/domain/repositories/vendor_repository.dart';
|
||||
|
||||
class VendorPage extends StatelessWidget {
|
||||
const VendorPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final enabled = Environment.flag('FEATURE_VENDORS_ENABLED');
|
||||
if (!enabled) {
|
||||
return SpecPage(
|
||||
title: '제조사(벤더) 관리',
|
||||
summary: '벤더 기본 정보를 등록하고 사용여부를 제어합니다.',
|
||||
trailing: ShadBadge(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(LucideIcons.info, size: 14),
|
||||
const SizedBox(width: 6),
|
||||
Text('비활성화 (백엔드 준비 중)'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
sections: const [
|
||||
SpecSection(
|
||||
title: '입력 폼',
|
||||
items: ['벤더코드 [Text]', '벤더명 [Text]', '사용여부 [Switch]', '비고 [Text]'],
|
||||
),
|
||||
SpecSection(
|
||||
title: '수정 폼',
|
||||
items: ['벤더코드 [ReadOnly]', '생성일시 [ReadOnly]', '수정일시 [ReadOnly]'],
|
||||
),
|
||||
SpecSection(
|
||||
title: '테이블 리스트',
|
||||
description: '1행 예시',
|
||||
table: SpecTable(
|
||||
columns: ['번호', '벤더코드', '벤더명', '사용여부', '비고', '변경일시'],
|
||||
rows: [
|
||||
['1', 'V-001', '슈퍼벤더', 'Y', '-', '2024-03-01 10:00'],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return const _VendorEnabledPage();
|
||||
}
|
||||
}
|
||||
|
||||
class _VendorEnabledPage extends StatefulWidget {
|
||||
const _VendorEnabledPage();
|
||||
|
||||
@override
|
||||
State<_VendorEnabledPage> createState() => _VendorEnabledPageState();
|
||||
}
|
||||
|
||||
class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
final _repo = GetIt.I<VendorRepository>();
|
||||
final _loading = ValueNotifier(false);
|
||||
final _vendors = ValueNotifier<List<Vendor>>([]);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_loading.dispose();
|
||||
_vendors.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
_loading.value = true;
|
||||
try {
|
||||
final list = await _repo.list(page: 1, pageSize: 50);
|
||||
_vendors.value = list;
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('벤더 조회 실패: $e')),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
_loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ShadTheme.of(context);
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('제조사(벤더) 관리', style: theme.textTheme.h2),
|
||||
const SizedBox(height: 6),
|
||||
Text('벤더코드, 명칭, 사용여부 관리', style: theme.textTheme.muted),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ValueListenableBuilder<bool>(
|
||||
valueListenable: _loading,
|
||||
builder: (_, loading, __) {
|
||||
return ShadButton(
|
||||
onPressed: loading ? null : _load,
|
||||
child: Text(loading ? '로딩 중...' : '데이터 조회'),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ShadCard(
|
||||
title: Text('벤더 목록', style: theme.textTheme.h3),
|
||||
child: ValueListenableBuilder<List<Vendor>>(
|
||||
valueListenable: _vendors,
|
||||
builder: (_, vendors, __) {
|
||||
if (vendors.isEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Text('데이터가 없습니다. 상단의 "데이터 조회"를 눌러주세요.',
|
||||
style: theme.textTheme.muted),
|
||||
);
|
||||
}
|
||||
return SizedBox(
|
||||
height: 56.0 * (vendors.length + 1),
|
||||
child: ShadTable.list(
|
||||
header: const [
|
||||
'ID',
|
||||
'벤더코드',
|
||||
'벤더명',
|
||||
'사용',
|
||||
'비고',
|
||||
'변경일시',
|
||||
].map((h) => ShadTableCell.header(child: Text(h))).toList(),
|
||||
children: vendors
|
||||
.map(
|
||||
(v) => [
|
||||
'${v.id ?? '-'}',
|
||||
v.vendorCode,
|
||||
v.vendorName,
|
||||
v.isActive ? 'Y' : 'N',
|
||||
v.note ?? '-',
|
||||
v.updatedAt?.toIso8601String() ?? '-',
|
||||
].map((c) => ShadTableCell(child: Text(c))).toList(),
|
||||
)
|
||||
.toList(),
|
||||
columnSpanExtent: (index) => const FixedTableSpanExtent(160),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user