new plus button
This commit is contained in:
@@ -16,5 +16,23 @@ const _months = [
|
||||
'Dec',
|
||||
];
|
||||
|
||||
const _monthNames = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
];
|
||||
|
||||
String formatDate(DateTime date) =>
|
||||
'${date.day} ${_months[date.month - 1]} ${date.year}';
|
||||
|
||||
String formatMonthYear(DateTime date) =>
|
||||
'${_monthNames[date.month - 1]} ${date.year}';
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/// Month-grid helpers for the shop-log heatmap.
|
||||
library;
|
||||
|
||||
DateTime dateOnly(DateTime date) => DateTime(date.year, date.month, date.day);
|
||||
|
||||
DateTime monthStart(DateTime date) => DateTime(date.year, date.month);
|
||||
|
||||
bool isSameDay(DateTime a, DateTime b) =>
|
||||
a.year == b.year && a.month == b.month && a.day == b.day;
|
||||
|
||||
bool isSameMonth(DateTime a, DateTime b) =>
|
||||
a.year == b.year && a.month == b.month;
|
||||
|
||||
DateTime addMonths(DateTime month, int delta) =>
|
||||
DateTime(month.year, month.month + delta);
|
||||
|
||||
/// Monday-first calendar cells for [month], padded with nulls to full weeks.
|
||||
List<DateTime?> monthGrid(DateTime month) {
|
||||
final start = DateTime(month.year, month.month, 1);
|
||||
final daysInMonth = DateTime(month.year, month.month + 1, 0).day;
|
||||
final leading = start.weekday - DateTime.monday;
|
||||
final cells = <DateTime?>[
|
||||
...List<DateTime?>.filled(leading, null),
|
||||
for (var day = 1; day <= daysInMonth; day++)
|
||||
DateTime(month.year, month.month, day),
|
||||
];
|
||||
while (cells.length % 7 != 0) {
|
||||
cells.add(null);
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
/// How many trips landed on each calendar day.
|
||||
Map<DateTime, int> tripCountsByDay(Iterable<DateTime> shoppedAt) {
|
||||
final counts = <DateTime, int>{};
|
||||
for (final date in shoppedAt) {
|
||||
final key = dateOnly(date);
|
||||
counts[key] = (counts[key] ?? 0) + 1;
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
/// GitHub-style 0–4 intensity from a trip count.
|
||||
int heatmapLevel(int count) {
|
||||
if (count <= 0) return 0;
|
||||
if (count >= 4) return 4;
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/// Filters shop-log rows by day, tag, and product search.
|
||||
library;
|
||||
|
||||
import '../models/shop.dart';
|
||||
import 'fuzzy.dart';
|
||||
import 'shop_calendar.dart';
|
||||
|
||||
List<Shop> filterShops({
|
||||
required List<Shop> shops,
|
||||
required Map<int, List<String>> productNames,
|
||||
DateTime? selectedDay,
|
||||
String? tag,
|
||||
String query = '',
|
||||
}) {
|
||||
final needle = query.trim();
|
||||
final tagFilter = tag?.trim();
|
||||
return [
|
||||
for (final shop in shops)
|
||||
if (_matches(
|
||||
shop: shop,
|
||||
names: productNames[shop.id] ?? const [],
|
||||
selectedDay: selectedDay,
|
||||
tag: tagFilter,
|
||||
query: needle,
|
||||
))
|
||||
shop,
|
||||
];
|
||||
}
|
||||
|
||||
bool _matches({
|
||||
required Shop shop,
|
||||
required List<String> names,
|
||||
required DateTime? selectedDay,
|
||||
required String? tag,
|
||||
required String query,
|
||||
}) {
|
||||
if (selectedDay != null && !isSameDay(shop.shoppedAt, selectedDay)) {
|
||||
return false;
|
||||
}
|
||||
if (tag != null && tag.isNotEmpty) {
|
||||
final wanted = tag.toLowerCase();
|
||||
if (!shop.tags.any((item) => item.toLowerCase() == wanted)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (query.isEmpty) return true;
|
||||
return names.any((name) => fuzzyScore(query, name) != null);
|
||||
}
|
||||
Reference in New Issue
Block a user