// Version: 2.0 - 担当者マスタ画面(リッチ編集ダイアログ統合) // ※ EmployeeEditDialog を使用した簡易実装 import 'package:flutter/material.dart'; import '../models/employee.dart'; import '../widgets/employee_edit_dialog.dart'; /// 担当者マスタ管理画面 class EmployeeMasterScreen extends StatefulWidget { const EmployeeMasterScreen({super.key}); @override State createState() => _EmployeeMasterScreenState(); } class _EmployeeMasterScreenState extends State { List _employees = []; bool _loading = true; // 検索キーワード String get _filteredEmployees => _searchKeyword.isEmpty ? _employees : _employees.where((e) => e.name.toLowerCase().contains(_searchKeyword.toLowerCase()) || (e.department.isNotEmpty && e.department.toLowerCase().contains(_searchKeyword.toLowerCase()))).toList(); @override void initState() { super.initState(); _loadEmployees(); } /// 従業員データをロード(デモデータ) Future _loadEmployees() async { setState(() => _loading = true); try { // サンプルデータを初期化 final demoData = [ Employee(id: 1, name: '山田太郎', email: 'tanaka@company.com', tel: '03-1234-5678', department: '営業部', role: '営業担当'), Employee(id: 2, name: '田中花子', email: 'tanaka@company.com', tel: '03-2345-6789', department: '総務部', role: '総務担当'), Employee(id: 3, name: '鈴木一郎', email: 'suzuki@company.com', tel: '03-3456-7890', department: '経理部', role: '経理担当'), ]; setState(() => _employees = demoData); } catch (e) { if (mounted) ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('読み込みエラー:$e'), backgroundColor: Colors.red), ); } finally { setState(() => _loading = false); } } /// 新規従業員追加 Future _addEmployee() async { final edited = await showDialog( context: context, builder: (ctx) => EmployeeEditDialog( title: '担当者登録', initialData: null, ), ); if (edited != null && mounted) { setState(() => _employees.add(edited)); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('担当者登録完了'), backgroundColor: Colors.green), ); } } /// 従業員編集 Future _editEmployee(Employee employee) async { final edited = await showDialog( context: context, builder: (ctx) => EmployeeEditDialog( title: '担当者編集', initialData: employee, ), ); if (edited != null && mounted) { setState(() { _employees = _employees.map((e) => e.id == edited.id ? edited : e).toList(); }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('担当者更新完了'), backgroundColor: Colors.green), ); } } /// 従業員削除 Future _deleteEmployee(Employee employee) async { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('担当者削除'), content: Text('この担当者を実際に削除しますか?'), actions: [ TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('キャンセル')), ElevatedButton( onPressed: () => Navigator.pop(ctx, true), style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: const Text('削除'), ), ], ), ); if (confirmed == true && mounted) { setState(() { _employees.removeWhere((e) => e.id == employee.id); }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('担当者削除完了'), backgroundColor: Colors.green), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('/M5. 担当者マスタ'), actions: [ IconButton(icon: const Icon(Icons.refresh), onPressed: _loadEmployees), IconButton(icon: const Icon(Icons.add), onPressed: _addEmployee), ], ), body: Column( children: [ // 検索バー Padding( padding: const EdgeInsets.all(8.0), child: TextField( decoration: InputDecoration( hintText: '担当者名で検索...', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), ), onChanged: (value) => setState(() => _searchKeyword = value), ), ), // 一覧リスト Expanded( child: _loading ? const Center(child: CircularProgressIndicator()) : _filteredEmployees.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.person_outline, size: 64, color: Colors.grey[300]), SizedBox(height: 16), Text('担当者データがありません', style: TextStyle(color: Colors.grey)), SizedBox(height: 16), ElevatedButton.icon( onPressed: _addEmployee, icon: const Icon(Icons.add), label: const Text('新規登録'), ), ], ), ) : ListView.builder( padding: const EdgeInsets.all(8), itemCount: _filteredEmployees.length, itemBuilder: (context, index) { final employee = _filteredEmployees[index]; return Card( margin: EdgeInsets.zero, child: ListTile( leading: CircleAvatar( backgroundColor: Colors.purple.shade100, child: Text('${employee.department.substring(0, 1)}', style: const TextStyle(fontWeight: FontWeight.bold)), ), title: Text(employee.name ?? '未入力', style: const TextStyle(fontWeight: FontWeight.w500)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (employee.department.isNotEmpty) Text('部署:${employee.department}', style: const TextStyle(fontSize: 12)), if (employee.role.isNotEmpty) Text('役職:${employee.role}', style: const TextStyle(fontSize: 12)), if (employee.tel.isNotEmpty) Text('TEL: ${employee.tel}', style: const TextStyle(fontSize: 10, color: Colors.grey)), ], ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton(icon: const Icon(Icons.edit), onPressed: () => _editEmployee(employee)), IconButton(icon: const Icon(Icons.delete_outline), onPressed: () => _deleteEmployee(employee)), ], ), ), ); }, ), ), ], ), ); } }