184 lines
7.4 KiB
TypeScript
184 lines
7.4 KiB
TypeScript
// ================================================================
|
|
// frontend/src/api/services/tenant.ts
|
|
// ================================================================
|
|
/**
|
|
* Tenant Service - Complete backend alignment
|
|
*
|
|
* Backend API structure (3-tier architecture):
|
|
* - ATOMIC: tenants.py, tenant_members.py
|
|
* - OPERATIONS: tenant_operations.py
|
|
*
|
|
* Last Updated: 2025-10-05
|
|
* Status: ✅ Complete - Zero drift with backend
|
|
*/
|
|
import { apiClient } from '../client';
|
|
import {
|
|
BakeryRegistration,
|
|
TenantResponse,
|
|
TenantAccessResponse,
|
|
TenantUpdate,
|
|
TenantMemberResponse,
|
|
TenantStatistics,
|
|
TenantSearchParams,
|
|
TenantNearbyParams,
|
|
} from '../types/tenant';
|
|
|
|
export class TenantService {
|
|
private readonly baseUrl = '/tenants';
|
|
|
|
// ===================================================================
|
|
// ATOMIC: Tenant CRUD
|
|
// Backend: services/tenant/app/api/tenants.py
|
|
// ===================================================================
|
|
async registerBakery(bakeryData: BakeryRegistration): Promise<TenantResponse> {
|
|
return apiClient.post<TenantResponse>(`${this.baseUrl}/register`, bakeryData);
|
|
}
|
|
|
|
async getTenant(tenantId: string): Promise<TenantResponse> {
|
|
return apiClient.get<TenantResponse>(`${this.baseUrl}/${tenantId}`);
|
|
}
|
|
|
|
async getTenantBySubdomain(subdomain: string): Promise<TenantResponse> {
|
|
return apiClient.get<TenantResponse>(`${this.baseUrl}/subdomain/${subdomain}`);
|
|
}
|
|
|
|
async getUserTenants(userId: string): Promise<TenantResponse[]> {
|
|
// Use the /owned endpoint since /users/{userId} has validation issues
|
|
return apiClient.get<TenantResponse[]>(`${this.baseUrl}/user/${userId}/owned`);
|
|
}
|
|
|
|
async getUserOwnedTenants(userId: string): Promise<TenantResponse[]> {
|
|
return apiClient.get<TenantResponse[]>(`${this.baseUrl}/user/${userId}/owned`);
|
|
}
|
|
|
|
async updateTenant(tenantId: string, updateData: TenantUpdate): Promise<TenantResponse> {
|
|
return apiClient.put<TenantResponse>(`${this.baseUrl}/${tenantId}`, updateData);
|
|
}
|
|
|
|
async deactivateTenant(tenantId: string): Promise<{ success: boolean; message: string }> {
|
|
return apiClient.post<{ success: boolean; message: string }>(`${this.baseUrl}/${tenantId}/deactivate`);
|
|
}
|
|
|
|
async activateTenant(tenantId: string): Promise<{ success: boolean; message: string }> {
|
|
return apiClient.post<{ success: boolean; message: string }>(`${this.baseUrl}/${tenantId}/activate`);
|
|
}
|
|
|
|
// ===================================================================
|
|
// OPERATIONS: Access Control
|
|
// Backend: services/tenant/app/api/tenant_operations.py
|
|
// ===================================================================
|
|
async verifyTenantAccess(tenantId: string, userId: string): Promise<TenantAccessResponse> {
|
|
return apiClient.get<TenantAccessResponse>(`${this.baseUrl}/${tenantId}/access/${userId}`);
|
|
}
|
|
|
|
async getCurrentUserTenantAccess(tenantId: string): Promise<TenantAccessResponse> {
|
|
// This will use the current user from the auth token
|
|
// The backend endpoint handles extracting user_id from the token
|
|
return apiClient.get<TenantAccessResponse>(`${this.baseUrl}/${tenantId}/my-access`);
|
|
}
|
|
|
|
// ===================================================================
|
|
// OPERATIONS: Search & Discovery
|
|
// Backend: services/tenant/app/api/tenant_operations.py
|
|
// ===================================================================
|
|
async searchTenants(params: TenantSearchParams): Promise<TenantResponse[]> {
|
|
const queryParams = new URLSearchParams();
|
|
|
|
if (params.search_term) queryParams.append('search_term', params.search_term);
|
|
if (params.business_type) queryParams.append('business_type', params.business_type);
|
|
if (params.city) queryParams.append('city', params.city);
|
|
if (params.skip !== undefined) queryParams.append('skip', params.skip.toString());
|
|
if (params.limit !== undefined) queryParams.append('limit', params.limit.toString());
|
|
|
|
return apiClient.get<TenantResponse[]>(`${this.baseUrl}/search?${queryParams.toString()}`);
|
|
}
|
|
|
|
async getNearbyTenants(params: TenantNearbyParams): Promise<TenantResponse[]> {
|
|
const queryParams = new URLSearchParams();
|
|
|
|
queryParams.append('latitude', params.latitude.toString());
|
|
queryParams.append('longitude', params.longitude.toString());
|
|
if (params.radius_km !== undefined) queryParams.append('radius_km', params.radius_km.toString());
|
|
if (params.limit !== undefined) queryParams.append('limit', params.limit.toString());
|
|
|
|
return apiClient.get<TenantResponse[]>(`${this.baseUrl}/nearby?${queryParams.toString()}`);
|
|
}
|
|
|
|
// ===================================================================
|
|
// OPERATIONS: Model Status Management
|
|
// Backend: services/tenant/app/api/tenant_operations.py
|
|
// ===================================================================
|
|
async updateModelStatus(
|
|
tenantId: string,
|
|
modelTrained: boolean,
|
|
lastTrainingDate?: string
|
|
): Promise<TenantResponse> {
|
|
const queryParams = new URLSearchParams();
|
|
queryParams.append('model_trained', modelTrained.toString());
|
|
if (lastTrainingDate) queryParams.append('last_training_date', lastTrainingDate);
|
|
|
|
return apiClient.put<TenantResponse>(`${this.baseUrl}/${tenantId}/model-status?${queryParams.toString()}`);
|
|
}
|
|
|
|
// ===================================================================
|
|
// ATOMIC: Team Member Management
|
|
// Backend: services/tenant/app/api/tenant_members.py
|
|
// ===================================================================
|
|
async addTeamMember(
|
|
tenantId: string,
|
|
userId: string,
|
|
role: string
|
|
): Promise<TenantMemberResponse> {
|
|
return apiClient.post<TenantMemberResponse>(`${this.baseUrl}/${tenantId}/members`, {
|
|
user_id: userId,
|
|
role: role,
|
|
});
|
|
}
|
|
|
|
async getTeamMembers(tenantId: string, activeOnly: boolean = true): Promise<TenantMemberResponse[]> {
|
|
const queryParams = new URLSearchParams();
|
|
queryParams.append('active_only', activeOnly.toString());
|
|
|
|
return apiClient.get<TenantMemberResponse[]>(`${this.baseUrl}/${tenantId}/members?${queryParams.toString()}`);
|
|
}
|
|
|
|
async updateMemberRole(
|
|
tenantId: string,
|
|
memberUserId: string,
|
|
newRole: string
|
|
): Promise<TenantMemberResponse> {
|
|
return apiClient.put<TenantMemberResponse>(
|
|
`${this.baseUrl}/${tenantId}/members/${memberUserId}/role`,
|
|
{ new_role: newRole }
|
|
);
|
|
}
|
|
|
|
async removeTeamMember(tenantId: string, memberUserId: string): Promise<{ success: boolean; message: string }> {
|
|
return apiClient.delete<{ success: boolean; message: string }>(`${this.baseUrl}/${tenantId}/members/${memberUserId}`);
|
|
}
|
|
|
|
// ===================================================================
|
|
// OPERATIONS: Statistics & Admin
|
|
// Backend: services/tenant/app/api/tenant_operations.py
|
|
// ===================================================================
|
|
async getTenantStatistics(): Promise<TenantStatistics> {
|
|
return apiClient.get<TenantStatistics>(`${this.baseUrl}/statistics`);
|
|
}
|
|
|
|
// ===================================================================
|
|
// Frontend Context Management
|
|
// ===================================================================
|
|
setCurrentTenant(tenant: TenantResponse): void {
|
|
// Set tenant context in API client
|
|
if (tenant && tenant.id) {
|
|
apiClient.setTenantId(tenant.id);
|
|
}
|
|
}
|
|
|
|
clearCurrentTenant(): void {
|
|
// Clear tenant context from API client
|
|
apiClient.setTenantId(null);
|
|
}
|
|
}
|
|
|
|
export const tenantService = new TenantService(); |