Update backend/frontend

This commit is contained in:
2025-02-14 11:26:02 -05:00
parent 0ef1b6100e
commit cc22fd8c35
16 changed files with 552 additions and 480 deletions

View File

@@ -85,9 +85,7 @@ export function DataManagement() {
const [] = useState<ImportProgress | null>(null);
const [eventSource, setEventSource] = useState<EventSource | null>(null);
const [importHistory, setImportHistory] = useState<ImportHistoryRecord[]>([]);
const [calculateHistory, setCalculateHistory] = useState<
CalculateHistoryRecord[]
>([]);
const [calculateHistory, setCalculateHistory] = useState<CalculateHistoryRecord[]>([]);
const [moduleStatus, setModuleStatus] = useState<ModuleStatus[]>([]);
const [tableStatus, setTableStatus] = useState<TableStatus[]>([]);
const [scriptOutput, setScriptOutput] = useState<string[]>([]);
@@ -368,6 +366,10 @@ export function DataManagement() {
fetch(`${config.apiUrl}/csv/status/tables`),
]);
if (!importRes.ok || !calcRes.ok || !moduleRes.ok || !tableRes.ok) {
throw new Error('One or more requests failed');
}
const [importData, calcData, moduleData, tableData] = await Promise.all([
importRes.json(),
calcRes.json(),
@@ -375,52 +377,66 @@ export function DataManagement() {
tableRes.json(),
]);
setImportHistory(importData);
setCalculateHistory(calcData);
setModuleStatus(moduleData);
setTableStatus(tableData);
// Ensure we're setting arrays even if the response is empty or invalid
setImportHistory(Array.isArray(importData) ? importData : []);
setCalculateHistory(Array.isArray(calcData) ? calcData : []);
setModuleStatus(Array.isArray(moduleData) ? moduleData : []);
setTableStatus(Array.isArray(tableData) ? tableData : []);
} catch (error) {
console.error("Error fetching history:", error);
// Set empty arrays as fallback
setImportHistory([]);
setCalculateHistory([]);
setModuleStatus([]);
setTableStatus([]);
}
};
const refreshTableStatus = async () => {
try {
const response = await fetch(`${config.apiUrl}/csv/status/tables`);
if (!response.ok) throw new Error('Failed to fetch table status');
const data = await response.json();
setTableStatus(data);
setTableStatus(Array.isArray(data) ? data : []);
} catch (error) {
toast.error("Failed to refresh table status");
setTableStatus([]);
}
};
const refreshModuleStatus = async () => {
try {
const response = await fetch(`${config.apiUrl}/csv/status/modules`);
if (!response.ok) throw new Error('Failed to fetch module status');
const data = await response.json();
setModuleStatus(data);
setModuleStatus(Array.isArray(data) ? data : []);
} catch (error) {
toast.error("Failed to refresh module status");
setModuleStatus([]);
}
};
const refreshImportHistory = async () => {
try {
const response = await fetch(`${config.apiUrl}/csv/history/import`);
if (!response.ok) throw new Error('Failed to fetch import history');
const data = await response.json();
setImportHistory(data);
setImportHistory(Array.isArray(data) ? data : []);
} catch (error) {
toast.error("Failed to refresh import history");
setImportHistory([]);
}
};
const refreshCalculateHistory = async () => {
try {
const response = await fetch(`${config.apiUrl}/csv/history/calculate`);
if (!response.ok) throw new Error('Failed to fetch calculate history');
const data = await response.json();
setCalculateHistory(data);
setCalculateHistory(Array.isArray(data) ? data : []);
} catch (error) {
toast.error("Failed to refresh calculate history");
setCalculateHistory([]);
}
};

View File

@@ -112,7 +112,7 @@ export default function PurchaseOrders() {
statuses: string[];
}>({
vendors: [],
statuses: [],
statuses: []
});
const [pagination, setPagination] = useState({
total: 0,
@@ -153,15 +153,57 @@ export default function PurchaseOrders() {
fetch('/api/purchase-orders/cost-analysis')
]);
const [
purchaseOrdersData,
vendorMetricsData,
costAnalysisData
] = await Promise.all([
purchaseOrdersRes.json() as Promise<PurchaseOrdersResponse>,
vendorMetricsRes.json(),
costAnalysisRes.json()
]);
// Initialize default data
let purchaseOrdersData: PurchaseOrdersResponse = {
orders: [],
summary: {
order_count: 0,
total_ordered: 0,
total_received: 0,
fulfillment_rate: 0,
total_value: 0,
avg_cost: 0
},
pagination: {
total: 0,
pages: 0,
page: 1,
limit: 100
},
filters: {
vendors: [],
statuses: []
}
};
let vendorMetricsData: VendorMetrics[] = [];
let costAnalysisData: CostAnalysis = {
unique_products: 0,
avg_cost: 0,
min_cost: 0,
max_cost: 0,
cost_variance: 0,
total_spend_by_category: []
};
// Only try to parse responses if they were successful
if (purchaseOrdersRes.ok) {
purchaseOrdersData = await purchaseOrdersRes.json();
} else {
console.error('Failed to fetch purchase orders:', await purchaseOrdersRes.text());
}
if (vendorMetricsRes.ok) {
vendorMetricsData = await vendorMetricsRes.json();
} else {
console.error('Failed to fetch vendor metrics:', await vendorMetricsRes.text());
}
if (costAnalysisRes.ok) {
costAnalysisData = await costAnalysisRes.json();
} else {
console.error('Failed to fetch cost analysis:', await costAnalysisRes.text());
}
setPurchaseOrders(purchaseOrdersData.orders);
setPagination(purchaseOrdersData.pagination);
@@ -171,6 +213,27 @@ export default function PurchaseOrders() {
setCostAnalysis(costAnalysisData);
} catch (error) {
console.error('Error fetching data:', error);
// Set default values in case of error
setPurchaseOrders([]);
setPagination({ total: 0, pages: 0, page: 1, limit: 100 });
setFilterOptions({ vendors: [], statuses: [] });
setSummary({
order_count: 0,
total_ordered: 0,
total_received: 0,
fulfillment_rate: 0,
total_value: 0,
avg_cost: 0
});
setVendorMetrics([]);
setCostAnalysis({
unique_products: 0,
avg_cost: 0,
min_cost: 0,
max_cost: 0,
cost_variance: 0,
total_spend_by_category: []
});
} finally {
setLoading(false);
}
@@ -310,7 +373,7 @@ export default function PurchaseOrders() {
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Vendors</SelectItem>
{filterOptions.vendors.map(vendor => (
{filterOptions?.vendors?.map(vendor => (
<SelectItem key={vendor} value={vendor}>
{vendor}
</SelectItem>

View File

@@ -3,10 +3,10 @@ export interface Product {
title: string;
SKU: string;
stock_quantity: number;
price: string; // DECIMAL(15,3)
regular_price: string; // DECIMAL(15,3)
cost_price: string; // DECIMAL(15,3)
landing_cost_price: string | null; // DECIMAL(15,3)
price: string; // numeric(15,3)
regular_price: string; // numeric(15,3)
cost_price: string; // numeric(15,3)
landing_cost_price: string | null; // numeric(15,3)
barcode: string;
vendor: string;
vendor_reference: string;
@@ -24,32 +24,32 @@ export interface Product {
updated_at: string;
// Metrics
daily_sales_avg?: string; // DECIMAL(15,3)
weekly_sales_avg?: string; // DECIMAL(15,3)
monthly_sales_avg?: string; // DECIMAL(15,3)
avg_quantity_per_order?: string; // DECIMAL(15,3)
daily_sales_avg?: string; // numeric(15,3)
weekly_sales_avg?: string; // numeric(15,3)
monthly_sales_avg?: string; // numeric(15,3)
avg_quantity_per_order?: string; // numeric(15,3)
number_of_orders?: number;
first_sale_date?: string;
last_sale_date?: string;
last_purchase_date?: string;
days_of_inventory?: string; // DECIMAL(15,3)
weeks_of_inventory?: string; // DECIMAL(15,3)
reorder_point?: string; // DECIMAL(15,3)
safety_stock?: string; // DECIMAL(15,3)
avg_margin_percent?: string; // DECIMAL(15,3)
total_revenue?: string; // DECIMAL(15,3)
inventory_value?: string; // DECIMAL(15,3)
cost_of_goods_sold?: string; // DECIMAL(15,3)
gross_profit?: string; // DECIMAL(15,3)
gmroi?: string; // DECIMAL(15,3)
avg_lead_time_days?: string; // DECIMAL(15,3)
days_of_inventory?: string; // numeric(15,3)
weeks_of_inventory?: string; // numeric(15,3)
reorder_point?: string; // numeric(15,3)
safety_stock?: string; // numeric(15,3)
avg_margin_percent?: string; // numeric(15,3)
total_revenue?: string; // numeric(15,3)
inventory_value?: string; // numeric(15,3)
cost_of_goods_sold?: string; // numeric(15,3)
gross_profit?: string; // numeric(15,3)
gmroi?: string; // numeric(15,3)
avg_lead_time_days?: string; // numeric(15,3)
last_received_date?: string;
abc_class?: string;
stock_status?: string;
turnover_rate?: string; // DECIMAL(15,3)
current_lead_time?: string; // DECIMAL(15,3)
target_lead_time?: string; // DECIMAL(15,3)
turnover_rate?: string; // numeric(15,3)
current_lead_time?: string; // numeric(15,3)
target_lead_time?: string; // numeric(15,3)
lead_time_status?: string;
reorder_qty?: number;
overstocked_amt?: string; // DECIMAL(15,3)
overstocked_amt?: string; // numeric(15,3)
}