coverage working

This commit is contained in:
José Salazar
2026-01-27 11:27:14 -05:00
parent 3dc2e90962
commit 0902c0ecf0
6 changed files with 19845 additions and 19282 deletions

View File

@@ -1,16 +1,16 @@
# Basic Usage
```dart
ExampleConnector.instance.createBusiness(createBusinessVariables).execute();
ExampleConnector.instance.updateBusiness(updateBusinessVariables).execute();
ExampleConnector.instance.deleteBusiness(deleteBusinessVariables).execute();
ExampleConnector.instance.listCustomRateCards().execute();
ExampleConnector.instance.getCustomRateCardById(getCustomRateCardByIdVariables).execute();
ExampleConnector.instance.listClientFeedbacks(listClientFeedbacksVariables).execute();
ExampleConnector.instance.getClientFeedbackById(getClientFeedbackByIdVariables).execute();
ExampleConnector.instance.listClientFeedbacksByBusinessId(listClientFeedbacksByBusinessIdVariables).execute();
ExampleConnector.instance.listClientFeedbacksByVendorId(listClientFeedbacksByVendorIdVariables).execute();
ExampleConnector.instance.listClientFeedbacksByBusinessAndVendor(listClientFeedbacksByBusinessAndVendorVariables).execute();
ExampleConnector.instance.createStaffRole(createStaffRoleVariables).execute();
ExampleConnector.instance.deleteStaffRole(deleteStaffRoleVariables).execute();
ExampleConnector.instance.createTeamMember(createTeamMemberVariables).execute();
ExampleConnector.instance.updateTeamMember(updateTeamMemberVariables).execute();
ExampleConnector.instance.updateTeamMemberInviteStatus(updateTeamMemberInviteStatusVariables).execute();
ExampleConnector.instance.acceptInviteByCode(acceptInviteByCodeVariables).execute();
ExampleConnector.instance.cancelInviteByCode(cancelInviteByCodeVariables).execute();
ExampleConnector.instance.deleteTeamMember(deleteTeamMemberVariables).execute();
ExampleConnector.instance.createUserConversation(createUserConversationVariables).execute();
ExampleConnector.instance.updateUserConversation(updateUserConversationVariables).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.updateActivityLog({ ... })
.userId(...)
await ExampleConnector.instance.updateShift({ ... })
.title(...)
.execute();
```

View File

@@ -0,0 +1,388 @@
part of 'generated.dart';
class ListStaffsApplicationsByBusinessForDayVariablesBuilder {
String businessId;
Timestamp dayStart;
Timestamp dayEnd;
Optional<int> _offset = Optional.optional(nativeFromJson, nativeToJson);
Optional<int> _limit = Optional.optional(nativeFromJson, nativeToJson);
final FirebaseDataConnect _dataConnect; ListStaffsApplicationsByBusinessForDayVariablesBuilder offset(int? t) {
_offset.value = t;
return this;
}
ListStaffsApplicationsByBusinessForDayVariablesBuilder limit(int? t) {
_limit.value = t;
return this;
}
ListStaffsApplicationsByBusinessForDayVariablesBuilder(this._dataConnect, {required this.businessId,required this.dayStart,required this.dayEnd,});
Deserializer<ListStaffsApplicationsByBusinessForDayData> dataDeserializer = (dynamic json) => ListStaffsApplicationsByBusinessForDayData.fromJson(jsonDecode(json));
Serializer<ListStaffsApplicationsByBusinessForDayVariables> varsSerializer = (ListStaffsApplicationsByBusinessForDayVariables vars) => jsonEncode(vars.toJson());
Future<QueryResult<ListStaffsApplicationsByBusinessForDayData, ListStaffsApplicationsByBusinessForDayVariables>> execute() {
return ref().execute();
}
QueryRef<ListStaffsApplicationsByBusinessForDayData, ListStaffsApplicationsByBusinessForDayVariables> ref() {
ListStaffsApplicationsByBusinessForDayVariables vars= ListStaffsApplicationsByBusinessForDayVariables(businessId: businessId,dayStart: dayStart,dayEnd: dayEnd,offset: _offset,limit: _limit,);
return _dataConnect.query("listStaffsApplicationsByBusinessForDay", dataDeserializer, varsSerializer, vars);
}
}
@immutable
class ListStaffsApplicationsByBusinessForDayApplications {
final String id;
final String shiftId;
final String roleId;
final Timestamp? checkInTime;
final Timestamp? checkOutTime;
final Timestamp? appliedAt;
final EnumValue<ApplicationStatus> status;
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRole shiftRole;
final ListStaffsApplicationsByBusinessForDayApplicationsStaff staff;
ListStaffsApplicationsByBusinessForDayApplications.fromJson(dynamic json):
id = nativeFromJson<String>(json['id']),
shiftId = nativeFromJson<String>(json['shiftId']),
roleId = nativeFromJson<String>(json['roleId']),
checkInTime = json['checkInTime'] == null ? null : Timestamp.fromJson(json['checkInTime']),
checkOutTime = json['checkOutTime'] == null ? null : Timestamp.fromJson(json['checkOutTime']),
appliedAt = json['appliedAt'] == null ? null : Timestamp.fromJson(json['appliedAt']),
status = applicationStatusDeserializer(json['status']),
shiftRole = ListStaffsApplicationsByBusinessForDayApplicationsShiftRole.fromJson(json['shiftRole']),
staff = ListStaffsApplicationsByBusinessForDayApplicationsStaff.fromJson(json['staff']);
@override
bool operator ==(Object other) {
if(identical(this, other)) {
return true;
}
if(other.runtimeType != runtimeType) {
return false;
}
final ListStaffsApplicationsByBusinessForDayApplications otherTyped = other as ListStaffsApplicationsByBusinessForDayApplications;
return id == otherTyped.id &&
shiftId == otherTyped.shiftId &&
roleId == otherTyped.roleId &&
checkInTime == otherTyped.checkInTime &&
checkOutTime == otherTyped.checkOutTime &&
appliedAt == otherTyped.appliedAt &&
status == otherTyped.status &&
shiftRole == otherTyped.shiftRole &&
staff == otherTyped.staff;
}
@override
int get hashCode => Object.hashAll([id.hashCode, shiftId.hashCode, roleId.hashCode, checkInTime.hashCode, checkOutTime.hashCode, appliedAt.hashCode, status.hashCode, shiftRole.hashCode, staff.hashCode]);
Map<String, dynamic> toJson() {
Map<String, dynamic> json = {};
json['id'] = nativeToJson<String>(id);
json['shiftId'] = nativeToJson<String>(shiftId);
json['roleId'] = nativeToJson<String>(roleId);
if (checkInTime != null) {
json['checkInTime'] = checkInTime!.toJson();
}
if (checkOutTime != null) {
json['checkOutTime'] = checkOutTime!.toJson();
}
if (appliedAt != null) {
json['appliedAt'] = appliedAt!.toJson();
}
json['status'] =
applicationStatusSerializer(status)
;
json['shiftRole'] = shiftRole.toJson();
json['staff'] = staff.toJson();
return json;
}
ListStaffsApplicationsByBusinessForDayApplications({
required this.id,
required this.shiftId,
required this.roleId,
this.checkInTime,
this.checkOutTime,
this.appliedAt,
required this.status,
required this.shiftRole,
required this.staff,
});
}
@immutable
class ListStaffsApplicationsByBusinessForDayApplicationsShiftRole {
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift shift;
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole role;
ListStaffsApplicationsByBusinessForDayApplicationsShiftRole.fromJson(dynamic json):
shift = ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift.fromJson(json['shift']),
role = ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole.fromJson(json['role']);
@override
bool operator ==(Object other) {
if(identical(this, other)) {
return true;
}
if(other.runtimeType != runtimeType) {
return false;
}
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRole otherTyped = other as ListStaffsApplicationsByBusinessForDayApplicationsShiftRole;
return shift == otherTyped.shift &&
role == otherTyped.role;
}
@override
int get hashCode => Object.hashAll([shift.hashCode, role.hashCode]);
Map<String, dynamic> toJson() {
Map<String, dynamic> json = {};
json['shift'] = shift.toJson();
json['role'] = role.toJson();
return json;
}
ListStaffsApplicationsByBusinessForDayApplicationsShiftRole({
required this.shift,
required this.role,
});
}
@immutable
class ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift {
final String? location;
ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift.fromJson(dynamic json):
location = json['location'] == null ? null : nativeFromJson<String>(json['location']);
@override
bool operator ==(Object other) {
if(identical(this, other)) {
return true;
}
if(other.runtimeType != runtimeType) {
return false;
}
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift otherTyped = other as ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift;
return location == otherTyped.location;
}
@override
int get hashCode => location.hashCode;
Map<String, dynamic> toJson() {
Map<String, dynamic> json = {};
if (location != null) {
json['location'] = nativeToJson<String?>(location);
}
return json;
}
ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift({
this.location,
});
}
@immutable
class ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole {
final String name;
ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole.fromJson(dynamic json):
name = nativeFromJson<String>(json['name']);
@override
bool operator ==(Object other) {
if(identical(this, other)) {
return true;
}
if(other.runtimeType != runtimeType) {
return false;
}
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole otherTyped = other as ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole;
return name == otherTyped.name;
}
@override
int get hashCode => name.hashCode;
Map<String, dynamic> toJson() {
Map<String, dynamic> json = {};
json['name'] = nativeToJson<String>(name);
return json;
}
ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole({
required this.name,
});
}
@immutable
class ListStaffsApplicationsByBusinessForDayApplicationsStaff {
final String id;
final String fullName;
final String? email;
final String? phone;
final String? photoUrl;
ListStaffsApplicationsByBusinessForDayApplicationsStaff.fromJson(dynamic json):
id = nativeFromJson<String>(json['id']),
fullName = nativeFromJson<String>(json['fullName']),
email = json['email'] == null ? null : nativeFromJson<String>(json['email']),
phone = json['phone'] == null ? null : nativeFromJson<String>(json['phone']),
photoUrl = json['photoUrl'] == null ? null : nativeFromJson<String>(json['photoUrl']);
@override
bool operator ==(Object other) {
if(identical(this, other)) {
return true;
}
if(other.runtimeType != runtimeType) {
return false;
}
final ListStaffsApplicationsByBusinessForDayApplicationsStaff otherTyped = other as ListStaffsApplicationsByBusinessForDayApplicationsStaff;
return id == otherTyped.id &&
fullName == otherTyped.fullName &&
email == otherTyped.email &&
phone == otherTyped.phone &&
photoUrl == otherTyped.photoUrl;
}
@override
int get hashCode => Object.hashAll([id.hashCode, fullName.hashCode, email.hashCode, phone.hashCode, photoUrl.hashCode]);
Map<String, dynamic> toJson() {
Map<String, dynamic> json = {};
json['id'] = nativeToJson<String>(id);
json['fullName'] = nativeToJson<String>(fullName);
if (email != null) {
json['email'] = nativeToJson<String?>(email);
}
if (phone != null) {
json['phone'] = nativeToJson<String?>(phone);
}
if (photoUrl != null) {
json['photoUrl'] = nativeToJson<String?>(photoUrl);
}
return json;
}
ListStaffsApplicationsByBusinessForDayApplicationsStaff({
required this.id,
required this.fullName,
this.email,
this.phone,
this.photoUrl,
});
}
@immutable
class ListStaffsApplicationsByBusinessForDayData {
final List<ListStaffsApplicationsByBusinessForDayApplications> applications;
ListStaffsApplicationsByBusinessForDayData.fromJson(dynamic json):
applications = (json['applications'] as List<dynamic>)
.map((e) => ListStaffsApplicationsByBusinessForDayApplications.fromJson(e))
.toList();
@override
bool operator ==(Object other) {
if(identical(this, other)) {
return true;
}
if(other.runtimeType != runtimeType) {
return false;
}
final ListStaffsApplicationsByBusinessForDayData otherTyped = other as ListStaffsApplicationsByBusinessForDayData;
return applications == otherTyped.applications;
}
@override
int get hashCode => applications.hashCode;
Map<String, dynamic> toJson() {
Map<String, dynamic> json = {};
json['applications'] = applications.map((e) => e.toJson()).toList();
return json;
}
ListStaffsApplicationsByBusinessForDayData({
required this.applications,
});
}
@immutable
class ListStaffsApplicationsByBusinessForDayVariables {
final String businessId;
final Timestamp dayStart;
final Timestamp dayEnd;
late final Optional<int>offset;
late final Optional<int>limit;
@Deprecated('fromJson is deprecated for Variable classes as they are no longer required for deserialization.')
ListStaffsApplicationsByBusinessForDayVariables.fromJson(Map<String, dynamic> json):
businessId = nativeFromJson<String>(json['businessId']),
dayStart = Timestamp.fromJson(json['dayStart']),
dayEnd = Timestamp.fromJson(json['dayEnd']) {
offset = Optional.optional(nativeFromJson, nativeToJson);
offset.value = json['offset'] == null ? null : nativeFromJson<int>(json['offset']);
limit = Optional.optional(nativeFromJson, nativeToJson);
limit.value = json['limit'] == null ? null : nativeFromJson<int>(json['limit']);
}
@override
bool operator ==(Object other) {
if(identical(this, other)) {
return true;
}
if(other.runtimeType != runtimeType) {
return false;
}
final ListStaffsApplicationsByBusinessForDayVariables otherTyped = other as ListStaffsApplicationsByBusinessForDayVariables;
return businessId == otherTyped.businessId &&
dayStart == otherTyped.dayStart &&
dayEnd == otherTyped.dayEnd &&
offset == otherTyped.offset &&
limit == otherTyped.limit;
}
@override
int get hashCode => Object.hashAll([businessId.hashCode, dayStart.hashCode, dayEnd.hashCode, offset.hashCode, limit.hashCode]);
Map<String, dynamic> toJson() {
Map<String, dynamic> json = {};
json['businessId'] = nativeToJson<String>(businessId);
json['dayStart'] = dayStart.toJson();
json['dayEnd'] = dayEnd.toJson();
if(offset.state == OptionalState.set) {
json['offset'] = offset.toJson();
}
if(limit.state == OptionalState.set) {
json['limit'] = limit.toJson();
}
return json;
}
ListStaffsApplicationsByBusinessForDayVariables({
required this.businessId,
required this.dayStart,
required this.dayEnd,
required this.offset,
required this.limit,
});
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'data/repositories_impl/coverage_repository_impl.dart';
import 'domain/repositories/coverage_repository.dart';
import 'domain/usecases/get_coverage_stats_usecase.dart';
@@ -11,7 +12,9 @@ class CoverageModule extends Module {
@override
void binds(Injector i) {
// Repositories
i.addSingleton<CoverageRepository>(CoverageRepositoryImpl.new);
i.addSingleton<CoverageRepository>(
() => CoverageRepositoryImpl(dataConnect: ExampleConnector.instance),
);
// Use Cases
i.addSingleton(GetShiftsForDateUseCase.new);

View File

@@ -1,3 +1,5 @@
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import '../../domain/repositories/coverage_repository.dart';
import '../../domain/ui_entities/coverage_entities.dart';
@@ -13,82 +15,43 @@ import '../../domain/ui_entities/coverage_entities.dart';
/// - Returns domain entities from `domain/ui_entities`.
class CoverageRepositoryImpl implements CoverageRepository {
/// Creates a [CoverageRepositoryImpl].
CoverageRepositoryImpl();
CoverageRepositoryImpl({required dc.ExampleConnector dataConnect})
: _dataConnect = dataConnect;
final dc.ExampleConnector _dataConnect;
/// Fetches shifts for a specific date.
@override
Future<List<CoverageShift>> getShiftsForDate({required DateTime date}) async {
// Simulate network delay
await Future<void>.delayed(const Duration(milliseconds: 500));
// Mock data - in production, this would come from data_connect
final DateTime today = DateTime.now();
final bool isToday = date.year == today.year &&
date.month == today.month &&
date.day == today.day;
if (!isToday) {
// Return empty list for non-today dates
final String? businessId =
dc.ClientSessionStore.instance.session?.business?.id;
print('Coverage: now=${DateTime.now().toIso8601String()}');
if (businessId == null || businessId.isEmpty) {
print('Coverage: missing businessId for date=${date.toIso8601String()}');
return <CoverageShift>[];
}
return <CoverageShift>[
CoverageShift(
id: '1',
title: 'Banquet Server',
location: 'Grand Ballroom',
startTime: '16:00',
workersNeeded: 10,
date: date,
workers: const <CoverageWorker>[
CoverageWorker(
name: 'Sarah Wilson',
status: 'confirmed',
checkInTime: '15:55',
),
CoverageWorker(
name: 'Mike Ross',
status: 'confirmed',
checkInTime: '16:00',
),
CoverageWorker(
name: 'Jane Doe',
status: 'confirmed',
checkInTime: null,
),
CoverageWorker(
name: 'John Smith',
status: 'late',
checkInTime: null,
),
],
),
CoverageShift(
id: '2',
title: 'Bartender',
location: 'Lobby Bar',
startTime: '17:00',
workersNeeded: 4,
date: date,
workers: const <CoverageWorker>[
CoverageWorker(
name: 'Emily Blunt',
status: 'confirmed',
checkInTime: '16:45',
),
CoverageWorker(
name: 'Chris Evans',
status: 'confirmed',
checkInTime: '16:50',
),
CoverageWorker(
name: 'Tom Holland',
status: 'confirmed',
checkInTime: null,
),
],
),
];
final DateTime start = DateTime(date.year, date.month, date.day);
final DateTime end =
DateTime(date.year, date.month, date.day, 23, 59, 59, 999);
print(
'Coverage: request businessId=$businessId dayStart=${start.toIso8601String()} dayEnd=${end.toIso8601String()}',
);
final fdc.QueryResult<
dc.ListStaffsApplicationsByBusinessForDayData,
dc.ListStaffsApplicationsByBusinessForDayVariables> result =
await _dataConnect
.listStaffsApplicationsByBusinessForDay(
businessId: businessId,
dayStart: _toTimestamp(start),
dayEnd: _toTimestamp(end),
)
.execute();
print(
'Coverage: ${date.toIso8601String()} staffsApplications=${result.data.applications.length}',
);
return _mapCoverageShifts(result.data.applications, date);
}
/// Fetches coverage statistics for a specific date.
@@ -120,4 +83,122 @@ class CoverageRepositoryImpl implements CoverageRepository {
late: late,
);
}
fdc.Timestamp _toTimestamp(DateTime dateTime) {
final int seconds = dateTime.millisecondsSinceEpoch ~/ 1000;
final int nanoseconds =
(dateTime.millisecondsSinceEpoch % 1000) * 1000000;
return fdc.Timestamp(nanoseconds, seconds);
}
Future<List<CoverageShift>> _mapCoverageShifts(
List<dc.ListStaffsApplicationsByBusinessForDayApplications> applications,
DateTime date,
) async {
if (applications.isEmpty) {
return <CoverageShift>[];
}
final Map<String, _CoverageGroup> groups =
<String, _CoverageGroup>{};
for (final dc.ListStaffsApplicationsByBusinessForDayApplications app
in applications) {
final String key = '${app.shiftId}:${app.roleId}';
final _CoverageGroup existing = groups[key] ??
_CoverageGroup(
shiftId: app.shiftId,
roleId: app.roleId,
roleName: app.shiftRole.role.name,
location: app.shiftRole.shift.location ?? '',
workers: <CoverageWorker>[],
);
existing.workers.add(
CoverageWorker(
name: app.staff.fullName,
status: _mapWorkerStatus(app.status),
checkInTime: _formatTime(app.checkInTime),
),
);
groups[key] = existing;
}
final List<_CoverageGroup> groupList = groups.values.toList();
final List<CoverageShift> shifts = <CoverageShift>[];
for (final _CoverageGroup group in groupList) {
final fdc.QueryResult<dc.GetShiftRoleByIdData,
dc.GetShiftRoleByIdVariables> shiftRoleResult =
await _dataConnect
.getShiftRoleById(
shiftId: group.shiftId,
roleId: group.roleId,
)
.execute();
final dc.GetShiftRoleByIdShiftRole? shiftRole =
shiftRoleResult.data.shiftRole;
if (shiftRole == null) {
continue;
}
final String startTime = _formatTime(shiftRole.startTime) ?? '00:00';
shifts.add(
CoverageShift(
id: shiftRole.id,
title: group.roleName,
location: group.location,
startTime: startTime,
workersNeeded: shiftRole.count,
date: date,
workers: group.workers,
),
);
}
return shifts;
}
String _mapWorkerStatus(
dc.EnumValue<dc.ApplicationStatus> status,
) {
if (status is dc.Known<dc.ApplicationStatus>) {
switch (status.value) {
case dc.ApplicationStatus.LATE:
return 'late';
case dc.ApplicationStatus.CHECKED_IN:
case dc.ApplicationStatus.CHECKED_OUT:
case dc.ApplicationStatus.ACCEPTED:
case dc.ApplicationStatus.CONFIRMED:
case dc.ApplicationStatus.PENDING:
case dc.ApplicationStatus.REJECTED:
case dc.ApplicationStatus.NO_SHOW:
return 'confirmed';
}
}
return 'confirmed';
}
String? _formatTime(fdc.Timestamp? timestamp) {
if (timestamp == null) {
return null;
}
final DateTime date = timestamp.toDateTime();
final String hour = date.hour.toString().padLeft(2, '0');
final String minute = date.minute.toString().padLeft(2, '0');
return '$hour:$minute';
}
}
class _CoverageGroup {
_CoverageGroup({
required this.shiftId,
required this.roleId,
required this.roleName,
required this.location,
required this.workers,
});
final String shiftId;
final String roleId;
final String roleName;
final String location;
final List<CoverageWorker> workers;
}