Add frontend order API
This commit is contained in:
332
frontend/src/api/hooks/orders.ts
Normal file
332
frontend/src/api/hooks/orders.ts
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
/**
|
||||||
|
* Orders React Query hooks
|
||||||
|
*/
|
||||||
|
import { useMutation, useQuery, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
||||||
|
import { OrdersService } from '../services/orders';
|
||||||
|
import {
|
||||||
|
OrderResponse,
|
||||||
|
OrderCreate,
|
||||||
|
OrderUpdate,
|
||||||
|
CustomerResponse,
|
||||||
|
CustomerCreate,
|
||||||
|
CustomerUpdate,
|
||||||
|
OrdersDashboardSummary,
|
||||||
|
DemandRequirements,
|
||||||
|
BusinessModelDetection,
|
||||||
|
ServiceStatus,
|
||||||
|
GetOrdersParams,
|
||||||
|
GetCustomersParams,
|
||||||
|
UpdateOrderStatusParams,
|
||||||
|
GetDemandRequirementsParams,
|
||||||
|
} from '../types/orders';
|
||||||
|
import { ApiError } from '../client/apiClient';
|
||||||
|
|
||||||
|
// Query Keys
|
||||||
|
export const ordersKeys = {
|
||||||
|
all: ['orders'] as const,
|
||||||
|
|
||||||
|
// Orders
|
||||||
|
orders: () => [...ordersKeys.all, 'orders'] as const,
|
||||||
|
ordersList: (params: GetOrdersParams) => [...ordersKeys.orders(), 'list', params] as const,
|
||||||
|
order: (tenantId: string, orderId: string) => [...ordersKeys.orders(), 'detail', tenantId, orderId] as const,
|
||||||
|
|
||||||
|
// Customers
|
||||||
|
customers: () => [...ordersKeys.all, 'customers'] as const,
|
||||||
|
customersList: (params: GetCustomersParams) => [...ordersKeys.customers(), 'list', params] as const,
|
||||||
|
customer: (tenantId: string, customerId: string) => [...ordersKeys.customers(), 'detail', tenantId, customerId] as const,
|
||||||
|
|
||||||
|
// Dashboard & Analytics
|
||||||
|
dashboard: (tenantId: string) => [...ordersKeys.all, 'dashboard', tenantId] as const,
|
||||||
|
demandRequirements: (params: GetDemandRequirementsParams) => [...ordersKeys.all, 'demand', params] as const,
|
||||||
|
businessModel: (tenantId: string) => [...ordersKeys.all, 'business-model', tenantId] as const,
|
||||||
|
|
||||||
|
// Status
|
||||||
|
status: (tenantId: string) => [...ordersKeys.all, 'status', tenantId] as const,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// ===== Order Queries =====
|
||||||
|
|
||||||
|
export const useOrders = (
|
||||||
|
params: GetOrdersParams,
|
||||||
|
options?: Omit<UseQueryOptions<OrderResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||||||
|
) => {
|
||||||
|
return useQuery<OrderResponse[], ApiError>({
|
||||||
|
queryKey: ordersKeys.ordersList(params),
|
||||||
|
queryFn: () => OrdersService.getOrders(params),
|
||||||
|
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useOrder = (
|
||||||
|
tenantId: string,
|
||||||
|
orderId: string,
|
||||||
|
options?: Omit<UseQueryOptions<OrderResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||||
|
) => {
|
||||||
|
return useQuery<OrderResponse, ApiError>({
|
||||||
|
queryKey: ordersKeys.order(tenantId, orderId),
|
||||||
|
queryFn: () => OrdersService.getOrder(tenantId, orderId),
|
||||||
|
staleTime: 1 * 60 * 1000, // 1 minute
|
||||||
|
enabled: !!tenantId && !!orderId,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== Customer Queries =====
|
||||||
|
|
||||||
|
export const useCustomers = (
|
||||||
|
params: GetCustomersParams,
|
||||||
|
options?: Omit<UseQueryOptions<CustomerResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||||||
|
) => {
|
||||||
|
return useQuery<CustomerResponse[], ApiError>({
|
||||||
|
queryKey: ordersKeys.customersList(params),
|
||||||
|
queryFn: () => OrdersService.getCustomers(params),
|
||||||
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCustomer = (
|
||||||
|
tenantId: string,
|
||||||
|
customerId: string,
|
||||||
|
options?: Omit<UseQueryOptions<CustomerResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||||
|
) => {
|
||||||
|
return useQuery<CustomerResponse, ApiError>({
|
||||||
|
queryKey: ordersKeys.customer(tenantId, customerId),
|
||||||
|
queryFn: () => OrdersService.getCustomer(tenantId, customerId),
|
||||||
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||||
|
enabled: !!tenantId && !!customerId,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== Dashboard & Analytics Queries =====
|
||||||
|
|
||||||
|
export const useOrdersDashboard = (
|
||||||
|
tenantId: string,
|
||||||
|
options?: Omit<UseQueryOptions<OrdersDashboardSummary, ApiError>, 'queryKey' | 'queryFn'>
|
||||||
|
) => {
|
||||||
|
return useQuery<OrdersDashboardSummary, ApiError>({
|
||||||
|
queryKey: ordersKeys.dashboard(tenantId),
|
||||||
|
queryFn: () => OrdersService.getDashboardSummary(tenantId),
|
||||||
|
staleTime: 1 * 60 * 1000, // 1 minute
|
||||||
|
enabled: !!tenantId,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDemandRequirements = (
|
||||||
|
params: GetDemandRequirementsParams,
|
||||||
|
options?: Omit<UseQueryOptions<DemandRequirements, ApiError>, 'queryKey' | 'queryFn'>
|
||||||
|
) => {
|
||||||
|
return useQuery<DemandRequirements, ApiError>({
|
||||||
|
queryKey: ordersKeys.demandRequirements(params),
|
||||||
|
queryFn: () => OrdersService.getDemandRequirements(params),
|
||||||
|
staleTime: 30 * 60 * 1000, // 30 minutes
|
||||||
|
enabled: !!params.tenant_id && !!params.target_date,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useBusinessModelDetection = (
|
||||||
|
tenantId: string,
|
||||||
|
options?: Omit<UseQueryOptions<BusinessModelDetection, ApiError>, 'queryKey' | 'queryFn'>
|
||||||
|
) => {
|
||||||
|
return useQuery<BusinessModelDetection, ApiError>({
|
||||||
|
queryKey: ordersKeys.businessModel(tenantId),
|
||||||
|
queryFn: () => OrdersService.detectBusinessModel(tenantId),
|
||||||
|
staleTime: 60 * 60 * 1000, // 1 hour
|
||||||
|
enabled: !!tenantId,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useOrdersServiceStatus = (
|
||||||
|
tenantId: string,
|
||||||
|
options?: Omit<UseQueryOptions<ServiceStatus, ApiError>, 'queryKey' | 'queryFn'>
|
||||||
|
) => {
|
||||||
|
return useQuery<ServiceStatus, ApiError>({
|
||||||
|
queryKey: ordersKeys.status(tenantId),
|
||||||
|
queryFn: () => OrdersService.getServiceStatus(tenantId),
|
||||||
|
staleTime: 30 * 1000, // 30 seconds
|
||||||
|
enabled: !!tenantId,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== Order Mutations =====
|
||||||
|
|
||||||
|
export const useCreateOrder = (
|
||||||
|
options?: UseMutationOptions<OrderResponse, ApiError, OrderCreate>
|
||||||
|
) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<OrderResponse, ApiError, OrderCreate>({
|
||||||
|
mutationFn: (orderData: OrderCreate) => OrdersService.createOrder(orderData),
|
||||||
|
onSuccess: (data, variables) => {
|
||||||
|
// Invalidate orders list for this tenant
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.orders(),
|
||||||
|
predicate: (query) => {
|
||||||
|
const queryKey = query.queryKey as string[];
|
||||||
|
return queryKey.includes('list') &&
|
||||||
|
JSON.stringify(queryKey).includes(variables.tenant_id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Invalidate dashboard
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.dashboard(variables.tenant_id),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the new order to cache
|
||||||
|
queryClient.setQueryData(
|
||||||
|
ordersKeys.order(variables.tenant_id, data.id),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateOrderStatus = (
|
||||||
|
options?: UseMutationOptions<OrderResponse, ApiError, UpdateOrderStatusParams>
|
||||||
|
) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<OrderResponse, ApiError, UpdateOrderStatusParams>({
|
||||||
|
mutationFn: (params: UpdateOrderStatusParams) => OrdersService.updateOrderStatus(params),
|
||||||
|
onSuccess: (data, variables) => {
|
||||||
|
// Update the specific order in cache
|
||||||
|
queryClient.setQueryData(
|
||||||
|
ordersKeys.order(variables.tenant_id, variables.order_id),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
// Invalidate orders list for this tenant
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.orders(),
|
||||||
|
predicate: (query) => {
|
||||||
|
const queryKey = query.queryKey as string[];
|
||||||
|
return queryKey.includes('list') &&
|
||||||
|
JSON.stringify(queryKey).includes(variables.tenant_id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Invalidate dashboard
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.dashboard(variables.tenant_id),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== Customer Mutations =====
|
||||||
|
|
||||||
|
export const useCreateCustomer = (
|
||||||
|
options?: UseMutationOptions<CustomerResponse, ApiError, CustomerCreate>
|
||||||
|
) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<CustomerResponse, ApiError, CustomerCreate>({
|
||||||
|
mutationFn: (customerData: CustomerCreate) => OrdersService.createCustomer(customerData),
|
||||||
|
onSuccess: (data, variables) => {
|
||||||
|
// Invalidate customers list for this tenant
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.customers(),
|
||||||
|
predicate: (query) => {
|
||||||
|
const queryKey = query.queryKey as string[];
|
||||||
|
return queryKey.includes('list') &&
|
||||||
|
JSON.stringify(queryKey).includes(variables.tenant_id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the new customer to cache
|
||||||
|
queryClient.setQueryData(
|
||||||
|
ordersKeys.customer(variables.tenant_id, data.id),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
// Invalidate dashboard (for customer metrics)
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.dashboard(variables.tenant_id),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateCustomer = (
|
||||||
|
options?: UseMutationOptions<CustomerResponse, ApiError, { tenantId: string; customerId: string; data: CustomerUpdate }>
|
||||||
|
) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<CustomerResponse, ApiError, { tenantId: string; customerId: string; data: CustomerUpdate }>({
|
||||||
|
mutationFn: ({ tenantId, customerId, data }) => OrdersService.updateCustomer(tenantId, customerId, data),
|
||||||
|
onSuccess: (data, variables) => {
|
||||||
|
// Update the specific customer in cache
|
||||||
|
queryClient.setQueryData(
|
||||||
|
ordersKeys.customer(variables.tenantId, variables.customerId),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
// Invalidate customers list for this tenant
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.customers(),
|
||||||
|
predicate: (query) => {
|
||||||
|
const queryKey = query.queryKey as string[];
|
||||||
|
return queryKey.includes('list') &&
|
||||||
|
JSON.stringify(queryKey).includes(variables.tenantId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== Utility Functions =====
|
||||||
|
|
||||||
|
export const useInvalidateOrders = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return {
|
||||||
|
invalidateAllOrders: (tenantId?: string) => {
|
||||||
|
if (tenantId) {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.all,
|
||||||
|
predicate: (query) => {
|
||||||
|
return JSON.stringify(query.queryKey).includes(tenantId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ordersKeys.all });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
invalidateOrdersList: (tenantId: string) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.orders(),
|
||||||
|
predicate: (query) => {
|
||||||
|
const queryKey = query.queryKey as string[];
|
||||||
|
return queryKey.includes('list') &&
|
||||||
|
JSON.stringify(queryKey).includes(tenantId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
invalidateCustomersList: (tenantId: string) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.customers(),
|
||||||
|
predicate: (query) => {
|
||||||
|
const queryKey = query.queryKey as string[];
|
||||||
|
return queryKey.includes('list') &&
|
||||||
|
JSON.stringify(queryKey).includes(tenantId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
invalidateDashboard: (tenantId: string) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersKeys.dashboard(tenantId),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -24,6 +24,7 @@ export { foodSafetyService } from './services/foodSafety';
|
|||||||
export { trainingService } from './services/training';
|
export { trainingService } from './services/training';
|
||||||
export { alertProcessorService } from './services/alert_processor';
|
export { alertProcessorService } from './services/alert_processor';
|
||||||
export { suppliersService } from './services/suppliers';
|
export { suppliersService } from './services/suppliers';
|
||||||
|
export { OrdersService } from './services/orders';
|
||||||
|
|
||||||
// Types - Auth
|
// Types - Auth
|
||||||
export type {
|
export type {
|
||||||
@@ -234,6 +235,42 @@ export {
|
|||||||
PerformanceMetricType,
|
PerformanceMetricType,
|
||||||
} from './types/suppliers';
|
} from './types/suppliers';
|
||||||
|
|
||||||
|
// Types - Orders
|
||||||
|
export type {
|
||||||
|
CustomerType,
|
||||||
|
DeliveryMethod,
|
||||||
|
PaymentTerms as OrdersPaymentTerms,
|
||||||
|
PaymentMethod,
|
||||||
|
PaymentStatus,
|
||||||
|
CustomerSegment,
|
||||||
|
PriorityLevel,
|
||||||
|
OrderType,
|
||||||
|
OrderStatus,
|
||||||
|
OrderSource,
|
||||||
|
SalesChannel,
|
||||||
|
BusinessModel,
|
||||||
|
CustomerBase,
|
||||||
|
CustomerCreate,
|
||||||
|
CustomerUpdate,
|
||||||
|
CustomerResponse,
|
||||||
|
OrderItemBase,
|
||||||
|
OrderItemCreate,
|
||||||
|
OrderItemUpdate,
|
||||||
|
OrderItemResponse,
|
||||||
|
OrderBase,
|
||||||
|
OrderCreate,
|
||||||
|
OrderUpdate,
|
||||||
|
OrderResponse,
|
||||||
|
OrdersDashboardSummary,
|
||||||
|
DemandRequirements,
|
||||||
|
BusinessModelDetection,
|
||||||
|
ServiceStatus,
|
||||||
|
GetOrdersParams,
|
||||||
|
GetCustomersParams,
|
||||||
|
UpdateOrderStatusParams,
|
||||||
|
GetDemandRequirementsParams,
|
||||||
|
} from './types/orders';
|
||||||
|
|
||||||
// Hooks - Auth
|
// Hooks - Auth
|
||||||
export {
|
export {
|
||||||
useAuthProfile,
|
useAuthProfile,
|
||||||
@@ -460,6 +497,24 @@ export {
|
|||||||
suppliersKeys,
|
suppliersKeys,
|
||||||
} from './hooks/suppliers';
|
} from './hooks/suppliers';
|
||||||
|
|
||||||
|
// Hooks - Orders
|
||||||
|
export {
|
||||||
|
useOrders,
|
||||||
|
useOrder,
|
||||||
|
useCustomers,
|
||||||
|
useCustomer,
|
||||||
|
useOrdersDashboard,
|
||||||
|
useDemandRequirements,
|
||||||
|
useBusinessModelDetection,
|
||||||
|
useOrdersServiceStatus,
|
||||||
|
useCreateOrder,
|
||||||
|
useUpdateOrderStatus,
|
||||||
|
useCreateCustomer,
|
||||||
|
useUpdateCustomer,
|
||||||
|
useInvalidateOrders,
|
||||||
|
ordersKeys,
|
||||||
|
} from './hooks/orders';
|
||||||
|
|
||||||
// Query Key Factories (for advanced usage)
|
// Query Key Factories (for advanced usage)
|
||||||
export {
|
export {
|
||||||
authKeys,
|
authKeys,
|
||||||
@@ -474,5 +529,6 @@ export {
|
|||||||
trainingKeys,
|
trainingKeys,
|
||||||
alertProcessorKeys,
|
alertProcessorKeys,
|
||||||
suppliersKeys,
|
suppliersKeys,
|
||||||
|
ordersKeys,
|
||||||
dataImportKeys,
|
dataImportKeys,
|
||||||
};
|
};
|
||||||
173
frontend/src/api/services/orders.ts
Normal file
173
frontend/src/api/services/orders.ts
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
* Orders Service - API endpoints for Orders Service
|
||||||
|
*
|
||||||
|
* This service mirrors the backend API endpoints defined in:
|
||||||
|
* services/orders/app/api/orders.py
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { apiClient } from '../client/apiClient';
|
||||||
|
import {
|
||||||
|
OrderResponse,
|
||||||
|
OrderCreate,
|
||||||
|
OrderUpdate,
|
||||||
|
CustomerResponse,
|
||||||
|
CustomerCreate,
|
||||||
|
CustomerUpdate,
|
||||||
|
OrdersDashboardSummary,
|
||||||
|
DemandRequirements,
|
||||||
|
BusinessModelDetection,
|
||||||
|
ServiceStatus,
|
||||||
|
GetOrdersParams,
|
||||||
|
GetCustomersParams,
|
||||||
|
UpdateOrderStatusParams,
|
||||||
|
GetDemandRequirementsParams,
|
||||||
|
} from '../types/orders';
|
||||||
|
|
||||||
|
export class OrdersService {
|
||||||
|
// ===== Dashboard and Analytics Endpoints =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get comprehensive dashboard summary for orders
|
||||||
|
* GET /tenants/{tenant_id}/orders/dashboard-summary
|
||||||
|
*/
|
||||||
|
static async getDashboardSummary(tenantId: string): Promise<OrdersDashboardSummary> {
|
||||||
|
return apiClient.get<OrdersDashboardSummary>(`/tenants/${tenantId}/orders/dashboard-summary`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get demand requirements for production planning
|
||||||
|
* GET /tenants/{tenant_id}/orders/demand-requirements
|
||||||
|
*/
|
||||||
|
static async getDemandRequirements(params: GetDemandRequirementsParams): Promise<DemandRequirements> {
|
||||||
|
const { tenant_id, target_date } = params;
|
||||||
|
return apiClient.get<DemandRequirements>(
|
||||||
|
`/tenants/${tenant_id}/orders/demand-requirements?target_date=${target_date}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Order Management Endpoints =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new customer order
|
||||||
|
* POST /tenants/{tenant_id}/orders
|
||||||
|
*/
|
||||||
|
static async createOrder(orderData: OrderCreate): Promise<OrderResponse> {
|
||||||
|
const { tenant_id, ...data } = orderData;
|
||||||
|
return apiClient.post<OrderResponse>(`/tenants/${tenant_id}/orders`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get order details with items
|
||||||
|
* GET /tenants/{tenant_id}/orders/{order_id}
|
||||||
|
*/
|
||||||
|
static async getOrder(tenantId: string, orderId: string): Promise<OrderResponse> {
|
||||||
|
return apiClient.get<OrderResponse>(`/tenants/${tenantId}/orders/${orderId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get orders with filtering and pagination
|
||||||
|
* GET /tenants/{tenant_id}/orders
|
||||||
|
*/
|
||||||
|
static async getOrders(params: GetOrdersParams): Promise<OrderResponse[]> {
|
||||||
|
const { tenant_id, status_filter, start_date, end_date, skip = 0, limit = 100 } = params;
|
||||||
|
|
||||||
|
const queryParams = new URLSearchParams({
|
||||||
|
skip: skip.toString(),
|
||||||
|
limit: limit.toString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status_filter) {
|
||||||
|
queryParams.append('status_filter', status_filter);
|
||||||
|
}
|
||||||
|
if (start_date) {
|
||||||
|
queryParams.append('start_date', start_date);
|
||||||
|
}
|
||||||
|
if (end_date) {
|
||||||
|
queryParams.append('end_date', end_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiClient.get<OrderResponse[]>(`/tenants/${tenant_id}/orders?${queryParams.toString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update order status
|
||||||
|
* PUT /tenants/{tenant_id}/orders/{order_id}/status
|
||||||
|
*/
|
||||||
|
static async updateOrderStatus(params: UpdateOrderStatusParams): Promise<OrderResponse> {
|
||||||
|
const { tenant_id, order_id, new_status, reason } = params;
|
||||||
|
|
||||||
|
const queryParams = new URLSearchParams();
|
||||||
|
if (reason) {
|
||||||
|
queryParams.append('reason', reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `/tenants/${tenant_id}/orders/${order_id}/status${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
|
||||||
|
|
||||||
|
return apiClient.put<OrderResponse>(url, { status: new_status });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Customer Management Endpoints =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new customer
|
||||||
|
* POST /tenants/{tenant_id}/customers
|
||||||
|
*/
|
||||||
|
static async createCustomer(customerData: CustomerCreate): Promise<CustomerResponse> {
|
||||||
|
const { tenant_id, ...data } = customerData;
|
||||||
|
return apiClient.post<CustomerResponse>(`/tenants/${tenant_id}/customers`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get customers with filtering and pagination
|
||||||
|
* GET /tenants/{tenant_id}/customers
|
||||||
|
*/
|
||||||
|
static async getCustomers(params: GetCustomersParams): Promise<CustomerResponse[]> {
|
||||||
|
const { tenant_id, active_only = true, skip = 0, limit = 100 } = params;
|
||||||
|
|
||||||
|
const queryParams = new URLSearchParams({
|
||||||
|
active_only: active_only.toString(),
|
||||||
|
skip: skip.toString(),
|
||||||
|
limit: limit.toString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return apiClient.get<CustomerResponse[]>(`/tenants/${tenant_id}/customers?${queryParams.toString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get customer details
|
||||||
|
* GET /tenants/{tenant_id}/customers/{customer_id}
|
||||||
|
*/
|
||||||
|
static async getCustomer(tenantId: string, customerId: string): Promise<CustomerResponse> {
|
||||||
|
return apiClient.get<CustomerResponse>(`/tenants/${tenantId}/customers/${customerId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update customer details
|
||||||
|
* PUT /tenants/{tenant_id}/customers/{customer_id}
|
||||||
|
*/
|
||||||
|
static async updateCustomer(tenantId: string, customerId: string, customerData: CustomerUpdate): Promise<CustomerResponse> {
|
||||||
|
return apiClient.put<CustomerResponse>(`/tenants/${tenantId}/customers/${customerId}`, customerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Business Intelligence Endpoints =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect business model based on order patterns
|
||||||
|
* GET /tenants/{tenant_id}/orders/business-model
|
||||||
|
*/
|
||||||
|
static async detectBusinessModel(tenantId: string): Promise<BusinessModelDetection> {
|
||||||
|
return apiClient.get<BusinessModelDetection>(`/tenants/${tenantId}/orders/business-model`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Health and Status Endpoints =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get orders service status
|
||||||
|
* GET /tenants/{tenant_id}/orders/status
|
||||||
|
*/
|
||||||
|
static async getServiceStatus(tenantId: string): Promise<ServiceStatus> {
|
||||||
|
return apiClient.get<ServiceStatus>(`/tenants/${tenantId}/orders/status`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrdersService;
|
||||||
283
frontend/src/api/types/orders.ts
Normal file
283
frontend/src/api/types/orders.ts
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
/**
|
||||||
|
* TypeScript types for Orders Service
|
||||||
|
* Based on backend schemas in services/orders/app/schemas/order_schemas.py
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type CustomerType = 'individual' | 'business' | 'central_bakery';
|
||||||
|
export type DeliveryMethod = 'delivery' | 'pickup';
|
||||||
|
export type PaymentTerms = 'immediate' | 'net_30' | 'net_60';
|
||||||
|
export type PaymentMethod = 'cash' | 'card' | 'bank_transfer' | 'account';
|
||||||
|
export type PaymentStatus = 'pending' | 'partial' | 'paid' | 'failed' | 'refunded';
|
||||||
|
export type CustomerSegment = 'vip' | 'regular' | 'wholesale';
|
||||||
|
export type PriorityLevel = 'high' | 'normal' | 'low';
|
||||||
|
export type OrderType = 'standard' | 'rush' | 'recurring' | 'special';
|
||||||
|
export type OrderStatus = 'pending' | 'confirmed' | 'in_production' | 'ready' | 'out_for_delivery' | 'delivered' | 'cancelled' | 'failed';
|
||||||
|
export type OrderSource = 'manual' | 'online' | 'phone' | 'app' | 'api';
|
||||||
|
export type SalesChannel = 'direct' | 'wholesale' | 'retail';
|
||||||
|
export type BusinessModel = 'individual_bakery' | 'central_bakery';
|
||||||
|
|
||||||
|
// ===== Customer Types =====
|
||||||
|
|
||||||
|
export interface CustomerBase {
|
||||||
|
name: string;
|
||||||
|
business_name?: string;
|
||||||
|
customer_type: CustomerType;
|
||||||
|
email?: string;
|
||||||
|
phone?: string;
|
||||||
|
address_line1?: string;
|
||||||
|
address_line2?: string;
|
||||||
|
city?: string;
|
||||||
|
state?: string;
|
||||||
|
postal_code?: string;
|
||||||
|
country: string;
|
||||||
|
is_active: boolean;
|
||||||
|
preferred_delivery_method: DeliveryMethod;
|
||||||
|
payment_terms: PaymentTerms;
|
||||||
|
credit_limit?: number;
|
||||||
|
discount_percentage: number;
|
||||||
|
customer_segment: CustomerSegment;
|
||||||
|
priority_level: PriorityLevel;
|
||||||
|
special_instructions?: string;
|
||||||
|
delivery_preferences?: Record<string, any>;
|
||||||
|
product_preferences?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomerCreate extends CustomerBase {
|
||||||
|
customer_code: string;
|
||||||
|
tenant_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomerUpdate extends Partial<Omit<CustomerBase, 'country' | 'is_active' | 'preferred_delivery_method' | 'payment_terms' | 'discount_percentage' | 'customer_segment' | 'priority_level'>> {
|
||||||
|
country?: string;
|
||||||
|
is_active?: boolean;
|
||||||
|
preferred_delivery_method?: DeliveryMethod;
|
||||||
|
payment_terms?: PaymentTerms;
|
||||||
|
discount_percentage?: number;
|
||||||
|
customer_segment?: CustomerSegment;
|
||||||
|
priority_level?: PriorityLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomerResponse extends CustomerBase {
|
||||||
|
id: string;
|
||||||
|
tenant_id: string;
|
||||||
|
customer_code: string;
|
||||||
|
total_orders: number;
|
||||||
|
total_spent: number;
|
||||||
|
average_order_value: number;
|
||||||
|
last_order_date?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Order Item Types =====
|
||||||
|
|
||||||
|
export interface OrderItemBase {
|
||||||
|
product_id: string;
|
||||||
|
product_name: string;
|
||||||
|
product_sku?: string;
|
||||||
|
product_category?: string;
|
||||||
|
quantity: number;
|
||||||
|
unit_of_measure: string;
|
||||||
|
weight?: number;
|
||||||
|
unit_price: number;
|
||||||
|
line_discount: number;
|
||||||
|
product_specifications?: Record<string, any>;
|
||||||
|
customization_details?: string;
|
||||||
|
special_instructions?: string;
|
||||||
|
recipe_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderItemCreate extends OrderItemBase {}
|
||||||
|
|
||||||
|
export interface OrderItemUpdate {
|
||||||
|
quantity?: number;
|
||||||
|
unit_price?: number;
|
||||||
|
line_discount?: number;
|
||||||
|
product_specifications?: Record<string, any>;
|
||||||
|
customization_details?: string;
|
||||||
|
special_instructions?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderItemResponse extends OrderItemBase {
|
||||||
|
id: string;
|
||||||
|
order_id: string;
|
||||||
|
line_total: number;
|
||||||
|
status: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Order Types =====
|
||||||
|
|
||||||
|
export interface OrderBase {
|
||||||
|
customer_id: string;
|
||||||
|
order_type: OrderType;
|
||||||
|
priority: PriorityLevel;
|
||||||
|
requested_delivery_date: string;
|
||||||
|
delivery_method: DeliveryMethod;
|
||||||
|
delivery_address?: Record<string, any>;
|
||||||
|
delivery_instructions?: string;
|
||||||
|
delivery_window_start?: string;
|
||||||
|
delivery_window_end?: string;
|
||||||
|
discount_percentage: number;
|
||||||
|
delivery_fee: number;
|
||||||
|
payment_method?: PaymentMethod;
|
||||||
|
payment_terms: PaymentTerms;
|
||||||
|
special_instructions?: string;
|
||||||
|
custom_requirements?: Record<string, any>;
|
||||||
|
allergen_warnings?: Record<string, any>;
|
||||||
|
order_source: OrderSource;
|
||||||
|
sales_channel: SalesChannel;
|
||||||
|
order_origin?: string;
|
||||||
|
communication_preferences?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderCreate extends OrderBase {
|
||||||
|
tenant_id: string;
|
||||||
|
items: OrderItemCreate[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderUpdate {
|
||||||
|
status?: OrderStatus;
|
||||||
|
priority?: PriorityLevel;
|
||||||
|
requested_delivery_date?: string;
|
||||||
|
confirmed_delivery_date?: string;
|
||||||
|
delivery_method?: DeliveryMethod;
|
||||||
|
delivery_address?: Record<string, any>;
|
||||||
|
delivery_instructions?: string;
|
||||||
|
delivery_window_start?: string;
|
||||||
|
delivery_window_end?: string;
|
||||||
|
payment_method?: PaymentMethod;
|
||||||
|
payment_status?: PaymentStatus;
|
||||||
|
special_instructions?: string;
|
||||||
|
custom_requirements?: Record<string, any>;
|
||||||
|
allergen_warnings?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderResponse extends OrderBase {
|
||||||
|
id: string;
|
||||||
|
tenant_id: string;
|
||||||
|
order_number: string;
|
||||||
|
status: OrderStatus;
|
||||||
|
order_date: string;
|
||||||
|
confirmed_delivery_date?: string;
|
||||||
|
actual_delivery_date?: string;
|
||||||
|
subtotal: number;
|
||||||
|
discount_amount: number;
|
||||||
|
tax_amount: number;
|
||||||
|
total_amount: number;
|
||||||
|
payment_status: PaymentStatus;
|
||||||
|
business_model?: string;
|
||||||
|
estimated_business_model?: string;
|
||||||
|
production_batch_id?: string;
|
||||||
|
quality_score?: number;
|
||||||
|
customer_rating?: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
items: OrderItemResponse[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Dashboard and Analytics Types =====
|
||||||
|
|
||||||
|
export interface OrdersDashboardSummary {
|
||||||
|
// Current period metrics
|
||||||
|
total_orders_today: number;
|
||||||
|
total_orders_this_week: number;
|
||||||
|
total_orders_this_month: number;
|
||||||
|
|
||||||
|
// Revenue metrics
|
||||||
|
revenue_today: number;
|
||||||
|
revenue_this_week: number;
|
||||||
|
revenue_this_month: number;
|
||||||
|
|
||||||
|
// Order status breakdown
|
||||||
|
pending_orders: number;
|
||||||
|
confirmed_orders: number;
|
||||||
|
in_production_orders: number;
|
||||||
|
ready_orders: number;
|
||||||
|
delivered_orders: number;
|
||||||
|
|
||||||
|
// Customer metrics
|
||||||
|
total_customers: number;
|
||||||
|
new_customers_this_month: number;
|
||||||
|
repeat_customers_rate: number;
|
||||||
|
|
||||||
|
// Performance metrics
|
||||||
|
average_order_value: number;
|
||||||
|
order_fulfillment_rate: number;
|
||||||
|
on_time_delivery_rate: number;
|
||||||
|
|
||||||
|
// Business model detection
|
||||||
|
business_model?: string;
|
||||||
|
business_model_confidence?: number;
|
||||||
|
|
||||||
|
// Recent activity
|
||||||
|
recent_orders: OrderResponse[];
|
||||||
|
high_priority_orders: OrderResponse[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DemandRequirements {
|
||||||
|
date: string;
|
||||||
|
tenant_id: string;
|
||||||
|
|
||||||
|
// Product demand breakdown
|
||||||
|
product_demands: Record<string, any>[];
|
||||||
|
|
||||||
|
// Aggregate metrics
|
||||||
|
total_orders: number;
|
||||||
|
total_quantity: number;
|
||||||
|
total_value: number;
|
||||||
|
|
||||||
|
// Business context
|
||||||
|
business_model?: string;
|
||||||
|
rush_orders_count: number;
|
||||||
|
special_requirements: string[];
|
||||||
|
|
||||||
|
// Timing requirements
|
||||||
|
earliest_delivery: string;
|
||||||
|
latest_delivery: string;
|
||||||
|
average_lead_time_hours: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BusinessModelDetection {
|
||||||
|
business_model: string;
|
||||||
|
confidence: string;
|
||||||
|
detected_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceStatus {
|
||||||
|
service: string;
|
||||||
|
status: string;
|
||||||
|
timestamp: string;
|
||||||
|
tenant_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Query Parameters Types =====
|
||||||
|
|
||||||
|
export interface GetOrdersParams {
|
||||||
|
tenant_id: string;
|
||||||
|
status_filter?: string;
|
||||||
|
start_date?: string;
|
||||||
|
end_date?: string;
|
||||||
|
skip?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetCustomersParams {
|
||||||
|
tenant_id: string;
|
||||||
|
active_only?: boolean;
|
||||||
|
skip?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateOrderStatusParams {
|
||||||
|
tenant_id: string;
|
||||||
|
order_id: string;
|
||||||
|
new_status: OrderStatus;
|
||||||
|
reason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetDemandRequirementsParams {
|
||||||
|
tenant_id: string;
|
||||||
|
target_date: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user