CoffeeAtHome/lib/components/machine_dialog.dart
2026-03-29 08:13:38 -07:00

329 lines
9.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/machine.dart';
import '../providers/app_state.dart';
class MachineDialog extends StatefulWidget {
final Machine? machine; // null for add, non-null for edit
const MachineDialog({super.key, this.machine});
@override
State<MachineDialog> createState() => _MachineDialogState();
}
class _MachineDialogState extends State<MachineDialog> {
final _formKey = GlobalKey<FormState>();
late final TextEditingController _modelController;
late final TextEditingController _manufacturerController;
late final TextEditingController _yearController;
late final TextEditingController _detailsController;
late MachineType _selectedType;
late bool _hasSteamWand;
@override
void initState() {
super.initState();
final machine = widget.machine;
_modelController = TextEditingController(text: machine?.model ?? '');
_manufacturerController = TextEditingController(text: machine?.manufacturer ?? '');
_yearController = TextEditingController(text: machine?.year.toString() ?? '');
_detailsController = TextEditingController(text: machine?.details ?? '');
_selectedType = machine?.type ?? MachineType.espresso;
_hasSteamWand = machine?.steamWand ?? false;
}
@override
void dispose() {
_modelController.dispose();
_manufacturerController.dispose();
_yearController.dispose();
_detailsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Dialog(
child: Container(
width: MediaQuery.of(context).size.width > 600 ? 600 : double.infinity,
height: MediaQuery.of(context).size.height * 0.8,
padding: const EdgeInsets.all(24),
child: Column(
children: [
// Header
Row(
children: [
Icon(
Icons.kitchen,
color: Theme.of(context).colorScheme.primary,
size: 28,
),
const SizedBox(width: 12),
Expanded(
child: Text(
widget.machine == null ? 'Add New Machine' : 'Edit Machine',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w600,
),
),
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
],
),
const Divider(),
// Form
Expanded(
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildBasicInfoSection(),
const SizedBox(height: 24),
_buildSpecificationSection(),
const SizedBox(height: 24),
_buildFeaturesSection(),
],
),
),
),
),
// Actions
const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
const SizedBox(width: 12),
ElevatedButton(
onPressed: _saveMachine,
child: Text(widget.machine == null ? 'Add Machine' : 'Update Machine'),
),
],
),
],
),
),
);
}
Widget _buildBasicInfoSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Basic Information',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 16),
TextFormField(
controller: _modelController,
decoration: const InputDecoration(
labelText: 'Model Name *',
hintText: 'e.g., Breville Barista Express',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Model name is required';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _manufacturerController,
decoration: const InputDecoration(
labelText: 'Manufacturer *',
hintText: 'e.g., Breville, De\'Longhi, Gaggia',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Manufacturer is required';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _yearController,
decoration: const InputDecoration(
labelText: 'Year',
hintText: 'e.g., 2023',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
validator: (value) {
if (value != null && value.isNotEmpty) {
final year = int.tryParse(value);
if (year == null || year < 1900 || year > DateTime.now().year + 1) {
return 'Please enter a valid year';
}
}
return null;
},
),
],
);
}
Widget _buildSpecificationSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Specifications',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 16),
DropdownButtonFormField<MachineType>(
value: _selectedType,
decoration: const InputDecoration(
labelText: 'Machine Type',
border: OutlineInputBorder(),
),
items: MachineType.values.map((type) {
return DropdownMenuItem(
value: type,
child: Text(_formatMachineType(type)),
);
}).toList(),
onChanged: (value) {
if (value != null) {
setState(() {
_selectedType = value;
});
}
},
),
const SizedBox(height: 16),
TextFormField(
controller: _detailsController,
decoration: const InputDecoration(
labelText: 'Details & Notes',
hintText: 'Additional information about the machine',
border: OutlineInputBorder(),
),
maxLines: 3,
),
],
);
}
Widget _buildFeaturesSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Features',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 16),
SwitchListTile(
title: const Text('Steam Wand'),
subtitle: const Text('Does this machine have a steam wand for milk?'),
value: _hasSteamWand,
onChanged: (value) {
setState(() {
_hasSteamWand = value;
});
},
),
],
);
}
String _formatMachineType(MachineType type) {
switch (type) {
case MachineType.espresso:
return 'Espresso Machine';
case MachineType.drip:
return 'Drip Coffee Maker';
case MachineType.percolation:
return 'Percolation';
case MachineType.frenchPress:
return 'French Press';
case MachineType.coldBrew:
return 'Cold Brew Maker';
case MachineType.e61:
return 'E61 Group Head';
case MachineType.pod:
return 'Pod Machine';
case MachineType.espressoPod:
return 'Espresso Pod Machine';
case MachineType.grinder:
return 'Coffee Grinder';
}
}
void _saveMachine() async {
if (!_formKey.currentState!.validate()) {
return;
}
try {
final machine = Machine(
id: widget.machine?.id ?? DateTime.now().millisecondsSinceEpoch.toString(),
model: _modelController.text.trim(),
manufacturer: _manufacturerController.text.trim(),
year: int.tryParse(_yearController.text.trim()) ?? DateTime.now().year,
type: _selectedType,
steamWand: _hasSteamWand,
details: _detailsController.text.trim(),
isOwned: true,
rating: 4.0,
popularity: 50,
portafilters: [],
specifications: {},
);
final appState = Provider.of<AppState>(context, listen: false);
if (widget.machine == null) {
await appState.addMachine(machine);
} else {
await appState.updateMachine(machine);
}
if (mounted) {
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(widget.machine == null
? 'Machine added successfully!'
: 'Machine updated successfully!'),
backgroundColor: Theme.of(context).colorScheme.primary,
),
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error saving machine: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
}