import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; class BuildScheduleView extends StatefulWidget { const BuildScheduleView({super.key, required this.status}); final WaterHeaterStatusModel status; @override _BuildScheduleViewState createState() => _BuildScheduleViewState(); } class _BuildScheduleViewState extends State { @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); return BlocProvider.value( value: bloc, child: Dialog( backgroundColor: Colors.white, insetPadding: const EdgeInsets.all(20), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), child: SizedBox( width: 700, child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20), child: BlocBuilder( builder: (context, state) { if (state is WaterHeaterDeviceStatusLoaded) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _scheduleHeader(context), const SizedBox(height: 20), _buildScheduleModeSelector(context, state), const SizedBox(height: 20), if (state.scheduleMode == ScheduleModes.schedule) _buildScheduleManagementUI(state), if (state.scheduleMode == ScheduleModes.countdown || state.scheduleMode == ScheduleModes.inching) ..._buildCountDownAngInchingView(context, state), const SizedBox(height: 20), _buildSaveStopCancelButtons(context, state), ], ); } return const SizedBox(); }, ), ), ), ), ), ); } Row _scheduleHeader(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const SizedBox(), Text( 'Scheduling', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 22, color: ColorsManager.dialogBlueTitle, ), ), Container( width: 25, decoration: BoxDecoration( color: Colors.transparent, shape: BoxShape.circle, border: Border.all( color: Colors.grey, width: 1.0, ), ), child: IconButton( padding: EdgeInsets.all(1), icon: const Icon( Icons.close, color: Colors.grey, size: 18, ), onPressed: () { Navigator.of(context).pop(); }, ), ), ], ); } Widget _buildScheduleModeSelector( BuildContext context, WaterHeaterDeviceStatusLoaded state) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Type:', style: context.textTheme.bodySmall!.copyWith( fontSize: 13, color: ColorsManager.grayColor, ), ), const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildRadioTile( context, 'Countdown', ScheduleModes.countdown, state), _buildRadioTile(context, 'Schedule', ScheduleModes.schedule, state), _buildRadioTile( context, 'Circulate', ScheduleModes.circulate, state), _buildRadioTile(context, 'Inching', ScheduleModes.inching, state), ], ), ], ); } Widget _buildRadioTile(BuildContext context, String label, ScheduleModes mode, WaterHeaterDeviceStatusLoaded state) { return Flexible( child: ListTile( contentPadding: EdgeInsets.zero, title: Text( label, style: context.textTheme.bodySmall!.copyWith( fontSize: 13, color: ColorsManager.blackColor, ), ), leading: Radio( value: mode, groupValue: state.scheduleMode, onChanged: (ScheduleModes? value) { if (value != null) { context.read().add(UpdateScheduleEvent( scheduleMode: value, hours: state.hours ?? 0, minutes: state.minutes ?? 0, )); } }, ), ), ); } Widget _buildScheduleManagementUI(WaterHeaterDeviceStatusLoaded state) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 170, height: 40, child: DefaultButton( borderColor: ColorsManager.boxColor, padding: 2, backgroundColor: ColorsManager.graysColor, borderRadius: 15, onPressed: () => _showAddScheduleDialog(context, schedule: null, index: null), child: Row( children: [ const Icon(Icons.add, color: ColorsManager.primaryColor), Text( ' Add new schedule', style: context.textTheme.bodySmall!.copyWith( color: ColorsManager.blackColor, ), ), ], ), ), ), const SizedBox(height: 20), _buildScheduleTable(state), ], ); } Widget _buildScheduleTable(WaterHeaterDeviceStatusLoaded state) { return Column( children: [ Table( border: TableBorder.all( color: ColorsManager.graysColor, borderRadius: const BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20)), ), children: [ TableRow( decoration: const BoxDecoration( color: ColorsManager.boxColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20), ), ), children: [ _buildTableHeader('Active'), _buildTableHeader('Days'), _buildTableHeader('Time'), _buildTableHeader('Function'), _buildTableHeader('Action'), ], ), ], ), Container( height: 200, decoration: BoxDecoration( border: Border.all(color: ColorsManager.graysColor), borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), ), child: state.schedules.isEmpty ? _buildEmptyState(context) : _buildTableBody(state), ), ], ); } Widget _buildEmptyState(BuildContext context) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ SvgPicture.asset(Assets.emptyRecords, width: 40, height: 40), const SizedBox(height: 8), Padding( padding: const EdgeInsets.all(8.0), child: Text( 'No schedules added yet', style: context.textTheme.bodySmall!.copyWith( fontSize: 13, color: ColorsManager.grayColor, ), ), ), ], ), ); } Widget _buildTableBody(WaterHeaterDeviceStatusLoaded state) { return SingleChildScrollView( child: Table( border: TableBorder.all(color: ColorsManager.graysColor), defaultVerticalAlignment: TableCellVerticalAlignment.middle, children: [ for (int i = 0; i < state.schedules.length; i++) _buildScheduleRow(state.schedules[i], i, context), ], ), ); } TableRow _buildScheduleRow( ScheduleEntry schedule, int index, BuildContext context) { return TableRow( children: [ Center( child: schedule.functionOn ? const Icon(Icons.radio_button_checked, color: ColorsManager.blueColor) : const Icon(Icons.radio_button_unchecked)), Center(child: Text(_getSelectedDays(schedule.selectedDays))), Center(child: Text(schedule.time.format(context))), Center(child: Text(schedule.functionOn ? 'On' : 'Off')), Center( child: Wrap( runAlignment: WrapAlignment.center, children: [ TextButton( style: TextButton.styleFrom(padding: EdgeInsets.zero), onPressed: () { _showAddScheduleDialog(context, schedule: schedule, index: index); }, child: Text( 'Edit', style: context.textTheme.bodySmall! .copyWith(color: ColorsManager.blueColor), ), ), TextButton( style: TextButton.styleFrom(padding: EdgeInsets.zero), onPressed: () { context .read() .add(DeleteScheduleEvent(index)); }, child: Text( 'Delete', style: context.textTheme.bodySmall! .copyWith(color: ColorsManager.blueColor), ), ), ], ), ), ], ); } String _getSelectedDays(List selectedDays) { final days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; List selectedDaysStr = []; for (int i = 0; i < selectedDays.length; i++) { if (selectedDays[i]) { selectedDaysStr.add(days[i]); } } return selectedDaysStr.join(', '); } Widget _buildTableHeader(String label) { return TableCell( child: Padding( padding: const EdgeInsets.all(12), child: Text( label, style: context.textTheme.bodySmall!.copyWith( fontSize: 13, color: ColorsManager.grayColor, ), ), ), ); } void _showAddScheduleDialog(BuildContext context, {ScheduleEntry? schedule, int? index}) { final bloc = context.read(); if (schedule != null) { bloc.add(InitializeAddScheduleEvent( selectedTime: schedule.time, selectedDays: schedule.selectedDays, functionOn: schedule.functionOn, isEditing: true, index: index, )); } else { bloc.add( const InitializeAddScheduleEvent( selectedDays: [false, false, false, false, false, false, false], functionOn: false, isEditing: false, index: null, selectedTime: null, ), ); } showDialog( context: context, builder: (ctx) { return BlocProvider.value( value: bloc, child: BlocBuilder( builder: (context, state) { if (state is WaterHeaterDeviceStatusLoaded) { return AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const SizedBox(), Text( 'Scheduling', style: context.textTheme.titleLarge!.copyWith( color: ColorsManager.dialogBlueTitle, fontWeight: FontWeight.bold, ), ), const SizedBox(), ], ), const SizedBox(height: 24), SizedBox( width: 150, height: 40, child: DefaultButton( padding: 8, backgroundColor: ColorsManager.boxColor, borderRadius: 15, onPressed: () async { TimeOfDay? time = await showTimePicker( context: context, initialTime: state.selectedTime ?? TimeOfDay.now(), ); if (time != null) { bloc.add(UpdateSelectedTimeEvent(time)); } }, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( state.selectedTime == null ? 'Time' : state.selectedTime!.format(context), style: context.textTheme.bodySmall!.copyWith( color: ColorsManager.grayColor, ), ), const Icon( Icons.access_time, color: ColorsManager.grayColor, size: 18, ), ], ), ), ), const SizedBox(height: 16), _buildDayCheckboxes(context, state.selectedDays), const SizedBox(height: 16), _buildFunctionSwitch(context, state.functionOn), ], ), actions: [ SizedBox( width: 200, child: DefaultButton( height: 40, onPressed: () { Navigator.pop(context); }, backgroundColor: ColorsManager.boxColor, child: Text( 'Cancel', style: context.textTheme.bodyMedium, ), ), ), SizedBox( width: 200, child: DefaultButton( height: 40, onPressed: () { if (state.selectedTime != null) { if (state.isEditing && index != null) { bloc.add(UpdateScheduleEntryEvent( deviceId: state.status.uuid, category: 'kg', functionOn: state.functionOn, )); } else { bloc.add(AddScheduleEvent( category: 'kg', time: state.selectedTime!, selectedDays: state.selectedDays, functionOn: state.functionOn, )); } Navigator.pop(context); } }, backgroundColor: ColorsManager.primaryColor, child: const Text('Save'), ), ), ], ); } return const SizedBox(); }, ), ); }, ); } Widget _buildDayCheckboxes(BuildContext context, List selectedDays) { return Row( children: List.generate(7, (index) { final dayLabels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; return Row( children: [ Checkbox( value: selectedDays[index], onChanged: (bool? value) { context .read() .add(UpdateSelectedDayEvent(index, value!)); }, ), Text(dayLabels[index]), ], ); }), ); } Widget _buildFunctionSwitch(BuildContext context, bool isOn) { return Row( children: [ Text( 'Function:', style: context.textTheme.bodySmall! .copyWith(color: ColorsManager.grayColor), ), const SizedBox(width: 10), Radio( value: true, groupValue: isOn, onChanged: (bool? value) { context .read() .add(const UpdateFunctionOnEvent(true)); }, ), const Text('On'), const SizedBox(width: 10), Radio( value: false, groupValue: isOn, onChanged: (bool? value) { context .read() .add(const UpdateFunctionOnEvent(false)); }, ), const Text('Off'), ], ); } Center _buildSaveStopCancelButtons( BuildContext context, WaterHeaterDeviceStatusLoaded state) { return Center( child: SizedBox( width: 400, height: 50, child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ Expanded( child: DefaultButton( height: 40, onPressed: () { Navigator.pop(context); }, backgroundColor: ColorsManager.boxColor, child: Text( 'Cancel', style: context.textTheme.bodyMedium, ), ), ), const SizedBox(width: 20), Expanded( child: (state.countdownRemaining != null && state.isActive == true) ? DefaultButton( height: 40, onPressed: () { late String code; if (state.scheduleMode == ScheduleModes.countdown) { code = 'countdown_1'; } else if (state.scheduleMode == ScheduleModes.inching) { code = 'switch_inching'; } context .read() .add(StopScheduleEvent(widget.status.uuid)); context.read().add( ToggleWaterHeaterEvent( deviceId: widget.status.uuid, code: code, value: 0, ), ); }, backgroundColor: Colors.red, child: const Text('Stop'), ) : DefaultButton( height: 40, onPressed: () { late String code; if (state.scheduleMode == ScheduleModes.countdown) { code = 'countdown_1'; } else if (state.scheduleMode == ScheduleModes.inching) { code = 'switch_inching'; } context.read().add( ToggleWaterHeaterEvent( deviceId: widget.status.uuid, code: code, value: Duration( hours: state.hours ?? 0, minutes: state.minutes ?? 0) .inSeconds, ), ); }, backgroundColor: ColorsManager.primaryColor, child: const Text('Save'), ), ), ], ), ), ); } List _buildCountDownAngInchingView( BuildContext context, WaterHeaterDeviceStatusLoaded state) { final isCountDown = state.scheduleMode?.name == ScheduleModes.countdown.name; return [ Text( isCountDown ? 'Countdown:' : 'Inching:', style: context.textTheme.bodySmall!.copyWith( fontSize: 13, color: ColorsManager.grayColor, ), ), const SizedBox(height: 8), Visibility( visible: !isCountDown, child: const Text( 'Once enabled this feature, each time the device is turned on, it will automatically turn of after a period time as pre-set.'), ), const SizedBox(height: 8), _hourMinutesWheel(state, context) ]; } Row _hourMinutesWheel( WaterHeaterDeviceStatusLoaded state, BuildContext context) { final isActive = (state.countdownRemaining != null && state.isActive == true); return Row( mainAxisAlignment: MainAxisAlignment.start, children: [ _buildPickerColumn(context, 'h', state.hours ?? 0, 24, (value) { context.read().add(UpdateScheduleEvent( scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, hours: value, minutes: state.minutes ?? 0, )); }, isActive: isActive), const SizedBox(width: 10), _buildPickerColumn(context, 'm', state.minutes ?? 0, 60, (value) { context.read().add(UpdateScheduleEvent( scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, hours: state.hours ?? 0, minutes: value, )); }, isActive: isActive), ], ); } Widget _buildPickerColumn(BuildContext context, String label, int initialValue, int itemCount, ValueChanged onSelected, {required bool isActive}) { return Row( mainAxisSize: MainAxisSize.min, children: [ Container( height: 40, width: 80, padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( color: ColorsManager.boxColor, borderRadius: BorderRadius.circular(8), ), child: ListWheelScrollView.useDelegate( key: ValueKey('$label-$initialValue'), controller: FixedExtentScrollController( initialItem: initialValue, ), itemExtent: 40.0, physics: const FixedExtentScrollPhysics(), onSelectedItemChanged: onSelected, childDelegate: ListWheelChildBuilderDelegate( builder: (context, index) { return Center( child: Text( index.toString().padLeft(2, '0'), style: TextStyle( fontSize: 24, color: isActive ? ColorsManager.grayColor : Colors.black, ), ), ); }, childCount: itemCount, ), ), ), const SizedBox(width: 8), Text( label, style: const TextStyle( color: ColorsManager.grayColor, fontSize: 18, ), ), ], ); } }