|
|
|
|
@@ -1,15 +1,16 @@
|
|
|
|
|
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
|
|
|
|
import 'package:krow_data_connect/src/session/staff_session_store.dart';
|
|
|
|
|
import 'package:krow_domain/krow_domain.dart';
|
|
|
|
|
import 'package:intl/intl.dart';
|
|
|
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
|
|
|
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
|
|
|
|
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
|
|
|
|
|
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
|
|
|
|
|
import 'package:krow_core/core.dart';
|
|
|
|
|
import '../../domain/repositories/shifts_repository_interface.dart';
|
|
|
|
|
|
|
|
|
|
class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
class ShiftsRepositoryImpl
|
|
|
|
|
with dc.DataErrorHandler
|
|
|
|
|
implements ShiftsRepositoryInterface {
|
|
|
|
|
final dc.ExampleConnector _dataConnect;
|
|
|
|
|
final FirebaseAuth _auth = FirebaseAuth.instance;
|
|
|
|
|
final firebase_auth.FirebaseAuth _auth = firebase_auth.FirebaseAuth.instance;
|
|
|
|
|
|
|
|
|
|
ShiftsRepositoryImpl() : _dataConnect = dc.ExampleConnector.instance;
|
|
|
|
|
|
|
|
|
|
@@ -22,7 +23,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
|
|
|
|
|
Future<String> _getStaffId() async {
|
|
|
|
|
// 1. Check Session Store
|
|
|
|
|
final StaffSession? session = StaffSessionStore.instance.session;
|
|
|
|
|
final dc.StaffSession? session = dc.StaffSessionStore.instance.session;
|
|
|
|
|
if (session?.staff?.id != null) {
|
|
|
|
|
return session!.staff!.id;
|
|
|
|
|
}
|
|
|
|
|
@@ -31,15 +32,15 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
if (_cachedStaffId != null) return _cachedStaffId!;
|
|
|
|
|
|
|
|
|
|
// 3. Fetch from Data Connect using Firebase UID
|
|
|
|
|
final user = _auth.currentUser;
|
|
|
|
|
final firebase_auth.User? user = _auth.currentUser;
|
|
|
|
|
if (user == null) {
|
|
|
|
|
throw Exception('User is not authenticated');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final response = await _dataConnect
|
|
|
|
|
final fdc.QueryResult<dc.GetStaffByUserIdData, dc.GetStaffByUserIdVariables> response = await executeProtected(() => _dataConnect
|
|
|
|
|
.getStaffByUserId(userId: user.uid)
|
|
|
|
|
.execute();
|
|
|
|
|
.execute());
|
|
|
|
|
if (response.data.staffs.isNotEmpty) {
|
|
|
|
|
_cachedStaffId = response.data.staffs.first.id;
|
|
|
|
|
return _cachedStaffId!;
|
|
|
|
|
@@ -52,10 +53,10 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
return user.uid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DateTime? _toDateTime(dynamic t, {String? debugKey}) {
|
|
|
|
|
DateTime? _toDateTime(dynamic t) {
|
|
|
|
|
if (t == null) return null;
|
|
|
|
|
DateTime? dt;
|
|
|
|
|
if (t is Timestamp) {
|
|
|
|
|
if (t is fdc.Timestamp) {
|
|
|
|
|
dt = t.toDateTime();
|
|
|
|
|
} else if (t is String) {
|
|
|
|
|
dt = DateTime.tryParse(t);
|
|
|
|
|
@@ -73,11 +74,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
|
|
|
|
|
if (dt != null) {
|
|
|
|
|
final local = DateTimeUtils.toDeviceTime(dt);
|
|
|
|
|
if (debugKey != null && debugKey.isNotEmpty) {
|
|
|
|
|
print(
|
|
|
|
|
'ShiftDate convert: key=$debugKey raw=$t parsed=${dt.toIso8601String()} local=${local.toIso8601String()}',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return local;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
@@ -103,135 +100,125 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<List<Shift>> getHistoryShifts() async {
|
|
|
|
|
try {
|
|
|
|
|
final staffId = await _getStaffId();
|
|
|
|
|
final response = await _dataConnect
|
|
|
|
|
.listCompletedApplicationsByStaffId(staffId: staffId)
|
|
|
|
|
.execute();
|
|
|
|
|
final List<Shift> shifts = [];
|
|
|
|
|
final staffId = await _getStaffId();
|
|
|
|
|
final fdc.QueryResult<dc.ListCompletedApplicationsByStaffIdData, dc.ListCompletedApplicationsByStaffIdVariables> response = await executeProtected(() => _dataConnect
|
|
|
|
|
.listCompletedApplicationsByStaffId(staffId: staffId)
|
|
|
|
|
.execute());
|
|
|
|
|
final List<Shift> shifts = [];
|
|
|
|
|
|
|
|
|
|
for (final app in response.data.applications) {
|
|
|
|
|
_shiftToAppIdMap[app.shift.id] = app.id;
|
|
|
|
|
_appToRoleIdMap[app.id] = app.shiftRole.id;
|
|
|
|
|
for (final app in response.data.applications) {
|
|
|
|
|
_shiftToAppIdMap[app.shift.id] = app.id;
|
|
|
|
|
_appToRoleIdMap[app.id] = app.shiftRole.id;
|
|
|
|
|
|
|
|
|
|
final String roleName = app.shiftRole.role.name;
|
|
|
|
|
final String orderName =
|
|
|
|
|
(app.shift.order.eventName ?? '').trim().isNotEmpty
|
|
|
|
|
? app.shift.order.eventName!
|
|
|
|
|
: app.shift.order.business.businessName;
|
|
|
|
|
final String title = '$roleName - $orderName';
|
|
|
|
|
final DateTime? shiftDate = _toDateTime(app.shift.date);
|
|
|
|
|
final DateTime? startDt = _toDateTime(app.shiftRole.startTime);
|
|
|
|
|
final DateTime? endDt = _toDateTime(app.shiftRole.endTime);
|
|
|
|
|
final DateTime? createdDt = _toDateTime(app.createdAt);
|
|
|
|
|
final String roleName = app.shiftRole.role.name;
|
|
|
|
|
final String orderName =
|
|
|
|
|
(app.shift.order.eventName ?? '').trim().isNotEmpty
|
|
|
|
|
? app.shift.order.eventName!
|
|
|
|
|
: app.shift.order.business.businessName;
|
|
|
|
|
final String title = '$roleName - $orderName';
|
|
|
|
|
final DateTime? shiftDate = _toDateTime(app.shift.date);
|
|
|
|
|
final DateTime? startDt = _toDateTime(app.shiftRole.startTime);
|
|
|
|
|
final DateTime? endDt = _toDateTime(app.shiftRole.endTime);
|
|
|
|
|
final DateTime? createdDt = _toDateTime(app.createdAt);
|
|
|
|
|
|
|
|
|
|
shifts.add(
|
|
|
|
|
Shift(
|
|
|
|
|
id: app.shift.id,
|
|
|
|
|
roleId: app.shiftRole.roleId,
|
|
|
|
|
title: title,
|
|
|
|
|
clientName: app.shift.order.business.businessName,
|
|
|
|
|
logoUrl: app.shift.order.business.companyLogoUrl,
|
|
|
|
|
hourlyRate: app.shiftRole.role.costPerHour,
|
|
|
|
|
location: app.shift.location ?? '',
|
|
|
|
|
locationAddress: app.shift.order.teamHub.hubName,
|
|
|
|
|
date: shiftDate?.toIso8601String() ?? '',
|
|
|
|
|
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
|
|
|
|
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
|
|
|
|
createdDate: createdDt?.toIso8601String() ?? '',
|
|
|
|
|
status: _mapStatus(dc.ApplicationStatus.CHECKED_OUT),
|
|
|
|
|
description: app.shift.description,
|
|
|
|
|
durationDays: app.shift.durationDays,
|
|
|
|
|
requiredSlots: app.shiftRole.count,
|
|
|
|
|
filledSlots: app.shiftRole.assigned ?? 0,
|
|
|
|
|
hasApplied: true,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return shifts;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return <Shift>[];
|
|
|
|
|
shifts.add(
|
|
|
|
|
Shift(
|
|
|
|
|
id: app.shift.id,
|
|
|
|
|
roleId: app.shiftRole.roleId,
|
|
|
|
|
title: title,
|
|
|
|
|
clientName: app.shift.order.business.businessName,
|
|
|
|
|
logoUrl: app.shift.order.business.companyLogoUrl,
|
|
|
|
|
hourlyRate: app.shiftRole.role.costPerHour,
|
|
|
|
|
location: app.shift.location ?? '',
|
|
|
|
|
locationAddress: app.shift.order.teamHub.hubName,
|
|
|
|
|
date: shiftDate?.toIso8601String() ?? '',
|
|
|
|
|
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
|
|
|
|
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
|
|
|
|
createdDate: createdDt?.toIso8601String() ?? '',
|
|
|
|
|
status: _mapStatus(dc.ApplicationStatus.CHECKED_OUT),
|
|
|
|
|
description: app.shift.description,
|
|
|
|
|
durationDays: app.shift.durationDays,
|
|
|
|
|
requiredSlots: app.shiftRole.count,
|
|
|
|
|
filledSlots: app.shiftRole.assigned ?? 0,
|
|
|
|
|
hasApplied: true,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return shifts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<List<Shift>> _fetchApplications({
|
|
|
|
|
DateTime? start,
|
|
|
|
|
DateTime? end,
|
|
|
|
|
}) async {
|
|
|
|
|
try {
|
|
|
|
|
final staffId = await _getStaffId();
|
|
|
|
|
var query = _dataConnect.getApplicationsByStaffId(staffId: staffId);
|
|
|
|
|
if (start != null && end != null) {
|
|
|
|
|
query = query
|
|
|
|
|
.dayStart(_toTimestamp(start))
|
|
|
|
|
.dayEnd(_toTimestamp(end));
|
|
|
|
|
}
|
|
|
|
|
final response = await query.execute();
|
|
|
|
|
|
|
|
|
|
final apps = response.data.applications;
|
|
|
|
|
final List<Shift> shifts = [];
|
|
|
|
|
|
|
|
|
|
for (final app in apps) {
|
|
|
|
|
_shiftToAppIdMap[app.shift.id] = app.id;
|
|
|
|
|
_appToRoleIdMap[app.id] = app.shiftRole.id;
|
|
|
|
|
|
|
|
|
|
final String roleName = app.shiftRole.role.name;
|
|
|
|
|
final String orderName =
|
|
|
|
|
(app.shift.order.eventName ?? '').trim().isNotEmpty
|
|
|
|
|
? app.shift.order.eventName!
|
|
|
|
|
: app.shift.order.business.businessName;
|
|
|
|
|
final String title = '$roleName - $orderName';
|
|
|
|
|
final DateTime? shiftDate = _toDateTime(app.shift.date);
|
|
|
|
|
final DateTime? startDt = _toDateTime(app.shiftRole.startTime);
|
|
|
|
|
final DateTime? endDt = _toDateTime(app.shiftRole.endTime);
|
|
|
|
|
final DateTime? createdDt = _toDateTime(app.createdAt);
|
|
|
|
|
|
|
|
|
|
// Override status to reflect the application state (e.g., CHECKED_OUT, ACCEPTED)
|
|
|
|
|
final bool hasCheckIn = app.checkInTime != null;
|
|
|
|
|
final bool hasCheckOut = app.checkOutTime != null;
|
|
|
|
|
dc.ApplicationStatus? appStatus;
|
|
|
|
|
if (app.status is dc.Known<dc.ApplicationStatus>) {
|
|
|
|
|
appStatus = (app.status as dc.Known<dc.ApplicationStatus>).value;
|
|
|
|
|
}
|
|
|
|
|
final String mappedStatus = hasCheckOut
|
|
|
|
|
? 'completed'
|
|
|
|
|
: hasCheckIn
|
|
|
|
|
? 'checked_in'
|
|
|
|
|
: _mapStatus(appStatus ?? dc.ApplicationStatus.ACCEPTED);
|
|
|
|
|
shifts.add(
|
|
|
|
|
Shift(
|
|
|
|
|
id: app.shift.id,
|
|
|
|
|
roleId: app.shiftRole.roleId,
|
|
|
|
|
title: title,
|
|
|
|
|
clientName: app.shift.order.business.businessName,
|
|
|
|
|
logoUrl: app.shift.order.business.companyLogoUrl,
|
|
|
|
|
hourlyRate: app.shiftRole.role.costPerHour,
|
|
|
|
|
location: app.shift.location ?? '',
|
|
|
|
|
locationAddress: app.shift.order.teamHub.hubName,
|
|
|
|
|
date: shiftDate?.toIso8601String() ?? '',
|
|
|
|
|
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
|
|
|
|
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
|
|
|
|
createdDate: createdDt?.toIso8601String() ?? '',
|
|
|
|
|
status: mappedStatus,
|
|
|
|
|
description: app.shift.description,
|
|
|
|
|
durationDays: app.shift.durationDays,
|
|
|
|
|
requiredSlots: app.shiftRole.count,
|
|
|
|
|
filledSlots: app.shiftRole.assigned ?? 0,
|
|
|
|
|
hasApplied: true,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return shifts;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return <Shift>[];
|
|
|
|
|
final staffId = await _getStaffId();
|
|
|
|
|
var query = _dataConnect.getApplicationsByStaffId(staffId: staffId);
|
|
|
|
|
if (start != null && end != null) {
|
|
|
|
|
query = query.dayStart(_toTimestamp(start)).dayEnd(_toTimestamp(end));
|
|
|
|
|
}
|
|
|
|
|
final fdc.QueryResult<dc.GetApplicationsByStaffIdData, dc.GetApplicationsByStaffIdVariables> response = await executeProtected(() => query.execute());
|
|
|
|
|
|
|
|
|
|
final apps = response.data.applications;
|
|
|
|
|
final List<Shift> shifts = [];
|
|
|
|
|
|
|
|
|
|
for (final app in apps) {
|
|
|
|
|
_shiftToAppIdMap[app.shift.id] = app.id;
|
|
|
|
|
_appToRoleIdMap[app.id] = app.shiftRole.id;
|
|
|
|
|
|
|
|
|
|
final String roleName = app.shiftRole.role.name;
|
|
|
|
|
final String orderName =
|
|
|
|
|
(app.shift.order.eventName ?? '').trim().isNotEmpty
|
|
|
|
|
? app.shift.order.eventName!
|
|
|
|
|
: app.shift.order.business.businessName;
|
|
|
|
|
final String title = '$roleName - $orderName';
|
|
|
|
|
final DateTime? shiftDate = _toDateTime(app.shift.date);
|
|
|
|
|
final DateTime? startDt = _toDateTime(app.shiftRole.startTime);
|
|
|
|
|
final DateTime? endDt = _toDateTime(app.shiftRole.endTime);
|
|
|
|
|
final DateTime? createdDt = _toDateTime(app.createdAt);
|
|
|
|
|
|
|
|
|
|
// Override status to reflect the application state (e.g., CHECKED_OUT, ACCEPTED)
|
|
|
|
|
final bool hasCheckIn = app.checkInTime != null;
|
|
|
|
|
final bool hasCheckOut = app.checkOutTime != null;
|
|
|
|
|
dc.ApplicationStatus? appStatus;
|
|
|
|
|
if (app.status is dc.Known<dc.ApplicationStatus>) {
|
|
|
|
|
appStatus = (app.status as dc.Known<dc.ApplicationStatus>).value;
|
|
|
|
|
}
|
|
|
|
|
final String mappedStatus = hasCheckOut
|
|
|
|
|
? 'completed'
|
|
|
|
|
: hasCheckIn
|
|
|
|
|
? 'checked_in'
|
|
|
|
|
: _mapStatus(appStatus ?? dc.ApplicationStatus.ACCEPTED);
|
|
|
|
|
shifts.add(
|
|
|
|
|
Shift(
|
|
|
|
|
id: app.shift.id,
|
|
|
|
|
roleId: app.shiftRole.roleId,
|
|
|
|
|
title: title,
|
|
|
|
|
clientName: app.shift.order.business.businessName,
|
|
|
|
|
logoUrl: app.shift.order.business.companyLogoUrl,
|
|
|
|
|
hourlyRate: app.shiftRole.role.costPerHour,
|
|
|
|
|
location: app.shift.location ?? '',
|
|
|
|
|
locationAddress: app.shift.order.teamHub.hubName,
|
|
|
|
|
date: shiftDate?.toIso8601String() ?? '',
|
|
|
|
|
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
|
|
|
|
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
|
|
|
|
createdDate: createdDt?.toIso8601String() ?? '',
|
|
|
|
|
status: mappedStatus,
|
|
|
|
|
description: app.shift.description,
|
|
|
|
|
durationDays: app.shift.durationDays,
|
|
|
|
|
requiredSlots: app.shiftRole.count,
|
|
|
|
|
filledSlots: app.shiftRole.assigned ?? 0,
|
|
|
|
|
hasApplied: true,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return shifts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Timestamp _toTimestamp(DateTime dateTime) {
|
|
|
|
|
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
|
|
|
|
final DateTime utc = dateTime.toUtc();
|
|
|
|
|
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
|
|
|
|
final int nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000;
|
|
|
|
|
return Timestamp(nanoseconds, seconds);
|
|
|
|
|
return fdc.Timestamp(nanoseconds, seconds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String _mapStatus(dc.ApplicationStatus status) {
|
|
|
|
|
@@ -252,74 +239,60 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<List<Shift>> getAvailableShifts(String query, String type) async {
|
|
|
|
|
try {
|
|
|
|
|
final String? vendorId = dc.StaffSessionStore.instance.session?.ownerId;
|
|
|
|
|
if (vendorId == null || vendorId.isEmpty) {
|
|
|
|
|
return <Shift>[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final result = await _dataConnect
|
|
|
|
|
.listShiftRolesByVendorId(vendorId: vendorId)
|
|
|
|
|
.execute();
|
|
|
|
|
final allShiftRoles = result.data.shiftRoles;
|
|
|
|
|
|
|
|
|
|
final List<Shift> mappedShifts = [];
|
|
|
|
|
for (final sr in allShiftRoles) {
|
|
|
|
|
print(
|
|
|
|
|
'FindShifts raw: shiftId=${sr.shiftId} roleId=${sr.roleId} '
|
|
|
|
|
'start=${sr.startTime?.toJson()} end=${sr.endTime?.toJson()} '
|
|
|
|
|
'shiftDate=${sr.shift.date?.toJson()}',
|
|
|
|
|
);
|
|
|
|
|
final DateTime? shiftDate = _toDateTime(sr.shift.date);
|
|
|
|
|
final startDt = _toDateTime(sr.startTime);
|
|
|
|
|
final endDt = _toDateTime(sr.endTime);
|
|
|
|
|
final createdDt = _toDateTime(sr.createdAt);
|
|
|
|
|
print(
|
|
|
|
|
'FindShifts mapped: shiftId=${sr.shiftId} '
|
|
|
|
|
'origStart=${sr.startTime?.toJson()} '
|
|
|
|
|
'origEnd=${sr.endTime?.toJson()} '
|
|
|
|
|
'mappedStart=${startDt != null ? DateFormat('HH:mm').format(startDt) : ''} '
|
|
|
|
|
'mappedEnd=${endDt != null ? DateFormat('HH:mm').format(endDt) : ''}',
|
|
|
|
|
);
|
|
|
|
|
mappedShifts.add(
|
|
|
|
|
Shift(
|
|
|
|
|
id: sr.shiftId,
|
|
|
|
|
roleId: sr.roleId,
|
|
|
|
|
title: sr.role.name,
|
|
|
|
|
clientName: sr.shift.order.business.businessName,
|
|
|
|
|
logoUrl: null,
|
|
|
|
|
hourlyRate: sr.role.costPerHour,
|
|
|
|
|
location: sr.shift.location ?? '',
|
|
|
|
|
locationAddress: sr.shift.locationAddress ?? '',
|
|
|
|
|
date: shiftDate?.toIso8601String() ?? '',
|
|
|
|
|
startTime: startDt != null
|
|
|
|
|
? DateFormat('HH:mm').format(startDt)
|
|
|
|
|
: '',
|
|
|
|
|
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
|
|
|
|
createdDate: createdDt?.toIso8601String() ?? '',
|
|
|
|
|
status: sr.shift.status?.stringValue.toLowerCase() ?? 'open',
|
|
|
|
|
description: sr.shift.description,
|
|
|
|
|
durationDays: sr.shift.durationDays,
|
|
|
|
|
requiredSlots: sr.count,
|
|
|
|
|
filledSlots: sr.assigned ?? 0,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (query.isNotEmpty) {
|
|
|
|
|
return mappedShifts
|
|
|
|
|
.where(
|
|
|
|
|
(s) =>
|
|
|
|
|
s.title.toLowerCase().contains(query.toLowerCase()) ||
|
|
|
|
|
s.clientName.toLowerCase().contains(query.toLowerCase()),
|
|
|
|
|
)
|
|
|
|
|
.toList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mappedShifts;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
final String? vendorId = dc.StaffSessionStore.instance.session?.ownerId;
|
|
|
|
|
if (vendorId == null || vendorId.isEmpty) {
|
|
|
|
|
return <Shift>[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final fdc.QueryResult<dc.ListShiftRolesByVendorIdData, dc.ListShiftRolesByVendorIdVariables> result = await executeProtected(() => _dataConnect
|
|
|
|
|
.listShiftRolesByVendorId(vendorId: vendorId)
|
|
|
|
|
.execute());
|
|
|
|
|
final allShiftRoles = result.data.shiftRoles;
|
|
|
|
|
|
|
|
|
|
final List<Shift> mappedShifts = [];
|
|
|
|
|
for (final sr in allShiftRoles) {
|
|
|
|
|
|
|
|
|
|
final DateTime? shiftDate = _toDateTime(sr.shift.date);
|
|
|
|
|
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: sr.role.costPerHour,
|
|
|
|
|
location: sr.shift.location ?? '',
|
|
|
|
|
locationAddress: sr.shift.locationAddress ?? '',
|
|
|
|
|
date: shiftDate?.toIso8601String() ?? '',
|
|
|
|
|
startTime: startDt != null
|
|
|
|
|
? DateFormat('HH:mm').format(startDt)
|
|
|
|
|
: '',
|
|
|
|
|
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
|
|
|
|
createdDate: createdDt?.toIso8601String() ?? '',
|
|
|
|
|
status: sr.shift.status?.stringValue.toLowerCase() ?? 'open',
|
|
|
|
|
description: sr.shift.description,
|
|
|
|
|
durationDays: sr.shift.durationDays,
|
|
|
|
|
requiredSlots: sr.count,
|
|
|
|
|
filledSlots: sr.assigned ?? 0,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (query.isNotEmpty) {
|
|
|
|
|
return mappedShifts
|
|
|
|
|
.where(
|
|
|
|
|
(s) =>
|
|
|
|
|
s.title.toLowerCase().contains(query.toLowerCase()) ||
|
|
|
|
|
s.clientName.toLowerCase().contains(query.toLowerCase()),
|
|
|
|
|
)
|
|
|
|
|
.toList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mappedShifts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
@@ -328,108 +301,101 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
if (roleId != null && roleId.isNotEmpty) {
|
|
|
|
|
final roleResult = await executeProtected(() => _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 DateTime? startDt = _toDateTime(sr.startTime);
|
|
|
|
|
final DateTime? endDt = _toDateTime(sr.endTime);
|
|
|
|
|
final DateTime? createdDt = _toDateTime(sr.createdAt);
|
|
|
|
|
|
|
|
|
|
final String? staffId = await _getStaffId();
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
final String staffId = await _getStaffId();
|
|
|
|
|
bool hasApplied = false;
|
|
|
|
|
String status = 'open';
|
|
|
|
|
final apps = await executeProtected(() =>
|
|
|
|
|
_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.location ?? sr.shift.order.teamHub.hubName,
|
|
|
|
|
locationAddress: sr.shift.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;
|
|
|
|
|
|
|
|
|
|
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 ?? '',
|
|
|
|
|
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.location ?? sr.shift.order.teamHub.hubName,
|
|
|
|
|
locationAddress: sr.shift.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,
|
|
|
|
|
status: status,
|
|
|
|
|
description: sr.shift.description,
|
|
|
|
|
durationDays: null,
|
|
|
|
|
requiredSlots: sr.count,
|
|
|
|
|
filledSlots: sr.assigned ?? 0,
|
|
|
|
|
hasApplied: hasApplied,
|
|
|
|
|
totalValue: sr.totalValue,
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final fdc.QueryResult<dc.GetShiftByIdData, dc.GetShiftByIdVariables> result =
|
|
|
|
|
await executeProtected(() => _dataConnect.getShiftById(id: shiftId).execute());
|
|
|
|
|
final s = result.data.shift;
|
|
|
|
|
if (s == null) return null;
|
|
|
|
|
|
|
|
|
|
int? required;
|
|
|
|
|
int? filled;
|
|
|
|
|
try {
|
|
|
|
|
final rolesRes = await executeProtected(() =>
|
|
|
|
|
_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,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
@@ -445,14 +411,15 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
throw Exception('Missing role id.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final roleResult = await _dataConnect
|
|
|
|
|
final roleResult = await executeProtected(() => _dataConnect
|
|
|
|
|
.getShiftRoleById(shiftId: shiftId, roleId: targetRoleId)
|
|
|
|
|
.execute();
|
|
|
|
|
.execute());
|
|
|
|
|
final role = roleResult.data.shiftRole;
|
|
|
|
|
if (role == null) {
|
|
|
|
|
throw Exception('Shift role not found');
|
|
|
|
|
}
|
|
|
|
|
final shiftResult = await _dataConnect.getShiftById(id: shiftId).execute();
|
|
|
|
|
final shiftResult =
|
|
|
|
|
await executeProtected(() => _dataConnect.getShiftById(id: shiftId).execute());
|
|
|
|
|
final shift = shiftResult.data.shift;
|
|
|
|
|
if (shift == null) {
|
|
|
|
|
throw Exception('Shift not found');
|
|
|
|
|
@@ -474,26 +441,23 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
999,
|
|
|
|
|
999,
|
|
|
|
|
);
|
|
|
|
|
print(
|
|
|
|
|
'Staff applyForShift: dayStartUtc=${_toTimestamp(dayStartUtc).toJson()} '
|
|
|
|
|
'dayEndUtc=${_toTimestamp(dayEndUtc).toJson()}',
|
|
|
|
|
);
|
|
|
|
|
final dayApplications = await _dataConnect
|
|
|
|
|
|
|
|
|
|
final dayApplications = await executeProtected(() => _dataConnect
|
|
|
|
|
.vaidateDayStaffApplication(staffId: staffId)
|
|
|
|
|
.dayStart(_toTimestamp(dayStartUtc))
|
|
|
|
|
.dayEnd(_toTimestamp(dayEndUtc))
|
|
|
|
|
.execute();
|
|
|
|
|
.execute());
|
|
|
|
|
if (dayApplications.data.applications.isNotEmpty) {
|
|
|
|
|
throw Exception('The user already has a shift that day.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
final existingApplicationResult = await _dataConnect
|
|
|
|
|
final existingApplicationResult = await executeProtected(() => _dataConnect
|
|
|
|
|
.getApplicationByStaffShiftAndRole(
|
|
|
|
|
staffId: staffId,
|
|
|
|
|
shiftId: shiftId,
|
|
|
|
|
roleId: targetRoleId,
|
|
|
|
|
)
|
|
|
|
|
.execute();
|
|
|
|
|
.execute());
|
|
|
|
|
if (existingApplicationResult.data.applications.isNotEmpty) {
|
|
|
|
|
throw Exception('Application already exists.');
|
|
|
|
|
}
|
|
|
|
|
@@ -508,7 +472,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
bool updatedRole = false;
|
|
|
|
|
bool updatedShift = false;
|
|
|
|
|
try {
|
|
|
|
|
final appResult = await _dataConnect
|
|
|
|
|
final appResult = await executeProtected(() => _dataConnect
|
|
|
|
|
.createApplication(
|
|
|
|
|
shiftId: shiftId,
|
|
|
|
|
staffId: staffId,
|
|
|
|
|
@@ -517,29 +481,36 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
origin: dc.ApplicationOrigin.STAFF,
|
|
|
|
|
)
|
|
|
|
|
// TODO: this should be PENDING so a vendor can accept it.
|
|
|
|
|
.execute();
|
|
|
|
|
.execute());
|
|
|
|
|
appId = appResult.data.application_insert.id;
|
|
|
|
|
|
|
|
|
|
await _dataConnect
|
|
|
|
|
await executeProtected(() => _dataConnect
|
|
|
|
|
.updateShiftRole(shiftId: shiftId, roleId: targetRoleId)
|
|
|
|
|
.assigned(assigned + 1)
|
|
|
|
|
.execute();
|
|
|
|
|
.execute());
|
|
|
|
|
updatedRole = true;
|
|
|
|
|
|
|
|
|
|
await _dataConnect.updateShift(id: shiftId).filled(filled + 1).execute();
|
|
|
|
|
await executeProtected(
|
|
|
|
|
() => _dataConnect.updateShift(id: shiftId).filled(filled + 1).execute());
|
|
|
|
|
updatedShift = true;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
if (updatedShift) {
|
|
|
|
|
await _dataConnect.updateShift(id: shiftId).filled(filled).execute();
|
|
|
|
|
try {
|
|
|
|
|
await _dataConnect.updateShift(id: shiftId).filled(filled).execute();
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
}
|
|
|
|
|
if (updatedRole) {
|
|
|
|
|
await _dataConnect
|
|
|
|
|
.updateShiftRole(shiftId: shiftId, roleId: targetRoleId)
|
|
|
|
|
.assigned(assigned)
|
|
|
|
|
.execute();
|
|
|
|
|
try {
|
|
|
|
|
await _dataConnect
|
|
|
|
|
.updateShiftRole(shiftId: shiftId, roleId: targetRoleId)
|
|
|
|
|
.assigned(assigned)
|
|
|
|
|
.execute();
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
}
|
|
|
|
|
if (appId != null) {
|
|
|
|
|
await _dataConnect.deleteApplication(id: appId).execute();
|
|
|
|
|
try {
|
|
|
|
|
await _dataConnect.deleteApplication(id: appId).execute();
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
}
|
|
|
|
|
rethrow;
|
|
|
|
|
}
|
|
|
|
|
@@ -573,9 +544,8 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
} else {
|
|
|
|
|
// Fallback fetch
|
|
|
|
|
final staffId = await _getStaffId();
|
|
|
|
|
final apps = await _dataConnect
|
|
|
|
|
.getApplicationsByStaffId(staffId: staffId)
|
|
|
|
|
.execute();
|
|
|
|
|
final apps = await executeProtected(() =>
|
|
|
|
|
_dataConnect.getApplicationsByStaffId(staffId: staffId).execute());
|
|
|
|
|
final app = apps.data.applications
|
|
|
|
|
.where((a) => a.shiftId == shiftId)
|
|
|
|
|
.firstOrNull;
|
|
|
|
|
@@ -588,13 +558,12 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
if (appId == null || roleId == null) {
|
|
|
|
|
// If we are rejecting and can't find an application, create one as rejected (declining an available shift)
|
|
|
|
|
if (newStatus == dc.ApplicationStatus.REJECTED) {
|
|
|
|
|
final rolesResult = await _dataConnect
|
|
|
|
|
.listShiftRolesByShiftId(shiftId: shiftId)
|
|
|
|
|
.execute();
|
|
|
|
|
final rolesResult = await executeProtected(() =>
|
|
|
|
|
_dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute());
|
|
|
|
|
if (rolesResult.data.shiftRoles.isNotEmpty) {
|
|
|
|
|
final role = rolesResult.data.shiftRoles.first;
|
|
|
|
|
final staffId = await _getStaffId();
|
|
|
|
|
await _dataConnect
|
|
|
|
|
await executeProtected(() => _dataConnect
|
|
|
|
|
.createApplication(
|
|
|
|
|
shiftId: shiftId,
|
|
|
|
|
staffId: staffId,
|
|
|
|
|
@@ -602,16 +571,16 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|
|
|
|
status: dc.ApplicationStatus.REJECTED,
|
|
|
|
|
origin: dc.ApplicationOrigin.STAFF,
|
|
|
|
|
)
|
|
|
|
|
.execute();
|
|
|
|
|
.execute());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
throw Exception("Application not found for shift $shiftId");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await _dataConnect
|
|
|
|
|
.updateApplicationStatus(id: appId)
|
|
|
|
|
await executeProtected(() => _dataConnect
|
|
|
|
|
.updateApplicationStatus(id: appId!)
|
|
|
|
|
.status(newStatus)
|
|
|
|
|
.execute();
|
|
|
|
|
.execute());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|