feat: add entities for staff personal info, reports, shifts, and user sessions
- Implemented StaffPersonalInfo entity for staff profile data. - Created ReportSummary entity for summarizing report metrics. - Added SpendReport and SpendDataPoint entities for spend reporting. - Introduced AssignedShift, CancelledShift, CompletedShift, OpenShift, PendingAssignment, ShiftDetail, TodayShift entities for shift management. - Developed ClientSession and StaffSession entities for user session management.
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
import '../../entities/availability/availability_slot.dart';
|
||||
|
||||
/// Adapter for [AvailabilitySlot] domain entity.
|
||||
class AvailabilityAdapter {
|
||||
static const Map<String, Map<String, String>> _slotDefinitions = <String, Map<String, String>>{
|
||||
'MORNING': <String, String>{
|
||||
'id': 'morning',
|
||||
'label': 'Morning',
|
||||
'timeRange': '4:00 AM - 12:00 PM',
|
||||
},
|
||||
'AFTERNOON': <String, String>{
|
||||
'id': 'afternoon',
|
||||
'label': 'Afternoon',
|
||||
'timeRange': '12:00 PM - 6:00 PM',
|
||||
},
|
||||
'EVENING': <String, String>{
|
||||
'id': 'evening',
|
||||
'label': 'Evening',
|
||||
'timeRange': '6:00 PM - 12:00 AM',
|
||||
},
|
||||
};
|
||||
|
||||
/// Converts a backend slot name (e.g. 'MORNING') to a Domain [AvailabilitySlot].
|
||||
static AvailabilitySlot fromPrimitive(String slotName, {bool isAvailable = false}) {
|
||||
final Map<String, String> def = _slotDefinitions[slotName.toUpperCase()] ?? _slotDefinitions['MORNING']!;
|
||||
return AvailabilitySlot(
|
||||
id: def['id']!,
|
||||
label: def['label']!,
|
||||
timeRange: def['timeRange']!,
|
||||
isAvailable: isAvailable,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import '../../entities/clock_in/attendance_status.dart';
|
||||
|
||||
/// Adapter for Clock In related data.
|
||||
class ClockInAdapter {
|
||||
|
||||
/// Converts primitive attendance data to [AttendanceStatus].
|
||||
static AttendanceStatus toAttendanceStatus({
|
||||
required String status,
|
||||
DateTime? checkInTime,
|
||||
DateTime? checkOutTime,
|
||||
String? activeShiftId,
|
||||
String? activeApplicationId,
|
||||
}) {
|
||||
final bool isCheckedIn = status == 'CHECKED_IN' || status == 'LATE'; // Assuming LATE is also checked in?
|
||||
|
||||
// Statuses that imply active attendance: CHECKED_IN, LATE.
|
||||
// Statuses that imply completed: CHECKED_OUT.
|
||||
|
||||
return AttendanceStatus(
|
||||
isCheckedIn: isCheckedIn,
|
||||
checkInTime: checkInTime,
|
||||
checkOutTime: checkOutTime,
|
||||
activeShiftId: activeShiftId,
|
||||
activeApplicationId: activeApplicationId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import '../../../entities/financial/bank_account/business_bank_account.dart';
|
||||
|
||||
/// Adapter for [BusinessBankAccount] to map data layer values to domain entity.
|
||||
class BusinessBankAccountAdapter {
|
||||
/// Maps primitive values to [BusinessBankAccount].
|
||||
static BusinessBankAccount fromPrimitives({
|
||||
required String id,
|
||||
required String bank,
|
||||
required String last4,
|
||||
required bool isPrimary,
|
||||
DateTime? expiryTime,
|
||||
}) {
|
||||
return BusinessBankAccount(
|
||||
id: id,
|
||||
bankName: bank,
|
||||
last4: last4,
|
||||
isPrimary: isPrimary,
|
||||
expiryTime: expiryTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import '../../entities/financial/staff_payment.dart';
|
||||
|
||||
/// Adapter for Payment related data.
|
||||
class PaymentAdapter {
|
||||
|
||||
/// Converts string status to [PaymentStatus].
|
||||
static PaymentStatus toPaymentStatus(String status) {
|
||||
switch (status) {
|
||||
case 'PAID':
|
||||
return PaymentStatus.paid;
|
||||
case 'PENDING':
|
||||
return PaymentStatus.pending;
|
||||
case 'FAILED':
|
||||
return PaymentStatus.failed;
|
||||
default:
|
||||
return PaymentStatus.unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import '../../entities/financial/time_card.dart';
|
||||
|
||||
/// Adapter for [TimeCard] to map data layer values to domain entity.
|
||||
class TimeCardAdapter {
|
||||
/// Maps primitive values to [TimeCard].
|
||||
static TimeCard fromPrimitives({
|
||||
required String id,
|
||||
required String shiftTitle,
|
||||
required String clientName,
|
||||
required DateTime date,
|
||||
required String startTime,
|
||||
required String endTime,
|
||||
required double totalHours,
|
||||
required double hourlyRate,
|
||||
required double totalPay,
|
||||
required String status,
|
||||
String? location,
|
||||
}) {
|
||||
return TimeCard(
|
||||
id: id,
|
||||
shiftTitle: shiftTitle,
|
||||
clientName: clientName,
|
||||
date: date,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
totalHours: totalHours,
|
||||
hourlyRate: hourlyRate,
|
||||
totalPay: totalPay,
|
||||
status: _stringToStatus(status),
|
||||
location: location,
|
||||
);
|
||||
}
|
||||
|
||||
static TimeCardStatus _stringToStatus(String status) {
|
||||
switch (status.toUpperCase()) {
|
||||
case 'CHECKED_OUT':
|
||||
case 'COMPLETED':
|
||||
return TimeCardStatus.approved; // Assuming completed = approved for now
|
||||
case 'PAID':
|
||||
return TimeCardStatus.paid; // If this status exists
|
||||
case 'DISPUTED':
|
||||
return TimeCardStatus.disputed;
|
||||
case 'CHECKED_IN':
|
||||
case 'CONFIRMED':
|
||||
default:
|
||||
return TimeCardStatus.pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import '../../entities/financial/bank_account/staff_bank_account.dart';
|
||||
|
||||
/// Adapter for [StaffBankAccount] to map data layer values to domain entity.
|
||||
class BankAccountAdapter {
|
||||
/// Maps primitive values to [StaffBankAccount].
|
||||
static StaffBankAccount fromPrimitives({
|
||||
required String id,
|
||||
required String userId,
|
||||
required String bankName,
|
||||
required String? type,
|
||||
String? accountNumber,
|
||||
String? last4,
|
||||
String? sortCode,
|
||||
bool? isPrimary,
|
||||
}) {
|
||||
return StaffBankAccount(
|
||||
id: id,
|
||||
userId: userId,
|
||||
bankName: bankName,
|
||||
accountNumber: accountNumber ?? '',
|
||||
accountName: '', // Not provided by backend
|
||||
last4: last4,
|
||||
sortCode: sortCode,
|
||||
type: _stringToType(type),
|
||||
isPrimary: isPrimary ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
static StaffBankAccountType _stringToType(String? value) {
|
||||
if (value == null) return StaffBankAccountType.checking;
|
||||
try {
|
||||
// Assuming backend enum names match or are uppercase
|
||||
return StaffBankAccountType.values.firstWhere(
|
||||
(StaffBankAccountType e) => e.name.toLowerCase() == value.toLowerCase(),
|
||||
orElse: () => StaffBankAccountType.other,
|
||||
);
|
||||
} catch (_) {
|
||||
return StaffBankAccountType.other;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts domain type to string for backend.
|
||||
static String typeToString(StaffBankAccountType type) {
|
||||
switch (type) {
|
||||
case StaffBankAccountType.checking:
|
||||
return 'CHECKING';
|
||||
case StaffBankAccountType.savings:
|
||||
return 'SAVINGS';
|
||||
default:
|
||||
return 'CHECKING';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import '../../entities/profile/emergency_contact.dart';
|
||||
|
||||
/// Adapter for [EmergencyContact] to map data layer values to domain entity.
|
||||
class EmergencyContactAdapter {
|
||||
/// Maps primitive values to [EmergencyContact].
|
||||
static EmergencyContact fromPrimitives({
|
||||
required String id,
|
||||
required String name,
|
||||
required String phone,
|
||||
String? relationship,
|
||||
}) {
|
||||
return EmergencyContact(
|
||||
id: id,
|
||||
name: name,
|
||||
phone: phone,
|
||||
relationship: EmergencyContact.stringToRelationshipType(relationship),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/// Adapter for Experience data (skills/industries) to map data layer values to domain models.
|
||||
class ExperienceAdapter {
|
||||
/// Converts a dynamic list (from backend AnyValue) to List<String>.
|
||||
///
|
||||
/// Handles nulls and converts elements to Strings.
|
||||
static List<String> fromDynamicList(dynamic data) {
|
||||
if (data == null) return <String>[];
|
||||
|
||||
if (data is List) {
|
||||
return data
|
||||
.where((dynamic e) => e != null)
|
||||
.map((dynamic e) => e.toString())
|
||||
.toList();
|
||||
}
|
||||
|
||||
return <String>[];
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import '../../entities/profile/tax_form.dart';
|
||||
|
||||
/// Adapter for [TaxForm] to map data layer values to domain entity.
|
||||
class TaxFormAdapter {
|
||||
/// Maps primitive values to [TaxForm].
|
||||
static TaxForm fromPrimitives({
|
||||
required String id,
|
||||
required String type,
|
||||
required String title,
|
||||
String? subtitle,
|
||||
String? description,
|
||||
required String status,
|
||||
String? staffId,
|
||||
dynamic formData,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) {
|
||||
final TaxFormType formType = _stringToType(type);
|
||||
final TaxFormStatus formStatus = _stringToStatus(status);
|
||||
final Map<String, dynamic> formDetails =
|
||||
formData is Map ? Map<String, dynamic>.from(formData) : <String, dynamic>{};
|
||||
|
||||
if (formType == TaxFormType.i9) {
|
||||
return I9TaxForm(
|
||||
id: id,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
description: description,
|
||||
status: formStatus,
|
||||
staffId: staffId,
|
||||
formData: formDetails,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
);
|
||||
} else {
|
||||
return W4TaxForm(
|
||||
id: id,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
description: description,
|
||||
status: formStatus,
|
||||
staffId: staffId,
|
||||
formData: formDetails,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static TaxFormType _stringToType(String? value) {
|
||||
if (value == null) return TaxFormType.i9;
|
||||
try {
|
||||
return TaxFormType.values.firstWhere(
|
||||
(TaxFormType e) => e.name.toLowerCase() == value.toLowerCase(),
|
||||
orElse: () => TaxFormType.i9,
|
||||
);
|
||||
} catch (_) {
|
||||
return TaxFormType.i9;
|
||||
}
|
||||
}
|
||||
|
||||
static TaxFormStatus _stringToStatus(String? value) {
|
||||
if (value == null) return TaxFormStatus.notStarted;
|
||||
try {
|
||||
final String normalizedValue = value.replaceAll('_', '').toLowerCase();
|
||||
// map DRAFT to inProgress
|
||||
if (normalizedValue == 'draft') return TaxFormStatus.inProgress;
|
||||
|
||||
return TaxFormStatus.values.firstWhere(
|
||||
(TaxFormStatus e) {
|
||||
// Handle differences like not_started vs notStarted if any,
|
||||
// but standardizing to lowercase is a good start.
|
||||
// The enum names are camelCase in Dart, but might be SNAKE_CASE from backend.
|
||||
final String normalizedEnum = e.name.toLowerCase();
|
||||
return normalizedValue == normalizedEnum;
|
||||
},
|
||||
orElse: () => TaxFormStatus.notStarted,
|
||||
);
|
||||
} catch (_) {
|
||||
return TaxFormStatus.notStarted;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts domain [TaxFormType] to string for backend.
|
||||
static String typeToString(TaxFormType type) {
|
||||
return type.name.toUpperCase();
|
||||
}
|
||||
|
||||
/// Converts domain [TaxFormStatus] to string for backend.
|
||||
static String statusToString(TaxFormStatus status) {
|
||||
switch (status) {
|
||||
case TaxFormStatus.notStarted:
|
||||
return 'NOT_STARTED';
|
||||
case TaxFormStatus.inProgress:
|
||||
return 'DRAFT';
|
||||
case TaxFormStatus.submitted:
|
||||
return 'SUBMITTED';
|
||||
case TaxFormStatus.approved:
|
||||
return 'APPROVED';
|
||||
case TaxFormStatus.rejected:
|
||||
return 'REJECTED';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import '../../../entities/shifts/break/break.dart';
|
||||
|
||||
/// Adapter for Break related data.
|
||||
class BreakAdapter {
|
||||
/// Maps break data to a Break entity.
|
||||
///
|
||||
/// [isPaid] whether the break is paid.
|
||||
/// [breakTime] the string representation of the break duration (e.g., 'MIN_10', 'MIN_30').
|
||||
static Break fromData({
|
||||
required bool isPaid,
|
||||
required String? breakTime,
|
||||
}) {
|
||||
return Break(
|
||||
isBreakPaid: isPaid,
|
||||
duration: _parseDuration(breakTime),
|
||||
);
|
||||
}
|
||||
|
||||
static BreakDuration _parseDuration(String? breakTime) {
|
||||
if (breakTime == null) return BreakDuration.none;
|
||||
|
||||
switch (breakTime.toUpperCase()) {
|
||||
case 'MIN_10':
|
||||
return BreakDuration.ten;
|
||||
case 'MIN_15':
|
||||
return BreakDuration.fifteen;
|
||||
case 'MIN_20':
|
||||
return BreakDuration.twenty;
|
||||
case 'MIN_30':
|
||||
return BreakDuration.thirty;
|
||||
case 'MIN_45':
|
||||
return BreakDuration.fortyFive;
|
||||
case 'MIN_60':
|
||||
return BreakDuration.sixty;
|
||||
default:
|
||||
return BreakDuration.none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../entities/shifts/shift.dart';
|
||||
|
||||
/// Adapter for Shift related data.
|
||||
class ShiftAdapter {
|
||||
/// Maps application data to a Shift entity.
|
||||
///
|
||||
/// This method handles the common mapping logic used across different
|
||||
/// repositories when converting application data from Data Connect to
|
||||
/// domain Shift entities.
|
||||
static Shift fromApplicationData({
|
||||
required String shiftId,
|
||||
required String roleId,
|
||||
required String roleName,
|
||||
required String businessName,
|
||||
String? companyLogoUrl,
|
||||
required double costPerHour,
|
||||
String? shiftLocation,
|
||||
required String teamHubName,
|
||||
DateTime? shiftDate,
|
||||
DateTime? startTime,
|
||||
DateTime? endTime,
|
||||
DateTime? createdAt,
|
||||
required String status,
|
||||
String? description,
|
||||
int? durationDays,
|
||||
required int count,
|
||||
int? assigned,
|
||||
String? eventName,
|
||||
bool hasApplied = false,
|
||||
}) {
|
||||
final String orderName = (eventName ?? '').trim().isNotEmpty
|
||||
? eventName!
|
||||
: businessName;
|
||||
final String title = '$roleName - $orderName';
|
||||
|
||||
return Shift(
|
||||
id: shiftId,
|
||||
roleId: roleId,
|
||||
title: title,
|
||||
clientName: businessName,
|
||||
logoUrl: companyLogoUrl,
|
||||
hourlyRate: costPerHour,
|
||||
location: shiftLocation ?? '',
|
||||
locationAddress: teamHubName,
|
||||
date: shiftDate?.toIso8601String() ?? '',
|
||||
startTime: startTime != null ? DateFormat('HH:mm').format(startTime) : '',
|
||||
endTime: endTime != null ? DateFormat('HH:mm').format(endTime) : '',
|
||||
createdDate: createdAt?.toIso8601String() ?? '',
|
||||
status: status,
|
||||
description: description,
|
||||
durationDays: durationDays,
|
||||
requiredSlots: count,
|
||||
filledSlots: assigned ?? 0,
|
||||
hasApplied: hasApplied,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,11 @@ abstract class BaseApiService {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? params,
|
||||
});
|
||||
|
||||
/// Performs a DELETE request to the specified [endpoint].
|
||||
Future<ApiResponse> delete(
|
||||
String endpoint, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? params,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'package:krow_domain/src/entities/availability/time_slot.dart';
|
||||
import 'package:krow_domain/src/entities/enums/availability_status.dart';
|
||||
|
||||
/// Availability for a single calendar date.
|
||||
///
|
||||
/// Returned by `GET /staff/availability`. The backend generates one entry
|
||||
/// per date in the requested range by projecting the staff member's
|
||||
/// recurring weekly availability pattern.
|
||||
class AvailabilityDay extends Equatable {
|
||||
/// Creates an [AvailabilityDay].
|
||||
const AvailabilityDay({
|
||||
required this.date,
|
||||
required this.dayOfWeek,
|
||||
required this.availabilityStatus,
|
||||
this.slots = const <TimeSlot>[],
|
||||
});
|
||||
|
||||
/// Deserialises from the V2 API JSON response.
|
||||
factory AvailabilityDay.fromJson(Map<String, dynamic> json) {
|
||||
final dynamic rawSlots = json['slots'];
|
||||
final List<TimeSlot> parsedSlots = rawSlots is List<dynamic>
|
||||
? rawSlots
|
||||
.map((dynamic e) =>
|
||||
TimeSlot.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
: <TimeSlot>[];
|
||||
|
||||
return AvailabilityDay(
|
||||
date: json['date'] as String,
|
||||
dayOfWeek: json['dayOfWeek'] as int,
|
||||
availabilityStatus:
|
||||
AvailabilityStatus.fromJson(json['availabilityStatus'] as String?),
|
||||
slots: parsedSlots,
|
||||
);
|
||||
}
|
||||
|
||||
/// ISO date string (`YYYY-MM-DD`).
|
||||
final String date;
|
||||
|
||||
/// Day of week (0 = Sunday, 6 = Saturday).
|
||||
final int dayOfWeek;
|
||||
|
||||
/// Availability status for this day.
|
||||
final AvailabilityStatus availabilityStatus;
|
||||
|
||||
/// Time slots when the worker is available (relevant for `PARTIAL`).
|
||||
final List<TimeSlot> slots;
|
||||
|
||||
/// Whether the worker has any availability on this day.
|
||||
bool get isAvailable => availabilityStatus != AvailabilityStatus.unavailable;
|
||||
|
||||
/// Creates a copy with the given fields replaced.
|
||||
AvailabilityDay copyWith({
|
||||
String? date,
|
||||
int? dayOfWeek,
|
||||
AvailabilityStatus? availabilityStatus,
|
||||
List<TimeSlot>? slots,
|
||||
}) {
|
||||
return AvailabilityDay(
|
||||
date: date ?? this.date,
|
||||
dayOfWeek: dayOfWeek ?? this.dayOfWeek,
|
||||
availabilityStatus: availabilityStatus ?? this.availabilityStatus,
|
||||
slots: slots ?? this.slots,
|
||||
);
|
||||
}
|
||||
|
||||
/// Serialises to JSON.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'date': date,
|
||||
'dayOfWeek': dayOfWeek,
|
||||
'availabilityStatus': availabilityStatus.toJson(),
|
||||
'slots': slots.map((TimeSlot s) => s.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
date,
|
||||
dayOfWeek,
|
||||
availabilityStatus,
|
||||
slots,
|
||||
];
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a specific time slot within a day (e.g., Morning, Afternoon, Evening).
|
||||
class AvailabilitySlot extends Equatable {
|
||||
|
||||
const AvailabilitySlot({
|
||||
required this.id,
|
||||
required this.label,
|
||||
required this.timeRange,
|
||||
this.isAvailable = true,
|
||||
});
|
||||
final String id;
|
||||
final String label;
|
||||
final String timeRange;
|
||||
final bool isAvailable;
|
||||
|
||||
AvailabilitySlot copyWith({
|
||||
String? id,
|
||||
String? label,
|
||||
String? timeRange,
|
||||
bool? isAvailable,
|
||||
}) {
|
||||
return AvailabilitySlot(
|
||||
id: id ?? this.id,
|
||||
label: label ?? this.label,
|
||||
timeRange: timeRange ?? this.timeRange,
|
||||
isAvailable: isAvailable ?? this.isAvailable,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, label, timeRange, isAvailable];
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'availability_slot.dart';
|
||||
|
||||
/// Represents availability configuration for a specific date.
|
||||
class DayAvailability extends Equatable {
|
||||
|
||||
const DayAvailability({
|
||||
required this.date,
|
||||
this.isAvailable = false,
|
||||
this.slots = const <AvailabilitySlot>[],
|
||||
});
|
||||
final DateTime date;
|
||||
final bool isAvailable;
|
||||
final List<AvailabilitySlot> slots;
|
||||
|
||||
DayAvailability copyWith({
|
||||
DateTime? date,
|
||||
bool? isAvailable,
|
||||
List<AvailabilitySlot>? slots,
|
||||
}) {
|
||||
return DayAvailability(
|
||||
date: date ?? this.date,
|
||||
isAvailable: isAvailable ?? this.isAvailable,
|
||||
slots: slots ?? this.slots,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[date, isAvailable, slots];
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A time range within a day of availability.
|
||||
///
|
||||
/// Embedded inside [AvailabilityDay.slots]. Times are stored as `HH:MM`
|
||||
/// strings because the backend stores them in a JSONB array and they
|
||||
/// are timezone-agnostic display values.
|
||||
class TimeSlot extends Equatable {
|
||||
/// Creates a [TimeSlot].
|
||||
const TimeSlot({
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
});
|
||||
|
||||
/// Deserialises from a JSON map inside the availability slots array.
|
||||
factory TimeSlot.fromJson(Map<String, dynamic> json) {
|
||||
return TimeSlot(
|
||||
startTime: json['startTime'] as String? ?? '00:00',
|
||||
endTime: json['endTime'] as String? ?? '00:00',
|
||||
);
|
||||
}
|
||||
|
||||
/// Start time in `HH:MM` format.
|
||||
final String startTime;
|
||||
|
||||
/// End time in `HH:MM` format.
|
||||
final String endTime;
|
||||
|
||||
/// Serialises to JSON.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'startTime': startTime,
|
||||
'endTime': endTime,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[startTime, endTime];
|
||||
}
|
||||
@@ -1,26 +1,73 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a staff member's benefit balance.
|
||||
import 'package:krow_domain/src/entities/enums/benefit_status.dart';
|
||||
|
||||
/// A benefit accrued by a staff member (e.g. sick leave, vacation).
|
||||
///
|
||||
/// Returned by `GET /staff/profile/benefits`.
|
||||
class Benefit extends Equatable {
|
||||
/// Creates a [Benefit].
|
||||
/// Creates a [Benefit] instance.
|
||||
const Benefit({
|
||||
required this.benefitId,
|
||||
required this.benefitType,
|
||||
required this.title,
|
||||
required this.entitlementHours,
|
||||
required this.usedHours,
|
||||
required this.status,
|
||||
required this.trackedHours,
|
||||
required this.targetHours,
|
||||
});
|
||||
|
||||
/// The title of the benefit (e.g., Sick Leave, Holiday, Vacation).
|
||||
/// Deserialises a [Benefit] from a V2 API JSON map.
|
||||
factory Benefit.fromJson(Map<String, dynamic> json) {
|
||||
return Benefit(
|
||||
benefitId: json['benefitId'] as String,
|
||||
benefitType: json['benefitType'] as String,
|
||||
title: json['title'] as String,
|
||||
status: BenefitStatus.fromJson(json['status'] as String?),
|
||||
trackedHours: (json['trackedHours'] as num).toInt(),
|
||||
targetHours: (json['targetHours'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Unique identifier.
|
||||
final String benefitId;
|
||||
|
||||
/// Type code (e.g. SICK_LEAVE, VACATION).
|
||||
final String benefitType;
|
||||
|
||||
/// Human-readable title.
|
||||
final String title;
|
||||
|
||||
/// The total entitlement in hours.
|
||||
final double entitlementHours;
|
||||
/// Current benefit status.
|
||||
final BenefitStatus status;
|
||||
|
||||
/// The hours used so far.
|
||||
final double usedHours;
|
||||
/// Hours tracked so far.
|
||||
final int trackedHours;
|
||||
|
||||
/// The hours remaining.
|
||||
double get remainingHours => entitlementHours - usedHours;
|
||||
/// Target hours to accrue.
|
||||
final int targetHours;
|
||||
|
||||
/// Remaining hours to reach the target.
|
||||
int get remainingHours => targetHours - trackedHours;
|
||||
|
||||
/// Serialises this [Benefit] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'benefitId': benefitId,
|
||||
'benefitType': benefitType,
|
||||
'title': title,
|
||||
'status': status.toJson(),
|
||||
'trackedHours': trackedHours,
|
||||
'targetHours': targetHours,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [title, entitlementHours, usedHours];
|
||||
List<Object?> get props => <Object?>[
|
||||
benefitId,
|
||||
benefitType,
|
||||
title,
|
||||
status,
|
||||
trackedHours,
|
||||
targetHours,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a legal or service contract.
|
||||
///
|
||||
/// Can be between a business and the platform, or a business and staff.
|
||||
class BizContract extends Equatable {
|
||||
|
||||
const BizContract({
|
||||
required this.id,
|
||||
required this.businessId,
|
||||
required this.name,
|
||||
required this.startDate,
|
||||
this.endDate,
|
||||
required this.contentUrl,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The [Business] party to the contract.
|
||||
final String businessId;
|
||||
|
||||
/// Descriptive name of the contract.
|
||||
final String name;
|
||||
|
||||
/// Valid from date.
|
||||
final DateTime startDate;
|
||||
|
||||
/// Valid until date (null if indefinite).
|
||||
final DateTime? endDate;
|
||||
|
||||
/// URL to the document content (PDF/HTML).
|
||||
final String contentUrl;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, businessId, name, startDate, endDate, contentUrl];
|
||||
}
|
||||
@@ -1,47 +1,111 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// The operating status of a [Business].
|
||||
enum BusinessStatus {
|
||||
/// Business created but not yet approved.
|
||||
pending,
|
||||
import 'package:krow_domain/src/entities/enums/business_status.dart';
|
||||
|
||||
/// Fully active and operational.
|
||||
active,
|
||||
|
||||
/// Temporarily suspended (e.g. for non-payment).
|
||||
suspended,
|
||||
|
||||
/// Permanently inactive.
|
||||
inactive,
|
||||
}
|
||||
|
||||
/// Represents a Client Company / Business.
|
||||
/// A client company registered on the platform.
|
||||
///
|
||||
/// This is the top-level organizational entity in the system.
|
||||
/// Maps to the V2 `businesses` table.
|
||||
class Business extends Equatable {
|
||||
|
||||
/// Creates a [Business] instance.
|
||||
const Business({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.registrationNumber,
|
||||
required this.tenantId,
|
||||
required this.slug,
|
||||
required this.businessName,
|
||||
required this.status,
|
||||
this.avatar,
|
||||
this.contactName,
|
||||
this.contactEmail,
|
||||
this.contactPhone,
|
||||
this.metadata = const <String, dynamic>{},
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
});
|
||||
/// Unique identifier for the business.
|
||||
|
||||
/// Deserialises a [Business] from a V2 API JSON map.
|
||||
factory Business.fromJson(Map<String, dynamic> json) {
|
||||
return Business(
|
||||
id: json['id'] as String,
|
||||
tenantId: json['tenantId'] as String,
|
||||
slug: json['slug'] as String,
|
||||
businessName: json['businessName'] as String,
|
||||
status: BusinessStatus.fromJson(json['status'] as String?),
|
||||
contactName: json['contactName'] as String?,
|
||||
contactEmail: json['contactEmail'] as String?,
|
||||
contactPhone: json['contactPhone'] as String?,
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// Tenant this business belongs to.
|
||||
final String tenantId;
|
||||
|
||||
/// URL-safe slug.
|
||||
final String slug;
|
||||
|
||||
/// Display name of the business.
|
||||
final String name;
|
||||
final String businessName;
|
||||
|
||||
/// Legal registration or tax number.
|
||||
final String registrationNumber;
|
||||
|
||||
/// Current operating status.
|
||||
/// Current account status.
|
||||
final BusinessStatus status;
|
||||
|
||||
/// URL to the business logo.
|
||||
final String? avatar;
|
||||
/// Primary contact name.
|
||||
final String? contactName;
|
||||
|
||||
/// Primary contact email.
|
||||
final String? contactEmail;
|
||||
|
||||
/// Primary contact phone.
|
||||
final String? contactPhone;
|
||||
|
||||
/// Flexible metadata bag.
|
||||
final Map<String, dynamic> metadata;
|
||||
|
||||
/// When the record was created.
|
||||
final DateTime? createdAt;
|
||||
|
||||
/// When the record was last updated.
|
||||
final DateTime? updatedAt;
|
||||
|
||||
/// Serialises this [Business] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'id': id,
|
||||
'tenantId': tenantId,
|
||||
'slug': slug,
|
||||
'businessName': businessName,
|
||||
'status': status.toJson(),
|
||||
'contactName': contactName,
|
||||
'contactEmail': contactEmail,
|
||||
'contactPhone': contactPhone,
|
||||
'metadata': metadata,
|
||||
'createdAt': createdAt?.toIso8601String(),
|
||||
'updatedAt': updatedAt?.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, name, registrationNumber, status, avatar];
|
||||
}
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
tenantId,
|
||||
slug,
|
||||
businessName,
|
||||
status,
|
||||
contactName,
|
||||
contactEmail,
|
||||
contactPhone,
|
||||
metadata,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents payroll and operational configuration for a [Business].
|
||||
class BusinessSetting extends Equatable {
|
||||
|
||||
const BusinessSetting({
|
||||
required this.id,
|
||||
required this.businessId,
|
||||
required this.prefix,
|
||||
required this.overtimeEnabled,
|
||||
this.clockInRequirement,
|
||||
this.clockOutRequirement,
|
||||
});
|
||||
/// Unique identifier for the settings record.
|
||||
final String id;
|
||||
|
||||
/// The [Business] these settings apply to.
|
||||
final String businessId;
|
||||
|
||||
/// Prefix for generated invoices (e.g., "INV-").
|
||||
final String prefix;
|
||||
|
||||
/// Whether overtime calculations are applied.
|
||||
final bool overtimeEnabled;
|
||||
|
||||
/// Requirement method for clocking in (e.g. "qr_code", "geo_fence").
|
||||
final String? clockInRequirement;
|
||||
|
||||
/// Requirement method for clocking out.
|
||||
final String? clockOutRequirement;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
businessId,
|
||||
prefix,
|
||||
overtimeEnabled,
|
||||
clockInRequirement,
|
||||
clockOutRequirement,
|
||||
];
|
||||
}
|
||||
@@ -1,22 +1,37 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a financial cost center used for billing and tracking.
|
||||
/// A financial cost center used for billing and tracking.
|
||||
///
|
||||
/// Returned by `GET /client/cost-centers`.
|
||||
class CostCenter extends Equatable {
|
||||
/// Creates a [CostCenter] instance.
|
||||
const CostCenter({
|
||||
required this.id,
|
||||
required this.costCenterId,
|
||||
required this.name,
|
||||
this.code,
|
||||
});
|
||||
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
/// Deserialises a [CostCenter] from a V2 API JSON map.
|
||||
factory CostCenter.fromJson(Map<String, dynamic> json) {
|
||||
return CostCenter(
|
||||
costCenterId: json['costCenterId'] as String,
|
||||
name: json['name'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
/// Display name of the cost center.
|
||||
/// Unique identifier.
|
||||
final String costCenterId;
|
||||
|
||||
/// Display name.
|
||||
final String name;
|
||||
|
||||
/// Optional alphanumeric code associated with this cost center.
|
||||
final String? code;
|
||||
/// Serialises this [CostCenter] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'costCenterId': costCenterId,
|
||||
'name': name,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, name, code];
|
||||
List<Object?> get props => <Object?>[costCenterId, name];
|
||||
}
|
||||
|
||||
@@ -1,51 +1,107 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'cost_center.dart';
|
||||
|
||||
/// The status of a [Hub].
|
||||
enum HubStatus {
|
||||
/// Fully operational.
|
||||
active,
|
||||
|
||||
/// Closed or inactive.
|
||||
inactive,
|
||||
|
||||
/// Not yet ready for operations.
|
||||
underConstruction,
|
||||
}
|
||||
|
||||
/// Represents a branch location or operational unit within a [Business].
|
||||
/// A physical clock-point location (hub) belonging to a business.
|
||||
///
|
||||
/// Maps to the V2 `clock_points` table; returned by `GET /client/hubs`.
|
||||
class Hub extends Equatable {
|
||||
/// Creates a [Hub] instance.
|
||||
const Hub({
|
||||
required this.id,
|
||||
required this.businessId,
|
||||
required this.hubId,
|
||||
required this.name,
|
||||
required this.address,
|
||||
this.fullAddress,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.nfcTagId,
|
||||
required this.status,
|
||||
this.costCenter,
|
||||
this.city,
|
||||
this.state,
|
||||
this.zipCode,
|
||||
this.costCenterId,
|
||||
this.costCenterName,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The parent [Business].
|
||||
final String businessId;
|
||||
/// Deserialises a [Hub] from a V2 API JSON map.
|
||||
factory Hub.fromJson(Map<String, dynamic> json) {
|
||||
return Hub(
|
||||
hubId: json['hubId'] as String,
|
||||
name: json['name'] as String,
|
||||
fullAddress: json['fullAddress'] as String?,
|
||||
latitude: json['latitude'] != null
|
||||
? double.parse(json['latitude'].toString())
|
||||
: null,
|
||||
longitude: json['longitude'] != null
|
||||
? double.parse(json['longitude'].toString())
|
||||
: null,
|
||||
nfcTagId: json['nfcTagId'] as String?,
|
||||
city: json['city'] as String?,
|
||||
state: json['state'] as String?,
|
||||
zipCode: json['zipCode'] as String?,
|
||||
costCenterId: json['costCenterId'] as String?,
|
||||
costCenterName: json['costCenterName'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// Display name of the hub (e.g. "Downtown Branch").
|
||||
/// Unique identifier (clock_point id).
|
||||
final String hubId;
|
||||
|
||||
/// Display label for the hub.
|
||||
final String name;
|
||||
|
||||
/// Physical address of this hub.
|
||||
final String address;
|
||||
/// Full street address.
|
||||
final String? fullAddress;
|
||||
|
||||
/// Unique identifier of the NFC tag assigned to this hub.
|
||||
/// GPS latitude.
|
||||
final double? latitude;
|
||||
|
||||
/// GPS longitude.
|
||||
final double? longitude;
|
||||
|
||||
/// NFC tag UID assigned to this hub.
|
||||
final String? nfcTagId;
|
||||
|
||||
/// Operational status.
|
||||
final HubStatus status;
|
||||
/// City from metadata.
|
||||
final String? city;
|
||||
|
||||
/// Assigned cost center for this hub.
|
||||
final CostCenter? costCenter;
|
||||
/// State from metadata.
|
||||
final String? state;
|
||||
|
||||
/// Zip code from metadata.
|
||||
final String? zipCode;
|
||||
|
||||
/// Associated cost center ID.
|
||||
final String? costCenterId;
|
||||
|
||||
/// Associated cost center name.
|
||||
final String? costCenterName;
|
||||
|
||||
/// Serialises this [Hub] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'hubId': hubId,
|
||||
'name': name,
|
||||
'fullAddress': fullAddress,
|
||||
'latitude': latitude,
|
||||
'longitude': longitude,
|
||||
'nfcTagId': nfcTagId,
|
||||
'city': city,
|
||||
'state': state,
|
||||
'zipCode': zipCode,
|
||||
'costCenterId': costCenterId,
|
||||
'costCenterName': costCenterName,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, businessId, name, address, nfcTagId, status, costCenter];
|
||||
List<Object?> get props => <Object?>[
|
||||
hubId,
|
||||
name,
|
||||
fullAddress,
|
||||
latitude,
|
||||
longitude,
|
||||
nfcTagId,
|
||||
city,
|
||||
state,
|
||||
zipCode,
|
||||
costCenterId,
|
||||
costCenterName,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a department within a [Hub].
|
||||
///
|
||||
/// Used for more granular organization of staff and events (e.g. "Kitchen", "Service").
|
||||
class HubDepartment extends Equatable {
|
||||
|
||||
const HubDepartment({
|
||||
required this.id,
|
||||
required this.hubId,
|
||||
required this.name,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The [Hub] this department belongs to.
|
||||
final String hubId;
|
||||
|
||||
/// Name of the department.
|
||||
final String name;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, hubId, name];
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A manager assigned to a hub (clock point).
|
||||
///
|
||||
/// Returned by `GET /client/hubs/:id/managers`.
|
||||
class HubManager extends Equatable {
|
||||
/// Creates a [HubManager] instance.
|
||||
const HubManager({
|
||||
required this.managerAssignmentId,
|
||||
required this.businessMembershipId,
|
||||
required this.managerId,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
/// Deserialises a [HubManager] from a V2 API JSON map.
|
||||
factory HubManager.fromJson(Map<String, dynamic> json) {
|
||||
return HubManager(
|
||||
managerAssignmentId: json['managerAssignmentId'] as String,
|
||||
businessMembershipId: json['businessMembershipId'] as String,
|
||||
managerId: json['managerId'] as String,
|
||||
name: json['name'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
/// Primary key of the hub_managers row.
|
||||
final String managerAssignmentId;
|
||||
|
||||
/// Business membership ID of the manager.
|
||||
final String businessMembershipId;
|
||||
|
||||
/// User ID of the manager.
|
||||
final String managerId;
|
||||
|
||||
/// Display name of the manager.
|
||||
final String name;
|
||||
|
||||
/// Serialises this [HubManager] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'managerAssignmentId': managerAssignmentId,
|
||||
'businessMembershipId': businessMembershipId,
|
||||
'managerId': managerId,
|
||||
'name': name,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
managerAssignmentId,
|
||||
businessMembershipId,
|
||||
managerId,
|
||||
name,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A member of a business team (business membership + user).
|
||||
///
|
||||
/// Returned by `GET /client/team-members`.
|
||||
class TeamMember extends Equatable {
|
||||
/// Creates a [TeamMember] instance.
|
||||
const TeamMember({
|
||||
required this.businessMembershipId,
|
||||
required this.userId,
|
||||
required this.name,
|
||||
this.email,
|
||||
this.role,
|
||||
});
|
||||
|
||||
/// Deserialises a [TeamMember] from a V2 API JSON map.
|
||||
factory TeamMember.fromJson(Map<String, dynamic> json) {
|
||||
return TeamMember(
|
||||
businessMembershipId: json['businessMembershipId'] as String,
|
||||
userId: json['userId'] as String,
|
||||
name: json['name'] as String,
|
||||
email: json['email'] as String?,
|
||||
role: json['role'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// Business membership primary key.
|
||||
final String businessMembershipId;
|
||||
|
||||
/// User ID.
|
||||
final String userId;
|
||||
|
||||
/// Display name.
|
||||
final String name;
|
||||
|
||||
/// Email address.
|
||||
final String? email;
|
||||
|
||||
/// Business role (owner, manager, member, viewer).
|
||||
final String? role;
|
||||
|
||||
/// Serialises this [TeamMember] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'businessMembershipId': businessMembershipId,
|
||||
'userId': userId,
|
||||
'name': name,
|
||||
'email': email,
|
||||
'role': role,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
businessMembershipId,
|
||||
userId,
|
||||
name,
|
||||
email,
|
||||
role,
|
||||
];
|
||||
}
|
||||
@@ -1,15 +1,86 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a staffing vendor.
|
||||
import 'package:krow_domain/src/entities/enums/business_status.dart';
|
||||
|
||||
/// A staffing vendor that supplies workers to businesses.
|
||||
///
|
||||
/// Maps to the V2 `vendors` table.
|
||||
class Vendor extends Equatable {
|
||||
const Vendor({required this.id, required this.name, required this.rates});
|
||||
/// Creates a [Vendor] instance.
|
||||
const Vendor({
|
||||
required this.id,
|
||||
required this.tenantId,
|
||||
required this.slug,
|
||||
required this.companyName,
|
||||
required this.status,
|
||||
this.contactName,
|
||||
this.contactEmail,
|
||||
this.contactPhone,
|
||||
});
|
||||
|
||||
/// Deserialises a [Vendor] from a V2 API JSON map.
|
||||
factory Vendor.fromJson(Map<String, dynamic> json) {
|
||||
return Vendor(
|
||||
id: json['id'] as String? ?? json['vendorId'] as String,
|
||||
tenantId: json['tenantId'] as String? ?? '',
|
||||
slug: json['slug'] as String? ?? '',
|
||||
companyName: json['companyName'] as String? ??
|
||||
json['vendorName'] as String? ??
|
||||
'',
|
||||
status: BusinessStatus.fromJson(json['status'] as String?),
|
||||
contactName: json['contactName'] as String?,
|
||||
contactEmail: json['contactEmail'] as String?,
|
||||
contactPhone: json['contactPhone'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
final String name;
|
||||
|
||||
/// A map of role names to hourly rates.
|
||||
final Map<String, double> rates;
|
||||
/// Tenant this vendor belongs to.
|
||||
final String tenantId;
|
||||
|
||||
/// URL-safe slug.
|
||||
final String slug;
|
||||
|
||||
/// Display name of the vendor company.
|
||||
final String companyName;
|
||||
|
||||
/// Current account status.
|
||||
final BusinessStatus status;
|
||||
|
||||
/// Primary contact name.
|
||||
final String? contactName;
|
||||
|
||||
/// Primary contact email.
|
||||
final String? contactEmail;
|
||||
|
||||
/// Primary contact phone.
|
||||
final String? contactPhone;
|
||||
|
||||
/// Serialises this [Vendor] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'id': id,
|
||||
'tenantId': tenantId,
|
||||
'slug': slug,
|
||||
'companyName': companyName,
|
||||
'status': status.toJson(),
|
||||
'contactName': contactName,
|
||||
'contactEmail': contactEmail,
|
||||
'contactPhone': contactPhone,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, name, rates];
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
tenantId,
|
||||
slug,
|
||||
companyName,
|
||||
status,
|
||||
contactName,
|
||||
contactEmail,
|
||||
contactPhone,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A role available through a vendor with its billing rate.
|
||||
///
|
||||
/// Returned by `GET /client/vendors/:id/roles`.
|
||||
class VendorRole extends Equatable {
|
||||
/// Creates a [VendorRole] instance.
|
||||
const VendorRole({
|
||||
required this.roleId,
|
||||
required this.roleCode,
|
||||
required this.roleName,
|
||||
required this.hourlyRateCents,
|
||||
});
|
||||
|
||||
/// Deserialises a [VendorRole] from a V2 API JSON map.
|
||||
factory VendorRole.fromJson(Map<String, dynamic> json) {
|
||||
return VendorRole(
|
||||
roleId: json['roleId'] as String,
|
||||
roleCode: json['roleCode'] as String,
|
||||
roleName: json['roleName'] as String,
|
||||
hourlyRateCents: (json['hourlyRateCents'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Unique identifier from the roles catalog.
|
||||
final String roleId;
|
||||
|
||||
/// Short code for the role (e.g. BARISTA).
|
||||
final String roleCode;
|
||||
|
||||
/// Human-readable role name.
|
||||
final String roleName;
|
||||
|
||||
/// Billing rate in cents per hour.
|
||||
final int hourlyRateCents;
|
||||
|
||||
/// Serialises this [VendorRole] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'roleId': roleId,
|
||||
'roleCode': roleCode,
|
||||
'roleName': roleName,
|
||||
'hourlyRateCents': hourlyRateCents,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[roleId, roleCode, roleName, hourlyRateCents];
|
||||
}
|
||||
@@ -1,27 +1,56 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Simple entity to hold attendance state
|
||||
class AttendanceStatus extends Equatable {
|
||||
import 'package:krow_domain/src/entities/enums/attendance_status_type.dart';
|
||||
|
||||
/// Current clock-in / attendance status of the staff member.
|
||||
///
|
||||
/// Returned by `GET /staff/clock-in/status`. When no open session exists
|
||||
/// the API returns `attendanceStatus: 'NOT_CLOCKED_IN'` with null IDs.
|
||||
class AttendanceStatus extends Equatable {
|
||||
/// Creates an [AttendanceStatus].
|
||||
const AttendanceStatus({
|
||||
this.isCheckedIn = false,
|
||||
this.checkInTime,
|
||||
this.checkOutTime,
|
||||
this.activeShiftId,
|
||||
this.activeApplicationId,
|
||||
required this.attendanceStatus,
|
||||
this.clockInAt,
|
||||
});
|
||||
final bool isCheckedIn;
|
||||
final DateTime? checkInTime;
|
||||
final DateTime? checkOutTime;
|
||||
|
||||
/// Deserialises from the V2 API JSON response.
|
||||
factory AttendanceStatus.fromJson(Map<String, dynamic> json) {
|
||||
return AttendanceStatus(
|
||||
activeShiftId: json['activeShiftId'] as String?,
|
||||
attendanceStatus:
|
||||
AttendanceStatusType.fromJson(json['attendanceStatus'] as String?),
|
||||
clockInAt: json['clockInAt'] != null
|
||||
? DateTime.parse(json['clockInAt'] as String)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// The shift id of the currently active attendance session, if any.
|
||||
final String? activeShiftId;
|
||||
final String? activeApplicationId;
|
||||
|
||||
/// Attendance session status.
|
||||
final AttendanceStatusType attendanceStatus;
|
||||
|
||||
/// Timestamp of clock-in, if currently clocked in.
|
||||
final DateTime? clockInAt;
|
||||
|
||||
/// Whether the worker is currently clocked in.
|
||||
bool get isClockedIn => attendanceStatus == AttendanceStatusType.open;
|
||||
|
||||
/// Serialises to JSON.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'activeShiftId': activeShiftId,
|
||||
'attendanceStatus': attendanceStatus.toJson(),
|
||||
'clockInAt': clockInAt?.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
isCheckedIn,
|
||||
checkInTime,
|
||||
checkOutTime,
|
||||
activeShiftId,
|
||||
activeApplicationId,
|
||||
attendanceStatus,
|
||||
clockInAt,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'package:krow_domain/src/entities/enums/assignment_status.dart';
|
||||
|
||||
/// A worker assigned to a coverage shift.
|
||||
///
|
||||
/// Nested within [ShiftWithWorkers].
|
||||
class AssignedWorker extends Equatable {
|
||||
/// Creates an [AssignedWorker] instance.
|
||||
const AssignedWorker({
|
||||
required this.assignmentId,
|
||||
required this.staffId,
|
||||
required this.fullName,
|
||||
required this.status,
|
||||
this.checkInAt,
|
||||
});
|
||||
|
||||
/// Deserialises an [AssignedWorker] from a V2 API JSON map.
|
||||
factory AssignedWorker.fromJson(Map<String, dynamic> json) {
|
||||
return AssignedWorker(
|
||||
assignmentId: json['assignmentId'] as String,
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
/// Assignment ID.
|
||||
final String assignmentId;
|
||||
|
||||
/// Staff member ID.
|
||||
final String staffId;
|
||||
|
||||
/// Worker display name.
|
||||
final String fullName;
|
||||
|
||||
/// Assignment status.
|
||||
final AssignmentStatus status;
|
||||
|
||||
/// When the worker clocked in (null if not yet).
|
||||
final DateTime? checkInAt;
|
||||
|
||||
/// Serialises this [AssignedWorker] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'assignmentId': assignmentId,
|
||||
'staffId': staffId,
|
||||
'fullName': fullName,
|
||||
'status': status.toJson(),
|
||||
'checkInAt': checkInAt?.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
assignmentId,
|
||||
staffId,
|
||||
fullName,
|
||||
status,
|
||||
checkInAt,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A staff member on the business's core team (favorites).
|
||||
///
|
||||
/// Returned by `GET /client/coverage/core-team`.
|
||||
class CoreTeamMember extends Equatable {
|
||||
/// Creates a [CoreTeamMember] instance.
|
||||
const CoreTeamMember({
|
||||
required this.staffId,
|
||||
required this.fullName,
|
||||
this.primaryRole,
|
||||
required this.averageRating,
|
||||
required this.ratingCount,
|
||||
required this.favorite,
|
||||
});
|
||||
|
||||
/// Deserialises a [CoreTeamMember] from a V2 API JSON map.
|
||||
factory CoreTeamMember.fromJson(Map<String, dynamic> json) {
|
||||
return CoreTeamMember(
|
||||
staffId: json['staffId'] as String,
|
||||
fullName: json['fullName'] as String,
|
||||
primaryRole: json['primaryRole'] as String?,
|
||||
averageRating: (json['averageRating'] as num).toDouble(),
|
||||
ratingCount: (json['ratingCount'] as num).toInt(),
|
||||
favorite: json['favorite'] as bool? ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Staff member ID.
|
||||
final String staffId;
|
||||
|
||||
/// Display name.
|
||||
final String fullName;
|
||||
|
||||
/// Primary role code.
|
||||
final String? primaryRole;
|
||||
|
||||
/// Average review rating (0-5).
|
||||
final double averageRating;
|
||||
|
||||
/// Total number of reviews.
|
||||
final int ratingCount;
|
||||
|
||||
/// Whether this staff is favorited by the business.
|
||||
final bool favorite;
|
||||
|
||||
/// Serialises this [CoreTeamMember] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'staffId': staffId,
|
||||
'fullName': fullName,
|
||||
'primaryRole': primaryRole,
|
||||
'averageRating': averageRating,
|
||||
'ratingCount': ratingCount,
|
||||
'favorite': favorite,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
staffId,
|
||||
fullName,
|
||||
primaryRole,
|
||||
averageRating,
|
||||
ratingCount,
|
||||
favorite,
|
||||
];
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'coverage_worker.dart';
|
||||
|
||||
/// Domain entity representing a shift in the coverage view.
|
||||
///
|
||||
/// This is a feature-specific domain entity that encapsulates shift information
|
||||
/// including scheduling details and assigned workers.
|
||||
class CoverageShift extends Equatable {
|
||||
/// Creates a [CoverageShift].
|
||||
const CoverageShift({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.location,
|
||||
required this.startTime,
|
||||
required this.workersNeeded,
|
||||
required this.date,
|
||||
required this.workers,
|
||||
});
|
||||
|
||||
/// The unique identifier for the shift.
|
||||
final String id;
|
||||
|
||||
/// The title or role of the shift.
|
||||
final String title;
|
||||
|
||||
/// The location where the shift takes place.
|
||||
final String location;
|
||||
|
||||
/// The start time of the shift (e.g., "16:00").
|
||||
final String startTime;
|
||||
|
||||
/// The number of workers needed for this shift.
|
||||
final int workersNeeded;
|
||||
|
||||
/// The date of the shift.
|
||||
final DateTime date;
|
||||
|
||||
/// The list of workers assigned to this shift.
|
||||
final List<CoverageWorker> workers;
|
||||
|
||||
/// Calculates the coverage percentage for this shift.
|
||||
int get coveragePercent {
|
||||
if (workersNeeded == 0) return 0;
|
||||
return ((workers.length / workersNeeded) * 100).round();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
title,
|
||||
location,
|
||||
startTime,
|
||||
workersNeeded,
|
||||
date,
|
||||
workers,
|
||||
];
|
||||
}
|
||||
@@ -1,45 +1,69 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Domain entity representing coverage statistics.
|
||||
/// Aggregated coverage statistics for a specific date.
|
||||
///
|
||||
/// Aggregates coverage metrics for a specific date.
|
||||
/// Returned by `GET /client/coverage/stats`.
|
||||
class CoverageStats extends Equatable {
|
||||
/// Creates a [CoverageStats].
|
||||
/// Creates a [CoverageStats] instance.
|
||||
const CoverageStats({
|
||||
required this.totalNeeded,
|
||||
required this.totalConfirmed,
|
||||
required this.checkedIn,
|
||||
required this.enRoute,
|
||||
required this.late,
|
||||
required this.totalPositionsNeeded,
|
||||
required this.totalPositionsConfirmed,
|
||||
required this.totalWorkersCheckedIn,
|
||||
required this.totalWorkersEnRoute,
|
||||
required this.totalWorkersLate,
|
||||
required this.totalCoveragePercentage,
|
||||
});
|
||||
|
||||
/// The total number of workers needed.
|
||||
final int totalNeeded;
|
||||
/// Deserialises a [CoverageStats] from a V2 API JSON map.
|
||||
factory CoverageStats.fromJson(Map<String, dynamic> json) {
|
||||
return CoverageStats(
|
||||
totalPositionsNeeded: (json['totalPositionsNeeded'] as num).toInt(),
|
||||
totalPositionsConfirmed: (json['totalPositionsConfirmed'] as num).toInt(),
|
||||
totalWorkersCheckedIn: (json['totalWorkersCheckedIn'] as num).toInt(),
|
||||
totalWorkersEnRoute: (json['totalWorkersEnRoute'] as num).toInt(),
|
||||
totalWorkersLate: (json['totalWorkersLate'] as num).toInt(),
|
||||
totalCoveragePercentage:
|
||||
(json['totalCoveragePercentage'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// The total number of confirmed workers.
|
||||
final int totalConfirmed;
|
||||
/// Total positions that need to be filled.
|
||||
final int totalPositionsNeeded;
|
||||
|
||||
/// The number of workers who have checked in.
|
||||
final int checkedIn;
|
||||
/// Total positions that have been confirmed.
|
||||
final int totalPositionsConfirmed;
|
||||
|
||||
/// The number of workers en route.
|
||||
final int enRoute;
|
||||
/// Workers who have checked in.
|
||||
final int totalWorkersCheckedIn;
|
||||
|
||||
/// The number of late workers.
|
||||
final int late;
|
||||
/// Workers en route (accepted but not checked in).
|
||||
final int totalWorkersEnRoute;
|
||||
|
||||
/// Calculates the overall coverage percentage.
|
||||
int get coveragePercent {
|
||||
if (totalNeeded == 0) return 0;
|
||||
return ((totalConfirmed / totalNeeded) * 100).round();
|
||||
/// Workers marked as late / no-show.
|
||||
final int totalWorkersLate;
|
||||
|
||||
/// Overall coverage percentage (0-100).
|
||||
final int totalCoveragePercentage;
|
||||
|
||||
/// Serialises this [CoverageStats] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'totalPositionsNeeded': totalPositionsNeeded,
|
||||
'totalPositionsConfirmed': totalPositionsConfirmed,
|
||||
'totalWorkersCheckedIn': totalWorkersCheckedIn,
|
||||
'totalWorkersEnRoute': totalWorkersEnRoute,
|
||||
'totalWorkersLate': totalWorkersLate,
|
||||
'totalCoveragePercentage': totalCoveragePercentage,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
totalNeeded,
|
||||
totalConfirmed,
|
||||
checkedIn,
|
||||
enRoute,
|
||||
late,
|
||||
totalPositionsNeeded,
|
||||
totalPositionsConfirmed,
|
||||
totalWorkersCheckedIn,
|
||||
totalWorkersEnRoute,
|
||||
totalWorkersLate,
|
||||
totalCoveragePercentage,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Worker status enum matching ApplicationStatus from Data Connect.
|
||||
enum CoverageWorkerStatus {
|
||||
/// Application is pending approval.
|
||||
pending,
|
||||
|
||||
/// Application has been accepted.
|
||||
accepted,
|
||||
|
||||
/// Application has been rejected.
|
||||
rejected,
|
||||
|
||||
/// Worker has confirmed attendance.
|
||||
confirmed,
|
||||
|
||||
/// Worker has checked in.
|
||||
checkedIn,
|
||||
|
||||
/// Worker has checked out.
|
||||
checkedOut,
|
||||
|
||||
/// Worker is late.
|
||||
late,
|
||||
|
||||
/// Worker did not show up.
|
||||
noShow,
|
||||
|
||||
/// Shift is completed.
|
||||
completed,
|
||||
}
|
||||
|
||||
/// Domain entity representing a worker in the coverage view.
|
||||
///
|
||||
/// This entity tracks worker status including check-in information.
|
||||
class CoverageWorker extends Equatable {
|
||||
/// Creates a [CoverageWorker].
|
||||
const CoverageWorker({
|
||||
required this.name,
|
||||
required this.status,
|
||||
this.checkInTime,
|
||||
});
|
||||
|
||||
/// The name of the worker.
|
||||
final String name;
|
||||
|
||||
/// The status of the worker.
|
||||
final CoverageWorkerStatus status;
|
||||
|
||||
/// The time the worker checked in, if applicable.
|
||||
final String? checkInTime;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[name, status, checkInTime];
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'assigned_worker.dart';
|
||||
import 'time_range.dart';
|
||||
|
||||
/// A shift in the coverage view with its assigned workers.
|
||||
///
|
||||
/// Returned by `GET /client/coverage`.
|
||||
class ShiftWithWorkers extends Equatable {
|
||||
/// Creates a [ShiftWithWorkers] instance.
|
||||
const ShiftWithWorkers({
|
||||
required this.shiftId,
|
||||
required this.roleName,
|
||||
required this.timeRange,
|
||||
required this.requiredWorkerCount,
|
||||
required this.assignedWorkerCount,
|
||||
this.assignedWorkers = const <AssignedWorker>[],
|
||||
});
|
||||
|
||||
/// Deserialises a [ShiftWithWorkers] from a V2 API JSON map.
|
||||
factory ShiftWithWorkers.fromJson(Map<String, dynamic> json) {
|
||||
final dynamic workersRaw = json['assignedWorkers'];
|
||||
final List<AssignedWorker> workersList = workersRaw is List
|
||||
? workersRaw
|
||||
.map((dynamic e) =>
|
||||
AssignedWorker.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
: const <AssignedWorker>[];
|
||||
|
||||
return ShiftWithWorkers(
|
||||
shiftId: json['shiftId'] as String,
|
||||
roleName: json['roleName'] as String? ?? '',
|
||||
timeRange: TimeRange.fromJson(json['timeRange'] as Map<String, dynamic>),
|
||||
requiredWorkerCount: (json['requiredWorkerCount'] as num).toInt(),
|
||||
assignedWorkerCount: (json['assignedWorkerCount'] as num).toInt(),
|
||||
assignedWorkers: workersList,
|
||||
);
|
||||
}
|
||||
|
||||
/// Shift ID.
|
||||
final String shiftId;
|
||||
|
||||
/// Role name for this shift.
|
||||
final String roleName;
|
||||
|
||||
/// Start and end time range.
|
||||
final TimeRange timeRange;
|
||||
|
||||
/// Total workers required.
|
||||
final int requiredWorkerCount;
|
||||
|
||||
/// Workers currently assigned.
|
||||
final int assignedWorkerCount;
|
||||
|
||||
/// List of assigned workers with their statuses.
|
||||
final List<AssignedWorker> assignedWorkers;
|
||||
|
||||
/// Serialises this [ShiftWithWorkers] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'shiftId': shiftId,
|
||||
'roleName': roleName,
|
||||
'timeRange': timeRange.toJson(),
|
||||
'requiredWorkerCount': requiredWorkerCount,
|
||||
'assignedWorkerCount': assignedWorkerCount,
|
||||
'assignedWorkers':
|
||||
assignedWorkers.map((AssignedWorker w) => w.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
shiftId,
|
||||
roleName,
|
||||
timeRange,
|
||||
requiredWorkerCount,
|
||||
assignedWorkerCount,
|
||||
assignedWorkers,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A time range with start and end timestamps.
|
||||
///
|
||||
/// Used within [ShiftWithWorkers] for shift time windows.
|
||||
class TimeRange extends Equatable {
|
||||
/// Creates a [TimeRange] instance.
|
||||
const TimeRange({
|
||||
required this.startsAt,
|
||||
required this.endsAt,
|
||||
});
|
||||
|
||||
/// 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),
|
||||
);
|
||||
}
|
||||
|
||||
/// Start timestamp.
|
||||
final DateTime startsAt;
|
||||
|
||||
/// End timestamp.
|
||||
final DateTime endsAt;
|
||||
|
||||
/// Serialises this [TimeRange] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'startsAt': startsAt.toIso8601String(),
|
||||
'endsAt': endsAt.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[startsAt, endsAt];
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/// Type of bank account.
|
||||
///
|
||||
/// Used by both staff bank accounts and client billing accounts.
|
||||
enum AccountType {
|
||||
/// Checking account.
|
||||
checking('CHECKING'),
|
||||
|
||||
/// Savings account.
|
||||
savings('SAVINGS'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const AccountType(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static AccountType fromJson(String? value) {
|
||||
if (value == null) return AccountType.checking;
|
||||
for (final AccountType type in AccountType.values) {
|
||||
if (type.value == value) return type;
|
||||
}
|
||||
return AccountType.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/// Status of a worker's application to a shift.
|
||||
///
|
||||
/// Maps to the `status` CHECK constraint in the V2 `applications` table.
|
||||
enum ApplicationStatus {
|
||||
/// Application submitted, awaiting review.
|
||||
pending('PENDING'),
|
||||
|
||||
/// Application confirmed / approved.
|
||||
confirmed('CONFIRMED'),
|
||||
|
||||
/// Worker has checked in for the shift.
|
||||
checkedIn('CHECKED_IN'),
|
||||
|
||||
/// Worker is late for check-in.
|
||||
late_('LATE'),
|
||||
|
||||
/// Worker did not show up.
|
||||
noShow('NO_SHOW'),
|
||||
|
||||
/// Application / attendance completed.
|
||||
completed('COMPLETED'),
|
||||
|
||||
/// Application rejected.
|
||||
rejected('REJECTED'),
|
||||
|
||||
/// Application cancelled.
|
||||
cancelled('CANCELLED'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const ApplicationStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static ApplicationStatus fromJson(String? value) {
|
||||
if (value == null) return ApplicationStatus.unknown;
|
||||
for (final ApplicationStatus status in ApplicationStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return ApplicationStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/// Status of a worker's assignment to a shift.
|
||||
///
|
||||
/// Maps to the `status` CHECK constraint in the V2 `assignments` table.
|
||||
enum AssignmentStatus {
|
||||
/// Worker has been assigned but not yet accepted.
|
||||
assigned('ASSIGNED'),
|
||||
|
||||
/// Worker accepted the assignment.
|
||||
accepted('ACCEPTED'),
|
||||
|
||||
/// Worker requested to swap this assignment.
|
||||
swapRequested('SWAP_REQUESTED'),
|
||||
|
||||
/// Worker has checked in.
|
||||
checkedIn('CHECKED_IN'),
|
||||
|
||||
/// Worker has checked out.
|
||||
checkedOut('CHECKED_OUT'),
|
||||
|
||||
/// Assignment completed.
|
||||
completed('COMPLETED'),
|
||||
|
||||
/// Assignment cancelled.
|
||||
cancelled('CANCELLED'),
|
||||
|
||||
/// Worker did not show up.
|
||||
noShow('NO_SHOW'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const AssignmentStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static AssignmentStatus fromJson(String? value) {
|
||||
if (value == null) return AssignmentStatus.unknown;
|
||||
for (final AssignmentStatus status in AssignmentStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return AssignmentStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/// Attendance session status for clock-in tracking.
|
||||
///
|
||||
/// Maps to the `status` CHECK constraint in the V2 `attendance_events` table.
|
||||
enum AttendanceStatusType {
|
||||
/// Worker has not clocked in yet.
|
||||
notClockedIn('NOT_CLOCKED_IN'),
|
||||
|
||||
/// Attendance session is open (worker is clocked in).
|
||||
open('OPEN'),
|
||||
|
||||
/// Attendance session is closed (worker clocked out).
|
||||
closed('CLOSED'),
|
||||
|
||||
/// Attendance record is disputed.
|
||||
disputed('DISPUTED'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const AttendanceStatusType(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static AttendanceStatusType fromJson(String? value) {
|
||||
if (value == null) return AttendanceStatusType.notClockedIn;
|
||||
for (final AttendanceStatusType status in AttendanceStatusType.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return AttendanceStatusType.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/// Availability status for a calendar day.
|
||||
///
|
||||
/// Used by the staff availability feature to indicate whether a worker
|
||||
/// is available on a given date.
|
||||
enum AvailabilityStatus {
|
||||
/// Worker is available for the full day.
|
||||
available('AVAILABLE'),
|
||||
|
||||
/// Worker is not available.
|
||||
unavailable('UNAVAILABLE'),
|
||||
|
||||
/// Worker is available for partial time slots.
|
||||
partial('PARTIAL'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const AvailabilityStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static AvailabilityStatus fromJson(String? value) {
|
||||
if (value == null) return AvailabilityStatus.unavailable;
|
||||
for (final AvailabilityStatus status in AvailabilityStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return AvailabilityStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/// Status of a staff benefit accrual.
|
||||
///
|
||||
/// Used by the benefits feature to track whether a benefit is currently
|
||||
/// active, paused, or pending activation.
|
||||
enum BenefitStatus {
|
||||
/// Benefit is active and accruing.
|
||||
active('ACTIVE'),
|
||||
|
||||
/// Benefit is inactive / paused.
|
||||
inactive('INACTIVE'),
|
||||
|
||||
/// Benefit is pending activation.
|
||||
pending('PENDING'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const BenefitStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static BenefitStatus fromJson(String? value) {
|
||||
if (value == null) return BenefitStatus.unknown;
|
||||
for (final BenefitStatus status in BenefitStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return BenefitStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/// Account status of a business or vendor.
|
||||
///
|
||||
/// Maps to the `status` CHECK constraint in the V2 `businesses` and
|
||||
/// `vendors` tables.
|
||||
enum BusinessStatus {
|
||||
/// Account is active.
|
||||
active('ACTIVE'),
|
||||
|
||||
/// Account is inactive / suspended.
|
||||
inactive('INACTIVE'),
|
||||
|
||||
/// Account has been archived.
|
||||
archived('ARCHIVED'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const BusinessStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static BusinessStatus fromJson(String? value) {
|
||||
if (value == null) return BusinessStatus.unknown;
|
||||
for (final BusinessStatus status in BusinessStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return BusinessStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/// Lifecycle status of an invoice.
|
||||
///
|
||||
/// Maps to the `status` CHECK constraint in the V2 `invoices` table.
|
||||
enum InvoiceStatus {
|
||||
/// Invoice created but not yet sent.
|
||||
draft('DRAFT'),
|
||||
|
||||
/// Invoice sent, awaiting payment.
|
||||
pending('PENDING'),
|
||||
|
||||
/// Invoice under review.
|
||||
pendingReview('PENDING_REVIEW'),
|
||||
|
||||
/// Invoice approved for payment.
|
||||
approved('APPROVED'),
|
||||
|
||||
/// Invoice paid.
|
||||
paid('PAID'),
|
||||
|
||||
/// Invoice overdue.
|
||||
overdue('OVERDUE'),
|
||||
|
||||
/// Invoice disputed by the client.
|
||||
disputed('DISPUTED'),
|
||||
|
||||
/// Invoice voided / cancelled.
|
||||
void_('VOID'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const InvoiceStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static InvoiceStatus fromJson(String? value) {
|
||||
if (value == null) return InvoiceStatus.unknown;
|
||||
for (final InvoiceStatus status in InvoiceStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return InvoiceStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/// Onboarding progress status for a staff member.
|
||||
///
|
||||
/// Maps to the `onboarding_status` CHECK constraint in the V2 `staffs` table.
|
||||
enum OnboardingStatus {
|
||||
/// Onboarding not yet started.
|
||||
pending('PENDING'),
|
||||
|
||||
/// Onboarding in progress.
|
||||
inProgress('IN_PROGRESS'),
|
||||
|
||||
/// Onboarding completed.
|
||||
completed('COMPLETED'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const OnboardingStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static OnboardingStatus fromJson(String? value) {
|
||||
if (value == null) return OnboardingStatus.unknown;
|
||||
for (final OnboardingStatus status in OnboardingStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return OnboardingStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/// Type of order placed by a business client.
|
||||
///
|
||||
/// Maps to the `order_type` CHECK constraint in the V2 `orders` table.
|
||||
enum OrderType {
|
||||
/// A single occurrence order.
|
||||
oneTime('ONE_TIME'),
|
||||
|
||||
/// A recurring/repeating order.
|
||||
recurring('RECURRING'),
|
||||
|
||||
/// A permanent/ongoing order.
|
||||
permanent('PERMANENT'),
|
||||
|
||||
/// A rapid-fill order.
|
||||
rapid('RAPID'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const OrderType(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static OrderType fromJson(String? value) {
|
||||
if (value == null) return OrderType.unknown;
|
||||
for (final OrderType type in OrderType.values) {
|
||||
if (type.value == value) return type;
|
||||
}
|
||||
return OrderType.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/// Payment processing status.
|
||||
///
|
||||
/// Maps to the `payment_status` CHECK constraint in the V2 schema.
|
||||
enum PaymentStatus {
|
||||
/// Payment not yet processed.
|
||||
pending('PENDING'),
|
||||
|
||||
/// Payment is being processed.
|
||||
processing('PROCESSING'),
|
||||
|
||||
/// Payment completed successfully.
|
||||
paid('PAID'),
|
||||
|
||||
/// Payment processing failed.
|
||||
failed('FAILED'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const PaymentStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static PaymentStatus fromJson(String? value) {
|
||||
if (value == null) return PaymentStatus.unknown;
|
||||
for (final PaymentStatus status in PaymentStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return PaymentStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/// Lifecycle status of a shift.
|
||||
///
|
||||
/// Maps to the `status` CHECK constraint in the V2 `shifts` table.
|
||||
enum ShiftStatus {
|
||||
/// Shift created but not yet published.
|
||||
draft('DRAFT'),
|
||||
|
||||
/// Open for applications.
|
||||
open('OPEN'),
|
||||
|
||||
/// Awaiting worker confirmation.
|
||||
pendingConfirmation('PENDING_CONFIRMATION'),
|
||||
|
||||
/// All roles filled and confirmed.
|
||||
assigned('ASSIGNED'),
|
||||
|
||||
/// Currently in progress.
|
||||
active('ACTIVE'),
|
||||
|
||||
/// Shift finished.
|
||||
completed('COMPLETED'),
|
||||
|
||||
/// Shift cancelled.
|
||||
cancelled('CANCELLED'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const ShiftStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static ShiftStatus fromJson(String? value) {
|
||||
if (value == null) return ShiftStatus.unknown;
|
||||
for (final ShiftStatus status in ShiftStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return ShiftStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/// Account status of a staff member.
|
||||
///
|
||||
/// Maps to the `status` CHECK constraint in the V2 `staffs` table.
|
||||
enum StaffStatus {
|
||||
/// Staff account is active.
|
||||
active('ACTIVE'),
|
||||
|
||||
/// Staff has been invited but not yet onboarded.
|
||||
invited('INVITED'),
|
||||
|
||||
/// Staff account is inactive / suspended.
|
||||
inactive('INACTIVE'),
|
||||
|
||||
/// Staff account has been blocked.
|
||||
blocked('BLOCKED'),
|
||||
|
||||
/// Fallback for unrecognised API values.
|
||||
unknown('UNKNOWN');
|
||||
|
||||
const StaffStatus(this.value);
|
||||
|
||||
/// The V2 API string representation.
|
||||
final String value;
|
||||
|
||||
/// Deserialises from a V2 API string with safe fallback.
|
||||
static StaffStatus fromJson(String? value) {
|
||||
if (value == null) return StaffStatus.unknown;
|
||||
for (final StaffStatus status in StaffStatus.values) {
|
||||
if (status.value == value) return status;
|
||||
}
|
||||
return StaffStatus.unknown;
|
||||
}
|
||||
|
||||
/// Serialises to the V2 API string.
|
||||
String toJson() => value;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// The status of a staff [Assignment].
|
||||
enum AssignmentStatus {
|
||||
/// Staff member has been assigned but hasn't confirmed.
|
||||
assigned,
|
||||
|
||||
/// Staff member has accepted the assignment.
|
||||
confirmed,
|
||||
|
||||
/// Work is currently in progress (Clocked In).
|
||||
ongoing,
|
||||
|
||||
/// Work completed successfully (Clocked Out).
|
||||
completed,
|
||||
|
||||
/// Staff rejected the assignment offer.
|
||||
declinedByStaff,
|
||||
|
||||
/// Staff canceled after accepting.
|
||||
canceledByStaff,
|
||||
|
||||
/// Staff did not show up.
|
||||
noShowed,
|
||||
}
|
||||
|
||||
/// Represents the link between a [Staff] member and an [EventShiftPosition].
|
||||
class Assignment extends Equatable {
|
||||
|
||||
const Assignment({
|
||||
required this.id,
|
||||
required this.positionId,
|
||||
required this.staffId,
|
||||
required this.status,
|
||||
this.clockIn,
|
||||
this.clockOut,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The job position being filled.
|
||||
final String positionId;
|
||||
|
||||
/// The staff member filling the position.
|
||||
final String staffId;
|
||||
|
||||
/// Current status of the assignment.
|
||||
final AssignmentStatus status;
|
||||
|
||||
/// Actual timestamp when staff clocked in.
|
||||
final DateTime? clockIn;
|
||||
|
||||
/// Actual timestamp when staff clocked out.
|
||||
final DateTime? clockOut;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, positionId, staffId, status, clockIn, clockOut];
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// The workflow status of an [Event].
|
||||
enum EventStatus {
|
||||
/// Created but incomplete.
|
||||
draft,
|
||||
|
||||
/// Waiting for approval or publication.
|
||||
pending,
|
||||
|
||||
/// Published and staff have been assigned.
|
||||
assigned,
|
||||
|
||||
/// Fully confirmed and ready to start.
|
||||
confirmed,
|
||||
|
||||
/// Currently in progress.
|
||||
active,
|
||||
|
||||
/// Work has finished.
|
||||
finished,
|
||||
|
||||
/// All post-event processes (invoicing) complete.
|
||||
completed,
|
||||
|
||||
/// Archived.
|
||||
closed,
|
||||
|
||||
/// Flagged for administrative review.
|
||||
underReview,
|
||||
}
|
||||
|
||||
/// Represents a Job Posting or Event.
|
||||
///
|
||||
/// This is the central entity for scheduling work. An Event contains [EventShift]s.
|
||||
class Event extends Equatable {
|
||||
|
||||
const Event({
|
||||
required this.id,
|
||||
required this.businessId,
|
||||
required this.hubId,
|
||||
required this.name,
|
||||
required this.date,
|
||||
required this.status,
|
||||
required this.contractType,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The [Business] hosting the event.
|
||||
final String businessId;
|
||||
|
||||
/// The [Hub] location.
|
||||
final String hubId;
|
||||
|
||||
/// Title of the event.
|
||||
final String name;
|
||||
|
||||
/// Date of the event.
|
||||
final DateTime date;
|
||||
|
||||
/// Current workflow status.
|
||||
final EventStatus status;
|
||||
|
||||
/// Type of employment contract (e.g., 'freelance', 'permanent').
|
||||
final String contractType;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, businessId, hubId, name, date, status, contractType];
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a specific time block or "shift" within an [Event].
|
||||
///
|
||||
/// An Event can have multiple shifts (e.g. "Morning Shift", "Evening Shift").
|
||||
class EventShift extends Equatable {
|
||||
|
||||
const EventShift({
|
||||
required this.id,
|
||||
required this.eventId,
|
||||
required this.name,
|
||||
required this.address,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The [Event] this shift belongs to.
|
||||
final String eventId;
|
||||
|
||||
/// Descriptive name (e.g. "Setup Crew").
|
||||
final String name;
|
||||
|
||||
/// Specific address for this shift (if different from Hub).
|
||||
final String address;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, eventId, name, address];
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a specific job opening within a [EventShift].
|
||||
///
|
||||
/// Defines the requirement for a specific [Skill], the quantity needed, and the pay.
|
||||
class EventShiftPosition extends Equatable {
|
||||
|
||||
const EventShiftPosition({
|
||||
required this.id,
|
||||
required this.shiftId,
|
||||
required this.skillId,
|
||||
required this.count,
|
||||
required this.rate,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.breakDurationMinutes,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The [EventShift] this position is part of.
|
||||
final String shiftId;
|
||||
|
||||
/// The [Skill] required for this position.
|
||||
final String skillId;
|
||||
|
||||
/// Number of staff needed.
|
||||
final int count;
|
||||
|
||||
/// Hourly pay rate.
|
||||
final double rate;
|
||||
|
||||
/// Start time of this specific position.
|
||||
final DateTime startTime;
|
||||
|
||||
/// End time of this specific position.
|
||||
final DateTime endTime;
|
||||
|
||||
/// Deducted break duration in minutes.
|
||||
final int breakDurationMinutes;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
shiftId,
|
||||
skillId,
|
||||
count,
|
||||
rate,
|
||||
startTime,
|
||||
endTime,
|
||||
breakDurationMinutes,
|
||||
];
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a verified record of time worked.
|
||||
///
|
||||
/// Derived from [Assignment] clock-in/out times, used for payroll.
|
||||
class WorkSession extends Equatable {
|
||||
|
||||
const WorkSession({
|
||||
required this.id,
|
||||
required this.assignmentId,
|
||||
required this.startTime,
|
||||
this.endTime,
|
||||
required this.breakDurationMinutes,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The [Assignment] this session belongs to.
|
||||
final String assignmentId;
|
||||
|
||||
/// Verified start time.
|
||||
final DateTime startTime;
|
||||
|
||||
/// Verified end time.
|
||||
final DateTime? endTime;
|
||||
|
||||
/// Verified break duration.
|
||||
final int breakDurationMinutes;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, assignmentId, startTime, endTime, breakDurationMinutes];
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'package:krow_domain/src/entities/enums/account_type.dart';
|
||||
|
||||
/// A bank account belonging to a staff member.
|
||||
///
|
||||
/// Returned by `GET /staff/profile/bank-accounts`.
|
||||
class BankAccount extends Equatable {
|
||||
/// Creates a [BankAccount] instance.
|
||||
const BankAccount({
|
||||
required this.accountId,
|
||||
required this.bankName,
|
||||
required this.providerReference,
|
||||
this.last4,
|
||||
required this.isPrimary,
|
||||
required this.accountType,
|
||||
});
|
||||
|
||||
/// Deserialises a [BankAccount] from a V2 API JSON map.
|
||||
factory BankAccount.fromJson(Map<String, dynamic> json) {
|
||||
return BankAccount(
|
||||
accountId: json['accountId'] as String,
|
||||
bankName: json['bankName'] as String,
|
||||
providerReference: json['providerReference'] as String,
|
||||
last4: json['last4'] as String?,
|
||||
isPrimary: json['isPrimary'] as bool,
|
||||
accountType: AccountType.fromJson(json['accountType'] as String?),
|
||||
);
|
||||
}
|
||||
|
||||
/// Unique identifier.
|
||||
final String accountId;
|
||||
|
||||
/// Name of the bank / payment provider.
|
||||
final String bankName;
|
||||
|
||||
/// External provider reference.
|
||||
final String providerReference;
|
||||
|
||||
/// Last 4 digits of the account number.
|
||||
final String? last4;
|
||||
|
||||
/// Whether this is the primary payout account.
|
||||
final bool isPrimary;
|
||||
|
||||
/// Account type.
|
||||
final AccountType accountType;
|
||||
|
||||
/// Serialises this [BankAccount] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'accountId': accountId,
|
||||
'bankName': bankName,
|
||||
'providerReference': providerReference,
|
||||
'last4': last4,
|
||||
'isPrimary': isPrimary,
|
||||
'accountType': accountType.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
accountId,
|
||||
bankName,
|
||||
providerReference,
|
||||
last4,
|
||||
isPrimary,
|
||||
accountType,
|
||||
];
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Abstract base class for all types of bank accounts.
|
||||
abstract class BankAccount extends Equatable {
|
||||
/// Creates a [BankAccount].
|
||||
const BankAccount({
|
||||
required this.id,
|
||||
required this.bankName,
|
||||
required this.isPrimary,
|
||||
this.last4,
|
||||
});
|
||||
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// Name of the bank or provider.
|
||||
final String bankName;
|
||||
|
||||
/// Whether this is the primary payment method.
|
||||
final bool isPrimary;
|
||||
|
||||
/// Last 4 digits of the account/card.
|
||||
final String? last4;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, bankName, isPrimary, last4];
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import 'bank_account.dart';
|
||||
|
||||
/// Domain model representing a business bank account or payment method.
|
||||
class BusinessBankAccount extends BankAccount {
|
||||
/// Creates a [BusinessBankAccount].
|
||||
const BusinessBankAccount({
|
||||
required super.id,
|
||||
required super.bankName,
|
||||
required String last4,
|
||||
required super.isPrimary,
|
||||
this.expiryTime,
|
||||
}) : super(last4: last4);
|
||||
|
||||
/// Expiration date if applicable.
|
||||
final DateTime? expiryTime;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
...super.props,
|
||||
expiryTime,
|
||||
];
|
||||
|
||||
/// Getter for non-nullable last4 in Business context.
|
||||
@override
|
||||
String get last4 => super.last4!;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import 'bank_account.dart';
|
||||
|
||||
/// Type of staff bank account.
|
||||
enum StaffBankAccountType {
|
||||
/// Checking account.
|
||||
checking,
|
||||
|
||||
/// Savings account.
|
||||
savings,
|
||||
|
||||
/// Other type.
|
||||
other,
|
||||
}
|
||||
|
||||
/// Domain entity representing a staff's bank account.
|
||||
class StaffBankAccount extends BankAccount {
|
||||
/// Creates a [StaffBankAccount].
|
||||
const StaffBankAccount({
|
||||
required super.id,
|
||||
required this.userId,
|
||||
required super.bankName,
|
||||
required this.accountNumber,
|
||||
required this.accountName,
|
||||
required super.isPrimary,
|
||||
super.last4,
|
||||
this.sortCode,
|
||||
this.type = StaffBankAccountType.checking,
|
||||
});
|
||||
|
||||
/// User identifier.
|
||||
final String userId;
|
||||
|
||||
/// Full account number.
|
||||
final String accountNumber;
|
||||
|
||||
/// Name of the account holder.
|
||||
final String accountName;
|
||||
|
||||
/// Sort code (optional).
|
||||
final String? sortCode;
|
||||
|
||||
/// Account type.
|
||||
final StaffBankAccountType type;
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
<Object?>[...super.props, userId, accountNumber, accountName, sortCode, type];
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'package:krow_domain/src/entities/enums/account_type.dart';
|
||||
|
||||
/// A billing/bank account belonging to a business.
|
||||
///
|
||||
/// Returned by `GET /client/billing/accounts`.
|
||||
class BillingAccount extends Equatable {
|
||||
/// Creates a [BillingAccount] instance.
|
||||
const BillingAccount({
|
||||
required this.accountId,
|
||||
required this.bankName,
|
||||
required this.providerReference,
|
||||
this.last4,
|
||||
required this.isPrimary,
|
||||
required this.accountType,
|
||||
this.routingNumberMasked,
|
||||
});
|
||||
|
||||
/// Deserialises a [BillingAccount] from a V2 API JSON map.
|
||||
factory BillingAccount.fromJson(Map<String, dynamic> json) {
|
||||
return BillingAccount(
|
||||
accountId: json['accountId'] as String,
|
||||
bankName: json['bankName'] as String,
|
||||
providerReference: json['providerReference'] as String,
|
||||
last4: json['last4'] as String?,
|
||||
isPrimary: json['isPrimary'] as bool,
|
||||
accountType: AccountType.fromJson(json['accountType'] as String?),
|
||||
routingNumberMasked: json['routingNumberMasked'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// Unique identifier.
|
||||
final String accountId;
|
||||
|
||||
/// Name of the bank / payment provider.
|
||||
final String bankName;
|
||||
|
||||
/// External provider reference (e.g. Stripe account ID).
|
||||
final String providerReference;
|
||||
|
||||
/// Last 4 digits of the account number.
|
||||
final String? last4;
|
||||
|
||||
/// Whether this is the primary billing account.
|
||||
final bool isPrimary;
|
||||
|
||||
/// Account type.
|
||||
final AccountType accountType;
|
||||
|
||||
/// Masked routing number.
|
||||
final String? routingNumberMasked;
|
||||
|
||||
/// Serialises this [BillingAccount] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'accountId': accountId,
|
||||
'bankName': bankName,
|
||||
'providerReference': providerReference,
|
||||
'last4': last4,
|
||||
'isPrimary': isPrimary,
|
||||
'accountType': accountType.toJson(),
|
||||
'routingNumberMasked': routingNumberMasked,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
accountId,
|
||||
bankName,
|
||||
providerReference,
|
||||
last4,
|
||||
isPrimary,
|
||||
accountType,
|
||||
routingNumberMasked,
|
||||
];
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/// Defines the period for billing calculations.
|
||||
enum BillingPeriod {
|
||||
/// Weekly billing period.
|
||||
week,
|
||||
|
||||
/// Monthly billing period.
|
||||
month,
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// The current outstanding bill for a business.
|
||||
///
|
||||
/// Returned by `GET /client/billing/current-bill`.
|
||||
class CurrentBill extends Equatable {
|
||||
/// Creates a [CurrentBill] instance.
|
||||
const CurrentBill({required this.currentBillCents});
|
||||
|
||||
/// Deserialises a [CurrentBill] from a V2 API JSON map.
|
||||
factory CurrentBill.fromJson(Map<String, dynamic> json) {
|
||||
return CurrentBill(
|
||||
currentBillCents: (json['currentBillCents'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Outstanding bill amount in cents.
|
||||
final int currentBillCents;
|
||||
|
||||
/// Serialises this [CurrentBill] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'currentBillCents': currentBillCents,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[currentBillCents];
|
||||
}
|
||||
@@ -1,148 +1,88 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// The workflow status of an [Invoice].
|
||||
enum InvoiceStatus {
|
||||
/// Generated but not yet sent/finalized.
|
||||
open,
|
||||
import 'package:krow_domain/src/entities/enums/invoice_status.dart';
|
||||
|
||||
/// Client has disputed a line item.
|
||||
disputed,
|
||||
|
||||
/// Dispute has been handled.
|
||||
resolved,
|
||||
|
||||
/// Invoice accepted by client.
|
||||
verified,
|
||||
|
||||
/// Payment received.
|
||||
paid,
|
||||
|
||||
/// Payment reconciled in accounting.
|
||||
reconciled,
|
||||
|
||||
/// Payment not received by due date.
|
||||
overdue,
|
||||
}
|
||||
|
||||
/// Represents a bill sent to a [Business] for services rendered.
|
||||
/// An invoice issued to a business for services rendered.
|
||||
///
|
||||
/// Returned by `GET /client/billing/invoices/*`.
|
||||
class Invoice extends Equatable {
|
||||
|
||||
/// Creates an [Invoice] instance.
|
||||
const Invoice({
|
||||
required this.id,
|
||||
required this.eventId,
|
||||
required this.businessId,
|
||||
required this.invoiceId,
|
||||
required this.invoiceNumber,
|
||||
required this.amountCents,
|
||||
required this.status,
|
||||
required this.totalAmount,
|
||||
required this.workAmount,
|
||||
required this.addonsAmount,
|
||||
this.invoiceNumber,
|
||||
this.issueDate,
|
||||
this.title,
|
||||
this.clientName,
|
||||
this.locationAddress,
|
||||
this.staffCount,
|
||||
this.totalHours,
|
||||
this.workers = const [],
|
||||
this.dueDate,
|
||||
this.paymentDate,
|
||||
this.vendorId,
|
||||
this.vendorName,
|
||||
});
|
||||
|
||||
/// Deserialises an [Invoice] from a V2 API JSON map.
|
||||
factory Invoice.fromJson(Map<String, dynamic> json) {
|
||||
return Invoice(
|
||||
invoiceId: json['invoiceId'] as String,
|
||||
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,
|
||||
vendorId: json['vendorId'] as String?,
|
||||
vendorName: json['vendorName'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The [Event] this invoice covers.
|
||||
final String eventId;
|
||||
|
||||
/// The [Business] being billed.
|
||||
final String businessId;
|
||||
|
||||
/// Current payment/approval status.
|
||||
final InvoiceStatus status;
|
||||
|
||||
/// Grand total amount.
|
||||
final double totalAmount;
|
||||
|
||||
/// Total amount for labor costs.
|
||||
final double workAmount;
|
||||
|
||||
/// Total amount for addons/extras.
|
||||
final double addonsAmount;
|
||||
final String invoiceId;
|
||||
|
||||
/// Human-readable invoice number.
|
||||
final String? invoiceNumber;
|
||||
final String invoiceNumber;
|
||||
|
||||
/// Date when the invoice was issued.
|
||||
final DateTime? issueDate;
|
||||
/// Total amount in cents.
|
||||
final int amountCents;
|
||||
|
||||
/// Human-readable title (e.g. event name).
|
||||
final String? title;
|
||||
/// Current invoice lifecycle status.
|
||||
final InvoiceStatus status;
|
||||
|
||||
/// Name of the client business.
|
||||
final String? clientName;
|
||||
/// When payment is due.
|
||||
final DateTime? dueDate;
|
||||
|
||||
/// Address of the event/location.
|
||||
final String? locationAddress;
|
||||
/// When the invoice was paid (history endpoint).
|
||||
final DateTime? paymentDate;
|
||||
|
||||
/// Number of staff worked.
|
||||
final int? staffCount;
|
||||
/// Vendor ID associated with this invoice.
|
||||
final String? vendorId;
|
||||
|
||||
/// Total hours worked.
|
||||
final double? totalHours;
|
||||
/// Vendor company name.
|
||||
final String? vendorName;
|
||||
|
||||
/// List of workers associated with this invoice.
|
||||
final List<InvoiceWorker> workers;
|
||||
/// Serialises this [Invoice] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'invoiceId': invoiceId,
|
||||
'invoiceNumber': invoiceNumber,
|
||||
'amountCents': amountCents,
|
||||
'status': status.toJson(),
|
||||
'dueDate': dueDate?.toIso8601String(),
|
||||
'paymentDate': paymentDate?.toIso8601String(),
|
||||
'vendorId': vendorId,
|
||||
'vendorName': vendorName,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
eventId,
|
||||
businessId,
|
||||
status,
|
||||
totalAmount,
|
||||
workAmount,
|
||||
addonsAmount,
|
||||
invoiceId,
|
||||
invoiceNumber,
|
||||
issueDate,
|
||||
title,
|
||||
clientName,
|
||||
locationAddress,
|
||||
staffCount,
|
||||
totalHours,
|
||||
workers,
|
||||
];
|
||||
}
|
||||
|
||||
/// Represents a worker entry in an [Invoice].
|
||||
class InvoiceWorker extends Equatable {
|
||||
const InvoiceWorker({
|
||||
required this.name,
|
||||
required this.role,
|
||||
required this.amount,
|
||||
required this.hours,
|
||||
required this.rate,
|
||||
this.checkIn,
|
||||
this.checkOut,
|
||||
this.breakMinutes = 0,
|
||||
this.avatarUrl,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String role;
|
||||
final double amount;
|
||||
final double hours;
|
||||
final double rate;
|
||||
final DateTime? checkIn;
|
||||
final DateTime? checkOut;
|
||||
final int breakMinutes;
|
||||
final String? avatarUrl;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
name,
|
||||
role,
|
||||
amount,
|
||||
hours,
|
||||
rate,
|
||||
checkIn,
|
||||
checkOut,
|
||||
breakMinutes,
|
||||
avatarUrl,
|
||||
amountCents,
|
||||
status,
|
||||
dueDate,
|
||||
paymentDate,
|
||||
vendorId,
|
||||
vendorName,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a reason or log for a declined [Invoice].
|
||||
class InvoiceDecline extends Equatable {
|
||||
|
||||
const InvoiceDecline({
|
||||
required this.id,
|
||||
required this.invoiceId,
|
||||
required this.reason,
|
||||
required this.declinedAt,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The [Invoice] that was declined.
|
||||
final String invoiceId;
|
||||
|
||||
/// Reason provided by the client.
|
||||
final String reason;
|
||||
|
||||
/// When the decline happened.
|
||||
final DateTime declinedAt;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, invoiceId, reason, declinedAt];
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a line item in an [Invoice].
|
||||
///
|
||||
/// Corresponds to the work done by one [Staff] member.
|
||||
class InvoiceItem extends Equatable {
|
||||
|
||||
const InvoiceItem({
|
||||
required this.id,
|
||||
required this.invoiceId,
|
||||
required this.staffId,
|
||||
required this.workHours,
|
||||
required this.rate,
|
||||
required this.amount,
|
||||
});
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
|
||||
/// The [Invoice] this item belongs to.
|
||||
final String invoiceId;
|
||||
|
||||
/// The [Staff] member whose work is being billed.
|
||||
final String staffId;
|
||||
|
||||
/// Total billed hours.
|
||||
final double workHours;
|
||||
|
||||
/// Hourly rate applied.
|
||||
final double rate;
|
||||
|
||||
/// Total line item amount (workHours * rate).
|
||||
final double amount;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, invoiceId, staffId, workHours, rate, amount];
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A single data point in the staff payment chart.
|
||||
///
|
||||
/// Returned by `GET /staff/payments/chart`.
|
||||
class PaymentChartPoint extends Equatable {
|
||||
/// Creates a [PaymentChartPoint] instance.
|
||||
const PaymentChartPoint({
|
||||
required this.bucket,
|
||||
required this.amountCents,
|
||||
});
|
||||
|
||||
/// 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),
|
||||
amountCents: (json['amountCents'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Time bucket start (day, week, or month).
|
||||
final DateTime bucket;
|
||||
|
||||
/// Aggregated payment amount in cents for this bucket.
|
||||
final int amountCents;
|
||||
|
||||
/// Serialises this [PaymentChartPoint] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'bucket': bucket.toIso8601String(),
|
||||
'amountCents': amountCents,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[bucket, amountCents];
|
||||
}
|
||||
@@ -1,24 +1,29 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Summary of staff earnings.
|
||||
/// Aggregated payment summary for a staff member over a date range.
|
||||
///
|
||||
/// Returned by `GET /staff/payments/summary`.
|
||||
class PaymentSummary extends Equatable {
|
||||
/// Creates a [PaymentSummary] instance.
|
||||
const PaymentSummary({required this.totalEarningsCents});
|
||||
|
||||
const PaymentSummary({
|
||||
required this.weeklyEarnings,
|
||||
required this.monthlyEarnings,
|
||||
required this.pendingEarnings,
|
||||
required this.totalEarnings,
|
||||
});
|
||||
final double weeklyEarnings;
|
||||
final double monthlyEarnings;
|
||||
final double pendingEarnings;
|
||||
final double totalEarnings;
|
||||
/// Deserialises a [PaymentSummary] from a V2 API JSON map.
|
||||
factory PaymentSummary.fromJson(Map<String, dynamic> json) {
|
||||
return PaymentSummary(
|
||||
totalEarningsCents: (json['totalEarningsCents'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Total earnings in cents for the queried period.
|
||||
final int totalEarningsCents;
|
||||
|
||||
/// Serialises this [PaymentSummary] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'totalEarningsCents': totalEarningsCents,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
weeklyEarnings,
|
||||
monthlyEarnings,
|
||||
pendingEarnings,
|
||||
totalEarnings,
|
||||
];
|
||||
List<Object?> get props => <Object?>[totalEarningsCents];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Accumulated savings for a business.
|
||||
///
|
||||
/// Returned by `GET /client/billing/savings`.
|
||||
class Savings extends Equatable {
|
||||
/// Creates a [Savings] instance.
|
||||
const Savings({required this.savingsCents});
|
||||
|
||||
/// Deserialises a [Savings] from a V2 API JSON map.
|
||||
factory Savings.fromJson(Map<String, dynamic> json) {
|
||||
return Savings(
|
||||
savingsCents: (json['savingsCents'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Total savings amount in cents.
|
||||
final int savingsCents;
|
||||
|
||||
/// Serialises this [Savings] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'savingsCents': savingsCents,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[savingsCents];
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A single category in the spend breakdown.
|
||||
///
|
||||
/// Returned by `GET /client/billing/spend-breakdown`.
|
||||
class SpendItem extends Equatable {
|
||||
/// Creates a [SpendItem] instance.
|
||||
const SpendItem({
|
||||
required this.category,
|
||||
required this.amountCents,
|
||||
required this.percentage,
|
||||
});
|
||||
|
||||
/// Deserialises a [SpendItem] from a V2 API JSON map.
|
||||
factory SpendItem.fromJson(Map<String, dynamic> json) {
|
||||
return SpendItem(
|
||||
category: json['category'] as String,
|
||||
amountCents: (json['amountCents'] as num).toInt(),
|
||||
percentage: (json['percentage'] as num).toDouble(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Role/category name.
|
||||
final String category;
|
||||
|
||||
/// Total spend in cents for this category.
|
||||
final int amountCents;
|
||||
|
||||
/// Percentage of total spend.
|
||||
final double percentage;
|
||||
|
||||
/// Serialises this [SpendItem] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'category': category,
|
||||
'amountCents': amountCents,
|
||||
'percentage': percentage,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[category, amountCents, percentage];
|
||||
}
|
||||
@@ -1,76 +1,88 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Status of a staff payout.
|
||||
enum PaymentStatus {
|
||||
/// Payout calculated but not processed.
|
||||
pending,
|
||||
import 'package:krow_domain/src/entities/enums/payment_status.dart';
|
||||
|
||||
/// Submitted to banking provider.
|
||||
processing,
|
||||
|
||||
/// Successfully transferred to staff.
|
||||
paid,
|
||||
|
||||
/// Transfer failed.
|
||||
failed,
|
||||
|
||||
/// Status unknown.
|
||||
unknown,
|
||||
}
|
||||
|
||||
/// Represents a payout to a [Staff] member for a completed [Assignment].
|
||||
class StaffPayment extends Equatable {
|
||||
|
||||
const StaffPayment({
|
||||
required this.id,
|
||||
required this.staffId,
|
||||
required this.assignmentId,
|
||||
required this.amount,
|
||||
/// A single payment record for a staff member.
|
||||
///
|
||||
/// Returned by `GET /staff/payments/history`.
|
||||
class PaymentRecord extends Equatable {
|
||||
/// Creates a [PaymentRecord] instance.
|
||||
const PaymentRecord({
|
||||
required this.paymentId,
|
||||
required this.amountCents,
|
||||
required this.date,
|
||||
required this.status,
|
||||
this.paidAt,
|
||||
this.shiftTitle,
|
||||
this.shiftLocation,
|
||||
this.locationAddress,
|
||||
this.hoursWorked,
|
||||
this.hourlyRate,
|
||||
this.workedTime,
|
||||
this.shiftName,
|
||||
this.location,
|
||||
this.hourlyRateCents,
|
||||
this.minutesWorked,
|
||||
});
|
||||
|
||||
/// Deserialises a [PaymentRecord] from a V2 API JSON map.
|
||||
factory PaymentRecord.fromJson(Map<String, dynamic> json) {
|
||||
return PaymentRecord(
|
||||
paymentId: json['paymentId'] as String,
|
||||
amountCents: (json['amountCents'] as num).toInt(),
|
||||
date: DateTime.parse(json['date'] as String),
|
||||
status: PaymentStatus.fromJson(json['status'] as String?),
|
||||
shiftName: json['shiftName'] as String?,
|
||||
location: json['location'] as String?,
|
||||
hourlyRateCents: json['hourlyRateCents'] != null
|
||||
? (json['hourlyRateCents'] as num).toInt()
|
||||
: null,
|
||||
minutesWorked: json['minutesWorked'] != null
|
||||
? (json['minutesWorked'] as num).toInt()
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
final String paymentId;
|
||||
|
||||
/// The recipient [Staff].
|
||||
final String staffId;
|
||||
/// Payment amount in cents.
|
||||
final int amountCents;
|
||||
|
||||
/// The [Assignment] being paid for.
|
||||
final String assignmentId;
|
||||
/// Date the payment was processed or created.
|
||||
final DateTime date;
|
||||
|
||||
/// Amount to be paid.
|
||||
final double amount;
|
||||
|
||||
/// Processing status.
|
||||
/// Payment processing status.
|
||||
final PaymentStatus status;
|
||||
|
||||
/// When the payment was successfully processed.
|
||||
final DateTime? paidAt;
|
||||
/// Title of the associated shift.
|
||||
final String? shiftName;
|
||||
|
||||
/// Title of the shift worked.
|
||||
final String? shiftTitle;
|
||||
/// Location/hub name.
|
||||
final String? location;
|
||||
|
||||
/// Location/hub name of the shift.
|
||||
final String? shiftLocation;
|
||||
/// Hourly pay rate in cents.
|
||||
final int? hourlyRateCents;
|
||||
|
||||
/// Address of the shift location.
|
||||
final String? locationAddress;
|
||||
/// Total minutes worked for this payment.
|
||||
final int? minutesWorked;
|
||||
|
||||
/// Number of hours worked.
|
||||
final double? hoursWorked;
|
||||
|
||||
/// Hourly rate for the shift.
|
||||
final double? hourlyRate;
|
||||
|
||||
/// Work session duration or status.
|
||||
final String? workedTime;
|
||||
/// Serialises this [PaymentRecord] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'paymentId': paymentId,
|
||||
'amountCents': amountCents,
|
||||
'date': date.toIso8601String(),
|
||||
'status': status.toJson(),
|
||||
'shiftName': shiftName,
|
||||
'location': location,
|
||||
'hourlyRateCents': hourlyRateCents,
|
||||
'minutesWorked': minutesWorked,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, staffId, assignmentId, amount, status, paidAt, shiftTitle, shiftLocation, locationAddress, hoursWorked, hourlyRate, workedTime];
|
||||
}
|
||||
List<Object?> get props => <Object?>[
|
||||
paymentId,
|
||||
amountCents,
|
||||
date,
|
||||
status,
|
||||
shiftName,
|
||||
location,
|
||||
hourlyRateCents,
|
||||
minutesWorked,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,78 +1,88 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Status of a time card.
|
||||
enum TimeCardStatus {
|
||||
/// Waiting for approval or payment.
|
||||
pending,
|
||||
/// Approved by manager.
|
||||
approved,
|
||||
/// Payment has been issued.
|
||||
paid,
|
||||
/// Disputed by staff or client.
|
||||
disputed;
|
||||
|
||||
/// Whether the card is approved.
|
||||
bool get isApproved => this == TimeCardStatus.approved;
|
||||
/// Whether the card is paid.
|
||||
bool get isPaid => this == TimeCardStatus.paid;
|
||||
/// Whether the card is disputed.
|
||||
bool get isDisputed => this == TimeCardStatus.disputed;
|
||||
/// Whether the card is pending.
|
||||
bool get isPending => this == TimeCardStatus.pending;
|
||||
}
|
||||
|
||||
/// Represents a time card for a staff member.
|
||||
class TimeCard extends Equatable {
|
||||
|
||||
/// Creates a [TimeCard].
|
||||
const TimeCard({
|
||||
required this.id,
|
||||
required this.shiftTitle,
|
||||
required this.clientName,
|
||||
/// A single time-card entry for a completed shift.
|
||||
///
|
||||
/// Returned by `GET /staff/profile/time-card`.
|
||||
class TimeCardEntry extends Equatable {
|
||||
/// Creates a [TimeCardEntry] instance.
|
||||
const TimeCardEntry({
|
||||
required this.date,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.totalHours,
|
||||
required this.hourlyRate,
|
||||
required this.totalPay,
|
||||
required this.status,
|
||||
required this.shiftName,
|
||||
this.location,
|
||||
this.clockInAt,
|
||||
this.clockOutAt,
|
||||
required this.minutesWorked,
|
||||
this.hourlyRateCents,
|
||||
required this.totalPayCents,
|
||||
});
|
||||
/// Unique identifier of the time card (often matches Application ID).
|
||||
final String id;
|
||||
/// Title of the shift.
|
||||
final String shiftTitle;
|
||||
/// Name of the client business.
|
||||
final String clientName;
|
||||
|
||||
/// 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),
|
||||
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,
|
||||
minutesWorked: (json['minutesWorked'] as num).toInt(),
|
||||
hourlyRateCents: json['hourlyRateCents'] != null
|
||||
? (json['hourlyRateCents'] as num).toInt()
|
||||
: null,
|
||||
totalPayCents: (json['totalPayCents'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Date of the shift.
|
||||
final DateTime date;
|
||||
/// Actual or scheduled start time.
|
||||
final String startTime;
|
||||
/// Actual or scheduled end time.
|
||||
final String endTime;
|
||||
/// Total hours worked.
|
||||
final double totalHours;
|
||||
/// Hourly pay rate.
|
||||
final double hourlyRate;
|
||||
/// Total pay amount.
|
||||
final double totalPay;
|
||||
/// Current status of the time card.
|
||||
final TimeCardStatus status;
|
||||
/// Location name.
|
||||
|
||||
/// Title of the shift.
|
||||
final String shiftName;
|
||||
|
||||
/// Location/hub name.
|
||||
final String? location;
|
||||
|
||||
/// Clock-in timestamp.
|
||||
final DateTime? clockInAt;
|
||||
|
||||
/// Clock-out timestamp.
|
||||
final DateTime? clockOutAt;
|
||||
|
||||
/// Total minutes worked (regular + overtime).
|
||||
final int minutesWorked;
|
||||
|
||||
/// Hourly pay rate in cents.
|
||||
final int? hourlyRateCents;
|
||||
|
||||
/// Gross pay in cents.
|
||||
final int totalPayCents;
|
||||
|
||||
/// Serialises this [TimeCardEntry] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'date': date.toIso8601String(),
|
||||
'shiftName': shiftName,
|
||||
'location': location,
|
||||
'clockInAt': clockInAt?.toIso8601String(),
|
||||
'clockOutAt': clockOutAt?.toIso8601String(),
|
||||
'minutesWorked': minutesWorked,
|
||||
'hourlyRateCents': hourlyRateCents,
|
||||
'totalPayCents': totalPayCents,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
shiftTitle,
|
||||
clientName,
|
||||
date,
|
||||
startTime,
|
||||
endTime,
|
||||
totalHours,
|
||||
hourlyRate,
|
||||
totalPay,
|
||||
status,
|
||||
shiftName,
|
||||
location,
|
||||
clockInAt,
|
||||
clockOutAt,
|
||||
minutesWorked,
|
||||
hourlyRateCents,
|
||||
totalPayCents,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'coverage_metrics.dart';
|
||||
import 'live_activity_metrics.dart';
|
||||
import 'spending_summary.dart';
|
||||
|
||||
/// Client dashboard data aggregating key business metrics.
|
||||
///
|
||||
/// Returned by `GET /client/dashboard`.
|
||||
class ClientDashboard extends Equatable {
|
||||
/// Creates a [ClientDashboard] instance.
|
||||
const ClientDashboard({
|
||||
required this.userName,
|
||||
required this.businessName,
|
||||
required this.businessId,
|
||||
required this.spending,
|
||||
required this.coverage,
|
||||
required this.liveActivity,
|
||||
});
|
||||
|
||||
/// Deserialises a [ClientDashboard] from a V2 API JSON map.
|
||||
factory ClientDashboard.fromJson(Map<String, dynamic> json) {
|
||||
return ClientDashboard(
|
||||
userName: json['userName'] as String,
|
||||
businessName: json['businessName'] as String,
|
||||
businessId: json['businessId'] as String,
|
||||
spending:
|
||||
SpendingSummary.fromJson(json['spending'] as Map<String, dynamic>),
|
||||
coverage:
|
||||
CoverageMetrics.fromJson(json['coverage'] as Map<String, dynamic>),
|
||||
liveActivity: LiveActivityMetrics.fromJson(
|
||||
json['liveActivity'] as Map<String, dynamic>),
|
||||
);
|
||||
}
|
||||
|
||||
/// Display name of the logged-in user.
|
||||
final String userName;
|
||||
|
||||
/// Name of the business.
|
||||
final String businessName;
|
||||
|
||||
/// Business ID.
|
||||
final String businessId;
|
||||
|
||||
/// Spending summary.
|
||||
final SpendingSummary spending;
|
||||
|
||||
/// Today's coverage metrics.
|
||||
final CoverageMetrics coverage;
|
||||
|
||||
/// Live activity metrics.
|
||||
final LiveActivityMetrics liveActivity;
|
||||
|
||||
/// Serialises this [ClientDashboard] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'userName': userName,
|
||||
'businessName': businessName,
|
||||
'businessId': businessId,
|
||||
'spending': spending.toJson(),
|
||||
'coverage': coverage.toJson(),
|
||||
'liveActivity': liveActivity.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
userName,
|
||||
businessName,
|
||||
businessId,
|
||||
spending,
|
||||
coverage,
|
||||
liveActivity,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Today's coverage metrics nested in [ClientDashboard].
|
||||
class CoverageMetrics extends Equatable {
|
||||
/// Creates a [CoverageMetrics] instance.
|
||||
const CoverageMetrics({
|
||||
required this.neededWorkersToday,
|
||||
required this.filledWorkersToday,
|
||||
required this.openPositionsToday,
|
||||
});
|
||||
|
||||
/// Deserialises a [CoverageMetrics] from a V2 API JSON map.
|
||||
factory CoverageMetrics.fromJson(Map<String, dynamic> json) {
|
||||
return CoverageMetrics(
|
||||
neededWorkersToday: (json['neededWorkersToday'] as num).toInt(),
|
||||
filledWorkersToday: (json['filledWorkersToday'] as num).toInt(),
|
||||
openPositionsToday: (json['openPositionsToday'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Workers needed today.
|
||||
final int neededWorkersToday;
|
||||
|
||||
/// Workers filled today.
|
||||
final int filledWorkersToday;
|
||||
|
||||
/// Open (unfilled) positions today.
|
||||
final int openPositionsToday;
|
||||
|
||||
/// Serialises this [CoverageMetrics] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'neededWorkersToday': neededWorkersToday,
|
||||
'filledWorkersToday': filledWorkersToday,
|
||||
'openPositionsToday': openPositionsToday,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
<Object?>[neededWorkersToday, filledWorkersToday, openPositionsToday];
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Entity representing dashboard data for the home screen.
|
||||
///
|
||||
/// This entity provides aggregated metrics such as spending and shift counts
|
||||
/// for both the current week and the upcoming 7 days.
|
||||
class HomeDashboardData extends Equatable {
|
||||
|
||||
/// Creates a [HomeDashboardData] instance.
|
||||
const HomeDashboardData({
|
||||
required this.weeklySpending,
|
||||
required this.next7DaysSpending,
|
||||
required this.weeklyShifts,
|
||||
required this.next7DaysScheduled,
|
||||
required this.totalNeeded,
|
||||
required this.totalFilled,
|
||||
});
|
||||
/// Total spending for the current week.
|
||||
final double weeklySpending;
|
||||
|
||||
/// Projected spending for the next 7 days.
|
||||
final double next7DaysSpending;
|
||||
|
||||
/// Total shifts scheduled for the current week.
|
||||
final int weeklyShifts;
|
||||
|
||||
/// Shifts scheduled for the next 7 days.
|
||||
final int next7DaysScheduled;
|
||||
|
||||
/// Total workers needed for today's shifts.
|
||||
final int totalNeeded;
|
||||
|
||||
/// Total workers filled for today's shifts.
|
||||
final int totalFilled;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
weeklySpending,
|
||||
next7DaysSpending,
|
||||
weeklyShifts,
|
||||
next7DaysScheduled,
|
||||
totalNeeded,
|
||||
totalFilled,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Live activity metrics nested in [ClientDashboard].
|
||||
class LiveActivityMetrics extends Equatable {
|
||||
/// Creates a [LiveActivityMetrics] instance.
|
||||
const LiveActivityMetrics({
|
||||
required this.lateWorkersToday,
|
||||
required this.checkedInWorkersToday,
|
||||
required this.averageShiftCostCents,
|
||||
});
|
||||
|
||||
/// Deserialises a [LiveActivityMetrics] from a V2 API JSON map.
|
||||
factory LiveActivityMetrics.fromJson(Map<String, dynamic> json) {
|
||||
return LiveActivityMetrics(
|
||||
lateWorkersToday: (json['lateWorkersToday'] as num).toInt(),
|
||||
checkedInWorkersToday: (json['checkedInWorkersToday'] as num).toInt(),
|
||||
averageShiftCostCents:
|
||||
(json['averageShiftCostCents'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Workers marked late/no-show today.
|
||||
final int lateWorkersToday;
|
||||
|
||||
/// Workers who have checked in today.
|
||||
final int checkedInWorkersToday;
|
||||
|
||||
/// Average shift cost in cents.
|
||||
final int averageShiftCostCents;
|
||||
|
||||
/// Serialises this [LiveActivityMetrics] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'lateWorkersToday': lateWorkersToday,
|
||||
'checkedInWorkersToday': checkedInWorkersToday,
|
||||
'averageShiftCostCents': averageShiftCostCents,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
<Object?>[lateWorkersToday, checkedInWorkersToday, averageShiftCostCents];
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Summary of a completed order used for reorder suggestions.
|
||||
class ReorderItem extends Equatable {
|
||||
const ReorderItem({
|
||||
required this.orderId,
|
||||
required this.title,
|
||||
required this.location,
|
||||
required this.totalCost,
|
||||
required this.workers,
|
||||
required this.type,
|
||||
this.hourlyRate = 0,
|
||||
this.hours = 0,
|
||||
});
|
||||
|
||||
/// Unique identifier of the order.
|
||||
final String orderId;
|
||||
|
||||
/// Display title of the order (e.g., event name or first shift title).
|
||||
final String title;
|
||||
|
||||
/// Location of the order (e.g., first shift location).
|
||||
final String location;
|
||||
|
||||
/// Total calculated cost for the order.
|
||||
final double totalCost;
|
||||
|
||||
/// Total number of workers required for the order.
|
||||
final int workers;
|
||||
|
||||
/// The type of order (e.g., ONE_TIME, RECURRING).
|
||||
final String type;
|
||||
|
||||
/// Average or primary hourly rate (optional, for display).
|
||||
final double hourlyRate;
|
||||
|
||||
/// Total hours for the order (optional, for display).
|
||||
final double hours;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
orderId,
|
||||
title,
|
||||
location,
|
||||
totalCost,
|
||||
workers,
|
||||
type,
|
||||
hourlyRate,
|
||||
hours,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Spending summary nested in [ClientDashboard].
|
||||
class SpendingSummary extends Equatable {
|
||||
/// Creates a [SpendingSummary] instance.
|
||||
const SpendingSummary({
|
||||
required this.weeklySpendCents,
|
||||
required this.projectedNext7DaysCents,
|
||||
});
|
||||
|
||||
/// Deserialises a [SpendingSummary] from a V2 API JSON map.
|
||||
factory SpendingSummary.fromJson(Map<String, dynamic> json) {
|
||||
return SpendingSummary(
|
||||
weeklySpendCents: (json['weeklySpendCents'] as num).toInt(),
|
||||
projectedNext7DaysCents:
|
||||
(json['projectedNext7DaysCents'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Total spend this week in cents.
|
||||
final int weeklySpendCents;
|
||||
|
||||
/// Projected spend for the next 7 days in cents.
|
||||
final int projectedNext7DaysCents;
|
||||
|
||||
/// Serialises this [SpendingSummary] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'weeklySpendCents': weeklySpendCents,
|
||||
'projectedNext7DaysCents': projectedNext7DaysCents,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
<Object?>[weeklySpendCents, projectedNext7DaysCents];
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import '../benefits/benefit.dart';
|
||||
|
||||
/// Staff dashboard data with shifts and benefits overview.
|
||||
///
|
||||
/// Returned by `GET /staff/dashboard`.
|
||||
class StaffDashboard extends Equatable {
|
||||
/// Creates a [StaffDashboard] instance.
|
||||
const StaffDashboard({
|
||||
required this.staffName,
|
||||
this.todaysShifts = const <Map<String, dynamic>>[],
|
||||
this.tomorrowsShifts = const <Map<String, dynamic>>[],
|
||||
this.recommendedShifts = const <Map<String, dynamic>>[],
|
||||
this.benefits = const <Benefit>[],
|
||||
});
|
||||
|
||||
/// Deserialises a [StaffDashboard] from a V2 API JSON map.
|
||||
factory StaffDashboard.fromJson(Map<String, dynamic> json) {
|
||||
final dynamic benefitsRaw = json['benefits'];
|
||||
final List<Benefit> benefitsList = benefitsRaw is List
|
||||
? benefitsRaw
|
||||
.map((dynamic e) => Benefit.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
: const <Benefit>[];
|
||||
|
||||
return StaffDashboard(
|
||||
staffName: json['staffName'] as String,
|
||||
todaysShifts: _castShiftList(json['todaysShifts']),
|
||||
tomorrowsShifts: _castShiftList(json['tomorrowsShifts']),
|
||||
recommendedShifts: _castShiftList(json['recommendedShifts']),
|
||||
benefits: benefitsList,
|
||||
);
|
||||
}
|
||||
|
||||
/// Display name of the staff member.
|
||||
final String staffName;
|
||||
|
||||
/// Shifts assigned for today.
|
||||
final List<Map<String, dynamic>> todaysShifts;
|
||||
|
||||
/// Shifts assigned for tomorrow.
|
||||
final List<Map<String, dynamic>> tomorrowsShifts;
|
||||
|
||||
/// Recommended open shifts.
|
||||
final List<Map<String, dynamic>> recommendedShifts;
|
||||
|
||||
/// Active benefits.
|
||||
final List<Benefit> benefits;
|
||||
|
||||
/// Serialises this [StaffDashboard] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'staffName': staffName,
|
||||
'todaysShifts': todaysShifts,
|
||||
'tomorrowsShifts': tomorrowsShifts,
|
||||
'recommendedShifts': recommendedShifts,
|
||||
'benefits': benefits.map((Benefit b) => b.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
static List<Map<String, dynamic>> _castShiftList(dynamic raw) {
|
||||
if (raw is List) {
|
||||
return raw
|
||||
.map((dynamic e) =>
|
||||
Map<String, dynamic>.from(e as Map<dynamic, dynamic>))
|
||||
.toList();
|
||||
}
|
||||
return const <Map<String, dynamic>>[];
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
staffName,
|
||||
todaysShifts,
|
||||
tomorrowsShifts,
|
||||
recommendedShifts,
|
||||
benefits,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'package:krow_domain/src/entities/enums/application_status.dart';
|
||||
|
||||
/// Summary of a worker assigned to an order line item.
|
||||
///
|
||||
/// Nested within [OrderItem].
|
||||
class AssignedWorkerSummary extends Equatable {
|
||||
/// Creates an [AssignedWorkerSummary] instance.
|
||||
const AssignedWorkerSummary({
|
||||
this.applicationId,
|
||||
this.workerName,
|
||||
this.role,
|
||||
this.confirmationStatus,
|
||||
});
|
||||
|
||||
/// Deserialises an [AssignedWorkerSummary] from a V2 API JSON map.
|
||||
factory AssignedWorkerSummary.fromJson(Map<String, dynamic> json) {
|
||||
return AssignedWorkerSummary(
|
||||
applicationId: json['applicationId'] as String?,
|
||||
workerName: json['workerName'] as String?,
|
||||
role: json['role'] as String?,
|
||||
confirmationStatus: json['confirmationStatus'] != null
|
||||
? ApplicationStatus.fromJson(json['confirmationStatus'] as String?)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// Application ID for this worker assignment.
|
||||
final String? applicationId;
|
||||
|
||||
/// Display name of the worker.
|
||||
final String? workerName;
|
||||
|
||||
/// Role the worker is assigned to.
|
||||
final String? role;
|
||||
|
||||
/// Confirmation status of the assignment.
|
||||
final ApplicationStatus? confirmationStatus;
|
||||
|
||||
/// Serialises this [AssignedWorkerSummary] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'applicationId': applicationId,
|
||||
'workerName': workerName,
|
||||
'role': role,
|
||||
'confirmationStatus': confirmationStatus?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
applicationId,
|
||||
workerName,
|
||||
role,
|
||||
confirmationStatus,
|
||||
];
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'one_time_order_position.dart';
|
||||
|
||||
/// Represents a customer's request for a single event or shift.
|
||||
///
|
||||
/// Encapsulates the date, primary location, and a list of specific [OneTimeOrderPosition] requirements.
|
||||
class OneTimeOrder extends Equatable {
|
||||
|
||||
const OneTimeOrder({
|
||||
required this.date,
|
||||
required this.location,
|
||||
required this.positions,
|
||||
this.hub,
|
||||
this.eventName,
|
||||
this.vendorId,
|
||||
this.hubManagerId,
|
||||
this.roleRates = const <String, double>{},
|
||||
});
|
||||
/// The specific date for the shift or event.
|
||||
final DateTime date;
|
||||
|
||||
/// The primary location where the work will take place.
|
||||
final String location;
|
||||
|
||||
/// The list of positions and headcounts required for this order.
|
||||
final List<OneTimeOrderPosition> positions;
|
||||
|
||||
/// Selected hub details for this order.
|
||||
final OneTimeOrderHubDetails? hub;
|
||||
|
||||
/// Optional order name.
|
||||
final String? eventName;
|
||||
|
||||
/// Selected vendor id for this order.
|
||||
final String? vendorId;
|
||||
|
||||
/// Optional hub manager id.
|
||||
final String? hubManagerId;
|
||||
|
||||
/// Role hourly rates keyed by role id.
|
||||
final Map<String, double> roleRates;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
date,
|
||||
location,
|
||||
positions,
|
||||
hub,
|
||||
eventName,
|
||||
vendorId,
|
||||
hubManagerId,
|
||||
roleRates,
|
||||
];
|
||||
}
|
||||
|
||||
/// Minimal hub details used during order creation.
|
||||
class OneTimeOrderHubDetails extends Equatable {
|
||||
const OneTimeOrderHubDetails({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.address,
|
||||
this.placeId,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.city,
|
||||
this.state,
|
||||
this.street,
|
||||
this.country,
|
||||
this.zipCode,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
final String address;
|
||||
final String? placeId;
|
||||
final double? latitude;
|
||||
final double? longitude;
|
||||
final String? city;
|
||||
final String? state;
|
||||
final String? street;
|
||||
final String? country;
|
||||
final String? zipCode;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
name,
|
||||
address,
|
||||
placeId,
|
||||
latitude,
|
||||
longitude,
|
||||
city,
|
||||
state,
|
||||
street,
|
||||
country,
|
||||
zipCode,
|
||||
];
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a specific position requirement within a [OneTimeOrder].
|
||||
///
|
||||
/// Defines the role, headcount, and scheduling details for a single staffing requirement.
|
||||
class OneTimeOrderPosition extends Equatable {
|
||||
|
||||
const OneTimeOrderPosition({
|
||||
required this.role,
|
||||
required this.count,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
this.lunchBreak = 'NO_BREAK',
|
||||
this.location,
|
||||
});
|
||||
/// The job role or title required.
|
||||
final String role;
|
||||
|
||||
/// The number of workers required for this position.
|
||||
final int count;
|
||||
|
||||
/// The scheduled start time (e.g., "09:00 AM").
|
||||
final String startTime;
|
||||
|
||||
/// The scheduled end time (e.g., "05:00 PM").
|
||||
final String endTime;
|
||||
|
||||
/// The break duration enum value (e.g., NO_BREAK, MIN_15, MIN_30).
|
||||
final String lunchBreak;
|
||||
|
||||
/// Optional specific location for this position, if different from the order's main location.
|
||||
final String? location;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
role,
|
||||
count,
|
||||
startTime,
|
||||
endTime,
|
||||
lunchBreak,
|
||||
location,
|
||||
];
|
||||
|
||||
/// Creates a copy of this position with the given fields replaced.
|
||||
OneTimeOrderPosition copyWith({
|
||||
String? role,
|
||||
int? count,
|
||||
String? startTime,
|
||||
String? endTime,
|
||||
String? lunchBreak,
|
||||
String? location,
|
||||
}) {
|
||||
return OneTimeOrderPosition(
|
||||
role: role ?? this.role,
|
||||
count: count ?? this.count,
|
||||
startTime: startTime ?? this.startTime,
|
||||
endTime: endTime ?? this.endTime,
|
||||
lunchBreak: lunchBreak ?? this.lunchBreak,
|
||||
location: location ?? this.location,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,117 +1,137 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'order_type.dart';
|
||||
import 'package:krow_domain/src/entities/enums/order_type.dart';
|
||||
import 'package:krow_domain/src/entities/enums/shift_status.dart';
|
||||
|
||||
/// Represents a customer's view of an order or shift.
|
||||
import 'assigned_worker_summary.dart';
|
||||
|
||||
/// A line item within an order, representing a role needed for a shift.
|
||||
///
|
||||
/// This entity captures the details necessary for the dashboard/view orders screen,
|
||||
/// including status and worker assignments.
|
||||
/// Returned by `GET /client/orders/view`.
|
||||
class OrderItem extends Equatable {
|
||||
/// Creates an [OrderItem].
|
||||
/// Creates an [OrderItem] instance.
|
||||
const OrderItem({
|
||||
required this.id,
|
||||
required this.itemId,
|
||||
required this.orderId,
|
||||
required this.orderType,
|
||||
required this.title,
|
||||
required this.clientName,
|
||||
required this.status,
|
||||
required this.roleName,
|
||||
required this.date,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.location,
|
||||
required this.locationAddress,
|
||||
required this.filled,
|
||||
required this.workersNeeded,
|
||||
required this.hourlyRate,
|
||||
required this.eventName,
|
||||
this.hours = 0,
|
||||
this.totalValue = 0,
|
||||
this.confirmedApps = const <Map<String, dynamic>>[],
|
||||
this.hubManagerId,
|
||||
this.hubManagerName,
|
||||
required this.startsAt,
|
||||
required this.endsAt,
|
||||
required this.requiredWorkerCount,
|
||||
required this.filledCount,
|
||||
required this.hourlyRateCents,
|
||||
required this.totalCostCents,
|
||||
this.locationName,
|
||||
required this.status,
|
||||
this.workers = const <AssignedWorkerSummary>[],
|
||||
});
|
||||
|
||||
/// Unique identifier of the order.
|
||||
final String id;
|
||||
/// Deserialises an [OrderItem] from a V2 API JSON map.
|
||||
factory OrderItem.fromJson(Map<String, dynamic> json) {
|
||||
final dynamic workersRaw = json['workers'];
|
||||
final List<AssignedWorkerSummary> workersList = workersRaw is List
|
||||
? workersRaw
|
||||
.map((dynamic e) => AssignedWorkerSummary.fromJson(
|
||||
e as Map<String, dynamic>))
|
||||
.toList()
|
||||
: const <AssignedWorkerSummary>[];
|
||||
|
||||
/// Parent order identifier.
|
||||
return OrderItem(
|
||||
itemId: json['itemId'] as String,
|
||||
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),
|
||||
requiredWorkerCount: (json['requiredWorkerCount'] as num).toInt(),
|
||||
filledCount: (json['filledCount'] as num).toInt(),
|
||||
hourlyRateCents: (json['hourlyRateCents'] as num).toInt(),
|
||||
totalCostCents: (json['totalCostCents'] as num).toInt(),
|
||||
locationName: json['locationName'] as String?,
|
||||
status: ShiftStatus.fromJson(json['status'] as String?),
|
||||
workers: workersList,
|
||||
);
|
||||
}
|
||||
|
||||
/// Shift-role ID (primary key).
|
||||
final String itemId;
|
||||
|
||||
/// Parent order ID.
|
||||
final String orderId;
|
||||
|
||||
/// The type of order (e.g., ONE_TIME, PERMANENT).
|
||||
/// Order type (ONE_TIME, RECURRING, PERMANENT, RAPID).
|
||||
final OrderType orderType;
|
||||
|
||||
/// Title or name of the role.
|
||||
final String title;
|
||||
/// Name of the role.
|
||||
final String roleName;
|
||||
|
||||
/// Name of the client company.
|
||||
final String clientName;
|
||||
/// Shift date.
|
||||
final DateTime date;
|
||||
|
||||
/// status of the order (e.g., 'open', 'filled', 'completed').
|
||||
final String status;
|
||||
/// Shift start time.
|
||||
final DateTime startsAt;
|
||||
|
||||
/// Date of the shift (ISO format).
|
||||
final String date;
|
||||
/// Shift end time.
|
||||
final DateTime endsAt;
|
||||
|
||||
/// Start time of the shift.
|
||||
final String startTime;
|
||||
/// Total workers required.
|
||||
final int requiredWorkerCount;
|
||||
|
||||
/// End time of the shift.
|
||||
final String endTime;
|
||||
/// Workers currently assigned/filled.
|
||||
final int filledCount;
|
||||
|
||||
/// Location name.
|
||||
final String location;
|
||||
/// Billing rate in cents per hour.
|
||||
final int hourlyRateCents;
|
||||
|
||||
/// Full address of the location.
|
||||
final String locationAddress;
|
||||
/// Total cost in cents.
|
||||
final int totalCostCents;
|
||||
|
||||
/// Number of workers currently filled.
|
||||
final int filled;
|
||||
/// Location/hub name.
|
||||
final String? locationName;
|
||||
|
||||
/// Total number of workers required.
|
||||
final int workersNeeded;
|
||||
/// Shift status.
|
||||
final ShiftStatus status;
|
||||
|
||||
/// Hourly pay rate.
|
||||
final double hourlyRate;
|
||||
/// Assigned workers for this line item.
|
||||
final List<AssignedWorkerSummary> workers;
|
||||
|
||||
/// Total hours for the shift role.
|
||||
final double hours;
|
||||
|
||||
/// Total value for the shift role.
|
||||
final double totalValue;
|
||||
|
||||
/// Name of the event.
|
||||
final String eventName;
|
||||
|
||||
/// List of confirmed worker applications.
|
||||
final List<Map<String, dynamic>> confirmedApps;
|
||||
|
||||
/// Optional ID of the assigned hub manager.
|
||||
final String? hubManagerId;
|
||||
|
||||
/// Optional Name of the assigned hub manager.
|
||||
final String? hubManagerName;
|
||||
/// Serialises this [OrderItem] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'itemId': itemId,
|
||||
'orderId': orderId,
|
||||
'orderType': orderType.toJson(),
|
||||
'roleName': roleName,
|
||||
'date': date.toIso8601String(),
|
||||
'startsAt': startsAt.toIso8601String(),
|
||||
'endsAt': endsAt.toIso8601String(),
|
||||
'requiredWorkerCount': requiredWorkerCount,
|
||||
'filledCount': filledCount,
|
||||
'hourlyRateCents': hourlyRateCents,
|
||||
'totalCostCents': totalCostCents,
|
||||
'locationName': locationName,
|
||||
'status': status.toJson(),
|
||||
'workers': workers.map((AssignedWorkerSummary w) => w.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
orderId,
|
||||
orderType,
|
||||
title,
|
||||
clientName,
|
||||
status,
|
||||
date,
|
||||
startTime,
|
||||
endTime,
|
||||
location,
|
||||
locationAddress,
|
||||
filled,
|
||||
workersNeeded,
|
||||
hourlyRate,
|
||||
hours,
|
||||
totalValue,
|
||||
eventName,
|
||||
confirmedApps,
|
||||
hubManagerId,
|
||||
hubManagerName,
|
||||
];
|
||||
itemId,
|
||||
orderId,
|
||||
orderType,
|
||||
roleName,
|
||||
date,
|
||||
startsAt,
|
||||
endsAt,
|
||||
requiredWorkerCount,
|
||||
filledCount,
|
||||
hourlyRateCents,
|
||||
totalCostCents,
|
||||
locationName,
|
||||
status,
|
||||
workers,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A preview of an order for reordering purposes.
|
||||
///
|
||||
/// Returned by `GET /client/orders/:id/reorder-preview`.
|
||||
class OrderPreview extends Equatable {
|
||||
/// Creates an [OrderPreview] instance.
|
||||
const OrderPreview({
|
||||
required this.orderId,
|
||||
required this.title,
|
||||
this.description,
|
||||
this.startsAt,
|
||||
this.endsAt,
|
||||
this.locationName,
|
||||
this.locationAddress,
|
||||
this.metadata = const <String, dynamic>{},
|
||||
this.shifts = const <OrderPreviewShift>[],
|
||||
});
|
||||
|
||||
/// Deserialises an [OrderPreview] from a V2 API JSON map.
|
||||
factory OrderPreview.fromJson(Map<String, dynamic> json) {
|
||||
final dynamic shiftsRaw = json['shifts'];
|
||||
final List<OrderPreviewShift> shiftsList = shiftsRaw is List
|
||||
? shiftsRaw
|
||||
.map((dynamic e) =>
|
||||
OrderPreviewShift.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
: const <OrderPreviewShift>[];
|
||||
|
||||
return OrderPreview(
|
||||
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,
|
||||
locationName: json['locationName'] as String?,
|
||||
locationAddress: json['locationAddress'] as String?,
|
||||
metadata: json['metadata'] is Map
|
||||
? Map<String, dynamic>.from(json['metadata'] as Map<dynamic, dynamic>)
|
||||
: const <String, dynamic>{},
|
||||
shifts: shiftsList,
|
||||
);
|
||||
}
|
||||
|
||||
/// Order ID.
|
||||
final String orderId;
|
||||
|
||||
/// Order title.
|
||||
final String title;
|
||||
|
||||
/// Order description.
|
||||
final String? description;
|
||||
|
||||
/// Order start time.
|
||||
final DateTime? startsAt;
|
||||
|
||||
/// Order end time.
|
||||
final DateTime? endsAt;
|
||||
|
||||
/// Location name.
|
||||
final String? locationName;
|
||||
|
||||
/// Location address.
|
||||
final String? locationAddress;
|
||||
|
||||
/// Flexible metadata bag.
|
||||
final Map<String, dynamic> metadata;
|
||||
|
||||
/// Shifts with their roles from the original order.
|
||||
final List<OrderPreviewShift> shifts;
|
||||
|
||||
/// Serialises this [OrderPreview] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'orderId': orderId,
|
||||
'title': title,
|
||||
'description': description,
|
||||
'startsAt': startsAt?.toIso8601String(),
|
||||
'endsAt': endsAt?.toIso8601String(),
|
||||
'locationName': locationName,
|
||||
'locationAddress': locationAddress,
|
||||
'metadata': metadata,
|
||||
'shifts': shifts.map((OrderPreviewShift s) => s.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
orderId,
|
||||
title,
|
||||
description,
|
||||
startsAt,
|
||||
endsAt,
|
||||
locationName,
|
||||
locationAddress,
|
||||
metadata,
|
||||
shifts,
|
||||
];
|
||||
}
|
||||
|
||||
/// A shift within a reorder preview.
|
||||
class OrderPreviewShift extends Equatable {
|
||||
/// Creates an [OrderPreviewShift] instance.
|
||||
const OrderPreviewShift({
|
||||
required this.shiftId,
|
||||
required this.shiftCode,
|
||||
required this.title,
|
||||
required this.startsAt,
|
||||
required this.endsAt,
|
||||
this.roles = const <OrderPreviewRole>[],
|
||||
});
|
||||
|
||||
/// Deserialises an [OrderPreviewShift] from a V2 API JSON map.
|
||||
factory OrderPreviewShift.fromJson(Map<String, dynamic> json) {
|
||||
final dynamic rolesRaw = json['roles'];
|
||||
final List<OrderPreviewRole> rolesList = rolesRaw is List
|
||||
? rolesRaw
|
||||
.map((dynamic e) =>
|
||||
OrderPreviewRole.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
: const <OrderPreviewRole>[];
|
||||
|
||||
return OrderPreviewShift(
|
||||
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),
|
||||
roles: rolesList,
|
||||
);
|
||||
}
|
||||
|
||||
/// Shift ID.
|
||||
final String shiftId;
|
||||
|
||||
/// Shift code.
|
||||
final String shiftCode;
|
||||
|
||||
/// Shift title.
|
||||
final String title;
|
||||
|
||||
/// Shift start time.
|
||||
final DateTime startsAt;
|
||||
|
||||
/// Shift end time.
|
||||
final DateTime endsAt;
|
||||
|
||||
/// Roles in this shift.
|
||||
final List<OrderPreviewRole> roles;
|
||||
|
||||
/// Serialises this [OrderPreviewShift] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'shiftId': shiftId,
|
||||
'shiftCode': shiftCode,
|
||||
'title': title,
|
||||
'startsAt': startsAt.toIso8601String(),
|
||||
'endsAt': endsAt.toIso8601String(),
|
||||
'roles': roles.map((OrderPreviewRole r) => r.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
<Object?>[shiftId, shiftCode, title, startsAt, endsAt, roles];
|
||||
}
|
||||
|
||||
/// A role within a reorder preview shift.
|
||||
class OrderPreviewRole extends Equatable {
|
||||
/// Creates an [OrderPreviewRole] instance.
|
||||
const OrderPreviewRole({
|
||||
required this.roleId,
|
||||
required this.roleCode,
|
||||
required this.roleName,
|
||||
required this.workersNeeded,
|
||||
required this.payRateCents,
|
||||
required this.billRateCents,
|
||||
});
|
||||
|
||||
/// Deserialises an [OrderPreviewRole] from a V2 API JSON map.
|
||||
factory OrderPreviewRole.fromJson(Map<String, dynamic> json) {
|
||||
return OrderPreviewRole(
|
||||
roleId: json['roleId'] as String,
|
||||
roleCode: json['roleCode'] as String,
|
||||
roleName: json['roleName'] as String,
|
||||
workersNeeded: (json['workersNeeded'] as num).toInt(),
|
||||
payRateCents: (json['payRateCents'] as num).toInt(),
|
||||
billRateCents: (json['billRateCents'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Role ID.
|
||||
final String roleId;
|
||||
|
||||
/// Role code.
|
||||
final String roleCode;
|
||||
|
||||
/// Role name.
|
||||
final String roleName;
|
||||
|
||||
/// Workers needed for this role.
|
||||
final int workersNeeded;
|
||||
|
||||
/// Pay rate in cents per hour.
|
||||
final int payRateCents;
|
||||
|
||||
/// Bill rate in cents per hour.
|
||||
final int billRateCents;
|
||||
|
||||
/// Serialises this [OrderPreviewRole] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'roleId': roleId,
|
||||
'roleCode': roleCode,
|
||||
'roleName': roleName,
|
||||
'workersNeeded': workersNeeded,
|
||||
'payRateCents': payRateCents,
|
||||
'billRateCents': billRateCents,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
roleId,
|
||||
roleCode,
|
||||
roleName,
|
||||
workersNeeded,
|
||||
payRateCents,
|
||||
billRateCents,
|
||||
];
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/// Defines the type of an order.
|
||||
enum OrderType {
|
||||
/// A single occurrence shift.
|
||||
oneTime,
|
||||
|
||||
/// A long-term or permanent staffing position.
|
||||
permanent,
|
||||
|
||||
/// Shifts that repeat on a defined schedule.
|
||||
recurring,
|
||||
|
||||
/// A quickly created shift.
|
||||
rapid;
|
||||
|
||||
/// Creates an [OrderType] from a string value (typically from the backend).
|
||||
static OrderType fromString(String value) {
|
||||
switch (value.toUpperCase()) {
|
||||
case 'ONE_TIME':
|
||||
return OrderType.oneTime;
|
||||
case 'PERMANENT':
|
||||
return OrderType.permanent;
|
||||
case 'RECURRING':
|
||||
return OrderType.recurring;
|
||||
case 'RAPID':
|
||||
return OrderType.rapid;
|
||||
default:
|
||||
return OrderType.oneTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'one_time_order.dart';
|
||||
import 'one_time_order_position.dart';
|
||||
|
||||
/// Represents a customer's request for permanent/ongoing staffing.
|
||||
class PermanentOrder extends Equatable {
|
||||
const PermanentOrder({
|
||||
required this.startDate,
|
||||
required this.permanentDays,
|
||||
required this.positions,
|
||||
this.hub,
|
||||
this.eventName,
|
||||
this.vendorId,
|
||||
this.hubManagerId,
|
||||
this.roleRates = const <String, double>{},
|
||||
});
|
||||
|
||||
final DateTime startDate;
|
||||
|
||||
/// List of days (e.g., ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'])
|
||||
final List<String> permanentDays;
|
||||
|
||||
final List<OneTimeOrderPosition> positions;
|
||||
final OneTimeOrderHubDetails? hub;
|
||||
final String? eventName;
|
||||
final String? vendorId;
|
||||
final String? hubManagerId;
|
||||
final Map<String, double> roleRates;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
startDate,
|
||||
permanentDays,
|
||||
positions,
|
||||
hub,
|
||||
eventName,
|
||||
vendorId,
|
||||
hubManagerId,
|
||||
roleRates,
|
||||
];
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a specific position requirement within a [PermanentOrder].
|
||||
class PermanentOrderPosition extends Equatable {
|
||||
const PermanentOrderPosition({
|
||||
required this.role,
|
||||
required this.count,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
this.lunchBreak = 'NO_BREAK',
|
||||
this.location,
|
||||
});
|
||||
|
||||
/// The job role or title required.
|
||||
final String role;
|
||||
|
||||
/// The number of workers required for this position.
|
||||
final int count;
|
||||
|
||||
/// The scheduled start time (e.g., "09:00 AM").
|
||||
final String startTime;
|
||||
|
||||
/// The scheduled end time (e.g., "05:00 PM").
|
||||
final String endTime;
|
||||
|
||||
/// The break duration enum value (e.g., NO_BREAK, MIN_15, MIN_30).
|
||||
final String lunchBreak;
|
||||
|
||||
/// Optional specific location for this position, if different from the order's main location.
|
||||
final String? location;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
role,
|
||||
count,
|
||||
startTime,
|
||||
endTime,
|
||||
lunchBreak,
|
||||
location,
|
||||
];
|
||||
|
||||
/// Creates a copy of this position with the given fields replaced.
|
||||
PermanentOrderPosition copyWith({
|
||||
String? role,
|
||||
int? count,
|
||||
String? startTime,
|
||||
String? endTime,
|
||||
String? lunchBreak,
|
||||
String? location,
|
||||
}) {
|
||||
return PermanentOrderPosition(
|
||||
role: role ?? this.role,
|
||||
count: count ?? this.count,
|
||||
startTime: startTime ?? this.startTime,
|
||||
endTime: endTime ?? this.endTime,
|
||||
lunchBreak: lunchBreak ?? this.lunchBreak,
|
||||
location: location ?? this.location,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'package:krow_domain/src/entities/enums/order_type.dart';
|
||||
|
||||
/// A recently completed order available for reordering.
|
||||
///
|
||||
/// Returned by `GET /client/reorders`.
|
||||
class RecentOrder extends Equatable {
|
||||
/// Creates a [RecentOrder] instance.
|
||||
const RecentOrder({
|
||||
required this.id,
|
||||
required this.title,
|
||||
this.date,
|
||||
this.hubName,
|
||||
required this.positionCount,
|
||||
required this.orderType,
|
||||
});
|
||||
|
||||
/// Deserialises a [RecentOrder] from a V2 API JSON map.
|
||||
factory RecentOrder.fromJson(Map<String, dynamic> json) {
|
||||
return RecentOrder(
|
||||
id: json['id'] as String,
|
||||
title: json['title'] as String,
|
||||
date: json['date'] != null
|
||||
? DateTime.parse(json['date'] as String)
|
||||
: null,
|
||||
hubName: json['hubName'] as String?,
|
||||
positionCount: (json['positionCount'] as num).toInt(),
|
||||
orderType: OrderType.fromJson(json['orderType'] as String?),
|
||||
);
|
||||
}
|
||||
|
||||
/// Order ID.
|
||||
final String id;
|
||||
|
||||
/// Order title.
|
||||
final String title;
|
||||
|
||||
/// Order date.
|
||||
final DateTime? date;
|
||||
|
||||
/// Hub/location name.
|
||||
final String? hubName;
|
||||
|
||||
/// Number of positions in the order.
|
||||
final int positionCount;
|
||||
|
||||
/// Type of order (ONE_TIME, RECURRING, PERMANENT, RAPID).
|
||||
final OrderType orderType;
|
||||
|
||||
/// Serialises this [RecentOrder] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'id': id,
|
||||
'title': title,
|
||||
'date': date?.toIso8601String(),
|
||||
'hubName': hubName,
|
||||
'positionCount': positionCount,
|
||||
'orderType': orderType.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, title, date, hubName, positionCount, orderType];
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'recurring_order_position.dart';
|
||||
|
||||
/// Represents a recurring staffing request spanning a date range.
|
||||
class RecurringOrder extends Equatable {
|
||||
const RecurringOrder({
|
||||
required this.startDate,
|
||||
required this.endDate,
|
||||
required this.recurringDays,
|
||||
required this.location,
|
||||
required this.positions,
|
||||
this.hub,
|
||||
this.eventName,
|
||||
this.vendorId,
|
||||
this.hubManagerId,
|
||||
this.roleRates = const <String, double>{},
|
||||
});
|
||||
|
||||
/// Start date for the recurring schedule.
|
||||
final DateTime startDate;
|
||||
|
||||
/// End date for the recurring schedule.
|
||||
final DateTime endDate;
|
||||
|
||||
/// Days of the week to repeat on (e.g., ["S", "M", ...]).
|
||||
final List<String> recurringDays;
|
||||
|
||||
/// The primary location where the work will take place.
|
||||
final String location;
|
||||
|
||||
/// The list of positions and headcounts required for this order.
|
||||
final List<RecurringOrderPosition> positions;
|
||||
|
||||
/// Selected hub details for this order.
|
||||
final RecurringOrderHubDetails? hub;
|
||||
|
||||
/// Optional order name.
|
||||
final String? eventName;
|
||||
|
||||
/// Selected vendor id for this order.
|
||||
final String? vendorId;
|
||||
|
||||
/// Optional hub manager id.
|
||||
final String? hubManagerId;
|
||||
|
||||
/// Role hourly rates keyed by role id.
|
||||
final Map<String, double> roleRates;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
startDate,
|
||||
endDate,
|
||||
recurringDays,
|
||||
location,
|
||||
positions,
|
||||
hub,
|
||||
eventName,
|
||||
vendorId,
|
||||
hubManagerId,
|
||||
roleRates,
|
||||
];
|
||||
}
|
||||
|
||||
/// Minimal hub details used during recurring order creation.
|
||||
class RecurringOrderHubDetails extends Equatable {
|
||||
const RecurringOrderHubDetails({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.address,
|
||||
this.placeId,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.city,
|
||||
this.state,
|
||||
this.street,
|
||||
this.country,
|
||||
this.zipCode,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
final String address;
|
||||
final String? placeId;
|
||||
final double? latitude;
|
||||
final double? longitude;
|
||||
final String? city;
|
||||
final String? state;
|
||||
final String? street;
|
||||
final String? country;
|
||||
final String? zipCode;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
name,
|
||||
address,
|
||||
placeId,
|
||||
latitude,
|
||||
longitude,
|
||||
city,
|
||||
state,
|
||||
street,
|
||||
country,
|
||||
zipCode,
|
||||
];
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Represents a specific position requirement within a [RecurringOrder].
|
||||
class RecurringOrderPosition extends Equatable {
|
||||
const RecurringOrderPosition({
|
||||
required this.role,
|
||||
required this.count,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
this.lunchBreak = 'NO_BREAK',
|
||||
this.location,
|
||||
});
|
||||
|
||||
/// The job role or title required.
|
||||
final String role;
|
||||
|
||||
/// The number of workers required for this position.
|
||||
final int count;
|
||||
|
||||
/// The scheduled start time (e.g., "09:00 AM").
|
||||
final String startTime;
|
||||
|
||||
/// The scheduled end time (e.g., "05:00 PM").
|
||||
final String endTime;
|
||||
|
||||
/// The break duration enum value (e.g., NO_BREAK, MIN_15, MIN_30).
|
||||
final String lunchBreak;
|
||||
|
||||
/// Optional specific location for this position, if different from the order's main location.
|
||||
final String? location;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
role,
|
||||
count,
|
||||
startTime,
|
||||
endTime,
|
||||
lunchBreak,
|
||||
location,
|
||||
];
|
||||
|
||||
/// Creates a copy of this position with the given fields replaced.
|
||||
RecurringOrderPosition copyWith({
|
||||
String? role,
|
||||
int? count,
|
||||
String? startTime,
|
||||
String? endTime,
|
||||
String? lunchBreak,
|
||||
String? location,
|
||||
}) {
|
||||
return RecurringOrderPosition(
|
||||
role: role ?? this.role,
|
||||
count: count ?? this.count,
|
||||
startTime: startTime ?? this.startTime,
|
||||
endTime: endTime ?? this.endTime,
|
||||
lunchBreak: lunchBreak ?? this.lunchBreak,
|
||||
location: location ?? this.location,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'one_time_order.dart';
|
||||
import 'order_type.dart';
|
||||
|
||||
/// Represents the full details of an order retrieved for reordering.
|
||||
class ReorderData extends Equatable {
|
||||
const ReorderData({
|
||||
required this.orderId,
|
||||
required this.orderType,
|
||||
required this.eventName,
|
||||
required this.vendorId,
|
||||
required this.hub,
|
||||
required this.positions,
|
||||
this.date,
|
||||
this.startDate,
|
||||
this.endDate,
|
||||
this.recurringDays = const <String>[],
|
||||
this.permanentDays = const <String>[],
|
||||
});
|
||||
|
||||
final String orderId;
|
||||
final OrderType orderType;
|
||||
final String eventName;
|
||||
final String? vendorId;
|
||||
final OneTimeOrderHubDetails hub;
|
||||
final List<ReorderPosition> positions;
|
||||
|
||||
// One-time specific
|
||||
final DateTime? date;
|
||||
|
||||
// Recurring/Permanent specific
|
||||
final DateTime? startDate;
|
||||
final DateTime? endDate;
|
||||
final List<String> recurringDays;
|
||||
final List<String> permanentDays;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
orderId,
|
||||
orderType,
|
||||
eventName,
|
||||
vendorId,
|
||||
hub,
|
||||
positions,
|
||||
date,
|
||||
startDate,
|
||||
endDate,
|
||||
recurringDays,
|
||||
permanentDays,
|
||||
];
|
||||
}
|
||||
|
||||
class ReorderPosition extends Equatable {
|
||||
const ReorderPosition({
|
||||
required this.roleId,
|
||||
required this.count,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
this.lunchBreak = 'NO_BREAK',
|
||||
});
|
||||
|
||||
final String roleId;
|
||||
final int count;
|
||||
final String startTime;
|
||||
final String endTime;
|
||||
final String lunchBreak;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
roleId,
|
||||
count,
|
||||
startTime,
|
||||
endTime,
|
||||
lunchBreak,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Status of an attire checklist item.
|
||||
enum AttireItemStatus {
|
||||
/// Photo has not been uploaded yet.
|
||||
notUploaded,
|
||||
|
||||
/// Upload is pending review.
|
||||
pending,
|
||||
|
||||
/// Photo has been verified/approved.
|
||||
verified,
|
||||
|
||||
/// Photo was rejected.
|
||||
rejected,
|
||||
|
||||
/// Document has expired.
|
||||
expired,
|
||||
}
|
||||
|
||||
/// An attire checklist item for a staff member.
|
||||
///
|
||||
/// Returned by `GET /staff/profile/attire`. Joins the `documents` catalog
|
||||
/// (filtered to ATTIRE type) with the staff-specific `staff_documents` record.
|
||||
class AttireChecklist extends Equatable {
|
||||
/// Creates an [AttireChecklist] instance.
|
||||
const AttireChecklist({
|
||||
required this.documentId,
|
||||
required this.name,
|
||||
this.description = '',
|
||||
this.mandatory = true,
|
||||
this.staffDocumentId,
|
||||
this.photoUri,
|
||||
required this.status,
|
||||
this.verificationStatus,
|
||||
});
|
||||
|
||||
/// Deserialises an [AttireChecklist] from the V2 API JSON response.
|
||||
factory AttireChecklist.fromJson(Map<String, dynamic> json) {
|
||||
return AttireChecklist(
|
||||
documentId: json['documentId'] as String,
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String? ?? '',
|
||||
mandatory: json['mandatory'] as bool? ?? true,
|
||||
staffDocumentId: json['staffDocumentId'] as String?,
|
||||
photoUri: json['photoUri'] as String?,
|
||||
status: _parseStatus(json['status'] as String?),
|
||||
verificationStatus: json['verificationStatus'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// Catalog document definition ID (UUID).
|
||||
final String documentId;
|
||||
|
||||
/// Human-readable attire item name.
|
||||
final String name;
|
||||
|
||||
/// Description of the attire requirement.
|
||||
final String description;
|
||||
|
||||
/// Whether this attire item is mandatory.
|
||||
final bool mandatory;
|
||||
|
||||
/// Staff-specific document record ID, or null if not uploaded.
|
||||
final String? staffDocumentId;
|
||||
|
||||
/// URI to the uploaded attire photo.
|
||||
final String? photoUri;
|
||||
|
||||
/// Current status of the attire item.
|
||||
final AttireItemStatus status;
|
||||
|
||||
/// Detailed verification status string (from metadata).
|
||||
final String? verificationStatus;
|
||||
|
||||
/// Whether a photo has been uploaded.
|
||||
bool get isUploaded => staffDocumentId != null;
|
||||
|
||||
/// Serialises this [AttireChecklist] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'documentId': documentId,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'mandatory': mandatory,
|
||||
'staffDocumentId': staffDocumentId,
|
||||
'photoUri': photoUri,
|
||||
'status': _statusToString(status),
|
||||
'verificationStatus': verificationStatus,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
documentId,
|
||||
name,
|
||||
description,
|
||||
mandatory,
|
||||
staffDocumentId,
|
||||
photoUri,
|
||||
status,
|
||||
verificationStatus,
|
||||
];
|
||||
|
||||
/// Parses a status string from the API.
|
||||
static AttireItemStatus _parseStatus(String? value) {
|
||||
switch (value?.toUpperCase()) {
|
||||
case 'NOT_UPLOADED':
|
||||
return AttireItemStatus.notUploaded;
|
||||
case 'PENDING':
|
||||
return AttireItemStatus.pending;
|
||||
case 'VERIFIED':
|
||||
return AttireItemStatus.verified;
|
||||
case 'REJECTED':
|
||||
return AttireItemStatus.rejected;
|
||||
case 'EXPIRED':
|
||||
return AttireItemStatus.expired;
|
||||
default:
|
||||
return AttireItemStatus.notUploaded;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts an [AttireItemStatus] to its API string.
|
||||
static String _statusToString(AttireItemStatus status) {
|
||||
switch (status) {
|
||||
case AttireItemStatus.notUploaded:
|
||||
return 'NOT_UPLOADED';
|
||||
case AttireItemStatus.pending:
|
||||
return 'PENDING';
|
||||
case AttireItemStatus.verified:
|
||||
return 'VERIFIED';
|
||||
case AttireItemStatus.rejected:
|
||||
return 'REJECTED';
|
||||
case AttireItemStatus.expired:
|
||||
return 'EXPIRED';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'attire_verification_status.dart';
|
||||
|
||||
/// Represents an attire item that a staff member might need or possess.
|
||||
///
|
||||
/// Attire items are specific clothing or equipment required for jobs.
|
||||
class AttireItem extends Equatable {
|
||||
/// Creates an [AttireItem].
|
||||
const AttireItem({
|
||||
required this.id,
|
||||
required this.code,
|
||||
required this.label,
|
||||
this.description,
|
||||
this.imageUrl,
|
||||
this.isMandatory = false,
|
||||
this.verificationStatus,
|
||||
this.photoUrl,
|
||||
this.verificationId,
|
||||
});
|
||||
|
||||
/// Unique identifier of the attire item (UUID).
|
||||
final String id;
|
||||
|
||||
/// String code for the attire item (e.g. BLACK_TSHIRT).
|
||||
final String code;
|
||||
|
||||
/// Display name of the item.
|
||||
final String label;
|
||||
|
||||
/// Optional description for the attire item.
|
||||
final String? description;
|
||||
|
||||
/// URL of the reference image.
|
||||
final String? imageUrl;
|
||||
|
||||
/// Whether this item is mandatory for onboarding.
|
||||
final bool isMandatory;
|
||||
|
||||
/// The current verification status of the uploaded photo.
|
||||
final AttireVerificationStatus? verificationStatus;
|
||||
|
||||
/// The URL of the photo uploaded by the staff member.
|
||||
final String? photoUrl;
|
||||
|
||||
/// The ID of the verification record.
|
||||
final String? verificationId;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
code,
|
||||
label,
|
||||
description,
|
||||
imageUrl,
|
||||
isMandatory,
|
||||
verificationStatus,
|
||||
photoUrl,
|
||||
verificationId,
|
||||
];
|
||||
|
||||
/// Creates a copy of this [AttireItem] with the given fields replaced.
|
||||
AttireItem copyWith({
|
||||
String? id,
|
||||
String? code,
|
||||
String? label,
|
||||
String? description,
|
||||
String? imageUrl,
|
||||
bool? isMandatory,
|
||||
AttireVerificationStatus? verificationStatus,
|
||||
String? photoUrl,
|
||||
String? verificationId,
|
||||
}) {
|
||||
return AttireItem(
|
||||
id: id ?? this.id,
|
||||
code: code ?? this.code,
|
||||
label: label ?? this.label,
|
||||
description: description ?? this.description,
|
||||
imageUrl: imageUrl ?? this.imageUrl,
|
||||
isMandatory: isMandatory ?? this.isMandatory,
|
||||
verificationStatus: verificationStatus ?? this.verificationStatus,
|
||||
photoUrl: photoUrl ?? this.photoUrl,
|
||||
verificationId: verificationId ?? this.verificationId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/// Represents the verification status of an attire item photo.
|
||||
enum AttireVerificationStatus {
|
||||
/// Job is created and waiting to be processed.
|
||||
pending('PENDING'),
|
||||
|
||||
/// Job is currently being processed by machine or human.
|
||||
processing('PROCESSING'),
|
||||
|
||||
/// Machine verification passed automatically.
|
||||
autoPass('AUTO_PASS'),
|
||||
|
||||
/// Machine verification failed automatically.
|
||||
autoFail('AUTO_FAIL'),
|
||||
|
||||
/// Machine results are inconclusive and require human review.
|
||||
needsReview('NEEDS_REVIEW'),
|
||||
|
||||
/// Human reviewer approved the verification.
|
||||
approved('APPROVED'),
|
||||
|
||||
/// Human reviewer rejected the verification.
|
||||
rejected('REJECTED'),
|
||||
|
||||
/// An error occurred during processing.
|
||||
error('ERROR');
|
||||
|
||||
const AttireVerificationStatus(this.value);
|
||||
|
||||
/// The string value expected by the Core API.
|
||||
final String value;
|
||||
|
||||
/// Creates a [AttireVerificationStatus] from a string.
|
||||
static AttireVerificationStatus fromString(String value) {
|
||||
return AttireVerificationStatus.values.firstWhere(
|
||||
(AttireVerificationStatus e) => e.value == value,
|
||||
orElse: () => AttireVerificationStatus.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Status of a staff certificate.
|
||||
enum CertificateStatus {
|
||||
/// Certificate uploaded, pending verification.
|
||||
pending,
|
||||
|
||||
/// Certificate has been verified.
|
||||
verified,
|
||||
|
||||
/// Certificate was rejected.
|
||||
rejected,
|
||||
|
||||
/// Certificate has expired.
|
||||
expired,
|
||||
}
|
||||
|
||||
/// A staff certificate record (e.g. food hygiene, SIA badge).
|
||||
///
|
||||
/// Returned by `GET /staff/profile/certificates`. Maps to the V2
|
||||
/// `certificates` table with additional metadata fields.
|
||||
/// Named [StaffCertificate] to distinguish from the catalog [Certificate]
|
||||
/// definition in `skills/certificate.dart`.
|
||||
class StaffCertificate extends Equatable {
|
||||
/// Creates a [StaffCertificate] instance.
|
||||
const StaffCertificate({
|
||||
required this.certificateId,
|
||||
required this.certificateType,
|
||||
required this.name,
|
||||
this.fileUri,
|
||||
this.issuer,
|
||||
this.certificateNumber,
|
||||
this.issuedAt,
|
||||
this.expiresAt,
|
||||
required this.status,
|
||||
this.verificationStatus,
|
||||
});
|
||||
|
||||
/// Deserialises a [StaffCertificate] from the V2 API JSON response.
|
||||
factory StaffCertificate.fromJson(Map<String, dynamic> json) {
|
||||
return StaffCertificate(
|
||||
certificateId: json['certificateId'] as String,
|
||||
certificateType: json['certificateType'] as String,
|
||||
name: json['name'] as String,
|
||||
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,
|
||||
status: _parseStatus(json['status'] as String?),
|
||||
verificationStatus: json['verificationStatus'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// Certificate record UUID.
|
||||
final String certificateId;
|
||||
|
||||
/// Type code (e.g. "FOOD_HYGIENE", "SIA_BADGE").
|
||||
final String certificateType;
|
||||
|
||||
/// Human-readable name (from metadata or falls back to certificateType).
|
||||
final String name;
|
||||
|
||||
/// URI to the uploaded certificate file.
|
||||
final String? fileUri;
|
||||
|
||||
/// Issuing authority name (from metadata).
|
||||
final String? issuer;
|
||||
|
||||
/// Certificate number/ID.
|
||||
final String? certificateNumber;
|
||||
|
||||
/// When the certificate was issued.
|
||||
final DateTime? issuedAt;
|
||||
|
||||
/// When the certificate expires.
|
||||
final DateTime? expiresAt;
|
||||
|
||||
/// Current verification status.
|
||||
final CertificateStatus status;
|
||||
|
||||
/// Detailed verification status string (from metadata).
|
||||
final String? verificationStatus;
|
||||
|
||||
/// Whether the certificate has expired based on [expiresAt].
|
||||
bool get isExpired => expiresAt != null && expiresAt!.isBefore(DateTime.now());
|
||||
|
||||
/// Serialises this [StaffCertificate] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'certificateId': certificateId,
|
||||
'certificateType': certificateType,
|
||||
'name': name,
|
||||
'fileUri': fileUri,
|
||||
'issuer': issuer,
|
||||
'certificateNumber': certificateNumber,
|
||||
'issuedAt': issuedAt?.toIso8601String(),
|
||||
'expiresAt': expiresAt?.toIso8601String(),
|
||||
'status': _statusToString(status),
|
||||
'verificationStatus': verificationStatus,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
certificateId,
|
||||
certificateType,
|
||||
name,
|
||||
fileUri,
|
||||
issuer,
|
||||
certificateNumber,
|
||||
issuedAt,
|
||||
expiresAt,
|
||||
status,
|
||||
verificationStatus,
|
||||
];
|
||||
|
||||
/// Parses a status string from the API.
|
||||
static CertificateStatus _parseStatus(String? value) {
|
||||
switch (value?.toUpperCase()) {
|
||||
case 'PENDING':
|
||||
return CertificateStatus.pending;
|
||||
case 'VERIFIED':
|
||||
return CertificateStatus.verified;
|
||||
case 'REJECTED':
|
||||
return CertificateStatus.rejected;
|
||||
case 'EXPIRED':
|
||||
return CertificateStatus.expired;
|
||||
default:
|
||||
return CertificateStatus.pending;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a [CertificateStatus] to its API string.
|
||||
static String _statusToString(CertificateStatus status) {
|
||||
switch (status) {
|
||||
case CertificateStatus.pending:
|
||||
return 'PENDING';
|
||||
case CertificateStatus.verified:
|
||||
return 'VERIFIED';
|
||||
case CertificateStatus.rejected:
|
||||
return 'REJECTED';
|
||||
case CertificateStatus.expired:
|
||||
return 'EXPIRED';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/// Represents the broad category of a compliance certificate.
|
||||
enum ComplianceType {
|
||||
backgroundCheck('BACKGROUND_CHECK'),
|
||||
foodHandler('FOOD_HANDLER'),
|
||||
rbs('RBS'),
|
||||
legal('LEGAL'),
|
||||
operational('OPERATIONAL'),
|
||||
safety('SAFETY'),
|
||||
training('TRAINING'),
|
||||
license('LICENSE'),
|
||||
other('OTHER');
|
||||
|
||||
const ComplianceType(this.value);
|
||||
|
||||
/// The string value expected by the backend.
|
||||
final String value;
|
||||
|
||||
/// Creates a [ComplianceType] from a string.
|
||||
static ComplianceType fromString(String value) {
|
||||
return ComplianceType.values.firstWhere(
|
||||
(ComplianceType e) => e.value == value,
|
||||
orElse: () => ComplianceType.other,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/// Represents the verification status of a compliance document.
|
||||
enum DocumentVerificationStatus {
|
||||
/// Job is created and waiting to be processed.
|
||||
pending('PENDING'),
|
||||
|
||||
/// Job is currently being processed by machine or human.
|
||||
processing('PROCESSING'),
|
||||
|
||||
/// Machine verification passed automatically.
|
||||
autoPass('AUTO_PASS'),
|
||||
|
||||
/// Machine verification failed automatically.
|
||||
autoFail('AUTO_FAIL'),
|
||||
|
||||
/// Machine results are inconclusive and require human review.
|
||||
needsReview('NEEDS_REVIEW'),
|
||||
|
||||
/// Human reviewer approved the verification.
|
||||
approved('APPROVED'),
|
||||
|
||||
/// Human reviewer rejected the verification.
|
||||
rejected('REJECTED'),
|
||||
|
||||
/// An error occurred during processing.
|
||||
error('ERROR');
|
||||
|
||||
const DocumentVerificationStatus(this.value);
|
||||
|
||||
/// The string value expected by the Core API.
|
||||
final String value;
|
||||
|
||||
/// Creates a [DocumentVerificationStatus] from a string.
|
||||
static DocumentVerificationStatus fromString(String value) {
|
||||
return DocumentVerificationStatus.values.firstWhere(
|
||||
(DocumentVerificationStatus e) => e.value == value,
|
||||
orElse: () => DocumentVerificationStatus.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +1,89 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'relationship_type.dart';
|
||||
|
||||
/// Represents an emergency contact for a user.
|
||||
/// An emergency contact for a staff member.
|
||||
///
|
||||
/// Critical for staff safety during shifts.
|
||||
/// Maps to the V2 `emergency_contacts` table. Returned by
|
||||
/// `GET /staff/profile/emergency-contacts`.
|
||||
class EmergencyContact extends Equatable {
|
||||
|
||||
/// Creates an [EmergencyContact] instance.
|
||||
const EmergencyContact({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.relationship,
|
||||
required this.contactId,
|
||||
required this.fullName,
|
||||
required this.phone,
|
||||
required this.relationshipType,
|
||||
this.isPrimary = false,
|
||||
});
|
||||
|
||||
/// Unique identifier.
|
||||
final String id;
|
||||
/// Deserialises an [EmergencyContact] from a V2 API JSON response.
|
||||
factory EmergencyContact.fromJson(Map<String, dynamic> json) {
|
||||
return EmergencyContact(
|
||||
contactId: json['contactId'] as String,
|
||||
fullName: json['fullName'] as String,
|
||||
phone: json['phone'] as String,
|
||||
relationshipType: json['relationshipType'] as String,
|
||||
isPrimary: json['isPrimary'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Full name of the contact.
|
||||
final String name;
|
||||
/// Unique contact record ID (UUID).
|
||||
final String contactId;
|
||||
|
||||
/// Relationship to the user (e.g. "Spouse", "Parent").
|
||||
final RelationshipType relationship;
|
||||
/// Full name of the contact person.
|
||||
final String fullName;
|
||||
|
||||
/// Phone number.
|
||||
final String phone;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[id, name, relationship, phone];
|
||||
/// Relationship to the staff member (e.g. "SPOUSE", "PARENT", "FRIEND").
|
||||
final String relationshipType;
|
||||
|
||||
/// Whether this is the primary emergency contact.
|
||||
final bool isPrimary;
|
||||
|
||||
/// Serialises this [EmergencyContact] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'contactId': contactId,
|
||||
'fullName': fullName,
|
||||
'phone': phone,
|
||||
'relationshipType': relationshipType,
|
||||
'isPrimary': isPrimary,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns a copy of this [EmergencyContact] with the given fields replaced.
|
||||
EmergencyContact copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
String? contactId,
|
||||
String? fullName,
|
||||
String? phone,
|
||||
RelationshipType? relationship,
|
||||
String? relationshipType,
|
||||
bool? isPrimary,
|
||||
}) {
|
||||
return EmergencyContact(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
contactId: contactId ?? this.contactId,
|
||||
fullName: fullName ?? this.fullName,
|
||||
phone: phone ?? this.phone,
|
||||
relationship: relationship ?? this.relationship,
|
||||
relationshipType: relationshipType ?? this.relationshipType,
|
||||
isPrimary: isPrimary ?? this.isPrimary,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns an empty [EmergencyContact].
|
||||
/// Returns an empty [EmergencyContact] for form initialisation.
|
||||
static EmergencyContact empty() {
|
||||
return const EmergencyContact(
|
||||
id: '',
|
||||
name: '',
|
||||
contactId: '',
|
||||
fullName: '',
|
||||
phone: '',
|
||||
relationship: RelationshipType.family,
|
||||
relationshipType: 'FAMILY',
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts a string value to a [RelationshipType].
|
||||
static RelationshipType stringToRelationshipType(String? value) {
|
||||
if (value != null) {
|
||||
final String strVal = value.toUpperCase();
|
||||
switch (strVal) {
|
||||
case 'FAMILY':
|
||||
return RelationshipType.family;
|
||||
case 'SPOUSE':
|
||||
return RelationshipType.spouse;
|
||||
case 'FRIEND':
|
||||
return RelationshipType.friend;
|
||||
case 'OTHER':
|
||||
return RelationshipType.other;
|
||||
default:
|
||||
return RelationshipType.other;
|
||||
}
|
||||
}
|
||||
return RelationshipType.other;
|
||||
}
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
contactId,
|
||||
fullName,
|
||||
phone,
|
||||
relationshipType,
|
||||
isPrimary,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
enum ExperienceSkill {
|
||||
foodService('food_service'),
|
||||
bartending('bartending'),
|
||||
eventSetup('event_setup'),
|
||||
hospitality('hospitality'),
|
||||
warehouse('warehouse'),
|
||||
customerService('customer_service'),
|
||||
cleaning('cleaning'),
|
||||
security('security'),
|
||||
retail('retail'),
|
||||
cooking('cooking'),
|
||||
cashier('cashier'),
|
||||
server('server'),
|
||||
barista('barista'),
|
||||
hostHostess('host_hostess'),
|
||||
busser('busser'),
|
||||
driving('driving');
|
||||
|
||||
final String value;
|
||||
const ExperienceSkill(this.value);
|
||||
|
||||
static ExperienceSkill? fromString(String value) {
|
||||
try {
|
||||
return ExperienceSkill.values.firstWhere((ExperienceSkill e) => e.value == value);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
enum Industry {
|
||||
hospitality('hospitality'),
|
||||
foodService('food_service'),
|
||||
warehouse('warehouse'),
|
||||
events('events'),
|
||||
retail('retail'),
|
||||
healthcare('healthcare'),
|
||||
other('other');
|
||||
|
||||
final String value;
|
||||
const Industry(this.value);
|
||||
|
||||
static Industry? fromString(String value) {
|
||||
try {
|
||||
return Industry.values.firstWhere((Industry e) => e.value == value);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Staff privacy settings returned by `GET /staff/profile/privacy`.
|
||||
///
|
||||
/// Currently contains a single visibility flag stored in the staff
|
||||
/// metadata blob. May expand as more privacy controls are added.
|
||||
class PrivacySettings extends Equatable {
|
||||
/// Creates a [PrivacySettings] instance.
|
||||
const PrivacySettings({
|
||||
this.profileVisible = true,
|
||||
});
|
||||
|
||||
/// Deserialises [PrivacySettings] from the V2 API JSON response.
|
||||
factory PrivacySettings.fromJson(Map<String, dynamic> json) {
|
||||
return PrivacySettings(
|
||||
profileVisible: json['profileVisible'] as bool? ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Whether the staff profile is visible to businesses.
|
||||
final bool profileVisible;
|
||||
|
||||
/// Serialises this [PrivacySettings] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'profileVisible': profileVisible,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[profileVisible];
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Profile completion data returned by `GET /staff/profile-completion`.
|
||||
///
|
||||
/// Contains per-field completion booleans derived from the staff metadata,
|
||||
/// plus an overall completion flag and a list of missing fields.
|
||||
class ProfileCompletion extends Equatable {
|
||||
/// Creates a [ProfileCompletion] instance.
|
||||
const ProfileCompletion({
|
||||
required this.staffId,
|
||||
this.completed = false,
|
||||
this.missingFields = const <String>[],
|
||||
this.fields = const <String, bool>{},
|
||||
});
|
||||
|
||||
/// Deserialises a [ProfileCompletion] from the V2 API JSON response.
|
||||
factory ProfileCompletion.fromJson(Map<String, dynamic> json) {
|
||||
final Object? rawFields = json['fields'];
|
||||
final Map<String, bool> fields = <String, bool>{};
|
||||
if (rawFields is Map<String, dynamic>) {
|
||||
for (final MapEntry<String, dynamic> entry in rawFields.entries) {
|
||||
fields[entry.key] = entry.value == true;
|
||||
}
|
||||
}
|
||||
|
||||
return ProfileCompletion(
|
||||
staffId: json['staffId'] as String,
|
||||
completed: json['completed'] as bool? ?? false,
|
||||
missingFields: _parseStringList(json['missingFields']),
|
||||
fields: fields,
|
||||
);
|
||||
}
|
||||
|
||||
/// Staff profile UUID.
|
||||
final String staffId;
|
||||
|
||||
/// Whether all required fields are complete.
|
||||
final bool completed;
|
||||
|
||||
/// List of field names that are still missing.
|
||||
final List<String> missingFields;
|
||||
|
||||
/// Per-field completion map (field name to boolean).
|
||||
///
|
||||
/// Known keys: firstName, lastName, email, phone, preferredLocations,
|
||||
/// skills, industries, emergencyContact.
|
||||
final Map<String, bool> fields;
|
||||
|
||||
/// Percentage of fields completed (0 - 100).
|
||||
int get percentComplete {
|
||||
if (fields.isEmpty) return 0;
|
||||
final int completedCount = fields.values.where((bool v) => v).length;
|
||||
return ((completedCount / fields.length) * 100).round();
|
||||
}
|
||||
|
||||
/// Serialises this [ProfileCompletion] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'staffId': staffId,
|
||||
'completed': completed,
|
||||
'missingFields': missingFields,
|
||||
'fields': fields,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
staffId,
|
||||
completed,
|
||||
missingFields,
|
||||
fields,
|
||||
];
|
||||
|
||||
/// Parses a dynamic value into a list of strings.
|
||||
static List<String> _parseStringList(Object? value) {
|
||||
if (value is List) {
|
||||
return value.map((Object? e) => e.toString()).toList();
|
||||
}
|
||||
return const <String>[];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Status of a profile document.
|
||||
enum ProfileDocumentStatus {
|
||||
/// Document has not been uploaded yet.
|
||||
notUploaded,
|
||||
|
||||
/// Upload is pending review.
|
||||
pending,
|
||||
|
||||
/// Document has been verified/approved.
|
||||
verified,
|
||||
|
||||
/// Document was rejected.
|
||||
rejected,
|
||||
|
||||
/// Document has expired.
|
||||
expired,
|
||||
}
|
||||
|
||||
/// Type category of a profile document.
|
||||
enum ProfileDocumentType {
|
||||
/// General compliance document.
|
||||
document,
|
||||
|
||||
/// Government-issued ID.
|
||||
governmentId,
|
||||
|
||||
/// Attire/uniform photo.
|
||||
attire,
|
||||
|
||||
/// Tax form (I-9, W-4).
|
||||
taxForm,
|
||||
}
|
||||
|
||||
/// A profile document (or document requirement) for a staff member.
|
||||
///
|
||||
/// Returned by `GET /staff/profile/documents`. Joins the `documents` catalog
|
||||
/// with the staff-specific `staff_documents` record (if uploaded).
|
||||
class ProfileDocument extends Equatable {
|
||||
/// Creates a [ProfileDocument] instance.
|
||||
const ProfileDocument({
|
||||
required this.documentId,
|
||||
required this.documentType,
|
||||
required this.name,
|
||||
this.staffDocumentId,
|
||||
this.fileUri,
|
||||
required this.status,
|
||||
this.expiresAt,
|
||||
this.metadata = const <String, dynamic>{},
|
||||
});
|
||||
|
||||
/// Deserialises a [ProfileDocument] from the V2 API JSON response.
|
||||
factory ProfileDocument.fromJson(Map<String, dynamic> json) {
|
||||
return ProfileDocument(
|
||||
documentId: json['documentId'] as String,
|
||||
documentType: _parseDocumentType(json['documentType'] as String?),
|
||||
name: json['name'] as String,
|
||||
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,
|
||||
metadata: (json['metadata'] as Map<String, dynamic>?) ?? const <String, dynamic>{},
|
||||
);
|
||||
}
|
||||
|
||||
/// Catalog document definition ID (UUID).
|
||||
final String documentId;
|
||||
|
||||
/// Type category of this document.
|
||||
final ProfileDocumentType documentType;
|
||||
|
||||
/// Human-readable document name.
|
||||
final String name;
|
||||
|
||||
/// Staff-specific document record ID, or null if not yet uploaded.
|
||||
final String? staffDocumentId;
|
||||
|
||||
/// URI to the uploaded file.
|
||||
final String? fileUri;
|
||||
|
||||
/// Current status of the document.
|
||||
final ProfileDocumentStatus status;
|
||||
|
||||
/// When the document expires, if applicable.
|
||||
final DateTime? expiresAt;
|
||||
|
||||
/// Flexible metadata JSON blob.
|
||||
final Map<String, dynamic> metadata;
|
||||
|
||||
/// Whether the document has been uploaded.
|
||||
bool get isUploaded => staffDocumentId != null;
|
||||
|
||||
/// Serialises this [ProfileDocument] to a JSON map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'documentId': documentId,
|
||||
'documentType': _documentTypeToString(documentType),
|
||||
'name': name,
|
||||
'staffDocumentId': staffDocumentId,
|
||||
'fileUri': fileUri,
|
||||
'status': _statusToString(status),
|
||||
'expiresAt': expiresAt?.toIso8601String(),
|
||||
'metadata': metadata,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
documentId,
|
||||
documentType,
|
||||
name,
|
||||
staffDocumentId,
|
||||
fileUri,
|
||||
status,
|
||||
expiresAt,
|
||||
metadata,
|
||||
];
|
||||
|
||||
/// Parses a document type string from the API.
|
||||
static ProfileDocumentType _parseDocumentType(String? value) {
|
||||
switch (value?.toUpperCase()) {
|
||||
case 'DOCUMENT':
|
||||
return ProfileDocumentType.document;
|
||||
case 'GOVERNMENT_ID':
|
||||
return ProfileDocumentType.governmentId;
|
||||
case 'ATTIRE':
|
||||
return ProfileDocumentType.attire;
|
||||
case 'TAX_FORM':
|
||||
return ProfileDocumentType.taxForm;
|
||||
default:
|
||||
return ProfileDocumentType.document;
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a status string from the API.
|
||||
static ProfileDocumentStatus _parseStatus(String? value) {
|
||||
switch (value?.toUpperCase()) {
|
||||
case 'NOT_UPLOADED':
|
||||
return ProfileDocumentStatus.notUploaded;
|
||||
case 'PENDING':
|
||||
return ProfileDocumentStatus.pending;
|
||||
case 'VERIFIED':
|
||||
return ProfileDocumentStatus.verified;
|
||||
case 'REJECTED':
|
||||
return ProfileDocumentStatus.rejected;
|
||||
case 'EXPIRED':
|
||||
return ProfileDocumentStatus.expired;
|
||||
default:
|
||||
return ProfileDocumentStatus.notUploaded;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a [ProfileDocumentType] to its API string.
|
||||
static String _documentTypeToString(ProfileDocumentType type) {
|
||||
switch (type) {
|
||||
case ProfileDocumentType.document:
|
||||
return 'DOCUMENT';
|
||||
case ProfileDocumentType.governmentId:
|
||||
return 'GOVERNMENT_ID';
|
||||
case ProfileDocumentType.attire:
|
||||
return 'ATTIRE';
|
||||
case ProfileDocumentType.taxForm:
|
||||
return 'TAX_FORM';
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a [ProfileDocumentStatus] to its API string.
|
||||
static String _statusToString(ProfileDocumentStatus status) {
|
||||
switch (status) {
|
||||
case ProfileDocumentStatus.notUploaded:
|
||||
return 'NOT_UPLOADED';
|
||||
case ProfileDocumentStatus.pending:
|
||||
return 'PENDING';
|
||||
case ProfileDocumentStatus.verified:
|
||||
return 'VERIFIED';
|
||||
case ProfileDocumentStatus.rejected:
|
||||
return 'REJECTED';
|
||||
case ProfileDocumentStatus.expired:
|
||||
return 'EXPIRED';
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user