Consolidate admin pages into tabbed layouts with warm stone theme
- Add SettingsClient with tabs: General, Users, Integrations - Add RouteTracePage with tabs: Dashboard, Lots, Lookup, Settings - Fix dark styling in AdminMeClient.tsx (red/green alerts) - Fix dark styling in AIClient.tsx (emerald/amber/green/red/blue) - Fix ImportCenterClient.tsx (already correct, no changes needed) - Fix TypeScript errors in LotListTable, RouteTraceDashboard - Convert lot.id to lot.lot_id for HaulingLot compatibility - Update route-trace components to warm stone theme
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import { getAdminUser } from "@/lib/admin-permissions";
|
||||
import { isFeatureEnabled } from "@/lib/feature-flags";
|
||||
import AdminLookupPage from "@/components/route-trace/AdminLookupPage";
|
||||
import RouteTraceNav from "@/components/route-trace/RouteTraceNav";
|
||||
import RouteTracePage from "@/components/route-trace/RouteTracePage";
|
||||
import {
|
||||
getRouteTraceStats,
|
||||
getRouteTraceLots,
|
||||
getHarvestLotsReadyToHaul,
|
||||
getFieldYieldSummary,
|
||||
getInventoryByCrop,
|
||||
getRecentLotEvents,
|
||||
} from "@/actions/route-trace/lots";
|
||||
|
||||
export default async function LookupPage() {
|
||||
const adminUser = await getAdminUser();
|
||||
@@ -13,16 +20,35 @@ export default async function LookupPage() {
|
||||
const enabled = await isFeatureEnabled(effectiveBrandId, "route_trace");
|
||||
if (!enabled) redirect("/admin/settings/apps?reason=route_trace");
|
||||
|
||||
const [statsResult, lotsResult, haulingResult, yieldResult, invResult, eventsResult] = await Promise.all([
|
||||
getRouteTraceStats(effectiveBrandId),
|
||||
getRouteTraceLots(effectiveBrandId),
|
||||
getHarvestLotsReadyToHaul(effectiveBrandId),
|
||||
getFieldYieldSummary(effectiveBrandId),
|
||||
getInventoryByCrop(effectiveBrandId),
|
||||
getRecentLotEvents(effectiveBrandId, 10),
|
||||
]);
|
||||
|
||||
const stats = statsResult.success ? statsResult.stats : {
|
||||
active_count: 0, in_transit_count: 0, at_shed_count: 0, total_lots_today: 0, total_harvested_today: 0, total_lots: 0,
|
||||
};
|
||||
const recentLots = lotsResult.success ? lotsResult.lots.slice(0, 8) : [];
|
||||
const allLots = lotsResult.success ? lotsResult.lots : [];
|
||||
const haulingLots = haulingResult.success ? haulingResult.lots : [];
|
||||
const fieldYield = yieldResult.success ? yieldResult.summary : [];
|
||||
const inventoryByCrop = invResult.success ? invResult.inventory : [];
|
||||
const recentActivity = eventsResult.success ? eventsResult.events : [];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-zinc-950 px-6 py-10">
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-semibold text-zinc-100">Lookup</h1>
|
||||
<p className="mt-1 text-sm text-zinc-500">Find lots by lot number or crop type</p>
|
||||
</div>
|
||||
<RouteTraceNav activeTab="lookup" />
|
||||
<AdminLookupPage brandId={effectiveBrandId} />
|
||||
</div>
|
||||
</div>
|
||||
<RouteTracePage
|
||||
stats={stats}
|
||||
recentLots={recentLots}
|
||||
haulingLots={haulingLots}
|
||||
fieldYield={fieldYield}
|
||||
inventoryByCrop={inventoryByCrop}
|
||||
recentActivity={recentActivity}
|
||||
brandId={effectiveBrandId}
|
||||
lots={allLots}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -2,9 +2,14 @@ import { redirect } from "next/navigation";
|
||||
import { getAdminUser } from "@/lib/admin-permissions";
|
||||
import { isFeatureEnabled } from "@/lib/feature-flags";
|
||||
import { getRouteTraceLots } from "@/actions/route-trace/lots";
|
||||
import RouteTraceNav from "@/components/route-trace/RouteTraceNav";
|
||||
import LotListTable from "@/components/route-trace/LotListTable";
|
||||
import Link from "next/link";
|
||||
import RouteTracePage from "@/components/route-trace/RouteTracePage";
|
||||
import {
|
||||
getRouteTraceStats,
|
||||
getHarvestLotsReadyToHaul,
|
||||
getFieldYieldSummary,
|
||||
getInventoryByCrop,
|
||||
getRecentLotEvents,
|
||||
} from "@/actions/route-trace/lots";
|
||||
|
||||
export default async function LotsPage() {
|
||||
const adminUser = await getAdminUser();
|
||||
@@ -15,27 +20,35 @@ export default async function LotsPage() {
|
||||
const enabled = await isFeatureEnabled(effectiveBrandId, "route_trace");
|
||||
if (!enabled) redirect("/admin/settings/apps?reason=route_trace");
|
||||
|
||||
const result = await getRouteTraceLots(effectiveBrandId);
|
||||
const lots = result.success ? result.lots : [];
|
||||
const [statsResult, lotsResult, haulingResult, yieldResult, invResult, eventsResult] = await Promise.all([
|
||||
getRouteTraceStats(effectiveBrandId),
|
||||
getRouteTraceLots(effectiveBrandId),
|
||||
getHarvestLotsReadyToHaul(effectiveBrandId),
|
||||
getFieldYieldSummary(effectiveBrandId),
|
||||
getInventoryByCrop(effectiveBrandId),
|
||||
getRecentLotEvents(effectiveBrandId, 10),
|
||||
]);
|
||||
|
||||
const stats = statsResult.success ? statsResult.stats : {
|
||||
active_count: 0, in_transit_count: 0, at_shed_count: 0, total_lots_today: 0, total_harvested_today: 0, total_lots: 0,
|
||||
};
|
||||
const recentLots = lotsResult.success ? lotsResult.lots.slice(0, 8) : [];
|
||||
const allLots = lotsResult.success ? lotsResult.lots : [];
|
||||
const haulingLots = haulingResult.success ? haulingResult.lots : [];
|
||||
const fieldYield = yieldResult.success ? yieldResult.summary : [];
|
||||
const inventoryByCrop = invResult.success ? invResult.inventory : [];
|
||||
const recentActivity = eventsResult.success ? eventsResult.events : [];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-zinc-950 px-6 py-10">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<div className="mb-8 flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold text-zinc-100">Lots</h1>
|
||||
<p className="mt-1 text-sm text-zinc-500">{lots.length} lot{lots.length !== 1 ? "s" : ""} total</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/admin/route-trace/lots/new"
|
||||
className="rounded-xl bg-stone-900 px-4 py-2.5 text-sm font-medium text-white hover:bg-stone-800 transition-colors"
|
||||
>
|
||||
+ New Lot
|
||||
</Link>
|
||||
</div>
|
||||
<RouteTraceNav activeTab="lots" />
|
||||
<LotListTable initialLots={lots} brandId={effectiveBrandId} />
|
||||
</div>
|
||||
</div>
|
||||
<RouteTracePage
|
||||
stats={stats}
|
||||
recentLots={recentLots}
|
||||
haulingLots={haulingLots}
|
||||
fieldYield={fieldYield}
|
||||
inventoryByCrop={inventoryByCrop}
|
||||
recentActivity={recentActivity}
|
||||
brandId={effectiveBrandId}
|
||||
lots={allLots}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
getInventoryByCrop,
|
||||
getRecentLotEvents,
|
||||
} from "@/actions/route-trace/lots";
|
||||
import RouteTraceDashboard from "@/components/route-trace/RouteTraceDashboard";
|
||||
import RouteTracePage from "@/components/route-trace/RouteTracePage";
|
||||
|
||||
export default async function RouteTraceDashboardPage() {
|
||||
const adminUser = await getAdminUser();
|
||||
@@ -33,24 +33,22 @@ export default async function RouteTraceDashboardPage() {
|
||||
active_count: 0, in_transit_count: 0, at_shed_count: 0, total_lots_today: 0, total_harvested_today: 0, total_lots: 0,
|
||||
};
|
||||
const recentLots = lotsResult.success ? lotsResult.lots.slice(0, 8) : [];
|
||||
const allLots = lotsResult.success ? lotsResult.lots : [];
|
||||
const haulingLots = haulingResult.success ? haulingResult.lots : [];
|
||||
const fieldYield = yieldResult.success ? yieldResult.summary : [];
|
||||
const inventoryByCrop = invResult.success ? invResult.inventory : [];
|
||||
const recentActivity = eventsResult.success ? eventsResult.events : [];
|
||||
|
||||
return (
|
||||
<main className="min-h-screen px-6 py-8">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<RouteTraceDashboard
|
||||
stats={stats}
|
||||
recentLots={recentLots}
|
||||
haulingLots={haulingLots}
|
||||
fieldYield={fieldYield}
|
||||
inventoryByCrop={inventoryByCrop}
|
||||
recentActivity={recentActivity}
|
||||
brandId={effectiveBrandId}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<RouteTracePage
|
||||
stats={stats}
|
||||
recentLots={recentLots}
|
||||
haulingLots={haulingLots}
|
||||
fieldYield={fieldYield}
|
||||
inventoryByCrop={inventoryByCrop}
|
||||
recentActivity={recentActivity}
|
||||
brandId={effectiveBrandId}
|
||||
lots={allLots}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import { getAdminUser } from "@/lib/admin-permissions";
|
||||
import { isFeatureEnabled } from "@/lib/feature-flags";
|
||||
import RouteTraceNav from "@/components/route-trace/RouteTraceNav";
|
||||
import RouteTracePage from "@/components/route-trace/RouteTracePage";
|
||||
import {
|
||||
getRouteTraceStats,
|
||||
getRouteTraceLots,
|
||||
getHarvestLotsReadyToHaul,
|
||||
getFieldYieldSummary,
|
||||
getInventoryByCrop,
|
||||
getRecentLotEvents,
|
||||
} from "@/actions/route-trace/lots";
|
||||
|
||||
export default async function RouteTraceSettingsPage() {
|
||||
const adminUser = await getAdminUser();
|
||||
@@ -12,24 +20,35 @@ export default async function RouteTraceSettingsPage() {
|
||||
const enabled = await isFeatureEnabled(effectiveBrandId, "route_trace");
|
||||
if (!enabled) redirect("/admin/settings/apps?reason=route_trace");
|
||||
|
||||
const [statsResult, lotsResult, haulingResult, yieldResult, invResult, eventsResult] = await Promise.all([
|
||||
getRouteTraceStats(effectiveBrandId),
|
||||
getRouteTraceLots(effectiveBrandId),
|
||||
getHarvestLotsReadyToHaul(effectiveBrandId),
|
||||
getFieldYieldSummary(effectiveBrandId),
|
||||
getInventoryByCrop(effectiveBrandId),
|
||||
getRecentLotEvents(effectiveBrandId, 10),
|
||||
]);
|
||||
|
||||
const stats = statsResult.success ? statsResult.stats : {
|
||||
active_count: 0, in_transit_count: 0, at_shed_count: 0, total_lots_today: 0, total_harvested_today: 0, total_lots: 0,
|
||||
};
|
||||
const recentLots = lotsResult.success ? lotsResult.lots.slice(0, 8) : [];
|
||||
const allLots = lotsResult.success ? lotsResult.lots : [];
|
||||
const haulingLots = haulingResult.success ? haulingResult.lots : [];
|
||||
const fieldYield = yieldResult.success ? yieldResult.summary : [];
|
||||
const inventoryByCrop = invResult.success ? invResult.inventory : [];
|
||||
const recentActivity = eventsResult.success ? eventsResult.events : [];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-zinc-950 px-6 py-10">
|
||||
<div className="mx-auto max-w-2xl">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-semibold text-zinc-100">Route Trace Settings</h1>
|
||||
<p className="mt-1 text-sm text-zinc-500">Configure your traceability workflow and defaults</p>
|
||||
</div>
|
||||
<RouteTraceNav activeTab="settings" />
|
||||
<div className="rounded-2xl border border-zinc-800 bg-zinc-900 shadow-black/20 p-6">
|
||||
<p className="text-sm text-zinc-500">Settings coming soon. For now, use the Add-ons page to manage Route Trace.</p>
|
||||
<a
|
||||
href="/admin/settings/apps"
|
||||
className="mt-4 inline-flex items-center gap-1.5 rounded-xl bg-stone-900 px-4 py-2.5 text-sm font-medium text-white hover:bg-stone-800 transition-colors"
|
||||
>
|
||||
→ Manage Add-ons
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<RouteTracePage
|
||||
stats={stats}
|
||||
recentLots={recentLots}
|
||||
haulingLots={haulingLots}
|
||||
fieldYield={fieldYield}
|
||||
inventoryByCrop={inventoryByCrop}
|
||||
recentActivity={recentActivity}
|
||||
brandId={effectiveBrandId}
|
||||
lots={allLots}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user