Improve the frontend modals

This commit is contained in:
Urtzi Alfaro
2025-10-27 16:33:26 +01:00
parent 61376b7a9f
commit 858d985c92
143 changed files with 9289 additions and 2306 deletions

View File

@@ -77,16 +77,16 @@ export class AuthService {
}
// ===================================================================
// ATOMIC: User Profile
// Backend: services/auth/app/api/users.py
// User Profile (authenticated)
// Backend: services/auth/app/api/auth_operations.py
// ===================================================================
async getProfile(): Promise<UserResponse> {
return apiClient.get<UserResponse>('/users/me');
return apiClient.get<UserResponse>(`${this.baseUrl}/me`);
}
async updateProfile(updateData: UserUpdate): Promise<UserResponse> {
return apiClient.put<UserResponse>('/users/me', updateData);
return apiClient.put<UserResponse>(`${this.baseUrl}/me`, updateData);
}
// ===================================================================
@@ -104,6 +104,106 @@ export class AuthService {
});
}
// ===================================================================
// Account Management (self-service)
// Backend: services/auth/app/api/account_deletion.py
// ===================================================================
async deleteAccount(confirmEmail: string, password: string, reason?: string): Promise<{ message: string; deletion_date: string }> {
return apiClient.delete(`${this.baseUrl}/me/account`, {
data: {
confirm_email: confirmEmail,
password: password,
reason: reason || ''
}
});
}
async getAccountDeletionInfo(): Promise<any> {
return apiClient.get(`${this.baseUrl}/me/account/deletion-info`);
}
// ===================================================================
// GDPR Consent Management
// Backend: services/auth/app/api/consent.py
// ===================================================================
async recordConsent(consentData: {
terms_accepted: boolean;
privacy_accepted: boolean;
marketing_consent?: boolean;
analytics_consent?: boolean;
consent_method: string;
consent_version?: string;
}): Promise<any> {
return apiClient.post(`${this.baseUrl}/me/consent`, consentData);
}
async getCurrentConsent(): Promise<any> {
return apiClient.get(`${this.baseUrl}/me/consent/current`);
}
async getConsentHistory(): Promise<any[]> {
return apiClient.get(`${this.baseUrl}/me/consent/history`);
}
async updateConsent(consentData: {
terms_accepted: boolean;
privacy_accepted: boolean;
marketing_consent?: boolean;
analytics_consent?: boolean;
consent_method: string;
consent_version?: string;
}): Promise<any> {
return apiClient.put(`${this.baseUrl}/me/consent`, consentData);
}
async withdrawConsent(): Promise<{ message: string; withdrawn_count: number }> {
return apiClient.post(`${this.baseUrl}/me/consent/withdraw`);
}
// ===================================================================
// Data Export (GDPR)
// Backend: services/auth/app/api/data_export.py
// ===================================================================
async exportMyData(): Promise<any> {
return apiClient.get(`${this.baseUrl}/me/export`);
}
async getExportSummary(): Promise<any> {
return apiClient.get(`${this.baseUrl}/me/export/summary`);
}
// ===================================================================
// Onboarding Progress
// Backend: services/auth/app/api/onboarding_progress.py
// ===================================================================
async getOnboardingProgress(): Promise<any> {
return apiClient.get(`${this.baseUrl}/me/onboarding/progress`);
}
async updateOnboardingStep(stepName: string, completed: boolean, data?: any): Promise<any> {
return apiClient.put(`${this.baseUrl}/me/onboarding/step`, {
step_name: stepName,
completed: completed,
data: data
});
}
async getNextOnboardingStep(): Promise<{ step: string }> {
return apiClient.get(`${this.baseUrl}/me/onboarding/next-step`);
}
async canAccessOnboardingStep(stepName: string): Promise<{ can_access: boolean }> {
return apiClient.get(`${this.baseUrl}/me/onboarding/can-access/${stepName}`);
}
async completeOnboarding(): Promise<{ success: boolean; message: string }> {
return apiClient.post(`${this.baseUrl}/me/onboarding/complete`);
}
// ===================================================================
// Health Check
// ===================================================================

View File

@@ -82,9 +82,10 @@ export class OrdersService {
* Create a new customer order
* POST /tenants/{tenant_id}/orders
*/
static async createOrder(orderData: OrderCreate): Promise<OrderResponse> {
const { tenant_id, ...data } = orderData;
return apiClient.post<OrderResponse>(`/tenants/${tenant_id}/orders`, data);
static async createOrder(orderData: OrderCreate): Promise<OrderResponse> {
const { tenant_id } = orderData;
// Note: tenant_id is in both URL path and request body (backend schema requirement)
return apiClient.post<OrderResponse>(`/tenants/${tenant_id}/orders`, orderData);
}
/**
@@ -144,11 +145,11 @@ export class OrdersService {
/**
* Create a new customer
* POST /tenants/{tenant_id}/customers
* POST /tenants/{tenant_id}/orders/customers
*/
static async createCustomer(customerData: CustomerCreate): Promise<CustomerResponse> {
const { tenant_id, ...data } = customerData;
return apiClient.post<CustomerResponse>(`/tenants/${tenant_id}/customers`, data);
return apiClient.post<CustomerResponse>(`/tenants/${tenant_id}/orders/customers`, data);
}
/**
@@ -413,4 +414,4 @@ export class OrdersService {
}
export default OrdersService;
export default OrdersService;

View File

@@ -594,4 +594,4 @@ export class POSService {
// Export singleton instance
export const posService = new POSService();
export default posService;
export default posService;

View File

@@ -24,6 +24,7 @@ import type {
RecipeCategoriesResponse,
RecipeQualityConfiguration,
RecipeQualityConfigurationUpdate,
RecipeDeletionSummary,
} from '../types/recipes';
export class RecipesService {
@@ -94,6 +95,22 @@ export class RecipesService {
return apiClient.delete<{ message: string }>(`${this.baseUrl}/${tenantId}/recipes/${recipeId}`);
}
/**
* Archive a recipe (soft delete by setting status to ARCHIVED)
* PATCH /tenants/{tenant_id}/recipes/{recipe_id}/archive
*/
async archiveRecipe(tenantId: string, recipeId: string): Promise<RecipeResponse> {
return apiClient.patch<RecipeResponse>(`${this.baseUrl}/${tenantId}/recipes/${recipeId}/archive`);
}
/**
* Get deletion summary for a recipe
* GET /tenants/{tenant_id}/recipes/{recipe_id}/deletion-summary
*/
async getRecipeDeletionSummary(tenantId: string, recipeId: string): Promise<RecipeDeletionSummary> {
return apiClient.get<RecipeDeletionSummary>(`${this.baseUrl}/${tenantId}/recipes/${recipeId}/deletion-summary`);
}
// ===================================================================
// ATOMIC: Quality Configuration CRUD
// Backend: services/recipes/app/api/recipe_quality_configs.py

View File

@@ -187,21 +187,48 @@ export class SubscriptionService {
/**
* Check if tenant can perform an action within quota limits
*/
async checkQuotaLimit(
async checkQuotaLimit(
tenantId: string,
quotaType: string,
requestedAmount?: number
): Promise<QuotaCheckResponse> {
const queryParams = new URLSearchParams();
if (requestedAmount !== undefined) {
queryParams.append('requested_amount', requestedAmount.toString());
// Map quotaType to the existing endpoints in tenant_operations.py
let endpoint: string;
switch (quotaType) {
case 'inventory_items':
endpoint = 'can-add-product';
break;
case 'users':
endpoint = 'can-add-user';
break;
case 'locations':
endpoint = 'can-add-location';
break;
default:
throw new Error(`Unsupported quota type: ${quotaType}`);
}
const url = queryParams.toString()
? `${this.baseUrl}/subscriptions/${tenantId}/quotas/${quotaType}/check?${queryParams.toString()}`
: `${this.baseUrl}/subscriptions/${tenantId}/quotas/${quotaType}/check`;
return apiClient.get<QuotaCheckResponse>(url);
const url = `${this.baseUrl}/subscriptions/${tenantId}/${endpoint}`;
// Get the response from the endpoint (returns different format than expected)
const response = await apiClient.get<{
can_add: boolean;
current_count?: number;
max_allowed?: number;
reason?: string;
message?: string;
}>(url);
// Map the response to QuotaCheckResponse format
return {
allowed: response.can_add,
current: response.current_count || 0,
limit: response.max_allowed || null,
remaining: response.max_allowed !== undefined && response.current_count !== undefined
? response.max_allowed - response.current_count
: null,
message: response.reason || response.message || ''
};
}
async validatePlanUpgrade(tenantId: string, planKey: string): Promise<PlanUpgradeValidation> {
@@ -348,4 +375,4 @@ export class SubscriptionService {
}
}
export const subscriptionService = new SubscriptionService();
export const subscriptionService = new SubscriptionService();

View File

@@ -22,6 +22,7 @@ import type {
SupplierApproval,
SupplierQueryParams,
SupplierStatistics,
SupplierDeletionSummary,
TopSuppliersResponse,
PurchaseOrderCreate,
PurchaseOrderUpdate,
@@ -53,7 +54,7 @@ class SuppliersService {
supplierData: SupplierCreate
): Promise<SupplierResponse> {
return apiClient.post<SupplierResponse>(
`${this.baseUrl}/${tenantId}/suppliers/suppliers`,
`${this.baseUrl}/${tenantId}/suppliers`,
supplierData
);
}
@@ -74,13 +75,13 @@ class SuppliersService {
const queryString = params.toString() ? `?${params.toString()}` : '';
return apiClient.get<PaginatedResponse<SupplierSummary>>(
`${this.baseUrl}/${tenantId}/suppliers/suppliers${queryString}`
`${this.baseUrl}/${tenantId}/suppliers${queryString}`
);
}
async getSupplier(tenantId: string, supplierId: string): Promise<SupplierResponse> {
return apiClient.get<SupplierResponse>(
`${this.baseUrl}/${tenantId}/suppliers/suppliers/${supplierId}`
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}`
);
}
@@ -90,7 +91,7 @@ class SuppliersService {
updateData: SupplierUpdate
): Promise<SupplierResponse> {
return apiClient.put<SupplierResponse>(
`${this.baseUrl}/${tenantId}/suppliers/suppliers/${supplierId}`,
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}`,
updateData
);
}
@@ -100,7 +101,16 @@ class SuppliersService {
supplierId: string
): Promise<{ message: string }> {
return apiClient.delete<{ message: string }>(
`${this.baseUrl}/${tenantId}/suppliers/suppliers/${supplierId}`
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}`
);
}
async hardDeleteSupplier(
tenantId: string,
supplierId: string
): Promise<SupplierDeletionSummary> {
return apiClient.delete<SupplierDeletionSummary>(
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}/hard`
);
}
@@ -113,7 +123,7 @@ class SuppliersService {
params.append('is_active', isActive.toString());
return apiClient.get<Array<{ inventory_product_id: string }>>(
`${this.baseUrl}/${tenantId}/suppliers/suppliers/${supplierId}/products?${params.toString()}`
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}/products?${params.toString()}`
);
}

View File

@@ -32,6 +32,10 @@ export class UserService {
async getUserById(userId: string): Promise<UserResponse> {
return apiClient.get<UserResponse>(`${this.baseUrl}/admin/${userId}`);
}
async getUserActivity(userId: string): Promise<any> {
return apiClient.get<any>(`/auth/users/${userId}/activity`);
}
}
export const userService = new UserService();
export const userService = new UserService();