'use client'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { InlineMessage } from '@/components/feedback/inline-message'; import { AppModal } from '@/components/ui/app-modal'; import { applyTlsCertificate, convertTlsCertificateToAcme, updateAcmeCertificate, } from '@/features/tls-certificates/api/tls-certificates'; import type { TlsCertificateItem } from '@/features/tls-certificates/types'; import { getDnsAccounts } from '@/features/dns-accounts/api/dns-accounts'; import { getDefaultAcmeAccount } from '@/features/acme-accounts/api/acme-accounts'; import { acmeApplySchema, defaultAcmeApplyValues, type AcmeApplyFormValues, } from '@/features/websites/schemas'; import { getErrorMessage } from '@/features/websites/utils'; import { PrimaryButton, ResourceField, ResourceInput, ResourceSelect, ToggleField, } from '@/features/shared/components/resource-primitives'; interface CertificateApplyModalProps { isOpen: boolean; onClose: () => void; onApplied?: (certificate: TlsCertificateItem) => void; mode?: 'create' | 'edit-acme' | 'convert-upload'; certificate?: TlsCertificateItem | null; } export function CertificateApplyModal({ isOpen, onClose, onApplied, mode = 'create', certificate, }: CertificateApplyModalProps) { const queryClient = useQueryClient(); const [feedback, setFeedback] = useState<{ tone: 'success' | 'danger'; message: string; } | null>(null); const [showAdvanced, setShowAdvanced] = useState(false); const dnsAccountsQuery = useQuery({ queryKey: ['dns-accounts'], queryFn: getDnsAccounts, enabled: isOpen, }); const defaultAcmeAccountQuery = useQuery({ queryKey: ['acme-accounts', 'default'], queryFn: getDefaultAcmeAccount, enabled: isOpen, }); const form = useForm({ resolver: zodResolver(acmeApplySchema), defaultValues: defaultAcmeApplyValues, }); useEffect(() => { if (!isOpen) return; setFeedback(null); setShowAdvanced(false); if (certificate) { form.reset({ name: certificate.name, primary_domain: mode === 'convert-upload' ? '' : certificate.primary_domain || '', other_domains: mode === 'convert-upload' ? '' : certificate.other_domains || '', remark: certificate.remark || '', acme_account_id: mode === 'convert-upload' ? 0 : certificate.acme_account_id, dns_account_id: mode === 'convert-upload' ? 0 : certificate.dns_account_id, key_algorithm: certificate.key_algorithm || 'EC256', auto_renew: mode === 'convert-upload' ? true : certificate.auto_renew, dns1: mode === 'convert-upload' ? '' : certificate.dns1 || '', dns2: mode === 'convert-upload' ? '' : certificate.dns2 || '', disable_cname: mode === 'convert-upload' ? false : certificate.disable_cname, skip_dns: mode === 'convert-upload' ? false : certificate.skip_dns, }); if ( mode !== 'convert-upload' && (certificate.dns1 || certificate.dns2 || certificate.disable_cname || certificate.skip_dns) ) { setShowAdvanced(true); } } else { form.reset(defaultAcmeApplyValues); } }, [isOpen, form, mode, certificate]); useEffect(() => { if ( defaultAcmeAccountQuery.data && form.getValues('acme_account_id') === 0 ) { form.setValue('acme_account_id', defaultAcmeAccountQuery.data.id); } }, [defaultAcmeAccountQuery.data, form, isOpen]); const applyMutation = useMutation({ mutationFn: (values: AcmeApplyFormValues) => { if (mode === 'edit-acme' && certificate) { return updateAcmeCertificate(certificate.id, values); } if (mode === 'convert-upload' && certificate) { return convertTlsCertificateToAcme(certificate.id, values); } return applyTlsCertificate(values); }, onSuccess: async (certificate) => { await queryClient.invalidateQueries({ queryKey: ['tls-certificates'] }); onApplied?.(certificate); onClose(); }, onError: (error) => { setFeedback({ tone: 'danger', message: getErrorMessage(error) }); }, }); const onSubmit = form.handleSubmit((values) => { setFeedback(null); applyMutation.mutate(values); }); return (
{feedback ? ( ) : null}