This commit is contained in:
José Salazar
2026-02-01 22:39:40 +09:00
parent 3cebb37dfd
commit 6277b9f5e2
19 changed files with 20900 additions and 18729 deletions

View File

@@ -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();
```

View File

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

View File

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

View File

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

View File

@@ -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,
];
}

View File

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

View File

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

View File

@@ -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();
}
}

View File

@@ -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];
}

View File

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

View File

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

View File

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

View File

@@ -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()));

View File

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

View File

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