"use client"; import { Button } from "@app/components/ui/button"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@app/components/ui/form"; import { Input } from "@app/components/ui/input"; import { Checkbox } from "@app/components/ui/checkbox"; import { toast } from "@app/hooks/useToast"; import { zodResolver } from "@hookform/resolvers/zod"; import { AxiosResponse } from "axios"; import { useState } from "react"; import { useForm, type Resolver } from "react-hook-form"; import { z } from "zod"; import CopyTextBox from "@app/components/CopyTextBox"; import { Credenza, CredenzaBody, CredenzaClose, CredenzaContent, CredenzaDescription, CredenzaFooter, CredenzaHeader, CredenzaTitle } from "@app/components/Credenza"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { GenerateNewLicenseResponse } from "@server/routers/generatedLicense/types"; import { useTranslations } from "next-intl"; import React from "react"; import { StrategySelect, StrategyOption } from "./StrategySelect"; import { Alert, AlertDescription, AlertTitle } from "./ui/alert"; import { InfoIcon } from "lucide-react"; import { useUserContext } from "@app/hooks/useUserContext"; const TIER_TO_LICENSE_ID = { starter: "small_license", scale: "big_license" } as const; type FormProps = { open: boolean; setOpen: (open: boolean) => void; orgId: string; onGenerated?: () => void; }; export default function NewPricingLicenseForm({ open, setOpen, orgId, onGenerated }: FormProps) { const t = useTranslations(); const { env } = useEnvContext(); const api = createApiClient({ env }); const { user } = useUserContext(); const [loading, setLoading] = useState(false); const [generatedKey, setGeneratedKey] = useState(null); const [personalUseOnly, setPersonalUseOnly] = useState(false); const [selectedTier, setSelectedTier] = useState<"starter" | "scale">( "starter" ); const personalFormSchema = z.object({ email: z.email(), firstName: z.string().min(1), lastName: z.string().min(1), primaryUse: z.string().min(1), country: z.string().min(1), phoneNumber: z.string().optional(), agreedToTerms: z.boolean().refine((val) => val === true), complianceConfirmed: z.boolean().refine((val) => val === true) }); const businessFormSchema = z.object({ email: z.email(), firstName: z.string().min(1), lastName: z.string().min(1), primaryUse: z.string().min(1), industry: z.string().min(1), companyName: z.string().min(1), companyWebsite: z.string().optional(), companyPhoneNumber: z.string().optional(), agreedToTerms: z.boolean().refine((val) => val === true), complianceConfirmed: z.boolean().refine((val) => val === true) }); type PersonalFormData = z.infer; type BusinessFormData = z.infer; const personalForm = useForm({ resolver: zodResolver(personalFormSchema) as Resolver, defaultValues: { email: user?.email || "", firstName: "", lastName: "", primaryUse: "", country: "", phoneNumber: "", agreedToTerms: false, complianceConfirmed: false } }); const businessForm = useForm({ resolver: zodResolver(businessFormSchema) as Resolver, defaultValues: { email: user?.email || "", firstName: "", lastName: "", primaryUse: "", industry: "", companyName: "", companyWebsite: "", companyPhoneNumber: "", agreedToTerms: false, complianceConfirmed: false } }); React.useEffect(() => { if (open) { resetForm(); setGeneratedKey(null); setPersonalUseOnly(false); setSelectedTier("starter"); } }, [open]); function resetForm() { personalForm.reset({ email: user?.email || "", firstName: "", lastName: "", primaryUse: "", country: "", phoneNumber: "", agreedToTerms: false, complianceConfirmed: false }); businessForm.reset({ email: user?.email || "", firstName: "", lastName: "", primaryUse: "", industry: "", companyName: "", companyWebsite: "", companyPhoneNumber: "", agreedToTerms: false, complianceConfirmed: false }); } const tierOptions: StrategyOption<"starter" | "scale">[] = [ { id: "starter", title: t("newPricingLicenseForm.tiers.starter.title"), description: t("newPricingLicenseForm.tiers.starter.description") }, { id: "scale", title: t("newPricingLicenseForm.tiers.scale.title"), description: t("newPricingLicenseForm.tiers.scale.description") } ]; const submitLicenseRequest = async ( payload: Record ): Promise => { setLoading(true); try { // Check if this is a business/enterprise license request if (!personalUseOnly) { const response = await api.put>( `/org/${orgId}/license/enterprise`, { ...payload, tier: TIER_TO_LICENSE_ID[selectedTier] } ); console.log("Checkout session response:", response.data); const checkoutUrl = response.data.data; if (checkoutUrl) { window.location.href = checkoutUrl; } else { toast({ title: "Failed to get checkout URL", description: "Please try again later", variant: "destructive" }); setLoading(false); } } else { // Personal license flow const response = await api.put< AxiosResponse >(`/org/${orgId}/license`, payload); if (response.data.data?.licenseKey?.licenseKey) { setGeneratedKey(response.data.data.licenseKey.licenseKey); onGenerated?.(); toast({ title: t("generateLicenseKeyForm.toasts.success.title"), description: t( "generateLicenseKeyForm.toasts.success.description" ), variant: "default" }); } } } catch (e) { console.error(e); toast({ title: t("generateLicenseKeyForm.toasts.error.title"), description: formatAxiosError( e, t("generateLicenseKeyForm.toasts.error.description") ), variant: "destructive" }); } setLoading(false); }; const onSubmitPersonal = async (values: PersonalFormData) => { await submitLicenseRequest({ email: values.email, useCaseType: "personal", personal: { firstName: values.firstName, lastName: values.lastName, aboutYou: { primaryUse: values.primaryUse }, personalInfo: { country: values.country, phoneNumber: values.phoneNumber || "" } }, business: undefined, consent: { agreedToTerms: values.agreedToTerms, acknowledgedPrivacyPolicy: values.agreedToTerms, complianceConfirmed: values.complianceConfirmed } }); }; const onSubmitBusiness = async (values: BusinessFormData) => { const payload = { email: values.email, useCaseType: "business", personal: undefined, business: { firstName: values.firstName, lastName: values.lastName, jobTitle: "N/A", aboutYou: { primaryUse: values.primaryUse, industry: values.industry, prospectiveUsers: 100, prospectiveSites: 100 }, companyInfo: { companyName: values.companyName, countryOfResidence: "N/A", stateProvinceRegion: "N/A", postalZipCode: "N/A", companyWebsite: values.companyWebsite || "", companyPhoneNumber: values.companyPhoneNumber || "" } }, consent: { agreedToTerms: values.agreedToTerms, acknowledgedPrivacyPolicy: values.agreedToTerms, complianceConfirmed: values.complianceConfirmed } }; await submitLicenseRequest(payload); }; const handleClose = () => { setOpen(false); setGeneratedKey(null); resetForm(); }; return ( {t("newPricingLicenseForm.title")} {t("newPricingLicenseForm.description")}
{generatedKey ? (
) : ( <> {/* Tier selection - required when not personal use */} {!personalUseOnly && (
setSelectedTier(value) } cols={2} /> {t( "newPricingLicenseForm.viewPricingLink" )}
)} {/* Personal use only checkbox at the bottom of options */}
{ setPersonalUseOnly( checked === true ); if (checked) { businessForm.reset(); } else { personalForm.reset(); } }} />
{/* License disclosure - only when personal use */} {personalUseOnly && ( {t( "generateLicenseKeyForm.alerts.commercialUseDisclosure.title" )} {t( "generateLicenseKeyForm.alerts.commercialUseDisclosure.description" ) .split( "Fossorial Commercial License Terms" ) .map((part, index) => ( {part} {index === 0 && ( Fossorial Commercial License Terms )} ))} )} {/* Personal form: only when personal use only is checked */} {personalUseOnly && (
( {t( "generateLicenseKeyForm.form.firstName" )} )} /> ( {t( "generateLicenseKeyForm.form.lastName" )} )} />
( {t( "generateLicenseKeyForm.form.primaryUseQuestion" )} )} />
( {t( "generateLicenseKeyForm.form.country" )} )} /> ( {t( "generateLicenseKeyForm.form.phoneNumberOptional" )} )} />
(
{t( "signUpTerms.IAgreeToThe" )}{" "} {t( "signUpTerms.termsOfService" )}{" "} {t( "signUpTerms.and" )}{" "} {t( "signUpTerms.privacyPolicy" )}
)} /> (
{t( "generateLicenseKeyForm.form.complianceConfirmation" )}{" "} https://pangolin.net/fcl.html
)} />
)} {/* Business form: when not personal use - enter business info then continue to checkout */} {!personalUseOnly && (
( {t( "generateLicenseKeyForm.form.firstName" )} )} /> ( {t( "generateLicenseKeyForm.form.lastName" )} )} />
( {t( "generateLicenseKeyForm.form.primaryUseQuestion" )} )} /> ( {t( "generateLicenseKeyForm.form.industryQuestion" )} )} /> ( {t( "generateLicenseKeyForm.form.companyName" )} )} />
( {t( "generateLicenseKeyForm.form.companyWebsite" )} )} /> ( {t( "generateLicenseKeyForm.form.companyPhoneNumber" )} )} />
(
{t( "signUpTerms.IAgreeToThe" )}{" "} {t( "signUpTerms.termsOfService" )}{" "} {t( "signUpTerms.and" )}{" "} {t( "signUpTerms.privacyPolicy" )}
)} /> (
{t( "generateLicenseKeyForm.form.complianceConfirmation" )}{" "} https://pangolin.net/fcl.html
)} />
)} )}
{!generatedKey && personalUseOnly && ( )} {!generatedKey && !personalUseOnly && ( )}
); }