changes
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
# Basic Usage
|
||||
|
||||
```dart
|
||||
ExampleConnector.instance.createRecentPayment(createRecentPaymentVariables).execute();
|
||||
ExampleConnector.instance.updateRecentPayment(updateRecentPaymentVariables).execute();
|
||||
ExampleConnector.instance.deleteRecentPayment(deleteRecentPaymentVariables).execute();
|
||||
ExampleConnector.instance.CreateStaff(createStaffVariables).execute();
|
||||
ExampleConnector.instance.UpdateStaff(updateStaffVariables).execute();
|
||||
ExampleConnector.instance.DeleteStaff(deleteStaffVariables).execute();
|
||||
ExampleConnector.instance.getStaffDocumentByKey(getStaffDocumentByKeyVariables).execute();
|
||||
ExampleConnector.instance.listStaffDocumentsByStaffId(listStaffDocumentsByStaffIdVariables).execute();
|
||||
ExampleConnector.instance.listStaffDocumentsByDocumentType(listStaffDocumentsByDocumentTypeVariables).execute();
|
||||
ExampleConnector.instance.listStaffDocumentsByStatus(listStaffDocumentsByStatusVariables).execute();
|
||||
ExampleConnector.instance.createHub(createHubVariables).execute();
|
||||
ExampleConnector.instance.updateHub(updateHubVariables).execute();
|
||||
ExampleConnector.instance.deleteHub(deleteHubVariables).execute();
|
||||
ExampleConnector.instance.listInvoices(listInvoicesVariables).execute();
|
||||
ExampleConnector.instance.getInvoiceById(getInvoiceByIdVariables).execute();
|
||||
ExampleConnector.instance.listInvoicesByVendorId(listInvoicesByVendorIdVariables).execute();
|
||||
ExampleConnector.instance.listInvoicesByBusinessId(listInvoicesByBusinessIdVariables).execute();
|
||||
ExampleConnector.instance.listInvoicesByOrderId(listInvoicesByOrderIdVariables).execute();
|
||||
ExampleConnector.instance.listInvoicesByStatus(listInvoicesByStatusVariables).execute();
|
||||
ExampleConnector.instance.filterInvoices(filterInvoicesVariables).execute();
|
||||
|
||||
```
|
||||
|
||||
@@ -23,8 +23,8 @@ Optional fields can be discovered based on classes that have `Optional` object t
|
||||
This is an example of a mutation with an optional field:
|
||||
|
||||
```dart
|
||||
await ExampleConnector.instance.updateAttireOption({ ... })
|
||||
.itemId(...)
|
||||
await ExampleConnector.instance.UpdateUser({ ... })
|
||||
.email(...)
|
||||
.execute();
|
||||
```
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -253,13 +253,15 @@ class GetShiftRoleByIdShiftRoleShiftOrder {
|
||||
final String? notes;
|
||||
final GetShiftRoleByIdShiftRoleShiftOrderBusiness business;
|
||||
final GetShiftRoleByIdShiftRoleShiftOrderVendor? vendor;
|
||||
final GetShiftRoleByIdShiftRoleShiftOrderTeamHub teamHub;
|
||||
GetShiftRoleByIdShiftRoleShiftOrder.fromJson(dynamic json):
|
||||
|
||||
recurringDays = json['recurringDays'] == null ? null : AnyValue.fromJson(json['recurringDays']),
|
||||
permanentDays = json['permanentDays'] == null ? null : AnyValue.fromJson(json['permanentDays']),
|
||||
notes = json['notes'] == null ? null : nativeFromJson<String>(json['notes']),
|
||||
business = GetShiftRoleByIdShiftRoleShiftOrderBusiness.fromJson(json['business']),
|
||||
vendor = json['vendor'] == null ? null : GetShiftRoleByIdShiftRoleShiftOrderVendor.fromJson(json['vendor']);
|
||||
vendor = json['vendor'] == null ? null : GetShiftRoleByIdShiftRoleShiftOrderVendor.fromJson(json['vendor']),
|
||||
teamHub = GetShiftRoleByIdShiftRoleShiftOrderTeamHub.fromJson(json['teamHub']);
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if(identical(this, other)) {
|
||||
@@ -274,11 +276,12 @@ class GetShiftRoleByIdShiftRoleShiftOrder {
|
||||
permanentDays == otherTyped.permanentDays &&
|
||||
notes == otherTyped.notes &&
|
||||
business == otherTyped.business &&
|
||||
vendor == otherTyped.vendor;
|
||||
vendor == otherTyped.vendor &&
|
||||
teamHub == otherTyped.teamHub;
|
||||
|
||||
}
|
||||
@override
|
||||
int get hashCode => Object.hashAll([recurringDays.hashCode, permanentDays.hashCode, notes.hashCode, business.hashCode, vendor.hashCode]);
|
||||
int get hashCode => Object.hashAll([recurringDays.hashCode, permanentDays.hashCode, notes.hashCode, business.hashCode, vendor.hashCode, teamHub.hashCode]);
|
||||
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@@ -296,6 +299,7 @@ class GetShiftRoleByIdShiftRoleShiftOrder {
|
||||
if (vendor != null) {
|
||||
json['vendor'] = vendor!.toJson();
|
||||
}
|
||||
json['teamHub'] = teamHub.toJson();
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -305,6 +309,7 @@ class GetShiftRoleByIdShiftRoleShiftOrder {
|
||||
this.notes,
|
||||
required this.business,
|
||||
this.vendor,
|
||||
required this.teamHub,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -312,10 +317,12 @@ class GetShiftRoleByIdShiftRoleShiftOrder {
|
||||
class GetShiftRoleByIdShiftRoleShiftOrderBusiness {
|
||||
final String id;
|
||||
final String businessName;
|
||||
final String? companyLogoUrl;
|
||||
GetShiftRoleByIdShiftRoleShiftOrderBusiness.fromJson(dynamic json):
|
||||
|
||||
id = nativeFromJson<String>(json['id']),
|
||||
businessName = nativeFromJson<String>(json['businessName']);
|
||||
businessName = nativeFromJson<String>(json['businessName']),
|
||||
companyLogoUrl = json['companyLogoUrl'] == null ? null : nativeFromJson<String>(json['companyLogoUrl']);
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if(identical(this, other)) {
|
||||
@@ -327,23 +334,28 @@ class GetShiftRoleByIdShiftRoleShiftOrderBusiness {
|
||||
|
||||
final GetShiftRoleByIdShiftRoleShiftOrderBusiness otherTyped = other as GetShiftRoleByIdShiftRoleShiftOrderBusiness;
|
||||
return id == otherTyped.id &&
|
||||
businessName == otherTyped.businessName;
|
||||
businessName == otherTyped.businessName &&
|
||||
companyLogoUrl == otherTyped.companyLogoUrl;
|
||||
|
||||
}
|
||||
@override
|
||||
int get hashCode => Object.hashAll([id.hashCode, businessName.hashCode]);
|
||||
int get hashCode => Object.hashAll([id.hashCode, businessName.hashCode, companyLogoUrl.hashCode]);
|
||||
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
Map<String, dynamic> json = {};
|
||||
json['id'] = nativeToJson<String>(id);
|
||||
json['businessName'] = nativeToJson<String>(businessName);
|
||||
if (companyLogoUrl != null) {
|
||||
json['companyLogoUrl'] = nativeToJson<String?>(companyLogoUrl);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
GetShiftRoleByIdShiftRoleShiftOrderBusiness({
|
||||
required this.id,
|
||||
required this.businessName,
|
||||
this.companyLogoUrl,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -386,6 +398,40 @@ class GetShiftRoleByIdShiftRoleShiftOrderVendor {
|
||||
});
|
||||
}
|
||||
|
||||
@immutable
|
||||
class GetShiftRoleByIdShiftRoleShiftOrderTeamHub {
|
||||
final String hubName;
|
||||
GetShiftRoleByIdShiftRoleShiftOrderTeamHub.fromJson(dynamic json):
|
||||
|
||||
hubName = nativeFromJson<String>(json['hubName']);
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if(identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if(other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final GetShiftRoleByIdShiftRoleShiftOrderTeamHub otherTyped = other as GetShiftRoleByIdShiftRoleShiftOrderTeamHub;
|
||||
return hubName == otherTyped.hubName;
|
||||
|
||||
}
|
||||
@override
|
||||
int get hashCode => hubName.hashCode;
|
||||
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
Map<String, dynamic> json = {};
|
||||
json['hubName'] = nativeToJson<String>(hubName);
|
||||
return json;
|
||||
}
|
||||
|
||||
GetShiftRoleByIdShiftRoleShiftOrderTeamHub({
|
||||
required this.hubName,
|
||||
});
|
||||
}
|
||||
|
||||
@immutable
|
||||
class GetShiftRoleByIdData {
|
||||
final GetShiftRoleByIdShiftRole? shiftRole;
|
||||
|
||||
@@ -204,6 +204,8 @@ class ListShiftRolesByVendorIdShiftRolesShift {
|
||||
final String? locationAddress;
|
||||
final String? description;
|
||||
final String orderId;
|
||||
final EnumValue<ShiftStatus>? status;
|
||||
final int? durationDays;
|
||||
final ListShiftRolesByVendorIdShiftRolesShiftOrder order;
|
||||
ListShiftRolesByVendorIdShiftRolesShift.fromJson(dynamic json):
|
||||
|
||||
@@ -214,6 +216,8 @@ class ListShiftRolesByVendorIdShiftRolesShift {
|
||||
locationAddress = json['locationAddress'] == null ? null : nativeFromJson<String>(json['locationAddress']),
|
||||
description = json['description'] == null ? null : nativeFromJson<String>(json['description']),
|
||||
orderId = nativeFromJson<String>(json['orderId']),
|
||||
status = json['status'] == null ? null : shiftStatusDeserializer(json['status']),
|
||||
durationDays = json['durationDays'] == null ? null : nativeFromJson<int>(json['durationDays']),
|
||||
order = ListShiftRolesByVendorIdShiftRolesShiftOrder.fromJson(json['order']);
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
@@ -232,11 +236,13 @@ class ListShiftRolesByVendorIdShiftRolesShift {
|
||||
locationAddress == otherTyped.locationAddress &&
|
||||
description == otherTyped.description &&
|
||||
orderId == otherTyped.orderId &&
|
||||
status == otherTyped.status &&
|
||||
durationDays == otherTyped.durationDays &&
|
||||
order == otherTyped.order;
|
||||
|
||||
}
|
||||
@override
|
||||
int get hashCode => Object.hashAll([id.hashCode, title.hashCode, date.hashCode, location.hashCode, locationAddress.hashCode, description.hashCode, orderId.hashCode, order.hashCode]);
|
||||
int get hashCode => Object.hashAll([id.hashCode, title.hashCode, date.hashCode, location.hashCode, locationAddress.hashCode, description.hashCode, orderId.hashCode, status.hashCode, durationDays.hashCode, order.hashCode]);
|
||||
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@@ -256,6 +262,14 @@ class ListShiftRolesByVendorIdShiftRolesShift {
|
||||
json['description'] = nativeToJson<String?>(description);
|
||||
}
|
||||
json['orderId'] = nativeToJson<String>(orderId);
|
||||
if (status != null) {
|
||||
json['status'] =
|
||||
shiftStatusSerializer(status!)
|
||||
;
|
||||
}
|
||||
if (durationDays != null) {
|
||||
json['durationDays'] = nativeToJson<int?>(durationDays);
|
||||
}
|
||||
json['order'] = order.toJson();
|
||||
return json;
|
||||
}
|
||||
@@ -268,6 +282,8 @@ class ListShiftRolesByVendorIdShiftRolesShift {
|
||||
this.locationAddress,
|
||||
this.description,
|
||||
required this.orderId,
|
||||
this.status,
|
||||
this.durationDays,
|
||||
required this.order,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ import 'package:krow_domain/krow_domain.dart' as domain;
|
||||
class StaffSession {
|
||||
final domain.User user;
|
||||
final domain.Staff? staff;
|
||||
final String? ownerId;
|
||||
|
||||
const StaffSession({
|
||||
required this.user,
|
||||
this.staff,
|
||||
this.ownerId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@ class Shift extends Equatable {
|
||||
final int? durationDays; // For multi-day shifts
|
||||
final int? requiredSlots;
|
||||
final int? filledSlots;
|
||||
final String? roleId;
|
||||
final bool? hasApplied;
|
||||
final double? totalValue;
|
||||
|
||||
const Shift({
|
||||
required this.id,
|
||||
@@ -53,6 +56,9 @@ class Shift extends Equatable {
|
||||
this.durationDays,
|
||||
this.requiredSlots,
|
||||
this.filledSlots,
|
||||
this.roleId,
|
||||
this.hasApplied,
|
||||
this.totalValue,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -82,6 +88,9 @@ class Shift extends Equatable {
|
||||
durationDays,
|
||||
requiredSlots,
|
||||
filledSlots,
|
||||
roleId,
|
||||
hasApplied,
|
||||
totalValue,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,11 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
avatar: staffRecord.photoUrl,
|
||||
);
|
||||
StaffSessionStore.instance.setSession(
|
||||
StaffSession(user: domainUser, staff: domainStaff),
|
||||
StaffSession(
|
||||
user: domainUser,
|
||||
staff: domainStaff,
|
||||
ownerId: staffRecord?.ownerId,
|
||||
),
|
||||
);
|
||||
return domainUser;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class ProfileSetupRepositoryImpl implements ProfileSetupRepository {
|
||||
|
||||
if (session != null) {
|
||||
StaffSessionStore.instance.setSession(
|
||||
StaffSession(user: session.user, staff: staff),
|
||||
StaffSession(user: session.user, staff: staff, ownerId: session.ownerId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,120 +156,254 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
||||
|
||||
@override
|
||||
Future<List<Shift>> getAvailableShifts(String query, String type) async {
|
||||
try {
|
||||
final result = await _dataConnect.listShifts().execute();
|
||||
final allShifts = result.data.shifts;
|
||||
|
||||
final List<Shift> mappedShifts = [];
|
||||
|
||||
for (final s in allShifts) {
|
||||
// For each shift, map to Domain Shift
|
||||
// Note: date fields in generated code might be specific types
|
||||
final startDt = _toDateTime(s.startTime);
|
||||
final endDt = _toDateTime(s.endTime);
|
||||
final createdDt = _toDateTime(s.createdAt);
|
||||
|
||||
mappedShifts.add(Shift(
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
clientName: s.order.business.businessName,
|
||||
logoUrl: null,
|
||||
hourlyRate: s.cost ?? 0.0,
|
||||
location: s.location ?? '',
|
||||
locationAddress: s.locationAddress ?? '',
|
||||
date: startDt?.toIso8601String() ?? '',
|
||||
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
||||
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
||||
createdDate: createdDt?.toIso8601String() ?? '',
|
||||
status: s.status?.stringValue.toLowerCase() ?? 'open',
|
||||
description: s.description,
|
||||
durationDays: s.durationDays,
|
||||
requiredSlots: null, // Basic list doesn't fetch detailed role stats yet
|
||||
filledSlots: null,
|
||||
));
|
||||
}
|
||||
|
||||
if (query.isNotEmpty) {
|
||||
return mappedShifts.where((s) =>
|
||||
s.title.toLowerCase().contains(query.toLowerCase()) ||
|
||||
s.clientName.toLowerCase().contains(query.toLowerCase())
|
||||
).toList();
|
||||
}
|
||||
|
||||
return mappedShifts;
|
||||
|
||||
} catch (e) {
|
||||
return <Shift>[];
|
||||
try {
|
||||
final String? vendorId =
|
||||
dc.StaffSessionStore.instance.session?.ownerId;
|
||||
if (vendorId == null || vendorId.isEmpty) {
|
||||
return <Shift>[];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Shift?> getShiftDetails(String shiftId) async {
|
||||
return _getShiftDetails(shiftId);
|
||||
}
|
||||
|
||||
Future<Shift?> _getShiftDetails(String shiftId) async {
|
||||
try {
|
||||
final result = await _dataConnect.getShiftById(id: shiftId).execute();
|
||||
final s = result.data.shift;
|
||||
if (s == null) return null;
|
||||
|
||||
int? required;
|
||||
int? filled;
|
||||
try {
|
||||
final rolesRes = await _dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute();
|
||||
if (rolesRes.data.shiftRoles.isNotEmpty) {
|
||||
required = 0;
|
||||
filled = 0;
|
||||
for(var r in rolesRes.data.shiftRoles) {
|
||||
required = (required ?? 0) + r.count;
|
||||
filled = (filled ?? 0) + (r.assigned ?? 0);
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
final result = await _dataConnect
|
||||
.listShiftRolesByVendorId(vendorId: vendorId)
|
||||
.execute();
|
||||
final allShiftRoles = result.data.shiftRoles;
|
||||
|
||||
final startDt = _toDateTime(s.startTime);
|
||||
final endDt = _toDateTime(s.endTime);
|
||||
final createdDt = _toDateTime(s.createdAt);
|
||||
|
||||
return Shift(
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
clientName: s.order.business.businessName,
|
||||
final List<Shift> mappedShifts = [];
|
||||
for (final sr in allShiftRoles) {
|
||||
final startDt = _toDateTime(sr.startTime);
|
||||
final endDt = _toDateTime(sr.endTime);
|
||||
final createdDt = _toDateTime(sr.createdAt);
|
||||
mappedShifts.add(
|
||||
Shift(
|
||||
id: sr.shiftId,
|
||||
roleId: sr.roleId,
|
||||
title: sr.role.name,
|
||||
clientName: sr.shift.order.business.businessName,
|
||||
logoUrl: null,
|
||||
hourlyRate: s.cost ?? 0.0,
|
||||
location: s.location ?? '',
|
||||
locationAddress: s.locationAddress ?? '',
|
||||
hourlyRate: sr.role.costPerHour,
|
||||
location: sr.shift.location ?? '',
|
||||
locationAddress: sr.shift.locationAddress ?? '',
|
||||
date: startDt?.toIso8601String() ?? '',
|
||||
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
||||
startTime:
|
||||
startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
||||
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
||||
createdDate: createdDt?.toIso8601String() ?? '',
|
||||
status: s.status?.stringValue ?? 'OPEN',
|
||||
description: s.description,
|
||||
durationDays: s.durationDays,
|
||||
requiredSlots: required,
|
||||
filledSlots: filled,
|
||||
status: sr.shift.status?.stringValue.toLowerCase() ?? 'open',
|
||||
description: sr.shift.description,
|
||||
durationDays: sr.shift.durationDays,
|
||||
requiredSlots: sr.count,
|
||||
filledSlots: sr.assigned ?? 0,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (query.isNotEmpty) {
|
||||
return mappedShifts
|
||||
.where(
|
||||
(s) =>
|
||||
s.title.toLowerCase().contains(query.toLowerCase()) ||
|
||||
s.clientName.toLowerCase().contains(query.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
return mappedShifts;
|
||||
} catch (e) {
|
||||
return <Shift>[];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> applyForShift(String shiftId, {bool isInstantBook = false}) async {
|
||||
final rolesResult = await _dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute();
|
||||
if (rolesResult.data.shiftRoles.isEmpty) throw Exception('No open roles for this shift');
|
||||
Future<Shift?> getShiftDetails(String shiftId, {String? roleId}) async {
|
||||
return _getShiftDetails(shiftId, roleId: roleId);
|
||||
}
|
||||
|
||||
Future<Shift?> _getShiftDetails(String shiftId, {String? roleId}) async {
|
||||
try {
|
||||
if (roleId != null && roleId.isNotEmpty) {
|
||||
final roleResult = await _dataConnect
|
||||
.getShiftRoleById(shiftId: shiftId, roleId: roleId)
|
||||
.execute();
|
||||
final sr = roleResult.data.shiftRole;
|
||||
if (sr == null) return null;
|
||||
|
||||
final DateTime? startDt = _toDateTime(sr.startTime);
|
||||
final DateTime? endDt = _toDateTime(sr.endTime);
|
||||
final DateTime? createdDt = _toDateTime(sr.createdAt);
|
||||
|
||||
final String? staffId = _auth.currentUser?.uid;
|
||||
bool hasApplied = false;
|
||||
String status = 'open';
|
||||
if (staffId != null) {
|
||||
final apps =
|
||||
await _dataConnect.getApplicationsByStaffId(staffId: staffId).execute();
|
||||
final app = apps.data.applications
|
||||
.where(
|
||||
(a) =>
|
||||
a.shiftId == shiftId && a.shiftRole.roleId == roleId,
|
||||
)
|
||||
.firstOrNull;
|
||||
if (app != null) {
|
||||
hasApplied = true;
|
||||
if (app.status is dc.Known<dc.ApplicationStatus>) {
|
||||
final dc.ApplicationStatus s =
|
||||
(app.status as dc.Known<dc.ApplicationStatus>).value;
|
||||
status = _mapStatus(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Shift(
|
||||
id: sr.shiftId,
|
||||
roleId: sr.roleId,
|
||||
title: sr.shift.order.business.businessName,
|
||||
clientName: sr.shift.order.business.businessName,
|
||||
logoUrl: sr.shift.order.business.companyLogoUrl,
|
||||
hourlyRate: sr.role.costPerHour,
|
||||
location: sr.shift.order.teamHub.hubName,
|
||||
locationAddress: '',
|
||||
date: startDt?.toIso8601String() ?? '',
|
||||
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
||||
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
||||
createdDate: createdDt?.toIso8601String() ?? '',
|
||||
status: status,
|
||||
description: sr.shift.description,
|
||||
durationDays: null,
|
||||
requiredSlots: sr.count,
|
||||
filledSlots: sr.assigned ?? 0,
|
||||
hasApplied: hasApplied,
|
||||
totalValue: sr.totalValue,
|
||||
);
|
||||
}
|
||||
|
||||
final result = await _dataConnect.getShiftById(id: shiftId).execute();
|
||||
final s = result.data.shift;
|
||||
if (s == null) return null;
|
||||
|
||||
final role = rolesResult.data.shiftRoles.first;
|
||||
|
||||
final staffId = await _getStaffId();
|
||||
await _dataConnect.createApplication(
|
||||
shiftId: shiftId,
|
||||
staffId: staffId,
|
||||
roleId: role.roleId,
|
||||
status: isInstantBook ? dc.ApplicationStatus.ACCEPTED : dc.ApplicationStatus.PENDING,
|
||||
origin: dc.ApplicationOrigin.STAFF,
|
||||
).execute();
|
||||
int? required;
|
||||
int? filled;
|
||||
try {
|
||||
final rolesRes = await _dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute();
|
||||
if (rolesRes.data.shiftRoles.isNotEmpty) {
|
||||
required = 0;
|
||||
filled = 0;
|
||||
for(var r in rolesRes.data.shiftRoles) {
|
||||
required = (required ?? 0) + r.count;
|
||||
filled = (filled ?? 0) + (r.assigned ?? 0);
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
final startDt = _toDateTime(s.startTime);
|
||||
final endDt = _toDateTime(s.endTime);
|
||||
final createdDt = _toDateTime(s.createdAt);
|
||||
|
||||
return Shift(
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
clientName: s.order.business.businessName,
|
||||
logoUrl: null,
|
||||
hourlyRate: s.cost ?? 0.0,
|
||||
location: s.location ?? '',
|
||||
locationAddress: s.locationAddress ?? '',
|
||||
date: startDt?.toIso8601String() ?? '',
|
||||
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
||||
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
||||
createdDate: createdDt?.toIso8601String() ?? '',
|
||||
status: s.status?.stringValue ?? 'OPEN',
|
||||
description: s.description,
|
||||
durationDays: s.durationDays,
|
||||
requiredSlots: required,
|
||||
filledSlots: filled,
|
||||
);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> applyForShift(
|
||||
String shiftId, {
|
||||
bool isInstantBook = false,
|
||||
String? roleId,
|
||||
}) async {
|
||||
final staffId = await _getStaffId();
|
||||
|
||||
String targetRoleId = roleId ?? '';
|
||||
if (targetRoleId.isEmpty) {
|
||||
final rolesResult =
|
||||
await _dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute();
|
||||
if (rolesResult.data.shiftRoles.isEmpty) {
|
||||
throw Exception('No open roles for this shift');
|
||||
}
|
||||
final sr = rolesResult.data.shiftRoles.firstWhere(
|
||||
(r) => (r.assigned ?? 0) < r.count,
|
||||
orElse: () => rolesResult.data.shiftRoles.first,
|
||||
);
|
||||
targetRoleId = sr.roleId;
|
||||
}
|
||||
|
||||
final roleResult = await _dataConnect
|
||||
.getShiftRoleById(shiftId: shiftId, roleId: targetRoleId)
|
||||
.execute();
|
||||
final role = roleResult.data.shiftRole;
|
||||
if (role == null) {
|
||||
throw Exception('Shift role not found');
|
||||
}
|
||||
final int assigned = role.assigned ?? 0;
|
||||
if (assigned >= role.count) {
|
||||
throw Exception('This shift is full.');
|
||||
}
|
||||
|
||||
final shiftResult = await _dataConnect.getShiftById(id: shiftId).execute();
|
||||
final shift = shiftResult.data.shift;
|
||||
if (shift == null) {
|
||||
throw Exception('Shift not found');
|
||||
}
|
||||
final int filled = shift.filled ?? 0;
|
||||
|
||||
String? appId;
|
||||
bool updatedRole = false;
|
||||
bool updatedShift = false;
|
||||
try {
|
||||
final appResult = await _dataConnect
|
||||
.createApplication(
|
||||
shiftId: shiftId,
|
||||
staffId: staffId,
|
||||
roleId: targetRoleId,
|
||||
status: dc.ApplicationStatus.ACCEPTED,
|
||||
origin: dc.ApplicationOrigin.STAFF,
|
||||
)
|
||||
// TODO: this should be PENDING so a vendor can accept it.
|
||||
.execute();
|
||||
appId = appResult.data.application_insert.id;
|
||||
|
||||
await _dataConnect
|
||||
.updateShiftRole(shiftId: shiftId, roleId: targetRoleId)
|
||||
.assigned(assigned + 1)
|
||||
.execute();
|
||||
updatedRole = true;
|
||||
|
||||
await _dataConnect
|
||||
.updateShift(id: shiftId)
|
||||
.filled(filled + 1)
|
||||
.execute();
|
||||
updatedShift = true;
|
||||
} catch (e) {
|
||||
if (updatedShift) {
|
||||
await _dataConnect.updateShift(id: shiftId).filled(filled).execute();
|
||||
}
|
||||
if (updatedRole) {
|
||||
await _dataConnect
|
||||
.updateShiftRole(shiftId: shiftId, roleId: targetRoleId)
|
||||
.assigned(assigned)
|
||||
.execute();
|
||||
}
|
||||
if (appId != null) {
|
||||
await _dataConnect.deleteApplication(id: appId).execute();
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -333,4 +467,3 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class GetShiftDetailsArguments extends Equatable {
|
||||
final String shiftId;
|
||||
final String? roleId;
|
||||
|
||||
const GetShiftDetailsArguments({
|
||||
required this.shiftId,
|
||||
this.roleId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [shiftId, roleId];
|
||||
}
|
||||
@@ -15,12 +15,16 @@ abstract interface class ShiftsRepositoryInterface {
|
||||
Future<List<Shift>> getPendingAssignments();
|
||||
|
||||
/// Retrieves detailed information for a specific shift by [shiftId].
|
||||
Future<Shift?> getShiftDetails(String shiftId);
|
||||
Future<Shift?> getShiftDetails(String shiftId, {String? roleId});
|
||||
|
||||
/// Applies for a specific open shift.
|
||||
///
|
||||
/// [isInstantBook] determines if the application should be immediately accepted.
|
||||
Future<void> applyForShift(String shiftId, {bool isInstantBook = false});
|
||||
Future<void> applyForShift(
|
||||
String shiftId, {
|
||||
bool isInstantBook = false,
|
||||
String? roleId,
|
||||
});
|
||||
|
||||
/// Accepts a pending shift assignment.
|
||||
Future<void> acceptShift(String shiftId);
|
||||
|
||||
@@ -5,7 +5,15 @@ class ApplyForShiftUseCase {
|
||||
|
||||
ApplyForShiftUseCase(this.repository);
|
||||
|
||||
Future<void> call(String shiftId, {bool isInstantBook = false}) async {
|
||||
return repository.applyForShift(shiftId, isInstantBook: isInstantBook);
|
||||
Future<void> call(
|
||||
String shiftId, {
|
||||
bool isInstantBook = false,
|
||||
String? roleId,
|
||||
}) async {
|
||||
return repository.applyForShift(
|
||||
shiftId,
|
||||
isInstantBook: isInstantBook,
|
||||
roleId: roleId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../arguments/get_shift_details_arguments.dart';
|
||||
import '../repositories/shifts_repository_interface.dart';
|
||||
|
||||
class GetShiftDetailsUseCase extends UseCase<String, Shift?> {
|
||||
class GetShiftDetailsUseCase extends UseCase<GetShiftDetailsArguments, Shift?> {
|
||||
final ShiftsRepositoryInterface repository;
|
||||
|
||||
GetShiftDetailsUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Shift?> call(String params) {
|
||||
return repository.getShiftDetails(params);
|
||||
Future<Shift?> call(GetShiftDetailsArguments params) {
|
||||
return repository.getShiftDetails(
|
||||
params.shiftId,
|
||||
roleId: params.roleId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:bloc/bloc.dart';
|
||||
import '../../../domain/usecases/apply_for_shift_usecase.dart';
|
||||
import '../../../domain/usecases/decline_shift_usecase.dart';
|
||||
import '../../../domain/usecases/get_shift_details_usecase.dart';
|
||||
import '../../../domain/arguments/get_shift_details_arguments.dart';
|
||||
import 'shift_details_event.dart';
|
||||
import 'shift_details_state.dart';
|
||||
|
||||
@@ -26,7 +27,12 @@ class ShiftDetailsBloc extends Bloc<ShiftDetailsEvent, ShiftDetailsState> {
|
||||
) async {
|
||||
emit(ShiftDetailsLoading());
|
||||
try {
|
||||
final shift = await getShiftDetails(event.shiftId);
|
||||
final shift = await getShiftDetails(
|
||||
GetShiftDetailsArguments(
|
||||
shiftId: event.shiftId,
|
||||
roleId: event.roleId,
|
||||
),
|
||||
);
|
||||
if (shift != null) {
|
||||
emit(ShiftDetailsLoaded(shift));
|
||||
} else {
|
||||
@@ -42,7 +48,11 @@ class ShiftDetailsBloc extends Bloc<ShiftDetailsEvent, ShiftDetailsState> {
|
||||
Emitter<ShiftDetailsState> emit,
|
||||
) async {
|
||||
try {
|
||||
await applyForShift(event.shiftId, isInstantBook: true);
|
||||
await applyForShift(
|
||||
event.shiftId,
|
||||
isInstantBook: true,
|
||||
roleId: event.roleId,
|
||||
);
|
||||
emit(const ShiftActionSuccess("Shift successfully booked!"));
|
||||
} catch (e) {
|
||||
emit(ShiftDetailsError(e.toString()));
|
||||
|
||||
@@ -9,18 +9,20 @@ abstract class ShiftDetailsEvent extends Equatable {
|
||||
|
||||
class LoadShiftDetailsEvent extends ShiftDetailsEvent {
|
||||
final String shiftId;
|
||||
const LoadShiftDetailsEvent(this.shiftId);
|
||||
final String? roleId;
|
||||
const LoadShiftDetailsEvent(this.shiftId, {this.roleId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [shiftId];
|
||||
List<Object?> get props => [shiftId, roleId];
|
||||
}
|
||||
|
||||
class BookShiftDetailsEvent extends ShiftDetailsEvent {
|
||||
final String shiftId;
|
||||
const BookShiftDetailsEvent(this.shiftId);
|
||||
final String? roleId;
|
||||
const BookShiftDetailsEvent(this.shiftId, {this.roleId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [shiftId];
|
||||
List<Object?> get props => [shiftId, roleId];
|
||||
}
|
||||
|
||||
class DeclineShiftDetailsEvent extends ShiftDetailsEvent {
|
||||
|
||||
@@ -122,7 +122,12 @@ class ShiftDetailsPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<ShiftDetailsBloc>(
|
||||
create: (_) =>
|
||||
Modular.get<ShiftDetailsBloc>()..add(LoadShiftDetailsEvent(shiftId)),
|
||||
Modular.get<ShiftDetailsBloc>()..add(
|
||||
LoadShiftDetailsEvent(
|
||||
shiftId,
|
||||
roleId: shift?.roleId,
|
||||
),
|
||||
),
|
||||
child: BlocListener<ShiftDetailsBloc, ShiftDetailsState>(
|
||||
listener: (context, state) {
|
||||
if (state is ShiftActionSuccess) {
|
||||
@@ -164,7 +169,8 @@ class ShiftDetailsPage extends StatelessWidget {
|
||||
}
|
||||
|
||||
final duration = _calculateDuration(displayShift);
|
||||
final estimatedTotal = (displayShift.hourlyRate) * duration;
|
||||
final estimatedTotal =
|
||||
displayShift.totalValue ?? (displayShift.hourlyRate * duration);
|
||||
final openSlots =
|
||||
(displayShift.requiredSlots ?? 0) -
|
||||
(displayShift.filledSlots ?? 0);
|
||||
@@ -457,20 +463,28 @@ class ShiftDetailsPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () =>
|
||||
_bookShift(context, displayShift!.id),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF10B981),
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
if ((displayShift!.hasApplied != true) &&
|
||||
(displayShift!.requiredSlots == null ||
|
||||
displayShift!.filledSlots == null ||
|
||||
displayShift!.filledSlots! <
|
||||
displayShift!.requiredSlots!))
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _bookShift(
|
||||
context,
|
||||
displayShift!.id,
|
||||
displayShift!.roleId,
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF10B981),
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
),
|
||||
),
|
||||
child: const Text("Book Shift"),
|
||||
),
|
||||
child: const Text("Book Shift"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
@@ -489,7 +503,7 @@ class ShiftDetailsPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _bookShift(BuildContext context, String id) {
|
||||
void _bookShift(BuildContext context, String id, String? roleId) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
@@ -505,7 +519,7 @@ class ShiftDetailsPage extends StatelessWidget {
|
||||
Navigator.of(ctx).pop();
|
||||
BlocProvider.of<ShiftDetailsBloc>(
|
||||
context,
|
||||
).add(BookShiftDetailsEvent(id));
|
||||
).add(BookShiftDetailsEvent(id, roleId: roleId));
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: const Color(0xFF10B981),
|
||||
|
||||
Reference in New Issue
Block a user