Restore accidentally removed files, a few additional import/calculation fixes

This commit is contained in:
2026-02-09 10:19:35 -05:00
parent 6aefc1b40d
commit 38b12c188f
209 changed files with 69925 additions and 412 deletions
@@ -31,7 +31,7 @@ function getSellThroughColor(rate: number): string {
}
export function AgingSellThrough() {
const { data, isLoading } = useQuery<AgingCohort[]>({
const { data, isLoading, isError } = useQuery<AgingCohort[]>({
queryKey: ['aging-sell-through'],
queryFn: async () => {
const response = await fetch(`${config.apiUrl}/analytics/aging`);
@@ -40,6 +40,19 @@ export function AgingSellThrough() {
},
});
if (isError) {
return (
<Card>
<CardHeader><CardTitle>Aging & Sell-Through</CardTitle></CardHeader>
<CardContent>
<div className="h-[350px] flex items-center justify-center">
<p className="text-sm text-destructive">Failed to load aging data</p>
</div>
</CardContent>
</Card>
);
}
if (isLoading || !data) {
return (
<Card>
@@ -38,7 +38,7 @@ function getGmroiColor(gmroi: number): string {
}
export function CapitalEfficiency() {
const { data, isLoading } = useQuery<EfficiencyData>({
const { data, isLoading, isError } = useQuery<EfficiencyData>({
queryKey: ['capital-efficiency'],
queryFn: async () => {
const response = await fetch(`${config.apiUrl}/analytics/efficiency`);
@@ -47,6 +47,19 @@ export function CapitalEfficiency() {
},
});
if (isError) {
return (
<Card>
<CardHeader><CardTitle>Capital Efficiency</CardTitle></CardHeader>
<CardContent>
<div className="h-[350px] flex items-center justify-center">
<p className="text-sm text-destructive">Failed to load efficiency data</p>
</div>
</CardContent>
</Card>
);
}
if (isLoading || !data) {
return (
<Card>
@@ -31,7 +31,7 @@ const CLASS_COLORS: Record<string, string> = {
};
export function DiscountImpact() {
const { data, isLoading } = useQuery<DiscountRow[]>({
const { data, isLoading, isError } = useQuery<DiscountRow[]>({
queryKey: ['discount-impact'],
queryFn: async () => {
const response = await fetch(`${config.apiUrl}/analytics/discounts`);
@@ -40,6 +40,19 @@ export function DiscountImpact() {
},
});
if (isError) {
return (
<Card>
<CardHeader><CardTitle>Discount Impact</CardTitle></CardHeader>
<CardContent>
<div className="h-[300px] flex items-center justify-center">
<p className="text-sm text-destructive">Failed to load discount data</p>
</div>
</CardContent>
</Card>
);
}
if (isLoading || !data) {
return (
<Card>
@@ -43,7 +43,7 @@ const GROWTH_COLORS: Record<string, string> = {
};
export function GrowthMomentum() {
const { data, isLoading } = useQuery<GrowthData>({
const { data, isLoading, isError } = useQuery<GrowthData>({
queryKey: ['growth-momentum'],
queryFn: async () => {
const response = await fetch(`${config.apiUrl}/analytics/growth`);
@@ -52,6 +52,19 @@ export function GrowthMomentum() {
},
});
if (isError) {
return (
<Card>
<CardHeader><CardTitle>YoY Growth Momentum</CardTitle></CardHeader>
<CardContent>
<div className="h-[300px] flex items-center justify-center">
<p className="text-sm text-destructive">Failed to load growth data</p>
</div>
</CardContent>
</Card>
);
}
if (isLoading || !data) {
return (
<Card>
@@ -32,7 +32,7 @@ function formatDate(dateStr: string, period: Period): string {
export function InventoryTrends() {
const [period, setPeriod] = useState<Period>(90);
const { data, isLoading } = useQuery<TrendPoint[]>({
const { data, isLoading, isError } = useQuery<TrendPoint[]>({
queryKey: ['inventory-trends', period],
queryFn: async () => {
const response = await fetch(`${config.apiUrl}/analytics/inventory-trends?period=${period}`);
@@ -69,7 +69,11 @@ export function InventoryTrends() {
</div>
</CardHeader>
<CardContent>
{isLoading || !data ? (
{isError ? (
<div className="h-[350px] flex items-center justify-center">
<p className="text-sm text-destructive">Failed to load trends data</p>
</div>
) : isLoading || !data ? (
<div className="h-[350px] flex items-center justify-center">
<div className="animate-pulse text-muted-foreground">Loading trends...</div>
</div>
@@ -39,7 +39,7 @@ interface PortfolioData {
}
export function PortfolioAnalysis() {
const { data, isLoading } = useQuery<PortfolioData>({
const { data, isLoading, isError } = useQuery<PortfolioData>({
queryKey: ['portfolio-analysis'],
queryFn: async () => {
const response = await fetch(`${config.apiUrl}/analytics/portfolio`);
@@ -48,6 +48,19 @@ export function PortfolioAnalysis() {
},
});
if (isError) {
return (
<Card>
<CardHeader><CardTitle>Portfolio & ABC Analysis</CardTitle></CardHeader>
<CardContent>
<div className="h-[300px] flex items-center justify-center">
<p className="text-sm text-destructive">Failed to load portfolio data</p>
</div>
</CardContent>
</Card>
);
}
if (isLoading || !data) {
return (
<Card>
@@ -69,7 +69,7 @@ function getCoverColor(bucket: string): string {
}
export function StockHealth() {
const { data, isLoading } = useQuery<StockHealthData>({
const { data, isLoading, isError } = useQuery<StockHealthData>({
queryKey: ['stock-health'],
queryFn: async () => {
const response = await fetch(`${config.apiUrl}/analytics/stock-health`);
@@ -78,6 +78,19 @@ export function StockHealth() {
},
});
if (isError) {
return (
<Card>
<CardHeader><CardTitle>Demand & Stock Health</CardTitle></CardHeader>
<CardContent>
<div className="h-[300px] flex items-center justify-center">
<p className="text-sm text-destructive">Failed to load stock health data</p>
</div>
</CardContent>
</Card>
);
}
if (isLoading || !data) {
return (
<Card>
@@ -46,7 +46,7 @@ function getRiskColor(product: RiskProduct): string {
}
export function StockoutRisk() {
const { data, isLoading } = useQuery<StockoutRiskData>({
const { data, isLoading, isError } = useQuery<StockoutRiskData>({
queryKey: ['stockout-risk'],
queryFn: async () => {
const response = await fetch(`${config.apiUrl}/analytics/stockout-risk`);
@@ -55,6 +55,19 @@ export function StockoutRisk() {
},
});
if (isError) {
return (
<Card>
<CardHeader><CardTitle>Reorder Risk</CardTitle></CardHeader>
<CardContent>
<div className="h-[350px] flex items-center justify-center">
<p className="text-sm text-destructive">Failed to load risk data</p>
</div>
</CardContent>
</Card>
);
}
if (isLoading || !data) {
return (
<Card>
@@ -127,7 +140,7 @@ export function StockoutRisk() {
{/* Diagonal risk line (y = x): products below this stock out before replenishment */}
<Scatter
data={(() => {
const max = Math.max(...products.map(d => Math.max(d.leadTimeDays, d.sellsOutInDays)));
const max = products.length > 0 ? Math.max(...products.map(d => Math.max(d.leadTimeDays, d.sellsOutInDays))) : 100;
return [
{ leadTimeDays: 0, sellsOutInDays: 0, revenue30d: 0 },
{ leadTimeDays: max, sellsOutInDays: max, revenue30d: 0 },
+8 -1
View File
@@ -25,7 +25,7 @@ interface InventorySummary {
}
export function Analytics() {
const { data: summary, isLoading } = useQuery<InventorySummary>({
const { data: summary, isLoading, isError } = useQuery<InventorySummary>({
queryKey: ['inventory-summary'],
queryFn: async () => {
const response = await fetch(`${config.apiUrl}/analytics/inventory-summary`);
@@ -41,6 +41,13 @@ export function Analytics() {
</div>
{/* KPI Summary Cards */}
{isError && (
<Card>
<CardContent className="py-4">
<p className="text-sm text-destructive">Failed to load inventory summary</p>
</CardContent>
</Card>
)}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
+1
View File
@@ -1,4 +1,5 @@
export function formatCurrency(value: number): string {
if (value < 0) return `-${formatCurrency(-value)}`;
if (value >= 1_000_000) return `$${(value / 1_000_000).toFixed(1)}M`;
if (value >= 1_000) return `$${(value / 1_000).toFixed(1)}k`;
return `$${value.toFixed(0)}`;