import 'package:flutter/material.dart'; import 'package:superport/models/license_model.dart'; import 'package:superport/screens/common/theme_shadcn.dart'; import 'package:superport/screens/common/components/shadcn_components.dart'; import 'package:superport/screens/license/controllers/license_list_controller.dart'; import 'package:superport/utils/constants.dart'; import 'package:superport/services/mock_data_service.dart'; /// shadcn/ui 스타일로 재설계된 유지보수 관리 화면 class LicenseListRedesign extends StatefulWidget { const LicenseListRedesign({super.key}); @override State createState() => _LicenseListRedesignState(); } class _LicenseListRedesignState extends State { late final LicenseListController _controller; final MockDataService _dataService = MockDataService(); int _currentPage = 1; final int _pageSize = 10; @override void initState() { super.initState(); _controller = LicenseListController(dataService: _dataService); _controller.loadData(); } /// 라이선스 목록 로드 void _loadLicenses() { setState(() { _controller.loadData(); }); } /// 회사명 반환 함수 String _getCompanyName(int companyId) { return _dataService.getCompanyById(companyId)?.name ?? '-'; } /// 라이선스 상태 표시 배지 (문자열 기반) Widget _buildStatusBadge(String status) { switch (status.toLowerCase()) { case 'active': case '활성': return ShadcnBadge( text: '활성', variant: ShadcnBadgeVariant.success, size: ShadcnBadgeSize.small, ); case 'expired': case '만료': return ShadcnBadge( text: '만료', variant: ShadcnBadgeVariant.destructive, size: ShadcnBadgeSize.small, ); case 'expiring': case '만료예정': return ShadcnBadge( text: '만료 예정', variant: ShadcnBadgeVariant.warning, size: ShadcnBadgeSize.small, ); default: return ShadcnBadge( text: '알수없음', variant: ShadcnBadgeVariant.secondary, size: ShadcnBadgeSize.small, ); } } /// 라이선스 추가 폼으로 이동 void _navigateToAdd() async { final result = await Navigator.pushNamed(context, Routes.licenseAdd); if (result == true) { _loadLicenses(); } } /// 라이선스 수정 폼으로 이동 void _navigateToEdit(int licenseId) async { final result = await Navigator.pushNamed( context, Routes.licenseEdit, arguments: licenseId, ); if (result == true) { _loadLicenses(); } } /// 라이선스 삭제 다이얼로그 void _showDeleteDialog(int licenseId) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('라이선스 삭제'), content: const Text('정말로 삭제하시겠습니까?'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('취소'), ), TextButton( onPressed: () { setState(() { _controller.deleteLicense(licenseId); }); Navigator.of(context).pop(); }, child: const Text('삭제'), ), ], ), ); } @override Widget build(BuildContext context) { final int totalCount = _controller.licenses.length; final int startIndex = (_currentPage - 1) * _pageSize; final int endIndex = (startIndex + _pageSize) > totalCount ? totalCount : (startIndex + _pageSize); final List pagedLicenses = _controller.licenses.sublist( startIndex, endIndex, ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 헤더 액션 바 Padding( padding: const EdgeInsets.all(ShadcnTheme.spacing6), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('총 $totalCount개 라이선스', style: ShadcnTheme.bodyMuted), Row( children: [ ShadcnButton( text: '새로고침', onPressed: _loadLicenses, variant: ShadcnButtonVariant.secondary, icon: Icon(Icons.refresh), ), const SizedBox(width: ShadcnTheme.spacing2), ShadcnButton( text: '라이선스 추가', onPressed: _navigateToAdd, variant: ShadcnButtonVariant.primary, textColor: Colors.white, icon: Icon(Icons.add), ), ], ), ], ), ), // 테이블 컨테이너 Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: ShadcnTheme.spacing6), child: Container( width: double.infinity, decoration: BoxDecoration( border: Border.all(color: ShadcnTheme.border), borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 테이블 헤더 Container( padding: const EdgeInsets.symmetric( horizontal: ShadcnTheme.spacing4, vertical: ShadcnTheme.spacing3, ), decoration: BoxDecoration( color: ShadcnTheme.muted.withValues(alpha: 0.3), border: Border( bottom: BorderSide(color: ShadcnTheme.border), ), ), child: Row( children: [ Expanded( flex: 1, child: Text('번호', style: ShadcnTheme.bodyMedium), ), Expanded( flex: 3, child: Text('라이선스명', style: ShadcnTheme.bodyMedium), ), Expanded( flex: 2, child: Text('종류', style: ShadcnTheme.bodyMedium), ), Expanded( flex: 2, child: Text('상태', style: ShadcnTheme.bodyMedium), ), Expanded( flex: 2, child: Text('회사명', style: ShadcnTheme.bodyMedium), ), Expanded( flex: 2, child: Text('등록일', style: ShadcnTheme.bodyMedium), ), Expanded( flex: 1, child: Text('관리', style: ShadcnTheme.bodyMedium), ), ], ), ), // 테이블 데이터 (스크롤 가능) Expanded( child: pagedLicenses.isEmpty ? Container( padding: const EdgeInsets.all(ShadcnTheme.spacing8), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.description_outlined, size: 48, color: ShadcnTheme.mutedForeground, ), const SizedBox(height: ShadcnTheme.spacing4), Text( '등록된 라이선스가 없습니다.', style: ShadcnTheme.bodyMuted, ), ], ), ), ) : SingleChildScrollView( child: Column( children: pagedLicenses.asMap().entries.map((entry) { final int index = entry.key; final License license = entry.value; return Container( padding: const EdgeInsets.symmetric( horizontal: ShadcnTheme.spacing4, vertical: ShadcnTheme.spacing3, ), decoration: BoxDecoration( border: Border( bottom: BorderSide(color: ShadcnTheme.border), ), ), child: Row( children: [ // 번호 Expanded( flex: 1, child: Text( '${startIndex + index + 1}', style: ShadcnTheme.bodySmall, ), ), // 라이선스명 Expanded( flex: 3, child: Text( license.name, style: ShadcnTheme.bodyMedium, ), ), // 종류 (기본값 사용) Expanded( flex: 2, child: Text( '소프트웨어', style: ShadcnTheme.bodySmall, ), ), // 상태 (기본값 활성으로 설정) Expanded(flex: 2, child: _buildStatusBadge('활성')), // 회사명 Expanded( flex: 2, child: Text( _getCompanyName(license.companyId), style: ShadcnTheme.bodySmall, ), ), // 등록일 (기본값 사용) Expanded( flex: 2, child: Text( '2024-01-01', style: ShadcnTheme.bodySmall, ), ), // 관리 Expanded( flex: 1, child: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: Icon( Icons.edit, size: 16, color: ShadcnTheme.primary, ), onPressed: license.id != null ? () => _navigateToEdit(license.id!) : null, tooltip: '수정', ), IconButton( icon: Icon( Icons.delete, size: 16, color: ShadcnTheme.destructive, ), onPressed: license.id != null ? () => _showDeleteDialog(license.id!) : null, tooltip: '삭제', ), ], ), ), ], ), ); }).toList(), ), ), ), ], ), ), ), ), // 페이지네이션 if (totalCount > _pageSize) Padding( padding: const EdgeInsets.all(ShadcnTheme.spacing6), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ShadcnButton( text: '이전', onPressed: _currentPage > 1 ? () { setState(() { _currentPage--; }); } : null, variant: ShadcnButtonVariant.secondary, size: ShadcnButtonSize.small, ), const SizedBox(width: ShadcnTheme.spacing2), Text( '$_currentPage / ${(totalCount / _pageSize).ceil()}', style: ShadcnTheme.bodyMuted, ), const SizedBox(width: ShadcnTheme.spacing2), ShadcnButton( text: '다음', onPressed: _currentPage < (totalCount / _pageSize).ceil() ? () { setState(() { _currentPage++; }); } : null, variant: ShadcnButtonVariant.secondary, size: ShadcnButtonSize.small, ), ], ), ), ], ); } }