enum ShiftStatus { open, booked, active, completed, cancelled, noShow } extension ShiftStatusX on ShiftStatus { String get label => switch (this) { ShiftStatus.open => 'Open', ShiftStatus.booked => 'Confirmed', ShiftStatus.active => 'In progress', ShiftStatus.completed => 'Completed', ShiftStatus.cancelled => 'Cancelled', ShiftStatus.noShow => 'No show', }; } class Shift { Shift({ required this.id, required this.role, required this.business, required this.venue, required this.address, required this.start, required this.end, required this.rate, required this.status, this.breakMinutes = 30, this.distanceMiles = 2.4, this.manager, this.clockIn, this.clockOut, this.requirements = const [], }); final String id; final String role; final String business; final String venue; final String address; final DateTime start; final DateTime end; final double rate; final ShiftStatus status; final int breakMinutes; final double distanceMiles; final String? manager; final DateTime? clockIn; final DateTime? clockOut; final List requirements; /// Paid duration: the scheduled window less the unpaid break. Duration get scheduledDuration => end.difference(start) - Duration(minutes: breakMinutes); /// What was actually worked, once clocked out. Duration? get workedDuration { if (clockIn == null || clockOut == null) return null; final d = clockOut!.difference(clockIn!) - Duration(minutes: breakMinutes); return d.isNegative ? Duration.zero : d; } double get scheduledPay => rate * scheduledDuration.inMinutes / 60; /// Gross, pre-tax. Falls back to the scheduled figure until clocked out. double get pay { if (status == ShiftStatus.cancelled || status == ShiftStatus.noShow) { return 0; } final worked = workedDuration; if (worked != null) return rate * worked.inMinutes / 60; return scheduledPay; } String get logoLetter => business.isEmpty ? '?' : business[0].toUpperCase(); Shift copyWith({ ShiftStatus? status, DateTime? clockIn, DateTime? clockOut, }) => Shift( id: id, role: role, business: business, venue: venue, address: address, start: start, end: end, rate: rate, status: status ?? this.status, breakMinutes: breakMinutes, distanceMiles: distanceMiles, manager: manager, clockIn: clockIn ?? this.clockIn, clockOut: clockOut ?? this.clockOut, requirements: requirements, ); }