"use client"; import { useState, useEffect } from "react"; import { LicenseKeyCache } from "@server/license/license"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/api"; import { LicenseKeysDataTable } from "../../../components/LicenseKeysDataTable"; import { AxiosResponse } from "axios"; 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 { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { Credenza, CredenzaBody, CredenzaClose, CredenzaContent, CredenzaDescription, CredenzaFooter, CredenzaHeader, CredenzaTitle } from "@app/components/Credenza"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { SettingsContainer, SettingsSectionTitle as SSTitle, SettingsSection, SettingsSectionDescription, SettingsSectionGrid, SettingsSectionHeader, SettingsSectionFooter } from "@app/components/Settings"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { Check, Heart, InfoIcon } from "lucide-react"; import CopyTextBox from "@app/components/CopyTextBox"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; import { SitePriceCalculator } from "../../../components/SitePriceCalculator"; import { Checkbox } from "@app/components/ui/checkbox"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; import { useTranslations } from "next-intl"; function obfuscateLicenseKey(key: string): string { if (key.length <= 8) return key; const firstPart = key.substring(0, 4); const lastPart = key.substring(key.length - 4); return `${firstPart}••••••••••••••••••••${lastPart}`; } export default function LicensePage() { const api = createApiClient(useEnvContext()); const [rows, setRows] = useState([]); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [selectedLicenseKey, setSelectedLicenseKey] = useState(null); const { licenseStatus, updateLicenseStatus } = useLicenseStatusContext(); const [hostLicense, setHostLicense] = useState(null); const [isPurchaseModalOpen, setIsPurchaseModalOpen] = useState(false); const [purchaseMode, setPurchaseMode] = useState<"license">("license"); // Separate loading states for different actions const [isInitialLoading, setIsInitialLoading] = useState(true); const [isActivatingLicense, setIsActivatingLicense] = useState(false); const [isDeletingLicense, setIsDeletingLicense] = useState(false); const [isRecheckingLicense, setIsRecheckingLicense] = useState(false); const { supporterStatus } = useSupporterStatusContext(); const t = useTranslations(); const formSchema = z.object({ licenseKey: z .string() .nonempty({ message: t("licenseKeyRequired") }) .max(255), agreeToTerms: z.boolean().refine((val) => val === true, { message: t("licenseTermsAgree") }) }); const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { licenseKey: "", agreeToTerms: false } }); useEffect(() => { async function load() { setIsInitialLoading(true); await loadLicenseKeys(); setIsInitialLoading(false); } load(); }, []); async function loadLicenseKeys() { try { const response = await api.get>( "/license/keys" ); const keys = response.data.data; setRows(keys); const hostKey = keys.find((key) => key.type === "host"); if (hostKey) { setHostLicense(hostKey.licenseKey); } else { setHostLicense(null); } } catch (e) { toast({ title: t("licenseErrorKeyLoad"), description: formatAxiosError( e, t("licenseErrorKeyLoadDescription") ) }); } } async function deleteLicenseKey(key: string) { try { setIsDeletingLicense(true); const encodedKey = encodeURIComponent(key); const res = await api.delete(`/license/${encodedKey}`); if (res.data.data) { updateLicenseStatus(res.data.data); } await loadLicenseKeys(); toast({ title: t("licenseKeyDeleted"), description: t("licenseKeyDeletedDescription") }); setIsDeleteModalOpen(false); } catch (e) { toast({ title: t("licenseErrorKeyDelete"), description: formatAxiosError( e, t("licenseErrorKeyDeleteDescription") ) }); } finally { setIsDeletingLicense(false); } } async function recheck() { try { setIsRecheckingLicense(true); const res = await api.post(`/license/recheck`); if (res.data.data) { updateLicenseStatus(res.data.data); } await loadLicenseKeys(); toast({ title: t("licenseErrorKeyRechecked"), description: t("licenseErrorKeyRecheckedDescription") }); } catch (e) { toast({ title: t("licenseErrorKeyRecheck"), description: formatAxiosError( e, t("licenseErrorKeyRecheckDescription") ) }); } finally { setIsRecheckingLicense(false); } } async function onSubmit(values: z.infer) { try { setIsActivatingLicense(true); const res = await api.post("/license/activate", { licenseKey: values.licenseKey }); if (res.data.data) { updateLicenseStatus(res.data.data); } toast({ title: t("licenseKeyActivated"), description: t("licenseKeyActivatedDescription") }); setIsCreateModalOpen(false); form.reset(); await loadLicenseKeys(); } catch (e) { toast({ variant: "destructive", title: t("licenseErrorKeyActivate"), description: formatAxiosError( e, t("licenseErrorKeyActivateDescription") ) }); } finally { setIsActivatingLicense(false); } } if (isInitialLoading) { return null; } return ( <> { setIsPurchaseModalOpen(val); }} mode={purchaseMode} /> { setIsCreateModalOpen(val); form.reset(); }} > {t("licenseActivateKey")} {t("licenseActivateKeyDescription")}
( {t("licenseKey")} )} /> (
{t("licenseAgreement")}
)} />
{selectedLicenseKey && ( { setIsDeleteModalOpen(val); setSelectedLicenseKey(null); }} dialog={

{t("licenseQuestionRemove")}

{t("licenseMessageRemove")}

} buttonText={t("licenseKeyDeleteConfirm")} onConfirm={async () => deleteLicenseKey(selectedLicenseKey.licenseKeyEncrypted) } string={selectedLicenseKey.licenseKey} title={t("licenseKeyDelete")} /> )} {/* */} {/* */} {/* */} {/* {t("licenseAbout")} */} {/* */} {/* */} {/* {t("licenseAboutDescription")} */} {/* */} {/* */} {t("licenseHost")} {t("licenseHostDescription")}
{licenseStatus?.isLicenseValid ? (
{t("licensed")}
) : (
{t("unlicensed")}
)}
{licenseStatus?.hostId && (
{t("hostId")}
)} {hostLicense && (
{t("licenseKey")}
)}
{ setSelectedLicenseKey(key); setIsDeleteModalOpen(true); }} onCreate={() => setIsCreateModalOpen(true)} />
); }