feat: add UTC parsing utilities and update date handling across entities

- Introduced `utc_parser.dart` with functions to convert UTC timestamps to local time.
- Updated date parsing in various entities to use the new utility functions for consistency.
- Refactored date handling in `BenefitHistory`, `Business`, `AttendanceStatus`, `AssignedWorker`, `TimeRange`, `Invoice`, `PaymentChartPoint`, `StaffPayment`, `TimeCardEntry`, `OrderItem`, `OrderPreview`, `RecentOrder`, `StaffRating`, `CoverageDayPoint`, `ForecastWeek`, `NoShowIncident`, `SpendDataPoint`, `AssignedShift`, `CancelledShift`, `CompletedShift`, `OpenShift`, `PendingAssignment`, `Shift`, `ShiftDetail`, `TodayShift`, `BusinessMembership`, and `Staff`.
- Updated `ReorderWidget` and `OrderEditSheet` to handle date formatting correctly.
This commit is contained in:
Achintha Isuru
2026-03-19 13:00:17 -04:00
parent eacf34999b
commit 5792aa6e98
37 changed files with 136 additions and 109 deletions

View File

@@ -4,4 +4,9 @@ class DateTimeUtils {
static DateTime toDeviceTime(DateTime date) {
return date.toLocal();
}
/// Converts a local [DateTime] back to UTC for API payloads.
static String toUtcIso(DateTime local) {
return local.toUtc().toIso8601String();
}
}

View File

@@ -25,6 +25,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';

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].
@@ -23,9 +25,7 @@ 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,
);
}

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

@@ -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

@@ -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.
@@ -42,9 +43,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(),

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

@@ -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
@@ -22,7 +24,7 @@ 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?,
);
}

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
@@ -24,10 +26,10 @@ 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),
);
}

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';
@@ -53,9 +54,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,

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?),
);
}

View File

@@ -75,7 +75,7 @@ class ReorderWidget extends StatelessWidget {
borderRadius: UiConstants.radiusLg,
),
child: const Icon(
UiIcons.building,
UiIcons.briefcase,
size: 16,
color: UiColors.primary,
),
@@ -104,18 +104,6 @@ class ReorderWidget extends StatelessWidget {
],
),
),
// Column(
// crossAxisAlignment: CrossAxisAlignment.end,
// children: <Widget>[
// // ASSUMPTION: No i18n key for 'positions' under
// // reorder section — carrying forward existing
// // hardcoded string pattern for this migration.
// Text(
// '${order.positionCount} positions',
// style: UiTypography.footnote2r.textSecondary,
// ),
// ],
// ),
],
),
const SizedBox(height: UiConstants.space3),
@@ -130,7 +118,7 @@ class ReorderWidget extends StatelessWidget {
),
const SizedBox(width: UiConstants.space2),
_Badge(
icon: UiIcons.building,
icon: UiIcons.users,
text: '${order.positionCount}',
color: UiColors.textSecondary,
bg: UiColors.buttonSecondaryStill,

View File

@@ -48,13 +48,13 @@ class OrderEditSheetState extends State<OrderEditSheet> {
_orderNameController = TextEditingController(text: widget.order.roleName);
final String startHH =
widget.order.startsAt.toLocal().hour.toString().padLeft(2, '0');
widget.order.startsAt.hour.toString().padLeft(2, '0');
final String startMM =
widget.order.startsAt.toLocal().minute.toString().padLeft(2, '0');
widget.order.startsAt.minute.toString().padLeft(2, '0');
final String endHH =
widget.order.endsAt.toLocal().hour.toString().padLeft(2, '0');
widget.order.endsAt.hour.toString().padLeft(2, '0');
final String endMM =
widget.order.endsAt.toLocal().minute.toString().padLeft(2, '0');
widget.order.endsAt.minute.toString().padLeft(2, '0');
_positions = <Map<String, dynamic>>[
<String, dynamic>{

View File

@@ -77,9 +77,8 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
/// Formats a [DateTime] to a display time string (e.g. "9:00 AM").
String _formatTime({required DateTime dateTime}) {
final DateTime local = dateTime.toLocal();
final int hour24 = local.hour;
final int minute = local.minute;
final int hour24 = dateTime.hour;
final int minute = dateTime.minute;
final String ampm = hour24 >= 12 ? 'PM' : 'AM';
int hour = hour24 % 12;
if (hour == 0) hour = 12;