Improve the frontend
This commit is contained in:
125
frontend/src/api/services/alert_analytics.ts
Normal file
125
frontend/src/api/services/alert_analytics.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Alert Analytics API Client
|
||||
* Handles all API calls for alert analytics and interaction tracking
|
||||
*/
|
||||
|
||||
import { apiClient } from '../client';
|
||||
|
||||
export interface AlertTrendData {
|
||||
date: string;
|
||||
count: number;
|
||||
urgentCount: number;
|
||||
highCount: number;
|
||||
mediumCount: number;
|
||||
lowCount: number;
|
||||
}
|
||||
|
||||
export interface AlertCategory {
|
||||
category: string;
|
||||
count: number;
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
export interface AlertAnalytics {
|
||||
trends: AlertTrendData[];
|
||||
averageResponseTime: number;
|
||||
topCategories: AlertCategory[];
|
||||
totalAlerts: number;
|
||||
resolvedAlerts: number;
|
||||
activeAlerts: number;
|
||||
resolutionRate: number;
|
||||
predictedDailyAverage: number;
|
||||
busiestDay: string;
|
||||
}
|
||||
|
||||
export interface AlertInteraction {
|
||||
alert_id: string;
|
||||
interaction_type: 'acknowledged' | 'resolved' | 'snoozed' | 'dismissed';
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface InteractionResponse {
|
||||
id: string;
|
||||
alert_id: string;
|
||||
interaction_type: string;
|
||||
interacted_at: string;
|
||||
response_time_seconds: number;
|
||||
}
|
||||
|
||||
export interface BatchInteractionResponse {
|
||||
created_count: number;
|
||||
interactions: Array<{
|
||||
id: string;
|
||||
alert_id: string;
|
||||
interaction_type: string;
|
||||
interacted_at: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track a single alert interaction
|
||||
*/
|
||||
export async function trackAlertInteraction(
|
||||
tenantId: string,
|
||||
alertId: string,
|
||||
interactionType: 'acknowledged' | 'resolved' | 'snoozed' | 'dismissed',
|
||||
metadata?: Record<string, any>
|
||||
): Promise<InteractionResponse> {
|
||||
return apiClient.post<InteractionResponse>(
|
||||
`/tenants/${tenantId}/alerts/${alertId}/interactions`,
|
||||
{
|
||||
alert_id: alertId,
|
||||
interaction_type: interactionType,
|
||||
metadata
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Track multiple alert interactions in batch
|
||||
*/
|
||||
export async function trackAlertInteractionsBatch(
|
||||
tenantId: string,
|
||||
interactions: AlertInteraction[]
|
||||
): Promise<BatchInteractionResponse> {
|
||||
return apiClient.post<BatchInteractionResponse>(
|
||||
`/tenants/${tenantId}/alerts/interactions/batch`,
|
||||
{
|
||||
interactions
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comprehensive alert analytics
|
||||
*/
|
||||
export async function getAlertAnalytics(
|
||||
tenantId: string,
|
||||
days: number = 7
|
||||
): Promise<AlertAnalytics> {
|
||||
console.log('[getAlertAnalytics] Calling API:', `/tenants/${tenantId}/alerts/analytics`, 'with days:', days);
|
||||
const data = await apiClient.get<AlertAnalytics>(
|
||||
`/tenants/${tenantId}/alerts/analytics`,
|
||||
{
|
||||
params: { days }
|
||||
}
|
||||
);
|
||||
console.log('[getAlertAnalytics] Received data:', data);
|
||||
console.log('[getAlertAnalytics] Data type:', typeof data);
|
||||
return data; // apiClient.get() already returns data, not response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* Get alert trends only
|
||||
*/
|
||||
export async function getAlertTrends(
|
||||
tenantId: string,
|
||||
days: number = 7
|
||||
): Promise<AlertTrendData[]> {
|
||||
return apiClient.get<AlertTrendData[]>(
|
||||
`/tenants/${tenantId}/alerts/analytics/trends`,
|
||||
{
|
||||
params: { days }
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -84,7 +84,7 @@ export class OrdersService {
|
||||
*/
|
||||
static async createOrder(orderData: OrderCreate): Promise<OrderResponse> {
|
||||
const { tenant_id, ...data } = orderData;
|
||||
return apiClient.post<OrderResponse>(`/tenants/${tenant_id}/orders/orders`, data);
|
||||
return apiClient.post<OrderResponse>(`/tenants/${tenant_id}/orders`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +92,7 @@ export class OrdersService {
|
||||
* GET /tenants/{tenant_id}/orders/{order_id}
|
||||
*/
|
||||
static async getOrder(tenantId: string, orderId: string): Promise<OrderResponse> {
|
||||
return apiClient.get<OrderResponse>(`/tenants/${tenantId}/orders/orders/${orderId}`);
|
||||
return apiClient.get<OrderResponse>(`/tenants/${tenantId}/orders/${orderId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +117,7 @@ export class OrdersService {
|
||||
queryParams.append('end_date', end_date);
|
||||
}
|
||||
|
||||
return apiClient.get<OrderResponse[]>(`/tenants/${tenant_id}/orders/orders?${queryParams.toString()}`);
|
||||
return apiClient.get<OrderResponse[]>(`/tenants/${tenant_id}/orders?${queryParams.toString()}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -381,7 +381,7 @@ export class ProductionService {
|
||||
}
|
||||
|
||||
async getProductionRequirements(tenantId: string, date: string): Promise<any> {
|
||||
return apiClient.get(`${this.baseUrl}/${tenantId}/production/dashboard/requirements/${date}`);
|
||||
return apiClient.get(`${this.baseUrl}/${tenantId}/production/dashboard/requirements?date=${date}`);
|
||||
}
|
||||
|
||||
async getCapacityOverview(tenantId: string, date?: string): Promise<any> {
|
||||
|
||||
239
frontend/src/api/services/purchase_orders.ts
Normal file
239
frontend/src/api/services/purchase_orders.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Purchase Orders API Client
|
||||
* Handles all API calls for purchase orders in the suppliers service
|
||||
*/
|
||||
|
||||
import { apiClient } from '../client';
|
||||
|
||||
export type PurchaseOrderStatus =
|
||||
| 'DRAFT'
|
||||
| 'PENDING_APPROVAL'
|
||||
| 'APPROVED'
|
||||
| 'SENT_TO_SUPPLIER'
|
||||
| 'CONFIRMED'
|
||||
| 'RECEIVED'
|
||||
| 'COMPLETED'
|
||||
| 'CANCELLED'
|
||||
| 'DISPUTED';
|
||||
|
||||
export type PurchaseOrderPriority = 'urgent' | 'high' | 'normal' | 'low';
|
||||
|
||||
export interface PurchaseOrderItem {
|
||||
id: string;
|
||||
inventory_product_id: string;
|
||||
product_code?: string;
|
||||
ordered_quantity: number;
|
||||
unit_of_measure: string;
|
||||
unit_price: string; // Decimal as string
|
||||
line_total: string; // Decimal as string
|
||||
received_quantity: number;
|
||||
remaining_quantity: number;
|
||||
quality_requirements?: string;
|
||||
item_notes?: string;
|
||||
}
|
||||
|
||||
export interface SupplierSummary {
|
||||
id: string;
|
||||
name: string;
|
||||
supplier_code: string;
|
||||
supplier_type: string;
|
||||
status: string;
|
||||
contact_person?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
}
|
||||
|
||||
export interface PurchaseOrderSummary {
|
||||
id: string;
|
||||
po_number: string;
|
||||
supplier_id: string;
|
||||
supplier_name?: string;
|
||||
status: PurchaseOrderStatus;
|
||||
priority: PurchaseOrderPriority;
|
||||
order_date: string;
|
||||
required_delivery_date?: string;
|
||||
total_amount: string; // Decimal as string
|
||||
currency: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface PurchaseOrderDetail extends PurchaseOrderSummary {
|
||||
reference_number?: string;
|
||||
estimated_delivery_date?: string;
|
||||
|
||||
// Financial information
|
||||
subtotal: string;
|
||||
tax_amount: string;
|
||||
shipping_cost: string;
|
||||
discount_amount: string;
|
||||
|
||||
// Delivery information
|
||||
delivery_address?: string;
|
||||
delivery_instructions?: string;
|
||||
delivery_contact?: string;
|
||||
delivery_phone?: string;
|
||||
|
||||
// Approval workflow
|
||||
requires_approval: boolean;
|
||||
approved_by?: string;
|
||||
approved_at?: string;
|
||||
rejection_reason?: string;
|
||||
|
||||
// Communication tracking
|
||||
sent_to_supplier_at?: string;
|
||||
supplier_confirmation_date?: string;
|
||||
supplier_reference?: string;
|
||||
|
||||
// Additional information
|
||||
notes?: string;
|
||||
internal_notes?: string;
|
||||
terms_and_conditions?: string;
|
||||
|
||||
// Audit fields
|
||||
updated_at: string;
|
||||
created_by: string;
|
||||
updated_by: string;
|
||||
|
||||
// Related data
|
||||
supplier?: SupplierSummary;
|
||||
items?: PurchaseOrderItem[];
|
||||
}
|
||||
|
||||
export interface PurchaseOrderSearchParams {
|
||||
supplier_id?: string;
|
||||
status?: PurchaseOrderStatus;
|
||||
priority?: PurchaseOrderPriority;
|
||||
date_from?: string; // YYYY-MM-DD
|
||||
date_to?: string; // YYYY-MM-DD
|
||||
search_term?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export interface PurchaseOrderUpdateData {
|
||||
status?: PurchaseOrderStatus;
|
||||
priority?: PurchaseOrderPriority;
|
||||
notes?: string;
|
||||
rejection_reason?: string;
|
||||
internal_notes?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of purchase orders with optional filters
|
||||
*/
|
||||
export async function listPurchaseOrders(
|
||||
tenantId: string,
|
||||
params?: PurchaseOrderSearchParams
|
||||
): Promise<PurchaseOrderSummary[]> {
|
||||
return apiClient.get<PurchaseOrderSummary[]>(
|
||||
`/tenants/${tenantId}/purchase-orders`,
|
||||
{ params }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get purchase orders by status
|
||||
*/
|
||||
export async function getPurchaseOrdersByStatus(
|
||||
tenantId: string,
|
||||
status: PurchaseOrderStatus,
|
||||
limit: number = 50
|
||||
): Promise<PurchaseOrderSummary[]> {
|
||||
return listPurchaseOrders(tenantId, { status, limit });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pending approval purchase orders
|
||||
*/
|
||||
export async function getPendingApprovalPurchaseOrders(
|
||||
tenantId: string,
|
||||
limit: number = 50
|
||||
): Promise<PurchaseOrderSummary[]> {
|
||||
return getPurchaseOrdersByStatus(tenantId, 'PENDING_APPROVAL', limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single purchase order by ID with full details
|
||||
*/
|
||||
export async function getPurchaseOrder(
|
||||
tenantId: string,
|
||||
poId: string
|
||||
): Promise<PurchaseOrderDetail> {
|
||||
return apiClient.get<PurchaseOrderDetail>(
|
||||
`/tenants/${tenantId}/purchase-orders/${poId}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update purchase order
|
||||
*/
|
||||
export async function updatePurchaseOrder(
|
||||
tenantId: string,
|
||||
poId: string,
|
||||
data: PurchaseOrderUpdateData
|
||||
): Promise<PurchaseOrderDetail> {
|
||||
return apiClient.put<PurchaseOrderDetail>(
|
||||
`/tenants/${tenantId}/purchase-orders/${poId}`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve a purchase order
|
||||
*/
|
||||
export async function approvePurchaseOrder(
|
||||
tenantId: string,
|
||||
poId: string,
|
||||
notes?: string
|
||||
): Promise<PurchaseOrderDetail> {
|
||||
return apiClient.post<PurchaseOrderDetail>(
|
||||
`/tenants/${tenantId}/purchase-orders/${poId}/approve`,
|
||||
{
|
||||
action: 'approve',
|
||||
notes: notes || 'Approved from dashboard'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject a purchase order
|
||||
*/
|
||||
export async function rejectPurchaseOrder(
|
||||
tenantId: string,
|
||||
poId: string,
|
||||
reason: string
|
||||
): Promise<PurchaseOrderDetail> {
|
||||
return apiClient.post<PurchaseOrderDetail>(
|
||||
`/tenants/${tenantId}/purchase-orders/${poId}/approve`,
|
||||
{
|
||||
action: 'reject',
|
||||
notes: reason
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk approve purchase orders
|
||||
*/
|
||||
export async function bulkApprovePurchaseOrders(
|
||||
tenantId: string,
|
||||
poIds: string[],
|
||||
notes?: string
|
||||
): Promise<PurchaseOrderDetail[]> {
|
||||
const approvalPromises = poIds.map(poId =>
|
||||
approvePurchaseOrder(tenantId, poId, notes)
|
||||
);
|
||||
return Promise.all(approvalPromises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete purchase order
|
||||
*/
|
||||
export async function deletePurchaseOrder(
|
||||
tenantId: string,
|
||||
poId: string
|
||||
): Promise<{ message: string }> {
|
||||
return apiClient.delete<{ message: string }>(
|
||||
`/tenants/${tenantId}/purchase-orders/${poId}`
|
||||
);
|
||||
}
|
||||
@@ -104,6 +104,19 @@ class SuppliersService {
|
||||
);
|
||||
}
|
||||
|
||||
async getSupplierProducts(
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
isActive: boolean = true
|
||||
): Promise<Array<{ inventory_product_id: string }>> {
|
||||
const params = new URLSearchParams();
|
||||
params.append('is_active', isActive.toString());
|
||||
|
||||
return apiClient.get<Array<{ inventory_product_id: string }>>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/suppliers/${supplierId}/products?${params.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// ATOMIC: Purchase Orders CRUD
|
||||
// Backend: services/suppliers/app/api/purchase_orders.py
|
||||
|
||||
Reference in New Issue
Block a user