diff --git a/src/actions/ai/preferences.ts b/src/actions/ai/preferences.ts deleted file mode 100644 index 384c3c5..0000000 --- a/src/actions/ai/preferences.ts +++ /dev/null @@ -1,108 +0,0 @@ -"use server"; - -import { pool } from "@/lib/db"; - -export type AIAuthConfig = { - provider: string; - api_key: string; - organization_id: string; - base_url: string; - model: string; - max_tokens: number; -}; - -type AIRow = { - api_key: string | null; - organization_id: string | null; - base_url: string | null; - model: string | null; - max_tokens: number | null; -}; - -export async function getAIPreferences(brandId: string): Promise<{ - api_key?: string; - organization_id?: string; - base_url?: string; - model?: string; - max_tokens?: number; -} | null> { - const { rows } = await pool.query( - `SELECT api_key, organization_id, base_url, model, max_tokens - FROM brand_ai_settings - WHERE brand_id = $1 - LIMIT 1`, - [brandId], - ); - const row = rows[0]; - if (!row) return null; - return { - api_key: row.api_key ?? undefined, - organization_id: row.organization_id ?? undefined, - base_url: row.base_url ?? undefined, - model: row.model ?? undefined, - max_tokens: row.max_tokens ?? undefined, - }; -} - -export async function saveAIPreferences( - brandId: string, - config: AIAuthConfig -): Promise<{ success: boolean; error?: string }> { - try { - await pool.query( - `INSERT INTO brand_ai_settings - (brand_id, provider, api_key, organization_id, base_url, model, max_tokens, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, NOW()) - ON CONFLICT (brand_id) DO UPDATE - SET provider = EXCLUDED.provider, - api_key = EXCLUDED.api_key, - organization_id = EXCLUDED.organization_id, - base_url = EXCLUDED.base_url, - model = EXCLUDED.model, - max_tokens = EXCLUDED.max_tokens, - updated_at = NOW()`, - [ - brandId, - config.provider, - config.api_key || null, - config.organization_id || null, - config.base_url || null, - config.model || "gpt-4o-mini", - config.max_tokens || 4000, - ], - ); - return { success: true }; - } catch (err) { - return { - success: false, - error: err instanceof Error ? err.message : "Failed to save AI preferences", - }; - } -} - -export async function testAIConnection(config: AIAuthConfig): Promise<{ ok: boolean; message: string }> { - if (!config.api_key?.trim()) { - return { ok: false, message: "API key is required" }; - } - - try { - const baseUrl = config.base_url?.trim() || "https://api.openai.com/v1"; - const url = `${baseUrl.replace(/\/$/, "")}/models`; - - const response = await fetch(url, { - headers: { - Authorization: `Bearer ${config.api_key}`, - }, - }); - - if (response.ok) { - return { ok: true, message: "Connection successful! Your API key is valid." }; - } else if (response.status === 401) { - return { ok: false, message: "Invalid API key. Please check and try again." }; - } else { - return { ok: false, message: `Error: ${response.status} ${response.statusText}` }; - } - } catch { - return { ok: false, message: "Could not connect. Check your network and API key." }; - } -} \ No newline at end of file diff --git a/src/actions/storefront.ts b/src/actions/storefront.ts index 67cbd31..f940725 100644 --- a/src/actions/storefront.ts +++ b/src/actions/storefront.ts @@ -98,12 +98,17 @@ export async function getStorefrontStops(brandId: string): Promise { const { rows } = await pool.query( - `SELECT id, brand_id, name, description, price::float8 AS price, + `SELECT id, brand_id, name, description, + price::float8 / 100 AS price, type, image_url, is_taxable, pickup_type, active FROM products WHERE brand_id = $1 @@ -137,16 +142,16 @@ export async function getStorefrontData(slug: string): Promise { export type StorefrontWholesaleSettings = { invoice_business_name: string | null; - invoice_business_address: string | null; - invoice_business_phone: string | null; - invoice_business_email: string | null; - invoice_business_website: string | null; }; /** - * Public wholesale-settings record for a brand — just the public-facing - * invoice/business info used by the storefront contact page. Returns - * `null` if the brand or its wholesale settings row doesn't exist. + * Public wholesale-settings record for a brand — just the invoice + * business name used by the storefront contact page. Returns `null` + * if the brand or its wholesale settings row doesn't exist. + * + * NOTE: only `invoice_business_name` is exposed; other invoice-contact + * fields don't exist on `wholesale_settings` yet and shouldn't be + * surfaced here. If you need them, add a migration first. */ export async function getStorefrontWholesaleSettings( slug: string, @@ -154,8 +159,7 @@ export async function getStorefrontWholesaleSettings( const brand = await getStorefrontBrandBySlug(slug); if (!brand) return null; const { rows } = await pool.query( - `SELECT invoice_business_name, invoice_business_address, - invoice_business_phone, invoice_business_email, invoice_business_website + `SELECT invoice_business_name FROM wholesale_settings WHERE brand_id = $1 LIMIT 1`, @@ -206,14 +210,17 @@ export async function getStorefrontBrandName( } /** - * Look up a single public stop by its URL slug, plus the brand's - * active products. Used by `/[brand]/stops/[slug]` pages. + * Look up a single public stop by its id, plus the brand's active + * products. Used by `/[brand]/stops/[id]` pages. + * + * NOTE: The URL parameter is named `[id]` (not `[slug]`) because the + * `stops` table has no slug column. URLs use the stop's UUID. * * Returns `null` if the stop is not public, not active, or its date has * already passed. Returns the empty product array if the brand has no * active products at the moment. */ -export async function getStorefrontStopBySlug(slug: string): Promise<{ +export async function getStorefrontStopById(id: string): Promise<{ stop: StorefrontStop; products: StorefrontProduct[]; } | null> { @@ -222,11 +229,11 @@ export async function getStorefrontStopBySlug(slug: string): Promise<{ date::text AS date, time, cutoff_date::text AS cutoff_date, status, notes, is_public FROM stops - WHERE slug = $1 + WHERE id = $1 AND is_public = true AND status = 'active' LIMIT 1`, - [slug], + [id], ); const stop = stopRows[0]; if (!stop) return null; diff --git a/src/app/indian-river-direct/contact/ContactClientPage.tsx b/src/app/indian-river-direct/contact/ContactClientPage.tsx index 493057c..8af3533 100644 --- a/src/app/indian-river-direct/contact/ContactClientPage.tsx +++ b/src/app/indian-river-direct/contact/ContactClientPage.tsx @@ -10,10 +10,6 @@ import { getStorefrontWholesaleSettings } from "@/actions/storefront"; type BrandSettings = { invoice_business_name: string | null; - invoice_business_address: string | null; - invoice_business_phone: string | null; - invoice_business_email: string | null; - invoice_business_website: string | null; }; type FormState = { @@ -90,7 +86,7 @@ export default function IndianRiverContactPage() { {[ { label: "Address", - value: brandSettings?.invoice_business_address ?? "Indian River Region, Florida", + value: "Indian River Region, Florida", icon: , iconPath: }, @@ -102,7 +98,7 @@ export default function IndianRiverContactPage() { }, { label: "Email", - value: brandSettings?.invoice_business_email ?? "Info@indianriverdirect.com", + value: "Info@indianriverdirect.com", icon: , iconPath: null }, diff --git a/src/app/indian-river-direct/stops/IndianRiverStopsList.tsx b/src/app/indian-river-direct/stops/IndianRiverStopsList.tsx index 20ca0a6..2d0ceec 100644 --- a/src/app/indian-river-direct/stops/IndianRiverStopsList.tsx +++ b/src/app/indian-river-direct/stops/IndianRiverStopsList.tsx @@ -69,7 +69,7 @@ export default function IndianRiverStopsList({ stops, brandName, brandSlug }: Pr {upcomingStops.map((stop) => (
diff --git a/src/app/indian-river-direct/stops/[slug]/error.tsx b/src/app/indian-river-direct/stops/[id]/error.tsx similarity index 100% rename from src/app/indian-river-direct/stops/[slug]/error.tsx rename to src/app/indian-river-direct/stops/[id]/error.tsx diff --git a/src/app/indian-river-direct/stops/[slug]/loading.tsx b/src/app/indian-river-direct/stops/[id]/loading.tsx similarity index 100% rename from src/app/indian-river-direct/stops/[slug]/loading.tsx rename to src/app/indian-river-direct/stops/[id]/loading.tsx diff --git a/src/app/indian-river-direct/stops/[slug]/page.tsx b/src/app/indian-river-direct/stops/[id]/page.tsx similarity index 98% rename from src/app/indian-river-direct/stops/[slug]/page.tsx rename to src/app/indian-river-direct/stops/[id]/page.tsx index 5468918..6ba0294 100644 --- a/src/app/indian-river-direct/stops/[slug]/page.tsx +++ b/src/app/indian-river-direct/stops/[id]/page.tsx @@ -9,7 +9,7 @@ import StopSetEffect from "@/components/storefront/StopSetEffect"; import StorefrontHeader from "@/components/storefront/StorefrontHeader"; import StorefrontFooter from "@/components/storefront/StorefrontFooter"; import LayoutContainer from "@/components/layout/LayoutContainer"; -import { getStorefrontStopBySlug } from "@/actions/storefront"; +import { getStorefrontStopById } from "@/actions/storefront"; import { formatDate } from "@/lib/format-date"; type Product = { @@ -53,7 +53,7 @@ export default function StopPage() { useEffect(() => { async function load() { - const result = await getStorefrontStopBySlug(slug); + const result = await getStorefrontStopById(slug); if (!result) return; setStop(result.stop as unknown as Stop); diff --git a/src/app/tuxedo/contact/ContactClientPage.tsx b/src/app/tuxedo/contact/ContactClientPage.tsx index 041ef3f..7a089b6 100644 --- a/src/app/tuxedo/contact/ContactClientPage.tsx +++ b/src/app/tuxedo/contact/ContactClientPage.tsx @@ -9,10 +9,6 @@ import { getStorefrontWholesaleSettings } from "@/actions/storefront"; type BrandSettings = { invoice_business_name: string | null; - invoice_business_address: string | null; - invoice_business_phone: string | null; - invoice_business_email: string | null; - invoice_business_website: string | null; }; type FormState = { @@ -72,7 +68,7 @@ export default function TuxedoContactPage() {

Farm Address

- {brandSettings?.invoice_business_address ?? "59751 David Road, Olathe, CO 81425"} + 59751 David Road, Olathe, CO 81425

@@ -96,8 +92,8 @@ export default function TuxedoContactPage() {

Email

- - {brandSettings?.invoice_business_email ?? "orders@tuxedocorn.com"} + + orders@tuxedocorn.com diff --git a/src/app/tuxedo/stops/TuxedoStopsList.tsx b/src/app/tuxedo/stops/TuxedoStopsList.tsx index c8f75c4..bdf04cf 100644 --- a/src/app/tuxedo/stops/TuxedoStopsList.tsx +++ b/src/app/tuxedo/stops/TuxedoStopsList.tsx @@ -86,7 +86,7 @@ export default function TuxedoStopsList({ stops, brandName, brandSlug }: Props) {upcomingStops.map((stop) => (
diff --git a/src/app/tuxedo/stops/[slug]/error.tsx b/src/app/tuxedo/stops/[id]/error.tsx similarity index 100% rename from src/app/tuxedo/stops/[slug]/error.tsx rename to src/app/tuxedo/stops/[id]/error.tsx diff --git a/src/app/tuxedo/stops/[slug]/loading.tsx b/src/app/tuxedo/stops/[id]/loading.tsx similarity index 100% rename from src/app/tuxedo/stops/[slug]/loading.tsx rename to src/app/tuxedo/stops/[id]/loading.tsx diff --git a/src/app/tuxedo/stops/[slug]/page.tsx b/src/app/tuxedo/stops/[id]/page.tsx similarity index 98% rename from src/app/tuxedo/stops/[slug]/page.tsx rename to src/app/tuxedo/stops/[id]/page.tsx index 0c331bf..9b3a9c1 100644 --- a/src/app/tuxedo/stops/[slug]/page.tsx +++ b/src/app/tuxedo/stops/[id]/page.tsx @@ -8,7 +8,7 @@ import StopSetEffect from "@/components/storefront/StopSetEffect"; import StorefrontHeader from "@/components/storefront/StorefrontHeader"; import StorefrontFooter from "@/components/storefront/StorefrontFooter"; import LayoutContainer from "@/components/layout/LayoutContainer"; -import { getStorefrontStopBySlug } from "@/actions/storefront"; +import { getStorefrontStopById } from "@/actions/storefront"; import { formatDate } from "@/lib/format-date"; type Product = { @@ -52,7 +52,7 @@ export default function StopPage() { useEffect(() => { async function load() { - const result = await getStorefrontStopBySlug(slug); + const result = await getStorefrontStopById(slug); if (!result) return; setStop(result.stop as unknown as Stop); diff --git a/src/components/storefront/StopCard.tsx b/src/components/storefront/StopCard.tsx index 72fd7df..51f590a 100644 --- a/src/components/storefront/StopCard.tsx +++ b/src/components/storefront/StopCard.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import { formatDate } from "@/lib/format-date"; type StopCardProps = { - slug: string; + id: string; city: string; state: string; date: string; @@ -13,7 +13,7 @@ type StopCardProps = { }; export default function StopCard({ - slug, city, state, date, time, location, + id, city, state, date, time, location, brandSlug = "tuxedo", brandAccent = "green", }: StopCardProps) { const ctaBg = @@ -25,7 +25,7 @@ export default function StopCard({ return (
- + {/* Location */}
@@ -56,7 +56,7 @@ export default function StopCard({ {/* CTA */}
Shop This Stop