Merge branch 'master' into add-product-upload-page
This commit is contained in:
@@ -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([]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -914,7 +914,7 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
|
||||
const hasMultiValueRows = rowsWithValues.some(count => count > 1);
|
||||
|
||||
// Filter out empty rows and rows with single values (if we have multi-value rows)
|
||||
const nonEmptyRows = data.filter((row, index) => {
|
||||
const nonEmptyRows = data.filter((_row, index) => {
|
||||
const nonEmptyCount = rowsWithValues[index];
|
||||
|
||||
// Keep the row if:
|
||||
|
||||
@@ -22,7 +22,7 @@ export function Login() {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const url = isDev ? "/auth-inv/login" : `${config.authUrl}/login`;
|
||||
const url = `${config.authUrl}/login`;
|
||||
console.log("Making login request:", {
|
||||
url,
|
||||
method: "POST",
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
import { motion } from 'motion/react';
|
||||
import {
|
||||
PurchaseOrderStatus,
|
||||
ReceivingStatus as ReceivingStatusCode,
|
||||
getPurchaseOrderStatusLabel,
|
||||
getReceivingStatusLabel,
|
||||
getPurchaseOrderStatusVariant,
|
||||
@@ -113,7 +112,7 @@ export default function PurchaseOrders() {
|
||||
statuses: string[];
|
||||
}>({
|
||||
vendors: [],
|
||||
statuses: [],
|
||||
statuses: []
|
||||
});
|
||||
const [pagination, setPagination] = useState({
|
||||
total: 0,
|
||||
@@ -154,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);
|
||||
@@ -172,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);
|
||||
}
|
||||
@@ -311,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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/app.tsx","./src/config.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/analytics/categoryperformance.tsx","./src/components/analytics/priceanalysis.tsx","./src/components/analytics/profitanalysis.tsx","./src/components/analytics/stockanalysis.tsx","./src/components/analytics/vendorperformance.tsx","./src/components/auth/requireauth.tsx","./src/components/dashboard/bestsellers.tsx","./src/components/dashboard/forecastmetrics.tsx","./src/components/dashboard/inventoryhealthsummary.tsx","./src/components/dashboard/inventorystats.tsx","./src/components/dashboard/keymetricscharts.tsx","./src/components/dashboard/lowstockalerts.tsx","./src/components/dashboard/overstockmetrics.tsx","./src/components/dashboard/overview.tsx","./src/components/dashboard/purchasemetrics.tsx","./src/components/dashboard/recentsales.tsx","./src/components/dashboard/replenishmentmetrics.tsx","./src/components/dashboard/salesbycategory.tsx","./src/components/dashboard/salesmetrics.tsx","./src/components/dashboard/stockmetrics.tsx","./src/components/dashboard/topoverstockedproducts.tsx","./src/components/dashboard/topreplenishproducts.tsx","./src/components/dashboard/trendingproducts.tsx","./src/components/dashboard/vendorperformance.tsx","./src/components/forecasting/columns.tsx","./src/components/layout/appsidebar.tsx","./src/components/layout/mainlayout.tsx","./src/components/products/productdetail.tsx","./src/components/products/productfilters.tsx","./src/components/products/producttable.tsx","./src/components/products/producttableskeleton.tsx","./src/components/products/productviews.tsx","./src/components/settings/calculationsettings.tsx","./src/components/settings/configuration.tsx","./src/components/settings/datamanagement.tsx","./src/components/settings/performancemetrics.tsx","./src/components/settings/stockmanagement.tsx","./src/components/ui/accordion.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/command.tsx","./src/components/ui/date-range-picker-narrow.tsx","./src/components/ui/date-range-picker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/drawer.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/scroll-area.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/toggle-group.tsx","./src/components/ui/toggle.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-mobile.tsx","./src/lib/utils.ts","./src/pages/analytics.tsx","./src/pages/categories.tsx","./src/pages/dashboard.tsx","./src/pages/forecasting.tsx","./src/pages/login.tsx","./src/pages/orders.tsx","./src/pages/products.tsx","./src/pages/purchaseorders.tsx","./src/pages/settings.tsx","./src/pages/vendors.tsx","./src/routes/forecasting.tsx","./src/types/products.ts"],"version":"5.6.3"}
|
||||
{"root":["./src/app.tsx","./src/config.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/analytics/categoryperformance.tsx","./src/components/analytics/priceanalysis.tsx","./src/components/analytics/profitanalysis.tsx","./src/components/analytics/stockanalysis.tsx","./src/components/analytics/vendorperformance.tsx","./src/components/auth/requireauth.tsx","./src/components/dashboard/bestsellers.tsx","./src/components/dashboard/forecastmetrics.tsx","./src/components/dashboard/inventoryhealthsummary.tsx","./src/components/dashboard/inventorystats.tsx","./src/components/dashboard/keymetricscharts.tsx","./src/components/dashboard/lowstockalerts.tsx","./src/components/dashboard/overstockmetrics.tsx","./src/components/dashboard/overview.tsx","./src/components/dashboard/purchasemetrics.tsx","./src/components/dashboard/recentsales.tsx","./src/components/dashboard/replenishmentmetrics.tsx","./src/components/dashboard/salesbycategory.tsx","./src/components/dashboard/salesmetrics.tsx","./src/components/dashboard/stockmetrics.tsx","./src/components/dashboard/topoverstockedproducts.tsx","./src/components/dashboard/topreplenishproducts.tsx","./src/components/dashboard/trendingproducts.tsx","./src/components/dashboard/vendorperformance.tsx","./src/components/forecasting/columns.tsx","./src/components/layout/appsidebar.tsx","./src/components/layout/mainlayout.tsx","./src/components/products/productdetail.tsx","./src/components/products/productfilters.tsx","./src/components/products/producttable.tsx","./src/components/products/producttableskeleton.tsx","./src/components/products/productviews.tsx","./src/components/settings/calculationsettings.tsx","./src/components/settings/configuration.tsx","./src/components/settings/datamanagement.tsx","./src/components/settings/performancemetrics.tsx","./src/components/settings/stockmanagement.tsx","./src/components/ui/accordion.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/command.tsx","./src/components/ui/date-range-picker-narrow.tsx","./src/components/ui/date-range-picker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/drawer.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/scroll-area.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/toggle-group.tsx","./src/components/ui/toggle.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-mobile.tsx","./src/lib/utils.ts","./src/pages/analytics.tsx","./src/pages/categories.tsx","./src/pages/dashboard.tsx","./src/pages/forecasting.tsx","./src/pages/login.tsx","./src/pages/orders.tsx","./src/pages/products.tsx","./src/pages/purchaseorders.tsx","./src/pages/settings.tsx","./src/pages/vendors.tsx","./src/types/products.ts","./src/types/status-codes.ts"],"version":"5.6.3"}
|
||||
Reference in New Issue
Block a user