inventory_mobile/lib/screens/admin/manufacturer/manufacturer.dart
2025-12-15 16:04:14 +08:00

319 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:inventory_system/services/manufacturer_service.dart';
import 'package:inventory_system/screens/bottom_nav_bar.dart';
import 'package:inventory_system/screens/nav_bar.dart';
import 'package:inventory_system/screens/title_bar.dart';
class ManufacturerScreen extends StatefulWidget {
const ManufacturerScreen({super.key});
@override
State<ManufacturerScreen> createState() => _ManufacturerScreenState();
}
class _ManufacturerScreenState extends State<ManufacturerScreen> {
int _selectedIndex = 0;
final ManufacturerService _manufacturerService = ManufacturerService();
late Future<List<dynamic>> _manufacturersFuture;
List<dynamic> _allManufacturers = [];
List<dynamic> _filteredManufacturers = [];
final TextEditingController _searchController = TextEditingController();
final TextEditingController _formController = TextEditingController();
@override
void initState() {
super.initState();
_manufacturersFuture = _fetchManufacturers();
_searchController.addListener(_onSearchChanged);
}
Future<List<dynamic>> _fetchManufacturers() async {
try {
final manufacturers = await _manufacturerService.fetchManufacturers();
manufacturers.sort((a, b) {
final nameA = a['manufacturerName']?.toString().toLowerCase() ?? '';
final nameB = b['manufacturerName']?.toString().toLowerCase() ?? '';
return nameA.compareTo(nameB);
});
if (mounted) {
setState(() {
_allManufacturers = manufacturers;
_filteredManufacturers = manufacturers;
});
}
return manufacturers;
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to load manufacturers: $e'), backgroundColor: Colors.red),
);
}
rethrow;
}
}
@override
void dispose() {
_searchController.dispose();
_formController.dispose();
super.dispose();
}
void _onSearchChanged() {
final query = _searchController.text.toLowerCase();
setState(() {
_filteredManufacturers = _allManufacturers.where((manufacturer) {
final name = manufacturer['manufacturerName']?.toString().toLowerCase() ?? '';
return name.contains(query);
}).toList();
});
}
void _showFormDialog({Map<String, dynamic>? manufacturer}) {
final isEdit = manufacturer != null;
_formController.text = isEdit ? manufacturer['manufacturerName'] : '';
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: Text(isEdit ? 'Edit Manufacturer' : 'Add Manufacturer'),
content: TextField(
controller: _formController,
autofocus: true,
decoration: InputDecoration(
labelText: 'Manufacturer Name',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
),
),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text('Cancel')),
ElevatedButton(
onPressed: () => _submitForm(isEdit, manufacturer?['manufacturerId']),
child: const Text('Save'),
),
],
),
);
}
void _submitForm(bool isEdit, int? id) async {
if (_formController.text.isEmpty) return;
Navigator.pop(context);
final String newName = _formController.text;
try {
if (isEdit) {
await _manufacturerService.updateManufacturer(id!, newName);
} else {
await _manufacturerService.addManufacturer(newName);
}
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(isEdit ? 'Manufacturer updated' : 'Manufacturer added'),
backgroundColor: Colors.green,
),
);
_fetchManufacturers();
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Operation failed: $e'),
backgroundColor: Colors.red,
),
);
}
}
void _confirmDelete(int id, String name) {
showDialog(
context: context,
builder: (dialogContext) => AlertDialog(
title: const Text('Delete Manufacturer'),
content: Text('Are you sure you want to delete $name?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext),
child: const Text('Cancel'),
),
TextButton(
onPressed: () async {
try {
await _manufacturerService.deleteManufacturer(id);
Navigator.pop(dialogContext);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Manufacturer deleted successfully'),
backgroundColor: Colors.green,
),
);
await _fetchManufacturers(); // Refresh only on success
} catch (e) {
Navigator.pop(dialogContext);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Deletion failed: $e'),
backgroundColor: Colors.red,
),
);
}
},
child: const Text('Delete', style: TextStyle(color: Colors.red)),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F5F7),
appBar: const TitleBar(title: 'Manufacturer'),
drawer: const NavBar(isAdmin: true, selectedScreen: AppScreen.manufacturer), // Placeholder userName
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Row(
children: [
ElevatedButton.icon(
onPressed: _showFormDialog,
icon: const Icon(Icons.add, size: 18),
label: const Text('Add Manufacturer'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple.shade100,
foregroundColor: Colors.black87,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
),
const SizedBox(width: 12),
Expanded(
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: 'Search',
hintStyle: TextStyle(color: Colors.grey.shade400),
prefixIcon: const Icon(Icons.search),
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none),
),
),
),
],
),
const SizedBox(height: 16),
Expanded(
child: RefreshIndicator(
onRefresh: _fetchManufacturers,
child: FutureBuilder<List<dynamic>>(
future: _manufacturersFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting && _allManufacturers.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError && _allManufacturers.isEmpty) {
return Center(child: Text('Error: ${snapshot.error}'));
}
if (_filteredManufacturers.isEmpty) {
return const Center(child: Text('No manufacturers found.'));
}
return ListView.builder(
itemCount: _filteredManufacturers.length,
itemBuilder: (context, index) {
final manufacturer = _filteredManufacturers[index];
final name = manufacturer['manufacturerName'] ?? 'No Name';
final id = manufacturer['manufacturerId'];
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Slidable(
key: Key(id.toString()),
endActionPane: ActionPane(
motion: const StretchMotion(),
children: [
SlidableAction(
onPressed: (context) => _showFormDialog(manufacturer: manufacturer),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
icon: Icons.edit,
label: 'Edit',
),
SlidableAction(
onPressed: (context) => _confirmDelete(id, name),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: Icons.delete,
label: 'Delete',
borderRadius: const BorderRadius.horizontal(
right: Radius.circular(16)),
),
],
),
child: Card(
margin: EdgeInsets.zero,
color: Colors.white,
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: Colors.grey.shade300, width: 1),
),
child: ListTile(
title: Text(
name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
),
),
),
);
},
);
},
),
),
),
],
),
),
bottomNavigationBar: BottomNavBar(
selectedIndex: _selectedIndex,
onItemTapped: (index) {
if (index == 0) {
Navigator.pop(context);
} else {
setState(() {
_selectedIndex = index;
});
}
},
),
);
}
}