diff --git a/assets/home_banner.png b/assets/home_banner.png new file mode 100644 index 00000000..55c63c09 Binary files /dev/null and b/assets/home_banner.png differ diff --git a/lib/presentation/home/bloc/schedule_timer_bloc.dart b/lib/presentation/home/bloc/schedule_timer_bloc.dart index 8ea2adf4..3cec7714 100644 --- a/lib/presentation/home/bloc/schedule_timer_bloc.dart +++ b/lib/presentation/home/bloc/schedule_timer_bloc.dart @@ -9,6 +9,7 @@ part 'schedule_timer_state.dart'; class ScheduleTimerBloc extends Bloc { StreamSubscription? _tickerSubscription; + Timer? _initialTimer; DateTime? _scheduleTime; ScheduleTimerBloc() : super(const ScheduleTimerInitial()) { @@ -21,6 +22,7 @@ class ScheduleTimerBloc extends Bloc { @override Future close() { _tickerSubscription?.cancel(); + _initialTimer?.cancel(); return super.close(); } @@ -28,6 +30,7 @@ class ScheduleTimerBloc extends Bloc { ScheduleTimerStarted event, Emitter emit) { _scheduleTime = event.scheduleTime; _tickerSubscription?.cancel(); + _initialTimer?.cancel(); // Calculate initial time difference final now = DateTime.now(); @@ -48,7 +51,10 @@ class ScheduleTimerBloc extends Bloc { final secondsUntilNextMinute = 60 - now.second; // Create an initial timer to sync with minute boundaries - Timer(Duration(seconds: secondsUntilNextMinute), () { + _initialTimer = Timer(Duration(seconds: secondsUntilNextMinute), () { + // Check if bloc is still active before adding events + if (isClosed) return; + // After the initial sync, start the regular minute timer add(ScheduleTimerTicked(DateTime.now())); @@ -57,7 +63,10 @@ class ScheduleTimerBloc extends Bloc { const Duration(minutes: 1), (_) => DateTime.now(), ).listen((currentTime) { - add(ScheduleTimerTicked(currentTime)); + // Check if bloc is still active before adding events + if (!isClosed) { + add(ScheduleTimerTicked(currentTime)); + } }); }); } @@ -83,6 +92,7 @@ class ScheduleTimerBloc extends Bloc { void _onTimerStopped( ScheduleTimerStopped event, Emitter emit) { _tickerSubscription?.cancel(); + _initialTimer?.cancel(); _scheduleTime = null; emit(const ScheduleTimerInitial()); } @@ -91,6 +101,7 @@ class ScheduleTimerBloc extends Bloc { ScheduleTimerUpdated event, Emitter emit) { if (event.scheduleTime == null) { _tickerSubscription?.cancel(); + _initialTimer?.cancel(); _scheduleTime = null; emit(const ScheduleTimerInitial()); } else { diff --git a/lib/presentation/home/components/month_calendar.dart b/lib/presentation/home/components/month_calendar.dart new file mode 100644 index 00000000..d93df054 --- /dev/null +++ b/lib/presentation/home/components/month_calendar.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:on_time_front/presentation/calendar/bloc/monthly_schedules_bloc.dart'; +import 'package:on_time_front/presentation/shared/components/calendar/centered_calendar_header.dart'; +import 'package:on_time_front/presentation/shared/theme/calendar_theme.dart'; +import 'package:table_calendar/table_calendar.dart'; + +class MonthCalendar extends StatefulWidget { + const MonthCalendar({ + super.key, + required this.monthlySchedulesState, + this.dispatchBlocEvents = true, + }); + + final MonthlySchedulesState monthlySchedulesState; + final bool dispatchBlocEvents; + + @override + State createState() => _MonthCalendarState(); +} + +class _MonthCalendarState extends State { + late DateTime _focusedDay; + + @override + void initState() { + super.initState(); + _focusedDay = DateTime.now(); + } + + void _onLeftArrowTap() { + final DateTime nextFocusedDay = + DateTime(_focusedDay.year, _focusedDay.month - 1, 1); + setState(() { + _focusedDay = nextFocusedDay; + }); + if (widget.dispatchBlocEvents) { + context.read().add(MonthlySchedulesMonthAdded( + date: DateTime(nextFocusedDay.year, nextFocusedDay.month, 1))); + } + } + + void _onRightArrowTap() { + final DateTime nextFocusedDay = + DateTime(_focusedDay.year, _focusedDay.month + 1, 1); + setState(() { + _focusedDay = nextFocusedDay; + }); + if (widget.dispatchBlocEvents) { + context.read().add(MonthlySchedulesMonthAdded( + date: DateTime(nextFocusedDay.year, nextFocusedDay.month, 1))); + } + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final textTheme = theme.textTheme; + final calendarTheme = theme.extension()!; + + return Container( + padding: const EdgeInsets.all(16.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(11), + ), + child: TableCalendar( + locale: Localizations.localeOf(context).toString(), + eventLoader: (day) { + day = DateTime(day.year, day.month, day.day); + return widget.monthlySchedulesState.schedules[day] ?? []; + }, + sixWeekMonthsEnforced: true, + rowHeight: 50, + availableGestures: AvailableGestures.none, + focusedDay: _focusedDay, + firstDay: DateTime(2024, 1, 1), + lastDay: DateTime(2025, 12, 31), + calendarFormat: CalendarFormat.month, + headerStyle: calendarTheme.headerStyle, + daysOfWeekStyle: calendarTheme.daysOfWeekStyle, + daysOfWeekHeight: 40, + calendarStyle: calendarTheme.calendarStyle, + onDaySelected: (selectedDay, focusedDay) { + // Handle day selection if needed + }, + onPageChanged: (focusedDay) { + setState(() { + _focusedDay = focusedDay; + }); + if (widget.dispatchBlocEvents) { + context.read().add(MonthlySchedulesMonthAdded( + date: DateTime( + focusedDay.year, focusedDay.month, focusedDay.day))); + } + }, + calendarBuilders: CalendarBuilders( + headerTitleBuilder: (context, date) { + return CenteredCalendarHeader( + focusedMonth: date, + onLeftArrowTap: _onLeftArrowTap, + onRightArrowTap: _onRightArrowTap, + titleTextStyle: calendarTheme.headerStyle.titleTextStyle, + leftIcon: calendarTheme.headerStyle.leftChevronIcon, + rightIcon: calendarTheme.headerStyle.rightChevronIcon, + ); + }, + todayBuilder: (context, day, focusedDay) => Container( + margin: const EdgeInsets.all(4.0), + alignment: Alignment.center, + decoration: calendarTheme.todayDecoration, + child: Text( + day.day.toString(), + style: textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onPrimary, + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/presentation/home/screens/home_screen_tmp.dart b/lib/presentation/home/screens/home_screen_tmp.dart index 42055bb1..69cbecd8 100644 --- a/lib/presentation/home/screens/home_screen_tmp.dart +++ b/lib/presentation/home/screens/home_screen_tmp.dart @@ -7,71 +7,88 @@ import 'package:on_time_front/presentation/app/bloc/app_bloc.dart'; import 'package:on_time_front/presentation/calendar/bloc/monthly_schedules_bloc.dart'; import 'package:on_time_front/presentation/home/components/todays_schedule_tile.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:on_time_front/presentation/shared/components/calendar/centered_calendar_header.dart'; -import 'package:table_calendar/table_calendar.dart'; import 'package:on_time_front/presentation/shared/components/arc_indicator.dart'; import 'package:on_time_front/presentation/shared/theme/theme.dart'; -import 'package:on_time_front/presentation/shared/theme/calendar_theme.dart'; +import 'package:on_time_front/presentation/home/components/month_calendar.dart'; -class HomeScreenTmp extends StatefulWidget { +/// Wrapper widget that provides the BlocProvider for HomeScreenTmp +class HomeScreenTmp extends StatelessWidget { const HomeScreenTmp({super.key}); - @override - State createState() => _HomeScreenTmpState(); -} - -class _HomeScreenTmpState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { final dateOfToday = DateTime( DateTime.now().year, DateTime.now().month, DateTime.now().day, 0, 0, 0); - final double score = context.select((AppBloc bloc) => - bloc.state.user.mapOrNull((user) => user.score) ?? -1); - final colorScheme = Theme.of(context).colorScheme; return BlocProvider( create: (context) => getIt.get() ..add(MonthlySchedulesSubscriptionRequested(date: dateOfToday)), child: BlocBuilder( builder: (context, state) { - return SingleChildScrollView( + return HomeScreenContent(state: state); + }, + ), + ); + } +} + +/// The actual home screen content that can be tested independently +class HomeScreenContent extends StatelessWidget { + const HomeScreenContent({ + super.key, + required this.state, + this.userScore, + }); + + final MonthlySchedulesState state; + final double? userScore; + + @override + Widget build(BuildContext context) { + final double score = userScore ?? + context.select((AppBloc bloc) => + bloc.state.user.mapOrNull((user) => user.score) ?? -1); + final colorScheme = Theme.of(context).colorScheme; + + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + color: colorScheme.primary, + padding: const EdgeInsets.only(top: 58.0), child: Column( - mainAxisSize: MainAxisSize.max, children: [ - Container( - color: colorScheme.primary, - padding: const EdgeInsets.only(top: 58.0), - child: Column( - children: [ - _CharacterSection(score: score), - todaysScheduleOverlayBuilder(state), - ], - ), - ), - Container( - padding: const EdgeInsets.only( - top: 0.0, left: 16.0, right: 16.0, bottom: 24.0), - decoration: BoxDecoration( - color: colorScheme.surface, - ), - child: _MonthlySchedule( - monthlySchedulesState: state, - ), - ), + _CharacterSection(score: score), + _TodaysScheduleOverlay(state: state), ], ), - ); - }, + ), + Container( + padding: const EdgeInsets.only( + top: 0.0, left: 16.0, right: 16.0, bottom: 24.0), + decoration: BoxDecoration( + color: colorScheme.surface, + ), + child: _MonthlySchedule( + monthlySchedulesState: state, + ), + ), + ], ), ); } +} - Widget todaysScheduleOverlayBuilder(MonthlySchedulesState state) { +class _TodaysScheduleOverlay extends StatelessWidget { + const _TodaysScheduleOverlay({ + required this.state, + }); + + final MonthlySchedulesState state; + + @override + Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = Theme.of(context).colorScheme; @@ -89,10 +106,12 @@ class _HomeScreenTmpState extends State { padding: const EdgeInsets.only(top: 49.0), child: Container( decoration: BoxDecoration( - color: colorScheme.surface, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16))), + color: colorScheme.surface, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), ), ), ), @@ -142,7 +161,7 @@ class _MonthlySchedule extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ _MonthlyScheduleHeader(), - _MonthCalendar( + MonthCalendar( monthlySchedulesState: monthlySchedulesState, ), ], @@ -193,108 +212,7 @@ class _MonthlyScheduleHeader extends StatelessWidget { } } -class _MonthCalendar extends StatefulWidget { - const _MonthCalendar({required this.monthlySchedulesState}); - - final MonthlySchedulesState monthlySchedulesState; - - @override - State<_MonthCalendar> createState() => _MonthCalendarState(); -} - -class _MonthCalendarState extends State<_MonthCalendar> { - late DateTime _focusedDay; - - @override - void initState() { - super.initState(); - _focusedDay = DateTime.now(); - } - - void _onLeftArrowTap() { - setState(() { - _focusedDay = DateTime(_focusedDay.year, _focusedDay.month - 1, 1); - }); - } - - void _onRightArrowTap() { - setState(() { - _focusedDay = DateTime(_focusedDay.year, _focusedDay.month + 1, 1); - }); - } - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final textTheme = theme.textTheme; - final calendarTheme = theme.extension()!; - - if (widget.monthlySchedulesState.schedules.isEmpty) { - if (widget.monthlySchedulesState.status == - MonthlySchedulesStatus.loading) { - return CircularProgressIndicator(); - } else if (widget.monthlySchedulesState.status != - MonthlySchedulesStatus.success) { - return const SizedBox(); - } - } - - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(11), - ), - child: TableCalendar( - locale: Localizations.localeOf(context).toString(), - eventLoader: (day) { - day = DateTime(day.year, day.month, day.day); - return widget.monthlySchedulesState.schedules[day] ?? []; - }, - focusedDay: _focusedDay, - firstDay: DateTime(2024, 1, 1), - lastDay: DateTime(2025, 12, 31), - calendarFormat: CalendarFormat.month, - headerStyle: calendarTheme.headerStyle, - daysOfWeekStyle: calendarTheme.daysOfWeekStyle, - daysOfWeekHeight: 40, - calendarStyle: calendarTheme.calendarStyle, - onDaySelected: (selectedDay, focusedDay) { - // Handle day selection if needed - }, - onPageChanged: (focusedDay) { - setState(() { - _focusedDay = focusedDay; - }); - context.read().add(MonthlySchedulesMonthAdded( - date: - DateTime(focusedDay.year, focusedDay.month, focusedDay.day))); - }, - calendarBuilders: CalendarBuilders( - headerTitleBuilder: (context, date) { - return CenteredCalendarHeader( - focusedMonth: date, - onLeftArrowTap: _onLeftArrowTap, - onRightArrowTap: _onRightArrowTap, - titleTextStyle: calendarTheme.headerStyle.titleTextStyle, - leftIcon: calendarTheme.headerStyle.leftChevronIcon, - rightIcon: calendarTheme.headerStyle.rightChevronIcon, - ); - }, - todayBuilder: (context, day, focusedDay) => Container( - margin: const EdgeInsets.all(4.0), - alignment: Alignment.center, - decoration: calendarTheme.todayDecoration, - child: Text( - day.day.toString(), - style: textTheme.bodySmall?.copyWith( - color: theme.colorScheme.onPrimary, - ), - ), - ), - ), - ), - ); - } -} +// Moved MonthCalendar into components/month_calendar.dart class _CharacterSection extends StatelessWidget { const _CharacterSection({ @@ -305,20 +223,7 @@ class _CharacterSection extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 17.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(top: 27.0), - child: _Slogan(comment: AppLocalizations.of(context)!.slogan), - ), - _Character(), - ], - ), - ); + return Image.asset('home_banner.png', package: 'assets'); } } diff --git a/widgetbook/lib/home_screen_tmp.dart b/widgetbook/lib/home_screen_tmp.dart new file mode 100644 index 00000000..7e16b285 --- /dev/null +++ b/widgetbook/lib/home_screen_tmp.dart @@ -0,0 +1,178 @@ +import 'package:flutter/material.dart'; +import 'package:on_time_front/domain/entities/place_entity.dart'; +import 'package:on_time_front/domain/entities/schedule_entity.dart'; +import 'package:on_time_front/presentation/calendar/bloc/monthly_schedules_bloc.dart'; +import 'package:on_time_front/presentation/home/screens/home_screen_tmp.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +/// Mock data helper functions +PlaceEntity _createMockPlace({ + String id = "mock-place-1", + String name = "Gangnam Station", +}) { + return PlaceEntity( + id: id, + placeName: name, + ); +} + +ScheduleEntity _createMockSchedule({ + String id = "mock-schedule-1", + String name = "Team Meeting", + DateTime? scheduleTime, + PlaceEntity? place, + Duration moveTime = const Duration(minutes: 30), + Duration spareTime = const Duration(minutes: 15), + String note = "Important meeting with the team", +}) { + return ScheduleEntity( + id: id, + place: place ?? _createMockPlace(), + scheduleName: name, + scheduleTime: scheduleTime ?? DateTime.now().add(const Duration(hours: 2)), + moveTime: moveTime, + isChanged: false, + isStarted: false, + scheduleSpareTime: spareTime, + scheduleNote: note, + latenessTime: 0, + ); +} + +MonthlySchedulesState _createMockStateWithSchedules() { + final today = DateTime.now(); + final todayKey = DateTime(today.year, today.month, today.day); + + final mockSchedules = { + todayKey: [ + _createMockSchedule( + id: "schedule-1", + name: "Morning Meeting", + scheduleTime: DateTime(today.year, today.month, today.day, 9, 30), + place: _createMockPlace(id: "place-1", name: "Conference Room A"), + ), + _createMockSchedule( + id: "schedule-2", + name: "Lunch with Client", + scheduleTime: DateTime(today.year, today.month, today.day, 12, 30), + place: _createMockPlace(id: "place-2", name: "Restaurant Downtown"), + ), + ], + DateTime(today.year, today.month, today.day + 1): [ + _createMockSchedule( + id: "schedule-3", + name: "Project Review", + scheduleTime: DateTime(today.year, today.month, today.day + 1, 14, 0), + place: _createMockPlace(id: "place-3", name: "Office Building"), + ), + ], + DateTime(today.year, today.month, today.day + 2): [ + _createMockSchedule( + id: "schedule-4", + name: "Doctor Appointment", + scheduleTime: DateTime(today.year, today.month, today.day + 2, 10, 0), + place: _createMockPlace(id: "place-4", name: "Seoul Hospital"), + ), + _createMockSchedule( + id: "schedule-5", + name: "Gym Session", + scheduleTime: DateTime(today.year, today.month, today.day + 2, 18, 0), + place: _createMockPlace(id: "place-5", name: "Fitness Center"), + ), + ], + }; + + return MonthlySchedulesState( + status: MonthlySchedulesStatus.success, + schedules: mockSchedules, + startDate: DateTime(today.year, today.month, 1), + endDate: DateTime(today.year, today.month + 1, 0), + ); +} + +MonthlySchedulesState _createEmptyMockState() { + final today = DateTime.now(); + return MonthlySchedulesState( + status: MonthlySchedulesStatus.success, + schedules: const {}, + startDate: DateTime(today.year, today.month, 1), + endDate: DateTime(today.year, today.month + 1, 0), + ); +} + +MonthlySchedulesState _createLoadingMockState() { + return const MonthlySchedulesState( + status: MonthlySchedulesStatus.loading, + schedules: {}, + ); +} + +@widgetbook.UseCase( + name: 'With Multiple Schedules', + type: HomeScreenContent, +) +Widget homeScreenContentWithSchedulesUseCase(BuildContext context) { + return Scaffold( + body: HomeScreenContent( + state: _createMockStateWithSchedules(), + userScore: 85.0, + ), + ); +} + +@widgetbook.UseCase( + name: 'Empty State', + type: HomeScreenContent, +) +Widget homeScreenContentEmptyUseCase(BuildContext context) { + return Scaffold( + body: HomeScreenContent( + state: _createEmptyMockState(), + userScore: 75.0, + ), + ); +} + +@widgetbook.UseCase( + name: 'Loading State', + type: HomeScreenContent, +) +Widget homeScreenContentLoadingUseCase(BuildContext context) { + return Scaffold( + body: HomeScreenContent( + state: _createLoadingMockState(), + userScore: 90.0, + ), + ); +} + +@widgetbook.UseCase( + name: 'Today Only Schedule', + type: HomeScreenContent, +) +Widget homeScreenContentTodayOnlyUseCase(BuildContext context) { + final today = DateTime.now(); + final todayKey = DateTime(today.year, today.month, today.day); + + final mockState = MonthlySchedulesState( + status: MonthlySchedulesStatus.success, + schedules: { + todayKey: [ + _createMockSchedule( + name: "Important Meeting", + scheduleTime: DateTime(today.year, today.month, today.day, 15, 0), + place: _createMockPlace(name: "Conference Room"), + ), + ], + }, + startDate: DateTime(today.year, today.month, 1), + endDate: DateTime(today.year, today.month + 1, 0), + ); + + return Scaffold( + body: HomeScreenContent( + state: mockState, + userScore: 95.0, + ), + ); +} diff --git a/widgetbook/lib/main.dart b/widgetbook/lib/main.dart index c6a36385..a0822db5 100644 --- a/widgetbook/lib/main.dart +++ b/widgetbook/lib/main.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:on_time_front/presentation/shared/theme/theme.dart'; import 'package:on_time_front/l10n/app_localizations.dart'; import 'package:widgetbook/widgetbook.dart'; +import 'package:on_time_front/core/di/di_setup.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; // This file does not exist yet, @@ -9,6 +10,7 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; import 'main.directories.g.dart'; void main() { + configureDependencies(); runApp(const WidgetbookApp()); } @@ -44,11 +46,22 @@ class WidgetbookApp extends StatelessWidget { BuilderAddon( name: 'background', builder: (context, child) { + final mediaQuery = MediaQuery.maybeOf(context); + final wrappedChild = mediaQuery == null + ? child + : MediaQuery( + data: mediaQuery.copyWith( + padding: EdgeInsets.zero, + viewPadding: EdgeInsets.zero, + ), + child: child, + ); + return Container( height: double.infinity, width: double.infinity, color: Colors.white, - child: child, + child: wrappedChild, ); }, ), diff --git a/widgetbook/lib/month_calendar.dart b/widgetbook/lib/month_calendar.dart new file mode 100644 index 00000000..35eb8731 --- /dev/null +++ b/widgetbook/lib/month_calendar.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:on_time_front/presentation/home/components/month_calendar.dart'; +import 'package:on_time_front/domain/entities/schedule_entity.dart'; +import 'package:on_time_front/presentation/calendar/bloc/monthly_schedules_bloc.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +@widgetbook.UseCase( + name: 'Default', + type: MonthCalendar, +) +Widget monthCalendarUseCase(BuildContext context) { + // Build a simple fake schedules map for demonstration + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final Map> schedules = { + today: [], + DateTime(now.year, now.month, (now.day + 1)): [], + }; + + final MonthlySchedulesState state = MonthlySchedulesState( + status: MonthlySchedulesStatus.success, + schedules: schedules, + startDate: DateTime(now.year, now.month, 1), + endDate: DateTime(now.year, now.month + 1, 0), + ); + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MonthCalendar( + monthlySchedulesState: state, + dispatchBlocEvents: false, + ), + ], + ), + ); +} diff --git a/widgetbook/lib/todays_schedule_tile.dart b/widgetbook/lib/todays_schedule_tile.dart new file mode 100644 index 00000000..b1e8b813 --- /dev/null +++ b/widgetbook/lib/todays_schedule_tile.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:on_time_front/domain/entities/place_entity.dart'; +import 'package:on_time_front/domain/entities/schedule_entity.dart'; +import 'package:on_time_front/presentation/home/components/todays_schedule_tile.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +@widgetbook.UseCase( + name: 'No Schedule', + type: TodaysScheduleTile, +) +Widget todaysScheduleTileNoSchedule(BuildContext context) { + return const Padding( + padding: EdgeInsets.all(16.0), + child: TodaysScheduleTile(), + ); +} + +@widgetbook.UseCase( + name: 'With Schedule', + type: TodaysScheduleTile, +) +Widget todaysScheduleTileWithSchedule(BuildContext context) { + final scheduleName = context.knobs.string( + label: 'Schedule Name', + initialValue: '팀 미팅', + ); + final placeName = context.knobs.string( + label: 'Place Name', + initialValue: '강남역 스타벅스', + ); + final scheduleTime = context.knobs.dateTime( + label: 'Schedule Time', + initialValue: DateTime.now().add(const Duration(hours: 2)), + start: DateTime(2020, 1, 1), + end: DateTime(2030, 12, 31), + ); + + final schedule = ScheduleEntity( + id: 'schedule_1', + place: PlaceEntity(id: 'place_1', placeName: placeName), + scheduleName: scheduleName, + scheduleTime: scheduleTime, + moveTime: const Duration(minutes: 25), + isChanged: false, + isStarted: false, + scheduleSpareTime: const Duration(minutes: 15), + scheduleNote: '', + latenessTime: 0, + ); + + return Padding( + padding: const EdgeInsets.all(16.0), + child: TodaysScheduleTile(schedule: schedule), + ); +} diff --git a/widgetbook/lib/week_calendar.dart b/widgetbook/lib/week_calendar.dart new file mode 100644 index 00000000..317c68c3 --- /dev/null +++ b/widgetbook/lib/week_calendar.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:on_time_front/presentation/home/components/week_calendar.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +@widgetbook.UseCase( + name: 'Default', + type: WeekCalendar, +) +Widget weekCalendarUseCase(BuildContext context) { + final now = DateTime.now(); + final highlightedCount = context.knobs.double + .slider( + label: 'Highlighted Days', + min: 0, + max: 7, + initialValue: 2, + ) + .toInt(); + + final highlighted = List.generate( + highlightedCount, + (i) => DateTime(now.year, now.month, now.day + i), + ); + + return Padding( + padding: const EdgeInsets.all(16.0), + child: WeekCalendar( + date: now, + highlightedDates: highlighted, + onDateSelected: (d) {}, + ), + ); +}