import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:image/image.dart' as img; import 'package:image_picker/image_picker.dart'; import 'package:provider/provider.dart'; import '../models/service.dart'; import '../models/vehicle.dart'; import '../services/session_manager.dart'; enum _ServicePerformer { shop, self } class AddServiceScreen extends StatefulWidget { final ServiceRecord? service; final VoidCallback? onSaved; final bool standalone; AddServiceScreen({this.service, this.onSaved, this.standalone = false}); @override _AddServiceScreenState createState() => _AddServiceScreenState(); } class _AddServiceScreenState extends State { final _formKey = GlobalKey(); String? _selectedVehicleId; ServiceType? _selectedType; final TextEditingController _customTypeController = TextEditingController(); final TextEditingController _itemNameController = TextEditingController(); final TextEditingController _costController = TextEditingController(); final TextEditingController _mileageController = TextEditingController(); final TextEditingController _shopController = TextEditingController(); _ServicePerformer _performer = _ServicePerformer.shop; final TextEditingController _noteController = TextEditingController(); final List _photos = []; DateTime? _selectedDate; final TextEditingController _dateController = TextEditingController(); bool _initialized = false; @override void didChangeDependencies() { super.didChangeDependencies(); if (_initialized) return; final session = Provider.of(context); if (widget.service != null) { final s = widget.service!; _selectedVehicleId = s.vehicleId; _selectedType = s.serviceType; if (s.customType != null) _customTypeController.text = s.customType!; if (s.itemName != null) _itemNameController.text = s.itemName!; _costController.text = s.cost.toString(); _mileageController.text = s.mileage.toString(); if (s.selfService) { _performer = _ServicePerformer.self; } else { _performer = _ServicePerformer.shop; if (s.shop != null) _shopController.text = s.shop!; } if (s.note != null) _noteController.text = s.note!; for (final p in s.photos) { try { final data = p.contains(',') ? p.split(',').last : p; _photos.add(base64Decode(data)); } catch (_) {} } _selectedDate = s.date; if (s.date != null) { _dateController.text = _formatDate(s.date!); } } else { _selectedVehicleId = session.defaultVehicle?.id; } _initialized = true; } String _formatDate(DateTime date) { final d = date.toLocal(); return '${d.year.toString().padLeft(4, '0')}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}'; } @override Widget build(BuildContext context) { final session = Provider.of(context); final vehicles = session.vehicles; final form = Padding( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ DropdownButtonFormField( decoration: InputDecoration(labelText: 'Vehicle'), value: _selectedVehicleId, items: vehicles .map((vehicle) => DropdownMenuItem( value: vehicle.id, child: Text(vehicle.name), )) .toList(), onChanged: (value) => setState(() => _selectedVehicleId = value), validator: (value) => value == null ? 'Please select a vehicle' : null, ), SizedBox(height: 16), DropdownButtonFormField( decoration: InputDecoration(labelText: 'Service Type'), value: _selectedType, items: ServiceType.values .map((t) => DropdownMenuItem( value: t, child: Text(t.label), )) .toList(), onChanged: (value) => setState(() => _selectedType = value), validator: (value) => value == null ? 'Please select a service type' : null, ), if (_selectedType == ServiceType.other) ...[ SizedBox(height: 16), TextFormField( controller: _customTypeController, decoration: InputDecoration(labelText: 'Custom Type'), validator: (v) => v == null || v.isEmpty ? 'Enter custom type' : null, ), ], SizedBox(height: 16), TextFormField( controller: _itemNameController, decoration: InputDecoration(labelText: 'Item Name'), ), SizedBox(height: 16), TextFormField( controller: _costController, decoration: InputDecoration(labelText: 'Cost'), keyboardType: TextInputType.number, validator: _numberValidator, ), SizedBox(height: 16), TextFormField( controller: _mileageController, decoration: InputDecoration(labelText: 'Mileage'), keyboardType: TextInputType.number, validator: _numberValidator, ), SizedBox(height: 16), RadioListTile<_ServicePerformer>( value: _ServicePerformer.shop, groupValue: _performer, title: Text('Performed at shop'), onChanged: (v) => setState(() => _performer = v!), ), RadioListTile<_ServicePerformer>( value: _ServicePerformer.self, groupValue: _performer, title: Text('Self Service'), onChanged: (v) => setState(() { _performer = v!; _shopController.clear(); }), ), if (_performer == _ServicePerformer.shop) ...[ SizedBox(height: 16), TextFormField( controller: _shopController, decoration: InputDecoration(labelText: 'Shop'), validator: (v) => v == null || v.isEmpty ? 'Enter shop name' : null, ), ], SizedBox(height: 16), TextFormField( controller: _noteController, decoration: InputDecoration(labelText: 'Note'), ), if (_photos.isNotEmpty) ...[ SizedBox(height: 16), Wrap( spacing: 8, runSpacing: 8, children: List.generate(_photos.length, (i) { return Stack( children: [ Image.memory( _photos[i], width: 80, height: 80, fit: BoxFit.cover, ), Positioned( right: 0, top: 0, child: GestureDetector( onTap: () => setState(() => _photos.removeAt(i)), child: Container( color: Colors.black54, child: Icon(Icons.close, color: Colors.white, size: 20), ), ), ), ], ); }), ), ], SizedBox(height: 16), TextButton.icon( onPressed: _pickPhoto, icon: Icon(Icons.add_a_photo), label: Text('Add Photo'), ), SizedBox(height: 16), TextFormField( controller: _dateController, decoration: InputDecoration(labelText: 'Date'), readOnly: true, onTap: () async { final now = DateTime.now(); final picked = await showDatePicker( context: context, initialDate: _selectedDate ?? now, firstDate: DateTime(2000), lastDate: DateTime(2100), ); if (picked != null) { setState(() { _selectedDate = picked; _dateController.text = _formatDate(picked); }); } }, validator: (v) => _selectedDate == null ? 'Select a date' : null, ), SizedBox(height: 24), ElevatedButton( onPressed: _submitForm, style: ElevatedButton.styleFrom(backgroundColor: Colors.green), child: Text(widget.service == null ? 'Create Service Record' : 'Update Service Record'), ), ], ), ), ), ); if (widget.standalone) { return Scaffold( appBar: AppBar( title: Text(widget.service == null ? 'Add Service Record' : 'Edit Service Record'), ), body: form, ); } return form; } Future _pickPhoto() async { final picker = ImagePicker(); final source = await showModalBottomSheet( context: context, builder: (context) => SafeArea( child: Wrap( children: [ ListTile( leading: Icon(Icons.camera_alt), title: Text('Take Photo'), onTap: () => Navigator.pop(context, ImageSource.camera), ), ListTile( leading: Icon(Icons.photo_library), title: Text('Choose from Gallery'), onTap: () => Navigator.pop(context, ImageSource.gallery), ), ], ), ), ); if (source == null) return; final file = await picker.pickImage(source: source); if (file == null) return; var bytes = await file.readAsBytes(); final decoded = img.decodeImage(bytes); if (decoded != null) { const maxDim = 800; img.Image resized; if (decoded.width > decoded.height) { resized = img.copyResize(decoded, width: maxDim); } else { resized = img.copyResize(decoded, height: maxDim); } bytes = Uint8List.fromList(img.encodeJpg(resized, quality: 70)); } setState(() => _photos.add(bytes)); } String? _numberValidator(String? value) { if (value == null || value.isEmpty) return 'This field cannot be empty'; if (double.tryParse(value) == null) return 'Enter a valid number'; return null; } Future _submitForm() async { if (!(_formKey.currentState?.validate() ?? false)) return; final session = Provider.of(context, listen: false); final service = ServiceRecord( id: widget.service?.id, vehicleId: _selectedVehicleId!, serviceType: _selectedType!, customType: _selectedType == ServiceType.other ? _customTypeController.text : null, itemName: _itemNameController.text.isEmpty ? null : _itemNameController.text, cost: double.parse(_costController.text), mileage: int.parse(_mileageController.text), shop: _performer == _ServicePerformer.shop ? _shopController.text : null, selfService: _performer == _ServicePerformer.self, note: _noteController.text.isEmpty ? null : _noteController.text, photos: _photos.map((b) => base64Encode(b)).toList(), date: _selectedDate, ); bool success = false; if (widget.service == null) { success = await session.addService(service); } else if (widget.service!.id != null) { success = await session.updateService(widget.service!.id!, service); } if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(success ? 'Service record saved successfully!' : 'Failed to save service record.')), ); if (success) { if (Navigator.canPop(context)) { Navigator.pop(context); } else { widget.onSaved?.call(); } } } @override void dispose() { _customTypeController.dispose(); _itemNameController.dispose(); _costController.dispose(); _mileageController.dispose(); _shopController.dispose(); _noteController.dispose(); _dateController.dispose(); super.dispose(); } }