feat: Update API endpoint usage in repositories to remove redundant path property

- Refactored multiple repository implementations across client and staff features to directly use endpoint objects without accessing the `path` property.
- Introduced a new `FeatureGate` class for client-side feature gating based on user scopes, allowing for better access control to API endpoints.
- Added `ApiEndpoint` class to represent API endpoints with their paths and required scopes for future feature gating.
This commit is contained in:
Achintha Isuru
2026-03-17 12:01:06 -04:00
parent 57bba8ab4e
commit 376b4e4431
47 changed files with 240 additions and 139 deletions

View File

@@ -45,7 +45,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
// Step 1: Call V2 sign-in endpoint — server handles Firebase Auth
// via Identity Toolkit and returns a full auth envelope.
final ApiResponse response = await _apiService.post(
AuthEndpoints.clientSignIn.path,
AuthEndpoints.clientSignIn,
data: <String, dynamic>{
'email': email,
'password': password,
@@ -107,7 +107,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
// - Creates user, tenant, business, memberships in one transaction
// - Returns full auth envelope with session tokens
final ApiResponse response = await _apiService.post(
AuthEndpoints.clientSignUp.path,
AuthEndpoints.clientSignUp,
data: <String, dynamic>{
'companyName': companyName,
'email': email,
@@ -172,7 +172,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
Future<void> signOut() async {
try {
// Step 1: Call V2 sign-out endpoint for server-side token revocation.
await _apiService.post(AuthEndpoints.clientSignOut.path);
await _apiService.post(AuthEndpoints.clientSignOut);
} catch (e) {
developer.log(
'V2 sign-out request failed: $e',

View File

@@ -17,7 +17,7 @@ class BillingRepositoryImpl implements BillingRepository {
@override
Future<List<BillingAccount>> getBankAccounts() async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.billingAccounts.path);
await _apiService.get(ClientEndpoints.billingAccounts);
final List<dynamic> items =
(response.data as Map<String, dynamic>)['items'] as List<dynamic>;
return items
@@ -29,7 +29,7 @@ class BillingRepositoryImpl implements BillingRepository {
@override
Future<List<Invoice>> getPendingInvoices() async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.billingInvoicesPending.path);
await _apiService.get(ClientEndpoints.billingInvoicesPending);
final List<dynamic> items =
(response.data as Map<String, dynamic>)['items'] as List<dynamic>;
return items
@@ -41,7 +41,7 @@ class BillingRepositoryImpl implements BillingRepository {
@override
Future<List<Invoice>> getInvoiceHistory() async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.billingInvoicesHistory.path);
await _apiService.get(ClientEndpoints.billingInvoicesHistory);
final List<dynamic> items =
(response.data as Map<String, dynamic>)['items'] as List<dynamic>;
return items
@@ -53,7 +53,7 @@ class BillingRepositoryImpl implements BillingRepository {
@override
Future<int> getCurrentBillCents() async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.billingCurrentBill.path);
await _apiService.get(ClientEndpoints.billingCurrentBill);
final Map<String, dynamic> data =
response.data as Map<String, dynamic>;
return (data['currentBillCents'] as num).toInt();
@@ -62,7 +62,7 @@ class BillingRepositoryImpl implements BillingRepository {
@override
Future<int> getSavingsCents() async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.billingSavings.path);
await _apiService.get(ClientEndpoints.billingSavings);
final Map<String, dynamic> data =
response.data as Map<String, dynamic>;
return (data['savingsCents'] as num).toInt();
@@ -74,7 +74,7 @@ class BillingRepositoryImpl implements BillingRepository {
required String endDate,
}) async {
final ApiResponse response = await _apiService.get(
ClientEndpoints.billingSpendBreakdown.path,
ClientEndpoints.billingSpendBreakdown,
params: <String, dynamic>{
'startDate': startDate,
'endDate': endDate,
@@ -90,13 +90,13 @@ class BillingRepositoryImpl implements BillingRepository {
@override
Future<void> approveInvoice(String id) async {
await _apiService.post(ClientEndpoints.invoiceApprove(id).path);
await _apiService.post(ClientEndpoints.invoiceApprove(id));
}
@override
Future<void> disputeInvoice(String id, String reason) async {
await _apiService.post(
ClientEndpoints.invoiceDispute(id).path,
ClientEndpoints.invoiceDispute(id),
data: <String, dynamic>{'reason': reason},
);
}

View File

@@ -20,7 +20,7 @@ class CoverageRepositoryImpl implements CoverageRepository {
final String dateStr =
'${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
final ApiResponse response = await _apiService.get(
ClientEndpoints.coverage.path,
ClientEndpoints.coverage,
params: <String, dynamic>{'date': dateStr},
);
final List<dynamic> items = response.data['items'] as List<dynamic>;
@@ -35,7 +35,7 @@ class CoverageRepositoryImpl implements CoverageRepository {
final String dateStr =
'${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
final ApiResponse response = await _apiService.get(
ClientEndpoints.coverageStats.path,
ClientEndpoints.coverageStats,
params: <String, dynamic>{'date': dateStr},
);
return CoverageStats.fromJson(response.data as Map<String, dynamic>);
@@ -67,7 +67,7 @@ class CoverageRepositoryImpl implements CoverageRepository {
body['markAsFavorite'] = markAsFavorite;
}
await _apiService.post(
ClientEndpoints.coverageReviews.path,
ClientEndpoints.coverageReviews,
data: body,
);
}
@@ -82,7 +82,7 @@ class CoverageRepositoryImpl implements CoverageRepository {
body['reason'] = reason;
}
await _apiService.post(
ClientEndpoints.coverageCancelLateWorker(assignmentId).path,
ClientEndpoints.coverageCancelLateWorker(assignmentId),
data: body,
);
}

View File

@@ -18,7 +18,7 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
@override
Future<ClientDashboard> getDashboard() async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.dashboard.path);
await _apiService.get(ClientEndpoints.dashboard);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
return ClientDashboard.fromJson(data);
}
@@ -26,7 +26,7 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
@override
Future<List<RecentOrder>> getRecentReorders() async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.reorders.path);
await _apiService.get(ClientEndpoints.reorders);
final Map<String, dynamic> body = response.data as Map<String, dynamic>;
final List<dynamic> items = body['items'] as List<dynamic>;
return items

View File

@@ -17,7 +17,7 @@ class HubRepositoryImpl implements HubRepositoryInterface {
@override
Future<List<Hub>> getHubs() async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.hubs.path);
await _apiService.get(ClientEndpoints.hubs);
final List<dynamic> items =
(response.data as Map<String, dynamic>)['items'] as List<dynamic>;
return items
@@ -28,7 +28,7 @@ class HubRepositoryImpl implements HubRepositoryInterface {
@override
Future<List<CostCenter>> getCostCenters() async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.costCenters.path);
await _apiService.get(ClientEndpoints.costCenters);
final List<dynamic> items =
(response.data as Map<String, dynamic>)['items'] as List<dynamic>;
return items
@@ -52,7 +52,7 @@ class HubRepositoryImpl implements HubRepositoryInterface {
String? costCenterId,
}) async {
final ApiResponse response = await _apiService.post(
ClientEndpoints.hubCreate.path,
ClientEndpoints.hubCreate,
data: <String, dynamic>{
'name': name,
'fullAddress': fullAddress,
@@ -88,7 +88,7 @@ class HubRepositoryImpl implements HubRepositoryInterface {
String? costCenterId,
}) async {
final ApiResponse response = await _apiService.put(
ClientEndpoints.hubUpdate(hubId).path,
ClientEndpoints.hubUpdate(hubId),
data: <String, dynamic>{
'hubId': hubId,
if (name != null) 'name': name,
@@ -111,7 +111,7 @@ class HubRepositoryImpl implements HubRepositoryInterface {
@override
Future<void> deleteHub(String hubId) async {
await _apiService.delete(ClientEndpoints.hubDelete(hubId).path);
await _apiService.delete(ClientEndpoints.hubDelete(hubId));
}
@override
@@ -120,7 +120,7 @@ class HubRepositoryImpl implements HubRepositoryInterface {
required String nfcTagId,
}) async {
await _apiService.post(
ClientEndpoints.hubAssignNfc(hubId).path,
ClientEndpoints.hubAssignNfc(hubId),
data: <String, dynamic>{'nfcTagId': nfcTagId},
);
}
@@ -128,7 +128,7 @@ class HubRepositoryImpl implements HubRepositoryInterface {
@override
Future<List<HubManager>> getManagers(String hubId) async {
final ApiResponse response =
await _apiService.get(ClientEndpoints.hubManagers(hubId).path);
await _apiService.get(ClientEndpoints.hubManagers(hubId));
final List<dynamic> items =
(response.data as Map<String, dynamic>)['items'] as List<dynamic>;
return items
@@ -143,7 +143,7 @@ class HubRepositoryImpl implements HubRepositoryInterface {
required List<String> businessMembershipIds,
}) async {
await _apiService.post(
ClientEndpoints.hubAssignManagers(hubId).path,
ClientEndpoints.hubAssignManagers(hubId),
data: <String, dynamic>{
'businessMembershipIds': businessMembershipIds,
},

View File

@@ -24,17 +24,17 @@ class ClientCreateOrderRepositoryImpl
@override
Future<void> createOneTimeOrder(Map<String, dynamic> payload) async {
await _api.post(ClientEndpoints.ordersOneTime.path, data: payload);
await _api.post(ClientEndpoints.ordersOneTime, data: payload);
}
@override
Future<void> createRecurringOrder(Map<String, dynamic> payload) async {
await _api.post(ClientEndpoints.ordersRecurring.path, data: payload);
await _api.post(ClientEndpoints.ordersRecurring, data: payload);
}
@override
Future<void> createPermanentOrder(Map<String, dynamic> payload) async {
await _api.post(ClientEndpoints.ordersPermanent.path, data: payload);
await _api.post(ClientEndpoints.ordersPermanent, data: payload);
}
@override
@@ -82,7 +82,7 @@ class ClientCreateOrderRepositoryImpl
@override
Future<OrderPreview> getOrderDetailsForReorder(String orderId) async {
final ApiResponse response = await _api.get(
ClientEndpoints.orderReorderPreview(orderId).path,
ClientEndpoints.orderReorderPreview(orderId),
);
return OrderPreview.fromJson(response.data as Map<String, dynamic>);
}

View File

@@ -19,7 +19,7 @@ class ClientOrderQueryRepositoryImpl
@override
Future<List<Vendor>> getVendors() async {
final ApiResponse response = await _api.get(ClientEndpoints.vendors.path);
final ApiResponse response = await _api.get(ClientEndpoints.vendors);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
final List<dynamic> items = data['items'] as List<dynamic>;
return items
@@ -30,7 +30,7 @@ class ClientOrderQueryRepositoryImpl
@override
Future<List<OrderRole>> getRolesByVendor(String vendorId) async {
final ApiResponse response =
await _api.get(ClientEndpoints.vendorRoles(vendorId).path);
await _api.get(ClientEndpoints.vendorRoles(vendorId));
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
final List<dynamic> items = data['items'] as List<dynamic>;
return items.map((dynamic json) {
@@ -46,7 +46,7 @@ class ClientOrderQueryRepositoryImpl
@override
Future<List<OrderHub>> getHubs() async {
final ApiResponse response = await _api.get(ClientEndpoints.hubs.path);
final ApiResponse response = await _api.get(ClientEndpoints.hubs);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
final List<dynamic> items = data['items'] as List<dynamic>;
return items.map((dynamic json) {
@@ -71,7 +71,7 @@ class ClientOrderQueryRepositoryImpl
@override
Future<List<OrderManager>> getManagersByHub(String hubId) async {
final ApiResponse response =
await _api.get(ClientEndpoints.hubManagers(hubId).path);
await _api.get(ClientEndpoints.hubManagers(hubId));
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
final List<dynamic> items = data['items'] as List<dynamic>;
return items.map((dynamic json) {

View File

@@ -20,7 +20,7 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
required DateTime end,
}) async {
final ApiResponse response = await _api.get(
ClientEndpoints.ordersView.path,
ClientEndpoints.ordersView,
params: <String, dynamic>{
'startDate': start.toIso8601String(),
'endDate': end.toIso8601String(),
@@ -40,7 +40,7 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
required Map<String, dynamic> payload,
}) async {
final ApiResponse response = await _api.post(
ClientEndpoints.orderEdit(orderId).path,
ClientEndpoints.orderEdit(orderId),
data: payload,
);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
@@ -53,7 +53,7 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
String? reason,
}) async {
await _api.post(
ClientEndpoints.orderCancel(orderId).path,
ClientEndpoints.orderCancel(orderId),
data: <String, dynamic>{
if (reason != null) 'reason': reason,
},
@@ -62,7 +62,7 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
@override
Future<List<Vendor>> getVendors() async {
final ApiResponse response = await _api.get(ClientEndpoints.vendors.path);
final ApiResponse response = await _api.get(ClientEndpoints.vendors);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
final List<dynamic> items = data['items'] as List<dynamic>;
return items
@@ -73,7 +73,7 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
@override
Future<List<Map<String, dynamic>>> getRolesByVendor(String vendorId) async {
final ApiResponse response =
await _api.get(ClientEndpoints.vendorRoles(vendorId).path);
await _api.get(ClientEndpoints.vendorRoles(vendorId));
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
final List<dynamic> items = data['items'] as List<dynamic>;
return items.cast<Map<String, dynamic>>();
@@ -81,7 +81,7 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
@override
Future<List<Map<String, dynamic>>> getHubs() async {
final ApiResponse response = await _api.get(ClientEndpoints.hubs.path);
final ApiResponse response = await _api.get(ClientEndpoints.hubs);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
final List<dynamic> items = data['items'] as List<dynamic>;
return items.cast<Map<String, dynamic>>();
@@ -90,7 +90,7 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
@override
Future<List<Map<String, dynamic>>> getManagersByHub(String hubId) async {
final ApiResponse response =
await _api.get(ClientEndpoints.hubManagers(hubId).path);
await _api.get(ClientEndpoints.hubManagers(hubId));
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
final List<dynamic> items = data['items'] as List<dynamic>;
return items.cast<Map<String, dynamic>>();

View File

@@ -32,7 +32,7 @@ class ReportsRepositoryImpl implements ReportsRepository {
required DateTime date,
}) async {
final ApiResponse response = await _apiService.get(
ClientEndpoints.reportsDailyOps.path,
ClientEndpoints.reportsDailyOps,
params: <String, dynamic>{'date': _iso(date)},
);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
@@ -45,7 +45,7 @@ class ReportsRepositoryImpl implements ReportsRepository {
required DateTime endDate,
}) async {
final ApiResponse response = await _apiService.get(
ClientEndpoints.reportsSpend.path,
ClientEndpoints.reportsSpend,
params: _rangeParams(startDate, endDate),
);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
@@ -58,7 +58,7 @@ class ReportsRepositoryImpl implements ReportsRepository {
required DateTime endDate,
}) async {
final ApiResponse response = await _apiService.get(
ClientEndpoints.reportsCoverage.path,
ClientEndpoints.reportsCoverage,
params: _rangeParams(startDate, endDate),
);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
@@ -71,7 +71,7 @@ class ReportsRepositoryImpl implements ReportsRepository {
required DateTime endDate,
}) async {
final ApiResponse response = await _apiService.get(
ClientEndpoints.reportsForecast.path,
ClientEndpoints.reportsForecast,
params: _rangeParams(startDate, endDate),
);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
@@ -84,7 +84,7 @@ class ReportsRepositoryImpl implements ReportsRepository {
required DateTime endDate,
}) async {
final ApiResponse response = await _apiService.get(
ClientEndpoints.reportsPerformance.path,
ClientEndpoints.reportsPerformance,
params: _rangeParams(startDate, endDate),
);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
@@ -97,7 +97,7 @@ class ReportsRepositoryImpl implements ReportsRepository {
required DateTime endDate,
}) async {
final ApiResponse response = await _apiService.get(
ClientEndpoints.reportsNoShow.path,
ClientEndpoints.reportsNoShow,
params: _rangeParams(startDate, endDate),
);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;
@@ -110,7 +110,7 @@ class ReportsRepositoryImpl implements ReportsRepository {
required DateTime endDate,
}) async {
final ApiResponse response = await _apiService.get(
ClientEndpoints.reportsSummary.path,
ClientEndpoints.reportsSummary,
params: _rangeParams(startDate, endDate),
);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;

View File

@@ -22,7 +22,7 @@ class SettingsRepositoryImpl implements SettingsRepositoryInterface {
Future<void> signOut() async {
try {
// Step 1: Call V2 sign-out endpoint for server-side token revocation.
await _apiService.post(AuthEndpoints.clientSignOut.path);
await _apiService.post(AuthEndpoints.clientSignOut);
} catch (e) {
developer.log(
'V2 sign-out request failed: $e',