Restore accidentally removed files, a few additional import/calculation fixes
This commit is contained in:
@@ -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 },
|
||||
|
||||
@@ -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,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)}`;
|
||||
|
||||
Reference in New Issue
Block a user