Merge branch 'origin/dev' into feature/session-persistence-new

This commit is contained in:
2026-03-20 12:44:25 +05:30
162 changed files with 6978 additions and 1283 deletions

View File

@@ -16,6 +16,7 @@ export 'src/entities/enums/benefit_status.dart';
export 'src/entities/enums/business_status.dart';
export 'src/entities/enums/invoice_status.dart';
export 'src/entities/enums/onboarding_status.dart';
export 'src/entities/enums/day_of_week.dart';
export 'src/entities/enums/order_type.dart';
export 'src/entities/enums/payment_status.dart';
export 'src/entities/enums/review_issue_flag.dart';
@@ -25,6 +26,9 @@ export 'src/entities/enums/staff_skill.dart';
export 'src/entities/enums/staff_status.dart';
export 'src/entities/enums/user_role.dart';
// Utils
export 'src/core/utils/utc_parser.dart';
// Core
export 'src/core/services/api_services/api_endpoint.dart';
export 'src/core/services/api_services/api_response.dart';
@@ -66,8 +70,12 @@ export 'src/entities/shifts/completed_shift.dart';
export 'src/entities/shifts/shift_detail.dart';
// Orders
export 'src/entities/orders/order_item.dart';
export 'src/entities/orders/available_order.dart';
export 'src/entities/orders/available_order_schedule.dart';
export 'src/entities/orders/assigned_worker_summary.dart';
export 'src/entities/orders/booking_assigned_shift.dart';
export 'src/entities/orders/order_booking.dart';
export 'src/entities/orders/order_item.dart';
export 'src/entities/orders/order_preview.dart';
export 'src/entities/orders/recent_order.dart';
@@ -99,6 +107,7 @@ export 'src/entities/profile/accessibility.dart';
// Ratings
export 'src/entities/ratings/staff_rating.dart';
export 'src/entities/ratings/staff_reliability_stats.dart';
// Home
export 'src/entities/home/client_dashboard.dart';

View File

@@ -0,0 +1,6 @@
/// Parses a UTC ISO 8601 timestamp and converts to local device time.
DateTime parseUtcToLocal(String value) => DateTime.parse(value).toLocal();
/// Parses a nullable UTC ISO 8601 timestamp. Returns null if input is null.
DateTime? tryParseUtcToLocal(String? value) =>
value != null ? DateTime.parse(value).toLocal() : null;

View File

@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/enums/benefit_status.dart';
import '../../core/utils/utc_parser.dart';
/// A historical record of a staff benefit accrual period.
///
/// Returned by `GET /staff/profile/benefits/history`.
@@ -28,10 +30,8 @@ class BenefitHistory extends Equatable {
benefitType: json['benefitType'] as String,
title: json['title'] as String,
status: BenefitStatus.fromJson(json['status'] as String?),
effectiveAt: DateTime.parse(json['effectiveAt'] as String),
endedAt: json['endedAt'] != null
? DateTime.parse(json['endedAt'] as String)
: null,
effectiveAt: parseUtcToLocal(json['effectiveAt'] as String),
endedAt: tryParseUtcToLocal(json['endedAt'] as String?),
trackedHours: (json['trackedHours'] as num).toInt(),
targetHours: (json['targetHours'] as num).toInt(),
notes: json['notes'] as String?,

View File

@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/enums/business_status.dart';
import '../../core/utils/utc_parser.dart';
/// A client company registered on the platform.
///
/// Maps to the V2 `businesses` table.
@@ -35,12 +37,8 @@ class Business extends Equatable {
metadata: json['metadata'] is Map
? Map<String, dynamic>.from(json['metadata'] as Map<dynamic, dynamic>)
: const <String, dynamic>{},
createdAt: json['createdAt'] != null
? DateTime.parse(json['createdAt'] as String)
: null,
updatedAt: json['updatedAt'] != null
? DateTime.parse(json['updatedAt'] as String)
: null,
createdAt: tryParseUtcToLocal(json['createdAt'] as String?),
updatedAt: tryParseUtcToLocal(json['updatedAt'] as String?),
);
}

View File

@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/enums/attendance_status_type.dart';
import '../../core/utils/utc_parser.dart';
/// Current clock-in / attendance status of the staff member.
///
/// Returned by `GET /staff/clock-in/status`. When no open session exists
@@ -20,9 +22,7 @@ class AttendanceStatus extends Equatable {
activeShiftId: json['activeShiftId'] as String?,
attendanceStatus:
AttendanceStatusType.fromJson(json['attendanceStatus'] as String?),
clockInAt: json['clockInAt'] != null
? DateTime.parse(json['clockInAt'] as String)
: null,
clockInAt: tryParseUtcToLocal(json['clockInAt'] as String?),
);
}

View File

@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/enums/assignment_status.dart';
import '../../core/utils/utc_parser.dart';
/// A worker assigned to a coverage shift.
///
/// Nested within [ShiftWithWorkers].
@@ -13,6 +15,7 @@ class AssignedWorker extends Equatable {
required this.fullName,
required this.status,
this.checkInAt,
this.hasReview = false,
});
/// Deserialises an [AssignedWorker] from a V2 API JSON map.
@@ -22,9 +25,8 @@ class AssignedWorker extends Equatable {
staffId: json['staffId'] as String,
fullName: json['fullName'] as String,
status: AssignmentStatus.fromJson(json['status'] as String?),
checkInAt: json['checkInAt'] != null
? DateTime.parse(json['checkInAt'] as String)
: null,
checkInAt: tryParseUtcToLocal(json['checkInAt'] as String?),
hasReview: json['hasReview'] as bool? ?? false,
);
}
@@ -43,6 +45,9 @@ class AssignedWorker extends Equatable {
/// When the worker clocked in (null if not yet).
final DateTime? checkInAt;
/// Whether this worker has already been reviewed for this assignment.
final bool hasReview;
/// Serialises this [AssignedWorker] to a JSON map.
Map<String, dynamic> toJson() {
return <String, dynamic>{
@@ -51,6 +56,7 @@ class AssignedWorker extends Equatable {
'fullName': fullName,
'status': status.toJson(),
'checkInAt': checkInAt?.toIso8601String(),
'hasReview': hasReview,
};
}
@@ -61,5 +67,6 @@ class AssignedWorker extends Equatable {
fullName,
status,
checkInAt,
hasReview,
];
}

View File

@@ -15,6 +15,8 @@ class ShiftWithWorkers extends Equatable {
required this.requiredWorkerCount,
required this.assignedWorkerCount,
this.assignedWorkers = const <AssignedWorker>[],
this.locationName = '',
this.locationAddress = '',
});
/// Deserialises a [ShiftWithWorkers] from a V2 API JSON map.
@@ -30,6 +32,8 @@ class ShiftWithWorkers extends Equatable {
return ShiftWithWorkers(
shiftId: json['shiftId'] as String,
roleName: json['roleName'] as String? ?? '',
locationName: json['locationName'] as String? ?? '',
locationAddress: json['locationAddress'] as String? ?? '',
timeRange: TimeRange.fromJson(json['timeRange'] as Map<String, dynamic>),
requiredWorkerCount: (json['requiredWorkerCount'] as num).toInt(),
assignedWorkerCount: (json['assignedWorkerCount'] as num).toInt(),
@@ -55,11 +59,19 @@ class ShiftWithWorkers extends Equatable {
/// List of assigned workers with their statuses.
final List<AssignedWorker> assignedWorkers;
/// Location or hub name for this shift.
final String locationName;
/// Street address for this shift.
final String locationAddress;
/// Serialises this [ShiftWithWorkers] to a JSON map.
Map<String, dynamic> toJson() {
return <String, dynamic>{
'shiftId': shiftId,
'roleName': roleName,
'locationName': locationName,
'locationAddress': locationAddress,
'timeRange': timeRange.toJson(),
'requiredWorkerCount': requiredWorkerCount,
'assignedWorkerCount': assignedWorkerCount,
@@ -76,5 +88,7 @@ class ShiftWithWorkers extends Equatable {
requiredWorkerCount,
assignedWorkerCount,
assignedWorkers,
locationName,
locationAddress,
];
}

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// A time range with start and end timestamps.
///
/// Used within [ShiftWithWorkers] for shift time windows.
@@ -13,8 +15,8 @@ class TimeRange extends Equatable {
/// Deserialises a [TimeRange] from a V2 API JSON map.
factory TimeRange.fromJson(Map<String, dynamic> json) {
return TimeRange(
startsAt: DateTime.parse(json['startsAt'] as String),
endsAt: DateTime.parse(json['endsAt'] as String),
startsAt: parseUtcToLocal(json['startsAt'] as String),
endsAt: parseUtcToLocal(json['endsAt'] as String),
);
}

View File

@@ -0,0 +1,46 @@
/// Day of the week for order scheduling.
///
/// Maps to the `day_of_week` values used in V2 order schedule responses.
enum DayOfWeek {
/// Monday.
mon('MON'),
/// Tuesday.
tue('TUE'),
/// Wednesday.
wed('WED'),
/// Thursday.
thu('THU'),
/// Friday.
fri('FRI'),
/// Saturday.
sat('SAT'),
/// Sunday.
sun('SUN'),
/// Fallback for unrecognised API values.
unknown('UNKNOWN');
const DayOfWeek(this.value);
/// The V2 API string representation.
final String value;
/// Deserialises from a V2 API string with safe fallback.
static DayOfWeek fromJson(String? value) {
if (value == null) return DayOfWeek.unknown;
final String upper = value.toUpperCase();
for (final DayOfWeek day in DayOfWeek.values) {
if (day.value == upper) return day;
}
return DayOfWeek.unknown;
}
/// Serialises to the V2 API string.
String toJson() => value;
}

View File

@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/enums/invoice_status.dart';
import '../../core/utils/utc_parser.dart';
/// An invoice issued to a business for services rendered.
///
/// Returned by `GET /client/billing/invoices/*`.
@@ -25,12 +27,8 @@ class Invoice extends Equatable {
invoiceNumber: json['invoiceNumber'] as String,
amountCents: (json['amountCents'] as num).toInt(),
status: InvoiceStatus.fromJson(json['status'] as String?),
dueDate: json['dueDate'] != null
? DateTime.parse(json['dueDate'] as String)
: null,
paymentDate: json['paymentDate'] != null
? DateTime.parse(json['paymentDate'] as String)
: null,
dueDate: tryParseUtcToLocal(json['dueDate'] as String?),
paymentDate: tryParseUtcToLocal(json['paymentDate'] as String?),
vendorId: json['vendorId'] as String?,
vendorName: json['vendorName'] as String?,
);

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// A single data point in the staff payment chart.
///
/// Returned by `GET /staff/payments/chart`.
@@ -13,7 +15,7 @@ class PaymentChartPoint extends Equatable {
/// Deserialises a [PaymentChartPoint] from a V2 API JSON map.
factory PaymentChartPoint.fromJson(Map<String, dynamic> json) {
return PaymentChartPoint(
bucket: DateTime.parse(json['bucket'] as String),
bucket: parseUtcToLocal(json['bucket'] as String),
amountCents: (json['amountCents'] as num).toInt(),
);
}

View File

@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/enums/payment_status.dart';
import '../../core/utils/utc_parser.dart';
/// A single payment record for a staff member.
///
/// Returned by `GET /staff/payments/history`.
@@ -23,7 +25,7 @@ class PaymentRecord extends Equatable {
return PaymentRecord(
paymentId: json['paymentId'] as String,
amountCents: (json['amountCents'] as num).toInt(),
date: DateTime.parse(json['date'] as String),
date: parseUtcToLocal(json['date'] as String),
status: PaymentStatus.fromJson(json['status'] as String?),
shiftName: json['shiftName'] as String?,
location: json['location'] as String?,

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// A single time-card entry for a completed shift.
///
/// Returned by `GET /staff/profile/time-card`.
@@ -19,15 +21,11 @@ class TimeCardEntry extends Equatable {
/// Deserialises a [TimeCardEntry] from a V2 API JSON map.
factory TimeCardEntry.fromJson(Map<String, dynamic> json) {
return TimeCardEntry(
date: DateTime.parse(json['date'] as String),
date: parseUtcToLocal(json['date'] as String),
shiftName: json['shiftName'] as String,
location: json['location'] as String?,
clockInAt: json['clockInAt'] != null
? DateTime.parse(json['clockInAt'] as String)
: null,
clockOutAt: json['clockOutAt'] != null
? DateTime.parse(json['clockOutAt'] as String)
: null,
clockInAt: tryParseUtcToLocal(json['clockInAt'] as String?),
clockOutAt: tryParseUtcToLocal(json['clockOutAt'] as String?),
minutesWorked: (json['minutesWorked'] as num).toInt(),
hourlyRateCents: json['hourlyRateCents'] != null
? (json['hourlyRateCents'] as num).toInt()

View File

@@ -0,0 +1,145 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/enums/order_type.dart';
import 'package:krow_domain/src/entities/orders/available_order_schedule.dart';
/// An available order in the staff marketplace.
///
/// Returned by `GET /staff/orders/available`. Represents an order-level card
/// that a staff member can book into, containing role, location, pay rate,
/// and schedule details.
class AvailableOrder extends Equatable {
/// Creates an [AvailableOrder].
const AvailableOrder({
required this.orderId,
required this.orderType,
required this.roleId,
required this.roleCode,
required this.roleName,
this.clientName = '',
this.location = '',
this.locationAddress = '',
required this.hourlyRateCents,
required this.hourlyRate,
required this.requiredWorkerCount,
required this.filledCount,
required this.instantBook,
this.dispatchTeam = '',
this.dispatchPriority = 0,
required this.schedule,
});
/// Deserialises from the V2 API JSON response.
factory AvailableOrder.fromJson(Map<String, dynamic> json) {
return AvailableOrder(
orderId: json['orderId'] as String,
orderType: OrderType.fromJson(json['orderType'] as String?),
roleId: json['roleId'] as String,
roleCode: json['roleCode'] as String? ?? '',
roleName: json['roleName'] as String? ?? '',
clientName: json['clientName'] as String? ?? '',
location: json['location'] as String? ?? '',
locationAddress: json['locationAddress'] as String? ?? '',
hourlyRateCents: json['hourlyRateCents'] as int? ?? 0,
hourlyRate: (json['hourlyRate'] as num?)?.toDouble() ?? 0.0,
requiredWorkerCount: json['requiredWorkerCount'] as int? ?? 1,
filledCount: json['filledCount'] as int? ?? 0,
instantBook: json['instantBook'] as bool? ?? false,
dispatchTeam: json['dispatchTeam'] as String? ?? '',
dispatchPriority: json['dispatchPriority'] as int? ?? 0,
schedule: AvailableOrderSchedule.fromJson(
json['schedule'] as Map<String, dynamic>,
),
);
}
/// The order row id.
final String orderId;
/// Type of order (one-time, recurring, permanent, etc.).
final OrderType orderType;
/// The shift-role row id.
final String roleId;
/// Machine-readable role code.
final String roleCode;
/// Display name of the role.
final String roleName;
/// Name of the client/business offering this order.
final String clientName;
/// Human-readable location label.
final String location;
/// Full street address of the location.
final String locationAddress;
/// Pay rate in cents per hour.
final int hourlyRateCents;
/// Pay rate in dollars per hour.
final double hourlyRate;
/// Total number of workers required for this role.
final int requiredWorkerCount;
/// Number of positions already filled.
final int filledCount;
/// Whether the order supports instant booking (no approval needed).
final bool instantBook;
/// Dispatch team identifier.
final String dispatchTeam;
/// Priority level for dispatch ordering.
final int dispatchPriority;
/// Schedule details including recurrence, times, and bounding timestamps.
final AvailableOrderSchedule schedule;
/// Serialises to JSON.
Map<String, dynamic> toJson() {
return <String, dynamic>{
'orderId': orderId,
'orderType': orderType.toJson(),
'roleId': roleId,
'roleCode': roleCode,
'roleName': roleName,
'clientName': clientName,
'location': location,
'locationAddress': locationAddress,
'hourlyRateCents': hourlyRateCents,
'hourlyRate': hourlyRate,
'requiredWorkerCount': requiredWorkerCount,
'filledCount': filledCount,
'instantBook': instantBook,
'dispatchTeam': dispatchTeam,
'dispatchPriority': dispatchPriority,
'schedule': schedule.toJson(),
};
}
@override
List<Object?> get props => <Object?>[
orderId,
orderType,
roleId,
roleCode,
roleName,
clientName,
location,
locationAddress,
hourlyRateCents,
hourlyRate,
requiredWorkerCount,
filledCount,
instantBook,
dispatchTeam,
dispatchPriority,
schedule,
];
}

View File

@@ -0,0 +1,99 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
import 'package:krow_domain/src/entities/enums/day_of_week.dart';
/// Schedule details for an available order in the marketplace.
///
/// Contains the recurrence pattern, time window, and bounding timestamps
/// for the order's shifts.
class AvailableOrderSchedule extends Equatable {
/// Creates an [AvailableOrderSchedule].
const AvailableOrderSchedule({
required this.totalShifts,
required this.startDate,
required this.endDate,
required this.daysOfWeek,
required this.startTime,
required this.endTime,
required this.timezone,
required this.firstShiftStartsAt,
required this.lastShiftEndsAt,
});
/// Deserialises from the V2 API JSON response.
factory AvailableOrderSchedule.fromJson(Map<String, dynamic> json) {
return AvailableOrderSchedule(
totalShifts: json['totalShifts'] as int? ?? 0,
startDate: json['startDate'] as String? ?? '',
endDate: json['endDate'] as String? ?? '',
daysOfWeek: (json['daysOfWeek'] as List<dynamic>?)
?.map(
(dynamic e) => DayOfWeek.fromJson(e as String),
)
.toList() ??
<DayOfWeek>[],
startTime: json['startTime'] as String? ?? '',
endTime: json['endTime'] as String? ?? '',
timezone: json['timezone'] as String? ?? 'UTC',
firstShiftStartsAt:
parseUtcToLocal(json['firstShiftStartsAt'] as String),
lastShiftEndsAt: parseUtcToLocal(json['lastShiftEndsAt'] as String),
);
}
/// Total number of shifts in this schedule.
final int totalShifts;
/// Date-only start string (e.g. "2026-03-24").
final String startDate;
/// Date-only end string.
final String endDate;
/// Days of the week the order repeats on.
final List<DayOfWeek> daysOfWeek;
/// Daily start time display string (e.g. "09:00").
final String startTime;
/// Daily end time display string (e.g. "15:00").
final String endTime;
/// IANA timezone identifier (e.g. "America/Los_Angeles").
final String timezone;
/// UTC timestamp of the first shift's start, converted to local time.
final DateTime firstShiftStartsAt;
/// UTC timestamp of the last shift's end, converted to local time.
final DateTime lastShiftEndsAt;
/// Serialises to JSON.
Map<String, dynamic> toJson() => <String, dynamic>{
'totalShifts': totalShifts,
'startDate': startDate,
'endDate': endDate,
'daysOfWeek':
daysOfWeek.map((DayOfWeek e) => e.toJson()).toList(),
'startTime': startTime,
'endTime': endTime,
'timezone': timezone,
'firstShiftStartsAt':
firstShiftStartsAt.toUtc().toIso8601String(),
'lastShiftEndsAt': lastShiftEndsAt.toUtc().toIso8601String(),
};
@override
List<Object?> get props => <Object?>[
totalShifts,
startDate,
endDate,
daysOfWeek,
startTime,
endTime,
timezone,
firstShiftStartsAt,
lastShiftEndsAt,
];
}

View File

@@ -0,0 +1,92 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
/// A shift assigned to a staff member as part of an order booking.
///
/// Returned within the `assignedShifts` array of the
/// `POST /staff/orders/:orderId/book` response.
class BookingAssignedShift extends Equatable {
/// Creates a [BookingAssignedShift].
const BookingAssignedShift({
required this.shiftId,
required this.date,
required this.startsAt,
required this.endsAt,
required this.startTime,
required this.endTime,
required this.timezone,
required this.assignmentId,
this.assignmentStatus = '',
});
/// Deserialises from the V2 API JSON response.
factory BookingAssignedShift.fromJson(Map<String, dynamic> json) {
return BookingAssignedShift(
shiftId: json['shiftId'] as String,
date: json['date'] as String? ?? '',
startsAt: parseUtcToLocal(json['startsAt'] as String),
endsAt: parseUtcToLocal(json['endsAt'] as String),
startTime: json['startTime'] as String? ?? '',
endTime: json['endTime'] as String? ?? '',
timezone: json['timezone'] as String? ?? 'UTC',
assignmentId: json['assignmentId'] as String,
assignmentStatus: json['assignmentStatus'] as String? ?? '',
);
}
/// The shift row id.
final String shiftId;
/// Date-only display string (e.g. "2026-03-24").
final String date;
/// UTC start timestamp converted to local time.
final DateTime startsAt;
/// UTC end timestamp converted to local time.
final DateTime endsAt;
/// Display start time string (e.g. "09:00").
final String startTime;
/// Display end time string (e.g. "15:00").
final String endTime;
/// IANA timezone identifier.
final String timezone;
/// The assignment row id linking staff to this shift.
final String assignmentId;
/// Current status of the assignment (e.g. "ASSIGNED").
final String assignmentStatus;
/// Serialises to JSON.
Map<String, dynamic> toJson() {
return <String, dynamic>{
'shiftId': shiftId,
'date': date,
'startsAt': startsAt.toUtc().toIso8601String(),
'endsAt': endsAt.toUtc().toIso8601String(),
'startTime': startTime,
'endTime': endTime,
'timezone': timezone,
'assignmentId': assignmentId,
'assignmentStatus': assignmentStatus,
};
}
@override
List<Object?> get props => <Object?>[
shiftId,
date,
startsAt,
endsAt,
startTime,
endTime,
timezone,
assignmentId,
assignmentStatus,
];
}

View File

@@ -0,0 +1,94 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/orders/booking_assigned_shift.dart';
/// Result of booking an order via `POST /staff/orders/:orderId/book`.
///
/// Contains the booking metadata and the list of shifts assigned to the
/// staff member as part of this booking.
class OrderBooking extends Equatable {
/// Creates an [OrderBooking].
const OrderBooking({
required this.bookingId,
required this.orderId,
required this.roleId,
this.roleCode = '',
this.roleName = '',
required this.assignedShiftCount,
this.status = 'PENDING',
this.assignedShifts = const <BookingAssignedShift>[],
});
/// Deserialises from the V2 API JSON response.
factory OrderBooking.fromJson(Map<String, dynamic> json) {
return OrderBooking(
bookingId: json['bookingId'] as String,
orderId: json['orderId'] as String,
roleId: json['roleId'] as String,
roleCode: json['roleCode'] as String? ?? '',
roleName: json['roleName'] as String? ?? '',
assignedShiftCount: json['assignedShiftCount'] as int? ?? 0,
status: json['status'] as String? ?? 'PENDING',
assignedShifts: (json['assignedShifts'] as List<dynamic>?)
?.map(
(dynamic e) => BookingAssignedShift.fromJson(
e as Map<String, dynamic>,
),
)
.toList() ??
<BookingAssignedShift>[],
);
}
/// Unique booking identifier.
final String bookingId;
/// The order this booking belongs to.
final String orderId;
/// The role row id within the order.
final String roleId;
/// Machine-readable role code.
final String roleCode;
/// Display name of the role.
final String roleName;
/// Number of shifts assigned in this booking.
final int assignedShiftCount;
/// Booking status (e.g. "PENDING", "CONFIRMED").
final String status;
/// The individual shifts assigned as part of this booking.
final List<BookingAssignedShift> assignedShifts;
/// Serialises to JSON.
Map<String, dynamic> toJson() {
return <String, dynamic>{
'bookingId': bookingId,
'orderId': orderId,
'roleId': roleId,
'roleCode': roleCode,
'roleName': roleName,
'assignedShiftCount': assignedShiftCount,
'status': status,
'assignedShifts': assignedShifts
.map((BookingAssignedShift e) => e.toJson())
.toList(),
};
}
@override
List<Object?> get props => <Object?>[
bookingId,
orderId,
roleId,
roleCode,
roleName,
assignedShiftCount,
status,
assignedShifts,
];
}

View File

@@ -3,6 +3,7 @@ import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/enums/order_type.dart';
import 'package:krow_domain/src/entities/enums/shift_status.dart';
import '../../core/utils/utc_parser.dart';
import 'assigned_worker_summary.dart';
/// A line item within an order, representing a role needed for a shift.
@@ -25,6 +26,16 @@ class OrderItem extends Equatable {
this.locationName,
required this.status,
this.workers = const <AssignedWorkerSummary>[],
this.eventName = '',
this.clientName = '',
this.hourlyRate = 0.0,
this.hours = 0.0,
this.totalValue = 0.0,
this.locationAddress,
this.startTime,
this.endTime,
this.hubManagerId,
this.hubManagerName,
});
/// Deserialises an [OrderItem] from a V2 API JSON map.
@@ -42,9 +53,9 @@ class OrderItem extends Equatable {
orderId: json['orderId'] as String,
orderType: OrderType.fromJson(json['orderType'] as String?),
roleName: json['roleName'] as String,
date: DateTime.parse(json['date'] as String),
startsAt: DateTime.parse(json['startsAt'] as String),
endsAt: DateTime.parse(json['endsAt'] as String),
date: parseUtcToLocal(json['date'] as String),
startsAt: parseUtcToLocal(json['startsAt'] as String),
endsAt: parseUtcToLocal(json['endsAt'] as String),
requiredWorkerCount: (json['requiredWorkerCount'] as num).toInt(),
filledCount: (json['filledCount'] as num).toInt(),
hourlyRateCents: (json['hourlyRateCents'] as num).toInt(),
@@ -52,6 +63,16 @@ class OrderItem extends Equatable {
locationName: json['locationName'] as String?,
status: ShiftStatus.fromJson(json['status'] as String?),
workers: workersList,
eventName: json['eventName'] as String? ?? '',
clientName: json['clientName'] as String? ?? '',
hourlyRate: (json['hourlyRate'] as num?)?.toDouble() ?? 0.0,
hours: (json['hours'] as num?)?.toDouble() ?? 0.0,
totalValue: (json['totalValue'] as num?)?.toDouble() ?? 0.0,
locationAddress: json['locationAddress'] as String?,
startTime: json['startTime'] as String?,
endTime: json['endTime'] as String?,
hubManagerId: json['hubManagerId'] as String?,
hubManagerName: json['hubManagerName'] as String?,
);
}
@@ -97,6 +118,36 @@ class OrderItem extends Equatable {
/// Assigned workers for this line item.
final List<AssignedWorkerSummary> workers;
/// Event/order name.
final String eventName;
/// Client/business name.
final String clientName;
/// Billing rate in dollars per hour.
final double hourlyRate;
/// Duration of the shift in fractional hours.
final double hours;
/// Total cost in dollars (rate x workers x hours).
final double totalValue;
/// Full street address of the location.
final String? locationAddress;
/// Display start time string (HH:MM UTC).
final String? startTime;
/// Display end time string (HH:MM UTC).
final String? endTime;
/// Hub manager's business membership ID.
final String? hubManagerId;
/// Hub manager's display name.
final String? hubManagerName;
/// Serialises this [OrderItem] to a JSON map.
Map<String, dynamic> toJson() {
return <String, dynamic>{
@@ -114,6 +165,16 @@ class OrderItem extends Equatable {
'locationName': locationName,
'status': status.toJson(),
'workers': workers.map((AssignedWorkerSummary w) => w.toJson()).toList(),
'eventName': eventName,
'clientName': clientName,
'hourlyRate': hourlyRate,
'hours': hours,
'totalValue': totalValue,
'locationAddress': locationAddress,
'startTime': startTime,
'endTime': endTime,
'hubManagerId': hubManagerId,
'hubManagerName': hubManagerName,
};
}
@@ -133,5 +194,15 @@ class OrderItem extends Equatable {
locationName,
status,
workers,
eventName,
clientName,
hourlyRate,
hours,
totalValue,
locationAddress,
startTime,
endTime,
hubManagerId,
hubManagerName,
];
}

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// A preview of an order for reordering purposes.
///
/// Returned by `GET /client/orders/:id/reorder-preview`.
@@ -31,12 +33,8 @@ class OrderPreview extends Equatable {
orderId: json['orderId'] as String,
title: json['title'] as String,
description: json['description'] as String?,
startsAt: json['startsAt'] != null
? DateTime.parse(json['startsAt'] as String)
: null,
endsAt: json['endsAt'] != null
? DateTime.parse(json['endsAt'] as String)
: null,
startsAt: tryParseUtcToLocal(json['startsAt'] as String?),
endsAt: tryParseUtcToLocal(json['endsAt'] as String?),
locationName: json['locationName'] as String?,
locationAddress: json['locationAddress'] as String?,
metadata: json['metadata'] is Map
@@ -128,8 +126,8 @@ class OrderPreviewShift extends Equatable {
shiftId: json['shiftId'] as String,
shiftCode: json['shiftCode'] as String,
title: json['title'] as String,
startsAt: DateTime.parse(json['startsAt'] as String),
endsAt: DateTime.parse(json['endsAt'] as String),
startsAt: parseUtcToLocal(json['startsAt'] as String),
endsAt: parseUtcToLocal(json['endsAt'] as String),
roles: rolesList,
);
}

View File

@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/entities/enums/order_type.dart';
import '../../core/utils/utc_parser.dart';
/// A recently completed order available for reordering.
///
/// Returned by `GET /client/reorders`.
@@ -21,9 +23,7 @@ class RecentOrder extends Equatable {
return RecentOrder(
id: json['id'] as String,
title: json['title'] as String,
date: json['date'] != null
? DateTime.parse(json['date'] as String)
: null,
date: tryParseUtcToLocal(json['date'] as String?),
hubName: json['hubName'] as String?,
positionCount: (json['positionCount'] as num).toInt(),
orderType: OrderType.fromJson(json['orderType'] as String?),

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// Status of a staff certificate.
enum CertificateStatus {
/// Certificate uploaded, pending verification.
@@ -45,8 +47,8 @@ class StaffCertificate extends Equatable {
fileUri: json['fileUri'] as String?,
issuer: json['issuer'] as String?,
certificateNumber: json['certificateNumber'] as String?,
issuedAt: json['issuedAt'] != null ? DateTime.parse(json['issuedAt'] as String) : null,
expiresAt: json['expiresAt'] != null ? DateTime.parse(json['expiresAt'] as String) : null,
issuedAt: tryParseUtcToLocal(json['issuedAt'] as String?),
expiresAt: tryParseUtcToLocal(json['expiresAt'] as String?),
status: _parseStatus(json['status'] as String?),
verificationStatus: json['verificationStatus'] as String?,
);

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// Status of a profile document.
enum ProfileDocumentStatus {
/// Document has not been uploaded yet.
@@ -59,7 +61,7 @@ class ProfileDocument extends Equatable {
staffDocumentId: json['staffDocumentId'] as String?,
fileUri: json['fileUri'] as String?,
status: _parseStatus(json['status'] as String?),
expiresAt: json['expiresAt'] != null ? DateTime.parse(json['expiresAt'] as String) : null,
expiresAt: tryParseUtcToLocal(json['expiresAt'] as String?),
metadata: (json['metadata'] as Map<String, dynamic>?) ?? const <String, dynamic>{},
);
}

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// A review left for a staff member after an assignment.
///
/// Maps to the V2 `staff_reviews` table.
@@ -35,9 +37,7 @@ class StaffRating extends Equatable {
rating: (json['rating'] as num).toInt(),
reviewText: json['reviewText'] as String?,
tags: tagsList,
createdAt: json['createdAt'] != null
? DateTime.parse(json['createdAt'] as String)
: null,
createdAt: tryParseUtcToLocal(json['createdAt'] as String?),
);
}

View File

@@ -0,0 +1,84 @@
import 'package:equatable/equatable.dart';
/// Aggregated reliability and performance statistics for a staff member.
///
/// Returned by `GET /staff/profile/stats`.
class StaffReliabilityStats extends Equatable {
/// Creates a [StaffReliabilityStats] instance.
const StaffReliabilityStats({
required this.staffId,
this.totalShifts = 0,
this.averageRating = 0,
this.ratingCount = 0,
this.onTimeRate = 0,
this.noShowCount = 0,
this.cancellationCount = 0,
this.reliabilityScore = 0,
});
/// Deserialises from a V2 API JSON map.
factory StaffReliabilityStats.fromJson(Map<String, dynamic> json) {
return StaffReliabilityStats(
staffId: json['staffId'] as String,
totalShifts: (json['totalShifts'] as num?)?.toInt() ?? 0,
averageRating: (json['averageRating'] as num?)?.toDouble() ?? 0,
ratingCount: (json['ratingCount'] as num?)?.toInt() ?? 0,
onTimeRate: (json['onTimeRate'] as num?)?.toDouble() ?? 0,
noShowCount: (json['noShowCount'] as num?)?.toInt() ?? 0,
cancellationCount: (json['cancellationCount'] as num?)?.toInt() ?? 0,
reliabilityScore: (json['reliabilityScore'] as num?)?.toDouble() ?? 0,
);
}
/// The staff member's unique identifier.
final String staffId;
/// Total completed shifts.
final int totalShifts;
/// Average rating from client reviews (0-5).
final double averageRating;
/// Number of ratings received.
final int ratingCount;
/// Percentage of shifts clocked in on time (0-100).
final double onTimeRate;
/// Number of no-show incidents.
final int noShowCount;
/// Number of worker-initiated cancellations.
final int cancellationCount;
/// Composite reliability score (0-100).
///
/// Weighted: 45% on-time rate + 35% completion rate + 20% rating score.
final double reliabilityScore;
/// Serialises to a JSON map.
Map<String, dynamic> toJson() {
return <String, dynamic>{
'staffId': staffId,
'totalShifts': totalShifts,
'averageRating': averageRating,
'ratingCount': ratingCount,
'onTimeRate': onTimeRate,
'noShowCount': noShowCount,
'cancellationCount': cancellationCount,
'reliabilityScore': reliabilityScore,
};
}
@override
List<Object?> get props => <Object?>[
staffId,
totalShifts,
averageRating,
ratingCount,
onTimeRate,
noShowCount,
cancellationCount,
reliabilityScore,
];
}

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// Coverage report with daily breakdown.
///
/// Returned by `GET /client/reports/coverage`.
@@ -75,7 +77,7 @@ class CoverageDayPoint extends Equatable {
/// Deserialises a [CoverageDayPoint] from a V2 API JSON map.
factory CoverageDayPoint.fromJson(Map<String, dynamic> json) {
return CoverageDayPoint(
day: DateTime.parse(json['day'] as String),
day: parseUtcToLocal(json['day'] as String),
needed: (json['needed'] as num).toInt(),
filled: (json['filled'] as num).toInt(),
coveragePercentage: (json['coveragePercentage'] as num).toDouble(),

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// Staffing and spend forecast report.
///
/// Returned by `GET /client/reports/forecast`.
@@ -83,7 +85,7 @@ class ForecastWeek extends Equatable {
/// Deserialises a [ForecastWeek] from a V2 API JSON map.
factory ForecastWeek.fromJson(Map<String, dynamic> json) {
return ForecastWeek(
week: DateTime.parse(json['week'] as String),
week: parseUtcToLocal(json['week'] as String),
shiftCount: (json['shiftCount'] as num).toInt(),
workerHours: (json['workerHours'] as num).toDouble(),
forecastSpendCents: (json['forecastSpendCents'] as num).toInt(),

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// No-show report with per-worker incident details.
///
/// Returned by `GET /client/reports/no-show`.
@@ -143,7 +145,7 @@ class NoShowIncident extends Equatable {
shiftId: json['shiftId'] as String,
shiftTitle: json['shiftTitle'] as String,
roleName: json['roleName'] as String,
date: DateTime.parse(json['date'] as String),
date: parseUtcToLocal(json['date'] as String),
);
}

View File

@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
import '../financial/spend_item.dart';
/// Spend report with total, chart data points, and category breakdown.
@@ -71,7 +72,7 @@ class SpendDataPoint extends Equatable {
/// Deserialises a [SpendDataPoint] from a V2 API JSON map.
factory SpendDataPoint.fromJson(Map<String, dynamic> json) {
return SpendDataPoint(
bucket: DateTime.parse(json['bucket'] as String),
bucket: parseUtcToLocal(json['bucket'] as String),
amountCents: (json['amountCents'] as num).toInt(),
);
}

View File

@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
import 'package:krow_domain/src/entities/enums/assignment_status.dart';
import 'package:krow_domain/src/entities/enums/order_type.dart';
@@ -33,9 +34,9 @@ class AssignedShift extends Equatable {
shiftId: json['shiftId'] as String,
roleName: json['roleName'] as String,
location: json['location'] as String? ?? '',
date: DateTime.parse(json['date'] as String),
startTime: DateTime.parse(json['startTime'] as String),
endTime: DateTime.parse(json['endTime'] as String),
date: parseUtcToLocal(json['date'] as String),
startTime: parseUtcToLocal(json['startTime'] as String),
endTime: parseUtcToLocal(json['endTime'] as String),
hourlyRateCents: json['hourlyRateCents'] as int? ?? 0,
hourlyRate: (json['hourlyRate'] as num?)?.toDouble() ?? 0.0,
totalRateCents: json['totalRateCents'] as int? ?? 0,

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
/// A shift whose assignment was cancelled.
///
/// Returned by `GET /staff/shifts/cancelled`. Shows past assignments
@@ -13,6 +15,14 @@ class CancelledShift extends Equatable {
required this.location,
required this.date,
this.cancellationReason,
this.roleName,
this.clientName,
this.startTime,
this.endTime,
this.hourlyRateCents,
this.hourlyRate,
this.totalRateCents,
this.totalRate,
});
/// Deserialises from the V2 API JSON response.
@@ -22,8 +32,16 @@ class CancelledShift extends Equatable {
shiftId: json['shiftId'] as String,
title: json['title'] as String? ?? '',
location: json['location'] as String? ?? '',
date: DateTime.parse(json['date'] as String),
date: parseUtcToLocal(json['date'] as String),
cancellationReason: json['cancellationReason'] as String?,
roleName: json['roleName'] as String?,
clientName: json['clientName'] as String?,
startTime: tryParseUtcToLocal(json['startTime'] as String?),
endTime: tryParseUtcToLocal(json['endTime'] as String?),
hourlyRateCents: json['hourlyRateCents'] as int?,
hourlyRate: (json['hourlyRate'] as num?)?.toDouble(),
totalRateCents: json['totalRateCents'] as int?,
totalRate: (json['totalRate'] as num?)?.toDouble(),
);
}
@@ -45,6 +63,30 @@ class CancelledShift extends Equatable {
/// Reason for cancellation, from assignment metadata.
final String? cancellationReason;
/// Display name of the role.
final String? roleName;
/// Name of the client/business.
final String? clientName;
/// Scheduled start time.
final DateTime? startTime;
/// Scheduled end time.
final DateTime? endTime;
/// Pay rate in cents per hour.
final int? hourlyRateCents;
/// Pay rate in dollars per hour.
final double? hourlyRate;
/// Total pay for this shift in cents.
final int? totalRateCents;
/// Total pay for this shift in dollars.
final double? totalRate;
/// Serialises to JSON.
Map<String, dynamic> toJson() {
return <String, dynamic>{
@@ -54,6 +96,14 @@ class CancelledShift extends Equatable {
'location': location,
'date': date.toIso8601String(),
'cancellationReason': cancellationReason,
'roleName': roleName,
'clientName': clientName,
'startTime': startTime?.toIso8601String(),
'endTime': endTime?.toIso8601String(),
'hourlyRateCents': hourlyRateCents,
'hourlyRate': hourlyRate,
'totalRateCents': totalRateCents,
'totalRate': totalRate,
};
}
@@ -65,5 +115,13 @@ class CancelledShift extends Equatable {
location,
date,
cancellationReason,
roleName,
clientName,
startTime,
endTime,
hourlyRateCents,
hourlyRate,
totalRateCents,
totalRate,
];
}

View File

@@ -1,5 +1,8 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
import 'package:krow_domain/src/entities/enums/assignment_status.dart';
import 'package:krow_domain/src/entities/enums/payment_status.dart';
/// A shift the staff member has completed.
///
@@ -34,12 +37,12 @@ class CompletedShift extends Equatable {
title: json['title'] as String? ?? '',
location: json['location'] as String? ?? '',
clientName: json['clientName'] as String? ?? '',
date: DateTime.parse(json['date'] as String),
date: parseUtcToLocal(json['date'] as String),
startTime: json['startTime'] != null
? DateTime.parse(json['startTime'] as String)
? parseUtcToLocal(json['startTime'] as String)
: DateTime.now(),
endTime: json['endTime'] != null
? DateTime.parse(json['endTime'] as String)
? parseUtcToLocal(json['endTime'] as String)
: DateTime.now(),
minutesWorked: json['minutesWorked'] as int? ?? 0,
hourlyRateCents: json['hourlyRateCents'] as int? ?? 0,

View File

@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
import 'package:krow_domain/src/entities/enums/order_type.dart';
/// An open shift available for the staff member to apply to.
@@ -32,9 +33,9 @@ class OpenShift extends Equatable {
roleName: json['roleName'] as String,
clientName: json['clientName'] as String? ?? '',
location: json['location'] as String? ?? '',
date: DateTime.parse(json['date'] as String),
startTime: DateTime.parse(json['startTime'] as String),
endTime: DateTime.parse(json['endTime'] as String),
date: parseUtcToLocal(json['date'] as String),
startTime: parseUtcToLocal(json['startTime'] as String),
endTime: parseUtcToLocal(json['endTime'] as String),
hourlyRateCents: json['hourlyRateCents'] as int? ?? 0,
hourlyRate: (json['hourlyRate'] as num?)?.toDouble() ?? 0.0,
orderType: OrderType.fromJson(json['orderType'] as String?),

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
/// An assignment awaiting the staff member's acceptance.
///
/// Returned by `GET /staff/shifts/pending`. These are assignments with
@@ -15,6 +17,11 @@ class PendingAssignment extends Equatable {
required this.endTime,
required this.location,
required this.responseDeadline,
this.clientName,
this.hourlyRateCents,
this.hourlyRate,
this.totalRateCents,
this.totalRate,
});
/// Deserialises from the V2 API JSON response.
@@ -24,10 +31,15 @@ class PendingAssignment extends Equatable {
shiftId: json['shiftId'] as String,
title: json['title'] as String? ?? '',
roleName: json['roleName'] as String,
startTime: DateTime.parse(json['startTime'] as String),
endTime: DateTime.parse(json['endTime'] as String),
startTime: parseUtcToLocal(json['startTime'] as String),
endTime: parseUtcToLocal(json['endTime'] as String),
location: json['location'] as String? ?? '',
responseDeadline: DateTime.parse(json['responseDeadline'] as String),
responseDeadline: parseUtcToLocal(json['responseDeadline'] as String),
clientName: json['clientName'] as String?,
hourlyRateCents: json['hourlyRateCents'] as int?,
hourlyRate: (json['hourlyRate'] as num?)?.toDouble(),
totalRateCents: json['totalRateCents'] as int?,
totalRate: (json['totalRate'] as num?)?.toDouble(),
);
}
@@ -55,6 +67,21 @@ class PendingAssignment extends Equatable {
/// Deadline by which the worker must respond.
final DateTime responseDeadline;
/// Name of the client/business.
final String? clientName;
/// Pay rate in cents per hour.
final int? hourlyRateCents;
/// Pay rate in dollars per hour.
final double? hourlyRate;
/// Total pay for this shift in cents.
final int? totalRateCents;
/// Total pay for this shift in dollars.
final double? totalRate;
/// Serialises to JSON.
Map<String, dynamic> toJson() {
return <String, dynamic>{
@@ -66,6 +93,11 @@ class PendingAssignment extends Equatable {
'endTime': endTime.toIso8601String(),
'location': location,
'responseDeadline': responseDeadline.toIso8601String(),
'clientName': clientName,
'hourlyRateCents': hourlyRateCents,
'hourlyRate': hourlyRate,
'totalRateCents': totalRateCents,
'totalRate': totalRate,
};
}
@@ -79,5 +111,10 @@ class PendingAssignment extends Equatable {
endTime,
location,
responseDeadline,
clientName,
hourlyRateCents,
hourlyRate,
totalRateCents,
totalRate,
];
}

View File

@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
import 'package:krow_domain/src/entities/enums/shift_status.dart';
/// Core shift entity aligned with the V2 `shifts` table.
@@ -48,10 +49,10 @@ class Shift extends Equatable {
clientName ??
'',
status: ShiftStatus.fromJson(json['status'] as String?),
startsAt: DateTime.parse(
startsAt: parseUtcToLocal(
json['startsAt'] as String? ?? json['startTime'] as String,
),
endsAt: DateTime.parse(
endsAt: parseUtcToLocal(
json['endsAt'] as String? ?? json['endTime'] as String,
),
timezone: json['timezone'] as String? ?? 'UTC',

View File

@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
import 'package:krow_domain/src/entities/enums/application_status.dart';
import 'package:krow_domain/src/entities/enums/assignment_status.dart';
import 'package:krow_domain/src/entities/enums/order_type.dart';
@@ -42,6 +43,7 @@ class ShiftDetail extends Equatable {
this.nfcTagId,
this.breakDurationMinutes,
this.isBreakPaid = false,
this.cancellationReason,
});
/// Deserialises from the V2 API JSON response.
@@ -55,9 +57,9 @@ class ShiftDetail extends Equatable {
clientName: json['clientName'] as String? ?? '',
latitude: Shift.parseDouble(json['latitude']),
longitude: Shift.parseDouble(json['longitude']),
date: DateTime.parse(json['date'] as String),
startTime: DateTime.parse(json['startTime'] as String),
endTime: DateTime.parse(json['endTime'] as String),
date: parseUtcToLocal(json['date'] as String),
startTime: parseUtcToLocal(json['startTime'] as String),
endTime: parseUtcToLocal(json['endTime'] as String),
roleId: json['roleId'] as String,
roleName: json['roleName'] as String,
hourlyRateCents: json['hourlyRateCents'] as int? ?? 0,
@@ -79,6 +81,7 @@ class ShiftDetail extends Equatable {
nfcTagId: json['nfcTagId'] as String?,
breakDurationMinutes: json['breakDurationMinutes'] as int?,
isBreakPaid: json['isBreakPaid'] as bool? ?? false,
cancellationReason: json['cancellationReason'] as String?,
);
}
@@ -166,6 +169,9 @@ class ShiftDetail extends Equatable {
/// Whether the break is paid.
final bool isBreakPaid;
/// Reason the shift was cancelled, if applicable.
final String? cancellationReason;
/// Duration of the shift in hours.
double get durationHours {
return endTime.difference(startTime).inMinutes / 60;
@@ -205,6 +211,7 @@ class ShiftDetail extends Equatable {
'nfcTagId': nfcTagId,
'breakDurationMinutes': breakDurationMinutes,
'isBreakPaid': isBreakPaid,
'cancellationReason': cancellationReason,
};
}
@@ -238,5 +245,6 @@ class ShiftDetail extends Equatable {
nfcTagId,
breakDurationMinutes,
isBreakPaid,
cancellationReason,
];
}

View File

@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:krow_domain/src/core/utils/utc_parser.dart';
import 'package:krow_domain/src/entities/enums/attendance_status_type.dart';
/// A shift assigned to the staff member for today.
@@ -33,8 +34,8 @@ class TodayShift extends Equatable {
shiftId: json['shiftId'] as String,
roleName: json['roleName'] as String,
location: json['location'] as String? ?? '',
startTime: DateTime.parse(json['startTime'] as String),
endTime: DateTime.parse(json['endTime'] as String),
startTime: parseUtcToLocal(json['startTime'] as String),
endTime: parseUtcToLocal(json['endTime'] as String),
attendanceStatus: AttendanceStatusType.fromJson(json['attendanceStatus'] as String?),
clientName: json['clientName'] as String? ?? '',
hourlyRateCents: json['hourlyRateCents'] as int? ?? 0,
@@ -42,9 +43,7 @@ class TodayShift extends Equatable {
totalRateCents: json['totalRateCents'] as int? ?? 0,
totalRate: (json['totalRate'] as num?)?.toDouble() ?? 0.0,
locationAddress: json['locationAddress'] as String?,
clockInAt: json['clockInAt'] != null
? DateTime.parse(json['clockInAt'] as String)
: null,
clockInAt: tryParseUtcToLocal(json['clockInAt'] as String?),
);
}

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// Membership status within a business.
enum BusinessMembershipStatus {
/// The user has been invited but has not accepted.
@@ -63,8 +65,8 @@ class BusinessMembership extends Equatable {
businessName: json['businessName'] as String?,
businessSlug: json['businessSlug'] as String?,
metadata: (json['metadata'] as Map<String, dynamic>?) ?? const <String, dynamic>{},
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'] as String) : null,
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'] as String) : null,
createdAt: tryParseUtcToLocal(json['createdAt'] as String?),
updatedAt: tryParseUtcToLocal(json['updatedAt'] as String?),
);
}

View File

@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart';
import 'package:krow_domain/krow_domain.dart' show OnboardingStatus, StaffStatus;
import '../../core/utils/utc_parser.dart';
/// Represents a worker profile in the KROW platform.
///
/// Maps to the V2 `staffs` table. Linked to a [User] via [userId].
@@ -47,8 +49,8 @@ class Staff extends Equatable {
workforceId: json['workforceId'] as String?,
vendorId: json['vendorId'] as String?,
workforceNumber: json['workforceNumber'] as String?,
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'] as String) : null,
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'] as String) : null,
createdAt: tryParseUtcToLocal(json['createdAt'] as String?),
updatedAt: tryParseUtcToLocal(json['updatedAt'] as String?),
);
}

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart';
import '../../core/utils/utc_parser.dart';
/// Account status for a platform user.
enum UserStatus {
/// User is active and can sign in.
@@ -37,8 +39,8 @@ class User extends Equatable {
phone: json['phone'] as String?,
status: _parseUserStatus(json['status'] as String?),
metadata: (json['metadata'] as Map<String, dynamic>?) ?? const <String, dynamic>{},
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'] as String) : null,
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'] as String) : null,
createdAt: tryParseUtcToLocal(json['createdAt'] as String?),
updatedAt: tryParseUtcToLocal(json['updatedAt'] as String?),
);
}