import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import '../models/menus.dart'; class PlatEditPage extends StatefulWidget { final List categories; final MenuPlat? plat; // if present = edit, else create final Function()? onSaved; // callback when saved const PlatEditPage({ super.key, required this.categories, this.plat, this.onSaved, }); @override State createState() => _PlatEditPageState(); } class _PlatEditPageState extends State { final _formKey = GlobalKey(); late TextEditingController nomCtrl, descCtrl, prixCtrl, ingredientsCtrl; late bool disponible; MenuCategory? selectedCategory; // Une seule catégorie pour correspondre à l'API @override void initState() { super.initState(); nomCtrl = TextEditingController(text: widget.plat?.nom ?? ""); descCtrl = TextEditingController(text: widget.plat?.commentaire ?? ""); prixCtrl = TextEditingController( text: widget.plat != null ? widget.plat!.prix.toStringAsFixed(2) : "", ); ingredientsCtrl = TextEditingController(text: widget.plat?.ingredients ?? ""); disponible = widget.plat?.disponible ?? true; // Utiliser la catégorie unique ou la première des multiples selectedCategory = widget.plat?.category ?? (widget.plat?.categories?.isNotEmpty == true ? widget.plat!.categories!.first : null); } @override void dispose() { nomCtrl.dispose(); descCtrl.dispose(); prixCtrl.dispose(); ingredientsCtrl.dispose(); super.dispose(); } Future submit() async { if (!_formKey.currentState!.validate()) return; if (selectedCategory == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Sélectionnez une catégorie.')), ); return; } // Build request data selon votre API final body = { "nom": nomCtrl.text, "commentaire": descCtrl.text, "ingredients": ingredientsCtrl.text, "prix": double.tryParse(prixCtrl.text) ?? 0, "categorie_id": selectedCategory!.id, // L'API attend categorie_id "disponible": disponible, }; try { final isEdit = widget.plat != null; final url = isEdit ? 'https://restaurant.careeracademy.mg/api/menus/${widget.plat!.id}' : 'https://restaurant.careeracademy.mg/api/menus'; final res = isEdit ? await http.put( Uri.parse(url), headers: {"Content-Type": "application/json"}, body: json.encode(body), ) : await http.post( Uri.parse(url), headers: {"Content-Type": "application/json"}, body: json.encode(body), ); if (res.statusCode == 200 || res.statusCode == 201) { widget.onSaved?.call(); Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(isEdit ? 'Plat modifié avec succès' : 'Plat créé avec succès'), backgroundColor: Colors.green, ), ); } else { print('Error: ${res.body}\nBody sent: $body'); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur lors de ${isEdit ? "la modification" : "la création"} du plat'), backgroundColor: Colors.red, ), ); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur réseau: $e'), backgroundColor: Colors.red, ), ); } } @override Widget build(BuildContext context) { final isEdit = widget.plat != null; return Scaffold( backgroundColor: const Color(0xfffcfbf9), appBar: AppBar( title: Text(isEdit ? 'Éditer le plat' : 'Nouveau plat'), leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => Navigator.pop(context), ), centerTitle: true, elevation: 0, backgroundColor: Colors.transparent, foregroundColor: Colors.black87, ), body: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 550), child: Card( elevation: 2, margin: const EdgeInsets.all(32), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(28), child: Form( key: _formKey, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( isEdit ? 'Modifier le plat' : 'Nouveau plat', style: const TextStyle( fontSize: 21, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 20), // Nom du plat TextFormField( controller: nomCtrl, decoration: const InputDecoration( labelText: "Nom du plat *", hintText: "Ex: Steak frites", border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(8)), ), filled: true, fillColor: Color(0xFFF7F7F7), ), validator: (v) => (v == null || v.isEmpty) ? "Obligatoire" : null, ), const SizedBox(height: 16), // Description TextFormField( controller: descCtrl, decoration: const InputDecoration( labelText: "Description", hintText: "Description détaillée du plat...", border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(8)), ), filled: true, fillColor: Color(0xFFF7F7F7), ), maxLines: 3, ), const SizedBox(height: 16), // Ingrédients TextFormField( controller: ingredientsCtrl, decoration: const InputDecoration( labelText: "Ingrédients", hintText: "Liste des ingrédients...", border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(8)), ), filled: true, fillColor: Color(0xFFF7F7F7), ), maxLines: 2, ), const SizedBox(height: 16), Row( children: [ // Prix Expanded( child: TextFormField( controller: prixCtrl, decoration: const InputDecoration( labelText: "Prix (MGA) *", border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(8)), ), filled: true, fillColor: Color(0xFFF7F7F7), ), validator: (v) => (v == null || v.isEmpty || double.tryParse(v) == null) ? "Obligatoire" : null, keyboardType: const TextInputType.numberWithOptions( decimal: true, ), ), ), const SizedBox(width: 16), // Catégorie Expanded( child: DropdownButtonFormField( value: selectedCategory, decoration: const InputDecoration( labelText: "Catégorie *", border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(8)), ), filled: true, fillColor: Color(0xFFF7F7F7), ), validator: (v) => v == null ? "Obligatoire" : null, items: widget.categories.map((cat) { return DropdownMenuItem( value: cat, child: Text(cat.nom), ); }).toList(), onChanged: (value) { setState(() => selectedCategory = value); }, ), ), ], ), const SizedBox(height: 20), // Switch disponibilité Row( children: [ Switch( value: disponible, activeColor: Colors.green, onChanged: (v) => setState(() => disponible = v), ), const SizedBox(width: 8), Text( 'Plat disponible', style: TextStyle( fontSize: 16, color: disponible ? Colors.black87 : Colors.grey, ), ), ], ), const SizedBox(height: 30), // Boutons Row( mainAxisAlignment: MainAxisAlignment.end, children: [ OutlinedButton( onPressed: () => Navigator.pop(context), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), ), child: const Text( "Annuler", style: TextStyle(color: Colors.black54), ), ), const SizedBox(width: 16), ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: Colors.green[700], foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), ), onPressed: submit, icon: const Icon(Icons.save, size: 18), label: Text(isEdit ? "Enregistrer" : "Créer le plat"), ), ], ), ], ), ), ), ), ), ), ), ); } }