feat: surface backend sync health in cockpit
This commit is contained in:
+43
-5
@@ -79,6 +79,8 @@ function App() {
|
||||
const [promptTarget, setPromptTarget] = useState<(typeof PROMPT_TARGETS)[number]>('OpenClaw')
|
||||
const [promptFeatureId, setPromptFeatureId] = useState('')
|
||||
const [backendMode, setBackendMode] = useState<'connecting' | 'appwrite' | 'local-cache'>('connecting')
|
||||
const [syncStatus, setSyncStatus] = useState<'connecting' | 'synced' | 'pending' | 'syncing' | 'degraded'>('connecting')
|
||||
const [lastSyncedAt, setLastSyncedAt] = useState<string | null>(null)
|
||||
const hasHydratedRemote = useRef(false)
|
||||
const initialLocalStateRef = useRef(appState)
|
||||
|
||||
@@ -104,9 +106,12 @@ function App() {
|
||||
setStatusMessage('Seeded Appwrite on Unraid from the local BuildPulse state.')
|
||||
}
|
||||
setBackendMode('appwrite')
|
||||
setSyncStatus('synced')
|
||||
setLastSyncedAt(nowIso())
|
||||
} catch {
|
||||
if (cancelled) return
|
||||
setBackendMode('local-cache')
|
||||
setSyncStatus('degraded')
|
||||
setStatusMessage('Appwrite backend unavailable, so BuildPulse is using the local cache for now.')
|
||||
} finally {
|
||||
hasHydratedRemote.current = true
|
||||
@@ -123,11 +128,19 @@ function App() {
|
||||
useEffect(() => {
|
||||
if (!hasHydratedRemote.current || backendMode !== 'appwrite') return
|
||||
|
||||
setSyncStatus('pending')
|
||||
const timer = window.setTimeout(() => {
|
||||
void pushRemoteState(appState).catch(() => {
|
||||
setBackendMode('local-cache')
|
||||
setStatusMessage('Saved locally. Appwrite sync tripped over itself, so the cache is carrying the load.')
|
||||
})
|
||||
setSyncStatus('syncing')
|
||||
void pushRemoteState(appState)
|
||||
.then(() => {
|
||||
setSyncStatus('synced')
|
||||
setLastSyncedAt(nowIso())
|
||||
})
|
||||
.catch(() => {
|
||||
setBackendMode('local-cache')
|
||||
setSyncStatus('degraded')
|
||||
setStatusMessage('Saved locally. Appwrite sync tripped over itself, so the cache is carrying the load.')
|
||||
})
|
||||
}, 350)
|
||||
|
||||
return () => window.clearTimeout(timer)
|
||||
@@ -548,6 +561,18 @@ function App() {
|
||||
|
||||
const currentFeatureCount = groupedFeatures.now.length
|
||||
const recentPulsePreview = [...appState.pulses].sort((a, b) => b.timestamp.localeCompare(a.timestamp)).slice(0, 3)
|
||||
const backendLabel =
|
||||
backendMode === 'appwrite' ? 'Appwrite backend · Unraid server' : backendMode === 'connecting' ? 'Connecting to Appwrite…' : 'Local cache fallback'
|
||||
const syncLabel =
|
||||
syncStatus === 'synced'
|
||||
? `Synced${lastSyncedAt ? ` ${formatDateTime(lastSyncedAt)}` : ''}`
|
||||
: syncStatus === 'pending'
|
||||
? 'Changes queued'
|
||||
: syncStatus === 'syncing'
|
||||
? 'Syncing now…'
|
||||
: syncStatus === 'degraded'
|
||||
? 'Sync degraded · local cache active'
|
||||
: 'Connecting…'
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
@@ -576,6 +601,19 @@ function App() {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section className="status-strip card">
|
||||
<div>
|
||||
<span className={`pill status-${backendMode === 'appwrite' ? 'healthy' : backendMode === 'connecting' ? 'connecting' : 'degraded'}`}>
|
||||
{backendLabel}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className={`pill status-${syncStatus === 'synced' ? 'healthy' : syncStatus === 'pending' || syncStatus === 'syncing' || syncStatus === 'connecting' ? 'connecting' : 'degraded'}`}>
|
||||
{syncLabel}
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="project-card card">
|
||||
<div className="section-heading compact">
|
||||
<div>
|
||||
@@ -1209,7 +1247,7 @@ function App() {
|
||||
|
||||
<footer className="status-bar">
|
||||
<span>{statusMessage}</span>
|
||||
<span>{backendMode === 'appwrite' ? 'Appwrite backend · Unraid server' : backendMode === 'connecting' ? 'Connecting to Appwrite…' : 'Local cache fallback'} · schema {appState.schema_version}</span>
|
||||
<span>{backendLabel} · {syncLabel} · schema {appState.schema_version}</span>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user