More bugfixes
This commit is contained in:
@@ -298,9 +298,9 @@ func (h *Handler) ExportAWSalesHistory(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
cols := []export.Column{
|
||||
{Key: "date", Label: "Date"},
|
||||
{Key: "total_revenue", Label: "Total Revenue"},
|
||||
{Key: "total_orders", Label: "Total Orders"},
|
||||
{Key: "avg_order_value", Label: "Avg Order Value"},
|
||||
{Key: "revenue", Label: "Revenue"},
|
||||
{Key: "cost", Label: "Cost"},
|
||||
{Key: "quantity", Label: "Quantity"},
|
||||
}
|
||||
b, err := export.ToXLSXBytes(r.Context(), "Sales History", cols, toMaps(data))
|
||||
if err != nil {
|
||||
@@ -342,10 +342,12 @@ func (h *Handler) ExportAWRepScores(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
cols := []export.Column{
|
||||
{Key: "rep_name", Label: "Sales Rep"},
|
||||
{Key: "total_revenue", Label: "Total Revenue"},
|
||||
{Key: "total_orders", Label: "Total Orders"},
|
||||
{Key: "avg_order_value", Label: "Avg Order Value"},
|
||||
{Key: "performance_score", Label: "Performance Score"},
|
||||
{Key: "territory", Label: "Territory"},
|
||||
{Key: "revenue", Label: "Revenue"},
|
||||
{Key: "orders", Label: "Orders"},
|
||||
{Key: "avg_deal_size", Label: "Avg Deal Size"},
|
||||
{Key: "margin_pct", Label: "Margin %"},
|
||||
{Key: "score", Label: "Performance Score"},
|
||||
}
|
||||
b, err := export.ToXLSXBytes(r.Context(), "Rep Scores", cols, toMaps(data))
|
||||
if err != nil {
|
||||
@@ -366,8 +368,10 @@ func (h *Handler) ExportAWProductDemand(w http.ResponseWriter, r *http.Request)
|
||||
cols := []export.Column{
|
||||
{Key: "product_name", Label: "Product"},
|
||||
{Key: "category", Label: "Category"},
|
||||
{Key: "total_quantity", Label: "Total Quantity"},
|
||||
{Key: "total_revenue", Label: "Total Revenue"},
|
||||
{Key: "quantity", Label: "Quantity"},
|
||||
{Key: "revenue", Label: "Revenue"},
|
||||
{Key: "orders", Label: "Orders"},
|
||||
{Key: "margin_pct", Label: "Margin %"},
|
||||
{Key: "demand_score", Label: "Demand Score"},
|
||||
}
|
||||
b, err := export.ToXLSXBytes(r.Context(), "Product Demand", cols, toMaps(data))
|
||||
@@ -411,10 +415,11 @@ func (h *Handler) ExportWWISupplierScores(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
cols := []export.Column{
|
||||
{Key: "supplier_name", Label: "Supplier"},
|
||||
{Key: "category", Label: "Category"},
|
||||
{Key: "total_orders", Label: "Total Orders"},
|
||||
{Key: "on_time_delivery_rate", Label: "On-Time Delivery Rate"},
|
||||
{Key: "avg_lead_time_days", Label: "Avg Lead Time (Days)"},
|
||||
{Key: "performance_score", Label: "Performance Score"},
|
||||
{Key: "fill_rate_pct", Label: "Fill Rate %"},
|
||||
{Key: "finalization_rate_pct", Label: "Finalization Rate %"},
|
||||
{Key: "score", Label: "Performance Score"},
|
||||
}
|
||||
b, err := export.ToXLSXBytes(r.Context(), "Supplier Scores", cols, toMaps(data))
|
||||
if err != nil {
|
||||
|
||||
@@ -68,14 +68,14 @@ export const getWWIJobs = (limit = 50) =>
|
||||
get<JobExecution[]>(`/api/jobs/wwi?limit=${limit}`, "frontend.jobs.wwi");
|
||||
|
||||
export const triggerAWJob = (jobName: string) =>
|
||||
post<{ triggered: boolean; job_name: string }>(
|
||||
`/api/jobs/aw/${jobName}/trigger`,
|
||||
post<{ status: string; job: string }>(
|
||||
`/api/aw/jobs/${jobName}/trigger`,
|
||||
"frontend.jobs.aw.trigger",
|
||||
);
|
||||
|
||||
export const triggerWWIJob = (jobName: string) =>
|
||||
post<{ triggered: boolean; job_name: string }>(
|
||||
`/api/jobs/wwi/${jobName}/trigger`,
|
||||
post<{ status: string; job: string }>(
|
||||
`/api/wwi/jobs/${jobName}/trigger`,
|
||||
"frontend.jobs.wwi.trigger",
|
||||
);
|
||||
|
||||
|
||||
@@ -126,11 +126,12 @@ export type WWIScenario = {
|
||||
export type AWAnomalyPoint = {
|
||||
date: string;
|
||||
revenue: number;
|
||||
rolling_mean: number;
|
||||
lower_band: number;
|
||||
upper_band: number;
|
||||
// null during the rolling-window warm-up period
|
||||
rolling_mean: number | null;
|
||||
lower_band: number | null;
|
||||
upper_band: number | null;
|
||||
is_anomaly: boolean;
|
||||
z_score: number;
|
||||
z_score: number | null;
|
||||
direction: "high" | "low" | null;
|
||||
};
|
||||
|
||||
|
||||
@@ -72,8 +72,16 @@ export default function AnomalyDetection() {
|
||||
anomalyRevenue: p.is_anomaly ? p.revenue : null,
|
||||
}));
|
||||
|
||||
const domainMin = Math.min(...series.map((p) => p.lower_band)) * 0.95;
|
||||
const domainMax = Math.max(...series.map((p) => p.upper_band)) * 1.05;
|
||||
// Bands are null during the rolling-window warm-up; ignore those and fall
|
||||
// back to revenue so anomaly points are never clipped off the chart.
|
||||
const lows = series
|
||||
.map((p) => (p.lower_band ?? p.revenue))
|
||||
.filter((v): v is number => v != null);
|
||||
const highs = series
|
||||
.map((p) => (p.upper_band ?? p.revenue))
|
||||
.filter((v): v is number => v != null);
|
||||
const domainMin = lows.length ? Math.min(...lows) * 0.95 : 0;
|
||||
const domainMax = highs.length ? Math.max(...highs) * 1.05 : 0;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 max-w-[1100px] mx-auto">
|
||||
@@ -228,16 +236,16 @@ export default function AnomalyDetection() {
|
||||
<td className="font-mono text-sm">{p.date}</td>
|
||||
<td>{money.format(p.revenue)}</td>
|
||||
<td className="text-[rgba(233,244,255,0.5)]">
|
||||
{money.format(p.rolling_mean)}
|
||||
{p.rolling_mean != null ? money.format(p.rolling_mean) : "—"}
|
||||
</td>
|
||||
<td
|
||||
className={
|
||||
Math.abs(p.z_score) > 3
|
||||
(p.z_score != null && Math.abs(p.z_score) > 3)
|
||||
? "text-red-400 font-semibold"
|
||||
: "text-amber-400"
|
||||
}
|
||||
>
|
||||
{p.z_score.toFixed(2)}
|
||||
{p.z_score != null ? p.z_score.toFixed(2) : "—"}
|
||||
</td>
|
||||
<td>{directionBadge(p.direction)}</td>
|
||||
</tr>
|
||||
|
||||
@@ -13,7 +13,6 @@ const AW_JOBS = [
|
||||
const WWI_JOBS = [
|
||||
{ id: "reorder", label: "Reorder Recommendations" },
|
||||
{ id: "supplier_scores", label: "Supplier Scores" },
|
||||
{ id: "events", label: "Business Events" },
|
||||
{ id: "data_quality", label: "Data Quality" },
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user