import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:superport_v2/widgets/app_layout.dart'; import 'package:superport_v2/widgets/components/empty_state.dart'; class DashboardPage extends StatelessWidget { const DashboardPage({super.key}); static const _recentTransactions = [ ('IN-20240312-003', '2024-03-12', '입고', '승인완료', '김담당'), ('OUT-20240311-005', '2024-03-11', '출고', '출고대기', '이물류'), ('RENT-20240310-001', '2024-03-10', '대여', '대여중', '박대여'), ('APP-20240309-004', '2024-03-09', '결재', '진행중', '최결재'), ]; static const _pendingApprovals = [ ('APP-20240312-010', '설비 구매', '2/4 단계 진행 중'), ('APP-20240311-004', '창고 정기 점검', '승인 대기'), ('APP-20240309-002', '계약 연장', '반려 후 재상신'), ]; @override Widget build(BuildContext context) { return AppLayout( title: '대시보드', subtitle: '입·출·대여 현황과 결재 대기를 한 눈에 확인합니다.', breadcrumbs: const [AppBreadcrumbItem(label: '대시보드')], child: SingleChildScrollView( padding: const EdgeInsets.only(right: 12, bottom: 24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Wrap( spacing: 16, runSpacing: 16, children: const [ _KpiCard( icon: lucide.LucideIcons.packagePlus, label: '오늘 입고', value: '12건', trend: '+3 vs 어제', ), _KpiCard( icon: lucide.LucideIcons.packageMinus, label: '오늘 출고', value: '9건', trend: '-2 vs 어제', ), _KpiCard( icon: lucide.LucideIcons.messageSquareWarning, label: '결재 대기', value: '5건', trend: '평균 12시간 지연', ), _KpiCard( icon: lucide.LucideIcons.users, label: '고객사 문의', value: '7건', trend: '지원팀 확인 중', ), ], ), const SizedBox(height: 24), LayoutBuilder( builder: (context, constraints) { final showSidePanel = constraints.maxWidth > 920; return Flex( direction: showSidePanel ? Axis.horizontal : Axis.vertical, crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 3, child: _RecentTransactionsCard( transactions: _recentTransactions, ), ), if (showSidePanel) const SizedBox(width: 16) else const SizedBox(height: 16), Flexible( flex: 2, child: _PendingApprovalCard(approvals: _pendingApprovals), ), ], ); }, ), const SizedBox(height: 24), const _ReminderPanel(), ], ), ), ); } } class _KpiCard extends StatelessWidget { const _KpiCard({ required this.icon, required this.label, required this.value, required this.trend, }); final IconData icon; final String label; final String value; final String trend; @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return ConstrainedBox( constraints: const BoxConstraints(minWidth: 220, maxWidth: 260), child: ShadCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 20, color: theme.colorScheme.primary), const SizedBox(height: 12), Text(label, style: theme.textTheme.small), const SizedBox(height: 6), Text(value, style: theme.textTheme.h3), const SizedBox(height: 8), Text(trend, style: theme.textTheme.muted), ], ), ), ); } } class _RecentTransactionsCard extends StatelessWidget { const _RecentTransactionsCard({required this.transactions}); final List<(String, String, String, String, String)> transactions; @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return ShadCard( title: Text('최근 트랜잭션', style: theme.textTheme.h3), description: Text( '최근 7일간의 입·출고 및 대여/결재 흐름입니다.', style: theme.textTheme.muted, ), child: SizedBox( height: 320, child: ShadTable.list( header: const [ ShadTableCell.header(child: Text('번호')), ShadTableCell.header(child: Text('일자')), ShadTableCell.header(child: Text('유형')), ShadTableCell.header(child: Text('상태')), ShadTableCell.header(child: Text('작성자')), ], children: [ for (final row in transactions) [ ShadTableCell(child: Text(row.$1)), ShadTableCell(child: Text(row.$2)), ShadTableCell(child: Text(row.$3)), ShadTableCell(child: Text(row.$4)), ShadTableCell(child: Text(row.$5)), ], ], columnSpanExtent: (index) => const FixedTableSpanExtent(140), rowSpanExtent: (index) => const FixedTableSpanExtent(52), ), ), ); } } class _PendingApprovalCard extends StatelessWidget { const _PendingApprovalCard({required this.approvals}); final List<(String, String, String)> approvals; @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); if (approvals.isEmpty) { return ShadCard( title: Text('내 결재 대기', style: theme.textTheme.h3), description: Text( '현재 승인 대기 중인 결재 요청입니다.', style: theme.textTheme.muted, ), child: const SuperportEmptyState( title: '대기 중인 결재가 없습니다', description: '새로운 결재 요청이 등록되면 이곳에서 바로 확인할 수 있습니다.', ), ); } return ShadCard( title: Text('내 결재 대기', style: theme.textTheme.h3), description: Text('현재 승인 대기 중인 결재 요청입니다.', style: theme.textTheme.muted), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (final approval in approvals) Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon( lucide.LucideIcons.bell, size: 18, color: theme.colorScheme.primary, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(approval.$1, style: theme.textTheme.small), const SizedBox(height: 4), Text(approval.$2, style: theme.textTheme.h4), const SizedBox(height: 4), Text(approval.$3, style: theme.textTheme.muted), ], ), ), ShadButton.ghost( size: ShadButtonSize.sm, child: const Text('상세'), onPressed: () {}, ), ], ), ), ], ), ); } } class _ReminderPanel extends StatelessWidget { const _ReminderPanel(); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return ShadCard( title: Text('주의/알림', style: theme.textTheme.h3), description: Text( '지연된 결재나 시스템 점검 일정을 확인하세요.', style: theme.textTheme.muted, ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: const [ _ReminderItem( icon: lucide.LucideIcons.clock, label: '결재 지연', message: '영업부 장비 구매 결재가 2일째 대기 중입니다.', ), SizedBox(height: 12), _ReminderItem( icon: lucide.LucideIcons.triangleAlert, label: '시스템 점검', message: '2024-03-15 22:00 ~ 23:00 서버 점검이 예정되어 있습니다.', ), SizedBox(height: 12), _ReminderItem( icon: lucide.LucideIcons.mail, label: '고객 문의', message: '3건의 신규 고객 문의가 접수되었습니다.', ), ], ), ); } } class _ReminderItem extends StatelessWidget { const _ReminderItem({ required this.icon, required this.label, required this.message, }); final IconData icon; final String label; final String message; @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 18, color: theme.colorScheme.secondary), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: theme.textTheme.small), const SizedBox(height: 4), Text(message, style: theme.textTheme.p), ], ), ), ], ); } }