feat: architecture overhaul, launchpad-style reports, and uber-style locations
- Strengthened Buffer Layer architecture to decouple Data Connect from Domain - Rewired Coverage, Performance, and Forecast reports to match Launchpad logic - Implemented Uber-style Preferred Locations search using Google Places API - Added session recovery logic to prevent crashes on app restart - Synchronized backend schemas & SDK for ShiftStatus enums - Fixed various build/compilation errors and localization duplicates
This commit is contained in:
@@ -1,119 +1,26 @@
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../../domain/repositories/home_repository_interface.dart';
|
||||
|
||||
/// Implementation of [HomeRepositoryInterface] that delegates to [HomeRepositoryMock].
|
||||
/// Implementation of [HomeRepositoryInterface] that delegates to [dc.HomeConnectorRepository].
|
||||
///
|
||||
/// This implementation resides in the data layer and acts as a bridge between the
|
||||
/// domain layer and the data source (in this case, a mock from data_connect).
|
||||
/// This implementation follows the "Buffer Layer" pattern by using a dedicated
|
||||
/// connector repository from the data_connect package.
|
||||
class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
/// Creates a [HomeRepositoryImpl].
|
||||
HomeRepositoryImpl(this._service);
|
||||
final dc.HomeConnectorRepository _connectorRepository;
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
HomeRepositoryImpl({
|
||||
dc.HomeConnectorRepository? connectorRepository,
|
||||
dc.DataConnectService? service,
|
||||
}) : _connectorRepository = connectorRepository ??
|
||||
dc.DataConnectService.instance.getHomeRepository(),
|
||||
_service = service ?? dc.DataConnectService.instance;
|
||||
|
||||
@override
|
||||
Future<HomeDashboardData> getDashboardData() async {
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final DateTime now = DateTime.now();
|
||||
final int daysFromMonday = now.weekday - DateTime.monday;
|
||||
final DateTime monday = DateTime(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
).subtract(Duration(days: daysFromMonday));
|
||||
final DateTime weekRangeStart = DateTime(
|
||||
monday.year,
|
||||
monday.month,
|
||||
monday.day,
|
||||
);
|
||||
final DateTime weekRangeEnd = DateTime(
|
||||
monday.year,
|
||||
monday.month,
|
||||
monday.day + 13,
|
||||
23,
|
||||
59,
|
||||
59,
|
||||
999,
|
||||
);
|
||||
final fdc.QueryResult<
|
||||
dc.GetCompletedShiftsByBusinessIdData,
|
||||
dc.GetCompletedShiftsByBusinessIdVariables
|
||||
>
|
||||
completedResult = await _service.connector
|
||||
.getCompletedShiftsByBusinessId(
|
||||
businessId: businessId,
|
||||
dateFrom: _service.toTimestamp(weekRangeStart),
|
||||
dateTo: _service.toTimestamp(weekRangeEnd),
|
||||
)
|
||||
.execute();
|
||||
|
||||
double weeklySpending = 0.0;
|
||||
double next7DaysSpending = 0.0;
|
||||
int weeklyShifts = 0;
|
||||
int next7DaysScheduled = 0;
|
||||
for (final dc.GetCompletedShiftsByBusinessIdShifts shift
|
||||
in completedResult.data.shifts) {
|
||||
final DateTime? shiftDate = shift.date?.toDateTime();
|
||||
if (shiftDate == null) {
|
||||
continue;
|
||||
}
|
||||
final int offset = shiftDate.difference(weekRangeStart).inDays;
|
||||
if (offset < 0 || offset > 13) {
|
||||
continue;
|
||||
}
|
||||
final double cost = shift.cost ?? 0.0;
|
||||
if (offset <= 6) {
|
||||
weeklySpending += cost;
|
||||
weeklyShifts += 1;
|
||||
} else {
|
||||
next7DaysSpending += cost;
|
||||
next7DaysScheduled += 1;
|
||||
}
|
||||
}
|
||||
|
||||
final DateTime start = DateTime(now.year, now.month, now.day);
|
||||
final DateTime end = DateTime(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
23,
|
||||
59,
|
||||
59,
|
||||
999,
|
||||
);
|
||||
|
||||
final fdc.QueryResult<
|
||||
dc.ListShiftRolesByBusinessAndDateRangeData,
|
||||
dc.ListShiftRolesByBusinessAndDateRangeVariables
|
||||
>
|
||||
result = await _service.connector
|
||||
.listShiftRolesByBusinessAndDateRange(
|
||||
businessId: businessId,
|
||||
start: _service.toTimestamp(start),
|
||||
end: _service.toTimestamp(end),
|
||||
)
|
||||
.execute();
|
||||
|
||||
int totalNeeded = 0;
|
||||
int totalFilled = 0;
|
||||
for (final dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole
|
||||
in result.data.shiftRoles) {
|
||||
totalNeeded += shiftRole.count;
|
||||
totalFilled += shiftRole.assigned ?? 0;
|
||||
}
|
||||
|
||||
return HomeDashboardData(
|
||||
weeklySpending: weeklySpending,
|
||||
next7DaysSpending: next7DaysSpending,
|
||||
weeklyShifts: weeklyShifts,
|
||||
next7DaysScheduled: next7DaysScheduled,
|
||||
totalNeeded: totalNeeded,
|
||||
totalFilled: totalFilled,
|
||||
);
|
||||
});
|
||||
final businessId = await _service.getBusinessId();
|
||||
return _connectorRepository.getDashboardData(businessId: businessId);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -121,7 +28,6 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
final dc.ClientSession? session = dc.ClientSessionStore.instance.session;
|
||||
final dc.ClientBusinessSession? business = session?.business;
|
||||
|
||||
// If session data is available, return it immediately
|
||||
if (business != null) {
|
||||
return UserSessionData(
|
||||
businessName: business.businessName,
|
||||
@@ -130,74 +36,38 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
}
|
||||
|
||||
return await _service.run(() async {
|
||||
// If session is not initialized, attempt to fetch business data to populate session
|
||||
final String businessId = await _service.getBusinessId();
|
||||
final fdc.QueryResult<dc.GetBusinessByIdData, dc.GetBusinessByIdVariables>
|
||||
businessResult = await _service.connector
|
||||
final businessResult = await _service.connector
|
||||
.getBusinessById(id: businessId)
|
||||
.execute();
|
||||
|
||||
if (businessResult.data.business == null) {
|
||||
final b = businessResult.data.business;
|
||||
if (b == null) {
|
||||
throw Exception('Business data not found for ID: $businessId');
|
||||
}
|
||||
|
||||
final dc.ClientSession updatedSession = dc.ClientSession(
|
||||
final updatedSession = dc.ClientSession(
|
||||
business: dc.ClientBusinessSession(
|
||||
id: businessResult.data.business!.id,
|
||||
businessName: businessResult.data.business?.businessName ?? '',
|
||||
email: businessResult.data.business?.email ?? '',
|
||||
city: businessResult.data.business?.city ?? '',
|
||||
contactName: businessResult.data.business?.contactName ?? '',
|
||||
companyLogoUrl: businessResult.data.business?.companyLogoUrl,
|
||||
id: b.id,
|
||||
businessName: b.businessName,
|
||||
email: b.email ?? '',
|
||||
city: b.city ?? '',
|
||||
contactName: b.contactName ?? '',
|
||||
companyLogoUrl: b.companyLogoUrl,
|
||||
),
|
||||
);
|
||||
dc.ClientSessionStore.instance.setSession(updatedSession);
|
||||
|
||||
return UserSessionData(
|
||||
businessName: businessResult.data.business!.businessName,
|
||||
photoUrl: businessResult.data.business!.companyLogoUrl,
|
||||
businessName: b.businessName,
|
||||
photoUrl: b.companyLogoUrl,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ReorderItem>> getRecentReorders() async {
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final DateTime now = DateTime.now();
|
||||
final DateTime start = now.subtract(const Duration(days: 30));
|
||||
final fdc.Timestamp startTimestamp = _service.toTimestamp(start);
|
||||
final fdc.Timestamp endTimestamp = _service.toTimestamp(now);
|
||||
|
||||
final fdc.QueryResult<
|
||||
dc.ListShiftRolesByBusinessDateRangeCompletedOrdersData,
|
||||
dc.ListShiftRolesByBusinessDateRangeCompletedOrdersVariables
|
||||
>
|
||||
result = await _service.connector
|
||||
.listShiftRolesByBusinessDateRangeCompletedOrders(
|
||||
businessId: businessId,
|
||||
start: startTimestamp,
|
||||
end: endTimestamp,
|
||||
)
|
||||
.execute();
|
||||
|
||||
return result.data.shiftRoles.map((
|
||||
dc.ListShiftRolesByBusinessDateRangeCompletedOrdersShiftRoles shiftRole,
|
||||
) {
|
||||
final String location =
|
||||
shiftRole.shift.location ?? shiftRole.shift.locationAddress ?? '';
|
||||
final String type = shiftRole.shift.order.orderType.stringValue;
|
||||
return ReorderItem(
|
||||
orderId: shiftRole.shift.order.id,
|
||||
title: '${shiftRole.role.name} - ${shiftRole.shift.title}',
|
||||
location: location,
|
||||
hourlyRate: shiftRole.role.costPerHour,
|
||||
hours: shiftRole.hours ?? 0,
|
||||
workers: shiftRole.count,
|
||||
type: type,
|
||||
);
|
||||
}).toList();
|
||||
});
|
||||
final businessId = await _service.getBusinessId();
|
||||
return _connectorRepository.getRecentReorders(businessId: businessId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user