Merge branch 'origin/dev' into feature/session-persistence-new
This commit is contained in:
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
@@ -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?,
|
||||
|
||||
@@ -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?),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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?),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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?,
|
||||
);
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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?,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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?),
|
||||
|
||||
@@ -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?,
|
||||
);
|
||||
|
||||
@@ -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>{},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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?),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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?),
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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?),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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?),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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?),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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?),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user