import React, { useEffect, useState } from 'react'; import { createRoot } from 'react-dom/client'; import './styles.css'; import { api } from './api.js'; import { ClientOverviewPage } from './components/ClientOverviewPage.jsx'; function App() { const previewReady = new URLSearchParams(window.location.search).has('preview-ready'); const [state, setState] = useState(null); const [subscriptionUrl, setSubscriptionUrl] = useState(''); const [servers, setServers] = useState([]); const [pendingTag, setPendingTag] = useState(''); const [busy, setBusy] = useState(false); const [error, setError] = useState(''); async function loadState() { const data = await api.state(); setState(data); setServers(data.servers || []); setPendingTag((current) => ( (data.servers || []).some((server) => server.tag === current) ? current : data.selectedTag || '' )); } useEffect(() => { loadState().catch((err) => setError(err.message)); const timer = setInterval(() => loadState().catch(() => {}), 5000); return () => clearInterval(timer); }, []); useEffect(() => { if (state?.mode) document.title = state.mode === 'gateway' ? 'Harbor Gateway' : 'Harbor Connect'; }, [state?.mode]); async function run(action) { setBusy(true); setError(''); try { await action(); await loadState(); } catch (err) { setError(err.message); throw err; } finally { setBusy(false); } } async function fetchSubscription() { return run(async () => { const data = await api.subscription.fetch(subscriptionUrl); setServers(data.servers || []); setPendingTag(''); }); } async function refreshSubscription() { const data = await api.subscription.refresh(); setState((current) => current ? { ...current, userInfo: data.userInfo, fetchedAt: data.fetchedAt, servers: data.servers, selectedTag: data.selectedTag, } : current); setServers(data.servers || []); setPendingTag(data.selectedTag || ''); return data; } async function forgetSubscription() { return run(async () => { await api.subscription.forget(); setSubscriptionUrl(''); setServers([]); setPendingTag(''); }); } if (!state) return