Start integrating the onboarding flow with backend 7
This commit is contained in:
300
frontend/src/hooks/business/onboarding/useInventorySetup.ts
Normal file
300
frontend/src/hooks/business/onboarding/useInventorySetup.ts
Normal file
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* Inventory setup hook for creating inventory from suggestions
|
||||
*/
|
||||
|
||||
import { useState, useCallback } from 'react';
|
||||
import {
|
||||
useCreateIngredient,
|
||||
useCreateSalesRecord,
|
||||
} from '../../../api';
|
||||
import { useCurrentTenant } from '../../../stores';
|
||||
import type { ProductSuggestionResponse } from './types';
|
||||
|
||||
interface InventorySetupState {
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
createdItems: any[];
|
||||
inventoryMapping: { [productName: string]: string };
|
||||
salesImportResult: {
|
||||
success: boolean;
|
||||
imported: boolean;
|
||||
records_created: number;
|
||||
message: string;
|
||||
} | null;
|
||||
isInventoryConfigured: boolean;
|
||||
}
|
||||
|
||||
interface InventorySetupActions {
|
||||
createInventoryFromSuggestions: (
|
||||
suggestions: ProductSuggestionResponse[]
|
||||
) => Promise<{
|
||||
success: boolean;
|
||||
createdItems?: any[];
|
||||
inventoryMapping?: { [productName: string]: string };
|
||||
}>;
|
||||
importSalesData: (
|
||||
salesData: any,
|
||||
inventoryMapping: { [productName: string]: string }
|
||||
) => Promise<{
|
||||
success: boolean;
|
||||
recordsCreated: number;
|
||||
message: string;
|
||||
}>;
|
||||
clearError: () => void;
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
export const useInventorySetup = () => {
|
||||
const [state, setState] = useState<InventorySetupState>({
|
||||
isLoading: false,
|
||||
error: null,
|
||||
createdItems: [],
|
||||
inventoryMapping: {},
|
||||
salesImportResult: null,
|
||||
isInventoryConfigured: false,
|
||||
});
|
||||
|
||||
const createIngredientMutation = useCreateIngredient();
|
||||
const createSalesRecordMutation = useCreateSalesRecord();
|
||||
const currentTenant = useCurrentTenant();
|
||||
|
||||
const createInventoryFromSuggestions = useCallback(async (
|
||||
suggestions: ProductSuggestionResponse[]
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
createdItems?: any[];
|
||||
inventoryMapping?: { [productName: string]: string };
|
||||
}> => {
|
||||
if (!suggestions || suggestions.length === 0) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
error: 'No hay sugerencias para crear el inventario',
|
||||
}));
|
||||
return { success: false };
|
||||
}
|
||||
|
||||
if (!currentTenant?.id) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
error: 'No se pudo obtener información del tenant',
|
||||
}));
|
||||
return { success: false };
|
||||
}
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: true,
|
||||
error: null
|
||||
}));
|
||||
|
||||
try {
|
||||
const createdItems = [];
|
||||
const inventoryMapping: { [key: string]: string } = {};
|
||||
|
||||
// Create ingredients from approved suggestions
|
||||
for (const suggestion of suggestions) {
|
||||
try {
|
||||
const ingredientData = {
|
||||
name: suggestion.name,
|
||||
category: suggestion.category || 'Sin categoría',
|
||||
description: suggestion.description || '',
|
||||
unit_of_measure: suggestion.unit_of_measure || 'unidad',
|
||||
cost_per_unit: suggestion.cost_per_unit || 0,
|
||||
supplier_info: suggestion.supplier_info || {},
|
||||
nutritional_info: suggestion.nutritional_info || {},
|
||||
storage_requirements: suggestion.storage_requirements || {},
|
||||
allergen_info: suggestion.allergen_info || {},
|
||||
is_active: true,
|
||||
};
|
||||
|
||||
const createdItem = await createIngredientMutation.mutateAsync({
|
||||
tenantId: currentTenant.id,
|
||||
ingredientData,
|
||||
});
|
||||
|
||||
createdItems.push(createdItem);
|
||||
inventoryMapping[suggestion.name] = createdItem.id;
|
||||
} catch (error) {
|
||||
console.error(`Error creating ingredient ${suggestion.name}:`, error);
|
||||
// Continue with other ingredients even if one fails
|
||||
}
|
||||
}
|
||||
|
||||
if (createdItems.length === 0) {
|
||||
throw new Error('No se pudo crear ningún elemento del inventario');
|
||||
}
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
createdItems,
|
||||
inventoryMapping,
|
||||
isInventoryConfigured: true,
|
||||
}));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
createdItems,
|
||||
inventoryMapping,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Error creando el inventario';
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
error: errorMessage
|
||||
}));
|
||||
return { success: false };
|
||||
}
|
||||
}, [createIngredientMutation, currentTenant]);
|
||||
|
||||
const importSalesData = useCallback(async (
|
||||
salesData: any,
|
||||
inventoryMapping: { [productName: string]: string }
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
recordsCreated: number;
|
||||
message: string;
|
||||
}> => {
|
||||
if (!currentTenant?.id) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
error: 'No se pudo obtener información del tenant',
|
||||
}));
|
||||
return {
|
||||
success: false,
|
||||
recordsCreated: 0,
|
||||
message: 'Error: No se pudo obtener información del tenant',
|
||||
};
|
||||
}
|
||||
|
||||
if (!salesData || !salesData.product_list) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
error: 'No hay datos de ventas para importar',
|
||||
}));
|
||||
return {
|
||||
success: false,
|
||||
recordsCreated: 0,
|
||||
message: 'Error: No hay datos de ventas para importar',
|
||||
};
|
||||
}
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: true,
|
||||
error: null
|
||||
}));
|
||||
|
||||
try {
|
||||
let recordsCreated = 0;
|
||||
|
||||
// Process actual sales data and create sales records
|
||||
if (salesData.raw_data && Array.isArray(salesData.raw_data)) {
|
||||
for (const salesRecord of salesData.raw_data) {
|
||||
try {
|
||||
// Map product name to inventory product ID
|
||||
const inventoryProductId = inventoryMapping[salesRecord.product_name];
|
||||
|
||||
if (inventoryProductId) {
|
||||
const salesRecordData = {
|
||||
date: salesRecord.date,
|
||||
product_name: salesRecord.product_name,
|
||||
inventory_product_id: inventoryProductId,
|
||||
quantity_sold: salesRecord.quantity,
|
||||
unit_price: salesRecord.unit_price,
|
||||
total_revenue: salesRecord.total_amount || (salesRecord.quantity * salesRecord.unit_price),
|
||||
channel: salesRecord.channel || 'tienda',
|
||||
customer_info: salesRecord.customer_info || {},
|
||||
notes: salesRecord.notes || '',
|
||||
};
|
||||
|
||||
await createSalesRecordMutation.mutateAsync({
|
||||
tenantId: currentTenant.id,
|
||||
salesData: salesRecordData,
|
||||
});
|
||||
|
||||
recordsCreated++;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating sales record:', error);
|
||||
// Continue with next record
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = {
|
||||
success: recordsCreated > 0,
|
||||
imported: recordsCreated > 0,
|
||||
records_created: recordsCreated,
|
||||
message: recordsCreated > 0
|
||||
? `Se importaron ${recordsCreated} registros de ventas exitosamente`
|
||||
: 'No se pudieron importar registros de ventas',
|
||||
};
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
salesImportResult: result,
|
||||
}));
|
||||
|
||||
return {
|
||||
success: result.success,
|
||||
recordsCreated,
|
||||
message: result.message,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Error importando datos de ventas';
|
||||
const result = {
|
||||
success: false,
|
||||
imported: false,
|
||||
records_created: 0,
|
||||
message: errorMessage,
|
||||
};
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
error: errorMessage,
|
||||
salesImportResult: result,
|
||||
}));
|
||||
|
||||
return {
|
||||
success: false,
|
||||
recordsCreated: 0,
|
||||
message: errorMessage,
|
||||
};
|
||||
}
|
||||
}, [createSalesRecordMutation, currentTenant]);
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
setState(prev => ({ ...prev, error: null }));
|
||||
}, []);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setState({
|
||||
isLoading: false,
|
||||
error: null,
|
||||
createdItems: [],
|
||||
inventoryMapping: {},
|
||||
salesImportResult: null,
|
||||
isInventoryConfigured: false,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return {
|
||||
// State
|
||||
isLoading: state.isLoading,
|
||||
error: state.error,
|
||||
createdItems: state.createdItems,
|
||||
inventoryMapping: state.inventoryMapping,
|
||||
salesImportResult: state.salesImportResult,
|
||||
isInventoryConfigured: state.isInventoryConfigured,
|
||||
|
||||
// Actions
|
||||
createInventoryFromSuggestions,
|
||||
importSalesData,
|
||||
clearError,
|
||||
reset,
|
||||
} satisfies InventorySetupState & InventorySetupActions;
|
||||
};
|
||||
Reference in New Issue
Block a user