mirror of
https://github.com/vleeuwenmenno/supplements.git
synced 2025-09-11 18:29:12 +02:00
215 lines
9.0 KiB
Dart
215 lines
9.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:shadcn_ui/shadcn_ui.dart';
|
|
|
|
import '../../models/supplement.dart';
|
|
import '../../providers/supplement_provider.dart';
|
|
|
|
/// Shows a bulk "Take supplements" dialog for a list of supplements.
|
|
/// - No time selection (records as now)
|
|
/// - Allows editing units per supplement
|
|
/// - Optional shared notes (applies to all)
|
|
Future<void> showBulkTakeDialog(
|
|
BuildContext context,
|
|
List<Supplement> supplements,
|
|
) async {
|
|
if (supplements.isEmpty) {
|
|
return;
|
|
}
|
|
|
|
// Controllers for each supplement's units
|
|
final Map<int, TextEditingController> unitControllers = {
|
|
for (final s in supplements.where((s) => s.id != null))
|
|
s.id!: TextEditingController(text: s.numberOfUnits.toString()),
|
|
};
|
|
|
|
final notesController = TextEditingController();
|
|
|
|
await showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
return AlertDialog(
|
|
title: Text('Take ${supplements.length} supplements'),
|
|
content: SizedBox(
|
|
width: double.maxFinite,
|
|
child: ConstrainedBox(
|
|
constraints: const BoxConstraints(maxWidth: 500),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
// List of supplements with editable units
|
|
// Use a scroll view with explicit max height to avoid intrinsic dimension issues.
|
|
ConstrainedBox(
|
|
constraints: const BoxConstraints(maxHeight: 320),
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
children: [
|
|
for (int index = 0; index < supplements.length; index++) ...[
|
|
() {
|
|
final s = supplements[index];
|
|
final controller = unitControllers[s.id] ??
|
|
TextEditingController(text: s.numberOfUnits.toString());
|
|
return Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(
|
|
color: Theme.of(context).colorScheme.outline.withValues(alpha: 0.3),
|
|
),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Name and per-unit info
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
s.name,
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
s.unitType,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
// Units editor
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextField(
|
|
controller: controller,
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
decoration: InputDecoration(
|
|
labelText: 'Units',
|
|
border: const OutlineInputBorder(),
|
|
suffixText: s.unitType,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
// Dosage line
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(6),
|
|
border: Border.all(
|
|
color: Theme.of(context).colorScheme.outline.withValues(alpha: 0.3),
|
|
),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
'Per unit:',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
Text(
|
|
s.ingredientsDisplay,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}(),
|
|
if (index != supplements.length - 1) const SizedBox(height: 8),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
// Shared notes
|
|
TextField(
|
|
controller: notesController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Notes for all (optional)',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
maxLines: 2,
|
|
),
|
|
],
|
|
),
|
|
)),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Cancel'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
final provider = context.read<SupplementProvider>();
|
|
int recorded = 0;
|
|
|
|
for (final s in supplements) {
|
|
if (s.id == null) continue;
|
|
final controller = unitControllers[s.id]!;
|
|
final units = double.tryParse(controller.text) ?? s.numberOfUnits.toDouble();
|
|
|
|
// totalDosageTaken stays 0.0 for now (ingredients-based tracking later)
|
|
provider.recordIntake(
|
|
s.id!,
|
|
0.0,
|
|
unitsTaken: units,
|
|
notes: notesController.text.isNotEmpty ? notesController.text : null,
|
|
takenAt: null, // "now"
|
|
);
|
|
recorded++;
|
|
}
|
|
|
|
Navigator.of(context).pop();
|
|
if (recorded > 0) {
|
|
final sonner = ShadSonner.of(context);
|
|
final id = DateTime.now().millisecondsSinceEpoch;
|
|
sonner.show(
|
|
ShadToast(
|
|
id: id,
|
|
title: Text('Recorded $recorded supplement${recorded == 1 ? '' : 's'}'),
|
|
action: ShadButton(
|
|
size: ShadButtonSize.sm,
|
|
child: const Text('Dismiss'),
|
|
onPressed: () => sonner.hide(id),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
child: const Text('Record All'),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
|
|
// Dispose controllers
|
|
for (final c in unitControllers.values) {
|
|
c.dispose();
|
|
}
|
|
notesController.dispose();
|
|
}
|