REFACTOR ALL APIs
This commit is contained in:
@@ -3,16 +3,15 @@
|
||||
*/
|
||||
import { useMutation, useQuery, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { authService } from '../services/auth';
|
||||
import {
|
||||
UserRegistration,
|
||||
UserLogin,
|
||||
TokenResponse,
|
||||
PasswordChange,
|
||||
PasswordReset,
|
||||
UserResponse,
|
||||
UserUpdate,
|
||||
TokenVerificationResponse,
|
||||
AuthHealthResponse
|
||||
import {
|
||||
UserRegistration,
|
||||
UserLogin,
|
||||
TokenResponse,
|
||||
PasswordChange,
|
||||
PasswordReset,
|
||||
UserResponse,
|
||||
UserUpdate,
|
||||
TokenVerification
|
||||
} from '../types/auth';
|
||||
import { ApiError } from '../client';
|
||||
import { useAuthStore } from '../../stores/auth.store';
|
||||
@@ -38,9 +37,9 @@ export const useAuthProfile = (
|
||||
};
|
||||
|
||||
export const useAuthHealth = (
|
||||
options?: Omit<UseQueryOptions<AuthHealthResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
options?: Omit<UseQueryOptions<{ status: string; service: string }, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<AuthHealthResponse, ApiError>({
|
||||
return useQuery<{ status: string; service: string }, ApiError>({
|
||||
queryKey: authKeys.health(),
|
||||
queryFn: () => authService.healthCheck(),
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
@@ -50,9 +49,9 @@ export const useAuthHealth = (
|
||||
|
||||
export const useVerifyToken = (
|
||||
token?: string,
|
||||
options?: Omit<UseQueryOptions<TokenVerificationResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
options?: Omit<UseQueryOptions<TokenVerification, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<TokenVerificationResponse, ApiError>({
|
||||
return useQuery<TokenVerification, ApiError>({
|
||||
queryKey: authKeys.verify(token),
|
||||
queryFn: () => authService.verifyToken(token),
|
||||
enabled: !!token,
|
||||
@@ -153,7 +152,7 @@ export const useUpdateProfile = (
|
||||
// Update the auth store user to maintain consistency
|
||||
const authStore = useAuthStore.getState();
|
||||
if (authStore.user) {
|
||||
authStore.updateUser(data);
|
||||
authStore.updateUser(data as any);
|
||||
}
|
||||
},
|
||||
...options,
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Classification React Query hooks
|
||||
*/
|
||||
import { useMutation, useQuery, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { classificationService } from '../services/classification';
|
||||
import {
|
||||
ProductClassificationRequest,
|
||||
BatchClassificationRequest,
|
||||
ProductSuggestionResponse
|
||||
} from '../types/classification';
|
||||
import { ApiError } from '../client';
|
||||
|
||||
// Query Keys
|
||||
export const classificationKeys = {
|
||||
all: ['classification'] as const,
|
||||
suggestions: {
|
||||
all: () => [...classificationKeys.all, 'suggestions'] as const,
|
||||
pending: (tenantId: string) => [...classificationKeys.suggestions.all(), 'pending', tenantId] as const,
|
||||
history: (tenantId: string, limit?: number, offset?: number) =>
|
||||
[...classificationKeys.suggestions.all(), 'history', tenantId, { limit, offset }] as const,
|
||||
},
|
||||
analysis: {
|
||||
all: () => [...classificationKeys.all, 'analysis'] as const,
|
||||
businessModel: (tenantId: string) => [...classificationKeys.analysis.all(), 'business-model', tenantId] as const,
|
||||
},
|
||||
} as const;
|
||||
|
||||
// Mutations
|
||||
export const useClassifyProduct = (
|
||||
options?: UseMutationOptions<
|
||||
ProductSuggestionResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; classificationData: ProductClassificationRequest }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
ProductSuggestionResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; classificationData: ProductClassificationRequest }
|
||||
>({
|
||||
mutationFn: ({ tenantId, classificationData }) =>
|
||||
classificationService.classifyProduct(tenantId, classificationData),
|
||||
onSuccess: (data, { tenantId }) => {
|
||||
// Invalidate pending suggestions to include the new one
|
||||
queryClient.invalidateQueries({ queryKey: classificationKeys.suggestions.pending(tenantId) });
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useClassifyProductsBatch = (
|
||||
options?: UseMutationOptions<
|
||||
ProductSuggestionResponse[],
|
||||
ApiError,
|
||||
{ tenantId: string; batchData: BatchClassificationRequest }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
ProductSuggestionResponse[],
|
||||
ApiError,
|
||||
{ tenantId: string; batchData: BatchClassificationRequest }
|
||||
>({
|
||||
mutationFn: ({ tenantId, batchData }) =>
|
||||
classificationService.classifyProductsBatch(tenantId, batchData),
|
||||
onSuccess: (data, { tenantId }) => {
|
||||
// Invalidate pending suggestions to include the new ones
|
||||
queryClient.invalidateQueries({ queryKey: classificationKeys.suggestions.pending(tenantId) });
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
@@ -1,365 +0,0 @@
|
||||
/**
|
||||
* Data Import React Query hooks
|
||||
* Provides data fetching, caching, and state management for data import operations
|
||||
*/
|
||||
|
||||
import { useMutation, useQuery, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { dataImportService } from '../services/dataImport';
|
||||
import { ApiError } from '../client/apiClient';
|
||||
import type {
|
||||
ImportValidationResponse,
|
||||
ImportProcessResponse,
|
||||
ImportStatusResponse,
|
||||
} from '../types/dataImport';
|
||||
|
||||
// Query Keys Factory
|
||||
export const dataImportKeys = {
|
||||
all: ['data-import'] as const,
|
||||
status: (tenantId: string, importId: string) =>
|
||||
[...dataImportKeys.all, 'status', tenantId, importId] as const,
|
||||
} as const;
|
||||
|
||||
// Status Query
|
||||
export const useImportStatus = (
|
||||
tenantId: string,
|
||||
importId: string,
|
||||
options?: Omit<UseQueryOptions<ImportStatusResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<ImportStatusResponse, ApiError>({
|
||||
queryKey: dataImportKeys.status(tenantId, importId),
|
||||
queryFn: () => dataImportService.getImportStatus(tenantId, importId),
|
||||
enabled: !!tenantId && !!importId,
|
||||
refetchInterval: 5000, // Poll every 5 seconds for active imports
|
||||
staleTime: 1000, // Consider data stale after 1 second
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Validation Mutations
|
||||
export const useValidateJsonData = (
|
||||
options?: UseMutationOptions<
|
||||
ImportValidationResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; data: any }
|
||||
>
|
||||
) => {
|
||||
return useMutation<
|
||||
ImportValidationResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; data: any }
|
||||
>({
|
||||
mutationFn: ({ tenantId, data }) => dataImportService.validateJsonData(tenantId, data),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useValidateCsvFile = (
|
||||
options?: UseMutationOptions<
|
||||
ImportValidationResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; file: File }
|
||||
>
|
||||
) => {
|
||||
return useMutation<
|
||||
ImportValidationResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; file: File }
|
||||
>({
|
||||
mutationFn: ({ tenantId, file }) => dataImportService.validateCsvFile(tenantId, file),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Import Mutations
|
||||
export const useImportJsonData = (
|
||||
options?: UseMutationOptions<
|
||||
ImportProcessResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; data: any; options?: { skip_validation?: boolean; chunk_size?: number } }
|
||||
>
|
||||
) => {
|
||||
return useMutation<
|
||||
ImportProcessResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; data: any; options?: { skip_validation?: boolean; chunk_size?: number } }
|
||||
>({
|
||||
mutationFn: ({ tenantId, data, options: importOptions }) =>
|
||||
dataImportService.importJsonData(tenantId, data, importOptions),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useImportCsvFile = (
|
||||
options?: UseMutationOptions<
|
||||
ImportProcessResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; file: File; options?: { skip_validation?: boolean; chunk_size?: number } }
|
||||
>
|
||||
) => {
|
||||
return useMutation<
|
||||
ImportProcessResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; file: File; options?: { skip_validation?: boolean; chunk_size?: number } }
|
||||
>({
|
||||
mutationFn: ({ tenantId, file, options: importOptions }) =>
|
||||
dataImportService.importCsvFile(tenantId, file, importOptions),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Combined validation and import hook for easier use
|
||||
// Validation-only hook for onboarding
|
||||
export const useValidateFileOnly = () => {
|
||||
const validateCsv = useValidateCsvFile();
|
||||
const validateJson = useValidateJsonData();
|
||||
|
||||
const validateFile = async (
|
||||
tenantId: string,
|
||||
file: File,
|
||||
options?: {
|
||||
onProgress?: (stage: string, progress: number, message: string) => void;
|
||||
}
|
||||
): Promise<{
|
||||
validationResult?: ImportValidationResponse;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}> => {
|
||||
try {
|
||||
let validationResult: ImportValidationResponse | undefined;
|
||||
|
||||
options?.onProgress?.('validating', 20, 'Validando estructura del archivo...');
|
||||
|
||||
const fileExtension = file.name.split('.').pop()?.toLowerCase();
|
||||
if (fileExtension === 'csv') {
|
||||
validationResult = await validateCsv.mutateAsync({ tenantId, file });
|
||||
} else if (fileExtension === 'json') {
|
||||
const jsonData = await file.text().then(text => JSON.parse(text));
|
||||
validationResult = await validateJson.mutateAsync({ tenantId, data: jsonData });
|
||||
} else {
|
||||
throw new Error('Formato de archivo no soportado. Use CSV o JSON.');
|
||||
}
|
||||
|
||||
options?.onProgress?.('validating', 50, 'Verificando integridad de datos...');
|
||||
|
||||
if (!validationResult.is_valid) {
|
||||
const errorMessage = validationResult.errors && validationResult.errors.length > 0
|
||||
? validationResult.errors.join(', ')
|
||||
: 'Error de validación desconocido';
|
||||
throw new Error(`Archivo inválido: ${errorMessage}`);
|
||||
}
|
||||
|
||||
// Report validation success with details
|
||||
options?.onProgress?.('completed', 100,
|
||||
`Archivo validado: ${validationResult.valid_records} registros válidos de ${validationResult.total_records} totales`
|
||||
);
|
||||
|
||||
return {
|
||||
validationResult,
|
||||
success: true,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Error validando archivo';
|
||||
options?.onProgress?.('error', 0, errorMessage);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
validateFile,
|
||||
};
|
||||
};
|
||||
|
||||
// Full validation + import hook (for later use)
|
||||
export const useValidateAndImportFile = () => {
|
||||
const validateCsv = useValidateCsvFile();
|
||||
const validateJson = useValidateJsonData();
|
||||
const importCsv = useImportCsvFile();
|
||||
const importJson = useImportJsonData();
|
||||
|
||||
const processFile = async (
|
||||
tenantId: string,
|
||||
file: File,
|
||||
options?: {
|
||||
skipValidation?: boolean;
|
||||
chunkSize?: number;
|
||||
onProgress?: (stage: string, progress: number, message: string) => void;
|
||||
}
|
||||
): Promise<{
|
||||
validationResult?: ImportValidationResponse;
|
||||
importResult?: ImportProcessResponse;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}> => {
|
||||
try {
|
||||
let validationResult: ImportValidationResponse | undefined;
|
||||
|
||||
// Step 1: Validation (unless skipped)
|
||||
if (!options?.skipValidation) {
|
||||
options?.onProgress?.('validating', 20, 'Validando estructura del archivo...');
|
||||
|
||||
const fileExtension = file.name.split('.').pop()?.toLowerCase();
|
||||
if (fileExtension === 'csv') {
|
||||
validationResult = await validateCsv.mutateAsync({ tenantId, file });
|
||||
} else if (fileExtension === 'json') {
|
||||
const jsonData = await file.text().then(text => JSON.parse(text));
|
||||
validationResult = await validateJson.mutateAsync({ tenantId, data: jsonData });
|
||||
} else {
|
||||
throw new Error('Formato de archivo no soportado. Use CSV o JSON.');
|
||||
}
|
||||
|
||||
options?.onProgress?.('validating', 50, 'Verificando integridad de datos...');
|
||||
|
||||
if (!validationResult.is_valid) {
|
||||
const errorMessage = validationResult.errors && validationResult.errors.length > 0
|
||||
? validationResult.errors.join(', ')
|
||||
: 'Error de validación desconocido';
|
||||
throw new Error(`Archivo inválido: ${errorMessage}`);
|
||||
}
|
||||
|
||||
// Report validation success with details
|
||||
options?.onProgress?.('validating', 60,
|
||||
`Archivo validado: ${validationResult.valid_records} registros válidos de ${validationResult.total_records} totales`
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: Import
|
||||
options?.onProgress?.('importing', 70, 'Importando datos...');
|
||||
|
||||
const importOptions = {
|
||||
skip_validation: options?.skipValidation || false,
|
||||
chunk_size: options?.chunkSize,
|
||||
};
|
||||
|
||||
let importResult: ImportProcessResponse;
|
||||
const fileExtension = file.name.split('.').pop()?.toLowerCase();
|
||||
|
||||
if (fileExtension === 'csv') {
|
||||
importResult = await importCsv.mutateAsync({
|
||||
tenantId,
|
||||
file,
|
||||
options: importOptions
|
||||
});
|
||||
} else if (fileExtension === 'json') {
|
||||
const jsonData = await file.text().then(text => JSON.parse(text));
|
||||
importResult = await importJson.mutateAsync({
|
||||
tenantId,
|
||||
data: jsonData,
|
||||
options: importOptions
|
||||
});
|
||||
} else {
|
||||
throw new Error('Formato de archivo no soportado. Use CSV o JSON.');
|
||||
}
|
||||
|
||||
// Report completion with details
|
||||
const completionMessage = importResult.success
|
||||
? `Importación completada: ${importResult.records_processed} registros procesados`
|
||||
: `Importación fallida: ${importResult.errors?.join(', ') || 'Error desconocido'}`;
|
||||
|
||||
options?.onProgress?.('completed', 100, completionMessage);
|
||||
|
||||
return {
|
||||
validationResult,
|
||||
importResult,
|
||||
success: importResult.success,
|
||||
error: importResult.success ? undefined : (importResult.errors?.join(', ') || 'Error en la importación'),
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Error procesando archivo';
|
||||
options?.onProgress?.('error', 0, errorMessage);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
processFile,
|
||||
validateCsv,
|
||||
validateJson,
|
||||
importCsv,
|
||||
importJson,
|
||||
isValidating: validateCsv.isPending || validateJson.isPending,
|
||||
isImporting: importCsv.isPending || importJson.isPending,
|
||||
isLoading: validateCsv.isPending || validateJson.isPending || importCsv.isPending || importJson.isPending,
|
||||
error: validateCsv.error || validateJson.error || importCsv.error || importJson.error,
|
||||
};
|
||||
};
|
||||
|
||||
// Import-only hook (for when validation has already been done)
|
||||
export const useImportFileOnly = () => {
|
||||
const importCsv = useImportCsvFile();
|
||||
const importJson = useImportJsonData();
|
||||
|
||||
const importFile = async (
|
||||
tenantId: string,
|
||||
file: File,
|
||||
options?: {
|
||||
chunkSize?: number;
|
||||
onProgress?: (stage: string, progress: number, message: string) => void;
|
||||
}
|
||||
): Promise<{
|
||||
importResult?: ImportProcessResponse;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}> => {
|
||||
try {
|
||||
options?.onProgress?.('importing', 10, 'Iniciando importación de datos...');
|
||||
|
||||
const fileExtension = file.name.split('.').pop()?.toLowerCase();
|
||||
let importResult: ImportProcessResponse;
|
||||
|
||||
if (fileExtension === 'csv') {
|
||||
importResult = await importCsv.mutateAsync({
|
||||
tenantId,
|
||||
file,
|
||||
options: {
|
||||
skip_validation: true, // Skip validation since already done
|
||||
chunk_size: options?.chunkSize
|
||||
}
|
||||
});
|
||||
} else if (fileExtension === 'json') {
|
||||
const jsonData = await file.text().then(text => JSON.parse(text));
|
||||
importResult = await importJson.mutateAsync({
|
||||
tenantId,
|
||||
data: jsonData,
|
||||
options: {
|
||||
skip_validation: true, // Skip validation since already done
|
||||
chunk_size: options?.chunkSize
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error('Formato de archivo no soportado. Use CSV o JSON.');
|
||||
}
|
||||
|
||||
options?.onProgress?.('completed', 100,
|
||||
`Importación completada: ${importResult.records_processed} registros procesados`
|
||||
);
|
||||
|
||||
return {
|
||||
importResult,
|
||||
success: importResult.success,
|
||||
error: importResult.success ? undefined : (importResult.errors?.join(', ') || 'Error en la importación'),
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Error importando archivo';
|
||||
options?.onProgress?.('error', 0, errorMessage);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
importFile,
|
||||
isImporting: importCsv.isPending || importJson.isPending,
|
||||
error: importCsv.error || importJson.error,
|
||||
};
|
||||
};
|
||||
@@ -1,384 +0,0 @@
|
||||
/**
|
||||
* Food Safety React Query hooks
|
||||
*/
|
||||
import { useMutation, useQuery, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { foodSafetyService } from '../services/foodSafety';
|
||||
import {
|
||||
FoodSafetyComplianceCreate,
|
||||
FoodSafetyComplianceUpdate,
|
||||
FoodSafetyComplianceResponse,
|
||||
TemperatureLogCreate,
|
||||
BulkTemperatureLogCreate,
|
||||
TemperatureLogResponse,
|
||||
FoodSafetyAlertCreate,
|
||||
FoodSafetyAlertUpdate,
|
||||
FoodSafetyAlertResponse,
|
||||
FoodSafetyFilter,
|
||||
TemperatureMonitoringFilter,
|
||||
FoodSafetyMetrics,
|
||||
TemperatureAnalytics,
|
||||
FoodSafetyDashboard,
|
||||
} from '../types/foodSafety';
|
||||
import { PaginatedResponse } from '../types/inventory';
|
||||
import { ApiError } from '../client';
|
||||
|
||||
// Query Keys
|
||||
export const foodSafetyKeys = {
|
||||
all: ['food-safety'] as const,
|
||||
compliance: {
|
||||
all: () => [...foodSafetyKeys.all, 'compliance'] as const,
|
||||
lists: () => [...foodSafetyKeys.compliance.all(), 'list'] as const,
|
||||
list: (tenantId: string, filter?: FoodSafetyFilter) =>
|
||||
[...foodSafetyKeys.compliance.lists(), tenantId, filter] as const,
|
||||
details: () => [...foodSafetyKeys.compliance.all(), 'detail'] as const,
|
||||
detail: (tenantId: string, recordId: string) =>
|
||||
[...foodSafetyKeys.compliance.details(), tenantId, recordId] as const,
|
||||
},
|
||||
temperature: {
|
||||
all: () => [...foodSafetyKeys.all, 'temperature'] as const,
|
||||
lists: () => [...foodSafetyKeys.temperature.all(), 'list'] as const,
|
||||
list: (tenantId: string, filter?: TemperatureMonitoringFilter) =>
|
||||
[...foodSafetyKeys.temperature.lists(), tenantId, filter] as const,
|
||||
analytics: (tenantId: string, location: string, startDate?: string, endDate?: string) =>
|
||||
[...foodSafetyKeys.temperature.all(), 'analytics', tenantId, location, { startDate, endDate }] as const,
|
||||
violations: (tenantId: string, limit?: number) =>
|
||||
[...foodSafetyKeys.temperature.all(), 'violations', tenantId, limit] as const,
|
||||
},
|
||||
alerts: {
|
||||
all: () => [...foodSafetyKeys.all, 'alerts'] as const,
|
||||
lists: () => [...foodSafetyKeys.alerts.all(), 'list'] as const,
|
||||
list: (tenantId: string, status?: string, severity?: string, limit?: number, offset?: number) =>
|
||||
[...foodSafetyKeys.alerts.lists(), tenantId, { status, severity, limit, offset }] as const,
|
||||
details: () => [...foodSafetyKeys.alerts.all(), 'detail'] as const,
|
||||
detail: (tenantId: string, alertId: string) =>
|
||||
[...foodSafetyKeys.alerts.details(), tenantId, alertId] as const,
|
||||
},
|
||||
dashboard: (tenantId: string) =>
|
||||
[...foodSafetyKeys.all, 'dashboard', tenantId] as const,
|
||||
metrics: (tenantId: string, startDate?: string, endDate?: string) =>
|
||||
[...foodSafetyKeys.all, 'metrics', tenantId, { startDate, endDate }] as const,
|
||||
complianceRate: (tenantId: string, startDate?: string, endDate?: string) =>
|
||||
[...foodSafetyKeys.all, 'compliance-rate', tenantId, { startDate, endDate }] as const,
|
||||
} as const;
|
||||
|
||||
// Compliance Queries
|
||||
export const useComplianceRecords = (
|
||||
tenantId: string,
|
||||
filter?: FoodSafetyFilter,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<FoodSafetyComplianceResponse>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<FoodSafetyComplianceResponse>, ApiError>({
|
||||
queryKey: foodSafetyKeys.compliance.list(tenantId, filter),
|
||||
queryFn: () => foodSafetyService.getComplianceRecords(tenantId, filter),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 1 * 60 * 1000, // 1 minute
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useComplianceRecord = (
|
||||
tenantId: string,
|
||||
recordId: string,
|
||||
options?: Omit<UseQueryOptions<FoodSafetyComplianceResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<FoodSafetyComplianceResponse, ApiError>({
|
||||
queryKey: foodSafetyKeys.compliance.detail(tenantId, recordId),
|
||||
queryFn: () => foodSafetyService.getComplianceRecord(tenantId, recordId),
|
||||
enabled: !!tenantId && !!recordId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Temperature Monitoring Queries
|
||||
export const useTemperatureLogs = (
|
||||
tenantId: string,
|
||||
filter?: TemperatureMonitoringFilter,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<TemperatureLogResponse>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<TemperatureLogResponse>, ApiError>({
|
||||
queryKey: foodSafetyKeys.temperature.list(tenantId, filter),
|
||||
queryFn: () => foodSafetyService.getTemperatureLogs(tenantId, filter),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useTemperatureAnalytics = (
|
||||
tenantId: string,
|
||||
location: string,
|
||||
startDate?: string,
|
||||
endDate?: string,
|
||||
options?: Omit<UseQueryOptions<TemperatureAnalytics, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<TemperatureAnalytics, ApiError>({
|
||||
queryKey: foodSafetyKeys.temperature.analytics(tenantId, location, startDate, endDate),
|
||||
queryFn: () => foodSafetyService.getTemperatureAnalytics(tenantId, location, startDate, endDate),
|
||||
enabled: !!tenantId && !!location,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useTemperatureViolations = (
|
||||
tenantId: string,
|
||||
limit: number = 20,
|
||||
options?: Omit<UseQueryOptions<TemperatureLogResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<TemperatureLogResponse[], ApiError>({
|
||||
queryKey: foodSafetyKeys.temperature.violations(tenantId, limit),
|
||||
queryFn: () => foodSafetyService.getTemperatureViolations(tenantId, limit),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Alert Queries
|
||||
export const useFoodSafetyAlerts = (
|
||||
tenantId: string,
|
||||
status?: 'open' | 'in_progress' | 'resolved' | 'dismissed',
|
||||
severity?: 'critical' | 'warning' | 'info',
|
||||
limit: number = 50,
|
||||
offset: number = 0,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<FoodSafetyAlertResponse>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<FoodSafetyAlertResponse>, ApiError>({
|
||||
queryKey: foodSafetyKeys.alerts.list(tenantId, status, severity, limit, offset),
|
||||
queryFn: () => foodSafetyService.getFoodSafetyAlerts(tenantId, status, severity, limit, offset),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useFoodSafetyAlert = (
|
||||
tenantId: string,
|
||||
alertId: string,
|
||||
options?: Omit<UseQueryOptions<FoodSafetyAlertResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<FoodSafetyAlertResponse, ApiError>({
|
||||
queryKey: foodSafetyKeys.alerts.detail(tenantId, alertId),
|
||||
queryFn: () => foodSafetyService.getFoodSafetyAlert(tenantId, alertId),
|
||||
enabled: !!tenantId && !!alertId,
|
||||
staleTime: 1 * 60 * 1000, // 1 minute
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Dashboard and Metrics Queries
|
||||
export const useFoodSafetyDashboard = (
|
||||
tenantId: string,
|
||||
options?: Omit<UseQueryOptions<FoodSafetyDashboard, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<FoodSafetyDashboard, ApiError>({
|
||||
queryKey: foodSafetyKeys.dashboard(tenantId),
|
||||
queryFn: () => foodSafetyService.getFoodSafetyDashboard(tenantId),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useFoodSafetyMetrics = (
|
||||
tenantId: string,
|
||||
startDate?: string,
|
||||
endDate?: string,
|
||||
options?: Omit<UseQueryOptions<FoodSafetyMetrics, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<FoodSafetyMetrics, ApiError>({
|
||||
queryKey: foodSafetyKeys.metrics(tenantId, startDate, endDate),
|
||||
queryFn: () => foodSafetyService.getFoodSafetyMetrics(tenantId, startDate, endDate),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useComplianceRate = (
|
||||
tenantId: string,
|
||||
startDate?: string,
|
||||
endDate?: string,
|
||||
options?: Omit<UseQueryOptions<{
|
||||
overall_rate: number;
|
||||
by_type: Record<string, number>;
|
||||
trend: Array<{ date: string; rate: number }>;
|
||||
}, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<{
|
||||
overall_rate: number;
|
||||
by_type: Record<string, number>;
|
||||
trend: Array<{ date: string; rate: number }>;
|
||||
}, ApiError>({
|
||||
queryKey: foodSafetyKeys.complianceRate(tenantId, startDate, endDate),
|
||||
queryFn: () => foodSafetyService.getComplianceRate(tenantId, startDate, endDate),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Compliance Mutations
|
||||
export const useCreateComplianceRecord = (
|
||||
options?: UseMutationOptions<
|
||||
FoodSafetyComplianceResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; complianceData: FoodSafetyComplianceCreate }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
FoodSafetyComplianceResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; complianceData: FoodSafetyComplianceCreate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, complianceData }) =>
|
||||
foodSafetyService.createComplianceRecord(tenantId, complianceData),
|
||||
onSuccess: (data, { tenantId }) => {
|
||||
// Add to cache
|
||||
queryClient.setQueryData(foodSafetyKeys.compliance.detail(tenantId, data.id), data);
|
||||
// Invalidate lists
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.compliance.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.dashboard(tenantId) });
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.metrics(tenantId) });
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateComplianceRecord = (
|
||||
options?: UseMutationOptions<
|
||||
FoodSafetyComplianceResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; recordId: string; updateData: FoodSafetyComplianceUpdate }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
FoodSafetyComplianceResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; recordId: string; updateData: FoodSafetyComplianceUpdate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, recordId, updateData }) =>
|
||||
foodSafetyService.updateComplianceRecord(tenantId, recordId, updateData),
|
||||
onSuccess: (data, { tenantId, recordId }) => {
|
||||
// Update cache
|
||||
queryClient.setQueryData(foodSafetyKeys.compliance.detail(tenantId, recordId), data);
|
||||
// Invalidate lists
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.compliance.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.dashboard(tenantId) });
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Temperature Mutations
|
||||
export const useCreateTemperatureLog = (
|
||||
options?: UseMutationOptions<
|
||||
TemperatureLogResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; logData: TemperatureLogCreate }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
TemperatureLogResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; logData: TemperatureLogCreate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, logData }) => foodSafetyService.createTemperatureLog(tenantId, logData),
|
||||
onSuccess: (data, { tenantId }) => {
|
||||
// Invalidate temperature queries
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.temperature.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.dashboard(tenantId) });
|
||||
|
||||
// If alert was triggered, invalidate alerts
|
||||
if (data.alert_triggered) {
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.alerts.lists() });
|
||||
}
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateBulkTemperatureLogs = (
|
||||
options?: UseMutationOptions<
|
||||
{ created_count: number; failed_count: number; errors?: string[] },
|
||||
ApiError,
|
||||
{ tenantId: string; bulkData: BulkTemperatureLogCreate }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
{ created_count: number; failed_count: number; errors?: string[] },
|
||||
ApiError,
|
||||
{ tenantId: string; bulkData: BulkTemperatureLogCreate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, bulkData }) => foodSafetyService.createBulkTemperatureLogs(tenantId, bulkData),
|
||||
onSuccess: (data, { tenantId }) => {
|
||||
// Invalidate temperature queries
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.temperature.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.dashboard(tenantId) });
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Alert Mutations
|
||||
export const useCreateFoodSafetyAlert = (
|
||||
options?: UseMutationOptions<
|
||||
FoodSafetyAlertResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; alertData: FoodSafetyAlertCreate }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
FoodSafetyAlertResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; alertData: FoodSafetyAlertCreate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, alertData }) => foodSafetyService.createFoodSafetyAlert(tenantId, alertData),
|
||||
onSuccess: (data, { tenantId }) => {
|
||||
// Add to cache
|
||||
queryClient.setQueryData(foodSafetyKeys.alerts.detail(tenantId, data.id), data);
|
||||
// Invalidate lists
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.alerts.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.dashboard(tenantId) });
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateFoodSafetyAlert = (
|
||||
options?: UseMutationOptions<
|
||||
FoodSafetyAlertResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; alertId: string; updateData: FoodSafetyAlertUpdate }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
FoodSafetyAlertResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; alertId: string; updateData: FoodSafetyAlertUpdate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, alertId, updateData }) =>
|
||||
foodSafetyService.updateFoodSafetyAlert(tenantId, alertId, updateData),
|
||||
onSuccess: (data, { tenantId, alertId }) => {
|
||||
// Update cache
|
||||
queryClient.setQueryData(foodSafetyKeys.alerts.detail(tenantId, alertId), data);
|
||||
// Invalidate lists
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.alerts.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: foodSafetyKeys.dashboard(tenantId) });
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
@@ -16,12 +16,8 @@ import {
|
||||
ForecastResponse,
|
||||
BatchForecastRequest,
|
||||
BatchForecastResponse,
|
||||
ForecastListResponse,
|
||||
ForecastByIdResponse,
|
||||
ForecastStatistics,
|
||||
DeleteForecastResponse,
|
||||
GetForecastsParams,
|
||||
ForecastingHealthResponse,
|
||||
ListForecastsParams,
|
||||
ForecastStatisticsParams,
|
||||
} from '../types/forecasting';
|
||||
import { ApiError } from '../client/apiClient';
|
||||
|
||||
@@ -32,7 +28,7 @@ import { ApiError } from '../client/apiClient';
|
||||
export const forecastingKeys = {
|
||||
all: ['forecasting'] as const,
|
||||
lists: () => [...forecastingKeys.all, 'list'] as const,
|
||||
list: (tenantId: string, filters?: GetForecastsParams) =>
|
||||
list: (tenantId: string, filters?: ListForecastsParams) =>
|
||||
[...forecastingKeys.lists(), tenantId, filters] as const,
|
||||
details: () => [...forecastingKeys.all, 'detail'] as const,
|
||||
detail: (tenantId: string, forecastId: string) =>
|
||||
@@ -51,10 +47,10 @@ export const forecastingKeys = {
|
||||
*/
|
||||
export const useTenantForecasts = (
|
||||
tenantId: string,
|
||||
params?: GetForecastsParams,
|
||||
options?: Omit<UseQueryOptions<ForecastListResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
params?: ListForecastsParams,
|
||||
options?: Omit<UseQueryOptions<{ forecasts: ForecastResponse[]; total: number }, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<ForecastListResponse, ApiError>({
|
||||
return useQuery<{ forecasts: ForecastResponse[]; total: number }, ApiError>({
|
||||
queryKey: forecastingKeys.list(tenantId, params),
|
||||
queryFn: () => forecastingService.getTenantForecasts(tenantId, params),
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
@@ -69,9 +65,9 @@ export const useTenantForecasts = (
|
||||
export const useForecastById = (
|
||||
tenantId: string,
|
||||
forecastId: string,
|
||||
options?: Omit<UseQueryOptions<ForecastByIdResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
options?: Omit<UseQueryOptions<ForecastResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<ForecastByIdResponse, ApiError>({
|
||||
return useQuery<ForecastResponse, ApiError>({
|
||||
queryKey: forecastingKeys.detail(tenantId, forecastId),
|
||||
queryFn: () => forecastingService.getForecastById(tenantId, forecastId),
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
@@ -85,9 +81,9 @@ export const useForecastById = (
|
||||
*/
|
||||
export const useForecastStatistics = (
|
||||
tenantId: string,
|
||||
options?: Omit<UseQueryOptions<ForecastStatistics, ApiError>, 'queryKey' | 'queryFn'>
|
||||
options?: Omit<UseQueryOptions<any, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<ForecastStatistics, ApiError>({
|
||||
return useQuery<any, ApiError>({
|
||||
queryKey: forecastingKeys.statistics(tenantId),
|
||||
queryFn: () => forecastingService.getForecastStatistics(tenantId),
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
@@ -100,9 +96,9 @@ export const useForecastStatistics = (
|
||||
* Health check for forecasting service
|
||||
*/
|
||||
export const useForecastingHealth = (
|
||||
options?: Omit<UseQueryOptions<ForecastingHealthResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
options?: Omit<UseQueryOptions<{ status: string; service: string }, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<ForecastingHealthResponse, ApiError>({
|
||||
return useQuery<{ status: string; service: string }, ApiError>({
|
||||
queryKey: forecastingKeys.health(),
|
||||
queryFn: () => forecastingService.getHealthCheck(),
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
@@ -119,24 +115,25 @@ export const useForecastingHealth = (
|
||||
*/
|
||||
export const useInfiniteTenantForecasts = (
|
||||
tenantId: string,
|
||||
baseParams?: Omit<GetForecastsParams, 'skip' | 'limit'>,
|
||||
options?: Omit<UseInfiniteQueryOptions<ForecastListResponse, ApiError>, 'queryKey' | 'queryFn' | 'getNextPageParam'>
|
||||
baseParams?: Omit<ListForecastsParams, 'skip' | 'limit'>,
|
||||
options?: Omit<UseInfiniteQueryOptions<{ forecasts: ForecastResponse[]; total: number }, ApiError>, 'queryKey' | 'queryFn' | 'getNextPageParam' | 'initialPageParam'>
|
||||
) => {
|
||||
const limit = baseParams?.limit || 20;
|
||||
const limit = 20;
|
||||
|
||||
return useInfiniteQuery<ForecastListResponse, ApiError>({
|
||||
return useInfiniteQuery<{ forecasts: ForecastResponse[]; total: number }, ApiError>({
|
||||
queryKey: [...forecastingKeys.list(tenantId, baseParams), 'infinite'],
|
||||
queryFn: ({ pageParam = 0 }) => {
|
||||
const params: GetForecastsParams = {
|
||||
const params: ListForecastsParams = {
|
||||
...baseParams,
|
||||
skip: pageParam as number,
|
||||
limit,
|
||||
};
|
||||
return forecastingService.getTenantForecasts(tenantId, params);
|
||||
},
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (lastPage, allPages) => {
|
||||
const totalFetched = allPages.reduce((sum, page) => sum + page.total_returned, 0);
|
||||
return lastPage.total_returned === limit ? totalFetched : undefined;
|
||||
const totalFetched = allPages.reduce((sum, page) => sum + page.forecasts.length, 0);
|
||||
return lastPage.forecasts.length === limit ? totalFetched : undefined;
|
||||
},
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
enabled: !!tenantId,
|
||||
@@ -222,11 +219,7 @@ export const useCreateBatchForecast = (
|
||||
data.forecasts.forEach((forecast) => {
|
||||
queryClient.setQueryData(
|
||||
forecastingKeys.detail(variables.tenantId, forecast.id),
|
||||
{
|
||||
...forecast,
|
||||
enhanced_features: true,
|
||||
repository_integration: true,
|
||||
} as ForecastByIdResponse
|
||||
forecast
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -245,7 +238,7 @@ export const useCreateBatchForecast = (
|
||||
*/
|
||||
export const useDeleteForecast = (
|
||||
options?: UseMutationOptions<
|
||||
DeleteForecastResponse,
|
||||
{ message: string },
|
||||
ApiError,
|
||||
{ tenantId: string; forecastId: string }
|
||||
>
|
||||
@@ -253,7 +246,7 @@ export const useDeleteForecast = (
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
DeleteForecastResponse,
|
||||
{ message: string },
|
||||
ApiError,
|
||||
{ tenantId: string; forecastId: string }
|
||||
>({
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
import { useMutation, useQuery, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { inventoryService } from '../services/inventory';
|
||||
import { transformationService } from '../services/transformations';
|
||||
// inventoryService merged into inventoryService
|
||||
import {
|
||||
IngredientCreate,
|
||||
IngredientUpdate,
|
||||
@@ -300,7 +300,7 @@ export const useHardDeleteIngredient = (
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.ingredients.byCategory(tenantId) });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.movements(tenantId) });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.analytics.all() });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.all });
|
||||
},
|
||||
...options,
|
||||
});
|
||||
@@ -427,7 +427,6 @@ export const useStockOperations = (tenantId: string) => {
|
||||
// Create stock entry via backend API
|
||||
const stockData: StockCreate = {
|
||||
ingredient_id: ingredientId,
|
||||
quantity,
|
||||
unit_price: unit_cost || 0,
|
||||
notes
|
||||
};
|
||||
@@ -475,7 +474,7 @@ export const useStockOperations = (tenantId: string) => {
|
||||
// Create adjustment movement via backend API
|
||||
const movementData: StockMovementCreate = {
|
||||
ingredient_id: ingredientId,
|
||||
movement_type: 'adjustment',
|
||||
movement_type: 'ADJUSTMENT' as any,
|
||||
quantity,
|
||||
notes
|
||||
};
|
||||
@@ -512,7 +511,7 @@ export const useTransformations = (
|
||||
) => {
|
||||
return useQuery<ProductTransformationResponse[], ApiError>({
|
||||
queryKey: inventoryKeys.transformations.list(tenantId, options),
|
||||
queryFn: () => transformationService.getTransformations(tenantId, options),
|
||||
queryFn: () => inventoryService.getTransformations(tenantId, options),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 1 * 60 * 1000, // 1 minute
|
||||
...queryOptions,
|
||||
@@ -526,7 +525,7 @@ export const useTransformation = (
|
||||
) => {
|
||||
return useQuery<ProductTransformationResponse, ApiError>({
|
||||
queryKey: inventoryKeys.transformations.detail(tenantId, transformationId),
|
||||
queryFn: () => transformationService.getTransformation(tenantId, transformationId),
|
||||
queryFn: () => inventoryService.getTransformation(tenantId, transformationId),
|
||||
enabled: !!tenantId && !!transformationId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
@@ -540,7 +539,7 @@ export const useTransformationSummary = (
|
||||
) => {
|
||||
return useQuery<any, ApiError>({
|
||||
queryKey: inventoryKeys.transformations.summary(tenantId, daysBack),
|
||||
queryFn: () => transformationService.getTransformationSummary(tenantId, daysBack),
|
||||
queryFn: () => inventoryService.getTransformationSummary(tenantId, daysBack),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
...options,
|
||||
@@ -555,7 +554,7 @@ export const useTransformationsByIngredient = (
|
||||
) => {
|
||||
return useQuery<ProductTransformationResponse[], ApiError>({
|
||||
queryKey: inventoryKeys.transformations.byIngredient(tenantId, ingredientId),
|
||||
queryFn: () => transformationService.getTransformationsForIngredient(tenantId, ingredientId, limit),
|
||||
queryFn: () => inventoryService.getTransformationsForIngredient(tenantId, ingredientId, limit),
|
||||
enabled: !!tenantId && !!ingredientId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
@@ -571,7 +570,7 @@ export const useTransformationsByStage = (
|
||||
) => {
|
||||
return useQuery<ProductTransformationResponse[], ApiError>({
|
||||
queryKey: inventoryKeys.transformations.byStage(tenantId, sourceStage, targetStage),
|
||||
queryFn: () => transformationService.getTransformationsByStage(tenantId, sourceStage, targetStage, limit),
|
||||
queryFn: () => inventoryService.getTransformationsByStage(tenantId, sourceStage, targetStage, limit),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
@@ -595,7 +594,7 @@ export const useCreateTransformation = (
|
||||
{ tenantId: string; transformationData: ProductTransformationCreate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, transformationData }) =>
|
||||
transformationService.createTransformation(tenantId, transformationData),
|
||||
inventoryService.createTransformation(tenantId, transformationData),
|
||||
onSuccess: (data, { tenantId, transformationData }) => {
|
||||
// Add to cache
|
||||
queryClient.setQueryData(
|
||||
@@ -650,7 +649,7 @@ export const useParBakeTransformation = (
|
||||
}
|
||||
>({
|
||||
mutationFn: ({ tenantId, ...transformationOptions }) =>
|
||||
transformationService.createParBakeToFreshTransformation(tenantId, transformationOptions),
|
||||
inventoryService.createParBakeToFreshTransformation(tenantId, transformationOptions),
|
||||
onSuccess: (data, { tenantId, source_ingredient_id, target_ingredient_id }) => {
|
||||
// Invalidate related queries
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.transformations.lists() });
|
||||
@@ -688,7 +687,7 @@ export const useTransformationOperations = (tenantId: string) => {
|
||||
expirationHours?: number;
|
||||
notes?: string;
|
||||
}) => {
|
||||
return transformationService.bakeParBakedCroissants(
|
||||
return inventoryService.bakeParBakedCroissants(
|
||||
tenantId,
|
||||
parBakedIngredientId,
|
||||
freshBakedIngredientId,
|
||||
@@ -715,7 +714,7 @@ export const useTransformationOperations = (tenantId: string) => {
|
||||
quantity: number;
|
||||
notes?: string;
|
||||
}) => {
|
||||
return transformationService.transformFrozenToPrepared(
|
||||
return inventoryService.transformFrozenToPrepared(
|
||||
tenantId,
|
||||
frozenIngredientId,
|
||||
preparedIngredientId,
|
||||
@@ -735,4 +734,13 @@ export const useTransformationOperations = (tenantId: string) => {
|
||||
bakeParBakedCroissants,
|
||||
transformFrozenToPrepared,
|
||||
};
|
||||
};
|
||||
};
|
||||
// Classification operations
|
||||
export const useClassifyBatch = (
|
||||
options?: UseMutationOptions<any, ApiError, { tenantId: string; products: { product_name: string }[] }>
|
||||
) => {
|
||||
return useMutation<any, ApiError, { tenantId: string; products: { product_name: string }[] }>({
|
||||
mutationFn: ({ tenantId, products }) => inventoryService.classifyBatch(tenantId, { products }),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
/**
|
||||
* Inventory Dashboard React Query hooks
|
||||
*/
|
||||
import { useMutation, useQuery, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { inventoryDashboardService } from '../services/inventoryDashboard';
|
||||
import {
|
||||
InventoryDashboardSummary,
|
||||
InventoryAnalytics,
|
||||
BusinessModelInsights,
|
||||
DashboardFilter,
|
||||
AlertsFilter,
|
||||
RecentActivity,
|
||||
} from '../types/dashboard';
|
||||
import { ApiError } from '../client';
|
||||
|
||||
// Query Keys
|
||||
export const inventoryDashboardKeys = {
|
||||
all: ['inventory-dashboard'] as const,
|
||||
summary: (tenantId: string, filter?: DashboardFilter) =>
|
||||
[...inventoryDashboardKeys.all, 'summary', tenantId, filter] as const,
|
||||
analytics: (tenantId: string, startDate?: string, endDate?: string) =>
|
||||
[...inventoryDashboardKeys.all, 'analytics', tenantId, { startDate, endDate }] as const,
|
||||
insights: (tenantId: string) =>
|
||||
[...inventoryDashboardKeys.all, 'business-insights', tenantId] as const,
|
||||
activity: (tenantId: string, limit?: number) =>
|
||||
[...inventoryDashboardKeys.all, 'recent-activity', tenantId, limit] as const,
|
||||
alerts: (tenantId: string, filter?: AlertsFilter) =>
|
||||
[...inventoryDashboardKeys.all, 'alerts', tenantId, filter] as const,
|
||||
stockSummary: (tenantId: string) =>
|
||||
[...inventoryDashboardKeys.all, 'stock-summary', tenantId] as const,
|
||||
topCategories: (tenantId: string, limit?: number) =>
|
||||
[...inventoryDashboardKeys.all, 'top-categories', tenantId, limit] as const,
|
||||
expiryCalendar: (tenantId: string, daysAhead?: number) =>
|
||||
[...inventoryDashboardKeys.all, 'expiry-calendar', tenantId, daysAhead] as const,
|
||||
} as const;
|
||||
|
||||
// Queries
|
||||
export const useInventoryDashboardSummary = (
|
||||
tenantId: string,
|
||||
filter?: DashboardFilter,
|
||||
options?: Omit<UseQueryOptions<InventoryDashboardSummary, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<InventoryDashboardSummary, ApiError>({
|
||||
queryKey: inventoryDashboardKeys.summary(tenantId, filter),
|
||||
queryFn: () => inventoryDashboardService.getDashboardSummary(tenantId, filter),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useInventoryAnalytics = (
|
||||
tenantId: string,
|
||||
startDate?: string,
|
||||
endDate?: string,
|
||||
options?: Omit<UseQueryOptions<InventoryAnalytics, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<InventoryAnalytics, ApiError>({
|
||||
queryKey: inventoryDashboardKeys.analytics(tenantId, startDate, endDate),
|
||||
queryFn: () => inventoryDashboardService.getInventoryAnalytics(tenantId, startDate, endDate),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useBusinessModelInsights = (
|
||||
tenantId: string,
|
||||
options?: Omit<UseQueryOptions<BusinessModelInsights, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<BusinessModelInsights, ApiError>({
|
||||
queryKey: inventoryDashboardKeys.insights(tenantId),
|
||||
queryFn: () => inventoryDashboardService.getBusinessModelInsights(tenantId),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 10 * 60 * 1000, // 10 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useRecentActivity = (
|
||||
tenantId: string,
|
||||
limit: number = 20,
|
||||
options?: Omit<UseQueryOptions<RecentActivity[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<RecentActivity[], ApiError>({
|
||||
queryKey: inventoryDashboardKeys.activity(tenantId, limit),
|
||||
queryFn: () => inventoryDashboardService.getRecentActivity(tenantId, limit),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useInventoryAlerts = (
|
||||
tenantId: string,
|
||||
filter?: AlertsFilter,
|
||||
options?: Omit<UseQueryOptions<{ items: any[]; total: number }, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<{ items: any[]; total: number }, ApiError>({
|
||||
queryKey: inventoryDashboardKeys.alerts(tenantId, filter),
|
||||
queryFn: () => inventoryDashboardService.getAlerts(tenantId, filter),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useStockSummary = (
|
||||
tenantId: string,
|
||||
options?: Omit<UseQueryOptions<{
|
||||
in_stock: number;
|
||||
low_stock: number;
|
||||
out_of_stock: number;
|
||||
overstock: number;
|
||||
total_value: number;
|
||||
}, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<{
|
||||
in_stock: number;
|
||||
low_stock: number;
|
||||
out_of_stock: number;
|
||||
overstock: number;
|
||||
total_value: number;
|
||||
}, ApiError>({
|
||||
queryKey: inventoryDashboardKeys.stockSummary(tenantId),
|
||||
queryFn: () => inventoryDashboardService.getStockSummary(tenantId),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 1 * 60 * 1000, // 1 minute
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useTopCategories = (
|
||||
tenantId: string,
|
||||
limit: number = 10,
|
||||
options?: Omit<UseQueryOptions<Array<{
|
||||
category: string;
|
||||
ingredient_count: number;
|
||||
total_value: number;
|
||||
low_stock_count: number;
|
||||
}>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<Array<{
|
||||
category: string;
|
||||
ingredient_count: number;
|
||||
total_value: number;
|
||||
low_stock_count: number;
|
||||
}>, ApiError>({
|
||||
queryKey: inventoryDashboardKeys.topCategories(tenantId, limit),
|
||||
queryFn: () => inventoryDashboardService.getTopCategories(tenantId, limit),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useExpiryCalendar = (
|
||||
tenantId: string,
|
||||
daysAhead: number = 30,
|
||||
options?: Omit<UseQueryOptions<Array<{
|
||||
date: string;
|
||||
items: Array<{
|
||||
ingredient_name: string;
|
||||
quantity: number;
|
||||
batch_number?: string;
|
||||
}>;
|
||||
}>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<Array<{
|
||||
date: string;
|
||||
items: Array<{
|
||||
ingredient_name: string;
|
||||
quantity: number;
|
||||
batch_number?: string;
|
||||
}>;
|
||||
}>, ApiError>({
|
||||
queryKey: inventoryDashboardKeys.expiryCalendar(tenantId, daysAhead),
|
||||
queryFn: () => inventoryDashboardService.getExpiryCalendar(tenantId, daysAhead),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
@@ -10,9 +10,8 @@ import type {
|
||||
ProductionBatchListResponse,
|
||||
ProductionDashboardSummary,
|
||||
DailyProductionRequirements,
|
||||
ProductionScheduleData,
|
||||
ProductionScheduleUpdate,
|
||||
ProductionCapacityStatus,
|
||||
ProductionRequirements,
|
||||
ProductionYieldMetrics,
|
||||
} from '../types/production';
|
||||
import { ApiError } from '../client';
|
||||
@@ -152,8 +151,8 @@ export const useYieldMetrics = (
|
||||
) => {
|
||||
return useQuery<any, ApiError>({
|
||||
queryKey: productionKeys.yieldMetrics(tenantId, startDate, endDate),
|
||||
queryFn: () => productionService.getYieldTrends(tenantId, startDate, endDate),
|
||||
enabled: !!tenantId && !!startDate && !!endDate,
|
||||
queryFn: () => productionService.getYieldTrends(tenantId),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 15 * 60 * 1000, // 15 minutes (metrics are less frequently changing)
|
||||
...options,
|
||||
});
|
||||
|
||||
@@ -19,7 +19,6 @@ import type {
|
||||
RecipeResponse,
|
||||
RecipeCreate,
|
||||
RecipeUpdate,
|
||||
RecipeSearchParams,
|
||||
RecipeDuplicateRequest,
|
||||
RecipeFeasibilityResponse,
|
||||
RecipeStatisticsResponse,
|
||||
@@ -31,7 +30,7 @@ export const recipesKeys = {
|
||||
all: ['recipes'] as const,
|
||||
tenant: (tenantId: string) => [...recipesKeys.all, 'tenant', tenantId] as const,
|
||||
lists: (tenantId: string) => [...recipesKeys.tenant(tenantId), 'list'] as const,
|
||||
list: (tenantId: string, filters: RecipeSearchParams) => [...recipesKeys.lists(tenantId), { filters }] as const,
|
||||
list: (tenantId: string, filters: any) => [...recipesKeys.lists(tenantId), { filters }] as const,
|
||||
details: (tenantId: string) => [...recipesKeys.tenant(tenantId), 'detail'] as const,
|
||||
detail: (tenantId: string, id: string) => [...recipesKeys.details(tenantId), id] as const,
|
||||
statistics: (tenantId: string) => [...recipesKeys.tenant(tenantId), 'statistics'] as const,
|
||||
@@ -63,7 +62,7 @@ export const useRecipe = (
|
||||
*/
|
||||
export const useRecipes = (
|
||||
tenantId: string,
|
||||
filters: RecipeSearchParams = {},
|
||||
filters: any = {},
|
||||
options?: Omit<UseQueryOptions<RecipeResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<RecipeResponse[], ApiError>({
|
||||
@@ -80,13 +79,14 @@ export const useRecipes = (
|
||||
*/
|
||||
export const useInfiniteRecipes = (
|
||||
tenantId: string,
|
||||
filters: Omit<RecipeSearchParams, 'offset'> = {},
|
||||
options?: Omit<UseInfiniteQueryOptions<RecipeResponse[], ApiError>, 'queryKey' | 'queryFn' | 'getNextPageParam'>
|
||||
filters: Omit<any, 'offset'> = {},
|
||||
options?: Omit<UseInfiniteQueryOptions<RecipeResponse[], ApiError>, 'queryKey' | 'queryFn' | 'getNextPageParam' | 'initialPageParam'>
|
||||
) => {
|
||||
return useInfiniteQuery<RecipeResponse[], ApiError>({
|
||||
queryKey: recipesKeys.list(tenantId, filters),
|
||||
queryFn: ({ pageParam = 0 }) =>
|
||||
recipesService.searchRecipes(tenantId, { ...filters, offset: pageParam }),
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (lastPage, allPages) => {
|
||||
const limit = filters.limit || 100;
|
||||
if (lastPage.length < limit) return undefined;
|
||||
|
||||
@@ -187,4 +187,29 @@ export const useValidateSalesRecord = (
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
};
|
||||
// Import/Export operations
|
||||
export const useValidateImportFile = (
|
||||
options?: UseMutationOptions<any, ApiError, { tenantId: string; file: File }>
|
||||
) => {
|
||||
return useMutation<any, ApiError, { tenantId: string; file: File }>({
|
||||
mutationFn: ({ tenantId, file }) => salesService.validateImportFile(tenantId, file),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useImportSalesData = (
|
||||
options?: UseMutationOptions<any, ApiError, { tenantId: string; file: File }>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<any, ApiError, { tenantId: string; file: File }>({
|
||||
mutationFn: ({ tenantId, file }) => salesService.importSalesData(tenantId, file),
|
||||
onSuccess: (data, { tenantId }) => {
|
||||
// Invalidate sales lists to include imported data
|
||||
queryClient.invalidateQueries({ queryKey: salesKeys.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: salesKeys.analytics(tenantId) });
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -12,23 +12,19 @@ import type {
|
||||
SupplierResponse,
|
||||
SupplierSummary,
|
||||
SupplierApproval,
|
||||
SupplierQueryParams,
|
||||
SupplierSearchParams,
|
||||
SupplierStatistics,
|
||||
TopSuppliersResponse,
|
||||
PurchaseOrderCreate,
|
||||
PurchaseOrderUpdate,
|
||||
PurchaseOrderResponse,
|
||||
PurchaseOrderApproval,
|
||||
PurchaseOrderQueryParams,
|
||||
PurchaseOrderSearchParams,
|
||||
DeliveryCreate,
|
||||
DeliveryUpdate,
|
||||
DeliveryResponse,
|
||||
DeliveryReceiptConfirmation,
|
||||
DeliveryQueryParams,
|
||||
PerformanceCalculationRequest,
|
||||
PerformanceMetrics,
|
||||
PerformanceAlert,
|
||||
PaginatedResponse,
|
||||
DeliverySearchParams,
|
||||
PerformanceMetric,
|
||||
} from '../types/suppliers';
|
||||
|
||||
// Query Keys Factory
|
||||
@@ -37,7 +33,7 @@ export const suppliersKeys = {
|
||||
suppliers: {
|
||||
all: () => [...suppliersKeys.all, 'suppliers'] as const,
|
||||
lists: () => [...suppliersKeys.suppliers.all(), 'list'] as const,
|
||||
list: (tenantId: string, params?: SupplierQueryParams) =>
|
||||
list: (tenantId: string, params?: SupplierSearchParams) =>
|
||||
[...suppliersKeys.suppliers.lists(), tenantId, params] as const,
|
||||
details: () => [...suppliersKeys.suppliers.all(), 'detail'] as const,
|
||||
detail: (tenantId: string, supplierId: string) =>
|
||||
@@ -52,7 +48,7 @@ export const suppliersKeys = {
|
||||
purchaseOrders: {
|
||||
all: () => [...suppliersKeys.all, 'purchase-orders'] as const,
|
||||
lists: () => [...suppliersKeys.purchaseOrders.all(), 'list'] as const,
|
||||
list: (params?: PurchaseOrderQueryParams) =>
|
||||
list: (params?: PurchaseOrderSearchParams) =>
|
||||
[...suppliersKeys.purchaseOrders.lists(), params] as const,
|
||||
details: () => [...suppliersKeys.purchaseOrders.all(), 'detail'] as const,
|
||||
detail: (orderId: string) =>
|
||||
@@ -61,7 +57,7 @@ export const suppliersKeys = {
|
||||
deliveries: {
|
||||
all: () => [...suppliersKeys.all, 'deliveries'] as const,
|
||||
lists: () => [...suppliersKeys.deliveries.all(), 'list'] as const,
|
||||
list: (params?: DeliveryQueryParams) =>
|
||||
list: (params?: DeliverySearchParams) =>
|
||||
[...suppliersKeys.deliveries.lists(), params] as const,
|
||||
details: () => [...suppliersKeys.deliveries.all(), 'detail'] as const,
|
||||
detail: (deliveryId: string) =>
|
||||
@@ -79,10 +75,10 @@ export const suppliersKeys = {
|
||||
// Supplier Queries
|
||||
export const useSuppliers = (
|
||||
tenantId: string,
|
||||
queryParams?: SupplierQueryParams,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<SupplierSummary>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
queryParams?: SupplierSearchParams,
|
||||
options?: Omit<UseQueryOptions<SupplierSummary[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<SupplierSummary>, ApiError>({
|
||||
return useQuery<SupplierSummary[], ApiError>({
|
||||
queryKey: suppliersKeys.suppliers.list(tenantId, queryParams),
|
||||
queryFn: () => suppliersService.getSuppliers(tenantId, queryParams),
|
||||
enabled: !!tenantId,
|
||||
@@ -120,11 +116,11 @@ export const useSupplierStatistics = (
|
||||
|
||||
export const useActiveSuppliers = (
|
||||
tenantId: string,
|
||||
queryParams?: Omit<SupplierQueryParams, 'status'>,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<SupplierSummary>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
queryParams?: Omit<SupplierSearchParams, 'status'>,
|
||||
options?: Omit<UseQueryOptions<SupplierSummary[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<SupplierSummary>, ApiError>({
|
||||
queryKey: suppliersKeys.suppliers.list(tenantId, { ...queryParams, status: 'active' }),
|
||||
return useQuery<SupplierSummary[], ApiError>({
|
||||
queryKey: suppliersKeys.suppliers.list(tenantId, { ...queryParams }),
|
||||
queryFn: () => suppliersService.getActiveSuppliers(tenantId, queryParams),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
@@ -134,9 +130,9 @@ export const useActiveSuppliers = (
|
||||
|
||||
export const useTopSuppliers = (
|
||||
tenantId: string,
|
||||
options?: Omit<UseQueryOptions<TopSuppliersResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
options?: Omit<UseQueryOptions<SupplierResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<TopSuppliersResponse, ApiError>({
|
||||
return useQuery<SupplierResponse[], ApiError>({
|
||||
queryKey: suppliersKeys.suppliers.top(tenantId),
|
||||
queryFn: () => suppliersService.getTopSuppliers(tenantId),
|
||||
enabled: !!tenantId,
|
||||
@@ -147,10 +143,10 @@ export const useTopSuppliers = (
|
||||
|
||||
export const usePendingApprovalSuppliers = (
|
||||
tenantId: string,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<SupplierSummary>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
options?: Omit<UseQueryOptions<SupplierSummary[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<SupplierSummary>, ApiError>({
|
||||
queryKey: suppliersKeys.suppliers.list(tenantId, { status: 'pending_approval' }),
|
||||
return useQuery<SupplierSummary[], ApiError>({
|
||||
queryKey: suppliersKeys.suppliers.list(tenantId, {}),
|
||||
queryFn: () => suppliersService.getPendingApprovalSuppliers(tenantId),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 1 * 60 * 1000, // 1 minute
|
||||
@@ -161,10 +157,10 @@ export const usePendingApprovalSuppliers = (
|
||||
export const useSuppliersByType = (
|
||||
tenantId: string,
|
||||
supplierType: string,
|
||||
queryParams?: Omit<SupplierQueryParams, 'supplier_type'>,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<SupplierSummary>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
queryParams?: Omit<SupplierSearchParams, 'supplier_type'>,
|
||||
options?: Omit<UseQueryOptions<SupplierSummary[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<SupplierSummary>, ApiError>({
|
||||
return useQuery<SupplierSummary[], ApiError>({
|
||||
queryKey: suppliersKeys.suppliers.byType(tenantId, supplierType),
|
||||
queryFn: () => suppliersService.getSuppliersByType(tenantId, supplierType, queryParams),
|
||||
enabled: !!tenantId && !!supplierType,
|
||||
@@ -175,25 +171,28 @@ export const useSuppliersByType = (
|
||||
|
||||
// Purchase Order Queries
|
||||
export const usePurchaseOrders = (
|
||||
queryParams?: PurchaseOrderQueryParams,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<PurchaseOrderResponse>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
tenantId: string,
|
||||
queryParams?: PurchaseOrderSearchParams,
|
||||
options?: Omit<UseQueryOptions<PurchaseOrderResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<PurchaseOrderResponse>, ApiError>({
|
||||
return useQuery<PurchaseOrderResponse[], ApiError>({
|
||||
queryKey: suppliersKeys.purchaseOrders.list(queryParams),
|
||||
queryFn: () => suppliersService.getPurchaseOrders(queryParams),
|
||||
queryFn: () => suppliersService.getPurchaseOrders(tenantId, queryParams as any),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 1 * 60 * 1000, // 1 minute
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const usePurchaseOrder = (
|
||||
tenantId: string,
|
||||
orderId: string,
|
||||
options?: Omit<UseQueryOptions<PurchaseOrderResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PurchaseOrderResponse, ApiError>({
|
||||
queryKey: suppliersKeys.purchaseOrders.detail(orderId),
|
||||
queryFn: () => suppliersService.getPurchaseOrder(orderId),
|
||||
enabled: !!orderId,
|
||||
queryFn: () => suppliersService.getPurchaseOrder(tenantId, orderId),
|
||||
enabled: !!tenantId && !!orderId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
});
|
||||
@@ -201,25 +200,28 @@ export const usePurchaseOrder = (
|
||||
|
||||
// Delivery Queries
|
||||
export const useDeliveries = (
|
||||
queryParams?: DeliveryQueryParams,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<DeliveryResponse>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
tenantId: string,
|
||||
queryParams?: DeliverySearchParams,
|
||||
options?: Omit<UseQueryOptions<DeliveryResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<DeliveryResponse>, ApiError>({
|
||||
return useQuery<DeliveryResponse[], ApiError>({
|
||||
queryKey: suppliersKeys.deliveries.list(queryParams),
|
||||
queryFn: () => suppliersService.getDeliveries(queryParams),
|
||||
queryFn: () => suppliersService.getDeliveries(tenantId, queryParams as any),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 1 * 60 * 1000, // 1 minute
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useDelivery = (
|
||||
tenantId: string,
|
||||
deliveryId: string,
|
||||
options?: Omit<UseQueryOptions<DeliveryResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<DeliveryResponse, ApiError>({
|
||||
queryKey: suppliersKeys.deliveries.detail(deliveryId),
|
||||
queryFn: () => suppliersService.getDelivery(deliveryId),
|
||||
enabled: !!deliveryId,
|
||||
queryFn: () => suppliersService.getDelivery(tenantId, deliveryId),
|
||||
enabled: !!tenantId && !!deliveryId,
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
...options,
|
||||
});
|
||||
@@ -229,11 +231,11 @@ export const useDelivery = (
|
||||
export const useSupplierPerformanceMetrics = (
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
options?: Omit<UseQueryOptions<PerformanceMetrics, ApiError>, 'queryKey' | 'queryFn'>
|
||||
options?: Omit<UseQueryOptions<PerformanceMetric[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PerformanceMetrics, ApiError>({
|
||||
return useQuery<PerformanceMetric[], ApiError>({
|
||||
queryKey: suppliersKeys.performance.metrics(tenantId, supplierId),
|
||||
queryFn: () => suppliersService.getSupplierPerformanceMetrics(tenantId, supplierId),
|
||||
queryFn: () => suppliersService.getPerformanceMetrics(tenantId, supplierId),
|
||||
enabled: !!tenantId && !!supplierId,
|
||||
staleTime: 10 * 60 * 1000, // 10 minutes
|
||||
...options,
|
||||
@@ -242,13 +244,13 @@ export const useSupplierPerformanceMetrics = (
|
||||
|
||||
export const usePerformanceAlerts = (
|
||||
tenantId: string,
|
||||
supplierId?: string,
|
||||
options?: Omit<UseQueryOptions<PerformanceAlert[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
supplierId: string,
|
||||
options?: Omit<UseQueryOptions<any[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PerformanceAlert[], ApiError>({
|
||||
return useQuery<any[], ApiError>({
|
||||
queryKey: suppliersKeys.performance.alerts(tenantId, supplierId),
|
||||
queryFn: () => suppliersService.getPerformanceAlerts(tenantId, supplierId),
|
||||
enabled: !!tenantId,
|
||||
queryFn: () => suppliersService.evaluatePerformanceAlerts(tenantId, supplierId),
|
||||
enabled: !!tenantId && !!supplierId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
});
|
||||
@@ -614,7 +616,7 @@ export const useEvaluatePerformanceAlerts = (
|
||||
ApiError,
|
||||
{ tenantId: string }
|
||||
>({
|
||||
mutationFn: ({ tenantId }) => suppliersService.evaluatePerformanceAlerts(tenantId),
|
||||
mutationFn: ({ tenantId, supplierId }) => suppliersService.evaluatePerformanceAlerts(tenantId, supplierId),
|
||||
onSuccess: (_, { tenantId }) => {
|
||||
// Invalidate performance alerts
|
||||
queryClient.invalidateQueries({
|
||||
|
||||
Reference in New Issue
Block a user