MotorSignal-App/website/components/ops/operations-error-state.tsx

132 lines
4.9 KiB
TypeScript

'use client';
import React, { useEffect, useState } from 'react';
import Link from 'next/link';
import { onAuthStateChanged } from 'firebase/auth';
import { firebaseAuth } from '../../lib/firebase-client';
interface OperationsErrorStateProps {
status: 401 | 403 | 404 | 503 | 'disabled' | 'network' | number;
message?: string;
onRetry?: () => void;
}
export function OperationsErrorState({
status,
message,
onRetry,
}: OperationsErrorStateProps) {
const [authenticating, setAuthenticating] = useState(false);
useEffect(() => {
if (status !== 401) return;
const unsubscribe = onAuthStateChanged(firebaseAuth, async (user) => {
if (user) {
setAuthenticating(true);
try {
const idToken = await user.getIdToken();
const res = await fetch('/api/auth/firebase-login', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ id_token: idToken }),
});
if (res.ok) {
window.location.reload();
return;
}
} catch {
// Ignore and remain on error state
} finally {
setAuthenticating(false);
}
}
});
return () => unsubscribe();
}, [status]);
let title = 'Operations Error';
let description = authenticating
? 'Verifying operator credentials and activating session...'
: (message ?? 'An unexpected error occurred in the Operations console.');
let icon = authenticating ? 'sync' : 'error';
let showSignIn = false;
let showRetry = true;
if (status === 401) {
title = authenticating ? 'Authenticating Operator...' : 'Authentication Required';
description = authenticating
? 'Verifying operator credentials and establishing secure session...'
: (message ?? 'Your Operations session has expired or is unauthenticated.');
icon = authenticating ? 'sync' : 'lock';
showSignIn = !authenticating;
showRetry = false;
} else if (status === 403) {
title = 'Access Restricted';
description = message ?? 'You do not have sufficient authority to access Operations.';
icon = 'gavel';
showSignIn = false;
showRetry = false;
} else if (status === 404) {
title = 'Resource Not Found';
description = message ?? 'The requested Operations resource does not exist.';
icon = 'search_off';
} else if (status === 503 || status === 'network') {
title = 'Operations Temporarily Unavailable';
description = message ?? 'Unable to communicate with the Operations API. Try again in a few moments.';
icon = 'cloud_off';
} else if (status === 'disabled') {
title = 'Operations Disabled';
description = message ?? 'The Operations platform is currently disabled by system policy.';
icon = 'block';
showRetry = false;
}
return (
<div className="p-8 rounded-xl bg-surface-container-low border border-outline-variant/30 text-center max-w-lg mx-auto my-12 space-y-4 shadow-sm">
<div className="w-12 h-12 rounded-full bg-surface-container-high border border-outline-variant/40 flex items-center justify-center text-outline mx-auto">
<span className="material-symbols-outlined text-[24px]">{icon}</span>
</div>
<div className="space-y-1">
<span className="text-[10px] font-mono text-outline font-semibold">
Status {status}
</span>
<h2 className="text-lg font-bold text-on-surface">{title}</h2>
<p className="text-xs text-on-surface-variant leading-relaxed">
{description}
</p>
</div>
<div className="pt-4 border-t border-outline-variant/20 flex items-center justify-center gap-3">
{showSignIn && (
<Link
href="/login?returnTo=/admin"
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg bg-primary-container text-on-primary-container text-xs font-bold shadow-sm hover:brightness-105 transition-all focus-visible:ring-2 focus-visible:ring-primary-container focus-visible:outline-none"
>
<span className="material-symbols-outlined text-[16px]">login</span>
<span>Sign In</span>
</Link>
)}
{showRetry && onRetry && (
<button
type="button"
onClick={onRetry}
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg bg-surface-container-high hover:bg-surface-bright text-on-surface border border-outline-variant/30 text-xs font-semibold transition-all focus-visible:ring-2 focus-visible:ring-primary-container focus-visible:outline-none"
>
<span className="material-symbols-outlined text-[16px]">refresh</span>
<span>Retry</span>
</button>
)}
<Link
href="/"
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-lg text-xs font-medium text-on-surface-variant hover:text-on-surface transition-colors"
>
Return Home
</Link>
</div>
</div>
);
}