feat: add settings and access dashboard slice

This commit is contained in:
jjbrunton committed 2026-08-08 15:25:34 +01:00
1 parent dcf2a1200c
commit 79de6aa2a1
11 files changed
+1066

No files matched your search

@@ -0,0 +1,59 @@
import { useLoginInfoQuery, useLoginStatusQuery, useStartLoginMutation } from './settings-queries.js';
function errorMessage(error: Error | null): string {
return error?.message ?? 'Something went wrong.';
}
function sessionLabel(status: ReturnType<typeof useLoginStatusQuery>['data']): string {
if (!status) return 'Unknown';
if (!status.ready) return 'Browser unavailable';
if ('error' in status) return 'Check failed';
return {
unknown: 'Unknown',
authenticated: 'Authenticated',
'login-required': 'Login required',
unreachable: 'Reddit unreachable',
}[status.status];
}
function noVncUrl(publicUrl: string | null, port: number): string {
if (publicUrl) return publicUrl;
return `${window.location.protocol}//${window.location.hostname}:${port}/vnc.html?autoconnect=true&resize=scale`;
}
export function AccessPanel() {
const infoQuery = useLoginInfoQuery();
const statusQuery = useLoginStatusQuery();
const loginMutation = useStartLoginMutation();
if (infoQuery.isPending || statusQuery.isPending) {
return <section aria-label="Loading access" className="panel panel-loading" role="status"><span className="spinner" />Loading Reddit access…</section>;
}
if (infoQuery.isError || statusQuery.isError) {
const error = infoQuery.error ?? statusQuery.error;
return (
<section className="panel error-panel" role="alert">
<strong>Reddit access unavailable</strong><span>{errorMessage(error)}</span>
<button className="button secondary" onClick={() => void Promise.all([infoQuery.refetch(), statusQuery.refetch()])} type="button">Retry access</button>
</section>
);
}
const info = infoQuery.data;
return (
<section aria-labelledby="access-heading" className="panel settings-section">
<div className="panel-heading"><div><span className="eyebrow">Browser session</span><h2 id="access-heading">Access</h2></div><span className={statusQuery.data.ready && 'status' in statusQuery.data && statusQuery.data.loggedIn ? 'pill success' : 'pill warning'}>{sessionLabel(statusQuery.data)}</span></div>
<p className="settings-copy">Open Reddit in the managed browser, complete login through noVNC, then recheck the session. Credentials are never entered or displayed here.</p>
<div className="access-actions">
<button className="button primary" disabled={loginMutation.isPending} onClick={() => loginMutation.mutate()} type="button">{loginMutation.isPending ? 'Opening login…' : 'Open Reddit login'}</button>
<button className="button secondary" disabled={statusQuery.isFetching} onClick={() => void statusQuery.refetch()} type="button">{statusQuery.isFetching ? 'Checking session…' : 'Recheck session'}</button>
{info.vncEnabled ? <a className="button secondary" href={noVncUrl(info.publicUrl, info.novncPort)} rel="noreferrer" target="_blank">Open noVNC login window</a> : null}
</div>
{!info.vncEnabled ? <div className="settings-notice warning" role="status">noVNC is not available in this deployment.</div> : null}
{info.passwordRequired ? <p className="settings-copy">A VNC password is required.</p> : null}
{loginMutation.isSuccess ? <div className="settings-notice success" role="status">Login page opened.</div> : null}
{loginMutation.isError ? <div className="settings-notice error" role="alert">{errorMessage(loginMutation.error)}</div> : null}
</section>
);
}