329 lines
9.8 KiB
Dart
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,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|