h-1.flutter.4/@workspace/lib/screens/master/employee_master_screen.dart

198 lines
No EOL
7.7 KiB
Dart

// 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<EmployeeMasterScreen> createState() => _EmployeeMasterScreenState();
}
class _EmployeeMasterScreenState extends State<EmployeeMasterScreen> {
List<Employee> _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<void> _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<void> _addEmployee() async {
final edited = await showDialog<Employee>(
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<void> _editEmployee(Employee employee) async {
final edited = await showDialog<Employee>(
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<void> _deleteEmployee(Employee employee) async {
final confirmed = await showDialog<bool>(
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)),
],
),
),
);
},
),
),
],
),
);
}
}