"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 } 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, Check } from "lucide-react"; import { useUserContext } from "@app/hooks/useUserContext"; type FormProps = { open: boolean; setOpen: (open: boolean) => void; orgId: string; onGenerated?: () => void; }; export default function GenerateLicenseKeyForm({ 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); // Personal form schema const personalFormSchema = z.object({ email: z.string().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) }); // Business form schema const businessFormSchema = z.object({ email: z.string().email(), firstName: z.string().min(1), lastName: z.string().min(1), jobTitle: z.string().min(1), primaryUse: z.string().min(1), industry: z.string().min(1), prospectiveUsers: z.coerce.number().optional(), prospectiveSites: z.coerce.number().optional(), companyName: z.string().min(1), countryOfResidence: z.string().min(1), stateProvinceRegion: z.string().min(1), postalZipCode: 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 [useCaseType, setUseCaseType] = useState( undefined ); // Personal form const personalForm = useForm({ resolver: zodResolver(personalFormSchema), defaultValues: { email: user?.email || "", firstName: "", lastName: "", primaryUse: "", country: "", phoneNumber: "", agreedToTerms: false, complianceConfirmed: false } }); // Business form const businessForm = useForm({ resolver: zodResolver(businessFormSchema), defaultValues: { email: user?.email || "", firstName: "", lastName: "", jobTitle: "", primaryUse: "", industry: "", prospectiveUsers: undefined, prospectiveSites: undefined, companyName: "", countryOfResidence: "", stateProvinceRegion: "", postalZipCode: "", companyWebsite: "", companyPhoneNumber: "", agreedToTerms: false, complianceConfirmed: false } }); // Reset form when dialog opens React.useEffect(() => { if (open) { resetForm(); setGeneratedKey(null); } }, [open]); function resetForm() { personalForm.reset({ email: user?.email || "", firstName: "", lastName: "", primaryUse: "", country: "", phoneNumber: "", agreedToTerms: false, complianceConfirmed: false }); businessForm.reset({ email: user?.email || "", firstName: "", lastName: "", jobTitle: "", primaryUse: "", industry: "", prospectiveUsers: undefined, prospectiveSites: undefined, companyName: "", countryOfResidence: "", stateProvinceRegion: "", postalZipCode: "", companyWebsite: "", companyPhoneNumber: "", agreedToTerms: false, complianceConfirmed: false }); } const useCaseOptions: StrategyOption<"personal" | "business">[] = [ { id: "personal", title: t("generateLicenseKeyForm.useCaseOptions.personal.title"), description: (

{t( "generateLicenseKeyForm.useCaseOptions.personal.description" )}

  • Home-lab enthusiasts and self-hosting hobbyists
  • Personal projects, learning, and experimentation
  • Individual developers and tech enthusiasts
) }, { id: "business", title: t("generateLicenseKeyForm.useCaseOptions.business.title"), description: (

{t( "generateLicenseKeyForm.useCaseOptions.business.description" )}

  • Companies, startups, and organizations
  • Professional services and client work
  • Revenue-generating or commercial use cases
) } ]; const submitLicenseRequest = async (payload: any) => { setLoading(true); try { 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) => { const payload = { 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 } }; await submitLicenseRequest(payload); }; const onSubmitBusiness = async (values: BusinessFormData) => { const payload = { email: values.email, useCaseType: "business", personal: undefined, business: { firstName: values.firstName, lastName: values.lastName, jobTitle: values.jobTitle, aboutYou: { primaryUse: values.primaryUse, industry: values.industry, prospectiveUsers: values.prospectiveUsers || undefined, prospectiveSites: values.prospectiveSites || undefined }, companyInfo: { companyName: values.companyName, countryOfResidence: values.countryOfResidence, stateProvinceRegion: values.stateProvinceRegion, postalZipCode: values.postalZipCode, 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("generateLicenseKey")} {t( "generateLicenseKeyForm.steps.emailLicenseType.description" )}
{generatedKey ? (
{useCaseType === "business" && ( {t( "generateLicenseKeyForm.alerts.trialPeriodInformation.title" )} {t( "generateLicenseKeyForm.alerts.trialPeriodInformation.description" )} )}
) : ( <> {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 )} ))}
{ setUseCaseType(value); resetForm(); }} cols={2} />
{useCaseType === "personal" && (
( {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" )}{" "} See license details:{" "} https://pangolin.net/fcl.html
)} />
)} {useCaseType === "business" && (
( {t( "generateLicenseKeyForm.form.firstName" )} )} /> ( {t( "generateLicenseKeyForm.form.lastName" )} )} />
( {t( "generateLicenseKeyForm.form.jobTitle" )} )} /> ( {t( "generateLicenseKeyForm.form.primaryUseQuestion" )} )} /> ( {t( "generateLicenseKeyForm.form.industryQuestion" )} )} />
( {t( "generateLicenseKeyForm.form.prospectiveUsersQuestion" )} )} /> ( {t( "generateLicenseKeyForm.form.prospectiveSitesQuestion" )} )} />
( {t( "generateLicenseKeyForm.form.companyName" )} )} /> ( {t( "generateLicenseKeyForm.form.countryOfResidence" )} )} />
( {t( "generateLicenseKeyForm.form.stateProvinceRegion" )} )} /> ( {t( "generateLicenseKeyForm.form.postalZipCode" )} )} />
( {t( "generateLicenseKeyForm.form.companyWebsite" )} )} /> ( {t( "generateLicenseKeyForm.form.companyPhoneNumber" )} )} />
(
{t( "signUpTerms.IAgreeToThe" )}{" "} {t( "signUpTerms.termsOfService" )}{" "} {t( "signUpTerms.and" )}{" "} {t( "signUpTerms.privacyPolicy" )}
)} /> (
{t( "generateLicenseKeyForm.form.complianceConfirmation" )}{" "} See license details:{" "} https://pangolin.net/fcl.html
)} />
)} )}
{!generatedKey && useCaseType === "personal" && ( )} {!generatedKey && useCaseType === "business" && ( )}
); }