import 'package:flutter/material.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:superport/data/models/vendor_dto.dart'; import 'package:superport/core/constants/app_constants.dart'; class VendorTable extends StatelessWidget { final List vendors; final int currentPage; final int totalPages; final Function(int) onPageChanged; final Function(int) onEdit; final Function(int, String) onDelete; final Function(int) onRestore; const VendorTable({ super.key, required this.vendors, required this.currentPage, required this.totalPages, required this.onPageChanged, required this.onEdit, required this.onDelete, required this.onRestore, }); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return Column( children: [ Expanded( child: ShadCard( child: LayoutBuilder( builder: (context, constraints) { // 최소폭 추정: No(64) + 벤더명(240) + 등록일(140) + 상태(120) + 작업(200) + 여백(24) const double actionsW = 256.0; // 아이콘 2개 + 내부 패딩 여유(단일 행 유지) const double minW = 64 + 240 + 140 + 120 + actionsW + 24; final double extra = constraints.maxWidth - minW; final double tableW = extra >= 0 ? constraints.maxWidth : minW; const double nameBase = 240.0; final double nameW = nameBase; // 넓은 화면에서도 벤더명은 고정 폭, 남는 폭은 빈 컬럼이 흡수 // 스크롤이 필요 없으면 전체 폭 사용 if (constraints.maxWidth >= minW) { return SizedBox( width: constraints.maxWidth, child: ShadTable.list( columnSpanExtent: (index) { switch (index) { case 0: return const FixedTableSpanExtent(64); case 1: return const FixedTableSpanExtent(nameBase); case 2: return const FixedTableSpanExtent(120); case 3: return const FixedTableSpanExtent(100); case 4: return const FixedTableSpanExtent(actionsW); case 5: return const RemainingTableSpanExtent(); default: return const FixedTableSpanExtent(100); } }, header: [ const ShadTableCell.header(child: Text('No')), ShadTableCell.header(child: SizedBox(width: nameW, child: const Text('벤더명'))), const ShadTableCell.header(child: Text('등록일')), const ShadTableCell.header(child: Text('상태')), ShadTableCell.header(child: SizedBox(width: actionsW, child: const Text('작업'))), const ShadTableCell.header(child: SizedBox.shrink()), ], children: vendors.asMap().entries.map((entry) { final index = entry.key; final vendor = entry.value; final rowNumber = (currentPage - 1) * AppConstants.vendorPageSize + index + 1; return [ ShadTableCell(child: Text(rowNumber.toString(), style: const TextStyle(color: Colors.grey))), ShadTableCell( child: SizedBox( width: nameW, child: Text(vendor.name, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w500)), ), ), ShadTableCell( child: SizedBox( width: 120, child: Text( vendor.createdAt != null ? vendor.createdAt!.toLocal().toString().split(' ')[0] : '-', maxLines: 1, softWrap: false, overflow: TextOverflow.clip, ), ), ), ShadTableCell( child: SizedBox( width: 100, child: ShadBadge( backgroundColor: vendor.isActive ? const Color(0xFF22C55E).withValues(alpha: 0.1) : Colors.grey.withValues(alpha: 0.1), foregroundColor: vendor.isActive ? const Color(0xFF16A34A) : Colors.grey.shade700, child: Text(vendor.isActive ? '활성' : '비활성'), ), ), ), ShadTableCell( child: SizedBox( width: actionsW, child: FittedBox( alignment: Alignment.centerLeft, fit: BoxFit.scaleDown, child: Row( mainAxisSize: MainAxisSize.min, children: [ if (vendor.id != null) ...[ if (vendor.isActive) ...[ ShadButton.ghost( onPressed: () => onEdit(vendor.id!), size: ShadButtonSize.sm, child: const Icon(Icons.edit, size: 16), ), const SizedBox(width: 4), ShadButton.ghost( onPressed: () => onDelete(vendor.id!, vendor.name), size: ShadButtonSize.sm, child: Icon(Icons.delete, size: 16, color: theme.colorScheme.destructive), ), ] else ShadButton.ghost( onPressed: () => onRestore(vendor.id!), size: ShadButtonSize.sm, child: const Icon(Icons.restore, size: 16, color: Color(0xFF10B981)), ), ], ], ), ), ), ), const ShadTableCell(child: SizedBox.shrink()), ]; }).toList(), ), ); } // 스크롤 필요한 경우만 수평 스크롤 허용 return SingleChildScrollView( scrollDirection: Axis.horizontal, child: SizedBox( width: tableW, child: ShadTable.list( columnSpanExtent: (index) { switch (index) { case 0: return const FixedTableSpanExtent(64); case 1: return const FixedTableSpanExtent(nameBase); case 2: return const FixedTableSpanExtent(120); case 3: return const FixedTableSpanExtent(100); case 4: return const FixedTableSpanExtent(actionsW); default: return const FixedTableSpanExtent(100); } }, header: [ const ShadTableCell.header(child: Text('No')), ShadTableCell.header(child: SizedBox(width: nameW, child: const Text('벤더명'))), ShadTableCell.header(child: SizedBox(width: 120, child: const Text('등록일'))), ShadTableCell.header(child: SizedBox(width: 100, child: const Text('상태'))), ShadTableCell.header(child: SizedBox(width: actionsW, child: const Text('작업'))), ], children: vendors.asMap().entries.map((entry) { final index = entry.key; final vendor = entry.value; final rowNumber = (currentPage - 1) * AppConstants.vendorPageSize + index + 1; return [ ShadTableCell(child: Text(rowNumber.toString(), style: const TextStyle(color: Colors.grey))), ShadTableCell( child: SizedBox( width: nameW, child: Text(vendor.name, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w500)), ), ), ShadTableCell( child: SizedBox( width: 120, child: Text( vendor.createdAt != null ? vendor.createdAt!.toLocal().toString().split(' ')[0] : '-', maxLines: 1, softWrap: false, overflow: TextOverflow.clip, ), ), ), ShadTableCell( child: ShadBadge( backgroundColor: vendor.isActive ? const Color(0xFF22C55E).withValues(alpha: 0.1) : Colors.grey.withValues(alpha: 0.1), foregroundColor: vendor.isActive ? const Color(0xFF16A34A) : Colors.grey.shade700, child: Text(vendor.isActive ? '활성' : '비활성'), ), ), ShadTableCell( child: SizedBox( width: actionsW, child: Wrap( alignment: WrapAlignment.start, spacing: 4, runSpacing: 4, children: [ if (vendor.id != null) ...[ if (vendor.isActive) ...[ ShadButton.ghost( onPressed: () => onEdit(vendor.id!), size: ShadButtonSize.sm, child: const Icon(Icons.edit, size: 16), ), ShadButton.ghost( onPressed: () => onDelete(vendor.id!, vendor.name), size: ShadButtonSize.sm, child: Icon(Icons.delete, size: 16, color: theme.colorScheme.destructive), ), ] else ShadButton.ghost( onPressed: () => onRestore(vendor.id!), size: ShadButtonSize.sm, child: const Icon(Icons.restore, size: 16, color: Color(0xFF10B981)), ), ], ], ), ), ), ]; }).toList(), ), ), ); }, ), ), ), // 페이지네이션 if (totalPages > 1) Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( border: Border( top: BorderSide( color: theme.colorScheme.border, width: 1, ), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ShadButton.ghost( onPressed: currentPage > 1 ? () => onPageChanged(currentPage - 1) : null, size: ShadButtonSize.sm, child: const Icon(Icons.chevron_left, size: 16), ), const SizedBox(width: 8), ...List.generate( totalPages > 5 ? 5 : totalPages, (index) { int pageNumber; if (totalPages <= 5) { pageNumber = index + 1; } else if (currentPage <= 3) { pageNumber = index + 1; } else if (currentPage >= totalPages - 2) { pageNumber = totalPages - 4 + index; } else { pageNumber = currentPage - 2 + index; } if (pageNumber > totalPages) return const SizedBox.shrink(); return Padding( padding: const EdgeInsets.symmetric(horizontal: 2), child: pageNumber == currentPage ? ShadButton( onPressed: () => onPageChanged(pageNumber), size: ShadButtonSize.sm, child: Text(pageNumber.toString()), ) : ShadButton.outline( onPressed: () => onPageChanged(pageNumber), size: ShadButtonSize.sm, child: Text(pageNumber.toString()), ), ); }, ), const SizedBox(width: 8), ShadButton.ghost( onPressed: currentPage < totalPages ? () => onPageChanged(currentPage + 1) : null, size: ShadButtonSize.sm, child: const Icon(Icons.chevron_right, size: 16), ), const SizedBox(width: 24), Text( '페이지 $currentPage / $totalPages', style: theme.textTheme.muted, ), ], ), ), ], ); } }