"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { Badge, Button, Container } from "@/components/ui"; import { useAuth } from "@/hooks/useAuth"; import { loadAssessmentSchema, loadCommercialBundles, loadCommercialPricing, loadCommercialProducts, requestRecommendations, } from "../adapters"; import type { AdapterResult, AssessmentQuestion, AssessmentSchema, CommercialBundle, CommercialPricingItem, CommercialProduct, RecommendationResult, } from "../types"; import { BundleCard, CommercialEmptyState, ProductCard, } from "../components"; import { loadAssessmentSession, saveAssessmentSession, type AssessmentSession, } from "../session"; type WizardStep = | "assessment" | "recommendations" | "bundles" | "pricing" | "trial_summary" | "create_workspace"; function QuestionField({ question, value, onChange, }: { question: AssessmentQuestion; value: unknown; onChange: (v: unknown) => void; }) { const inputClass = "mt-1 w-full rounded-xl border border-gray-200 bg-white px-4 py-2.5 text-sm focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary dark:border-gray-700 dark:bg-gray-900"; if (question.input_type === "boolean") { return ( ); } if (question.input_type === "number") { return ( ); } if ((question.input_type === "multi" || question.input_type === "single") && question.options?.length) { const multi = question.input_type === "multi"; const selected = multi ? (Array.isArray(value) ? (value as string[]) : []) : null; return (
{question.prompt}
{question.options.map((opt) => { const on = multi ? selected!.includes(opt.value) : value === opt.value; return ( ); })}
); } return ( ); } const STEPS: { id: WizardStep; label: string }[] = [ { id: "assessment", label: "کشف" }, { id: "recommendations", label: "پیشنهاد" }, { id: "bundles", label: "باندل" }, { id: "pricing", label: "قیمت" }, { id: "trial_summary", label: "Trial" }, ]; export function CommercialDiscoverExperience() { const { isAuthenticated, login, loading: authLoading } = useAuth(); const router = useRouter(); const searchParams = useSearchParams(); const typeParam = searchParams.get("type"); const [step, setStep] = useState("assessment"); const [schemaRes, setSchemaRes] = useState | null>(null); const [productsRes, setProductsRes] = useState | null>(null); const [bundlesRes, setBundlesRes] = useState | null>(null); const [pricingRes, setPricingRes] = useState | null>(null); const [recoRes, setRecoRes] = useState | null>(null); const [answers, setAnswers] = useState>({}); const [selectedBundle, setSelectedBundle] = useState(null); const [qIndex, setQIndex] = useState(0); const [busy, setBusy] = useState(false); const [booted, setBooted] = useState(false); useEffect(() => { const session = loadAssessmentSession(); const nextAnswers = { ...session.answers }; if (typeParam && !nextAnswers.business_type_code) { nextAnswers.business_type_code = typeParam; } setAnswers(nextAnswers); if (session.selected_bundle_code) setSelectedBundle(session.selected_bundle_code); void (async () => { setBusy(true); const [schema, products, bundles, pricing] = await Promise.all([ loadAssessmentSchema(), loadCommercialProducts(), loadCommercialBundles(), loadCommercialPricing(), ]); setSchemaRes(schema); setProductsRes(products); setBundlesRes(bundles); setPricingRes(pricing); setBusy(false); setBooted(true); })(); }, [typeParam]); const persist = useCallback((next: AssessmentSession) => { saveAssessmentSession(next); }, []); const questions = schemaRes?.data.questions ?? []; const currentQ = questions[qIndex]; const runRecommendations = async () => { setBusy(true); persist({ answers, selected_bundle_code: selectedBundle }); const reco = await requestRecommendations(answers); setRecoRes(reco); const top = reco.data.recommended_bundles[0]?.code; if (top && !selectedBundle) { setSelectedBundle(top); persist({ answers, selected_bundle_code: top }); } setStep("recommendations"); setBusy(false); }; const productByCode = useMemo(() => { const map = new Map(); (productsRes?.data ?? []).forEach((p) => map.set(p.product_code, p)); return map; }, [productsRes]); const bundleByCode = useMemo(() => { const map = new Map(); (bundlesRes?.data ?? []).forEach((b) => map.set(b.bundle_code, b)); return map; }, [bundlesRes]); const recommendedProducts = (recoRes?.data.recommended_products ?? []) .map((r) => ({ ...r, product: productByCode.get(r.code) })) .filter((r) => r.product); const optionalProducts = (productsRes?.data ?? []).filter( (p) => !recommendedProducts.some((r) => r.code === p.product_code) && p.visibility !== "hidden" ); const recommendedBundles = (recoRes?.data.recommended_bundles ?? []) .map((r) => ({ ...r, bundle: bundleByCode.get(r.code) })) .filter((r) => r.bundle); const displayBundles = recommendedBundles.length > 0 ? recommendedBundles.map((r) => r.bundle!) : bundlesRes?.data ?? []; const selectedBundleObj = selectedBundle ? bundleByCode.get(selectedBundle) : null; const pricingForBundle = (pricingRes?.data ?? []).filter((p) => selectedBundleObj?.pricing_refs?.includes(p.pricing_item_code) ); const goWorkspace = () => { persist({ answers, selected_bundle_code: selectedBundle, selected_product_codes: recoRes?.data.recommended_products.map((p) => p.code), activate_trial: true, }); if (isAuthenticated) { const intent = typeof answers.business_type_code === "string" ? answers.business_type_code : ""; router.push(intent ? `/onboarding?intent=${encodeURIComponent(intent)}` : "/onboarding"); } else { setStep("create_workspace"); } }; const stepIndex = STEPS.findIndex((s) => s.id === step); if (!booted) { return (

آماده‌سازی مسیر کشف…

); } return (
← بازگشت به خانه

کشف و راه‌اندازی

    {STEPS.map((s, i) => (
  1. {s.label}
  2. ))}
{step === "assessment" ? (

ارزیابی نیازها

سؤالات از assessment schema — صنایع و نیازها بدون لیست ثابت frontend.

{schemaRes?.availability === "unavailable" || schemaRes?.availability === "empty" || !questions.length ? (
) : currentQ ? (

سؤال {qIndex + 1} از {questions.length}

{ const next = { ...answers, [currentQ.question_id]: v }; setAnswers(next); persist({ answers: next, selected_bundle_code: selectedBundle }); }} />
{qIndex < questions.length - 1 ? ( ) : ( )}
) : null}
) : null} {step === "recommendations" ? (

پیشنهاد شخصی‌سازی‌شده

محصولات الزامی، اختیاری و مسیر رشد از recommendation + registry.

{recoRes?.message ? (

{recoRes.message}

) : null} {recoRes?.data.rule_trace?.length ? (
چرا این پیشنهاد؟
    {recoRes.data.rule_trace.map((step, i) => (
  1. {step}
  2. ))}
) : null}

محصولات پیشنهادی / الزامی

{recommendedProducts.length ? (
{recommendedProducts.map((r) => (
{r.reason ? (

{r.reason}

) : null}
))}
) : ( )}
{optionalProducts.length ? (

محصولات اختیاری / قابل گسترش

{optionalProducts.slice(0, 6).map((p) => ( ))}
) : null} {selectedBundleObj ? (

باندل پیشنهادی فعلی

{selectedBundleObj.display_name}

{selectedBundleObj.upgrade_path_code ? (

مسیر رشد: {selectedBundleObj.upgrade_path_code}

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

مقایسه باندل

Starter / Professional / Enterprise از metadata باندل — بدون پلن ثابت.

{displayBundles.length ? (
{displayBundles.map((b) => ( r.code === b.bundle_code)?.score } selected={selectedBundle === b.bundle_code} onSelect={() => { setSelectedBundle(b.bundle_code); persist({ answers, selected_bundle_code: b.bundle_code }); }} /> ))}
) : ( )}
) : null} {step === "pricing" ? (

مقایسه قیمت

آیتم‌های قیمت از رجیستری؛ تخفیف و پروموشن از discount_refs / metadata.

{(pricingForBundle.length ? pricingForBundle : pricingRes?.data ?? []).length ? (
    {(pricingForBundle.length ? pricingForBundle : pricingRes!.data).map((item) => (
  • {item.display_name}

    {item.pricing_model || "—"} ·{" "} {item.amount_minor != null ? `${item.amount_minor} ${item.currency || ""}` : "مبلغ از بک‌اند"}

    {item.trial_days != null ? ( Trial {item.trial_days} روز ) : null}
  • ))}
) : ( )}
) : null} {step === "trial_summary" ? (

خلاصه Trial

پس از ثبت‌نام و ساخت workspace، trial از Billing / commercial subscriptions فعال می‌شود — بدون اعداد ساختگی در این صفحه.

{selectedBundle ? (

باندل: {selectedBundle}

) : (

باندل انتخاب نشده — می‌توانید بعداً در Billing انتخاب کنید.

)}
  • ✓ ساخت tenant و زیردامنه
  • ✓ داشبورد تجاری و لانچر اپ‌ها
  • ✓ دامنه اختصاصی پس از entitlement
) : null} {step === "create_workspace" ? (

ثبت‌نام / ورود

انتخاب‌های ارزیابی در session می‌ماند و پس از ورود به onboarding منتقل می‌شود.

) : null}
); }