'use client'; import Link from 'next/link'; import { useEffect, useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { ArrowLeft, FileCode2, Loader2, RotateCcw, Save, Sparkles, } from 'lucide-react'; import { toast } from 'sonner'; import { useAuth } from '@/components/providers/auth-provider'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Button } from '@/components/ui/button'; import { EmptyStateWithBorder } from '@/components/layout/empty'; import { ErrorInline } from '@/components/layout/error'; import { LoadingStateWithBorder } from '@/components/layout/loading'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { HtmlEditorWorkspace } from '@/components/common/html-editor-workspace'; import { ORIGIN_ERROR_PAGE_HTML_MAX_BYTES, previewOriginErrorPageHTML, } from '@/lib/openflare/default-origin-error-page-html'; import { DEFAULT_ORIGIN_ERROR_PAGE_TEMPLATE_ID, getOriginErrorPageTemplate, ORIGIN_ERROR_PAGE_TEMPLATES, } from '@/lib/openflare/origin-error-page-templates'; import { OptionService } from '@/lib/services/openflare'; import { useTranslations } from 'next-intl'; import { invalidateResponseQueries, KEY_HTML, mapOptionsToErrorFields, OPTIONS_QUERY_KEY, optionsToMap, } from '../../components/shared'; export default function ErrorPageEditPage() { const t = useTranslations('responses'); const tc = useTranslations('common'); const { user, loading: authLoading } = useAuth(); const queryClient = useQueryClient(); const [html, setHtml] = useState(''); const [restoreOpen, setRestoreOpen] = useState(false); const [templateId, setTemplateId] = useState( DEFAULT_ORIGIN_ERROR_PAGE_TEMPLATE_ID, ); const optionsQuery = useQuery({ queryKey: OPTIONS_QUERY_KEY, queryFn: () => OptionService.list(), enabled: !!user?.is_admin, }); useEffect(() => { if (!optionsQuery.data) return; setHtml(mapOptionsToErrorFields(optionsToMap(optionsQuery.data)).html); }, [optionsQuery.data]); const saveMutation = useMutation({ mutationFn: async () => { const bytes = new TextEncoder().encode(html).length; if (bytes > ORIGIN_ERROR_PAGE_HTML_MAX_BYTES) { throw new Error(t('htmlTooLarge')); } await OptionService.updateBatch([{ key: KEY_HTML, value: html }]); }, onSuccess: async () => { toast.success(t('errorHtmlSaved')); await invalidateResponseQueries(queryClient); }, onError: (error) => { toast.error(error instanceof Error ? error.message : t('saveFailed')); }, }); const loadSelectedTemplate = () => { const tmpl = getOriginErrorPageTemplate(templateId); if (!tmpl) { toast.error(t('templateNotFound')); return; } setHtml(tmpl.html); toast.success(t('templateLoaded', { name: tmpl.name })); }; const restoreDefault = () => { setHtml(''); setRestoreOpen(false); toast.success(t('errorRestored')); }; if (authLoading) { return (
); } if (!user?.is_admin) { return (
); } if (optionsQuery.isLoading) { return (
); } if (optionsQuery.isError) { return (
void optionsQuery.refetch()} />
); } return (

{t('editErrorHtml')}

{t('builtinTemplates')}
} /> {t('restoreTitle')} {t('restoreErrorDesc')} {tc('cancel')} {t('confirmRestore')}
); }