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 (
);
}
return (
{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 (
);
}
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 (
);
}
function IframePanel({ title, url, viewportWidth }) {
return (
Open
);
}
function TestingPanel({ site, currentPage, onChecklistUpdate, onAddIssue, onDeleteIssue }) {
const [newIssue, setNewIssue] = useState(”);
const page = site.pages.find(p => p.path === currentPage);
if (!page) return null;
const checklistItems = [
{ key: ‘visualMatch’, label: ‘Visual Match’, description: ‘Layout, colors, fonts match’ },
{ key: ‘responsive’, label: ‘Responsive Design’, description: ‘Works on all viewport sizes’ },
{ key: ‘interactions’, label: ‘Interactions’, description: ‘Buttons, links, menus work’ },
{ key: ‘forms’, label: ‘Forms’, description: ‘Form submissions and validation’ },
{ key: ‘animations’, label: ‘Animations’, description: ‘Transitions and effects’ },
{ key: ‘performance’, label: ‘Performance’, description: ‘Load time acceptable’ }
];
const handleAddIssue = (e) => {
e.preventDefault();
if (newIssue.trim()) {
onAddIssue(page.id, newIssue);
setNewIssue(”);
}
};
const completedCount = Object.values(page.checklist).filter(v => v).length;
const totalCount = Object.keys(page.checklist).length;
const progress = (completedCount / totalCount) * 100;
return (
);
}
Loading dashboard…
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);
}
}}
>
{expandedSites[site.id] && site.pages.length > 0 && (
{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’}
{site.pages.map(page => (
)}
{
setSelectedSite(site);
setCurrentPage(page.path);
}}
>
))}
{page.status === ‘complete’ ? (
) : (
)}
{page.name}
{page.issues.length > 0 && (
{page.issues.length}
)}
{selectedSite ? (
<>
addPage(selectedSite.id, pageData)}
/>
{selectedSite.pages.length === 0 ? (
) : (
updatePageChecklist(selectedSite.id, pageId, checkItem)}
onAddIssue={(pageId, issueText) => addIssue(selectedSite.id, pageId, issueText)}
onDeleteIssue={(pageId, issueId) => deleteIssue(selectedSite.id, pageId, issueId)}
/>
)}
>
) : (
)}
{selectedSite.name}
{Object.entries(viewportSizes).map(([key, { icon: Icon, label }]) => (
))}
Add pages to start testing
Select a site from the sidebar
or add a new site to get started
Add New Site
{title}
href={url} target=”_blank” rel=”noopener noreferrer” className=”flex items-center gap-1 text-sm text-blue-600 hover:text-blue-700″ >{page.name}
Progress
{completedCount}/{totalCount}
Testing Checklist
{checklistItems.map(item => (
))}
Issues Found
{page.issues.length === 0 ? (No issues reported
) : (
{page.issues.map(issue => (
))}
)}
{issue.text}