import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:superport/data/models/zipcode_dto.dart'; class ZipcodeTable extends StatelessWidget { final List zipcodes; final int currentPage; final int totalPages; final Function(int) onPageChanged; final Function(ZipcodeDto) onSelect; const ZipcodeTable({ super.key, required this.zipcodes, required this.currentPage, required this.totalPages, required this.onPageChanged, required this.onSelect, }); void _copyToClipboard(BuildContext context, String text, String label) { Clipboard.setData(ClipboardData(text: text)); ShadToaster.of(context).show( ShadToast( title: Text('$label 복사됨'), description: Text(text), duration: const Duration(seconds: 2), ), ); } @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return Column( children: [ Expanded( child: ShadCard( child: SingleChildScrollView( child: DataTable( horizontalMargin: 16, columnSpacing: 24, columns: const [ DataColumn( label: Text( '우편번호', style: TextStyle(fontWeight: FontWeight.w600), ), ), DataColumn( label: Text( '시도', style: TextStyle(fontWeight: FontWeight.w600), ), ), DataColumn( label: Text( '구/군', style: TextStyle(fontWeight: FontWeight.w600), ), ), DataColumn( label: Text( '상세주소', style: TextStyle(fontWeight: FontWeight.w600), ), ), DataColumn( label: Text( '작업', style: TextStyle(fontWeight: FontWeight.w600), ), ), ], rows: zipcodes.map((zipcode) { return DataRow( cells: [ // 우편번호 DataCell( Row( children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: theme.colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(6), ), child: Text( zipcode.zipcode.toString().padLeft(5, '0'), style: TextStyle( fontWeight: FontWeight.w600, color: theme.colorScheme.primary, fontFamily: 'monospace', ), ), ), const SizedBox(width: 8), ShadButton.ghost( onPressed: () => _copyToClipboard( context, zipcode.zipcode.toString().padLeft(5, '0'), '우편번호' ), size: ShadButtonSize.sm, child: Icon( Icons.copy, size: 14, color: theme.colorScheme.mutedForeground, ), ), ], ), ), // 시도 DataCell( Row( children: [ Icon( Icons.location_city, size: 16, color: theme.colorScheme.mutedForeground, ), const SizedBox(width: 6), Flexible( child: Text( zipcode.sido, style: const TextStyle(fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis, ), ), ], ), ), // 구/군 DataCell( Row( children: [ Icon( Icons.location_on, size: 16, color: theme.colorScheme.mutedForeground, ), const SizedBox(width: 6), Flexible( child: Text( zipcode.gu, style: const TextStyle(fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis, ), ), ], ), ), // 상세주소 DataCell( ConstrainedBox( constraints: const BoxConstraints(maxWidth: 300), child: Row( children: [ Expanded( child: Text( zipcode.etc, overflow: TextOverflow.ellipsis, style: TextStyle( color: theme.colorScheme.foreground, ), ), ), const SizedBox(width: 8), ShadButton.ghost( onPressed: () => _copyToClipboard( context, zipcode.fullAddress, '전체주소' ), size: ShadButtonSize.sm, child: Icon( Icons.copy, size: 14, color: theme.colorScheme.mutedForeground, ), ), ], ), ), ), // 작업 DataCell( ShadButton( onPressed: () => onSelect(zipcode), size: ShadButtonSize.sm, child: const Text('선택', style: TextStyle(fontSize: 11)), ), ), ], ); }).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 > 7 ? 7 : totalPages, (index) { int pageNumber; if (totalPages <= 7) { pageNumber = index + 1; } else if (currentPage <= 4) { pageNumber = index + 1; } else if (currentPage >= totalPages - 3) { pageNumber = totalPages - 6 + index; } else { pageNumber = currentPage - 3 + 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, ), ], ), ), ], ); } }