"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { ArrowLeft, Copy, Key, Loader, Save, Trash2 } from "lucide-react"; import { useParams, useRouter } from "next/navigation"; import { useEffect, useMemo, useState } from "react"; // Import useEffect import { useForm } from "react-hook-form"; import * as z from "zod"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { apiClient } from "@/lib/api-client"; import { KeygenGroup, KeygenLicense, KeygenPolicy, KeygenProduct, KeygenUser, } from "@/lib/types"; import { parseMetadata } from "@/lib/utils"; import { toast } from "sonner"; const licenseSchema = z.object({ name: z.string().min(1, "License name is required"), user: z.string().optional(), policy: z.string(), expiry: z.string().min(1, "Expiry date is required"), group: z.string().optional(), product: z.string().optional(), metadata: z.string().optional(), }); type LicenseFormData = z.infer; export default function LicenseDetailPage() { const params = useParams(); const router = useRouter(); const queryClient = useQueryClient(); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const licenseId = params.id as string; const isNew = licenseId === "new"; const { data: license, isLoading: isLicenseLoading, isFetched: isLicenseFetched, } = useQuery({ queryKey: ["license", licenseId], queryFn: async () => await apiClient.getLicense(licenseId), enabled: !isNew, }); const { data: policies, isLoading: policiesLoading } = useQuery({ queryKey: ["policies"], queryFn: async () => await apiClient.getPolicies(1, 100), staleTime: 1000, }); const { data: users, isLoading: usersLoading } = useQuery({ queryKey: ["users"], queryFn: async () => await apiClient.getUsers(1, 100), }); const { data: products, isLoading: productsLoading } = useQuery({ queryKey: ["products"], queryFn: async () => await apiClient.getProducts(1, 100), }); const { data: groups, isLoading: groupsLoading } = useQuery({ queryKey: ["groups"], queryFn: async () => await apiClient.getGroups(1, 100), }); const createMutation = useMutation({ mutationFn: async (licenseData: Partial) => await apiClient.createLicense({ attributes: licenseData.attributes, relationships: licenseData.relationships, }), onSuccess: async (data) => { await queryClient.invalidateQueries({ queryKey: ["licenses"] }); toast.success("License created successfully"); router.push(`/dashboard/licenses/${data.data.id}`); }, onError: (error) => { toast.error(error.message || "Failed to create license"); }, }); const updateMutation = useMutation({ mutationFn: async (licenseData: Partial) => await apiClient.updateLicense(licenseId, { attributes: licenseData.attributes, relationships: licenseData.relationships, }), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["licenses"] }); await queryClient.invalidateQueries({ queryKey: ["license", licenseId] }); toast.success("License updated successfully"); }, onError: (error) => { toast.error(error.message || "Failed to update license"); }, }); const deleteMutation = useMutation({ mutationFn: () => apiClient.deleteLicense(licenseId), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["licenses"] }); toast.success("License deleted successfully"); router.push("/dashboard/licenses"); }, onError: (error) => { toast.error(error.message || "Failed to delete license"); }, }); // Function to create relationships object const getRelationships = (data: LicenseFormData) => ({ ...(data.user && { owner: { data: { type: "users", id: data.user }, }, }), ...(data.policy && { policy: { data: { type: "policies", id: data.policy }, }, }), ...(data.product && { product: { data: { type: "products", id: data.product }, }, }), ...(data.group && { group: { data: { type: "groups", id: data.group }, }, }), }); const onSubmit = async (data: LicenseFormData) => { const formattedExpiry = new Date(data.expiry).toISOString(); const licenseData = { attributes: { name: data.name, expiry: formattedExpiry, metadata: parseMetadata(data.metadata || ""), }, relationships: isNew ? getRelationships(data) : undefined, }; if (isNew) { await createMutation.mutateAsync(licenseData as Partial); } else { await updateMutation.mutateAsync(licenseData as Partial); } }; // Memoized default values const defaultValues = useMemo(() => { if (isNew || !license?.data) { return { name: "", user: undefined, policy: undefined, expiry: "", group: undefined, product: undefined, }; } if (!license) return undefined; const expiry = license.data.attributes.expiry ? new Date(license.data.attributes.expiry).toISOString().slice(0, 16) : ""; console.log({license}) return { name: license.data.attributes.name ?? "", user: license.data.relationships?.owner?.data?.id, policy: license.data.relationships?.policy?.data?.id, expiry, group: license.data.relationships?.group?.data?.id, product: license.data.relationships?.product?.data?.id, metadata: license.data.attributes.metadata ? JSON.stringify(license.data.attributes.metadata) : undefined, }; }, [isNew, license]); const form = useForm({ resolver: zodResolver(licenseSchema), mode: "onBlur", defaultValues, }); useEffect(() => { if ( !isNew && isLicenseFetched && license?.data && !form.formState.isDirty ) { form.reset(defaultValues); } else if (isNew && !form.formState.isDirty) { form.reset(defaultValues); } }, [form, isNew, isLicenseFetched, license?.data, defaultValues]); const handleDelete = () => { deleteMutation.mutate(); setIsDeleteDialogOpen(false); }; const copyLicenseKey = (key: string) => { navigator.clipboard.writeText(key); toast("License key copied to clipboard"); }; const allLoading = isLicenseLoading || policiesLoading || usersLoading || productsLoading || groupsLoading; if (allLoading || !defaultValues) { return (

Loading license details...

); } if (!isNew && !license?.data) { return (

License not found.

); } console.log({ defaultValues }); return ( <>

{isNew ? "Create License" : "Edit License"}

{isNew ? "Generate a new license" : "Update license information and settings"}

{!isNew && ( )}
{!isNew && license && ( License Key This is the generated license key for this license
{/* It's safer to only show the key if it exists */} {license.data.attributes.key ? "*********************************************************************************" : "Not available"} {license.data.attributes.key && ( )}
{license.data.attributes.status} Uses: {license.data.attributes.uses}
)} License Information {isNew ? "Configure the new license settings" : "Update the license information below"}
( License Name )} /> ( Expiry Date )} /> {/* USER SELECT */} ( Assign to User )} /> {/* POLICY SELECT */} ( Policy )} /> {/* GROUP SELECT */} ( Assign to Group (Optional) )} /> {/* PRODUCT SELECT */} ( Product (Optional) )} /> ( Metadata