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
@@ -5,7 +5,8 @@ import { motion } from "framer-motion";
import StorefrontHeader from "@/components/storefront/StorefrontHeader";
import StorefrontFooter from "@/components/storefront/StorefrontFooter";
import LayoutContainer from "@/components/layout/LayoutContainer";
import { supabase } from "@/lib/supabase";
import { getBrandSettingsPublic } from "@/actions/brand-settings";
import { getStorefrontWholesaleSettings } from "@/actions/storefront";
type BrandSettings = {
invoice_business_name: string | null;
@@ -34,17 +35,22 @@ export default function IndianRiverContactPage() {
const [contactPhone, setContactPhone] = useState<string | null>(null);
useEffect(() => {
supabase.from("brands").select("id").eq("slug", "indian-river-direct").single().then(({ data: brand }) => {
if (!brand?.id) return;
supabase.from("wholesale_settings")
.select("invoice_business_name, invoice_business_address, invoice_business_phone, invoice_business_email, invoice_business_website")
.eq("brand_id", brand.id).single().then(({ data }) => setBrandSettings((data as unknown as BrandSettings) ?? null));
supabase.from("brand_settings").select("logo_url, custom_footer_text, email, phone")
.eq("brand_id", brand.id).single().then(({ data: s }) => {
if (s) { setLogoUrl((s as { logo_url: string | null }).logo_url ?? null); setCustomFooterText((s as { custom_footer_text: string | null }).custom_footer_text ?? null); setContactEmail((s as { email: string | null }).email ?? null); setContactPhone((s as { phone: string | null }).phone ?? null); }
});
try { import("@/actions/admin-user").then(({ getCurrentAdminUser }) => { getCurrentAdminUser().then((u: unknown) => setIsAdmin(!!u)); }); } catch { /* not logged in */ }
Promise.all([
getStorefrontWholesaleSettings("indian-river-direct"),
getBrandSettingsPublic("indian-river-direct"),
]).then(([wholesale, brandSettings]) => {
setBrandSettings(wholesale as BrandSettings | null);
if (brandSettings.success && brandSettings.settings) {
const s = brandSettings.settings;
setLogoUrl(s.logo_url ?? null);
setCustomFooterText(s.custom_footer_text ?? null);
setContactEmail(s.email ?? null);
setContactPhone(s.phone ?? null);
}
});
import("@/actions/admin-user").then(({ getCurrentAdminUser }) => {
getCurrentAdminUser().then((u: unknown) => setIsAdmin(!!u));
}).catch(() => { /* not logged in */ });
}, []);
function handleSubmit(e: React.FormEvent) {
@@ -6,7 +6,7 @@ import { motion } from "framer-motion";
import StorefrontHeader from "@/components/storefront/StorefrontHeader";
import StorefrontFooter from "@/components/storefront/StorefrontFooter";
import LayoutContainer from "@/components/layout/LayoutContainer";
import { supabase } from "@/lib/supabase";
import { getBrandSettingsPublic } from "@/actions/brand-settings";
type FAQCategory = {
category: string;
@@ -136,10 +136,10 @@ export default function IndianRiverFAQPage() {
const [searchQuery, setSearchQuery] = useState("");
useEffect(() => {
supabase.from("brand_settings").select("phone").eq("brand_id",
"b1cb7a96-d82b-40b1-80b1-d6dd26c56e28"
).single().then(({ data }) => {
if (data && (data as { phone: string | null }).phone) setFaqCategories(buildFaqCategories((data as { phone: string | null }).phone!));
getBrandSettingsPublic("indian-river-direct").then((result) => {
if (result.success && result.settings?.phone) {
setFaqCategories(buildFaqCategories(result.settings.phone));
}
});
}, []);