finished schedules screens
This commit is contained in:
@@ -1,12 +1,31 @@
|
||||
import 'package:blind_master/BlindMasterResources/error_snackbar.dart';
|
||||
import 'package:blind_master/BlindMasterResources/secure_transmissions.dart';
|
||||
import 'package:blind_master/main.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class DayTimePicker extends StatefulWidget {
|
||||
const DayTimePicker({super.key, required this.defaultTime, required this.sendSchedule});
|
||||
const DayTimePicker({
|
||||
super.key,
|
||||
required this.defaultTime,
|
||||
required this.sendSchedule,
|
||||
required this.peripheralId,
|
||||
required this.peripheralNum,
|
||||
required this.deviceId,
|
||||
this.existingSchedule,
|
||||
this.scheduleId,
|
||||
});
|
||||
|
||||
final TimeOfDay defaultTime;
|
||||
final void Function(TimeOfDay) sendSchedule;
|
||||
final int peripheralId;
|
||||
final int peripheralNum;
|
||||
final int deviceId;
|
||||
final Map<String, dynamic>? existingSchedule;
|
||||
final String? scheduleId;
|
||||
|
||||
bool get isEditing => existingSchedule != null && scheduleId != null;
|
||||
|
||||
@override
|
||||
State<DayTimePicker> createState() => _DayTimePickerState();
|
||||
}
|
||||
@@ -16,10 +35,25 @@ class _DayTimePickerState extends State<DayTimePicker> {
|
||||
double _blindPosition = 0;
|
||||
String imagePath = "";
|
||||
Set<DaysOfWeek> days = <DaysOfWeek>{};
|
||||
bool showError = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// If editing, pre-populate with existing schedule data
|
||||
if (widget.isEditing && widget.existingSchedule != null) {
|
||||
final schedule = widget.existingSchedule!;
|
||||
final hour = schedule['schedule']['hours'][0] as int;
|
||||
final minute = schedule['schedule']['minutes'][0] as int;
|
||||
scheduleTime = TimeOfDay(hour: hour, minute: minute);
|
||||
_blindPosition = (schedule['pos'] as int).toDouble();
|
||||
|
||||
// Pre-populate days
|
||||
final daysOfWeek = schedule['schedule']['daysOfWeek'] as List;
|
||||
days = daysOfWeek.map((d) => DaysOfWeek.values[d as int]).toSet();
|
||||
}
|
||||
|
||||
updateBackground();
|
||||
}
|
||||
|
||||
@@ -84,30 +118,38 @@ class _DayTimePickerState extends State<DayTimePicker> {
|
||||
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(top: MediaQuery.of(context).size.width * 0.05),
|
||||
height: MediaQuery.of(context).size.width * 0.43,
|
||||
width: MediaQuery.of(context).size.width * 0.45,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: List.generate(10, (index) {
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
height: _blindPosition < 5 ?
|
||||
3.65 * (5 - _blindPosition)
|
||||
: 3.65 * (_blindPosition - 5),
|
||||
width: MediaQuery.of(context).size.width * 0.40, // example
|
||||
color: const Color.fromARGB(255, 121, 85, 72),
|
||||
);
|
||||
}),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final containerHeight = MediaQuery.of(context).size.width * 0.43;
|
||||
final maxSlatHeight = containerHeight / 10;
|
||||
final slatHeight = _blindPosition < 5
|
||||
? maxSlatHeight * (5 - _blindPosition) / 5
|
||||
: maxSlatHeight * (_blindPosition - 5) / 5;
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.only(top: MediaQuery.of(context).size.width * 0.05),
|
||||
height: containerHeight,
|
||||
width: MediaQuery.of(context).size.width * 0.45,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: List.generate(10, (index) {
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
height: slatHeight,
|
||||
width: MediaQuery.of(context).size.width * 0.40,
|
||||
color: const Color.fromARGB(255, 121, 85, 72),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
// Slider on the side
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.10,
|
||||
child: RotatedBox(
|
||||
quarterTurns: -1,
|
||||
child: Slider(
|
||||
@@ -171,6 +213,7 @@ class _DayTimePickerState extends State<DayTimePicker> {
|
||||
setState(() {
|
||||
if (selected) {
|
||||
days.add(day);
|
||||
showError = false;
|
||||
} else {
|
||||
days.remove(day);
|
||||
}
|
||||
@@ -179,6 +222,18 @@ class _DayTimePickerState extends State<DayTimePicker> {
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
if (showError)
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Text(
|
||||
'Please select at least one day',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
@@ -196,6 +251,78 @@ class _DayTimePickerState extends State<DayTimePicker> {
|
||||
),
|
||||
)
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (days.isEmpty) {
|
||||
setState(() {
|
||||
showError = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Convert DaysOfWeek enum to day numbers (0=Sunday, 1=Monday, etc.)
|
||||
final daysOfWeek = days.map((day) => day.index).toList();
|
||||
|
||||
final timeToUse = scheduleTime ?? widget.defaultTime;
|
||||
|
||||
final payload = {
|
||||
'periphId': widget.peripheralId,
|
||||
'periphNum': widget.peripheralNum,
|
||||
'deviceId': widget.deviceId,
|
||||
'newPos': _blindPosition.toInt(),
|
||||
'time': {
|
||||
'hour': timeToUse.hour,
|
||||
'minute': timeToUse.minute,
|
||||
},
|
||||
'daysOfWeek': daysOfWeek,
|
||||
};
|
||||
|
||||
// Add jobId if editing
|
||||
if (widget.isEditing) {
|
||||
payload['jobId'] = widget.scheduleId!;
|
||||
}
|
||||
|
||||
final endpoint = widget.isEditing ? 'update_schedule' : 'add_schedule';
|
||||
final response = await securePost(payload, endpoint);
|
||||
|
||||
if (response == null) throw Exception("Auth Error");
|
||||
|
||||
// Handle duplicate schedule (409 Conflict)
|
||||
if (response.statusCode == 409) {
|
||||
if (!context.mounted) return;
|
||||
Navigator.of(context).pop();
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('A schedule already exists at this time for this blind'),
|
||||
backgroundColor: Colors.orange,
|
||||
duration: Duration(seconds: 4),
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.statusCode != 201 && response.statusCode != 200) {
|
||||
throw Exception("Server Error");
|
||||
}
|
||||
|
||||
if (!context.mounted) return;
|
||||
Navigator.of(context).pop();
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(widget.isEditing ? 'Schedule updated successfully' : 'Schedule added successfully'),
|
||||
backgroundColor: Colors.green,
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(errorSnackbar(e));
|
||||
}
|
||||
},
|
||||
child: Text(widget.isEditing ? "Update" : "Add")
|
||||
)
|
||||
]
|
||||
)
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user