better stuffs

This commit is contained in:
g
2026-01-10 02:07:03 +00:00
parent c69367e6da
commit 6c90ba90c3
2 changed files with 180 additions and 23 deletions

View File

@@ -90,6 +90,8 @@ export default function PredictionsChart({
const getConfidenceColor = (confidence: string) => {
switch (confidence) {
case "very_high":
return "bg-emerald-600/20 text-emerald-700 dark:text-emerald-400 border-emerald-600/30";
case "high":
return "bg-green-500/10 text-green-700 dark:text-green-400";
case "medium":
@@ -101,6 +103,21 @@ export default function PredictionsChart({
}
};
const getConfidenceLabel = (confidence: string) => {
switch (confidence) {
case "very_high":
return "Very High";
case "high":
return "High";
case "medium":
return "Medium";
case "low":
return "Low";
default:
return confidence;
}
};
if (loading) {
return (
<Card>
@@ -212,14 +229,43 @@ export default function PredictionsChart({
<div className="text-2xl font-bold">
{formatGBP(predictions.sales.predicted)}
</div>
<div className="flex items-center gap-2">
<div className="flex items-center gap-2 flex-wrap">
<Badge
className={getConfidenceColor(
predictions.sales.confidence,
)}
>
{predictions.sales.confidence} confidence
{getConfidenceLabel(predictions.sales.confidence)} Confidence
{predictions.sales.confidenceScore !== undefined && (
<span className="ml-1 opacity-75">
({Math.round(predictions.sales.confidenceScore * 100)}%)
</span>
)}
</Badge>
{predictions.sales.trend && (
<Badge
variant="outline"
className={
predictions.sales.trend.direction === "up"
? "text-green-600 border-green-600"
: predictions.sales.trend.direction === "down"
? "text-red-600 border-red-600"
: ""
}
>
{predictions.sales.trend.direction === "up" && (
<TrendingUp className="h-3 w-3 mr-1" />
)}
{predictions.sales.trend.direction === "down" && (
<TrendingDown className="h-3 w-3 mr-1" />
)}
{predictions.sales.trend.direction === "up"
? "Trending Up"
: predictions.sales.trend.direction === "down"
? "Trending Down"
: "Stable"}
</Badge>
)}
<span className="text-xs text-muted-foreground">
Next {daysAhead} days
</span>
@@ -230,7 +276,38 @@ export default function PredictionsChart({
orders
</div>
)}
{predictions.sales.minPrediction &&
{predictions.sales.confidenceIntervals && (
<div className="text-xs text-muted-foreground space-y-1">
<div>
Range: {formatGBP(predictions.sales.confidenceIntervals.lower)} -{" "}
{formatGBP(predictions.sales.confidenceIntervals.upper)}
</div>
<div className="text-xs opacity-75">
95% confidence interval
</div>
{predictions.sales.confidenceIntervals.confidenceScore !== undefined && (
<div className="flex gap-3 text-xs opacity-75 mt-2 pt-2 border-t">
{predictions.sales.confidenceIntervals.avgModelAccuracy !== undefined && (
<div>
Model Accuracy: {Math.round(predictions.sales.confidenceIntervals.avgModelAccuracy * 100)}%
</div>
)}
{predictions.sales.confidenceIntervals.modelAgreement !== undefined && (
<div>
Agreement: {Math.round(predictions.sales.confidenceIntervals.modelAgreement * 100)}%
</div>
)}
{predictions.sales.confidenceIntervals.dataConsistency !== undefined && (
<div>
Data Quality: {Math.round(predictions.sales.confidenceIntervals.dataConsistency * 100)}%
</div>
)}
</div>
)}
</div>
)}
{!predictions.sales.confidenceIntervals &&
predictions.sales.minPrediction &&
predictions.sales.maxPrediction && (
<div className="text-xs text-muted-foreground">
Range: {formatGBP(predictions.sales.minPrediction)} -{" "}
@@ -266,7 +343,7 @@ export default function PredictionsChart({
predictions.demand.confidence,
)}
>
{predictions.demand.confidence} confidence
{getConfidenceLabel(predictions.demand.confidence)} Confidence
</Badge>
</div>
{predictions.demand.predictedWeekly && (
@@ -279,6 +356,12 @@ export default function PredictionsChart({
~{predictions.demand.predictedMonthly.toFixed(0)} units/month
</div>
)}
{predictions.demand.confidenceIntervals && (
<div className="text-xs text-muted-foreground">
Range: {predictions.demand.confidenceIntervals.lower.toFixed(1)} -{" "}
{predictions.demand.confidenceIntervals.upper.toFixed(1)} units/day
</div>
)}
</div>
) : (
<div className="text-sm text-muted-foreground">
@@ -369,19 +452,56 @@ export default function PredictionsChart({
{prediction.currentStock} {prediction.unitType}
</TableCell>
<TableCell>
{prediction.prediction.daysUntilOutOfStock !== null
? `${prediction.prediction.daysUntilOutOfStock} days`
: "N/A"}
{prediction.prediction.daysUntilOutOfStock !== null ? (
<div className="space-y-1">
<div className="font-medium">
{prediction.prediction.daysUntilOutOfStock} days
</div>
{prediction.prediction.optimisticDays !== null &&
prediction.prediction.pessimisticDays && (
<div className="text-xs text-muted-foreground">
{prediction.prediction.optimisticDays} -{" "}
{prediction.prediction.pessimisticDays} days
</div>
)}
</div>
) : (
"N/A"
)}
</TableCell>
<TableCell>
{prediction.prediction.estimatedDate
? format(
{prediction.prediction.estimatedDate ? (
<div className="space-y-1">
<div>
{format(
new Date(
prediction.prediction.estimatedDate,
),
"MMM d, yyyy",
)
: "N/A"}
)}
</div>
{prediction.prediction.optimisticDate &&
prediction.prediction.pessimisticDate && (
<div className="text-xs text-muted-foreground">
{format(
new Date(
prediction.prediction.optimisticDate,
),
"MMM d",
)}{" "}
-{" "}
{format(
new Date(
prediction.prediction.pessimisticDate,
),
"MMM d",
)}
</div>
)}
</div>
) : (
"N/A"
)}
</TableCell>
<TableCell>
<Badge
@@ -389,7 +509,7 @@ export default function PredictionsChart({
prediction.prediction.confidence,
)}
>
{prediction.prediction.confidence}
{getConfidenceLabel(prediction.prediction.confidence)}
</Badge>
</TableCell>
<TableCell>

View File

@@ -308,17 +308,42 @@ export interface SalesPrediction {
predicted: number;
date: string;
}>;
confidence: "high" | "medium" | "low";
confidence: "very_high" | "high" | "medium" | "low";
method: string;
methods?: {
movingAverage?: number | null;
exponentialAverage?: number | null;
linearRegression?: number | null;
trendAverage?: number | null;
weightedMovingAverage?: number | null;
exponentialSmoothing?: number | null;
holtWinters?: number | null;
weightedLinearRegression?: number | null;
trendAdjusted?: number | null;
};
trend?: {
direction: "up" | "down" | "neutral";
strength: number;
acceleration: number;
slope?: number;
};
variance?: number;
minPrediction?: number;
maxPrediction?: number;
confidenceScore?: number;
confidenceIntervals?: {
lower: number;
upper: number;
confidenceScore?: number;
modelAgreement?: number;
avgModelAccuracy?: number;
dataConsistency?: number;
};
modelPerformance?: {
[key: string]: {
mae?: number;
mape?: number;
rmse?: number;
accuracy?: number;
confidence?: string;
};
};
seasonality?: {
dayOfWeek: Record<string, number>;
month: Record<string, number>;
@@ -333,10 +358,18 @@ export interface DemandPrediction {
predictedDaily: number | null;
predictedWeekly: number | null;
predictedMonthly: number | null;
confidence: "high" | "medium" | "low";
confidence: "very_high" | "high" | "medium" | "low";
averageDaily?: number;
trendFactor?: number;
stdDev?: number;
confidenceIntervals?: {
lower: number;
upper: number;
confidenceScore?: number;
modelAgreement?: number;
avgModelAccuracy?: number;
dataConsistency?: number;
};
historicalPeriod: number;
predictionPeriod: number;
productId?: string | null;
@@ -352,9 +385,13 @@ export interface StockPrediction {
prediction: {
daysUntilOutOfStock: number | null;
estimatedDate: string | null;
confidence: "high" | "medium" | "low";
confidence: "very_high" | "high" | "medium" | "low";
averageDailySales?: number;
stdDev?: number;
optimisticDays?: number | null;
pessimisticDays?: number | null;
optimisticDate?: string | null;
pessimisticDate?: string | null;
message?: string;
};
needsRestock: boolean;