import 'package:scheduler_core/scheduler_core.dart'; import '../controllers/scheduler_command_controller.dart'; import '../controllers/scheduler_read_controller.dart'; import '../controllers/today_screen_controller.dart'; class DemoSchedulerComposition { DemoSchedulerComposition._({ required this.store, required this.todayQuery, required this.managementUseCases, required this.commandUseCases, required this.date, required this.readAt, }); static const ownerId = 'owner-1'; static const timeZoneId = 'UTC'; static const projectId = 'home'; final InMemoryApplicationUnitOfWork store; final GetTodayStateQuery todayQuery; final V1ApplicationManagementUseCases managementUseCases; final V1ApplicationCommandUseCases commandUseCases; final CivilDate date; final DateTime readAt; String get dateLabel => formatDateLabel(date); factory DemoSchedulerComposition.seeded({ Clock? clock, CivilDate? selectedDate, bool includeTodayItems = true, bool includeBacklogItems = true, }) { final resolvedDate = selectedDate ?? CivilDate.fromDateTime((clock ?? const SystemClock()).now()); final readAt = _instant(resolvedDate, 15, 55); final createdAt = _instant(resolvedDate, 8); final project = ProjectProfile( id: projectId, name: 'Home', colorKey: 'project-home', ); final tasks = [ if (includeTodayItems) ..._todaySeedTasks(resolvedDate, createdAt), if (includeBacklogItems) Task.quickCapture( id: 'later-backlog-note', title: 'Review reusable grocery list', projectId: projectId, createdAt: createdAt, updatedAt: createdAt, ), ]; final store = InMemoryApplicationUnitOfWork( initialProjects: [project], initialOwnerSettings: [ OwnerSettings( ownerId: ownerId, timeZoneId: timeZoneId, compactModeEnabled: true, ), ], initialTasks: tasks, ); return DemoSchedulerComposition._( store: store, todayQuery: GetTodayStateQuery( applicationStore: store, timeZoneResolver: const FixedOffsetTimeZoneResolver.utc(), ), managementUseCases: V1ApplicationManagementUseCases( applicationStore: store, ), commandUseCases: V1ApplicationCommandUseCases( applicationStore: store, timeZoneResolver: const FixedOffsetTimeZoneResolver.utc(), ), date: resolvedDate, readAt: readAt, ); } TodayScreenController createTodayScreenController() { return TodayScreenController( read: () { return todayQuery.execute( GetTodayStateRequest( context: context('ui-plan-1-read-today'), date: date, includeNextFlexibleItem: true, ), ); }, ); } UiReadController createTodayController() { return ApplicationReadController( read: () { return todayQuery.execute( GetTodayStateRequest( context: context('ui-read-today'), date: date, includeNextFlexibleItem: true, ), ); }, isEmpty: (state) { return state.timelineItems.isEmpty && state.pendingNotices.isEmpty; }, ); } UiReadController createBacklogController() { return ApplicationReadController( read: () { return managementUseCases.getBacklog( GetBacklogRequest( context: context('ui-read-backlog'), sortKey: BacklogSortKey.age, ), ); }, isEmpty: (state) => state.items.isEmpty, ); } SchedulerCommandController createCommandController({ required ReadRefresh refreshReads, }) { return SchedulerCommandController( commands: commandUseCases, contextFor: context, localDate: date, refreshReads: refreshReads, ); } ApplicationOperationContext context(String operationId) { return ApplicationOperationContext.start( operationId: operationId, ownerTimeZone: OwnerTimeZoneContext( ownerId: ownerId, timeZoneId: timeZoneId, ), clock: FixedClock(readAt), idGenerator: SequentialIdGenerator(prefix: operationId), ); } static String formatDateLabel(CivilDate date) { const months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; return '${months[date.month - 1]} ${date.day}, ${date.year}'; } static List _todaySeedTasks(CivilDate date, DateTime createdAt) { return [ _task( id: 'clean-coffee-maker', title: 'Clean coffee maker', type: TaskType.flexible, status: TaskStatus.planned, date: date, startHour: 16, startMinute: 15, endHour: 16, endMinute: 30, reward: RewardLevel.medium, difficulty: DifficultyLevel.easy, createdAt: createdAt, ), _task( id: 'cleaned-kitchen-counter-early', title: 'Cleaned kitchen counter', type: TaskType.surprise, status: TaskStatus.completed, date: date, startHour: 16, startMinute: 45, endHour: 17, endMinute: 0, reward: RewardLevel.medium, difficulty: DifficultyLevel.medium, completedAt: _instant(date, 16, 20), createdAt: createdAt, ), _task( id: 'pay-bill', title: 'Pay bill', type: TaskType.critical, status: TaskStatus.planned, date: date, startHour: 18, startMinute: 0, endHour: 18, endMinute: 10, reward: RewardLevel.low, difficulty: DifficultyLevel.medium, createdAt: createdAt, ), _task( id: 'doctor-appointment', title: 'Doctor appointment', type: TaskType.inflexible, status: TaskStatus.planned, date: date, startHour: 18, startMinute: 30, endHour: 19, endMinute: 15, reward: RewardLevel.medium, difficulty: DifficultyLevel.hard, createdAt: createdAt, ), _task( id: 'free-slot', title: 'Free Slot', type: TaskType.freeSlot, status: TaskStatus.planned, date: date, startHour: 19, startMinute: 15, endHour: 20, endMinute: 15, reward: RewardLevel.notSet, difficulty: DifficultyLevel.notSet, createdAt: createdAt, ), _task( id: 'cleaned-kitchen-counter-late', title: 'Cleaned kitchen counter', type: TaskType.surprise, status: TaskStatus.completed, date: date, startHour: 20, startMinute: 15, endHour: 20, endMinute: 30, reward: RewardLevel.medium, difficulty: DifficultyLevel.medium, completedAt: _instant(date, 20, 20), createdAt: createdAt, ), ]; } static Task _task({ required String id, required String title, required TaskType type, required TaskStatus status, required CivilDate date, required int startHour, required int startMinute, required int endHour, required int endMinute, required RewardLevel reward, required DifficultyLevel difficulty, required DateTime createdAt, DateTime? completedAt, }) { final start = _instant(date, startHour, startMinute); final end = _instant(date, endHour, endMinute); return Task( id: id, title: title, projectId: projectId, type: type, status: status, priority: PriorityLevel.medium, reward: reward, difficulty: difficulty, durationMinutes: end.difference(start).inMinutes, scheduledStart: start, scheduledEnd: end, actualStart: status == TaskStatus.completed ? start : null, actualEnd: status == TaskStatus.completed ? end : null, completedAt: completedAt, createdAt: createdAt, updatedAt: completedAt ?? createdAt, ); } static DateTime _instant(CivilDate date, int hour, [int minute = 0]) { return DateTime.utc(date.year, date.month, date.day, hour, minute); } }