chore(18-01): remove forecast route, sidebar entry, and revalidatePath calls (CLEAN-01)
- Remove Forecast NAV_ITEMS entry and TrendingUp import from AdminSidebar
- Delete src/app/admin/forecast/page.tsx and empty directory
- Remove revalidatePath("/admin/forecast") from assignOfferToProject, removeProjectOffer, updateProjectOfferTotal
This commit is contained in:
@@ -1,75 +0,0 @@
|
|||||||
import { getRevenueForecast12Months } from "@/lib/forecast-queries";
|
|
||||||
|
|
||||||
export const revalidate = 0;
|
|
||||||
|
|
||||||
export default async function ForecastPage() {
|
|
||||||
const forecast = await getRevenueForecast12Months();
|
|
||||||
const totalForecast = forecast.reduce((sum, m) => sum + m.total, 0);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="max-w-3xl mx-auto py-8 px-4">
|
|
||||||
<h1 className="text-2xl font-bold text-[#1a1a1a] mb-2">Revenue Forecast</h1>
|
|
||||||
<p className="text-sm text-[#71717a] mb-6">
|
|
||||||
Proiezione 12 mesi basata sulle offerte attive, i loro accepted_total e le durate in mesi.
|
|
||||||
Ogni offerta distribuisce il suo totale equamente per i mesi di durata a partire dalla data di inizio.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="bg-white rounded-lg border border-[#e5e7eb] overflow-hidden">
|
|
||||||
<table className="w-full text-sm">
|
|
||||||
<thead className="bg-[#f9f9f9] border-b border-[#e5e7eb]">
|
|
||||||
<tr>
|
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-[#71717a] uppercase tracking-wider">
|
|
||||||
Mese
|
|
||||||
</th>
|
|
||||||
<th className="px-4 py-3 text-right text-xs font-semibold text-[#71717a] uppercase tracking-wider">
|
|
||||||
Fatturato previsto
|
|
||||||
</th>
|
|
||||||
<th className="px-4 py-3 text-right text-xs font-semibold text-[#71717a] uppercase tracking-wider">
|
|
||||||
Barra
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="divide-y divide-[#e5e7eb]">
|
|
||||||
{forecast.map((month) => {
|
|
||||||
const maxTotal = Math.max(...forecast.map((m) => m.total), 1);
|
|
||||||
const pct = Math.round((month.total / maxTotal) * 100);
|
|
||||||
return (
|
|
||||||
<tr key={`${month.year}-${month.month}`} className="hover:bg-[#f9f9f9]">
|
|
||||||
<td className="px-4 py-3 text-[#1a1a1a] capitalize">{month.label}</td>
|
|
||||||
<td className="px-4 py-3 text-right font-mono text-[#1a1a1a]">
|
|
||||||
{month.total > 0 ? `€${month.total.toFixed(2)}` : <span className="text-[#71717a]">—</span>}
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
{month.total > 0 && (
|
|
||||||
<div className="flex justify-end items-center">
|
|
||||||
<div
|
|
||||||
className="bg-[#1A463C] h-2 rounded"
|
|
||||||
style={{ width: `${pct}%`, minWidth: "4px", maxWidth: "120px" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
<tfoot className="border-t-2 border-[#e5e7eb] bg-[#f9f9f9]">
|
|
||||||
<tr>
|
|
||||||
<td className="px-4 py-3 text-sm font-semibold text-[#1a1a1a]">Totale 12 mesi</td>
|
|
||||||
<td className="px-4 py-3 text-right font-mono font-bold text-[#1a1a1a]">
|
|
||||||
€{totalForecast.toFixed(2)}
|
|
||||||
</td>
|
|
||||||
<td />
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{forecast.every((m) => m.total === 0) && (
|
|
||||||
<p className="text-sm text-[#71717a] mt-4">
|
|
||||||
Nessuna offerta con accepted_total trovata. Assegna micro-offerte ai progetti e imposta il totale accettato.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -135,14 +135,12 @@ export async function assignOfferToProject(formData: FormData) {
|
|||||||
: null,
|
: null,
|
||||||
});
|
});
|
||||||
revalidatePath(`/admin/projects/${parsed.data.project_id}`);
|
revalidatePath(`/admin/projects/${parsed.data.project_id}`);
|
||||||
revalidatePath("/admin/forecast");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeProjectOffer(projectOfferId: string, projectId: string) {
|
export async function removeProjectOffer(projectOfferId: string, projectId: string) {
|
||||||
await requireAdmin();
|
await requireAdmin();
|
||||||
await db.delete(project_offers).where(eq(project_offers.id, projectOfferId));
|
await db.delete(project_offers).where(eq(project_offers.id, projectOfferId));
|
||||||
revalidatePath(`/admin/projects/${projectId}`);
|
revalidatePath(`/admin/projects/${projectId}`);
|
||||||
revalidatePath("/admin/forecast");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateProjectOfferTotal(
|
export async function updateProjectOfferTotal(
|
||||||
@@ -158,5 +156,4 @@ export async function updateProjectOfferTotal(
|
|||||||
.set({ accepted_total: amount.toFixed(2) })
|
.set({ accepted_total: amount.toFixed(2) })
|
||||||
.where(eq(project_offers.id, projectOfferId));
|
.where(eq(project_offers.id, projectOfferId));
|
||||||
revalidatePath(`/admin/projects/${projectId}`);
|
revalidatePath(`/admin/projects/${projectId}`);
|
||||||
revalidatePath("/admin/forecast");
|
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
Users,
|
Users,
|
||||||
FolderOpen,
|
FolderOpen,
|
||||||
Tag,
|
Tag,
|
||||||
TrendingUp,
|
|
||||||
BookOpen,
|
BookOpen,
|
||||||
Settings,
|
Settings,
|
||||||
LogOut,
|
LogOut,
|
||||||
@@ -21,7 +20,6 @@ const NAV_ITEMS = [
|
|||||||
{ href: "/admin/leads", label: "Lead", icon: Zap },
|
{ href: "/admin/leads", label: "Lead", icon: Zap },
|
||||||
{ href: "/admin/projects", label: "Progetti", icon: FolderOpen },
|
{ href: "/admin/projects", label: "Progetti", icon: FolderOpen },
|
||||||
{ href: "/admin/offers", label: "Offerte", icon: Tag },
|
{ href: "/admin/offers", label: "Offerte", icon: Tag },
|
||||||
{ href: "/admin/forecast", label: "Forecast", icon: TrendingUp },
|
|
||||||
{ href: "/admin/catalog", label: "Catalogo", icon: BookOpen },
|
{ href: "/admin/catalog", label: "Catalogo", icon: BookOpen },
|
||||||
{ href: "/admin/impostazioni", label: "Impostazioni", icon: Settings },
|
{ href: "/admin/impostazioni", label: "Impostazioni", icon: Settings },
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user