feat(breaks): Implement break functionality with Break entity and adapter

This commit is contained in:
Achintha Isuru
2026-02-16 13:26:04 -05:00
parent 2a0b39926a
commit 9b6cad3bde
7 changed files with 180 additions and 100 deletions

View File

@@ -141,6 +141,10 @@ class ShiftsRepositoryImpl
requiredSlots: app.shiftRole.count,
filledSlots: app.shiftRole.assigned ?? 0,
hasApplied: true,
breakInfo: BreakAdapter.fromData(
isPaid: app.shiftRole.isBreakPaid ?? false,
breakTime: app.shiftRole.breakType?.stringValue,
),
),
);
}
@@ -208,6 +212,10 @@ class ShiftsRepositoryImpl
requiredSlots: app.shiftRole.count,
filledSlots: app.shiftRole.assigned ?? 0,
hasApplied: true,
breakInfo: BreakAdapter.fromData(
isPaid: app.shiftRole.isBreakPaid ?? false,
breakTime: app.shiftRole.breakType?.stringValue,
),
),
);
}
@@ -277,6 +285,10 @@ class ShiftsRepositoryImpl
durationDays: sr.shift.durationDays,
requiredSlots: sr.count,
filledSlots: sr.assigned ?? 0,
breakInfo: BreakAdapter.fromData(
isPaid: sr.isBreakPaid ?? false,
breakTime: sr.breakType?.stringValue,
),
),
);
}
@@ -350,6 +362,10 @@ class ShiftsRepositoryImpl
filledSlots: sr.assigned ?? 0,
hasApplied: hasApplied,
totalValue: sr.totalValue,
breakInfo: BreakAdapter.fromData(
isPaid: sr.isBreakPaid ?? false,
breakTime: sr.breakType?.stringValue,
),
);
}
@@ -360,6 +376,7 @@ class ShiftsRepositoryImpl
int? required;
int? filled;
Break? breakInfo;
try {
final rolesRes = await executeProtected(() =>
_dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute());
@@ -370,6 +387,12 @@ class ShiftsRepositoryImpl
required = (required ?? 0) + r.count;
filled = (filled ?? 0) + (r.assigned ?? 0);
}
// Use the first role's break info as a representative
final firstRole = rolesRes.data.shiftRoles.first;
breakInfo = BreakAdapter.fromData(
isPaid: firstRole.isBreakPaid ?? false,
breakTime: firstRole.breakType?.stringValue,
);
}
} catch (_) {}
@@ -394,6 +417,7 @@ class ShiftsRepositoryImpl
durationDays: s.durationDays,
requiredSlots: required,
filledSlots: filled,
breakInfo: breakInfo,
);
}

View File

@@ -132,7 +132,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
return BlocProvider<ShiftDetailsBloc>(
create: (_) => Modular.get<ShiftDetailsBloc>()
..add(
LoadShiftDetailsEvent(widget.shiftId, roleId: widget.shift?.roleId),
LoadShiftDetailsEvent(widget.shiftId, roleId: widget.shift.roleId),
),
child: BlocListener<ShiftDetailsBloc, ShiftDetailsState>(
listener: (context, state) {
@@ -148,7 +148,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
);
Modular.to.toShifts(selectedDate: state.shiftDate);
} else if (state is ShiftDetailsError) {
if (_isApplying || widget.shift == null) {
if (_isApplying) {
UiSnackbar.show(
context,
message: translateErrorKey(state.message),
@@ -240,7 +240,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
const Divider(height: 1, thickness: 0.5),
// Date Section
// Date & Time Section
Padding(
padding: const EdgeInsets.all(UiConstants.space5),
child: Column(
@@ -248,8 +248,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
children: [
Text(
i18n.shift_date,
style: UiTypography
.titleUppercase4b
style: UiTypography.titleUppercase4b
.textSecondary,
),
const SizedBox(height: UiConstants.space2),
@@ -268,6 +267,24 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
),
],
),
const SizedBox(height: UiConstants.space4),
Row(
children: [
Expanded(
child: _buildTimeBox(
"CLOCK IN TIME",
displayShift.startTime,
),
),
const SizedBox(width: UiConstants.space4),
Expanded(
child: _buildTimeBox(
"CLOCK OUT TIME",
displayShift.endTime,
),
),
],
),
],
),
),
@@ -308,30 +325,6 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
const Divider(height: 1, thickness: 0.5),
// Time Section (New)
Padding(
padding: const EdgeInsets.all(UiConstants.space5),
child: Row(
children: [
Expanded(
child: _buildTimeBox(
"CLOCK IN TIME",
displayShift.startTime,
),
),
const SizedBox(width: UiConstants.space4),
Expanded(
child: _buildTimeBox(
"CLOCK OUT TIME",
displayShift.endTime,
),
),
],
),
),
const Divider(height: 1, thickness: 0.5),
// Location Section (New with Map)
Padding(
padding: const EdgeInsets.all(UiConstants.space5),
@@ -344,7 +337,6 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
.titleUppercase4b
.textSecondary,
),
const SizedBox(height: UiConstants.space3),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
@@ -366,12 +358,10 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
).showSnackBar(
SnackBar(
content: Text(
displayShift!
.locationAddress
displayShift.locationAddress
.isNotEmpty
? displayShift!
.locationAddress
: displayShift!.location,
? displayShift.locationAddress
: displayShift.location,
),
duration: const Duration(
seconds: 3,
@@ -509,36 +499,6 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
);
}
void _declineShift(BuildContext context, String id) {
final i18n = Translations.of(
context,
).staff_shifts.shift_details.decline_dialog;
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(i18n.title),
content: Text(i18n.message),
actions: [
TextButton(
onPressed: () => Modular.to.pop(),
child: Text(Translations.of(context).common.cancel),
),
TextButton(
onPressed: () {
BlocProvider.of<ShiftDetailsBloc>(
context,
).add(DeclineShiftDetailsEvent(id));
},
style: TextButton.styleFrom(foregroundColor: UiColors.destructive),
child: Text(
Translations.of(context).staff_shifts.shift_details.decline,
),
),
],
),
);
}
void _showApplyingDialog(BuildContext context, Shift shift) {
if (_actionDialogOpen) return;
_actionDialogOpen = true;