import { useState, useMemo, useEffect } from "react"; import { Loader2, CheckCircle2, CreditCard, FileText, ChevronsUpDown, Check } from "lucide-react"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/hooks/useAuth"; import { toast } from "sonner"; import { z } from "zod"; import { StripeEmbeddedCheckout } from "@/components/StripeEmbeddedCheckout"; import { getStripeEnvironment, hasPaymentsConfigured } from "@/lib/stripe"; import { cn } from "@/lib/utils"; const STORSTOCKHOLM_CITIES = [ "Stockholm", "Solna", "Sundbyberg", "Nacka", "Lidingö", "Danderyd", "Täby", "Sollentuna", "Järfälla", "Upplands Väsby", "Sigtuna", "Vallentuna", "Österåker", "Vaxholm", "Värmdö", "Tyresö", "Haninge", "Nynäshamn", "Botkyrka", "Salem", "Södertälje", "Nykvarn", "Huddinge", "Bromma", "Ekerö", "Upplands-Bro", ]; // Map första 3 siffrorna i postnummer → kommun/ort inom Storstockholm. const POSTAL_PREFIX_TO_CITY: Record = { "100": "Stockholm", "101": "Stockholm", "102": "Stockholm", "103": "Stockholm", "104": "Stockholm", "105": "Stockholm", "106": "Stockholm", "107": "Stockholm", "108": "Stockholm", "110": "Stockholm", "111": "Stockholm", "112": "Stockholm", "113": "Stockholm", "114": "Stockholm", "115": "Stockholm", "116": "Stockholm", "117": "Stockholm", "118": "Stockholm", "119": "Stockholm", "120": "Stockholm", "121": "Stockholm", "122": "Stockholm", "123": "Stockholm", "124": "Stockholm", "125": "Stockholm", "126": "Stockholm", "127": "Stockholm", "128": "Stockholm", "129": "Stockholm", "131": "Nacka", "132": "Nacka", "133": "Nacka", "138": "Nacka", "134": "Värmdö", "139": "Värmdö", "135": "Tyresö", "136": "Haninge", "137": "Haninge", "141": "Huddinge", "142": "Huddinge", "143": "Huddinge", "144": "Salem", "145": "Botkyrka", "146": "Botkyrka", "147": "Botkyrka", "148": "Nynäshamn", "149": "Nynäshamn", "150": "Södertälje", "151": "Södertälje", "152": "Södertälje", "153": "Södertälje", "155": "Nykvarn", "168": "Bromma", "169": "Bromma", "170": "Solna", "171": "Solna", "173": "Solna", "172": "Sundbyberg", "174": "Sundbyberg", "175": "Järfälla", "176": "Järfälla", "177": "Järfälla", "178": "Ekerö", "179": "Ekerö", "181": "Lidingö", "182": "Danderyd", "183": "Täby", "187": "Täby", "184": "Österåker", "185": "Vaxholm", "186": "Vallentuna", "191": "Sollentuna", "192": "Sollentuna", "193": "Sigtuna", "195": "Sigtuna", "194": "Upplands Väsby", "196": "Upplands-Bro", "197": "Upplands-Bro", }; const cityFromPostalCode = (postalCode: string): string | null => { const cleaned = postalCode.replace(/\s/g, ""); if (!/^\d{5}$/.test(cleaned)) return null; return POSTAL_PREFIX_TO_CITY[cleaned.slice(0, 3)] ?? null; }; const PRICE_PER_KG_EXKL = 325; // exkl. moms, används för Stripe/faktura const VAT_RATE = 0.25; const PRICE_PER_KG = PRICE_PER_KG_EXKL * (1 + VAT_RATE); // 406,25 kr ink moms – visas för kunden const KG_PER_EMPLOYEE = 0.3; const WEEKS_PER_MONTH = 4; const isStockholmPostalCode = (postalCode: string) => { const cleaned = postalCode.replace(/\s/g, ""); if (!/^\d{5}$/.test(cleaned)) return false; const num = parseInt(cleaned, 10); return num >= 10000 && num <= 19999; }; const stockholmDeliveryMessage = "Vi levererar endast till företag inom Storstockholm."; const formSchema = z.object({ företag: z.string().trim().min(1, "Fyll i företagsnamn").max(100), epost: z.string().trim().email("Ange en giltig e-postadress").max(255), telefon: z.string().trim().max(30).optional(), adress: z.string().trim().min(1, "Fyll i leveransadress").max(200), postnummer: z .string() .trim() .min(1, "Fyll i postnummer") .refine(isStockholmPostalCode, "Vi levererar endast inom Storstockholm (100 00 – 199 99)"), stad: z.string().trim().min(1, "Fyll i stad").max(100), kommentarer: z.string().trim().max(1000).optional(), }); type Method = "card" | "invoice"; const PlansSection = () => { const { user } = useAuth(); const [employees, setEmployees] = useState(10); const [showForm, setShowForm] = useState(false); const [method, setMethod] = useState("card"); const [submitting, setSubmitting] = useState(false); const [submitted, setSubmitted] = useState(null); const [errors, setErrors] = useState>({}); const [clientSecret, setClientSecret] = useState(null); const [cityOpen, setCityOpen] = useState(false); const [form, setForm] = useState({ företag: "", epost: "", telefon: "", adress: "", postnummer: "", stad: "", kommentarer: "", }); const filteredCities = useMemo(() => { const query = form.stad.trim().toLocaleLowerCase("sv-SE"); if (!query) return STORSTOCKHOLM_CITIES; return STORSTOCKHOLM_CITIES.filter((city) => city.toLocaleLowerCase("sv-SE").includes(query) ); }, [form.stad]); const recommendedKg = useMemo( () => Math.max(1, Math.round(employees * KG_PER_EMPLOYEE)), [employees] ); const weeklyPrice = recommendedKg * PRICE_PER_KG; const monthlyPrice = weeklyPrice * WEEKS_PER_MONTH; useEffect(() => { if (!user || !showForm) return; (async () => { const { data } = await supabase .from("profiles") .select("company_name, phone, street_address, postal_code, city") .eq("user_id", user.id) .maybeSingle(); setForm((prev) => { const postalCode = prev.postnummer || data?.postal_code || ""; const suggestedCity = cityFromPostalCode(postalCode); return { ...prev, företag: prev.företag || data?.company_name || "", epost: prev.epost || user.email || "", telefon: prev.telefon || data?.phone || "", adress: prev.adress || data?.street_address || "", postnummer: postalCode, stad: suggestedCity || prev.stad || data?.city || "", }; }); })(); }, [user, showForm]); useEffect(() => { const suggestedCity = cityFromPostalCode(form.postnummer); if (!suggestedCity || form.stad === suggestedCity) return; setForm((prev) => ({ ...prev, stad: suggestedCity })); setErrors((prev) => ({ ...prev, stad: "", postnummer: "" })); }, [form.postnummer, form.stad]); const handleChange = ( e: React.ChangeEvent ) => { const { name, value } = e.target; setForm((p) => { const next = { ...p, [name]: value }; if (name === "postnummer") { const suggested = cityFromPostalCode(value); if (suggested) next.stad = suggested; } return next; }); if (errors[name]) setErrors((p) => ({ ...p, [name]: "" })); if (name === "postnummer") { setErrors((p) => ({ ...p, postnummer: "", stad: "" })); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const result = formSchema.safeParse(form); if (!result.success) { const fe: Record = {}; result.error.errors.forEach((err) => { if (err.path[0]) fe[err.path[0] as string] = err.message; }); setErrors(fe); return; } if (!hasPaymentsConfigured()) { toast.error("Betalningar är inte konfigurerade ännu."); return; } setSubmitting(true); try { const body = { method, employees, kg_per_week: recommendedKg, company: result.data.företag, email: result.data.epost, phone: result.data.telefon ?? "", address: result.data.adress, postal_code: result.data.postnummer, city: result.data.stad, comments: result.data.kommentarer ?? "", return_url: `${window.location.origin}/order-confirmation?session_id={CHECKOUT_SESSION_ID}&method=card&kg=${recommendedKg}&email=${encodeURIComponent(result.data.epost)}`, environment: getStripeEnvironment(), }; const { data, error } = await supabase.functions.invoke("create-checkout", { body }); if (error) throw error; if ((data as any)?.error) throw new Error((data as any).error); if (method === "card") { if (!(data as any)?.clientSecret) throw new Error("Kunde inte skapa checkout-session"); setClientSecret((data as any).clientSecret); } else { window.location.href = `/order-confirmation?method=invoice&kg=${recommendedKg}&email=${encodeURIComponent(result.data.epost)}`; return; } } catch (err: any) { console.error(err); toast.error(err?.message || "Kunde inte starta abonnemang. Försök igen."); } finally { setSubmitting(false); } }; // Show embedded checkout after we get clientSecret if (clientSecret) { return (

Slutför betalning

{recommendedKg} kg/vecka · {monthlyPrice.toLocaleString("sv-SE")} kr/månad (ink moms)

clientSecret} />
); } return (

Hur många anställda har ni?

Vi rekommenderar automatiskt rätt mängd kakor utifrån antalet medarbetare.

{submitted === "invoice" ? (

Abonnemang startat!

Vi har skapat ditt abonnemang på{" "} {recommendedKg} kg kakor per vecka . Första fakturan skickas till {form.epost} inom kort.

) : !showForm ? ( <>
{employees}
{employees === 1 ? "anställd" : "anställda"}
setEmployees(parseInt(e.target.value, 10))} className="calculator-slider w-full" aria-label="Antal anställda" />
1 100
Rekommenderad mängd
{recommendedKg} kg
per vecka
Pris per månad
{monthlyPrice.toLocaleString("sv-SE")} kr
ink moms
) : (
Ditt abonnemang
{recommendedKg} kg/vecka —{" "} {monthlyPrice.toLocaleString("sv-SE")} kr/mån
ink moms · {employees} {employees === 1 ? "anställd" : "anställda"}
{/* Betalningsmetod */}
{stockholmDeliveryMessage} Ange postnummer 100 00–199 99 för att gå vidare.
{errors.företag && (

{errors.företag}

)}
{errors.epost && (

{errors.epost}

)}
{errors.adress && (

{errors.adress}

)}
{errors.postnummer && (

{errors.postnummer}

)}

Endast postnummer inom Storstockholm.

setCityOpen(true)} onBlur={() => window.setTimeout(() => setCityOpen(false), 120)} onChange={(e) => { handleChange(e); setCityOpen(true); }} className="w-full px-4 py-3 pr-10 rounded-sm bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary/30" />
{cityOpen && (
{filteredCities.length > 0 ? ( filteredCities.map((city) => ( )) ) : (
Ingen stad hittades.
)}
)}
{errors.stad && (

{errors.stad}

)}