part of 'main.dart'; class PersonsPage extends StatelessWidget { const PersonsPage({ super.key, required this.people, required this.events, required this.onAdd, required this.onEdit, required this.onDelete, }); final List people; final List events; final VoidCallback onAdd; final ValueChanged onEdit; final ValueChanged onDelete; @override Widget build(BuildContext context) { return PageFrame( eyebrow: '${people.length} csal\u00e1dtag', title: 'Csal\u00e1dtagok', action: FilledButton.icon( onPressed: onAdd, icon: const Icon(Icons.add), label: const Text('\u00daj csal\u00e1dtag'), ), child: people.isEmpty ? const EmptyState( icon: Icons.group_add_outlined, title: 'M\u00e9g nincs csal\u00e1dtag', subtitle: 'Vegy\u00e9l fel csal\u00e1dtagokat, gyerekeket vagy b\u00e1rkit.', ) : ListView.separated( padding: const EdgeInsets.only(bottom: 24), itemCount: people.length, separatorBuilder: (_, _) => const SizedBox(height: 10), itemBuilder: (_, i) { final person = people[i]; final personEvents = events .where((event) => event.personId == person.id) .toList() ..sort((a, b) => a.date.compareTo(b.date)); final nextEvent = personEvents.isEmpty ? null : personEvents.first; return Card( child: Padding( padding: const EdgeInsets.all(18), child: Row( children: [ FamilyAvatar(person: person), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( person.name, style: const TextStyle( color: ink, fontWeight: FontWeight.w800, fontSize: 17, ), ), const SizedBox(height: 4), Text( nextEvent == null ? 'Nincs hozz\u00e1rendelt esem\u00e9ny' : 'K\u00f6vetkez\u0151: ${nextEvent.title}, ${formatDate(nextEvent.date)} ${nextEvent.start}', style: const TextStyle(color: Colors.black54), ), ], ), ), Text( '${personEvents.length} esem\u00e9ny', style: const TextStyle( color: sage, fontWeight: FontWeight.w800, ), ), const SizedBox(width: 8), PopupMenuButton( onSelected: (value) { if (value == 'edit') onEdit(person); if (value == 'delete') onDelete(person); }, itemBuilder: (_) => const [ PopupMenuItem( value: 'edit', child: Text('\u00c1tnevez\u00e9s / avatar'), ), PopupMenuItem( value: 'delete', child: Text('T\u00f6rl\u00e9s'), ), ], ), ], ), ), ); }, ), ); } } class StylePage extends StatelessWidget { const StylePage({ super.key, required this.themeId, required this.onThemeChanged, }); final String themeId; final ValueChanged onThemeChanged; @override Widget build(BuildContext context) { return PageFrame( eyebrow: 'T\u00e9ma \u00e9s st\u00edlusok', title: 'Megjelen\u00e9s', child: ListView( padding: const EdgeInsets.only(bottom: 24), children: [ const Text( 'V\u00e1lassz egy alkalmaz\u00e1st\u00e9m\u00e1t. A v\u00e1laszt\u00e1s JSON-be ment\u0151dik, \u00e9s \u00fajraind\u00edt\u00e1s ut\u00e1n is megmarad.', style: TextStyle(color: Colors.black54, fontSize: 16), ), const SizedBox(height: 18), ...appThemes.map((theme) { final selected = theme.id == themeId; return Card( child: ListTile( contentPadding: const EdgeInsets.all(16), leading: CircleAvatar(backgroundColor: theme.seed), title: Text( theme.name, style: const TextStyle( color: ink, fontWeight: FontWeight.w800, ), ), subtitle: Text(theme.description), trailing: selected ? const Icon(Icons.check_circle, color: sage) : const Icon(Icons.circle_outlined, color: Colors.black26), onTap: () => onThemeChanged(theme.id), ), ); }), const SizedBox(height: 18), const Text( 'Csal\u00e1dtag sz\u00ednek \u00e9s avatar k\u00e9szlet', style: TextStyle( color: ink, fontWeight: FontWeight.w800, fontSize: 18, ), ), const SizedBox(height: 10), Wrap( spacing: 8, runSpacing: 8, children: avatarPresets .map( (preset) => Chip( avatar: Text(preset.symbol), label: Text(preset.label), ), ) .toList(), ), ], ), ); } } class ShoppingPage extends StatelessWidget { const ShoppingPage({ super.key, required this.items, required this.onChanged, required this.onAdd, }); final List items; final VoidCallback onChanged; final VoidCallback onAdd; @override Widget build(BuildContext context) { final remaining = items.where((e) => !e.done).length; return PageFrame( eyebrow: '$remaining t\u00e9tel van h\u00e1tra', title: 'Bev\u00e1s\u00e1rl\u00f3lista', action: IconButton.filled(onPressed: onAdd, icon: const Icon(Icons.add)), child: Column( children: [ Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: mint, borderRadius: BorderRadius.circular(18), ), child: Row( children: [ const Icon(Icons.auto_awesome_outlined, color: sage), const SizedBox(width: 12), Expanded( child: Text( items.isEmpty ? 'A list\u00e1d \u00fcres.' : '${items.length - remaining} t\u00e9tel m\u00e1r a kos\u00e1rban van.', style: const TextStyle(color: ink), ), ), Text( '${items.length - remaining}/${items.length}', style: const TextStyle( color: sage, fontWeight: FontWeight.w800, ), ), ], ), ), const SizedBox(height: 16), Expanded( child: ListView.separated( padding: const EdgeInsets.only(bottom: 24), itemCount: items.length, separatorBuilder: (_, _) => const SizedBox(height: 8), itemBuilder: (_, i) { final item = items[i]; return Card( child: CheckboxListTile( value: item.done, activeColor: sage, onChanged: (value) { item.done = value ?? false; onChanged(); }, title: Text( item.name, style: TextStyle( fontWeight: FontWeight.w700, decoration: item.done ? TextDecoration.lineThrough : null, color: item.done ? Colors.black38 : ink, ), ), subtitle: Text('${item.amount} \u00b7 ${item.category}'), secondary: const Icon( Icons.drag_indicator, color: Colors.black26, ), ), ); }, ), ), ], ), ); } } class StockPage extends StatefulWidget { const StockPage({ super.key, required this.items, required this.onChanged, required this.onAdd, required this.onEdit, required this.jarvisSettings, }); final List items; final VoidCallback onChanged; final VoidCallback onAdd; final ValueChanged onEdit; final JarvisSettings jarvisSettings; @override State createState() => _StockPageState(); } class _StockPageState extends State { String location = 'H\u0171t\u0151'; @override Widget build(BuildContext context) { final filtered = widget.items.where((e) => e.location == location).toList(); return PageFrame( eyebrow: '${widget.items.length} term\u00e9k nyilv\u00e1ntartva', title: 'Otthoni k\u00e9szlet', action: IconButton.filled( onPressed: widget.onAdd, icon: const Icon(Icons.add), ), child: Column( children: [ SegmentedButton( segments: const [ ButtonSegment( value: 'H\u0171t\u0151', label: Text('H\u0171t\u0151szekr\u00e9ny'), icon: Icon(Icons.kitchen), ), ButtonSegment( value: 'Kamra', label: Text('Sz\u00e1raz\u00e1ru'), icon: Icon(Icons.inventory_2_outlined), ), ], selected: {location}, onSelectionChanged: (s) => setState(() => location = s.first), ), const SizedBox(height: 18), Expanded( child: ListView.separated( padding: const EdgeInsets.only(bottom: 24), itemCount: filtered.length, separatorBuilder: (_, _) => const SizedBox(height: 9), itemBuilder: (_, i) { final item = filtered[i]; final warning = item.expiry == 'j\u00fal. 22.'; return Card( child: Padding( padding: const EdgeInsets.all(15), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: warning ? const Color(0xFFFFEBDD) : mint, borderRadius: BorderRadius.circular(13), ), child: Icon( location == 'H\u0171t\u0151' ? Icons.egg_alt_outlined : Icons.grain, color: warning ? orange : sage, ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.name, style: const TextStyle( color: ink, fontSize: 16, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 3), Text( 'Lej\u00e1rat: ${item.expiry}', style: TextStyle( color: warning ? orange : Colors.black45, fontWeight: warning ? FontWeight.w700 : FontWeight.normal, ), ), ], ), ), IconButton( onPressed: item.amount > 0 ? () { item.amount--; widget.onChanged(); } : null, icon: const Icon(Icons.remove_circle_outline), ), Text( '${item.amount} ${item.unit}', style: const TextStyle( color: ink, fontWeight: FontWeight.w800, ), ), IconButton( onPressed: () { item.amount++; widget.onChanged(); }, icon: const Icon(Icons.add_circle_outline), ), IconButton( tooltip: 'K\u00e9szlet szerkeszt\u00e9se', onPressed: () => widget.onEdit(item), icon: const Icon(Icons.edit_outlined), ), ], ), ), ); }, ), ), ], ), ); } } class InfoBox extends StatelessWidget { const InfoBox({super.key}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: mint, borderRadius: BorderRadius.circular(18), ), child: const Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(Icons.lock_outline, color: sage), SizedBox(width: 13), Expanded( child: Text( 'A hiteles\u00edt\u00e9si adatok nem ker\u00fclnek az alkalmaz\u00e1sba. ' 'A Google OAuth, az iCloud pedig biztons\u00e1gos CalDAV-kapcsolaton kereszt\u00fcl csatlakozik.', style: TextStyle(color: ink, height: 1.45), ), ), ], ), ); } } class AccountCard extends StatefulWidget { const AccountCard({ super.key, required this.icon, required this.color, required this.title, required this.subtitle, }); final IconData icon; final Color color; final String title; final String subtitle; @override State createState() => _AccountCardState(); } class _AccountCardState extends State { bool connected = false; @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(18), child: Row( children: [ CircleAvatar( backgroundColor: widget.color.withValues(alpha: .1), child: Icon(widget.icon, color: widget.color, size: 28), ), const SizedBox(width: 15), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.title, style: const TextStyle( color: ink, fontSize: 17, fontWeight: FontWeight.w800, ), ), const SizedBox(height: 3), Text( connected ? 'Csatlakoztatva \u00b7 Most friss\u00edtve' : widget.subtitle, style: TextStyle(color: connected ? sage : Colors.black54), ), ], ), ), OutlinedButton( onPressed: () { setState(() => connected = !connected); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( connected ? '${widget.title} dem\u00f3kapcsolat l\u00e9trehozva' : '${widget.title} lev\u00e1lasztva', ), ), ); }, child: Text( connected ? 'Lev\u00e1laszt\u00e1s' : 'Kapcsol\u00f3d\u00e1s', ), ), ], ), ), ); } }