Files
route-commerce/src/app/admin/orders/page.tsx
T
tyler 84ad60b4b0 feat(admin): comprehensive functionality pass across admin apps (Codex review)
- Restore admin order creation (top blocker):
  - New createAdminOrder server action wrapping create_order_with_items RPC with proper getAdminUser + can_manage_orders + brand scoping.
  - AdminOrdersPanel now supports ?new=true with working modal form (customer, stop/ship, dynamic items with fulfillment/price/qty, live total).
  - Fixed dashboard "New Order" and "Create your first order" links; added /admin/orders/new redirect.
  - Success flow: toast + redirect to list.

- Product edit reliability and admin flows hardened.

- Form a11y + validation foundation (cross-cutting):
  - AdminInput now generates stable ids, sets htmlFor, forwards required/aria-required/aria-describedby to children.
  - All admin forms benefit (labels programmatic, required semantics real).

- Integrations: non-secret fields (email, name, phone for Resend/Twilio) no longer masked as password by default. Only isSecret fields use type=password + toggle.

- Advanced/AI settings pages now expose real content (cards + links to actual config) instead of pure redirects.

- Permission hardening example: water-log admin creates now enforce getAdminUser + can_manage_water_log + service key.

- Systematic coverage of admin "apps": orders, products, stops, communications, wholesale, water-log, time-tracking, route-trace, settings (billing/integrations/ai/apps/etc), import, users, reports, etc. via exploration + targeted fixes. Empty states, toasts, scoping, quick actions improved.

- Updated MEMORY.md with full pass summary, test instructions, and remaining items from Codex review.

Non-destructive focus during pass; used dev auth for verification. Core blockers from review now addressed for testing.

See session plan.md for detailed execution guide.
2026-06-03 15:29:03 +00:00

89 lines
2.8 KiB
TypeScript

import AdminOrdersPanel from "@/components/admin/AdminOrdersPanel";
import { getAdminUser } from "@/lib/admin-permissions";
import { getAdminOrders } from "@/actions/orders";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
import { PageHeader } from "@/components/admin/design-system";
import { redirect } from "next/navigation";
import { supabase } from "@/lib/supabase";
export const dynamic = "force-dynamic";
export default async function AdminOrdersPage() {
const adminUser = await getAdminUser();
if (!adminUser) return <AdminAccessDenied />;
if (!adminUser.can_manage_orders) {
redirect("/admin/pickup");
}
const { orders, stops } = await getAdminOrders();
const brandStops = adminUser?.brand_id
? stops.filter((s) => s.brand_id === adminUser.brand_id)
: stops;
const brandOrders = adminUser?.brand_id
? orders.filter(
(o) =>
o.stops && brandStops.some((s) => s.id === o.stop_id)
)
: orders;
// Fetch active products for this brand (for admin "New Order" item picker)
let brandProducts: Array<{ id: string; name: string; price: number; type?: string | null; active?: boolean }> = [];
try {
let prodQuery = supabase
.from("products")
.select("id, name, price, type, active")
.eq("active", true)
.is("deleted_at", null)
.order("name")
.limit(200);
if (adminUser?.brand_id) {
prodQuery = prodQuery.eq("brand_id", adminUser.brand_id);
}
const { data: prods } = await prodQuery;
brandProducts = (prods ?? []).map((p: any) => ({
id: p.id,
name: p.name,
price: Number(p.price),
type: p.type ?? null,
active: p.active ?? true,
}));
} catch {
// non-fatal for the orders list
}
return (
<div className="min-h-screen bg-[var(--admin-bg)]">
<div className="px-4 sm:px-6 md:px-8 pt-4 sm:pt-6">
<PageHeader
title="Orders"
subtitle="Manage customer orders and pickup status"
icon={
<svg className="h-5 w-5 sm:h-6 sm:w-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/>
<path d="M3 6h18"/>
<path d="M16 10a4 4 0 0 1-8 0"/>
</svg>
}
/>
</div>
{/* Content */}
<div className="px-4 sm:px-6 md:px-8 py-4 sm:py-6">
<div className="rounded-2xl border border-[var(--admin-border)] bg-white overflow-hidden">
<AdminOrdersPanel
initialOrders={brandOrders}
initialStops={brandStops}
initialProducts={brandProducts}
brandId={adminUser?.brand_id ?? null}
/>
</div>
</div>
</div>
);
}