// MOCK DATA — TEMPORARY. See MockSwitch.dart. import 'package:dio/dio.dart'; import '../about/external/data/RoutineChain.dart'; import '../about/external/data/pages/request/CommitmentsRequest.dart'; import '../about/external/data/pages/request/HistoryRequest.dart'; import '../about/external/initial/AbandonRequest.dart'; import '../about/external/initial/AmnestyRequest.dart'; import '../about/external/initial/CommitmentRequest.dart'; import '../about/external/initial/CompletionRequest.dart'; import '../about/external/initial/DeferralRequest.dart'; import '../about/external/initial/DeviceRequest.dart'; import '../about/external/initial/GoalRequest.dart'; import '../about/external/initial/GroupGoalRequest.dart'; import '../about/external/initial/GroupMessageRequest.dart'; import '../about/external/initial/GroupRequest.dart'; import '../about/external/initial/JoinGroupRequest.dart'; import '../about/external/initial/NudgeRequest.dart'; import '../about/external/initial/ObjectiveProgressRequest.dart'; import '../about/external/initial/IdRequest.dart'; import '../about/external/initial/LoginData.dart'; import '../about/external/initial/ReportCardRequest.dart'; import '../about/external/initial/SessionLogRequest.dart'; import '../about/external/initial/SickModeRequest.dart'; import '../about/external/initial/ToneRequest.dart'; import '../comms/ConnectComms.dart'; import 'MockGroups.dart'; import 'MockStore.dart'; /// A fake backend. Answers every call from [MockStore] after a short delay, /// so loading states still flash the way they will against the real thing. class MockComms implements ConnectComms { MockComms() { MockStore.ensureSeeded(); } Future _ok(dynamic data) async { await Future.delayed(const Duration(milliseconds: 250)); return Response( requestOptions: RequestOptions(path: 'mock'), statusCode: 200, data: data, ); } Future _done() => _ok({'success': true}); Future _emptyPage() => _ok(MockStore.page(>[])); // ── Auth ────────────────────────────────────────────────────────────────── @override Future registerDevice(DeviceRequest request) => _done(); @override Future registerDeviceToken(String token) => _done(); @override Future login(LoginData request) => _ok({ 'id': 'mock-user', 'name': 'Alex', 'token': 'mock-token', 'pic': '', 'tone': 'Firm', 'standing': 'Good', 'amnestyTokens': 3, 'sickMode': false, }); @override Future logout() => _done(); @override Future me() => _ok({'id': 'mock-user', 'name': 'Alex'}); // ── Goals ───────────────────────────────────────────────────────────────── @override Future getMyGoals(HistoryRequest request) => _ok(MockStore.goalsWithCounts()); @override Future saveGoalEntry(GoalRequest request) { final Map json = request.toJson(); json['targetDate'] = request.targetDate.isEmpty ? null : request.targetDate; json['archived'] = false; final int index = MockStore.goals.indexWhere((goal) => goal['id'] == request.id); if (request.id == null || index < 0) { json['id'] = MockStore.newId('goal'); MockStore.goals.add(json); } else { MockStore.goals[index] = json; } return _ok(json); } @override Future getGoalTasks(IdRequest request) => _ok(MockStore.goalTasks(request.id)); @override Future archiveGoalEntry(IdRequest request) { for (Map goal in MockStore.goals) { if (goal['id'] == request.id) { goal['archived'] = true; } } return _done(); } // ── Commitments ─────────────────────────────────────────────────────────── @override Future getTodayPlan(CommitmentsRequest request) => _ok(MockStore.page(MockStore.todayPlan())); @override Future getMyCommitments(CommitmentsRequest request) => _ok(MockStore.page(MockStore.commitments)); @override Future getOverdueQueue(HistoryRequest request) => _ok(MockStore.page(MockStore.overdue())); @override Future saveCommitmentEntry(CommitmentRequest request) { final Map json = request.toJson(); json['id'] = MockStore.newId('task'); json['status'] = 'Open'; json['deferralCount'] = 0; MockStore.commitments.add(json); return _ok(json); } @override Future updateCommitmentEntry(CommitmentRequest request) { final Map? item = MockStore.commitment(request.id ?? ''); if (item != null) { item.addAll(request.toJson()); } return _done(); } @override Future deleteCommitmentEntry(IdRequest request) { MockStore.commitments.removeWhere((item) => item['id'] == request.id); return _done(); } @override Future completeCommitmentEntry(CompletionRequest request) { final Map? item = MockStore.commitment(request.commitmentId); if (item != null) { final DateTime? end = DateTime.tryParse(item['dueEnd'] ?? ''); item['status'] = end != null && end.isBefore(DateTime.now()) ? 'LateCompleted' : 'Completed'; } return _done(); } @override Future deferCommitmentEntry(DeferralRequest request) { final Map? item = MockStore.commitment(request.commitmentId); if (item != null) { final DateTime now = DateTime.now(); item['dueStart'] = request.newDueStart.isNotEmpty ? request.newDueStart : now.add(const Duration(hours: 1)).toIso8601String(); item['dueEnd'] = request.newDueEnd.isNotEmpty ? request.newDueEnd : now.add(const Duration(days: 1)).toIso8601String(); item['deferralCount'] = (item['deferralCount'] ?? 0) + 1; item['status'] = 'Deferred'; } return _done(); } @override Future abandonCommitmentEntry(AbandonRequest request) { final Map? item = MockStore.commitment(request.commitmentId); if (item != null) { item['status'] = 'Abandoned'; } return _done(); } @override Future getCommitmentHistory(HistoryRequest request) => _emptyPage(); @override Future getCommitmentEvents(IdRequest request) => _emptyPage(); // ── Capacity ────────────────────────────────────────────────────────────── @override Future checkCapacity(CommitmentsRequest request) => _done(); @override Future getCapacityProfileEntry() => _ok({}); // ── Debt & standing ─────────────────────────────────────────────────────── @override Future getDebtSummary() => _ok({}); @override Future getDebtLedger(HistoryRequest request) => _emptyPage(); @override Future getDebtTrend(ReportCardRequest request) => _ok([]); @override Future getStanding() => _ok({'standing': 'Good'}); @override Future getStandingHistory(HistoryRequest request) => _emptyPage(); // ── Excuses ─────────────────────────────────────────────────────────────── @override Future getExcuseClusters(ReportCardRequest request) => _ok(>[ { 'id': 'cluster-tired', 'label': 'Too tired', 'occurrences': 4, 'dominantCategory': 'Deep work', 'byWeekday': {'4': 3, '2': 1}, 'byHour': {'19': 3, '21': 1}, 'insight': "You've said \"too tired\" 4 times this month — 3 of them on Thursday evenings. Put deep work before lunch on Thursdays.", }, { 'id': 'cluster-time', 'label': 'Ran out of time', 'occurrences': 3, 'dominantCategory': 'Admin', 'byWeekday': {'1': 2, '5': 1}, 'byHour': {'17': 3}, 'insight': '', }, ]); // ── Proof ───────────────────────────────────────────────────────────────── @override Future uploadPhotoProof(FormData request) => _ok({'proofRef': 'mock-photo'}); @override Future submitTimerProofEntry(CompletionRequest request) => completeCommitmentEntry(request); @override Future submitLocationProofEntry(CompletionRequest request) => completeCommitmentEntry(request); // ── Guardrails ──────────────────────────────────────────────────────────── @override Future spendAmnestyToken(AmnestyRequest request) => _done(); @override Future getAmnestyBalance() => _ok({'balance': 3}); @override Future updateSickMode(SickModeRequest request) => _done(); @override Future checkDistress() => _ok({'distressed': false}); @override Future updateTone(ToneRequest request) => _done(); // ── Habits & routines ───────────────────────────────────────────────────── @override Future getMyHabits(HistoryRequest request) => _ok([]); @override Future saveHabitEntry(Map request) => _done(); @override Future logHabitEntry(IdRequest request) => _done(); @override Future getKeystoneHabits() => _ok([]); @override Future getMyRoutines(HistoryRequest request) => _ok( MockStore.routines.map((routine) => routine.toJson()).toList()); @override Future saveRoutineEntry(Map request) { final RoutineChain routine = RoutineChain.fromJson(request); final int index = MockStore.routines .indexWhere((item) => item.id != null && item.id == routine.id); if (routine.id == null || index < 0) { routine.id = MockStore.newId('routine'); MockStore.routines.add(routine); } else { // Keep today's progress across an edit. final RoutineChain before = MockStore.routines[index]; for (int step = 0; step < routine.steps.length && step < before.steps.length; step++) { routine.steps[step].completed = before.steps[step].completed; } MockStore.routines[index] = routine; } return _ok(routine.toJson()); } /// A logged run marks today's progress on the routine, so the library ring /// and the "done" tick reflect what just happened. @override Future logRoutineChainEntry(Map request) { for (RoutineChain routine in MockStore.routines) { if (routine.id == request['routineId']) { final int done = request['completedSteps'] ?? 0; for (int step = 0; step < routine.steps.length; step++) { routine.steps[step].completed = step < done; } routine.lastBreakIndex = request['breakIndex'] ?? -1; } } return _done(); } // ── Training ────────────────────────────────────────────────────────────── @override Future getMyPrograms(HistoryRequest request) => _ok(>[ { 'id': 'program-base', 'name': 'Base strength · 8 weeks', 'weeks': 8, 'sessionsPerWeek': 3, 'deloadWeeks': [4, 8], 'weeklyContactCeiling': 120, 'lowerBodyRecoveryHours': 48, 'active': true, }, ]); @override Future saveProgramEntry(Map request) => _done(); @override Future activateProgramEntry(IdRequest request) => _done(); @override Future getProgramSessions(IdRequest request) => _ok(>[ { 'id': 'session-a', 'programId': 'program-base', 'dayIndex': 0, 'name': 'Day A · Squat & press', 'highIntensityLowerBody': true, 'prescriptions': [{}, {}, {}, {}], }, { 'id': 'session-b', 'programId': 'program-base', 'dayIndex': 2, 'name': 'Day B · Pull & hinge', 'highIntensityLowerBody': false, 'prescriptions': [{}, {}, {}], }, ]); @override Future startSessionEntry(IdRequest request) => _done(); @override Future saveSessionLogEntry(SessionLogRequest request) => _done(); @override Future getSessionHistory(HistoryRequest request) => _emptyPage(); @override Future getLastSessionFor(IdRequest request) => _ok({}); @override Future getWeeklyVolume(ReportCardRequest request) => _ok({}); @override Future getContactVolume(ReportCardRequest request) => _ok({}); @override Future getPersonalRecords(HistoryRequest request) => _emptyPage(); // ── Report card ─────────────────────────────────────────────────────────── @override Future getWeeklyReportCard(ReportCardRequest request) => _ok(_report()); @override Future getMonthlyReportCard(ReportCardRequest request) => _ok(_report()); Map _report() { final DateTime now = DateTime.now(); return { 'id': 'report-week', 'periodStart': now.subtract(const Duration(days: 7)).toIso8601String(), 'periodEnd': now.toIso8601String(), 'completionByClass': { 'NonNegotiable': 0.92, 'Standard': 0.71, 'Elective': 0.40, }, 'completionByCategory': { 'Deep work': 0.64, 'Admin': 0.88, 'Health': 0.79, }, 'completionByWeekday': { '1': 0.9, '2': 0.8, '3': 0.75, '4': 0.45, '5': 0.7, '6': 0.6, '7': 0.85, }, 'worstHour': 19, 'debtTrend': [8, 9.5, 11, 10.2, 7.4, 6.8, 5.9], 'deferralLeaderboard': >[ {'commitmentId': 'c-email', 'title': 'Email supervisor the outline', 'count': 2}, {'commitmentId': 'c-kitchen', 'title': 'Deep clean the kitchen', 'count': 1}, ], 'excuseTaxonomy': [], 'estimationAccuracy': {'Deep work': 1.6, 'Admin': 1.1}, 'trainingAdherence': 0.83, 'programIntegrity': 0.9, 'assignedAction': 'Move deep work before noon on Thursdays. That is where the week leaks.', 'grade': 'B', 'praise': 'Every non-negotiable but one landed inside its window.', }; } // ── Notifications ───────────────────────────────────────────────────────── @override Future getMyNotifications(HistoryRequest request) => _emptyPage(); @override Future acknowledgeNudgeEntry(IdRequest request) => _done(); // ── Groups ──────────────────────────────────────────────────────────────── @override Future getMyGroups(HistoryRequest request) => _ok(MockGroups.mine()); @override Future getGroupEntry(IdRequest request) => _ok(MockGroups.one(request.id)); @override Future saveGroupEntry(GroupRequest request) => _ok(MockGroups.save(request)); @override Future joinGroupEntry(JoinGroupRequest request) => _ok(MockGroups.join(request)); @override Future leaveGroupEntry(IdRequest request) => _ok(MockGroups.leave(request.id)); @override Future saveGroupGoalEntry(GroupGoalRequest request) => _ok(MockGroups.saveGoal(request)); @override Future logObjectiveProgress(ObjectiveProgressRequest request) => _ok(MockGroups.logProgress(request)); @override Future getGroupMessages(IdRequest request) => _ok(MockGroups.messages(request.id)); @override Future sendGroupMessage(GroupMessageRequest request) => _ok(MockGroups.send(request)); @override Future nudgeGroupMember(NudgeRequest request) => _ok(MockGroups.nudge(request)); }