Improve the frontend

This commit is contained in:
Urtzi Alfaro
2025-10-21 19:50:07 +02:00
parent 05da20357d
commit 8d30172483
105 changed files with 14699 additions and 4630 deletions

View 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 }
}
);
}

View File

@@ -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()}`);
}
/**

View File

@@ -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> {

View 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}`
);
}

View File

@@ -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