Add onboarding.service.ts

This commit is contained in:
Urtzi Alfaro
2025-09-02 19:05:48 +02:00
parent b55da883c5
commit 0fb9f9d0f0

View File

@@ -0,0 +1,400 @@
/**
* Enhanced Onboarding API Service
* Provides integration with backend AI-powered onboarding endpoints
*/
import { apiClient } from './client';
export interface OnboardingFileValidationResponse {
is_valid: boolean;
total_records: number;
unique_products: number;
product_list: string[];
validation_errors: any[];
validation_warnings: any[];
summary: any;
}
export interface ProductSuggestion {
suggestion_id: string;
original_name: string;
suggested_name: string;
product_type: 'ingredient' | 'finished_product';
category: string;
unit_of_measure: string;
confidence_score: number;
estimated_shelf_life_days: number;
requires_refrigeration: boolean;
requires_freezing: boolean;
is_seasonal: boolean;
suggested_supplier?: string;
notes: string;
sales_data: {
total_quantity: number;
average_daily_sales: number;
peak_day: string;
frequency: number;
};
}
export interface BusinessModelAnalysis {
model: 'production' | 'retail' | 'hybrid';
confidence: number;
ingredient_count: number;
finished_product_count: number;
ingredient_ratio: number;
recommendations: string[];
}
export interface ProductSuggestionsResponse {
suggestions: ProductSuggestion[];
business_model_analysis: BusinessModelAnalysis;
total_products: number;
high_confidence_count: number;
low_confidence_count: number;
processing_time_seconds: number;
}
export interface InventoryCreationResponse {
created_items: any[];
failed_items: any[];
total_approved: number;
success_rate: number;
inventory_mapping?: { [productName: string]: string };
}
export interface SalesImportResponse {
import_job_id: string;
status: 'completed' | 'failed' | 'partial';
processed_rows: number;
successful_imports: number;
failed_imports: number;
errors: string[];
warnings: string[];
processing_time?: number;
}
export interface BusinessModelGuide {
title: string;
description: string;
next_steps: string[];
recommended_features: string[];
sample_workflows: string[];
}
class OnboardingApiService {
private readonly basePath = '/tenants';
private readonly salesBasePath = '/tenants';
/**
* Step 1: Validate uploaded file and extract unique products
*/
async validateOnboardingFile(
tenantId: string,
file: File
): Promise<OnboardingFileValidationResponse> {
try {
const formData = new FormData();
formData.append('file', file);
const response = await apiClient.post<OnboardingFileValidationResponse>(
`${this.basePath}/${tenantId}/onboarding/validate-file`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
}
);
if (!response.success) {
throw new Error(`Validation failed: ${response.error || 'Unknown error'}`);
}
return response.data;
} catch (error) {
console.error('File validation failed:', error);
throw error;
}
}
/**
* Step 2: Generate AI-powered inventory suggestions
*/
async generateInventorySuggestions(
tenantId: string,
file: File,
productList: string[]
): Promise<ProductSuggestionsResponse> {
try {
const formData = new FormData();
formData.append('file', file);
formData.append('product_list', JSON.stringify(productList));
const response = await apiClient.post<ProductSuggestionsResponse>(
`${this.basePath}/${tenantId}/onboarding/generate-suggestions`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
}
);
if (!response.success) {
throw new Error(`Suggestion generation failed: ${response.error || 'Unknown error'}`);
}
return response.data;
} catch (error) {
console.error('Suggestion generation failed:', error);
throw error;
}
}
/**
* Step 3: Create inventory items from approved suggestions
*/
async createInventoryFromSuggestions(
tenantId: string,
approvedSuggestions: any[]
): Promise<InventoryCreationResponse> {
try {
const response = await apiClient.post<InventoryCreationResponse>(
`${this.basePath}/${tenantId}/onboarding/create-inventory`,
{
suggestions: approvedSuggestions
}
);
if (!response.success) {
throw new Error(`Inventory creation failed: ${response.error || 'Unknown error'}`);
}
// Create inventory mapping if not provided
if (!response.data.inventory_mapping) {
response.data.inventory_mapping = {};
response.data.created_items.forEach((item, index) => {
if (approvedSuggestions[index]) {
response.data.inventory_mapping![approvedSuggestions[index].original_name] = item.id;
}
});
}
return response.data;
} catch (error) {
console.error('Inventory creation failed:', error);
throw error;
}
}
/**
* Step 4: Import sales data with inventory mapping
*/
async importSalesWithInventory(
tenantId: string,
file: File,
inventoryMapping: { [productName: string]: string }
): Promise<SalesImportResponse> {
try {
const formData = new FormData();
formData.append('file', file);
formData.append('inventory_mapping', JSON.stringify(inventoryMapping));
const response = await apiClient.post<SalesImportResponse>(
`${this.basePath}/${tenantId}/onboarding/import-sales`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
}
);
if (!response.success) {
throw new Error(`Sales import failed: ${response.error || 'Unknown error'}`);
}
return response.data;
} catch (error) {
console.error('Sales import failed:', error);
throw error;
}
}
/**
* Get business model specific recommendations
*/
async getBusinessModelGuide(
tenantId: string,
model: 'production' | 'retail' | 'hybrid'
): Promise<BusinessModelGuide> {
try {
const response = await apiClient.get<BusinessModelGuide>(
`${this.basePath}/${tenantId}/onboarding/business-model-guide?model=${model}`
);
if (!response.success) {
throw new Error(`Failed to get business model guide: ${response.error || 'Unknown error'}`);
}
return response.data;
} catch (error) {
console.error('Failed to get business model guide:', error);
throw error;
}
}
/**
* Validate sales data using the sales service (fallback)
*/
async validateSalesData(
tenantId: string,
file: File
): Promise<any> {
try {
const formData = new FormData();
formData.append('file', file);
const response = await apiClient.post<any>(
`${this.salesBasePath}/${tenantId}/sales/import/validate`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
}
);
if (!response.success) {
throw new Error(`Sales validation failed: ${response.error || 'Unknown error'}`);
}
return response.data;
} catch (error) {
console.error('Sales validation failed:', error);
throw error;
}
}
/**
* Import sales data using the sales service (fallback)
*/
async importSalesData(
tenantId: string,
file: File,
updateExisting: boolean = false
): Promise<any> {
try {
const formData = new FormData();
formData.append('file', file);
formData.append('update_existing', updateExisting.toString());
const response = await apiClient.post<any>(
`${this.salesBasePath}/${tenantId}/sales/import`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
}
);
if (!response.success) {
throw new Error(`Sales import failed: ${response.error || 'Unknown error'}`);
}
return response.data;
} catch (error) {
console.error('Sales import failed:', error);
throw error;
}
}
/**
* Get sales data import template
*/
async getSalesImportTemplate(
tenantId: string,
format: 'csv' | 'json' = 'csv'
): Promise<any> {
try {
const response = await apiClient.get<any>(
`${this.salesBasePath}/${tenantId}/sales/import/template?format=${format}`
);
if (!response.success) {
throw new Error(`Failed to get template: ${response.error || 'Unknown error'}`);
}
return response.data;
} catch (error) {
console.error('Failed to get template:', error);
throw error;
}
}
/**
* Download template file (utility method)
*/
downloadTemplate(templateData: any, filename: string, format: 'csv' | 'json' = 'csv'): void {
let content: string;
let mimeType: string;
if (format === 'csv') {
content = templateData.template;
mimeType = 'text/csv';
} else {
content = JSON.stringify(templateData.template, null, 2);
mimeType = 'application/json';
}
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
/**
* Utility: Check if a tenant has completed onboarding
*/
async checkOnboardingStatus(tenantId: string): Promise<{ completed: boolean; steps_completed: string[] }> {
try {
const response = await apiClient.get<any>(
`${this.basePath}/${tenantId}/onboarding/status`
);
return response.data || { completed: false, steps_completed: [] };
} catch (error) {
console.warn('Could not check onboarding status:', error);
return { completed: false, steps_completed: [] };
}
}
/**
* Utility: Mark onboarding as complete
*/
async completeOnboarding(tenantId: string, metadata?: any): Promise<void> {
try {
await apiClient.post(
`${this.basePath}/${tenantId}/onboarding/complete`,
{ metadata }
);
} catch (error) {
console.warn('Could not mark onboarding as complete:', error);
// Don't throw error, this is not critical
}
}
}
export const onboardingApiService = new OnboardingApiService();
export default OnboardingApiService;