Select Page
import React, { useState, useEffect } from ‘react’; import { Plus, X, Eye, CheckCircle2, AlertCircle, Monitor, Smartphone, Tablet, ExternalLink, ChevronDown, ChevronRight } from ‘lucide-react’; export default function DiviMigrationDashboard() { const [sites, setSites] = useState([]); const [selectedSite, setSelectedSite] = useState(null); const [currentPage, setCurrentPage] = useState(”); const [viewportSize, setViewportSize] = useState(‘desktop’); const [loading, setLoading] = useState(true); const [showAddSite, setShowAddSite] = useState(false); const [expandedSites, setExpandedSites] = useState({}); useEffect(() => { loadData(); }, []); const loadData = async () => { try { const result = await window.storage.get(‘divi-sites’); if (result?.value) { const data = JSON.parse(result.value); setSites(data.sites || []); setExpandedSites(data.expandedSites || {}); } } catch (error) { console.log(‘No existing data found, starting fresh’); } setLoading(false); }; const saveData = async (newSites, newExpanded = expandedSites) => { try { await window.storage.set(‘divi-sites’, JSON.stringify({ sites: newSites, expandedSites: newExpanded })); } catch (error) { console.error(‘Failed to save data:’, error); } }; const addSite = (siteData) => { const newSite = { id: Date.now().toString(), …siteData, pages: [], status: ‘not-started’, createdAt: new Date().toISOString() }; const newSites = […sites, newSite]; setSites(newSites); saveData(newSites); setShowAddSite(false); }; const deleteSite = (siteId) => { if (confirm(‘Are you sure you want to delete this site?’)) { const newSites = sites.filter(s => s.id !== siteId); setSites(newSites); saveData(newSites); if (selectedSite?.id === siteId) { setSelectedSite(null); } } }; const addPage = (siteId, pageData) => { const newSites = sites.map(site => { if (site.id === siteId) { const newPage = { id: Date.now().toString(), …pageData, checklist: { visualMatch: false, responsive: false, interactions: false, forms: false, animations: false, performance: false }, issues: [], status: ‘pending’ }; return { …site, pages: […site.pages, newPage] }; } return site; }); setSites(newSites); saveData(newSites); }; const updatePageChecklist = (siteId, pageId, checkItem) => { const newSites = sites.map(site => { if (site.id === siteId) { const pages = site.pages.map(page => { if (page.id === pageId) { const newChecklist = { …page.checklist, [checkItem]: !page.checklist[checkItem] }; const allChecked = Object.values(newChecklist).every(v => v === true); return { …page, checklist: newChecklist, status: allChecked ? ‘complete’ : ‘pending’ }; } return page; }); const allPagesComplete = pages.length > 0 && pages.every(p => p.status === ‘complete’); return { …site, pages, status: allPagesComplete ? ‘ready’ : pages.some(p => p.status === ‘complete’) ? ‘in-progress’ : ‘not-started’ }; } return site; }); setSites(newSites); saveData(newSites); }; const addIssue = (siteId, pageId, issueText) => { if (!issueText.trim()) return; const newSites = sites.map(site => { if (site.id === siteId) { return { …site, pages: site.pages.map(page => { if (page.id === pageId) { return { …page, issues: […page.issues, { id: Date.now().toString(), text: issueText, createdAt: new Date().toISOString() }] }; } return page; }) }; } return site; }); setSites(newSites); saveData(newSites); }; const deleteIssue = (siteId, pageId, issueId) => { const newSites = sites.map(site => { if (site.id === siteId) { return { …site, pages: site.pages.map(page => { if (page.id === pageId) { return { …page, issues: page.issues.filter(i => i.id !== issueId) }; } return page; }) }; } return site; }); setSites(newSites); saveData(newSites); }; const toggleSiteExpanded = (siteId) => { const newExpanded = { …expandedSites, [siteId]: !expandedSites[siteId] }; setExpandedSites(newExpanded); saveData(sites, newExpanded); }; const viewportSizes = { desktop: { width: ‘100%’, icon: Monitor, label: ‘Desktop’ }, tablet: { width: ‘768px’, icon: Tablet, label: ‘Tablet’ }, mobile: { width: ‘375px’, icon: Smartphone, label: ‘Mobile’ } }; const getStatusColor = (status) => { switch (status) { case ‘ready’: return ‘bg-green-100 text-green-800’; case ‘in-progress’: return ‘bg-yellow-100 text-yellow-800’; default: return ‘bg-gray-100 text-gray-800’; } }; const getStatusIcon = (status) => { switch (status) { case ‘ready’: return ; case ‘in-progress’: return ; default: return
; } }; const stats = { total: sites.length, ready: sites.filter(s => s.status === ‘ready’).length, inProgress: sites.filter(s => s.status === ‘in-progress’).length, notStarted: sites.filter(s => s.status === ‘not-started’).length }; if (loading) { return (
Loading dashboard…
); } return (

Divi 4.x → 5.x Migration Dashboard

Track and test your WordPress site upgrades

{stats.total}
Total Sites
{stats.ready}
Ready
{stats.inProgress}
In Progress
{sites.length === 0 ? (

No sites added yet.

Click “Add Site” to get started.

) : (
{sites.map(site => (
{ setSelectedSite(site); if (site.pages.length > 0) { setCurrentPage(site.pages[0].path); } }} >
{getStatusIcon(site.status)}

{site.name}

{site.productionUrl}

{site.status === ‘ready’ ? ‘Ready to Push’ : site.status === ‘in-progress’ ? ‘In Progress’ : ‘Not Started’} {site.pages.length} {site.pages.length === 1 ? ‘page’ : ‘pages’}
{expandedSites[site.id] && site.pages.length > 0 && (
{site.pages.map(page => (
{ setSelectedSite(site); setCurrentPage(page.path); }} >
{page.status === ‘complete’ ? ( ) : (
)} {page.name} {page.issues.length > 0 && ( {page.issues.length} )}
))}
)}
))}
)}
{selectedSite ? ( <>

{selectedSite.name}

{Object.entries(viewportSizes).map(([key, { icon: Icon, label }]) => ( ))}
addPage(selectedSite.id, pageData)} />
{selectedSite.pages.length === 0 ? (

Add pages to start testing

) : (
updatePageChecklist(selectedSite.id, pageId, checkItem)} onAddIssue={(pageId, issueText) => addIssue(selectedSite.id, pageId, issueText)} onDeleteIssue={(pageId, issueId) => deleteIssue(selectedSite.id, pageId, issueId)} />
)} ) : (

Select a site from the sidebar

or add a new site to get started

)}
{showAddSite && ( setShowAddSite(false)} /> )}
); } function AddSiteModal({ onAdd, onClose }) { const [formData, setFormData] = useState({ name: ”, productionUrl: ”, stagingUrl: ” }); const handleSubmit = (e) => { e.preventDefault(); if (formData.name && formData.productionUrl && formData.stagingUrl) { onAdd(formData); setFormData({ name: ”, productionUrl: ”, stagingUrl: ” }); } }; return (

Add New Site

setFormData({ …formData, name: e.target.value })} className=”w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent” placeholder=”My WordPress Site” required />
setFormData({ …formData, productionUrl: e.target.value })} className=”w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent” placeholder=”https://example.com” required />
setFormData({ …formData, stagingUrl: e.target.value })} className=”w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent” placeholder=”https://staging.example.com” required />
); } function AddPageForm({ onAdd }) { const [showForm, setShowForm] = useState(false); const [formData, setFormData] = useState({ name: ”, path: ‘/’ }); const handleSubmit = (e) => { e.preventDefault(); if (formData.name && formData.path) { onAdd(formData); setFormData({ name: ”, path: ‘/’ }); setShowForm(false); } }; if (!showForm) { return ( ); } return (
setFormData({ …formData, name: e.target.value })} className=”flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent” placeholder=”Page name (e.g., Home, About)” required /> setFormData({ …formData, path: e.target.value })} className=”w-40 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent” placeholder=”Path (e.g., /about)” required />
); } function IframePanel({ title, url, viewportWidth }) { return (

{title}

href={url} target=”_blank” rel=”noopener noreferrer” className=”flex items-center gap-1 text-sm text-blue-600 hover:text-blue-700″ > Open