// MOCK DATA — TEMPORARY. See MockSwitch.dart. import '../about/external/data/RoutineChain.dart'; import '../about/internal/application/CommitmentClass.dart'; import '../utils/RoutineTemplates.dart'; /// The fake backend's database: plain JSON maps, seeded relative to today, /// mutated by [MockComms] so taps actually change what you see. Static, so /// every screen shares one copy for the life of the app. class MockStore { static bool _seeded = false; static int _nextId = 100; static final List> goals = >[]; static final List> commitments = >[]; static final List routines = []; static String newId(String prefix) { _nextId = _nextId + 1; return "$prefix-$_nextId"; } static void ensureSeeded() { if (_seeded) { return; } _seeded = true; final DateTime now = DateTime.now(); final DateTime today = DateTime(now.year, now.month, now.day); final int weekday = now.weekday; DateTime at(int dayOffset, int hour, [int minute = 0]) { return today.add(Duration(days: dayOffset, hours: hour, minutes: minute)); } // ── Goals ────────────────────────────────────────────────────────────── goals.addAll(>[ { 'id': 'goal-strong', 'title': 'Get strong', 'description': 'Deadlift my bodyweight by spring, without getting hurt.', 'category': 'Health', 'defaultClass': 'Standard', 'startDate': at(-30, 0).toIso8601String(), 'targetDate': at(60, 0).toIso8601String(), 'colourHex': '#FF2F6F4E', 'archived': false, }, { 'id': 'goal-thesis', 'title': 'Finish the thesis', 'description': 'Submit before the department deadline. No extensions.', 'category': 'Work', 'defaultClass': 'NonNegotiable', 'startDate': at(-60, 0).toIso8601String(), 'targetDate': at(45, 0).toIso8601String(), 'colourHex': '#FF2C4A63', 'archived': false, }, { 'id': 'goal-calm', 'title': 'Calm mornings', 'description': 'Start the day on purpose instead of on the phone.', 'category': 'Mind', 'defaultClass': 'Elective', 'startDate': at(-10, 0).toIso8601String(), 'targetDate': null, 'colourHex': '#FF4F5D86', 'archived': false, }, ]); // ── Commitments ──────────────────────────────────────────────────────── Map task( String id, String title, { String? goal, String cls = 'Standard', required DateTime start, required DateTime end, int minutes = 30, String proof = 'Honour', int timer = 0, String status = 'Open', int deferrals = 0, String category = '', }) { return { 'id': id, 'type': 'TASK', 'commitmentClass': cls, 'title': title, 'category': category, 'dueStart': start.toIso8601String(), 'dueEnd': end.toIso8601String(), 'estMinutes': minutes, 'energy': 'Medium', 'proofType': proof, 'proofTimerMinutes': timer, 'rrule': '', 'parentId': goal, 'status': status, 'deferralCount': deferrals, }; } commitments.addAll(>[ task('c-walk', 'Morning walk, no phone', goal: 'goal-calm', cls: 'Elective', start: at(0, 6, 30), end: at(0, 8), minutes: 20, status: 'Completed'), task('c-chapter', 'Write 500 words of chapter 3', goal: 'goal-thesis', cls: 'NonNegotiable', start: at(0, 9), end: at(0, 23, 30), minutes: 25, proof: 'Timer', timer: 1, category: 'Deep work'), task('c-rent', 'Pay rent', cls: 'NonNegotiable', start: at(0, 8), end: at(0, 23, 59), minutes: 5, category: 'Admin'), task('c-physio', 'Book physio appointment', goal: 'goal-strong', start: at(0, 12), end: at(0, 23), minutes: 10, category: 'Admin'), task('c-read', 'Read two papers for the literature review', goal: 'goal-thesis', start: at(0, 18), end: at(0, 23, 45), minutes: 60, category: 'Deep work'), task('c-meal', 'Meal prep for the week', goal: 'goal-strong', start: at(1, 17), end: at(1, 20), minutes: 90), // Two things already past their window, so the overdue queue and debt // have something to show. task('c-email', 'Email supervisor the outline', goal: 'goal-thesis', start: at(-2, 9), end: at(-2, 12), minutes: 15, status: 'Overdue', deferrals: 1), task('c-kitchen', 'Deep clean the kitchen', start: at(-1, 18), end: at(-1, 21), minutes: 45, status: 'Overdue'), ]); // ── Routines ─────────────────────────────────────────────────────────── final RoutineChain strength = RoutineTemplates.fullBody() ..id = 'routine-strength' ..goalId = 'goal-strong' ..days = _withToday([1, 3, 5], weekday) ..startMinute = 7 * 60; final RoutineChain push = RoutineTemplates.upperPush() ..id = 'routine-push' ..goalId = 'goal-strong' ..days = [1, 2, 3, 4, 5, 6, 7].where((day) => day != weekday).take(2).toList(); final RoutineChain breathing = RoutineTemplates.boxBreathing() ..id = 'routine-breathing' ..goalId = 'goal-calm'; final RoutineChain morning = RoutineTemplates.morning() ..id = 'routine-morning' ..goalId = 'goal-calm' ..days = _withToday([1, 2, 3, 4, 5], weekday); for (int index = 0; index < morning.steps.length; index++) { morning.steps[index].completed = true; } final RoutineChain shutdown = RoutineTemplates.shutdown() ..id = 'routine-shutdown' ..days = _withToday([1, 2, 3, 4, 5], weekday) ..commitmentClass = CommitmentClass.Standard; shutdown.steps.first.completed = true; final RoutineChain mobility = RoutineTemplates.mobility() ..id = 'routine-mobility'; routines.addAll([ breathing, morning, strength, shutdown, push, mobility, ]); } static List _withToday(List days, int weekday) { final List result = List.from(days); if (!result.contains(weekday)) { result.add(weekday); } result.sort(); return result; } // ── Derived views ────────────────────────────────────────────────────── static bool _settled(Map item) { final String status = item['status']; return status == 'Completed' || status == 'LateCompleted' || status == 'Abandoned' || status == 'Archived'; } static bool _windowClosed(Map item) { final DateTime? end = DateTime.tryParse(item['dueEnd'] ?? ''); return end != null && end.isBefore(DateTime.now()); } static List> todayPlan() { final DateTime now = DateTime.now(); return commitments.where((item) { final DateTime? start = DateTime.tryParse(item['dueStart'] ?? ''); return start != null && start.year == now.year && start.month == now.month && start.day == now.day && item['status'] != 'Abandoned'; }).toList() ..sort((a, b) => (a['dueStart'] as String).compareTo(b['dueStart'])); } static List> overdue() { return commitments .where((item) => !_settled(item) && _windowClosed(item)) .map((item) => Map.from(item)..['status'] = 'Overdue') .toList(); } static List> goalTasks(String goalId) { return commitments.where((item) => item['parentId'] == goalId).toList(); } /// Goals with the server-derived counts filled in from the tasks. static List> goalsWithCounts() { return goals.where((goal) => goal['archived'] != true).map((goal) { final List> tasks = goalTasks(goal['id']); final int completed = tasks .where((item) => item['status'] == 'Completed' || item['status'] == 'LateCompleted') .length; final int late = tasks.where((item) => !_settled(item) && _windowClosed(item)).length; return Map.from(goal) ..['totalTasks'] = tasks.where((item) => item['status'] != 'Abandoned').length ..['completedTasks'] = completed ..['overdueTasks'] = late ..['debtContribution'] = late * 2.0; }).toList(); } static Map? commitment(String id) { for (Map item in commitments) { if (item['id'] == id) { return item; } } return null; } static Map page(List> content) { return { 'number': 0, 'size': content.length, 'totalElements': content.length, 'totalPages': 1, 'numberOfElements': content.length, 'first': true, 'last': true, 'content': content, }; } }