"use client"; import { FormEvent, Suspense, useEffect, useMemo, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { AuthGuard } from "@/components/AuthGuard"; import { Button, Container } from "@/components/ui"; import { api, ApiError, type TenantContext } from "@/lib/api"; import { useMe } from "@/hooks/useMe"; import { FeatureLock } from "@/modules/commercial/components"; import { evaluateDomainPolicy, loadWorkspaceSnapshot } from "@/modules/commercial/adapters"; import { commercialPost } from "@/modules/commercial/adapters/http"; import { loadAssessmentSession, saveAssessmentSession, } from "@/modules/commercial/session"; type Step = "business" | "branding" | "domain" | "review"; const STEPS: { id: Step; label: string }[] = [ { id: "business", label: "اطلاعات کسب‌وکار" }, { id: "branding", label: "برندینگ" }, { id: "domain", label: "دامنه" }, { id: "review", label: "بازبینی و تکمیل" }, ]; function slugify(value: string): string { return value .trim() .toLowerCase() .replace(/\s+/g, "-") .replace(/[^a-z0-9-]/g, "") .replace(/-+/g, "-") .replace(/^-|-$/g, ""); } const inputClass = "mt-1 w-full rounded-xl border border-gray-200 px-4 py-2.5 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary disabled:bg-gray-50"; function StepIndicator({ current }: { current: Step }) { const currentIndex = STEPS.findIndex((s) => s.id === current); return (
    {STEPS.map((step, index) => { const isActive = step.id === current; const isDone = index < currentIndex; return (
  1. {index + 1} {step.label} {index < STEPS.length - 1 && /}
  2. ); })}
); } function OnboardingWizard() { const router = useRouter(); const searchParams = useSearchParams(); const intentParam = searchParams.get("intent"); const { me, loading: meLoading } = useMe(); const [step, setStep] = useState("business"); const [ctx, setCtx] = useState(null); const [bootstrapping, setBootstrapping] = useState(true); const [error, setError] = useState(""); const [loading, setLoading] = useState(false); const [domainUnlocked, setDomainUnlocked] = useState(false); const [domainReason, setDomainReason] = useState(null); const session = useMemo(() => loadAssessmentSession(), []); const [businessName, setBusinessName] = useState(""); const [slug, setSlug] = useState(""); const [slugTouched, setSlugTouched] = useState(false); const [businessType, setBusinessType] = useState( (typeof session.answers.business_type_code === "string" ? session.answers.business_type_code : intentParam) || "" ); const [primaryColor, setPrimaryColor] = useState("#0284c7"); const [secondaryColor, setSecondaryColor] = useState("#0f172a"); const [logoUrl, setLogoUrl] = useState(""); const [customDomain, setCustomDomain] = useState(""); useEffect(() => { if (meLoading) return; if (!me) { setBootstrapping(false); return; } if (!me.onboarding_required) { router.replace("/dashboard"); return; } if (me.current_tenant_id) { api.tenantContext .current() .then(async (tenantCtx) => { setCtx(tenantCtx); const snap = await loadWorkspaceSnapshot(); if (snap.data) { setDomainUnlocked(snap.data.custom_domain_unlocked); setDomainReason(snap.data.domain_policy?.reason ?? null); } else { const pol = await evaluateDomainPolicy({ tenant_id: tenantCtx.tenant.id, subscription_status: tenantCtx.subscription_status, }); setDomainUnlocked(Boolean(pol.data.custom_domain_allowed)); setDomainReason(pol.data.reason ?? null); } setStep("branding"); }) .catch(() => undefined) .finally(() => setBootstrapping(false)); } else { setBootstrapping(false); } }, [me, meLoading, router]); const handleBusinessNameChange = (value: string) => { setBusinessName(value); if (!slugTouched) setSlug(slugify(value)); }; const handleCreateTenant = async (e: FormEvent) => { e.preventDefault(); setError(""); setLoading(true); try { const result = await api.onboarding.createTenant({ business_name: businessName, slug, business_type: businessType || undefined, }); setCtx(result); const pol = await evaluateDomainPolicy({ tenant_id: result.tenant.id, subscription_status: result.subscription_status, }); setDomainUnlocked(Boolean(pol.data.custom_domain_allowed)); setDomainReason(pol.data.reason ?? null); saveAssessmentSession({ ...loadAssessmentSession(), answers: { ...loadAssessmentSession().answers, business_type_code: businessType, }, }); setStep("branding"); } catch (err) { setError(err instanceof ApiError ? err.message : "ایجاد workspace ناموفق بود"); } finally { setLoading(false); } }; const handleBranding = async (e: FormEvent) => { e.preventDefault(); if (!ctx) return; setError(""); setLoading(true); try { const result = await api.onboarding.updateBranding(ctx.tenant.id, { primary_color: primaryColor || undefined, secondary_color: secondaryColor || undefined, logo_url: logoUrl.trim() || undefined, }); setCtx(result); setStep("domain"); } catch (err) { setError(err instanceof ApiError ? err.message : "ذخیره برندینگ ناموفق بود"); } finally { setLoading(false); } }; const handleDomain = async (e: FormEvent) => { e.preventDefault(); if (!ctx) return; setError(""); setLoading(true); try { let result = ctx; if (customDomain.trim()) { if (!domainUnlocked) { setError( domainReason || "دامنه اختصاصی بر اساس سیاست تجاری قفل است. از زیردامنه پلتفرم استفاده کنید." ); setLoading(false); return; } result = await api.onboarding.updateDomain(ctx.tenant.id, { custom_domain: customDomain.trim(), }); setCtx(result); } setStep("review"); } catch (err) { setError(err instanceof ApiError ? err.message : "ثبت دامنه ناموفق بود"); } finally { setLoading(false); } }; const handleComplete = async () => { if (!ctx) return; setError(""); setLoading(true); try { await api.onboarding.complete(ctx.tenant.id); const session = loadAssessmentSession(); if (session.activate_trial || session.selected_bundle_code) { await commercialPost("/api/v1/commercial/subscriptions", { tenant_id: ctx.tenant.id, mode: "trial", status: "trialing", bundle_code: session.selected_bundle_code || undefined, answers: session.answers, }).catch(() => null); } router.push("/dashboard"); } catch (err) { setError(err instanceof ApiError ? err.message : "تکمیل onboarding ناموفق بود"); setLoading(false); } }; if (meLoading || bootstrapping) { return (

در حال بارگذاری...

); } const selectedBundle = loadAssessmentSession().selected_bundle_code; return (

راه‌اندازی workspace

پس از تکمیل به داشبورد تجاری هدایت می‌شوید.

{error ? (

{error}

) : null}
{step === "business" ? (

{slug || "slug"}.torbatyar.ir

{selectedBundle ? (

باندل session: {selectedBundle}

) : null}
) : null} {step === "branding" && ctx ? (
) : null} {step === "domain" && ctx ? (

زیردامنه فعال:{" "} {ctx.tenant.slug}.torbatyar.ir

{domainReason || "دامنه اختصاصی قفل است — سیاست تجاری را در Billing/Admin بررسی کنید."}
} > ) : null} {step === "review" && ctx ? (

نام: {ctx.tenant.name}

slug:{" "} {ctx.tenant.slug}

) : null}
); } export default function OnboardingPage() { return (

در حال بارگذاری...

} >
); }