refactor(storefront): remove supabase shim and restore customer storefronts

The legacy src/lib/supabase.ts shim returned { data: null } for every
query, so 15+ pages were silently rendering empty state — including the
two customer-facing storefronts (tuxedo, indian-river-direct) and
several v1 admin pages. Replace every caller with canonical access:

- New src/actions/storefront.ts server-action module: brand lookup,
  public stops, active products, stop-by-slug, wholesale settings,
  portal config. Uses the shared pg Pool and SECURITY DEFINER RPCs.
- src/actions/brand-settings.ts: getBrandSettingsPublic inlined the
  brands + brand_settings LEFT JOIN wholesale_settings query (the
  RPC it called did not exist; try/catch was masking the failure).
- src/actions/ai/preferences.ts: switched get/saveAIPreferences to
  pool.query.
- src/actions/square-sync-ui.ts: new getSquareQueueCount action for
  SquareSyncWidget (replaces shim count).
- src/app/api/{tuxedo,indian-river-direct}/schedule-pdf/route.ts:
  use pool.query.
- next.config.ts: redirects /admin/{products/:id,reports,taxes,
  settings/{shipping,integrations,billing}} → their v2 / settings
  equivalents, then deleted those pages.
- Deleted src/lib/supabase.ts (no remaining imports).

Tests: 174/175 (unchanged from baseline; pre-existing getAdminUser
mock issue is tracked in Step 5).
This commit is contained in:
Nora
2026-06-25 17:12:28 -06:00
parent 9f3dc9b68e
commit 2daa8fd4b6
22 changed files with 241 additions and 1146 deletions
@@ -1,61 +0,0 @@
import { redirect } from "next/navigation";
import { supabase } from "@/lib/supabase";
import { getAdminUser } from "@/lib/admin-permissions";
import IntegrationsClientPage from "./IntegrationsClientPage";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
export const metadata = {
title: "Integrations - Route Commerce Admin",
description: "Configure integrations for AI, email, SMS, and payments",
};
export default async function IntegrationsPage() {
const adminUser = await getAdminUser();
if (!adminUser) return <AdminAccessDenied />;
const isPlatformAdmin = adminUser.role === "platform_admin";
const brandId = adminUser.brand_id ?? "";
// Platform admins: fetch all brands for the picker
const brands = isPlatformAdmin
? ((await supabase.from("brands").select("id, name").order("name")) as unknown as { data: { id: string; name: string }[] }).data ?? []
: [];
return (
<main className="min-h-screen bg-[var(--admin-bg)]">
<div className="px-4 sm:px-6 md:px-8 pt-4 sm:pt-6 pb-10">
{/* Breadcrumb */}
<nav className="flex items-center gap-2 text-xs text-[var(--admin-text-muted)] mb-6">
<a href="/admin" className="hover:text-[var(--admin-text-primary)] transition-colors">Admin</a>
<span>/</span>
<a href="/admin/settings" className="hover:text-[var(--admin-text-primary)] transition-colors">Settings</a>
<span>/</span>
<span className="text-[var(--admin-text-primary)]">Integrations</span>
</nav>
{/* Page Header */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-[var(--admin-accent)] text-white">
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
</svg>
</div>
<div>
<h1 className="text-2xl sm:text-3xl font-bold text-[var(--admin-text-primary)]">Integrations</h1>
<p className="text-sm text-[var(--admin-text-muted)]">
Connect AI, email, SMS, and payment providers to power your operations.
</p>
</div>
</div>
</div>
<IntegrationsClientPage
brandId={brandId}
brands={brands}
isPlatformAdmin={isPlatformAdmin}
/>
</div>
</main>
);
}