Merge pull request #428 from Oloodi/408-feature-implement-paidunpaid-breaks---client-app-frontend-development
DataConnectorService is implemented in the client appplication
This commit is contained in:
@@ -41,7 +41,7 @@ class ClientSignUpPage extends StatelessWidget {
|
||||
final TranslationsClientAuthenticationSignUpPageEn i18n = t.client_authentication.sign_up_page;
|
||||
final ClientAuthBloc authBloc = Modular.get<ClientAuthBloc>();
|
||||
|
||||
return BlocProvider.value(
|
||||
return BlocProvider<ClientAuthBloc>.value(
|
||||
value: authBloc,
|
||||
child: BlocConsumer<ClientAuthBloc, ClientAuthState>(
|
||||
listener: (BuildContext context, ClientAuthState state) {
|
||||
|
||||
@@ -54,7 +54,6 @@ class BillingBloc extends Bloc<BillingEvent, BillingState>
|
||||
_getSpendingBreakdown.call(state.period),
|
||||
]);
|
||||
|
||||
final double currentBill = results[0] as double;
|
||||
final double savings = results[1] as double;
|
||||
final List<Invoice> pendingInvoices = results[2] as List<Invoice>;
|
||||
final List<Invoice> invoiceHistory = results[3] as List<Invoice>;
|
||||
|
||||
@@ -10,24 +10,20 @@ import 'presentation/pages/coverage_page.dart';
|
||||
|
||||
/// Modular module for the coverage feature.
|
||||
class CoverageModule extends Module {
|
||||
@override
|
||||
List<Module> get imports => <Module>[DataConnectModule()];
|
||||
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
// Repositories
|
||||
i.addSingleton<CoverageRepository>(
|
||||
() => CoverageRepositoryImpl(dataConnect: ExampleConnector.instance),
|
||||
);
|
||||
i.addSingleton<CoverageRepository>(CoverageRepositoryImpl.new);
|
||||
|
||||
// Use Cases
|
||||
i.addSingleton(GetShiftsForDateUseCase.new);
|
||||
i.addSingleton(GetCoverageStatsUseCase.new);
|
||||
|
||||
// BLoCs
|
||||
i.addSingleton<CoverageBloc>(
|
||||
() => CoverageBloc(
|
||||
getShiftsForDate: i.get<GetShiftsForDateUseCase>(),
|
||||
getCoverageStats: i.get<GetCoverageStatsUseCase>(),
|
||||
),
|
||||
);
|
||||
i.addSingleton<CoverageBloc>(CoverageBloc.new);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -15,44 +15,36 @@ import '../../domain/repositories/coverage_repository.dart';
|
||||
/// - Returns domain entities from `domain/ui_entities`.
|
||||
class CoverageRepositoryImpl implements CoverageRepository {
|
||||
/// Creates a [CoverageRepositoryImpl].
|
||||
CoverageRepositoryImpl({required dc.ExampleConnector dataConnect})
|
||||
: _dataConnect = dataConnect;
|
||||
CoverageRepositoryImpl({required dc.DataConnectService service}) : _service = service;
|
||||
|
||||
final dc.ExampleConnector _dataConnect;
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
/// Fetches shifts for a specific date.
|
||||
@override
|
||||
Future<List<CoverageShift>> getShiftsForDate({required DateTime date}) async {
|
||||
try {
|
||||
final String? businessId =
|
||||
dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return <CoverageShift>[];
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final DateTime start = DateTime(date.year, date.month, date.day);
|
||||
final DateTime end =
|
||||
DateTime(date.year, date.month, date.day, 23, 59, 59, 999);
|
||||
final DateTime end = DateTime(date.year, date.month, date.day, 23, 59, 59, 999);
|
||||
|
||||
final fdc.QueryResult<
|
||||
dc.ListShiftRolesByBusinessAndDateRangeData,
|
||||
final fdc.QueryResult<dc.ListShiftRolesByBusinessAndDateRangeData,
|
||||
dc.ListShiftRolesByBusinessAndDateRangeVariables> shiftRolesResult =
|
||||
await _dataConnect
|
||||
await _service.connector
|
||||
.listShiftRolesByBusinessAndDateRange(
|
||||
businessId: businessId,
|
||||
start: _toTimestamp(start),
|
||||
end: _toTimestamp(end),
|
||||
start: _service.toTimestamp(start),
|
||||
end: _service.toTimestamp(end),
|
||||
)
|
||||
.execute();
|
||||
|
||||
final fdc.QueryResult<
|
||||
dc.ListStaffsApplicationsByBusinessForDayData,
|
||||
final fdc.QueryResult<dc.ListStaffsApplicationsByBusinessForDayData,
|
||||
dc.ListStaffsApplicationsByBusinessForDayVariables> applicationsResult =
|
||||
await _dataConnect
|
||||
await _service.connector
|
||||
.listStaffsApplicationsByBusinessForDay(
|
||||
businessId: businessId,
|
||||
dayStart: _toTimestamp(start),
|
||||
dayEnd: _toTimestamp(end),
|
||||
dayStart: _service.toTimestamp(start),
|
||||
dayEnd: _service.toTimestamp(end),
|
||||
)
|
||||
.execute();
|
||||
|
||||
@@ -61,18 +53,7 @@ class CoverageRepositoryImpl implements CoverageRepository {
|
||||
applicationsResult.data.applications,
|
||||
date,
|
||||
);
|
||||
} catch (e) {
|
||||
final String error = e.toString().toLowerCase();
|
||||
if (error.contains('network') ||
|
||||
error.contains('connection') ||
|
||||
error.contains('unavailable') ||
|
||||
error.contains('offline') ||
|
||||
error.contains('socket') ||
|
||||
error.contains('failed host lookup')) {
|
||||
throw NetworkException(technicalMessage: 'Coverage fetch failed: $e');
|
||||
}
|
||||
throw ServerException(technicalMessage: 'Coverage fetch failed: $e');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Fetches coverage statistics for a specific date.
|
||||
@@ -110,14 +91,6 @@ class CoverageRepositoryImpl implements CoverageRepository {
|
||||
);
|
||||
}
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||
final DateTime utc = dateTime.toUtc();
|
||||
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
||||
final int nanoseconds =
|
||||
(utc.millisecondsSinceEpoch % 1000) * 1000000;
|
||||
return fdc.Timestamp(nanoseconds, seconds);
|
||||
}
|
||||
|
||||
List<CoverageShift> _mapCoverageShifts(
|
||||
List<dc.ListShiftRolesByBusinessAndDateRangeShiftRoles> shiftRoles,
|
||||
List<dc.ListStaffsApplicationsByBusinessForDayApplications> applications,
|
||||
|
||||
@@ -6,8 +6,7 @@ import '../../domain/repositories/client_create_order_repository_interface.dart'
|
||||
|
||||
/// Implementation of [ClientCreateOrderRepositoryInterface].
|
||||
///
|
||||
/// This implementation coordinates data access for order creation by delegating
|
||||
/// to the [OrderRepositoryMock] and [ExampleConnector] from the shared
|
||||
/// This implementation coordinates data access for order creation by [DataConnectService] from the shared
|
||||
/// Data Connect package.
|
||||
///
|
||||
/// It follows the KROW Clean Architecture by keeping the data layer focused
|
||||
|
||||
@@ -23,11 +23,7 @@ class ClientHomeModule extends Module {
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
// Repositories
|
||||
i.addLazySingleton<HomeRepositoryInterface>(
|
||||
() => HomeRepositoryImpl(
|
||||
ExampleConnector.instance,
|
||||
),
|
||||
);
|
||||
i.addLazySingleton<HomeRepositoryInterface>(HomeRepositoryImpl.new);
|
||||
|
||||
// UseCases
|
||||
i.addLazySingleton(GetDashboardDataUseCase.new);
|
||||
@@ -35,13 +31,7 @@ class ClientHomeModule extends Module {
|
||||
i.addLazySingleton(GetUserSessionDataUseCase.new);
|
||||
|
||||
// BLoCs
|
||||
i.add<ClientHomeBloc>(
|
||||
() => ClientHomeBloc(
|
||||
getDashboardDataUseCase: i.get<GetDashboardDataUseCase>(),
|
||||
getRecentReordersUseCase: i.get<GetRecentReordersUseCase>(),
|
||||
getUserSessionDataUseCase: i.get<GetUserSessionDataUseCase>(),
|
||||
),
|
||||
);
|
||||
i.add<ClientHomeBloc>(ClientHomeBloc.new);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -8,25 +8,14 @@ import '../../domain/repositories/home_repository_interface.dart';
|
||||
/// 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).
|
||||
class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
|
||||
/// Creates a [HomeRepositoryImpl].
|
||||
HomeRepositoryImpl(this._dataConnect);
|
||||
final dc.ExampleConnector _dataConnect;
|
||||
HomeRepositoryImpl(this._service);
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
@override
|
||||
Future<HomeDashboardData> getDashboardData() async {
|
||||
try {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return const HomeDashboardData(
|
||||
weeklySpending: 0,
|
||||
next7DaysSpending: 0,
|
||||
weeklyShifts: 0,
|
||||
next7DaysScheduled: 0,
|
||||
totalNeeded: 0,
|
||||
totalFilled: 0,
|
||||
);
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final DateTime now = DateTime.now();
|
||||
final int daysFromMonday = now.weekday - DateTime.monday;
|
||||
@@ -35,14 +24,13 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
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,
|
||||
final fdc.QueryResult<dc.GetCompletedShiftsByBusinessIdData,
|
||||
dc.GetCompletedShiftsByBusinessIdVariables> completedResult =
|
||||
await _dataConnect
|
||||
await _service.connector
|
||||
.getCompletedShiftsByBusinessId(
|
||||
businessId: businessId,
|
||||
dateFrom: _toTimestamp(weekRangeStart),
|
||||
dateTo: _toTimestamp(weekRangeEnd),
|
||||
dateFrom: _service.toTimestamp(weekRangeStart),
|
||||
dateTo: _service.toTimestamp(weekRangeEnd),
|
||||
)
|
||||
.execute();
|
||||
|
||||
@@ -50,8 +38,7 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
double next7DaysSpending = 0.0;
|
||||
int weeklyShifts = 0;
|
||||
int next7DaysScheduled = 0;
|
||||
for (final dc.GetCompletedShiftsByBusinessIdShifts shift
|
||||
in completedResult.data.shifts) {
|
||||
for (final dc.GetCompletedShiftsByBusinessIdShifts shift in completedResult.data.shifts) {
|
||||
final DateTime? shiftDate = shift.date?.toDateTime();
|
||||
if (shiftDate == null) {
|
||||
continue;
|
||||
@@ -73,14 +60,13 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
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,
|
||||
final fdc.QueryResult<dc.ListShiftRolesByBusinessAndDateRangeData,
|
||||
dc.ListShiftRolesByBusinessAndDateRangeVariables> result =
|
||||
await _dataConnect
|
||||
await _service.connector
|
||||
.listShiftRolesByBusinessAndDateRange(
|
||||
businessId: businessId,
|
||||
start: _toTimestamp(start),
|
||||
end: _toTimestamp(end),
|
||||
start: _service.toTimestamp(start),
|
||||
end: _service.toTimestamp(end),
|
||||
)
|
||||
.execute();
|
||||
|
||||
@@ -100,18 +86,7 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
totalNeeded: totalNeeded,
|
||||
totalFilled: totalFilled,
|
||||
);
|
||||
} catch (e) {
|
||||
final String error = e.toString().toLowerCase();
|
||||
if (error.contains('network') ||
|
||||
error.contains('connection') ||
|
||||
error.contains('unavailable') ||
|
||||
error.contains('offline') ||
|
||||
error.contains('socket') ||
|
||||
error.contains('failed host lookup')) {
|
||||
throw NetworkException(technicalMessage: 'Home dashboard fetch failed: $e');
|
||||
}
|
||||
throw ServerException(technicalMessage: 'Home dashboard fetch failed: $e');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -125,34 +100,29 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
|
||||
@override
|
||||
Future<List<ReorderItem>> getRecentReorders() async {
|
||||
try {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return const <ReorderItem>[];
|
||||
}
|
||||
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 = _toTimestamp(start);
|
||||
final fdc.Timestamp endTimestamp = _toTimestamp(now);
|
||||
final fdc.Timestamp startTimestamp = _service.toTimestamp(start);
|
||||
final fdc.Timestamp endTimestamp = _service.toTimestamp(now);
|
||||
|
||||
final fdc.QueryResult<
|
||||
dc.ListShiftRolesByBusinessDateRangeCompletedOrdersData,
|
||||
final fdc.QueryResult<dc.ListShiftRolesByBusinessDateRangeCompletedOrdersData,
|
||||
dc.ListShiftRolesByBusinessDateRangeCompletedOrdersVariables> result =
|
||||
await _dataConnect.listShiftRolesByBusinessDateRangeCompletedOrders(
|
||||
await _service.connector
|
||||
.listShiftRolesByBusinessDateRangeCompletedOrders(
|
||||
businessId: businessId,
|
||||
start: startTimestamp,
|
||||
end: endTimestamp,
|
||||
).execute();
|
||||
)
|
||||
.execute();
|
||||
|
||||
return result.data.shiftRoles.map((
|
||||
return result.data.shiftRoles
|
||||
.map((
|
||||
dc.ListShiftRolesByBusinessDateRangeCompletedOrdersShiftRoles shiftRole,
|
||||
) {
|
||||
|
||||
final String location =
|
||||
shiftRole.shift.location ??
|
||||
shiftRole.shift.locationAddress ??
|
||||
'';
|
||||
final String location = shiftRole.shift.location ?? shiftRole.shift.locationAddress ?? '';
|
||||
final String type = shiftRole.shift.order.orderType.stringValue;
|
||||
return ReorderItem(
|
||||
orderId: shiftRole.shift.order.id,
|
||||
@@ -163,26 +133,8 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
workers: shiftRole.count,
|
||||
type: type,
|
||||
);
|
||||
}).toList();
|
||||
} catch (e) {
|
||||
final String error = e.toString().toLowerCase();
|
||||
if (error.contains('network') ||
|
||||
error.contains('connection') ||
|
||||
error.contains('unavailable') ||
|
||||
error.contains('offline') ||
|
||||
error.contains('socket') ||
|
||||
error.contains('failed host lookup')) {
|
||||
throw NetworkException(technicalMessage: 'Home reorders fetch failed: $e');
|
||||
}
|
||||
throw ServerException(technicalMessage: 'Home reorders fetch failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime date) {
|
||||
final DateTime utc = date.toUtc();
|
||||
final int millis = utc.millisecondsSinceEpoch;
|
||||
final int seconds = millis ~/ 1000;
|
||||
final int nanos = (millis % 1000) * 1000000;
|
||||
return fdc.Timestamp(nanos, seconds);
|
||||
})
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ class CoverageDashboard extends StatelessWidget {
|
||||
int totalConfirmed = 0;
|
||||
double todayCost = 0;
|
||||
|
||||
for (final s in shifts) {
|
||||
final int needed = s['workersNeeded'] as int? ?? 0;
|
||||
for (final dynamic s in shifts) {
|
||||
final int needed = (s as Map<String, dynamic>)['workersNeeded'] as int? ?? 0;
|
||||
final int confirmed = s['filled'] as int? ?? 0;
|
||||
final double rate = s['hourlyRate'] as double? ?? 0.0;
|
||||
final double hours = s['hours'] as double? ?? 0.0;
|
||||
@@ -39,10 +39,10 @@ class CoverageDashboard extends StatelessWidget {
|
||||
final int unfilledPositions = totalNeeded - totalConfirmed;
|
||||
|
||||
final int checkedInCount = applications
|
||||
.where((a) => (a as Map)['checkInTime'] != null)
|
||||
.where((dynamic a) => (a as Map<String, dynamic>)['checkInTime'] != null)
|
||||
.length;
|
||||
final int lateWorkersCount = applications
|
||||
.where((a) => (a as Map)['status'] == 'LATE')
|
||||
.where((dynamic a) => (a as Map<String, dynamic>)['status'] == 'LATE')
|
||||
.length;
|
||||
|
||||
final bool isCoverageGood = coveragePercent >= 90;
|
||||
|
||||
@@ -3,7 +3,6 @@ library;
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
import 'src/data/repositories_impl/hub_repository_impl.dart';
|
||||
import 'src/domain/repositories/hub_repository_interface.dart';
|
||||
import 'src/domain/usecases/assign_nfc_tag_usecase.dart';
|
||||
@@ -23,12 +22,7 @@ class ClientHubsModule extends Module {
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
// Repositories
|
||||
i.addLazySingleton<HubRepositoryInterface>(
|
||||
() => HubRepositoryImpl(
|
||||
firebaseAuth: firebase.FirebaseAuth.instance,
|
||||
dataConnect: ExampleConnector.instance,
|
||||
),
|
||||
);
|
||||
i.addLazySingleton<HubRepositoryInterface>(HubRepositoryImpl.new);
|
||||
|
||||
// UseCases
|
||||
i.addLazySingleton(GetHubsUseCase.new);
|
||||
@@ -37,14 +31,7 @@ class ClientHubsModule extends Module {
|
||||
i.addLazySingleton(AssignNfcTagUseCase.new);
|
||||
|
||||
// BLoCs
|
||||
i.add<ClientHubsBloc>(
|
||||
() => ClientHubsBloc(
|
||||
getHubsUseCase: i.get<GetHubsUseCase>(),
|
||||
createHubUseCase: i.get<CreateHubUseCase>(),
|
||||
deleteHubUseCase: i.get<DeleteHubUseCase>(),
|
||||
assignNfcTagUseCase: i.get<AssignNfcTagUseCase>(),
|
||||
),
|
||||
);
|
||||
i.add<ClientHubsBloc>(ClientHubsBloc.new);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -9,30 +9,26 @@ import 'package:krow_domain/krow_domain.dart' as domain;
|
||||
import 'package:krow_domain/krow_domain.dart'
|
||||
show
|
||||
HubHasOrdersException,
|
||||
HubCreationFailedException,
|
||||
BusinessNotFoundException,
|
||||
NotAuthenticatedException;
|
||||
|
||||
import '../../domain/repositories/hub_repository_interface.dart';
|
||||
|
||||
/// Implementation of [HubRepositoryInterface] backed by Data Connect.
|
||||
class HubRepositoryImpl
|
||||
with dc.DataErrorHandler
|
||||
implements HubRepositoryInterface {
|
||||
class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
HubRepositoryImpl({
|
||||
required firebase.FirebaseAuth firebaseAuth,
|
||||
required dc.ExampleConnector dataConnect,
|
||||
}) : _firebaseAuth = firebaseAuth,
|
||||
_dataConnect = dataConnect;
|
||||
required dc.DataConnectService service,
|
||||
}) : _service = service;
|
||||
|
||||
final firebase.FirebaseAuth _firebaseAuth;
|
||||
final dc.ExampleConnector _dataConnect;
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
@override
|
||||
Future<List<domain.Hub>> getHubs() async {
|
||||
return _service.run(() async {
|
||||
final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser();
|
||||
final String teamId = await _getOrCreateTeamId(business);
|
||||
return _fetchHubsForTeam(teamId: teamId, businessId: business.id);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -48,6 +44,7 @@ class HubRepositoryImpl
|
||||
String? country,
|
||||
String? zipCode,
|
||||
}) async {
|
||||
return _service.run(() async {
|
||||
final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser();
|
||||
final String teamId = await _getOrCreateTeamId(business);
|
||||
final _PlaceAddress? placeAddress =
|
||||
@@ -59,7 +56,7 @@ class HubRepositoryImpl
|
||||
final String? zipCodeValue = zipCode ?? placeAddress?.zipCode;
|
||||
|
||||
final OperationResult<dc.CreateTeamHubData, dc.CreateTeamHubVariables>
|
||||
result = await executeProtected(() => _dataConnect
|
||||
result = await _service.connector
|
||||
.createTeamHub(
|
||||
teamId: teamId,
|
||||
hubName: name,
|
||||
@@ -73,7 +70,7 @@ class HubRepositoryImpl
|
||||
.street(streetValue)
|
||||
.country(countryValue)
|
||||
.zipCode(zipCodeValue)
|
||||
.execute());
|
||||
.execute();
|
||||
final String createdId = result.data.teamHub_insert.id;
|
||||
|
||||
final List<domain.Hub> hubs = await _fetchHubsForTeam(
|
||||
@@ -96,26 +93,22 @@ class HubRepositoryImpl
|
||||
nfcTagId: null,
|
||||
status: domain.HubStatus.active,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteHub(String id) async {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
throw const BusinessNotFoundException(
|
||||
technicalMessage: 'Business ID missing from session',
|
||||
);
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final QueryResult<dc.ListOrdersByBusinessAndTeamHubData,
|
||||
dc.ListOrdersByBusinessAndTeamHubVariables> result =
|
||||
await executeProtected(() => _dataConnect
|
||||
await _service.connector
|
||||
.listOrdersByBusinessAndTeamHub(
|
||||
businessId: businessId,
|
||||
teamHubId: id,
|
||||
)
|
||||
.execute());
|
||||
.execute();
|
||||
|
||||
if (result.data.orders.isNotEmpty) {
|
||||
throw HubHasOrdersException(
|
||||
@@ -123,7 +116,8 @@ class HubRepositoryImpl
|
||||
);
|
||||
}
|
||||
|
||||
await executeProtected(() => _dataConnect.deleteTeamHub(id: id).execute());
|
||||
await _service.connector.deleteTeamHub(id: id).execute();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -141,7 +135,7 @@ class HubRepositoryImpl
|
||||
return dc.GetBusinessesByUserIdBusinesses(
|
||||
id: cachedBusiness.id,
|
||||
businessName: cachedBusiness.businessName,
|
||||
userId: _firebaseAuth.currentUser?.uid ?? '',
|
||||
userId: _service.auth.currentUser?.uid ?? '',
|
||||
rateGroup: const dc.Known<dc.BusinessRateGroup>(dc.BusinessRateGroup.STANDARD),
|
||||
status: const dc.Known<dc.BusinessStatus>(dc.BusinessStatus.ACTIVE),
|
||||
contactName: cachedBusiness.contactName,
|
||||
@@ -159,7 +153,7 @@ class HubRepositoryImpl
|
||||
);
|
||||
}
|
||||
|
||||
final firebase.User? user = _firebaseAuth.currentUser;
|
||||
final firebase.User? user = _service.auth.currentUser;
|
||||
if (user == null) {
|
||||
throw const NotAuthenticatedException(
|
||||
technicalMessage: 'No Firebase user in currentUser',
|
||||
@@ -168,11 +162,11 @@ class HubRepositoryImpl
|
||||
|
||||
final QueryResult<dc.GetBusinessesByUserIdData,
|
||||
dc.GetBusinessesByUserIdVariables> result =
|
||||
await executeProtected(() => _dataConnect.getBusinessesByUserId(
|
||||
await _service.connector.getBusinessesByUserId(
|
||||
userId: user.uid,
|
||||
).execute());
|
||||
).execute();
|
||||
if (result.data.businesses.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
await _service.auth.signOut();
|
||||
throw BusinessNotFoundException(
|
||||
technicalMessage: 'No business found for user ${user.uid}',
|
||||
);
|
||||
@@ -203,14 +197,14 @@ class HubRepositoryImpl
|
||||
dc.GetBusinessesByUserIdBusinesses business,
|
||||
) async {
|
||||
final QueryResult<dc.GetTeamsByOwnerIdData, dc.GetTeamsByOwnerIdVariables>
|
||||
teamsResult = await executeProtected(() => _dataConnect.getTeamsByOwnerId(
|
||||
teamsResult = await _service.connector.getTeamsByOwnerId(
|
||||
ownerId: business.id,
|
||||
).execute());
|
||||
).execute();
|
||||
if (teamsResult.data.teams.isNotEmpty) {
|
||||
return teamsResult.data.teams.first.id;
|
||||
}
|
||||
|
||||
final dc.CreateTeamVariablesBuilder createTeamBuilder = _dataConnect.createTeam(
|
||||
final dc.CreateTeamVariablesBuilder createTeamBuilder = _service.connector.createTeam(
|
||||
teamName: '${business.businessName} Team',
|
||||
ownerId: business.id,
|
||||
ownerName: business.contactName ?? '',
|
||||
@@ -222,7 +216,7 @@ class HubRepositoryImpl
|
||||
|
||||
final OperationResult<dc.CreateTeamData, dc.CreateTeamVariables>
|
||||
createTeamResult =
|
||||
await executeProtected(() => createTeamBuilder.execute());
|
||||
await createTeamBuilder.execute();
|
||||
final String teamId = createTeamResult.data.team_insert.id;
|
||||
|
||||
return teamId;
|
||||
@@ -234,9 +228,9 @@ class HubRepositoryImpl
|
||||
}) async {
|
||||
final QueryResult<dc.GetTeamHubsByTeamIdData,
|
||||
dc.GetTeamHubsByTeamIdVariables> hubsResult =
|
||||
await executeProtected(() => _dataConnect.getTeamHubsByTeamId(
|
||||
await _service.connector.getTeamHubsByTeamId(
|
||||
teamId: teamId,
|
||||
).execute());
|
||||
).execute();
|
||||
|
||||
return hubsResult.data.teamHubs
|
||||
.map(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||
import 'src/data/repositories_impl/settings_repository_impl.dart';
|
||||
import 'src/domain/repositories/settings_repository_interface.dart';
|
||||
import 'src/domain/usecases/sign_out_usecase.dart';
|
||||
@@ -9,20 +9,19 @@ import 'src/presentation/pages/client_settings_page.dart';
|
||||
|
||||
/// A [Module] for the client settings feature.
|
||||
class ClientSettingsModule extends Module {
|
||||
@override
|
||||
List<Module> get imports => <Module>[DataConnectModule()];
|
||||
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
// Repositories
|
||||
i.addLazySingleton<SettingsRepositoryInterface>(
|
||||
() => SettingsRepositoryImpl(firebaseAuth: FirebaseAuth.instance),
|
||||
);
|
||||
i.addLazySingleton<SettingsRepositoryInterface>(SettingsRepositoryImpl.new);
|
||||
|
||||
// UseCases
|
||||
i.addLazySingleton(SignOutUseCase.new);
|
||||
|
||||
// BLoCs
|
||||
i.add<ClientSettingsBloc>(
|
||||
() => ClientSettingsBloc(signOutUseCase: i.get<SignOutUseCase>()),
|
||||
);
|
||||
i.add<ClientSettingsBloc>(ClientSettingsBloc.new);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
|
||||
import '../../domain/repositories/settings_repository_interface.dart';
|
||||
|
||||
/// Implementation of [SettingsRepositoryInterface].
|
||||
///
|
||||
/// This implementation delegates authentication operations to [FirebaseAuth].
|
||||
/// This implementation delegates authentication operations to [DataConnectService].
|
||||
class SettingsRepositoryImpl implements SettingsRepositoryInterface {
|
||||
/// Creates a [SettingsRepositoryImpl] with the required [_firebaseAuth].
|
||||
const SettingsRepositoryImpl({required this.firebaseAuth});
|
||||
/// Creates a [SettingsRepositoryImpl] with the required [_service].
|
||||
const SettingsRepositoryImpl({required dc.DataConnectService service}) : _service = service;
|
||||
|
||||
/// The Firebase Auth instance.
|
||||
final FirebaseAuth firebaseAuth;
|
||||
/// The Data Connect service.
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
@override
|
||||
Future<void> signOut() async {
|
||||
try {
|
||||
await firebaseAuth.signOut();
|
||||
} catch (e) {
|
||||
throw Exception('Error signing out: ${e.toString()}');
|
||||
}
|
||||
return _service.run(() async {
|
||||
await _service.auth.signOut();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
@@ -6,53 +5,42 @@ import 'package:krow_domain/krow_domain.dart' as domain;
|
||||
import '../../domain/repositories/i_view_orders_repository.dart';
|
||||
|
||||
/// Implementation of [IViewOrdersRepository] using Data Connect.
|
||||
class ViewOrdersRepositoryImpl
|
||||
with dc.DataErrorHandler
|
||||
implements IViewOrdersRepository {
|
||||
final firebase.FirebaseAuth _firebaseAuth;
|
||||
final dc.ExampleConnector _dataConnect;
|
||||
class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
ViewOrdersRepositoryImpl({
|
||||
required firebase.FirebaseAuth firebaseAuth,
|
||||
required dc.ExampleConnector dataConnect,
|
||||
}) : _firebaseAuth = firebaseAuth,
|
||||
_dataConnect = dataConnect;
|
||||
required dc.DataConnectService service,
|
||||
}) : _service = service;
|
||||
|
||||
@override
|
||||
Future<List<domain.OrderItem>> getOrdersForRange({
|
||||
required DateTime start,
|
||||
required DateTime end,
|
||||
}) async {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
throw Exception('Business is missing. Please sign in again.');
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final fdc.Timestamp startTimestamp = _toTimestamp(_startOfDay(start));
|
||||
final fdc.Timestamp endTimestamp = _toTimestamp(_endOfDay(end));
|
||||
final fdc.Timestamp startTimestamp = _service.toTimestamp(_startOfDay(start));
|
||||
final fdc.Timestamp endTimestamp = _service.toTimestamp(_endOfDay(end));
|
||||
final fdc.QueryResult<dc.ListShiftRolesByBusinessAndDateRangeData,
|
||||
dc.ListShiftRolesByBusinessAndDateRangeVariables> result =
|
||||
await executeProtected(() => _dataConnect
|
||||
await _service.connector
|
||||
.listShiftRolesByBusinessAndDateRange(
|
||||
businessId: businessId,
|
||||
start: startTimestamp,
|
||||
end: endTimestamp,
|
||||
)
|
||||
.execute());
|
||||
.execute();
|
||||
print(
|
||||
'ViewOrders range start=${start.toIso8601String()} end=${end.toIso8601String()} shiftRoles=${result.data.shiftRoles.length}',
|
||||
);
|
||||
|
||||
final String businessName =
|
||||
dc.ClientSessionStore.instance.session?.business?.businessName ??
|
||||
'Your Company';
|
||||
dc.ClientSessionStore.instance.session?.business?.businessName ?? 'Your Company';
|
||||
|
||||
return result.data.shiftRoles.map((dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole) {
|
||||
final DateTime? shiftDate = shiftRole.shift.date?.toDateTime().toLocal();
|
||||
final String dateStr = shiftDate == null
|
||||
? ''
|
||||
: DateFormat('yyyy-MM-dd').format(shiftDate);
|
||||
final String dateStr = shiftDate == null ? '' : DateFormat('yyyy-MM-dd').format(shiftDate);
|
||||
final String startTime = _formatTime(shiftRole.startTime);
|
||||
final String endTime = _formatTime(shiftRole.endTime);
|
||||
final int filled = shiftRole.assigned ?? 0;
|
||||
@@ -69,8 +57,7 @@ class ViewOrdersRepositoryImpl
|
||||
'end=${shiftRole.endTime?.toJson()} hours=$hours totalValue=$totalValue',
|
||||
);
|
||||
|
||||
final String eventName =
|
||||
shiftRole.shift.order.eventName ?? shiftRole.shift.title;
|
||||
final String eventName = shiftRole.shift.order.eventName ?? shiftRole.shift.title;
|
||||
|
||||
return domain.OrderItem(
|
||||
id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId),
|
||||
@@ -91,36 +78,35 @@ class ViewOrdersRepositoryImpl
|
||||
confirmedApps: const <Map<String, dynamic>>[],
|
||||
);
|
||||
}).toList();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, List<Map<String, dynamic>>>> getAcceptedApplicationsForDay(
|
||||
DateTime day,
|
||||
) async {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
throw Exception('Business is missing. Please sign in again.');
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final fdc.Timestamp dayStart = _toTimestamp(_startOfDay(day));
|
||||
final fdc.Timestamp dayEnd = _toTimestamp(_endOfDay(day));
|
||||
final fdc.Timestamp dayStart = _service.toTimestamp(_startOfDay(day));
|
||||
final fdc.Timestamp dayEnd = _service.toTimestamp(_endOfDay(day));
|
||||
final fdc.QueryResult<dc.ListAcceptedApplicationsByBusinessForDayData,
|
||||
dc.ListAcceptedApplicationsByBusinessForDayVariables> result =
|
||||
await executeProtected(() => _dataConnect
|
||||
await _service.connector
|
||||
.listAcceptedApplicationsByBusinessForDay(
|
||||
businessId: businessId,
|
||||
dayStart: dayStart,
|
||||
dayEnd: dayEnd,
|
||||
)
|
||||
.execute());
|
||||
.execute();
|
||||
|
||||
print(
|
||||
'ViewOrders day=${day.toIso8601String()} applications=${result.data.applications.length}',
|
||||
);
|
||||
|
||||
final Map<String, List<Map<String, dynamic>>> grouped = <String, List<Map<String, dynamic>>>{};
|
||||
for (final dc.ListAcceptedApplicationsByBusinessForDayApplications application in result.data.applications) {
|
||||
for (final dc.ListAcceptedApplicationsByBusinessForDayApplications application
|
||||
in result.data.applications) {
|
||||
print(
|
||||
'ViewOrders app: shiftId=${application.shiftId} roleId=${application.roleId} '
|
||||
'checkIn=${application.checkInTime?.toJson()} checkOut=${application.checkOutTime?.toJson()}',
|
||||
@@ -138,19 +124,13 @@ class ViewOrdersRepositoryImpl
|
||||
});
|
||||
}
|
||||
return grouped;
|
||||
});
|
||||
}
|
||||
|
||||
String _shiftRoleKey(String shiftId, String roleId) {
|
||||
return '$shiftId:$roleId';
|
||||
}
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||
final DateTime utc = dateTime.toUtc();
|
||||
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
||||
final int nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000;
|
||||
return fdc.Timestamp(nanoseconds, seconds);
|
||||
}
|
||||
|
||||
DateTime _startOfDay(DateTime dateTime) {
|
||||
return DateTime(dateTime.year, dateTime.month, dateTime.day);
|
||||
}
|
||||
|
||||
@@ -80,23 +80,6 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats the date string for display.
|
||||
String _formatDate({required String dateStr}) {
|
||||
try {
|
||||
final DateTime date = DateTime.parse(dateStr);
|
||||
final DateTime now = DateTime.now();
|
||||
final DateTime today = DateTime(now.year, now.month, now.day);
|
||||
final DateTime tomorrow = today.add(const Duration(days: 1));
|
||||
final DateTime checkDate = DateTime(date.year, date.month, date.day);
|
||||
|
||||
if (checkDate == today) return t.client_view_orders.card.today;
|
||||
if (checkDate == tomorrow) return t.client_view_orders.card.tomorrow;
|
||||
return DateFormat('EEE, MMM d').format(date);
|
||||
} catch (_) {
|
||||
return dateStr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats the time string for display.
|
||||
String _formatTime({required String timeStr}) {
|
||||
if (timeStr.isEmpty) return '';
|
||||
@@ -829,10 +812,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
final String dateText = orderDate == null
|
||||
? widget.order.date
|
||||
: DateFormat('yyyy-MM-dd').format(orderDate);
|
||||
final String location = firstShift.order.teamHub?.hubName ??
|
||||
firstShift.locationAddress ??
|
||||
firstShift.location ??
|
||||
widget.order.locationAddress;
|
||||
final String location = firstShift.order.teamHub.hubName;
|
||||
|
||||
_dateController.text = dateText;
|
||||
_globalLocationController.text = location;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
|
||||
import 'data/repositories/view_orders_repository_impl.dart';
|
||||
import 'domain/repositories/i_view_orders_repository.dart';
|
||||
@@ -21,24 +20,14 @@ class ViewOrdersModule extends Module {
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
// Repositories
|
||||
i.add<IViewOrdersRepository>(
|
||||
() => ViewOrdersRepositoryImpl(
|
||||
firebaseAuth: firebase.FirebaseAuth.instance,
|
||||
dataConnect: ExampleConnector.instance,
|
||||
),
|
||||
);
|
||||
i.add<IViewOrdersRepository>(ViewOrdersRepositoryImpl.new);
|
||||
|
||||
// UseCases
|
||||
i.add(GetOrdersUseCase.new);
|
||||
i.add(GetAcceptedApplicationsForDayUseCase.new);
|
||||
|
||||
// BLoCs
|
||||
i.add(
|
||||
() => ViewOrdersCubit(
|
||||
getOrdersUseCase: i.get<GetOrdersUseCase>(),
|
||||
getAcceptedAppsUseCase: i.get<GetAcceptedApplicationsForDayUseCase>(),
|
||||
),
|
||||
);
|
||||
i.add(ViewOrdersCubit.new);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user