'use client'; import Link from 'next/link'; import { useEffect, useMemo, useState } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { Expand, Loader2, Pencil, Save } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { TagsInput } from '@/components/ui/tags-input'; import { previewOriginErrorPageHTML } from '@/lib/openflare/default-origin-error-page-html'; import { validateStatusCodeTagMessage, validateStatusCodeTags, } from '@/lib/openflare/status-code-tags'; import { OptionService } from '@/lib/services/openflare'; import { useTranslations } from 'next-intl'; import { defaultErrorPageFields, invalidateResponseQueries, KEY_ENABLED, KEY_GET_ONLY, KEY_STATUS_CODES, mapOptionsToErrorFields, type ErrorPageFields, } from './shared'; export function ErrorPageTab({ optionMap, }: { optionMap: Record; }) { const t = useTranslations('responses'); const tc = useTranslations('common'); const queryClient = useQueryClient(); const [fields, setFields] = useState(defaultErrorPageFields); const [tagError, setTagError] = useState(null); useEffect(() => { setFields(mapOptionsToErrorFields(optionMap)); setTagError(null); }, [optionMap]); const previewSrcDoc = useMemo( () => previewOriginErrorPageHTML(fields.html), [fields.html], ); /** 仅保存策略项 */ const savePolicyMutation = useMutation({ mutationFn: async () => { validateStatusCodeTags(fields.statusCodes); await OptionService.updateBatch([ { key: KEY_ENABLED, value: String(fields.enabled) }, { key: KEY_GET_ONLY, value: String(fields.getOnly) }, { key: KEY_STATUS_CODES, value: JSON.stringify(fields.statusCodes), }, ]); }, onSuccess: async () => { toast.success(t('policySaved')); await invalidateResponseQueries(queryClient); }, onError: (error) => { toast.error(error instanceof Error ? error.message : t('saveFailed')); }, }); const handleValidateTag = (tag: string) => { const message = validateStatusCodeTagMessage(tag); if (message) { setTagError(message); toast.error(message); return message; } setTagError(null); return null; }; return (
{t('triggerPolicy')} {t('triggerPolicyDesc')}

{t('enableErrorPageDesc')}

setFields((prev) => ({ ...prev, enabled })) } aria-label={t('enableErrorPage')} className='mt-0.5 shrink-0' />

{t('getOnlyDesc')}

setFields((prev) => ({ ...prev, getOnly })) } aria-label={t('getOnly')} className='mt-0.5 shrink-0' />

{t('statusCodesDesc')}

{ setFields((prev) => ({ ...prev, statusCodes })); setTagError(null); }} validateTag={handleValidateTag} placeholder={t('statusCodesPlaceholder')} aria-invalid={!!tagError} disabled={!fields.enabled} /> {tagError ? (

{tagError}

) : null}
{t('pagePreview')}