naptar-es-feladatkezelo/lib/pages.dart

530 lines
18 KiB
Dart

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<Person> people;
final List<CalendarEvent> events;
final VoidCallback onAdd;
final ValueChanged<Person> onEdit;
final ValueChanged<Person> 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<String>(
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<String> 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<ShoppingItem> 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<StockItem> items;
final VoidCallback onChanged;
final VoidCallback onAdd;
final ValueChanged<StockItem> onEdit;
final JarvisSettings jarvisSettings;
@override
State<StockPage> createState() => _StockPageState();
}
class _StockPageState extends State<StockPage> {
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<String>(
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<AccountCard> createState() => _AccountCardState();
}
class _AccountCardState extends State<AccountCard> {
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',
),
),
],
),
),
);
}
}