Import sorgenti Flutter di Henflow (export da Nextcloud: lib/ + documenti di progetto)
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
# Henflow - Documento di Progetto
|
||||||
|
|
||||||
|
## Panoramica
|
||||||
|
**Henflow** è un'applicazione mobile sviluppata in Flutter per la gestione professionale di un piccolo pollaio e delle relative prenotazioni di uova. L'app permette di monitorare la produzione stimata in base alle galline possedute e di organizzare le consegne ai clienti in modo automatizzato.
|
||||||
|
|
||||||
|
## Caratteristiche Principali
|
||||||
|
- **Gestione Produzione**: Calcolo automatico della produzione giornaliera basato sull'età e sullo stato delle galline.
|
||||||
|
- **Coda Prenotazioni**: Gestione FIFO degli ordini con ricalcolo dinamico delle date di consegna.
|
||||||
|
- **Anagrafica Clienti**: Rubrica integrata con ricerca rapida durante la creazione degli ordini.
|
||||||
|
- **Multilingua**: Supporto completo per Inglese (default), Italiano, Francese e Tedesco.
|
||||||
|
- **Backup e Ripristino**: Esportazione e importazione dei dati tramite file JSON compatibile con i principali servizi cloud.
|
||||||
|
- **Offline First**: Tutti i dati sono salvati localmente sul dispositivo.
|
||||||
|
|
||||||
|
## Schermate dell'Applicazione
|
||||||
|
|
||||||
|
### 1. Home Page
|
||||||
|
La schermata principale mostra il riepilogo della produzione giornaliera e la lista degli ordini attivi. Ogni ordine indica la data di consegna prevista e lo stato (Oggi, Scaduto, o giorni rimanenti). È possibile condividere i dettagli dell'ordine direttamente con il cliente.
|
||||||
|
|
||||||
|
### 2. Gestione Clienti
|
||||||
|
Una sezione dedicata alla gestione dell'anagrafica. Permette di registrare Nome, Cognome e Numero di telefono dei clienti per velocizzare l'inserimento degli ordini tramite suggerimenti automatici.
|
||||||
|
|
||||||
|
### 3. Configurazione Razze
|
||||||
|
In questa sezione l'utente può attivare le razze presenti nel proprio pollaio. Per ogni razza è disponibile una foto rappresentativa e una descrizione delle caratteristiche produttive (colore uova, produttività).
|
||||||
|
|
||||||
|
### 4. Il Mio Pollaio
|
||||||
|
Mostra un riepilogo delle galline possedute per ogni razza attiva. Da qui è possibile accedere alla gestione analitica di ogni singolo animale.
|
||||||
|
|
||||||
|
### 5. Anagrafica Galline
|
||||||
|
Scheda di dettaglio per ogni razza dove è possibile registrare le singole galline, impostare la loro data di nascita e monitorare il loro stato di deposizione. L'app calcola automaticamente l'età e determina se la gallina è produttiva (6+ mesi).
|
||||||
|
|
||||||
|
### 6. Backup e Ripristino
|
||||||
|
Strumento per la messa in sicurezza dei dati. Permette di esportare l'intero database (clienti, ordini, galline) in un unico file JSON condivisibile via email, WhatsApp o salvabile su Google Drive/iCloud.
|
||||||
|
|
||||||
|
## Specifiche Tecniche
|
||||||
|
- **Identificativo Pacchetto**: `it.carlobaratto.henflow`
|
||||||
|
- **Tecnologia**: Flutter SDK
|
||||||
|
- **Persistenza**: Shared Preferences / JSON Storage
|
||||||
|
- **Integrazioni**: File Picker, Share System API
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Flutter - Prenotazione Uova (fix calendario)
|
||||||
|
|
||||||
|
Correzione applicata:
|
||||||
|
|
||||||
|
- aggiunto flutter_localizations
|
||||||
|
- configurati localizationsDelegates e supportedLocales
|
||||||
|
- impostato Locale('it', 'IT')
|
||||||
|
- inizializzato Intl.defaultLocale = 'it_IT'
|
||||||
|
|
||||||
|
## Avvio
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter pub get
|
||||||
|
flutter run
|
||||||
|
```
|
||||||
|
|
||||||
|
## APK
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter build apk
|
||||||
|
```
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
Documento di Progetto
|
||||||
|
Feature: Modalità Utente (Produttore / Cliente) + Flusso Ordini Offline
|
||||||
|
|
||||||
|
1. Obiettivo
|
||||||
|
Implementare un sistema di selezione modalità utente alla prima apertura dell’app, con possibilità di modifica successiva dalle impostazioni.
|
||||||
|
|
||||||
|
Due modalità:
|
||||||
|
- Produttore
|
||||||
|
- Cliente
|
||||||
|
|
||||||
|
2. Requisiti Funzionali
|
||||||
|
|
||||||
|
2.1 Selezione iniziale
|
||||||
|
Alla prima apertura:
|
||||||
|
- mostrare schermata di scelta:
|
||||||
|
- "Sono un Produttore"
|
||||||
|
- "Sono un Cliente"
|
||||||
|
- salvare scelta in storage locale (user_mode)
|
||||||
|
|
||||||
|
2.2 Modifica modalità
|
||||||
|
- accessibile da Impostazioni
|
||||||
|
- voce: Modalità utente
|
||||||
|
- conferma cambio modalità
|
||||||
|
|
||||||
|
3. Architettura
|
||||||
|
|
||||||
|
Variabile:
|
||||||
|
userMode: "producer" | "client"
|
||||||
|
|
||||||
|
Persistenza:
|
||||||
|
- SharedPreferences / DataStore
|
||||||
|
- chiave: user_mode
|
||||||
|
|
||||||
|
4. Modalità Cliente
|
||||||
|
|
||||||
|
Flusso:
|
||||||
|
- selezione tipo uova: Marans / Araucana / Olive
|
||||||
|
- inserimento quantità
|
||||||
|
- click “Prenota”
|
||||||
|
|
||||||
|
Output JSON:
|
||||||
|
{
|
||||||
|
"order_id": "uuid",
|
||||||
|
"timestamp": "ISO8601",
|
||||||
|
"egg_type": "marans | araucana | olive",
|
||||||
|
"quantity": 12,
|
||||||
|
"client_note": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
File:
|
||||||
|
ordine_uova_<timestamp>.json
|
||||||
|
|
||||||
|
Condivisione:
|
||||||
|
- share system (Intent Android)
|
||||||
|
|
||||||
|
5. Modalità Produttore
|
||||||
|
|
||||||
|
UI:
|
||||||
|
- bottone "Importa ordine"
|
||||||
|
|
||||||
|
Flusso:
|
||||||
|
- apertura file picker
|
||||||
|
- selezione file JSON
|
||||||
|
- parsing
|
||||||
|
|
||||||
|
Validazione:
|
||||||
|
- order_id
|
||||||
|
- timestamp
|
||||||
|
- egg_type
|
||||||
|
- quantity
|
||||||
|
|
||||||
|
Errori:
|
||||||
|
- formato non valido
|
||||||
|
- duplicati
|
||||||
|
|
||||||
|
Dopo import:
|
||||||
|
- calcolo data consegna
|
||||||
|
- inserimento in database
|
||||||
|
|
||||||
|
6. Struttura dati
|
||||||
|
|
||||||
|
{
|
||||||
|
"order_id": "string",
|
||||||
|
"source": "imported | manual",
|
||||||
|
"egg_type": "string",
|
||||||
|
"quantity": "int",
|
||||||
|
"created_at": "datetime",
|
||||||
|
"delivery_date": "datetime"
|
||||||
|
}
|
||||||
|
|
||||||
|
7. UX
|
||||||
|
|
||||||
|
Cliente:
|
||||||
|
- semplice
|
||||||
|
- focus prenotazione
|
||||||
|
|
||||||
|
Produttore:
|
||||||
|
- UI esistente + import
|
||||||
|
|
||||||
|
8. Edge cases
|
||||||
|
|
||||||
|
- cambio modalità
|
||||||
|
- file corrotti
|
||||||
|
- quantità non valida
|
||||||
|
|
||||||
|
9. Non obiettivi
|
||||||
|
|
||||||
|
- cloud
|
||||||
|
- login
|
||||||
|
- notifiche
|
||||||
|
|
||||||
|
10. Priorità
|
||||||
|
|
||||||
|
1. selettore modalità
|
||||||
|
2. flusso cliente
|
||||||
|
3. import produttore
|
||||||
|
4. validazione
|
||||||
|
|
||||||
|
11. KPI
|
||||||
|
|
||||||
|
- completamento prenotazioni
|
||||||
|
- import corretti
|
||||||
|
- tempo creazione ordine
|
||||||
@@ -0,0 +1,480 @@
|
|||||||
|
enum AppLanguage { it, en, fr, de }
|
||||||
|
|
||||||
|
class TranslationService {
|
||||||
|
static AppLanguage _currentLanguage = AppLanguage.en;
|
||||||
|
|
||||||
|
static void setLanguage(AppLanguage lang) {
|
||||||
|
_currentLanguage = lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AppLanguage get currentLanguage => _currentLanguage;
|
||||||
|
|
||||||
|
static final Map<AppLanguage, Map<String, String>> _data = {
|
||||||
|
AppLanguage.it: {
|
||||||
|
'app_title': 'Henflow',
|
||||||
|
'daily_prod_est': 'Produzione giornaliera stimata',
|
||||||
|
'eggs_per_day': 'uova/giorno',
|
||||||
|
'no_breeds': 'Nessuna razza configurata.',
|
||||||
|
'hens': 'galline',
|
||||||
|
'eggs_per_g': 'uova/g',
|
||||||
|
'reservations': 'Prenotazioni',
|
||||||
|
'no_reservations': 'Nessuna prenotazione inserita.',
|
||||||
|
'use_button': 'Usa il pulsante "Nuovo ordine" per registrare un cliente.',
|
||||||
|
'new_order': 'Nuovo ordine',
|
||||||
|
'edit_order': 'Modifica ordine',
|
||||||
|
'breed': 'Razza',
|
||||||
|
'eggs_req': 'Uova richieste',
|
||||||
|
'delivery': 'Consegna',
|
||||||
|
'today': 'Oggi',
|
||||||
|
'ready_delivery': 'Pronti per la consegna',
|
||||||
|
'expired': 'Scaduto',
|
||||||
|
'in_day': 'Tra 1 giorno',
|
||||||
|
'in_days': 'Tra {} giorni',
|
||||||
|
'settings': 'Configurazione',
|
||||||
|
'save': 'Salva',
|
||||||
|
'available_breeds': 'Razze disponibili',
|
||||||
|
'my_coop': 'Il mio pollaio',
|
||||||
|
'no_breeds_sel': 'Nessuna razza selezionata.\\nVai in "Razze disponibili" per attivare quelle che allevi.',
|
||||||
|
'reg_hens': 'Galline registrate',
|
||||||
|
'in_laying': 'In deposizione',
|
||||||
|
'est_prod': 'Produzione stimata',
|
||||||
|
'manage_hens': 'Gestisci anagrafica galline',
|
||||||
|
'breed_sheet': 'Scheda {}',
|
||||||
|
'new_hen': 'Nuova gallina',
|
||||||
|
'egg_color': 'Colore uova',
|
||||||
|
'laying_6_months': 'Galline in deposizione (6+ mesi)',
|
||||||
|
'hen_registry': 'Anagrafica galline',
|
||||||
|
'no_hens_reg': 'Nessuna gallina registrata per questa razza.',
|
||||||
|
'birth': 'Nascita',
|
||||||
|
'age_months': 'Età: {} mesi',
|
||||||
|
'status_laying': 'Stato: in deposizione',
|
||||||
|
'status_rest': 'Stato: a riposo',
|
||||||
|
'status_young': 'Stato: troppo giovane per deporre',
|
||||||
|
'hen_name': 'Nome gallina',
|
||||||
|
'birth_date': 'Data di nascita',
|
||||||
|
'select_date': 'Seleziona data',
|
||||||
|
'cancel': 'Annulla',
|
||||||
|
'customer_name': 'Nome cliente',
|
||||||
|
'need_hen_error': 'Per creare un ordine serve almeno una gallina di 6 mesi o più in una delle razze configurate.',
|
||||||
|
'delivery_preview': 'Anteprima consegna',
|
||||||
|
'save_order': 'Salva ordine',
|
||||||
|
'language': 'Lingua',
|
||||||
|
'config_breed_first': 'Configura prima almeno una razza nelle impostazioni.',
|
||||||
|
'name_marans': 'Marans',
|
||||||
|
'name_araucana': 'Araucana',
|
||||||
|
'name_olive': 'Olive Egger',
|
||||||
|
'name_livorno': 'Livorno',
|
||||||
|
'name_sussex': 'Sussex',
|
||||||
|
'name_australorp': 'Australorp',
|
||||||
|
'name_generic': 'Ovaiola generica',
|
||||||
|
'desc_marans': 'Famosa per le sue uova color cioccolato fondente.',
|
||||||
|
'desc_araucana': 'Uccello senza coda che depone uova azzurro-verdi.',
|
||||||
|
'desc_olive': 'Incrocio che produce uova di un bellissimo verde oliva.',
|
||||||
|
'desc_livorno': 'Eccellente produttrice di uova bianche, snella e vivace.',
|
||||||
|
'desc_sussex': 'Razza versatile e docile, depone uova color crema.',
|
||||||
|
'desc_australorp': 'Detentrice del record mondiale di deposizione, uova marroni chiare.',
|
||||||
|
'desc_generic': 'Gallina ovaiola di razza mista o non specificata.',
|
||||||
|
'color_dark_brown': 'Marrone scuro',
|
||||||
|
'color_blue': 'Azzurro',
|
||||||
|
'color_olive': 'Verde Oliva',
|
||||||
|
'color_white': 'Bianco',
|
||||||
|
'color_cream': 'Crema',
|
||||||
|
'color_light_brown': 'Marrone chiaro',
|
||||||
|
'confirm_delete': 'Conferma eliminazione',
|
||||||
|
'delete_msg': 'Sei sicuro di voler eliminare questo elemento?',
|
||||||
|
'delete': 'Elimina',
|
||||||
|
'backup_restore': 'Backup e Ripristino',
|
||||||
|
'export_data': 'Esporta dati',
|
||||||
|
'import_data': 'Importa dati',
|
||||||
|
'copy_to_clipboard': 'Copia negli appunti',
|
||||||
|
'copied': 'Copiato!',
|
||||||
|
'paste_json_here': 'Incolla qui il JSON di backup',
|
||||||
|
'import_success': 'Dati importati con successo!',
|
||||||
|
'import_error': 'Errore durante l\'importazione. Controlla il formato.',
|
||||||
|
'confirm_deactivation': 'Conferma disattivazione',
|
||||||
|
'deactivation_msg': 'Disattivando questa razza, tutte le galline registrate verranno eliminate definitivamente. Vuoi procedere?',
|
||||||
|
'deactivate': 'Disattiva',
|
||||||
|
'new_customer': 'Nuovo cliente',
|
||||||
|
'first_name': 'Nome',
|
||||||
|
'last_name': 'Cognome',
|
||||||
|
'phone_number': 'Numero di telefono',
|
||||||
|
'save_customer': 'Salva cliente',
|
||||||
|
'customers_list': 'Elenco clienti',
|
||||||
|
'edit_customer': 'Modifica cliente',
|
||||||
|
'no_customers': 'Nessun cliente registrato.',
|
||||||
|
'share_order_msg': 'Ciao {}, il tuo ordine di {} uova di razza {} è previsto per il {}.',
|
||||||
|
'welcome': 'Benvenuto in Henflow!',
|
||||||
|
'intro_step1_title': '1. Seleziona le razze',
|
||||||
|
'intro_step1_desc': 'Vai nelle impostazioni e attiva le razze che allevi nel tuo pollaio.',
|
||||||
|
'intro_step2_title': '2. Aggiungi le galline',
|
||||||
|
'intro_step2_desc': 'Per ogni razza attiva, inserisci le tue galline. Solo quelle con più di 6 mesi contribuiscono alla produzione.',
|
||||||
|
'intro_step3_title': '3. Anagrafica clienti',
|
||||||
|
'intro_step3_desc': 'Registra i tuoi clienti per velocizzare la creazione degli ordini.',
|
||||||
|
'intro_step4_title': '4. Gestisci gli ordini',
|
||||||
|
'intro_step4_desc': 'Aggiungi le prenotazioni. Eggflow calcolerà automaticamente la data di consegna in base alla produzione.',
|
||||||
|
'start_app': 'Inizia ora',
|
||||||
|
'show_intro': 'Mostra introduzione',
|
||||||
|
'about': 'Informazioni',
|
||||||
|
'about_desc': 'Henflow è la tua soluzione completa per la gestione professionale di un piccolo pollaio. \\n\\nCon questa app puoi monitorare la produzione giornaliera delle tue galline, gestire un\'anagrafica clienti dettagliata e organizzare le prenotazioni delle uova con ricalcoli automatici delle date di consegna. \\n\\nProgettata per rendere semplice ed efficiente l\'attività di ogni allevatore.',
|
||||||
|
'calendar': 'Calendario',
|
||||||
|
'orders_list': 'Elenco ordini',
|
||||||
|
'warehouse': 'Magazzino',
|
||||||
|
'eggs_in_stock': 'Uova in magazzino',
|
||||||
|
'edit_stock': 'Modifica giacenza',
|
||||||
|
'last_update': 'Ultimo aggiornamento',
|
||||||
|
},
|
||||||
|
AppLanguage.en: {
|
||||||
|
'app_title': 'Henflow',
|
||||||
|
'daily_prod_est': 'Estimated daily production',
|
||||||
|
'eggs_per_day': 'eggs/day',
|
||||||
|
'no_breeds': 'No breeds configured.',
|
||||||
|
'hens': 'hens',
|
||||||
|
'eggs_per_g': 'eggs/d',
|
||||||
|
'reservations': 'Reservations',
|
||||||
|
'no_reservations': 'No reservations entered.',
|
||||||
|
'use_button': 'Use the "New order" button to register a customer.',
|
||||||
|
'new_order': 'New order',
|
||||||
|
'edit_order': 'Edit order',
|
||||||
|
'breed': 'Breed',
|
||||||
|
'eggs_req': 'Eggs requested',
|
||||||
|
'delivery': 'Delivery',
|
||||||
|
'today': 'Today',
|
||||||
|
'ready_delivery': 'Ready for delivery',
|
||||||
|
'expired': 'Expired',
|
||||||
|
'in_day': 'In 1 day',
|
||||||
|
'in_days': 'In {} days',
|
||||||
|
'settings': 'Configuration',
|
||||||
|
'save': 'Save',
|
||||||
|
'available_breeds': 'Available breeds',
|
||||||
|
'my_coop': 'My coop',
|
||||||
|
'no_breeds_sel': 'No breeds selected.\\nGo to "Available breeds" to activate the ones you raise.',
|
||||||
|
'reg_hens': 'Registered hens',
|
||||||
|
'in_laying': 'Laying',
|
||||||
|
'est_prod': 'Estimated production',
|
||||||
|
'manage_hens': 'Manage hen records',
|
||||||
|
'breed_sheet': '{} sheet',
|
||||||
|
'new_hen': 'New hen',
|
||||||
|
'egg_color': 'Egg color',
|
||||||
|
'laying_6_months': 'Laying hens (6+ months)',
|
||||||
|
'hen_registry': 'Hen registry',
|
||||||
|
'no_hens_reg': 'No hens registered for this breed.',
|
||||||
|
'birth': 'Birth',
|
||||||
|
'age_months': 'Age: {} months',
|
||||||
|
'status_laying': 'Status: laying',
|
||||||
|
'status_rest': 'Status: resting',
|
||||||
|
'status_young': 'Status: too young to lay',
|
||||||
|
'hen_name': 'Hen name',
|
||||||
|
'birth_date': 'Birth date',
|
||||||
|
'select_date': 'Select date',
|
||||||
|
'cancel': 'Cancel',
|
||||||
|
'customer_name': 'Customer name',
|
||||||
|
'need_hen_error': 'To create an order you need at least one hen 6 months or older in one of the configured breeds.',
|
||||||
|
'delivery_preview': 'Delivery preview',
|
||||||
|
'save_order': 'Save order',
|
||||||
|
'language': 'Language',
|
||||||
|
'config_breed_first': 'Configure at least one breed in settings first.',
|
||||||
|
'name_marans': 'Marans',
|
||||||
|
'name_araucana': 'Araucana',
|
||||||
|
'name_olive': 'Olive Egger',
|
||||||
|
'name_livorno': 'Leghorn',
|
||||||
|
'name_sussex': 'Sussex',
|
||||||
|
'name_australorp': 'Australorp',
|
||||||
|
'name_generic': 'Generic Layer',
|
||||||
|
'desc_marans': 'Famous for its dark chocolate-colored eggs.',
|
||||||
|
'desc_araucana': 'Rumpless bird that lays blue-green eggs.',
|
||||||
|
'desc_olive': 'Hybrid that produces beautiful olive green eggs.',
|
||||||
|
'desc_livorno': 'Excellent producer of white eggs, slender and active.',
|
||||||
|
'desc_sussex': 'Versatile and docile breed, lays cream-colored eggs.',
|
||||||
|
'desc_australorp': 'World record holder for egg laying, light brown eggs.',
|
||||||
|
'desc_generic': 'Laying hen of mixed or unspecified breed.',
|
||||||
|
'color_dark_brown': 'Dark brown',
|
||||||
|
'color_blue': 'Blue',
|
||||||
|
'color_olive': 'Olive',
|
||||||
|
'color_white': 'White',
|
||||||
|
'color_cream': 'Cream',
|
||||||
|
'color_light_brown': 'Light brown',
|
||||||
|
'confirm_delete': 'Confirm deletion',
|
||||||
|
'delete_msg': 'Are you sure you want to delete this item?',
|
||||||
|
'delete': 'Delete',
|
||||||
|
'backup_restore': 'Backup & Restore',
|
||||||
|
'export_data': 'Export data',
|
||||||
|
'import_data': 'Import data',
|
||||||
|
'copy_to_clipboard': 'Copy to clipboard',
|
||||||
|
'copied': 'Copied!',
|
||||||
|
'paste_json_here': 'Paste backup JSON here',
|
||||||
|
'import_success': 'Data imported successfully!',
|
||||||
|
'import_error': 'Import error. Check the format.',
|
||||||
|
'confirm_deactivation': 'Confirm deactivation',
|
||||||
|
'deactivation_msg': 'By deactivating this breed, all registered hens will be permanently deleted. Do you want to proceed?',
|
||||||
|
'deactivate': 'Deactivate',
|
||||||
|
'new_customer': 'New customer',
|
||||||
|
'first_name': 'First name',
|
||||||
|
'last_name': 'Last name',
|
||||||
|
'phone_number': 'Phone number',
|
||||||
|
'save_customer': 'Save customer',
|
||||||
|
'customers_list': 'Customers list',
|
||||||
|
'edit_customer': 'Edit customer',
|
||||||
|
'no_customers': 'No customers registered.',
|
||||||
|
'share_order_msg': 'Hi {}, your order for {} eggs of breed {} is scheduled for {}.',
|
||||||
|
'welcome': 'Welcome to Henflow!',
|
||||||
|
'intro_step1_title': '1. Select breeds',
|
||||||
|
'intro_step1_desc': 'Go to settings and activate the breeds you raise in your coop.',
|
||||||
|
'intro_step2_title': '2. Add hens',
|
||||||
|
'intro_step2_desc': 'For each active breed, enter your hens. Only those older than 6 months contribute to production.',
|
||||||
|
'intro_step3_title': '3. Customer records',
|
||||||
|
'intro_step3_desc': 'Register your customers to speed up order creation.',
|
||||||
|
'intro_step4_title': '4. Manage orders',
|
||||||
|
'intro_step4_desc': 'Add bookings. Eggflow will automatically calculate the delivery date based on production.',
|
||||||
|
'start_app': 'Get started',
|
||||||
|
'show_intro': 'Show introduction',
|
||||||
|
'about': 'About',
|
||||||
|
'about_desc': 'Henflow is your complete solution for professional management of a small poultry farm. \\n\\nWith this app, you can monitor the daily production of your hens, manage detailed customer records, and organize egg bookings with automatic recalculations of delivery dates. \\n\\nDesigned to make every breeder\'s activity simple and efficient.',
|
||||||
|
'calendar': 'Calendar',
|
||||||
|
'orders_list': 'Orders list',
|
||||||
|
'warehouse': 'Warehouse',
|
||||||
|
'eggs_in_stock': 'Eggs in stock',
|
||||||
|
'edit_stock': 'Edit stock',
|
||||||
|
'last_update': 'Last update',
|
||||||
|
},
|
||||||
|
AppLanguage.fr: {
|
||||||
|
'app_title': 'Henflow',
|
||||||
|
'daily_prod_est': 'Production quotidienne estimée',
|
||||||
|
'eggs_per_day': 'œufs/jour',
|
||||||
|
'no_breeds': 'Aucune race configurée.',
|
||||||
|
'hens': 'poules',
|
||||||
|
'eggs_per_g': 'œufs/j',
|
||||||
|
'reservations': 'Réservations',
|
||||||
|
'no_reservations': 'Aucune réservation saisie.',
|
||||||
|
'use_button': 'Utilisez le bouton "Nouvelle commande" pour enregistrer un client.',
|
||||||
|
'new_order': 'Nouvelle commande',
|
||||||
|
'edit_order': 'Modifier la commande',
|
||||||
|
'breed': 'Race',
|
||||||
|
'eggs_req': 'Œufs demandés',
|
||||||
|
'delivery': 'Livraison',
|
||||||
|
'today': 'Aujourd\'hui',
|
||||||
|
'ready_delivery': 'Prêt per la consegna',
|
||||||
|
'expired': 'Expiré',
|
||||||
|
'in_day': 'Dans 1 jour',
|
||||||
|
'in_days': 'Dans {} jours',
|
||||||
|
'settings': 'Configuration',
|
||||||
|
'save': 'Sauvegarder',
|
||||||
|
'available_breeds': 'Races disponibles',
|
||||||
|
'my_coop': 'Mon poulailler',
|
||||||
|
'no_breeds_sel': 'Aucune race sélectionnée.\\nAllez dans "Races disponibles" pour activer celles que vous élevez.',
|
||||||
|
'reg_hens': 'Poules enregistrées',
|
||||||
|
'in_laying': 'En ponte',
|
||||||
|
'est_prod': 'Production estimée',
|
||||||
|
'manage_hens': 'Gérer les fiches des poules',
|
||||||
|
'breed_sheet': 'Fiche {}',
|
||||||
|
'new_hen': 'Nouvelle poule',
|
||||||
|
'egg_color': 'Couleur des œufs',
|
||||||
|
'laying_6_months': 'Poules en ponte (6+ mois)',
|
||||||
|
'hen_registry': 'Registre des poules',
|
||||||
|
'no_hens_reg': 'Aucune poule enregistrée pour cette race.',
|
||||||
|
'birth': 'Naissance',
|
||||||
|
'age_months': 'Âge: {} mois',
|
||||||
|
'status_laying': 'Statut: en ponte',
|
||||||
|
'status_rest': 'Statut: au repos',
|
||||||
|
'status_young': 'Statut: trop jeune pour pondre',
|
||||||
|
'hen_name': 'Nom de la poule',
|
||||||
|
'birth_date': 'Date de naissance',
|
||||||
|
'select_date': 'Choisir une date',
|
||||||
|
'cancel': 'Annuler',
|
||||||
|
'customer_name': 'Nom du client',
|
||||||
|
'need_hen_error': 'Pour créer une commande, vous avez besoin d\'au moins une poule de 6 mois ou plus dans l\'une des races configurées.',
|
||||||
|
'delivery_preview': 'Aperçu de la livraison',
|
||||||
|
'save_order': 'Sauvegarder la commande',
|
||||||
|
'language': 'Langue',
|
||||||
|
'config_breed_first': 'Configurez d\'abord au moins une race dans les paramètres.',
|
||||||
|
'name_marans': 'Marans',
|
||||||
|
'name_araucana': 'Araucana',
|
||||||
|
'name_olive': 'Olive Egger',
|
||||||
|
'name_livorno': 'Livourne',
|
||||||
|
'name_sussex': 'Sussex',
|
||||||
|
'name_australorp': 'Australorp',
|
||||||
|
'name_generic': 'Pondeuse générique',
|
||||||
|
'desc_marans': 'Célèbre pour ses œufs couleur chocolat noir.',
|
||||||
|
'desc_araucana': 'Oiseau sans queue qui pond des œufs bleu-vert.',
|
||||||
|
'desc_olive': 'Croisement qui produit de magnifiques œufs vert olive.',
|
||||||
|
'desc_livorno': 'Excellente pondeuse d\'œufs blancs, svelte et vive.',
|
||||||
|
'desc_sussex': 'Race polyvalente et docile, pond des œufs de couleur crème.',
|
||||||
|
'desc_australorp': 'Détentrice du record mondial de ponte, œufs brun clair.',
|
||||||
|
'desc_generic': 'Poule pondeuse de race mixte ou non spécifiée.',
|
||||||
|
'color_dark_brown': 'Brun foncé',
|
||||||
|
'color_blue': 'Bleu',
|
||||||
|
'color_olive': 'Olive',
|
||||||
|
'color_white': 'Blanc',
|
||||||
|
'color_cream': 'Crème',
|
||||||
|
'color_light_brown': 'Brun clair',
|
||||||
|
'confirm_delete': 'Confirmer la suppression',
|
||||||
|
'delete_msg': 'Êtes-vous sûr de vouloir supprimer cet élément ?',
|
||||||
|
'delete': 'Supprimer',
|
||||||
|
'backup_restore': 'Sauvegarde et Restauration',
|
||||||
|
'export_data': 'Exporter les données',
|
||||||
|
'import_data': 'Importer des données',
|
||||||
|
'copy_to_clipboard': 'Copier dans le presse-papiers',
|
||||||
|
'copied': 'Copié !',
|
||||||
|
'paste_json_here': 'Collez le JSON de sauvegarde ici',
|
||||||
|
'import_success': 'Données importées avec succès !',
|
||||||
|
'import_error': 'Erreur d\'importation. Vérifiez le format.',
|
||||||
|
'confirm_deactivation': 'Confirmer la désactivation',
|
||||||
|
'deactivation_msg': 'En désactivant cette race, tutte le galline registrate verranno eliminate definitivamente. Vuoi procedere?',
|
||||||
|
'deactivate': 'Désactiver',
|
||||||
|
'new_customer': 'Nouveau client',
|
||||||
|
'first_name': 'Prénom',
|
||||||
|
'last_name': 'Nom',
|
||||||
|
'phone_number': 'Numéro de téléphone',
|
||||||
|
'save_customer': 'Sauvegarder le client',
|
||||||
|
'customers_list': 'Liste des clients',
|
||||||
|
'edit_customer': 'Modifier le client',
|
||||||
|
'no_customers': 'Aucun client enregistré.',
|
||||||
|
'share_order_msg': 'Bonjour {}, votre commande de {} œufs de race {} est prévue pour le {}.',
|
||||||
|
'welcome': 'Bienvenue sur Henflow !',
|
||||||
|
'intro_step1_title': '1. Sélectionnez les races',
|
||||||
|
'intro_step1_desc': 'Allez dans les paramètres et activez les races que vous élevez dans votre poulailler.',
|
||||||
|
'intro_step2_title': '2. Ajoutez les poules',
|
||||||
|
'intro_step2_desc': 'Pour chaque race active, saisissez vos poules. Seules celles de plus de 6 mois contribuent à la production.',
|
||||||
|
'intro_step3_title': '3. Fiches clients',
|
||||||
|
'intro_step3_desc': 'Enregistrez vos clients pour accélérer la création de commandes.',
|
||||||
|
'intro_step4_title': '4. Gérez les commandes',
|
||||||
|
'intro_step4_desc': 'Ajoutez des réservations. Eggflow calculera automaticamente la date de livraison en fonction de la production.',
|
||||||
|
'start_app': 'Commencer',
|
||||||
|
'show_intro': 'Afficher l\'introduction',
|
||||||
|
'about': 'À propos',
|
||||||
|
'about_desc': 'Henflow est votre solution complète per la gestione professionale di un piccolo pollaio. \\n\\nAvec cette application, vous pouvez surveiller la production quotidienne de vos poules, gestire un\'anagrafica clienti dettagliata e organizzare le prenotazioni delle uova con ricalcoli automatici delle date di consegna. \\n\\nConçue per rendere simple ed efficiente l\'attività di ogni allevatore.',
|
||||||
|
'calendar': 'Calendrier',
|
||||||
|
'orders_list': 'Liste des commandes',
|
||||||
|
'warehouse': 'Entrepôt',
|
||||||
|
'eggs_in_stock': 'Œufs in stock',
|
||||||
|
'edit_stock': 'Modifier le stock',
|
||||||
|
'last_update': 'Dernière mise à jour',
|
||||||
|
},
|
||||||
|
AppLanguage.de: {
|
||||||
|
'app_title': 'Henflow',
|
||||||
|
'daily_prod_est': 'Geschätzte tägliche Produktion',
|
||||||
|
'eggs_per_day': 'Eier/Tag',
|
||||||
|
'no_breeds': 'Keine Rassen konfiguriert.',
|
||||||
|
'hens': 'Hennen',
|
||||||
|
'eggs_per_g': 'Eier/Tag',
|
||||||
|
'reservations': 'Reservierungen',
|
||||||
|
'no_reservations': 'Keine Reservierungen eingetragen.',
|
||||||
|
'use_button': 'Verwenden Sie die Schaltfläche "Neue Bestellung", um einen Kunden zu registrieren.',
|
||||||
|
'new_order': 'Neue Bestellung',
|
||||||
|
'edit_order': 'Bestellung bearbeiten',
|
||||||
|
'breed': 'Rasse',
|
||||||
|
'eggs_req': 'Anzahl Eier',
|
||||||
|
'delivery': 'Lieferung',
|
||||||
|
'today': 'Heute',
|
||||||
|
'ready_delivery': 'Bereit zur Auslieferung',
|
||||||
|
'expired': 'Abgelaufen',
|
||||||
|
'in_day': 'In 1 Tag',
|
||||||
|
'in_days': 'In {} Tagen',
|
||||||
|
'settings': 'Konfiguration',
|
||||||
|
'save': 'Speichern',
|
||||||
|
'available_breeds': 'Verfügbare Rassen',
|
||||||
|
'my_coop': 'Mein Hühnerstall',
|
||||||
|
'no_breeds_sel': 'Keine Rassen ausgewählt.\\nGehen Sie zu "Verfügbare Rassen", um die von Ihnen gezüchteten zu aktivieren.',
|
||||||
|
'reg_hens': 'Registrierte Hennen',
|
||||||
|
'in_laying': 'In Legephase',
|
||||||
|
'est_prod': 'Geschätzte Produktion',
|
||||||
|
'manage_hens': 'Hühnerbestand verwalten',
|
||||||
|
'breed_sheet': 'Datenblatt {}',
|
||||||
|
'new_hen': 'Neue Henne',
|
||||||
|
'egg_color': 'Eierfarbe',
|
||||||
|
'laying_6_months': 'Legehennen (6+ Monate)',
|
||||||
|
'hen_registry': 'Hühnerregister',
|
||||||
|
'no_hens_reg': 'Keine Hennen für diese Rasse registriert.',
|
||||||
|
'birth': 'Geburt',
|
||||||
|
'age_months': 'Alter: {} Monate',
|
||||||
|
'status_laying': 'Status: legend',
|
||||||
|
'status_rest': 'Status: ruhend',
|
||||||
|
'status_young': 'Status: zu jung zum Legen',
|
||||||
|
'hen_name': 'Name der Henne',
|
||||||
|
'birth_date': 'Geburtsdatum',
|
||||||
|
'select_date': 'Datum auswählen',
|
||||||
|
'cancel': 'Abbrechen',
|
||||||
|
'customer_name': 'Kundenname',
|
||||||
|
'need_hen_error': 'Um eine Bestellung aufzugeben, benötigen Sie mindestens eine Henne, die 6 Monate oder älter ist, in einer der konfigurierten Rassen.',
|
||||||
|
'delivery_preview': 'Liefervorschau',
|
||||||
|
'save_order': 'Bestellung speichern',
|
||||||
|
'language': 'Sprache',
|
||||||
|
'config_breed_first': 'Konfigurieren Sie zuerst mindestens eine Rasse in den Einstellungen.',
|
||||||
|
'name_marans': 'Marans',
|
||||||
|
'name_araucana': 'Araucana',
|
||||||
|
'name_olive': 'Olive Egger',
|
||||||
|
'name_livorno': 'Leghorn',
|
||||||
|
'name_sussex': 'Sussex',
|
||||||
|
'name_australorp': 'Australorp',
|
||||||
|
'name_generic': 'Generische Leghenne',
|
||||||
|
'desc_marans': 'Berühmt für ihre dunklen, schokoladenbraunen Eier.',
|
||||||
|
'desc_araucana': 'Schwanzloses Huhn, das blau-grüne Eier legt.',
|
||||||
|
'desc_olive': 'Kreuzung, die wunderschöne olivgrüne Eier hervorbringt.',
|
||||||
|
'desc_livorno': 'Hervorragende Legerin von weißen Eiern, schlank und lebhaft.',
|
||||||
|
'desc_sussex': 'Vielseitige und sanftmütige Rasse, legt cremefarbene Eier.',
|
||||||
|
'desc_australorp': 'Weltrekordhalterin im Eierlegen, hellbraune Eier.',
|
||||||
|
'desc_generic': 'Legehenne gemischter oder nicht spezifizierter Rasse.',
|
||||||
|
'color_dark_brown': 'Dunkelbraun',
|
||||||
|
'color_blue': 'Blau',
|
||||||
|
'color_olive': 'Olivgrün',
|
||||||
|
'color_white': 'Weiß',
|
||||||
|
'color_cream': 'Creme',
|
||||||
|
'color_light_brown': 'Hellbraun',
|
||||||
|
'confirm_delete': 'Löschen bestätigen',
|
||||||
|
'delete_msg': 'Sind Sie sicher, dass Sie dieses Element löschen möchten?',
|
||||||
|
'delete': 'Löschen',
|
||||||
|
'backup_restore': 'Backup & Wiederherstellung',
|
||||||
|
'export_data': 'Daten exportieren',
|
||||||
|
'import_data': 'Daten importieren',
|
||||||
|
'copy_to_clipboard': 'In die Zwischenablage kopieren',
|
||||||
|
'copied': 'Kopiert!',
|
||||||
|
'paste_json_here': 'Backup-JSON hier einfügen',
|
||||||
|
'import_success': 'Daten erfolgreich importiert!',
|
||||||
|
'import_error': 'Fehler beim Importieren. Überprüfen Sie das Format.',
|
||||||
|
'confirm_deactivation': 'Deaktivierung bestätigen',
|
||||||
|
'deactivation_msg': 'Durch Deaktivieren dieser Rasse werden alle registrierten Hennen dauerhaft gelöscht. Möchten Sie fortfahren?',
|
||||||
|
'deactivate': 'Deaktivieren',
|
||||||
|
'new_customer': 'Neuer Kunde',
|
||||||
|
'first_name': 'Vorname',
|
||||||
|
'last_name': 'Nachname',
|
||||||
|
'phone_number': 'Telefonnummer',
|
||||||
|
'save_customer': 'Kunde speichern',
|
||||||
|
'customers_list': 'Kundenliste',
|
||||||
|
'edit_customer': 'Kunde bearbeiten',
|
||||||
|
'no_customers': 'Keine Kunden registriert.',
|
||||||
|
'share_order_msg': 'Hallo {}, Ihre Bestellung über {} Eier der Rasse {} ist für den {} geplant.',
|
||||||
|
'welcome': 'Willkommen bei Henflow!',
|
||||||
|
'intro_step1_title': '1. Rassen auswählen',
|
||||||
|
'intro_step1_desc': 'Gehen Sie zu den Einstellungen und aktivieren Sie die Rassen, die Sie in Ihrem Stall halten.',
|
||||||
|
'intro_step2_title': '2. Hennen hinzufügen',
|
||||||
|
'intro_step2_desc': 'Geben Sie für jede aktive Rasse Ihre Hennen ein. Nur Hennen, die älter als 6 Monate sind, tragen zur Produktion bei.',
|
||||||
|
'intro_step3_title': '3. Kundenkartei',
|
||||||
|
'intro_step3_desc': 'Registrieren Sie Ihre Kunden, um die Bestellerstellung zu beschleunigen.',
|
||||||
|
'intro_step4_title': '4. Bestellungen verwalten',
|
||||||
|
'intro_step4_desc': 'Fügen Sie Buchungen hinzu. Eggflow berechnet automatisch das Lieferdatum basierend auf der Produktion.',
|
||||||
|
'start_app': 'Jetzt starten',
|
||||||
|
'show_intro': 'Einführung anzeigen',
|
||||||
|
'about': 'Über uns',
|
||||||
|
'about_desc': 'Henflow ist Ihre Komplettlösung für die professionelle Verwaltung eines kleinen Hühnerstalls. \\n\\nMit dieser App können Sie die tägliche Produktion Ihrer Hennen überwachen, eine detaillierte Kundenkartei verwalten und Eierreservierungen mit automatischen Neuberechnungen der Liefertermine organisieren. \\n\\nEntwickelt, um die Tätigkeit jedes Züchters einfach und effizient zu gestalten.',
|
||||||
|
'calendar': 'Kalender',
|
||||||
|
'orders_list': 'Bestellliste',
|
||||||
|
'warehouse': 'Lager',
|
||||||
|
'eggs_in_stock': 'Eier auf Lager',
|
||||||
|
'edit_stock': 'Bestand bearbeiten',
|
||||||
|
'last_update': 'Letzte Aktualisierung',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static String t(String key, {dynamic arg}) {
|
||||||
|
String text = _data[_currentLanguage]?[key] ?? key;
|
||||||
|
if (arg != null) {
|
||||||
|
if (arg is List) {
|
||||||
|
for (var a in arg) {
|
||||||
|
text = text.replaceFirst('{}', a.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text = text.replaceFirst('{}', arg.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import 'package:path/path.dart';
|
||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
|
class DatabaseHelper {
|
||||||
|
static final DatabaseHelper instance = DatabaseHelper._init();
|
||||||
|
static Database? _database;
|
||||||
|
|
||||||
|
DatabaseHelper._init();
|
||||||
|
|
||||||
|
Future<Database> get database async {
|
||||||
|
if (_database != null) return _database!;
|
||||||
|
_database = await _initDB('egg_bookings.db');
|
||||||
|
return _database!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Database> _initDB(String filePath) async {
|
||||||
|
final dbPath = await getDatabasesPath();
|
||||||
|
final path = join(dbPath, filePath);
|
||||||
|
|
||||||
|
return openDatabase(
|
||||||
|
path,
|
||||||
|
version: 1,
|
||||||
|
onCreate: _createDB,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _createDB(Database db, int version) async {
|
||||||
|
await db.execute('''
|
||||||
|
CREATE TABLE bookings (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
date TEXT NOT NULL,
|
||||||
|
customer_name TEXT NOT NULL,
|
||||||
|
marans INTEGER NOT NULL DEFAULT 0,
|
||||||
|
araucana INTEGER NOT NULL DEFAULT 0,
|
||||||
|
olive INTEGER NOT NULL DEFAULT 0,
|
||||||
|
note TEXT NOT NULL DEFAULT ''
|
||||||
|
)
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Map<String, dynamic>>> getAllBookings() async {
|
||||||
|
final db = await instance.database;
|
||||||
|
return db.query(
|
||||||
|
'bookings',
|
||||||
|
orderBy: 'date ASC, customer_name ASC',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> insertBooking(Map<String, dynamic> booking) async {
|
||||||
|
final db = await instance.database;
|
||||||
|
return db.insert('bookings', booking);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> updateBooking(Map<String, dynamic> booking) async {
|
||||||
|
final db = await instance.database;
|
||||||
|
return db.update(
|
||||||
|
'bookings',
|
||||||
|
booking,
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [booking['id']],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> deleteBooking(int id) async {
|
||||||
|
final db = await instance.database;
|
||||||
|
return db.delete(
|
||||||
|
'bookings',
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [id],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> close() async {
|
||||||
|
final db = await instance.database;
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../app_language.dart';
|
||||||
|
|
||||||
|
Future<bool> showDeactivateDialog(BuildContext context) async {
|
||||||
|
final t = TranslationService.t;
|
||||||
|
return await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog(
|
||||||
|
title: Text(t('confirm_deactivation')),
|
||||||
|
content: Text(t('deactivation_msg')),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text(t('cancel'))),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(ctx, true),
|
||||||
|
child: Text(t('deactivate'), style: const TextStyle(color: Colors.red)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> showConfirmDialog(BuildContext context) async {
|
||||||
|
final t = TranslationService.t;
|
||||||
|
return await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog(
|
||||||
|
title: Text(t('confirm_delete')),
|
||||||
|
content: Text(t('delete_msg')),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text(t('cancel'))),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(ctx, true),
|
||||||
|
child: Text(t('delete'), style: const TextStyle(color: Colors.red)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
) ?? false;
|
||||||
|
}
|
||||||
+2986
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,83 @@
|
|||||||
|
class BreedCatalogItem {
|
||||||
|
final String id;
|
||||||
|
final String name;
|
||||||
|
final String description;
|
||||||
|
final String eggColor;
|
||||||
|
final double eggsPerHenPerDay;
|
||||||
|
final String assetPath;
|
||||||
|
|
||||||
|
const BreedCatalogItem({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required this.eggColor,
|
||||||
|
required this.eggsPerHenPerDay,
|
||||||
|
required this.assetPath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const List<BreedCatalogItem> breedCatalog = [
|
||||||
|
BreedCatalogItem(
|
||||||
|
id: 'marans',
|
||||||
|
name: 'name_marans',
|
||||||
|
description: 'desc_marans',
|
||||||
|
eggColor: 'color_dark_brown',
|
||||||
|
eggsPerHenPerDay: 1.0,
|
||||||
|
assetPath: 'assets/breeds/marans.jpg',
|
||||||
|
),
|
||||||
|
BreedCatalogItem(
|
||||||
|
id: 'araucana',
|
||||||
|
name: 'name_araucana',
|
||||||
|
description: 'desc_araucana',
|
||||||
|
eggColor: 'color_blue',
|
||||||
|
eggsPerHenPerDay: 1.0,
|
||||||
|
assetPath: 'assets/breeds/araucana.jpg',
|
||||||
|
),
|
||||||
|
BreedCatalogItem(
|
||||||
|
id: 'olive',
|
||||||
|
name: 'name_olive',
|
||||||
|
description: 'desc_olive',
|
||||||
|
eggColor: 'color_olive',
|
||||||
|
eggsPerHenPerDay: 1.0,
|
||||||
|
assetPath: 'assets/breeds/olive.jpg',
|
||||||
|
),
|
||||||
|
BreedCatalogItem(
|
||||||
|
id: 'livorno',
|
||||||
|
name: 'name_livorno',
|
||||||
|
description: 'desc_livorno',
|
||||||
|
eggColor: 'color_white',
|
||||||
|
eggsPerHenPerDay: 1.0,
|
||||||
|
assetPath: 'assets/breeds/livorno.jpg',
|
||||||
|
),
|
||||||
|
BreedCatalogItem(
|
||||||
|
id: 'sussex',
|
||||||
|
name: 'name_sussex',
|
||||||
|
description: 'desc_sussex',
|
||||||
|
eggColor: 'color_cream',
|
||||||
|
eggsPerHenPerDay: 1.0,
|
||||||
|
assetPath: 'assets/breeds/sussex.jpg',
|
||||||
|
),
|
||||||
|
BreedCatalogItem(
|
||||||
|
id: 'australorp',
|
||||||
|
name: 'name_australorp',
|
||||||
|
description: 'desc_australorp',
|
||||||
|
eggColor: 'color_light_brown',
|
||||||
|
eggsPerHenPerDay: 1.0,
|
||||||
|
assetPath: 'assets/breeds/australorp.jpg',
|
||||||
|
),
|
||||||
|
BreedCatalogItem(
|
||||||
|
id: 'generic',
|
||||||
|
name: 'name_generic',
|
||||||
|
description: 'desc_generic',
|
||||||
|
eggColor: 'color_light_brown',
|
||||||
|
eggsPerHenPerDay: 1.0,
|
||||||
|
assetPath: 'assets/breeds/generic.jpg',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
BreedCatalogItem breedFromId(String id) {
|
||||||
|
return breedCatalog.firstWhere(
|
||||||
|
(e) => e.id == id,
|
||||||
|
orElse: () => breedCatalog.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import 'breed_catalog.dart';
|
||||||
|
import 'hen.dart';
|
||||||
|
|
||||||
|
class BreedSetting {
|
||||||
|
final String breedId;
|
||||||
|
List<Hen> hens;
|
||||||
|
|
||||||
|
BreedSetting({
|
||||||
|
required this.breedId,
|
||||||
|
required this.hens,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'breedId': breedId,
|
||||||
|
'hens': hens.map((e) => e.toMap()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory BreedSetting.fromMap(Map<String, dynamic> map) {
|
||||||
|
return BreedSetting(
|
||||||
|
breedId: map['breedId'],
|
||||||
|
hens: ((map['hens'] ?? []) as List)
|
||||||
|
.map((e) => Hen.fromMap(Map<String, dynamic>.from(e)))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get activeHensCount {
|
||||||
|
return hens.where((hen) => hen.isLaying).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get dailyProduction =>
|
||||||
|
activeHensCount * breedFromId(breedId).eggsPerHenPerDay;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
class Customer {
|
||||||
|
final String id;
|
||||||
|
final String firstName;
|
||||||
|
final String lastName;
|
||||||
|
final String phoneNumber;
|
||||||
|
|
||||||
|
Customer({
|
||||||
|
required this.id,
|
||||||
|
required this.firstName,
|
||||||
|
required this.lastName,
|
||||||
|
required this.phoneNumber,
|
||||||
|
});
|
||||||
|
|
||||||
|
String get fullName => '$firstName $lastName'.trim();
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'firstName': firstName,
|
||||||
|
'lastName': lastName,
|
||||||
|
'phoneNumber': phoneNumber,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Customer.fromMap(Map<String, dynamic> map) {
|
||||||
|
return Customer(
|
||||||
|
id: map['id'],
|
||||||
|
firstName: map['firstName'],
|
||||||
|
lastName: map['lastName'],
|
||||||
|
phoneNumber: map['phoneNumber'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
class Hen {
|
||||||
|
final String id;
|
||||||
|
final String name;
|
||||||
|
final DateTime birthDate;
|
||||||
|
bool isLaying;
|
||||||
|
|
||||||
|
Hen({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.birthDate,
|
||||||
|
this.isLaying = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'name': name,
|
||||||
|
'birthDate': birthDate.toIso8601String(),
|
||||||
|
'isLaying': isLaying,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Hen.fromMap(Map<String, dynamic> map) {
|
||||||
|
return Hen(
|
||||||
|
id: map['id'],
|
||||||
|
name: map['name'],
|
||||||
|
birthDate: DateTime.parse(map['birthDate']),
|
||||||
|
isLaying: map['isLaying'] ?? true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
class Reservation {
|
||||||
|
final String id;
|
||||||
|
final String customerName;
|
||||||
|
final String breedId;
|
||||||
|
final int eggsRequested;
|
||||||
|
final DateTime createdAt;
|
||||||
|
final DateTime deliveryDate;
|
||||||
|
bool isDelivered;
|
||||||
|
|
||||||
|
Reservation({
|
||||||
|
required this.id,
|
||||||
|
required this.customerName,
|
||||||
|
required this.breedId,
|
||||||
|
required this.eggsRequested,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.deliveryDate,
|
||||||
|
this.isDelivered = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'customerName': customerName,
|
||||||
|
'breedId': breedId,
|
||||||
|
'eggsRequested': eggsRequested,
|
||||||
|
'createdAt': createdAt.toIso8601String(),
|
||||||
|
'deliveryDate': deliveryDate.toIso8601String(),
|
||||||
|
'isDelivered': isDelivered,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Reservation.fromMap(Map<String, dynamic> map) {
|
||||||
|
return Reservation(
|
||||||
|
id: map['id'],
|
||||||
|
customerName: map['customerName'],
|
||||||
|
breedId: map['breedId'],
|
||||||
|
eggsRequested: map['eggsRequested'],
|
||||||
|
createdAt: DateTime.parse(map['createdAt']),
|
||||||
|
deliveryDate: DateTime.parse(map['deliveryDate']),
|
||||||
|
isDelivered: map['isDelivered'] ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import '../models/breed_setting.dart';
|
||||||
|
import '../models/reservation.dart';
|
||||||
|
import '../models/customer.dart';
|
||||||
|
|
||||||
|
class StorageService {
|
||||||
|
static const String breedsKey = 'breed_settings_v2';
|
||||||
|
static const String reservationsKey = 'reservations_v2';
|
||||||
|
static const String customersKey = 'customers_v1';
|
||||||
|
static const String warehouseStockKey = 'warehouse_stock_v1';
|
||||||
|
static const String lastProductionUpdateKey = 'last_production_update_v1';
|
||||||
|
|
||||||
|
static Future<Map<String, double>> loadWarehouseStock() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final raw = prefs.getString(warehouseStockKey);
|
||||||
|
if (raw == null || raw.isEmpty) return {};
|
||||||
|
final map = jsonDecode(raw) as Map<String, dynamic>;
|
||||||
|
return map.map((k, v) => MapEntry(k, (v as num).toDouble()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> saveWarehouseStock(Map<String, double> stock) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setString(warehouseStockKey, jsonEncode(stock));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<DateTime?> loadLastProductionUpdate() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final raw = prefs.getString(lastProductionUpdateKey);
|
||||||
|
if (raw == null || raw.isEmpty) return null;
|
||||||
|
return DateTime.parse(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> saveLastProductionUpdate(DateTime date) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setString(lastProductionUpdateKey, date.toIso8601String());
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<List<BreedSetting>> loadBreedSettings() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final raw = prefs.getString(breedsKey);
|
||||||
|
if (raw == null || raw.isEmpty) return [];
|
||||||
|
final list = jsonDecode(raw) as List;
|
||||||
|
return list.map((e) => BreedSetting.fromMap(Map<String, dynamic>.from(e))).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> saveBreedSettings(List<BreedSetting> items) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setString(breedsKey, jsonEncode(items.map((e) => e.toMap()).toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<List<Reservation>> loadReservations() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final raw = prefs.getString(reservationsKey);
|
||||||
|
if (raw == null || raw.isEmpty) return [];
|
||||||
|
final list = jsonDecode(raw) as List;
|
||||||
|
return list.map((e) => Reservation.fromMap(Map<String, dynamic>.from(e))).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> saveReservations(List<Reservation> items) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setString(reservationsKey, jsonEncode(items.map((e) => e.toMap()).toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<List<Customer>> loadCustomers() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final raw = prefs.getString(customersKey);
|
||||||
|
if (raw == null || raw.isEmpty) return [];
|
||||||
|
final list = jsonDecode(raw) as List;
|
||||||
|
return list.map((e) => Customer.fromMap(Map<String, dynamic>.from(e))).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> saveCustomers(List<Customer> items) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setString(customersKey, jsonEncode(items.map((e) => e.toMap()).toList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CoopIcon extends StatelessWidget {
|
||||||
|
final double size;
|
||||||
|
final Color? color;
|
||||||
|
const CoopIcon({super.key, this.size = 24, this.color});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final iconColor = color ?? Theme.of(context).colorScheme.primary;
|
||||||
|
return SizedBox(
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: _CoopPainter(iconColor),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CoopPainter extends CustomPainter {
|
||||||
|
final Color color;
|
||||||
|
_CoopPainter(this.color);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final paint = Paint()
|
||||||
|
..color = color
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = size.width / 12
|
||||||
|
..strokeCap = StrokeCap.round
|
||||||
|
..strokeJoin = StrokeJoin.round;
|
||||||
|
|
||||||
|
final w = size.width;
|
||||||
|
final h = size.height;
|
||||||
|
|
||||||
|
final path = Path();
|
||||||
|
// Main structure
|
||||||
|
path.moveTo(w * 0.2, h * 0.85);
|
||||||
|
path.lineTo(w * 0.8, h * 0.85);
|
||||||
|
path.lineTo(w * 0.8, h * 0.4);
|
||||||
|
path.lineTo(w * 0.2, h * 0.4);
|
||||||
|
path.close();
|
||||||
|
|
||||||
|
// Roof
|
||||||
|
path.moveTo(w * 0.1, h * 0.4);
|
||||||
|
path.lineTo(w * 0.5, h * 0.1);
|
||||||
|
path.lineTo(w * 0.9, h * 0.4);
|
||||||
|
|
||||||
|
// Legs
|
||||||
|
path.moveTo(w * 0.3, h * 0.85);
|
||||||
|
path.lineTo(w * 0.3, h * 1.0);
|
||||||
|
path.moveTo(w * 0.7, h * 0.85);
|
||||||
|
path.lineTo(w * 0.7, h * 1.0);
|
||||||
|
|
||||||
|
// Window/Mesh
|
||||||
|
path.addRect(Rect.fromLTWH(w * 0.4, h * 0.55, w * 0.2, h * 0.2));
|
||||||
|
|
||||||
|
canvas.drawPath(path, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user