OPC # 0001: Extract OPC into standalone repo

This commit is contained in:
amadzarak
2026-04-25 17:26:42 -04:00
commit 42383bdc03
170 changed files with 21365 additions and 0 deletions
@@ -0,0 +1,156 @@
import { useEffect, useRef, useState } from 'react';
import { AnchorButton, Button, Callout, Intent, NonIdealState, Spinner, Tab, Tabs, Tag } from '@blueprintjs/core';
import DeployWizard from '../components/wizard/DeployWizard';
import { tenantUrl, CLARITY_DOMAIN } from '../config';
import { getTenants, subscribeToTenantLogs } from '../api/provisioningApi';
import type { TenantRecord } from '../types/provisioning';
const ENV_INTENT: Record<string, Intent> = {
prod: Intent.DANGER,
uat: Intent.WARNING,
fdev: Intent.PRIMARY,
};
function TenantCard({ t }: { t: TenantRecord }) {
const [logs, setLogs] = useState<string[]>([]);
const [logsOpen, setLogsOpen] = useState(false);
const logRef = useRef<HTMLDivElement>(null);
const sourceRef = useRef<EventSource | null>(null);
useEffect(() => {
if (!logsOpen) return;
const src = subscribeToTenantLogs(
t.subdomain,
(line) => setLogs((prev) => [...prev.slice(-500), line]), // cap at 500 lines
() => {}
);
sourceRef.current = src;
return () => { src.close(); sourceRef.current = null; };
}, [logsOpen, t.subdomain]);
// Auto-scroll to bottom when new lines arrive
useEffect(() => {
if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight;
}, [logs]);
return (
<div className="job-card">
<div className="job-card-header">
<div>
<strong>{t.clientName}</strong>
<span className="job-card-subdomain">{t.subdomain}</span>
</div>
<div style={{ display: 'flex', gap: '0.4rem', alignItems: 'center' }}>
<Tag intent={ENV_INTENT[t.environment] ?? Intent.NONE} round minimal>{t.environment}</Tag>
<Tag intent={t.status === 'Provisioned' ? Intent.SUCCESS : Intent.WARNING} round>{t.status}</Tag>
</div>
</div>
<Tabs onChange={(id) => setLogsOpen(id === 'logs')}>
<Tab id="info" title="Info" panel={
<div className="job-card-meta" style={{ paddingTop: '0.5rem' }}>
<span>Site: <code>{t.siteCode}</code></span>
<span>Container: <code>{t.containerName ?? '—'}{t.containerPort ? `:${t.containerPort}` : ''}</code></span>
<span>{new Date(t.provisionedAt).toLocaleString()}</span>
{t.status === 'Provisioned' && (
<AnchorButton
icon="share"
minimal
small
href={tenantUrl(t.subdomain)}
target="_blank"
rel="noopener noreferrer"
style={{ marginTop: '0.25rem' }}
>
{t.subdomain}.{CLARITY_DOMAIN}
</AnchorButton>
)}
</div>
} />
<Tab id="logs" title="Server" panel={
<div
ref={logRef}
style={{
fontFamily: 'monospace',
fontSize: '0.75rem',
background: '#1a1a1a',
color: '#d4d4d4',
padding: '0.5rem',
borderRadius: '4px',
height: '260px',
overflowY: 'auto',
whiteSpace: 'pre-wrap',
wordBreak: 'break-all',
marginTop: '0.5rem',
}}
>
{logs.length === 0
? <span style={{ color: '#666' }}>Connecting to container logs</span>
: logs.map((l, i) => <div key={i}>{l}</div>)
}
</div>
} />
</Tabs>
</div>
);
}
export default function DashboardPage() {
const [wizardOpen, setWizardOpen] = useState(false);
const [tenants, setTenants] = useState<TenantRecord[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const load = () => {
getTenants()
.then((data) => { setTenants(data); setError(null); })
.catch((e: Error) => setError(e.message))
.finally(() => setLoading(false));
};
useEffect(() => { load(); }, []);
const handleWizardClose = () => { setWizardOpen(false); setLoading(true); load(); };
if (wizardOpen) return <DeployWizard onClose={handleWizardClose} />;
return (
<>
<div className="page-header">
<div>
<h1>Provisioned Tenants</h1>
<p>Manage and monitor client deployments.</p>
</div>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<Button icon="refresh" minimal onClick={load} loading={loading} title="Refresh" />
<Button intent={Intent.PRIMARY} text="Deploy New Client" icon="plus" large onClick={() => setWizardOpen(true)} />
</div>
</div>
{loading && <NonIdealState icon={<Spinner />} title="Loading tenants..." />}
{error && (
<Callout intent={Intent.DANGER} title="Failed to load tenants" style={{ marginBottom: '1rem' }}>
{error}
</Callout>
)}
{!loading && !error && tenants.length === 0 && (
<div className="empty-state">
<div className="empty-state-icon">🏗</div>
<h3>No tenants provisioned yet</h3>
<p>Deploy your first client to get started.</p>
<Button intent={Intent.PRIMARY} text="Deploy New Client" icon="plus" onClick={() => setWizardOpen(true)} />
</div>
)}
{!loading && tenants.length > 0 && (
<div className="job-list">
{tenants.map((t) => (
<TenantCard key={t.subdomain} t={t} />
))}
</div>
)}
</>
);
}