enum TimecardStatus { needsReview, approved, disputed } extension TimecardStatusX on TimecardStatus { String get label => switch (this) { TimecardStatus.needsReview => 'Needs review', TimecardStatus.approved => 'Approved', TimecardStatus.disputed => 'Disputed', }; } class Timecard { Timecard({ required this.id, required this.shiftId, required this.role, required this.business, required this.scheduledStart, required this.scheduledEnd, required this.clockIn, required this.clockOut, required this.breakMinutes, required this.rate, required this.status, required this.submittedAt, this.disputeNote, }); final String id; final String shiftId; final String role; final String business; final DateTime scheduledStart; final DateTime scheduledEnd; final DateTime clockIn; final DateTime clockOut; final int breakMinutes; final double rate; final TimecardStatus status; final DateTime submittedAt; final String? disputeNote; DateTime get date => DateTime(scheduledStart.year, scheduledStart.month, scheduledStart.day); Duration get worked { final d = clockOut.difference(clockIn) - Duration(minutes: breakMinutes); return d.isNegative ? Duration.zero : d; } double get amount => rate * worked.inMinutes / 60; /// Minutes late (positive) or early (negative) against the schedule. int get varianceMinutes => clockIn.difference(scheduledStart).inMinutes; Timecard copyWith({TimecardStatus? status, String? disputeNote}) => Timecard( id: id, shiftId: shiftId, role: role, business: business, scheduledStart: scheduledStart, scheduledEnd: scheduledEnd, clockIn: clockIn, clockOut: clockOut, breakMinutes: breakMinutes, rate: rate, status: status ?? this.status, submittedAt: submittedAt, disputeNote: disputeNote ?? this.disputeNote, ); }