import 'package:flutter/material.dart'; import 'package:uuid/uuid.dart'; import '../models/department_model.dart'; import '../services/department_repository.dart'; class DepartmentMasterScreen extends StatefulWidget { const DepartmentMasterScreen({super.key}); @override State createState() => _DepartmentMasterScreenState(); } class _DepartmentMasterScreenState extends State { final DepartmentRepository _repository = DepartmentRepository(); final Uuid _uuid = const Uuid(); bool _isLoading = true; bool _includeInactive = true; List _departments = const []; @override void initState() { super.initState(); _loadDepartments(); } Future _loadDepartments() async { setState(() => _isLoading = true); final list = await _repository.fetchDepartments(includeInactive: _includeInactive); if (!mounted) return; setState(() { _departments = list; _isLoading = false; }); } Future _openForm({Department? department}) async { final result = await showDialog( context: context, builder: (ctx) => _DepartmentFormDialog( department: department, onSubmit: (data) => Navigator.of(ctx).pop(data), ), ); if (result == null) return; final saving = result.copyWith(id: result.id.isEmpty ? _uuid.v4() : result.id, updatedAt: DateTime.now()); await _repository.saveDepartment(saving); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('部門を保存しました'))); _loadDepartments(); } Future _deleteDepartment(Department department) async { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('部門を削除'), content: Text('${department.name} を削除しますか?'), actions: [ TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('キャンセル')), TextButton(onPressed: () => Navigator.pop(ctx, true), child: const Text('削除', style: TextStyle(color: Colors.red))), ], ), ); if (confirmed != true) return; await _repository.deleteDepartment(department.id); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('部門を削除しました'))); _loadDepartments(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: const BackButton(), title: const Text('M4:部門マスター'), actions: [ SwitchListTile.adaptive( value: _includeInactive, onChanged: (value) { setState(() => _includeInactive = value); _loadDepartments(); }, contentPadding: const EdgeInsets.only(right: 12), title: const Text('無効を表示'), ), ], ), floatingActionButton: FloatingActionButton( onPressed: () => _openForm(), child: const Icon(Icons.add), ), body: _isLoading ? const Center(child: CircularProgressIndicator()) : _departments.isEmpty ? const _EmptyState(message: '部門が登録されていません') : ListView.separated( padding: const EdgeInsets.all(16), itemCount: _departments.length, separatorBuilder: (context, _) => const SizedBox(height: 8), itemBuilder: (context, index) { final department = _departments[index]; return Card( child: ListTile( title: Text(department.name, style: const TextStyle(fontWeight: FontWeight.bold)), subtitle: Text([ if (department.code?.isNotEmpty == true) 'コード: ${department.code}', department.description ?? '', department.isActive ? '稼働中' : '無効', ].where((v) => v.isNotEmpty).join('\n')), trailing: PopupMenuButton( onSelected: (value) { switch (value) { case 'edit': _openForm(department: department); break; case 'delete': _deleteDepartment(department); break; } }, itemBuilder: (context) => const [ PopupMenuItem(value: 'edit', child: Text('編集')), PopupMenuItem(value: 'delete', child: Text('削除')), ], ), ), ); }, ), ); } } class _DepartmentFormDialog extends StatefulWidget { const _DepartmentFormDialog({required this.onSubmit, this.department}); final Department? department; final ValueChanged onSubmit; @override State<_DepartmentFormDialog> createState() => _DepartmentFormDialogState(); } class _DepartmentFormDialogState extends State<_DepartmentFormDialog> { late final TextEditingController _nameController; late final TextEditingController _codeController; late final TextEditingController _descriptionController; bool _isActive = true; @override void initState() { super.initState(); final department = widget.department; _nameController = TextEditingController(text: department?.name ?? ''); _codeController = TextEditingController(text: department?.code ?? ''); _descriptionController = TextEditingController(text: department?.description ?? ''); _isActive = department?.isActive ?? true; } void _submit() { if (_nameController.text.trim().isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('部門名は必須です'))); return; } widget.onSubmit( Department( id: widget.department?.id ?? '', name: _nameController.text.trim(), code: _codeController.text.trim().isEmpty ? null : _codeController.text.trim(), description: _descriptionController.text.trim().isEmpty ? null : _descriptionController.text.trim(), isActive: _isActive, updatedAt: DateTime.now(), ), ); } @override Widget build(BuildContext context) { return AlertDialog( title: Text(widget.department == null ? '部門を追加' : '部門を編集'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField(controller: _nameController, decoration: const InputDecoration(labelText: '部門名 *')), TextField(controller: _codeController, decoration: const InputDecoration(labelText: '部門コード')), TextField(controller: _descriptionController, decoration: const InputDecoration(labelText: '説明'), maxLines: 2), SwitchListTile( title: const Text('稼働中'), value: _isActive, onChanged: (value) => setState(() => _isActive = value), ), ], ), ), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: const Text('キャンセル')), FilledButton(onPressed: _submit, child: const Text('保存')), ], ); } } class _EmptyState extends StatelessWidget { const _EmptyState({required this.message}); final String message; @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.view_list, size: 64, color: Colors.grey), const SizedBox(height: 16), Text(message), ], ), ); } }