feat(insights): first isights impemetation

This commit is contained in:
misha
2026-11-09 15:35:10 +03:00
parent 67920b2016
commit 45d072cff4
63 changed files with 6374 additions and 1465 deletions

View File

@@ -0,0 +1,130 @@
import 'package:eatvio_app/features/insights/models/insights_payload_model.dart';
import 'package:eatvio_app/features/insights/presentation/widgets/insights_colors.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
/// Навигация по дням для вкладки «В течение дня»: `‹ дата ›` и выбор даты.
///
/// Колбэки `null` — навигация неактивна (данные других дней ещё не подключены).
class IntraDateNav extends StatelessWidget {
const IntraDateNav({
super.key,
required this.model,
this.onPreviousDay,
this.onNextDay,
this.onDatePicked,
});
final IntraChartModel model;
final VoidCallback? onPreviousDay;
final VoidCallback? onNextDay;
final ValueChanged<DateTime>? onDatePicked;
@override
Widget build(BuildContext context) {
final s = S.of(context);
final canGoNext = !model.isToday && onNextDay != null;
return Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: InsightsColors.accentBackground,
borderRadius: BorderRadius.circular(999),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_NavButton(
key: const Key('insights_intra_prev'),
icon: Icons.chevron_left_rounded,
tooltip: s.insights_prev_day,
onPressed: onPreviousDay,
),
InkWell(
key: const Key('insights_intra_pick'),
borderRadius: BorderRadius.circular(999),
onTap: onDatePicked == null ? null : () => _pickDate(context),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 72),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.calendar_today_rounded,
size: 14,
color: InsightsColors.blue,
),
const SizedBox(width: 6),
Text(
model.dateLabel,
key: const Key('insights_intra_date_label'),
style: const TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w700,
color: InsightsColors.title,
),
),
],
),
),
),
),
_NavButton(
key: const Key('insights_intra_next'),
icon: Icons.chevron_right_rounded,
tooltip: s.insights_next_day,
onPressed: canGoNext ? onNextDay : null,
),
],
),
);
}
Future<void> _pickDate(BuildContext context) async {
final lastDate = DateTime.tryParse(model.maxDate) ?? DateTime.now();
final current = DateTime.tryParse(model.date) ?? lastDate;
final picked = await showDatePicker(
context: context,
initialDate: current.isAfter(lastDate) ? lastDate : current,
firstDate: DateTime(2000),
lastDate: lastDate,
helpText: S.of(context).insights_pick_date,
);
if (picked != null) {
onDatePicked?.call(picked);
}
}
}
class _NavButton extends StatelessWidget {
const _NavButton({
super.key,
required this.icon,
required this.tooltip,
required this.onPressed,
});
final IconData icon;
final String tooltip;
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
return Opacity(
opacity: onPressed == null ? 0.35 : 1,
child: SizedBox(
width: 36,
height: 36,
child: IconButton(
padding: EdgeInsets.zero,
tooltip: tooltip,
onPressed: onPressed,
icon: Icon(icon, color: InsightsColors.blue),
),
),
);
}
}