import { useCallback, useEffect, useState } from 'react';
import { __, _x, sprintf } from '@wordpress/i18n';
import { api, cfg } from './api.js';
import * as UI from './ui.jsx';
import { Button, Icon, Notice, Spinner, Toasts } from './ui.jsx';
import Dashboard from './screens/Dashboard.jsx';
import Settings from './screens/Settings.jsx';
import Hosted from './screens/Hosted.jsx';
import Notifications from './screens/Notifications.jsx';
import Extend from './screens/Extend.jsx';
import Help from './screens/Help.jsx';
const CORE = { dashboard: Dashboard, settings: Settings, hosted: Hosted, notifications: Notifications, extend: Extend, help: Help };
// Screens in menu order come from PHP (Core's plus any add-on's via `dashbread_admin_screens`).
// Core components are wired here; an add-on's bundle (enqueued after 'dashbread-admin') calls
// window.DashbreadAdmin.registerScreen('license', Component) and the app re-renders.
const SCREENS = {};
// Product name and marks (the White Label add-on sets them; Core says Dashbread).
const brand = Object.assign({ name: 'Dashbread', logo: '', tagline: cfg.pro
? sprintf( /* translators: %s: Dashbread Core version number */ __('Core %s · Pro installed', 'dashbread'), cfg.version )
: sprintf( /* translators: %s: Dashbread Core version number */ __('Core %s · Pro not installed', 'dashbread'), cfg.version ) }, cfg.brand || {});
(cfg.screens || []).forEach((s) => { SCREENS[s.id] = { label: s.label, slug: s.slug, C: CORE[s.id] || null }; });
const screenListeners = new Set();
function registerScreen(id, Component) {
// PHP decides which screens exist (menus, capabilities, entitlements). A component for a screen
// PHP didn't list is kept aside, never given a tab.
if (!SCREENS[id]) return;
SCREENS[id].C = Component;
screenListeners.forEach((l) => l());
}
window.DashbreadAdmin = Object.assign(window.DashbreadAdmin || {}, { registerScreen, ui: UI, api, React: window.React });
function Pending() {
return ;
}
function screenFromUrl(fallback) {
const page = new URLSearchParams(location.search).get('page') || '';
return Object.keys(SCREENS).find((k) => SCREENS[k].slug === page) || fallback;
}
/** Keep the wp-admin submenu highlight in sync when switching screens client-side. */
function syncWpMenu(slug) {
document.querySelectorAll('#toplevel_page_dashbread .wp-submenu li').forEach((li) => {
const a = li.querySelector('a');
const on = a && a.getAttribute('href') === 'admin.php?page=' + slug;
li.classList.toggle('current', !!on);
a && a.classList.toggle('current', !!on);
});
}
function readMode() {
try { return localStorage.getItem('dashbread-admin-mode') || 'light'; } catch (e) { return 'light'; }
}
export default function App({ initial }) {
const [screen, setScreen] = useState(() => screenFromUrl(initial));
const [data, setData] = useState(null);
const [error, setError] = useState('');
const [mode, setMode] = useState(readMode);
const [, bump] = useState(0);
useEffect(() => { const l = () => bump((n) => n + 1); screenListeners.add(l); return () => screenListeners.delete(l); }, []);
const load = useCallback(async () => {
setError('');
try { setData(await api.bootstrap()); } catch (e) { setError(e.message); }
}, []);
useEffect(() => { load(); }, [load]);
useEffect(() => {
document.documentElement.setAttribute('data-sui-preset', 'console');
document.documentElement.setAttribute('data-sui-mode', mode);
try { localStorage.setItem('dashbread-admin-mode', mode); } catch (e) {}
return () => { document.documentElement.removeAttribute('data-sui-mode'); document.documentElement.removeAttribute('data-sui-preset'); };
}, [mode]);
const go = useCallback((key) => {
if (!SCREENS[key]) return;
setScreen(key);
history.pushState({ dashbread: key }, '', cfg.adminUrl + '?page=' + SCREENS[key].slug);
syncWpMenu(SCREENS[key].slug);
window.scrollTo(0, 0);
}, []);
useEffect(() => {
const h = () => setScreen(screenFromUrl('dashboard'));
window.addEventListener('popstate', h);
return () => window.removeEventListener('popstate', h);
}, []);
const Screen = (SCREENS[screen] && SCREENS[screen].C) || Pending;
return (
);
}