import { memo, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { toast } from 'sonner' import { deploymentApi } from '@/api' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form' import { Textarea } from '@/components/ui/textarea' import i18n from '@/i18n' import type { LokiDeployment } from '@/types' const schema = z.object({ yaml: z.string().trim().min(1, i18n.t('deployments:retention.yamlRequired')) }) type Values = z.infer interface Props { deployment: LokiDeployment | null onClose: () => void onSaved: () => void } function configLocation(deployment: LokiDeployment, t: (k: string, o?: Record) => string) { if (deployment.mode === 'host-binary') return t('retention.locationHostBinary') if (deployment.mode === 'host-docker') return t('retention.locationHostDocker', { id: deployment.id }) return t('retention.locationK8s', { namespace: deployment.namespace || 'log' }) } export const RetentionDialog = memo(function RetentionDialog({ deployment, onClose, onSaved }: Props) { const { t } = useTranslation('deployments') const [saving, setSaving] = useState(false) const [liveConfig, setLiveConfig] = useState<{ location: string; yaml: string } | null>(null) const [configLoading, setConfigLoading] = useState(false) const [configError, setConfigError] = useState('') const form = useForm({ resolver: zodResolver(schema), defaultValues: { yaml: 'limits_config:\n retention_period: 30d\n' }, }) useEffect(() => { if (deployment) { form.reset({ yaml: `limits_config:\n retention_period: ${deployment.retentionDays || 30}d\n` }) } setLiveConfig(null) setConfigError('') }, [deployment, form]) const loadConfig = async () => { if (!deployment) return setConfigLoading(true) setConfigError('') try { setLiveConfig(await deploymentApi.liveConfig(deployment.id)) } catch (err) { setConfigError(err instanceof Error ? err.message : t('retention.errLoad')) } finally { setConfigLoading(false) } } const save = (apply: boolean) => form.handleSubmit(async ({ yaml }) => { if (!deployment) return setSaving(true) try { const result = await deploymentApi.updateRetention(deployment.id, yaml, apply) onSaved() onClose() if (apply) toast.info(t('retention.savedApplied', { days: result.retentionDays })) } catch (err) { toast.error(err instanceof Error ? err.message : t('retention.errSave')) onSaved() } finally { setSaving(false) } }) return ( { if (!open && !saving) onClose() }}> {deployment ? t('retention.title', { name: deployment.name }) : ''}

{t('retention.locationLabel')}{deployment ? configLocation(deployment, t) : ''}

{t('retention.compactorNote')}

{t('retention.liveTitle')}

{t('retention.liveHint')}

{configError &&

{configError}

} {liveConfig && (

{liveConfig.location}

{liveConfig.yaml}
)}

{t('retention.templateNote')}

{'compactor:\n  retention_enabled: true\n  retention_delete_delay: 2h'}
( {t('retention.yamlLabel')}