import React, { useEffect, useMemo, useState } from 'react'; import { useAppStore } from '../../store'; import { showToast } from '../Toast'; import { useI18n } from '../../i18n'; import { getPersistedSiteValidationReport } from '../../navigation/siteValidationPersistence'; import './SiteValidationView.css'; type SiteValidationReport = { sitemapPath: string; sitemapChanged: boolean; missingUrlPaths: string[]; extraUrlPaths: string[]; expectedUrlCount: number; existingHtmlUrlCount: number; }; type SiteValidationApplyResult = { renderedUrlCount: number; deletedUrlCount: number; removedEmptyDirCount: number; }; export const SiteValidationView: React.FC = () => { const { t: tr } = useI18n(); const { activeProject } = useAppStore(); const [isLoading, setIsLoading] = useState(true); const [isApplying, setIsApplying] = useState(false); const [report, setReport] = useState(null); const loadPersistedReport = () => { setIsLoading(true); try { const projectId = activeProject?.id; if (!projectId) { setReport(null); return; } const persistedReport = getPersistedSiteValidationReport(projectId); setReport(persistedReport); } finally { setIsLoading(false); } }; useEffect(() => { loadPersistedReport(); }, [activeProject?.id]); useEffect(() => { const handler = (event: Event) => { const detail = (event as CustomEvent<{ projectId?: string }>).detail; if (!activeProject?.id || detail?.projectId !== activeProject.id) { return; } loadPersistedReport(); }; window.addEventListener('bds:site-validation-updated', handler); return () => window.removeEventListener('bds:site-validation-updated', handler); }, [activeProject?.id]); const canApply = useMemo(() => { if (!report) return false; return report.missingUrlPaths.length > 0 || report.extraUrlPaths.length > 0; }, [report]); const handleApply = async () => { if (!report || !canApply) { return; } setIsApplying(true); try { const result = await window.electronAPI.blog.applyValidation(report) as SiteValidationApplyResult; showToast.success(tr('siteValidation.toast.applySuccess', { rendered: result.renderedUrlCount, deleted: result.deletedUrlCount, })); } catch (error) { console.error('Applying site validation failed:', error); showToast.error(tr('siteValidation.error.apply')); } finally { setIsApplying(false); } }; if (isLoading) { return (

{tr('siteValidation.loading')}

); } if (!report) { return (

{tr('siteValidation.error.validate')}

); } return (

{tr('siteValidation.title')}

{tr('siteValidation.summary', { expected: report.expectedUrlCount, existing: report.existingHtmlUrlCount, missing: report.missingUrlPaths.length, extra: report.extraUrlPaths.length, })}

{tr('siteValidation.missingTitle')}

{report.missingUrlPaths.length === 0 ? (

{tr('siteValidation.noneMissing')}

) : (
    {report.missingUrlPaths.map((urlPath) => (
  • {urlPath}
  • ))}
)}

{tr('siteValidation.extraTitle')}

{report.extraUrlPaths.length === 0 ? (

{tr('siteValidation.noneExtra')}

) : (
    {report.extraUrlPaths.map((urlPath) => (
  • {urlPath}
  • ))}
)}
); };