import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:superport/data/models/model_dto.dart'; import 'package:superport/screens/model/controllers/model_controller.dart'; import 'package:superport/screens/model/model_form_dialog.dart'; import 'package:superport/injection_container.dart' as di; class ModelListScreen extends StatefulWidget { const ModelListScreen({super.key}); @override State createState() => _ModelListScreenState(); } class _ModelListScreenState extends State { late final ModelController _controller; @override void initState() { super.initState(); _controller = di.sl(); WidgetsBinding.instance.addPostFrameCallback((_) { _controller.loadInitialData(); }); } @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: _controller, child: Consumer( builder: (context, controller, _) { return ShadCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(), const SizedBox(height: 16), _buildFilters(), const SizedBox(height: 16), if (controller.errorMessage != null) ...[ ShadAlert( icon: const Icon(Icons.error), title: const Text('오류'), description: Text(controller.errorMessage!), ), const SizedBox(height: 16), ], Expanded( child: controller.isLoading ? const Center(child: CircularProgressIndicator()) : _buildModelTable(), ), ], ), ); }, ), ); } Widget _buildHeader() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '모델 관리', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), ShadButton( onPressed: () => _showCreateDialog(), child: const Row( children: [ Icon(Icons.add, size: 16), SizedBox(width: 8), Text('새 모델 등록'), ], ), ), ], ); } Widget _buildFilters() { return Consumer( builder: (context, controller, _) { return Row( children: [ Expanded( flex: 2, child: ShadInput( placeholder: const Text('모델명 검색...'), onChanged: controller.setSearchQuery, ), ), const SizedBox(width: 16), Expanded( child: ShadSelect( placeholder: const Text('제조사 선택'), options: [ const ShadOption( value: null, child: Text('전체'), ), ...controller.vendors.map( (vendor) => ShadOption( value: vendor.id, child: Text(vendor.name), ), ), ], selectedOptionBuilder: (context, value) { if (value == null) { return const Text('전체'); } final vendor = controller.vendors.firstWhere((v) => v.id == value); return Text(vendor.name); }, onChanged: controller.setVendorFilter, ), ), const SizedBox(width: 16), ShadButton.outline( onPressed: controller.refreshModels, child: const Icon(Icons.refresh), ), ], ); }, ); } Widget _buildModelTable() { return Consumer( builder: (context, controller, _) { if (controller.models.isEmpty) { return const Center( child: Text('등록된 모델이 없습니다.'), ); } return SizedBox( width: double.infinity, height: 500, // 명시적 높이 제공 child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: SizedBox( width: 1200, // 고정된 너비 제공 child: ShadTable( builder: (context, tableVicinity) { final row = tableVicinity.row; final column = tableVicinity.column; // Header if (row == 0) { const headers = ['ID', '제조사', '모델명', '설명', '상태', '작업']; return ShadTableCell( child: Container( padding: const EdgeInsets.all(12), color: Colors.grey.shade100, child: Text( headers[column], style: const TextStyle(fontWeight: FontWeight.bold), ), ), ); } // Data rows final modelIndex = row - 1; if (modelIndex < controller.models.length) { final model = controller.models[modelIndex]; final vendor = controller.getVendorById(model.vendorsId); switch (column) { case 0: return ShadTableCell( child: Container( padding: const EdgeInsets.all(12), child: Text(model.id.toString()), ), ); case 1: return ShadTableCell( child: Container( padding: const EdgeInsets.all(12), child: Text(vendor?.name ?? 'Unknown'), ), ); case 2: return ShadTableCell( child: Container( padding: const EdgeInsets.all(12), child: Text(model.name), ), ); case 3: return ShadTableCell( child: Container( padding: const EdgeInsets.all(12), child: Text('-'), ), ); case 4: return ShadTableCell( child: Container( padding: const EdgeInsets.all(12), child: ShadBadge( backgroundColor: model.isActive ? Colors.green.shade100 : Colors.grey.shade200, child: Text( model.isActive ? '활성' : '비활성', style: TextStyle( color: model.isActive ? Colors.green.shade700 : Colors.grey.shade700, ), ), ), ), ); case 5: return ShadTableCell( child: Container( padding: const EdgeInsets.all(4), child: PopupMenuButton( icon: const Icon(Icons.more_vert, size: 16), padding: EdgeInsets.zero, itemBuilder: (context) => [ PopupMenuItem( value: 'edit', child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.edit, size: 16, color: Colors.grey[600]), const SizedBox(width: 8), const Text('편집'), ], ), ), PopupMenuItem( value: 'delete', child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.delete, size: 16, color: Colors.red[600]), const SizedBox(width: 8), const Text('삭제'), ], ), ), ], onSelected: (value) { switch (value) { case 'edit': _showEditDialog(model); break; case 'delete': _showDeleteConfirmation(model); break; } }, ), ), ); default: return const ShadTableCell(child: SizedBox()); } } return const ShadTableCell(child: SizedBox()); }, rowCount: controller.models.length + 1, // +1 for header columnCount: 6, ), ), ), ); }, ); } void _showCreateDialog() { showShadDialog( context: context, builder: (context) => ModelFormDialog( controller: _controller, ), ); } void _showEditDialog(ModelDto model) { showShadDialog( context: context, builder: (context) => ModelFormDialog( controller: _controller, model: model, ), ); } void _showDeleteConfirmation(ModelDto model) { showShadDialog( context: context, builder: (context) => ShadDialog( title: const Text('모델 삭제'), description: Text('${model.name} 모델을 삭제하시겠습니까?'), actions: [ ShadButton.outline( onPressed: () => Navigator.of(context).pop(), child: const Text('취소'), ), ShadButton.destructive( onPressed: () async { final success = await _controller.deleteModel(model.id!); if (context.mounted) { Navigator.of(context).pop(); if (success) { ShadToaster.of(context).show( const ShadToast( title: Text('성공'), description: Text('모델이 삭제되었습니다.'), ), ); } else { ShadToaster.of(context).show( ShadToast( title: const Text('오류'), description: Text(_controller.errorMessage ?? '삭제 실패'), backgroundColor: Colors.red, ), ); } } }, child: const Text('삭제'), ), ], ), ); } }