"use client"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { ArrowLeft, Save, Trash2, User } from "lucide-react"; import { useParams, useRouter } from "next/navigation"; import { useMemo, useState } from "react"; import { useForm } from "react-hook-form"; import PolicyForm from "@/components/molecules/policy-form"; 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 } from "@/components/ui/form"; import { apiClient } from "@/lib/api-client"; import { KeygenPolicy, PolicyFormData } from "@/lib/types"; import { parseMetadata } from "@/lib/utils"; import { toast } from "sonner"; const defaultPolicy: PolicyFormData = { name: "", productId: undefined, duration: undefined, strict: true, floating: true, scheme: "ED25519_SIGN", requireProductScope: false, requirePolicyScope: false, requireMachineScope: false, requireFingerprintScope: false, requireComponentsScope: false, requireUserScope: false, requireChecksumScope: false, requireVersionScope: false, requireCheckIn: false, checkInInterval: undefined, checkInIntervalCount: undefined, usePool: false, maxMachines: undefined, maxProcesses: undefined, maxUsers: undefined, maxCores: undefined, maxMemory: undefined, maxDisk: undefined, maxUses: undefined, encrypted: false, protected: true, requireHeartbeat: false, heartbeatDuration: undefined, heartbeatCullStrategy: "DEACTIVATE_DEAD", heartbeatResurrectionStrategy: "NO_REVIVE", heartbeatBasis: "FROM_FIRST_PING", machineUniquenessStrategy: "UNIQUE_PER_LICENSE", machineMatchingStrategy: "MATCH_ALL", componentUniquenessStrategy: "UNIQUE_PER_MACHINE", componentMatchingStrategy: "MATCH_ALL", expirationStrategy: "RESTRICT_ACCESS", expirationBasis: "FROM_CREATION", renewalBasis: "FROM_EXPIRY", transferStrategy: "KEEP_EXPIRY", authenticationStrategy: "LICENSE", machineLeasingStrategy: "PER_LICENSE", processLeasingStrategy: "PER_MACHINE", overageStrategy: "NO_OVERAGE", metadata: undefined, } export default function PolicyDetailPage() { const params = useParams(); const router = useRouter(); const queryClient = useQueryClient(); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const { data: products, isLoading: productsLoading } = useQuery({ queryKey: ["products"], queryFn: async () => await apiClient.getProducts(1, 100), staleTime: 5 * 1000, }); const policyId = params.id as string; const isNew = policyId === "new"; const { data: policy, isLoading } = useQuery({ queryKey: ["policy", policyId], queryFn: async () => await apiClient.getPolicy(policyId), enabled: !isNew, }); const defaultValues: PolicyFormData | undefined = useMemo(() => { if (isNew) { const data: PolicyFormData = { name: "", productId: undefined, duration: undefined, strict: true, floating: true, scheme: "ED25519_SIGN", requireProductScope: false, requirePolicyScope: false, requireMachineScope: false, requireFingerprintScope: false, requireComponentsScope: false, requireUserScope: false, requireChecksumScope: false, requireVersionScope: false, requireCheckIn: false, checkInInterval: undefined, checkInIntervalCount: undefined, usePool: false, maxMachines: undefined, maxProcesses: undefined, maxUsers: undefined, maxCores: undefined, maxMemory: undefined, maxDisk: undefined, maxUses: undefined, encrypted: true, protected: true, requireHeartbeat: false, heartbeatDuration: undefined, heartbeatCullStrategy: "DEACTIVATE_DEAD", heartbeatResurrectionStrategy: "NO_REVIVE", heartbeatBasis: "FROM_FIRST_PING", machineUniquenessStrategy: "UNIQUE_PER_LICENSE", machineMatchingStrategy: "MATCH_ALL", componentUniquenessStrategy: "UNIQUE_PER_MACHINE", componentMatchingStrategy: "MATCH_ALL", expirationStrategy: "RESTRICT_ACCESS", expirationBasis: "FROM_CREATION", renewalBasis: "FROM_EXPIRY", transferStrategy: "KEEP_EXPIRY", authenticationStrategy: "LICENSE", machineLeasingStrategy: "PER_LICENSE", processLeasingStrategy: "PER_MACHINE", overageStrategy: "NO_OVERAGE", metadata: undefined, }; return data; } if (!policy) return undefined; const attributes = policy?.data.attributes; const data: PolicyFormData = { name: attributes.name, productId: policy.data.relationships?.product?.data.id, duration: attributes.duration, strict: attributes.strict, floating: attributes.floating, scheme: attributes.scheme as PolicyFormData["scheme"], requireProductScope: attributes.requireProductScope, requirePolicyScope: attributes.requirePolicyScope, requireMachineScope: attributes.requireMachineScope, requireFingerprintScope: attributes.requireFingerprintScope, requireComponentsScope: attributes.requireComponentsScope, requireUserScope: attributes.requireUserScope, requireChecksumScope: attributes.requireChecksumScope, requireVersionScope: attributes.requireVersionScope, requireCheckIn: attributes.requireCheckIn, checkInInterval: attributes.checkInInterval, checkInIntervalCount: attributes.checkInIntervalCount, usePool: attributes.usePool, maxMachines: attributes.maxMachines, maxProcesses: attributes.maxProcesses, maxUsers: attributes.maxUsers, maxCores: attributes.maxCores, maxMemory: attributes.maxMemory, maxDisk: attributes.maxDisk, maxUses: attributes.maxUses, encrypted: attributes.encrypted, protected: attributes.protected, requireHeartbeat: attributes.requireHeartbeat, heartbeatDuration: attributes.heartbeatDuration, heartbeatCullStrategy: attributes.heartbeatCullStrategy, heartbeatResurrectionStrategy: attributes.heartbeatResurrectionStrategy, heartbeatBasis: attributes.heartbeatBasis, machineUniquenessStrategy: attributes.machineUniquenessStrategy, machineMatchingStrategy: attributes.machineMatchingStrategy, componentUniquenessStrategy: attributes.componentUniquenessStrategy, componentMatchingStrategy: attributes.componentMatchingStrategy, expirationStrategy: attributes.expirationStrategy, expirationBasis: attributes.expirationBasis, renewalBasis: attributes.renewalBasis, transferStrategy: attributes.transferStrategy, authenticationStrategy: attributes.authenticationStrategy, machineLeasingStrategy: attributes.machineLeasingStrategy, processLeasingStrategy: attributes.processLeasingStrategy, overageStrategy: attributes.overageStrategy, metadata: attributes.metadata ? JSON.stringify(attributes.metadata, null, 2) : undefined, }; return data; }, [policy, isNew]); const payload = { name: policy?.data.attributes.name || "", productId: policy?.data.relationships?.product?.data.id, duration: policy?.data.attributes.duration, strict: policy?.data.attributes.strict, floating: policy?.data.attributes.floating, scheme: policy?.data.attributes.scheme as PolicyFormData["scheme"], requireProductScope: policy?.data.attributes.requireProductScope, requirePolicyScope: policy?.data.attributes.requirePolicyScope, requireMachineScope: policy?.data.attributes.requireMachineScope, requireFingerprintScope: policy?.data.attributes.requireFingerprintScope, requireComponentsScope: policy?.data.attributes.requireComponentsScope, requireUserScope: policy?.data.attributes.requireUserScope, requireChecksumScope: policy?.data.attributes.requireChecksumScope, requireVersionScope: policy?.data.attributes.requireVersionScope, requireCheckIn: policy?.data.attributes.requireCheckIn, checkInInterval: policy?.data.attributes.checkInInterval, checkInIntervalCount: policy?.data.attributes.checkInIntervalCount, usePool: policy?.data.attributes.usePool, maxMachines: policy?.data.attributes.maxMachines, maxProcesses: policy?.data.attributes.maxProcesses, maxUsers: policy?.data.attributes.maxUsers, maxCores: policy?.data.attributes.maxCores, maxMemory: policy?.data.attributes.maxMemory, maxDisk: policy?.data.attributes.maxDisk, maxUses: policy?.data.attributes.maxUses, encrypted: policy?.data.attributes.encrypted, protected: policy?.data.attributes.protected, requireHeartbeat: policy?.data.attributes.requireHeartbeat, heartbeatDuration: policy?.data.attributes.heartbeatDuration, heartbeatCullStrategy: policy?.data.attributes.heartbeatCullStrategy, heartbeatResurrectionStrategy: policy?.data.attributes.heartbeatResurrectionStrategy, heartbeatBasis: policy?.data.attributes.heartbeatBasis, machineUniquenessStrategy: policy?.data.attributes.machineUniquenessStrategy, machineMatchingStrategy: policy?.data.attributes.machineMatchingStrategy, componentUniquenessStrategy: policy?.data.attributes.componentUniquenessStrategy, componentMatchingStrategy: policy?.data.attributes.componentMatchingStrategy, expirationStrategy: policy?.data.attributes.expirationStrategy, expirationBasis: policy?.data.attributes.expirationBasis, renewalBasis: policy?.data.attributes.renewalBasis, transferStrategy: policy?.data.attributes.transferStrategy, authenticationStrategy: policy?.data.attributes.authenticationStrategy, machineLeasingStrategy: policy?.data.attributes.machineLeasingStrategy, processLeasingStrategy: policy?.data.attributes.processLeasingStrategy, overageStrategy: policy?.data.attributes.overageStrategy, metadata: policy?.data.attributes.metadata ? JSON.stringify(policy?.data.attributes.metadata, null, 2) : undefined, } const createMutation = useMutation({ mutationFn: async (policyData: Partial) => { console.log({policyData}); await apiClient.createPolicy({ ...policyData, }); }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["policies"] }); toast.success("Policy created successfully"); router.push("/dashboard/policies"); }, onError: (error) => { toast.error(error.message); }, }); const updateMutation = useMutation({ mutationFn: async (policyData: Partial) => await apiClient.updatePolicy(policyId, { ...policyData, }), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["policies"] }); await queryClient.invalidateQueries({ queryKey: ["policy", policyId] }); toast.success("Policy updated successfully"); }, onError: (error) => { toast.error(error.message); }, }); const deleteMutation = useMutation({ mutationFn: async () => await apiClient.deletePolicy(policyId), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["policies"] }); toast.success("Policy deleted successfully"); router.push("/dashboard/policies"); }, onError: (error) => { toast.error(error.message); }, }); const onSubmit = async (data: PolicyFormData) => { const policyPayload = { attributes: { name: data.name, duration: Number(data.duration), strict: data.strict, floating: data.floating, scheme: data.scheme, requireProductScope: data.requireProductScope, requirePolicyScope: data.requirePolicyScope, requireMachineScope: data.requireMachineScope, requireFingerprintScope: data.requireFingerprintScope, requireComponentsScope: data.requireComponentsScope, requireUserScope: data.requireUserScope, requireChecksumScope: data.requireChecksumScope, requireVersionScope: data.requireVersionScope, requireCheckIn: data.requireCheckIn, checkInInterval: Number(data.checkInInterval), checkInIntervalCount: Number(data.checkInIntervalCount), usePool: data.usePool, maxMachines: Number(data.maxMachines), maxProcesses: Number(data.maxProcesses), maxUsers: Number(data.maxUsers), maxCores: Number(data.maxCores), maxMemory: Number(data.maxMemory), maxDisk: Number(data.maxDisk), maxUses: Number(data.maxUses), encrypted: data.encrypted, protected: data.protected, requireHeartbeat: data.requireHeartbeat, heartbeatDuration: Number(data.heartbeatDuration), heartbeatCullStrategy: data.heartbeatCullStrategy, heartbeatResurrectionStrategy: data.heartbeatResurrectionStrategy, heartbeatBasis: data.heartbeatBasis, machineUniquenessStrategy: data.machineUniquenessStrategy, machineMatchingStrategy: data.machineMatchingStrategy, componentUniquenessStrategy: data.componentUniquenessStrategy, componentMatchingStrategy: data.componentMatchingStrategy, expirationStrategy: data.expirationStrategy, expirationBasis: data.expirationBasis, renewalBasis: data.renewalBasis, transferStrategy: data.transferStrategy, authenticationStrategy: data.authenticationStrategy, machineLeasingStrategy: data.machineLeasingStrategy, processLeasingStrategy: data.processLeasingStrategy, overageStrategy: data.overageStrategy, metadata: parseMetadata(data.metadata || ""), }, relationships: isNew ? { product: { data: { type: "products", id: data.productId, }, }, } : undefined, }; if (isNew) { await createMutation.mutateAsync(policyPayload as Partial); } else { await updateMutation.mutateAsync(policyPayload as Partial); } }; const handleDelete = async () => { await deleteMutation.mutateAsync(); setIsDeleteDialogOpen(false); }; const form = useForm({ // resolver: zodResolver(policySchema), // values: policy? payload : defaultPolicy values: policy? payload : defaultPolicy }); if ((isLoading || productsLoading) && (!isNew && !policy)) { return (

Loading policy...

); } return (
{/* Header */}

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

{isNew ? "Add a new policy to your system" : "Update policy information and settings"}

{!isNew && ( )}
{/* Form */} Policy Information {isNew ? "Enter the details for the new policy" : "Update the policy information below"} {defaultValues && products && ( <>
{/* form */} {/* Actions */}
)}
{/* Delete Policy Dialog */} Delete Policy Are you sure you want to delete this policy? This action cannot be undone.
); }