98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import Link from 'next/link';
|
|
|
|
interface OperationsErrorStateProps {
|
|
status: 401 | 403 | 404 | 503 | 'disabled' | 'network' | number;
|
|
message?: string;
|
|
onRetry?: () => void;
|
|
}
|
|
|
|
export function OperationsErrorState({
|
|
status,
|
|
message,
|
|
onRetry,
|
|
}: OperationsErrorStateProps) {
|
|
let title = 'Operations Error';
|
|
let description = message ?? 'An unexpected error occurred in the Operations console.';
|
|
let icon = 'error';
|
|
let showSignIn = false;
|
|
let showRetry = true;
|
|
|
|
if (status === 401) {
|
|
title = 'Authentication Required';
|
|
description = message ?? 'Your Operations session has expired or is unauthenticated.';
|
|
icon = 'lock';
|
|
showSignIn = true;
|
|
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 uppercase tracking-widest text-outline">
|
|
STATUS {typeof status === 'number' ? status : status.toUpperCase()}
|
|
</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"
|
|
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>
|
|
);
|
|
}
|