import 'package:flutter/material.dart'; import 'package:superport/screens/common/theme_shadcn.dart'; /// 표준 데이터 테이블 컬럼 정의 class DataColumn { final String label; final double? width; final int? flex; final bool isNumeric; final TextAlign textAlign; DataColumn({ required this.label, this.width, this.flex, this.isNumeric = false, TextAlign? textAlign, }) : textAlign = textAlign ?? (isNumeric ? TextAlign.right : TextAlign.left); } /// 표준 데이터 테이블 위젯 /// /// 모든 리스트 화면에서 일관된 테이블 스타일 제공 class StandardDataTable extends StatelessWidget { final List columns; final List rows; final bool showCheckbox; final bool? isAllSelected; final ValueChanged? onSelectAll; final bool enableHorizontalScroll; final ScrollController? horizontalScrollController; final Widget? emptyWidget; final bool applyZebraStripes; // 짝수 행 배경색 적용 여부 const StandardDataTable({ Key? key, required this.columns, required this.rows, this.showCheckbox = false, this.isAllSelected, this.onSelectAll, this.enableHorizontalScroll = false, this.horizontalScrollController, this.emptyWidget, this.applyZebraStripes = true, }) : super(key: key); @override Widget build(BuildContext context) { if (rows.isEmpty) { return _buildEmptyState(); } final table = Container( width: double.infinity, decoration: BoxDecoration( color: ShadcnTheme.card, borderRadius: BorderRadius.circular(ShadcnTheme.radiusLg), border: Border.all(color: Colors.black), boxShadow: ShadcnTheme.cardShadow, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 테이블 헤더 _buildHeader(), // 테이블 데이터 행들 ...rows, ], ), ); if (enableHorizontalScroll) { return SingleChildScrollView( scrollDirection: Axis.horizontal, controller: horizontalScrollController, child: table, ); } return table; } Widget _buildHeader() { return Container( padding: const EdgeInsets.symmetric( horizontal: ShadcnTheme.spacing4, vertical: 10, ), decoration: BoxDecoration( color: ShadcnTheme.muted.withValues(alpha: 0.3), border: Border( bottom: BorderSide(color: Colors.black), ), ), child: Row( children: [ // 체크박스 컬럼 if (showCheckbox) SizedBox( width: 40, child: Checkbox( value: isAllSelected, onChanged: onSelectAll, tristate: false, ), ), // 데이터 컬럼들 ...columns.map((column) { Widget child = Text( column.label, style: ShadcnTheme.bodyMedium.copyWith( fontWeight: FontWeight.bold, ), textAlign: column.textAlign, ); if (column.width != null) { return SizedBox( width: column.width, child: child, ); } else if (column.flex != null) { return Expanded( flex: column.flex!, child: child, ); } else { return Expanded(child: child); } }), ], ), ); } Widget _buildEmptyState() { return Container( width: double.infinity, padding: const EdgeInsets.all(ShadcnTheme.spacing8), decoration: BoxDecoration( color: ShadcnTheme.card, borderRadius: BorderRadius.circular(ShadcnTheme.radiusLg), border: Border.all(color: Colors.black), boxShadow: ShadcnTheme.cardShadow, ), child: emptyWidget ?? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.inbox_outlined, size: 48, color: ShadcnTheme.muted, ), const SizedBox(height: ShadcnTheme.spacing4), Text( '데이터가 없습니다', style: ShadcnTheme.bodyMuted, ), ], ), ), ); } } /// 표준 데이터 행 위젯 class StandardDataRow extends StatelessWidget { final int index; final List cells; final bool showCheckbox; final bool? isSelected; final ValueChanged? onSelect; final bool applyZebraStripes; final List columns; const StandardDataRow({ Key? key, required this.index, required this.cells, this.showCheckbox = false, this.isSelected, this.onSelect, this.applyZebraStripes = true, required this.columns, }) : super(key: key); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric( horizontal: ShadcnTheme.spacing4, vertical: 4, ), decoration: BoxDecoration( color: applyZebraStripes && index % 2 == 1 ? ShadcnTheme.muted.withValues(alpha: 0.1) : null, border: Border( bottom: BorderSide(color: Colors.black), ), ), child: Row( children: [ // 체크박스 if (showCheckbox) SizedBox( width: 40, child: Checkbox( value: isSelected, onChanged: onSelect, tristate: false, ), ), // 데이터 셀들 ...cells.asMap().entries.map((entry) { final cellIndex = entry.key; final cell = entry.value; if (cellIndex >= columns.length) return const SizedBox.shrink(); final column = columns[cellIndex]; if (column.width != null) { return SizedBox( width: column.width, child: cell, ); } else if (column.flex != null) { return Expanded( flex: column.flex!, child: cell, ); } else { return Expanded(child: cell); } }), ], ), ); } } /// 표준 관리 버튼 세트 class StandardActionButtons extends StatelessWidget { final VoidCallback? onView; final VoidCallback? onEdit; final VoidCallback? onDelete; final List? customButtons; final double buttonSize; const StandardActionButtons({ Key? key, this.onView, this.onEdit, this.onDelete, this.customButtons, this.buttonSize = 32, }) : super(key: key); @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ if (onView != null) _buildIconButton( Icons.visibility_outlined, onView!, '보기', ShadcnTheme.primary, ), if (onEdit != null) _buildIconButton( Icons.edit_outlined, onEdit!, '수정', ShadcnTheme.primary, ), if (onDelete != null) _buildIconButton( Icons.delete_outline, onDelete!, '삭제', ShadcnTheme.destructive, ), if (customButtons != null) ...customButtons!, ], ); } Widget _buildIconButton( IconData icon, VoidCallback onPressed, String tooltip, Color color, ) { return IconButton( constraints: BoxConstraints( minWidth: buttonSize, minHeight: buttonSize, maxWidth: buttonSize, maxHeight: buttonSize, ), padding: const EdgeInsets.all(4), icon: Icon(icon, size: 16, color: color), onPressed: onPressed, tooltip: tooltip, ); } }