Applied UI rendering fixes
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
||||
import React, { Component, ErrorInfo, ReactNode, useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { AlertTriangle, RefreshCw, Home } from 'lucide-react';
|
||||
import { AuthContext } from '../../auth/AuthProvider';
|
||||
import { evaluateUserPermission } from '../../auth/rbac';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
@@ -11,6 +14,70 @@ interface State {
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
interface FallbackContentProps {
|
||||
fallbackTitle?: string;
|
||||
error: Error | null;
|
||||
onReset: () => void;
|
||||
}
|
||||
|
||||
const ErrorBoundaryFallbackContent: React.FC<FallbackContentProps> = ({
|
||||
fallbackTitle,
|
||||
error,
|
||||
onReset,
|
||||
}) => {
|
||||
const auth = useContext(AuthContext);
|
||||
const roles = auth?.roles || [];
|
||||
const claims = auth?.claims || {};
|
||||
const isUnrestricted =
|
||||
roles.some((r) => r.toLowerCase().trim() === 'admin') ||
|
||||
claims?.unrestricted === true ||
|
||||
claims?.unrestricted === 'all';
|
||||
|
||||
const canAccessDashboard = isUnrestricted || evaluateUserPermission(roles, claims, 'dashboard', 'view');
|
||||
|
||||
return (
|
||||
<div className="min-h-[400px] flex items-center justify-center p-6">
|
||||
<div className="max-w-md w-full bg-[#0f1523]/90 backdrop-blur-md rounded-2xl border border-rose-500/30 p-6 text-center shadow-2xl">
|
||||
<div className="w-12 h-12 rounded-2xl bg-rose-500/10 border border-rose-500/20 text-rose-400 flex items-center justify-center mx-auto mb-4">
|
||||
<AlertTriangle className="w-6 h-6" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-white mb-2">
|
||||
{fallbackTitle || 'Display Module Encountered an Issue'}
|
||||
</h3>
|
||||
<p className="text-xs text-slate-400 mb-4 leading-relaxed">
|
||||
{canAccessDashboard
|
||||
? 'An unexpected display error occurred while rendering this view. You can reload the view or return to the executive dashboard.'
|
||||
: 'An unexpected display error occurred while rendering this view. Please try reloading the view.'}
|
||||
</p>
|
||||
{error?.message && (
|
||||
<div className="mb-4 p-2.5 bg-rose-950/40 rounded-xl border border-rose-800/40 text-left text-[11px] text-rose-300 font-mono break-all max-h-24 overflow-y-auto">
|
||||
{error.message}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onReset}
|
||||
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-xl text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-white border border-slate-700 transition cursor-pointer"
|
||||
>
|
||||
<RefreshCw className="w-3.5 h-3.5 text-blue-400" />
|
||||
<span>Reload View</span>
|
||||
</button>
|
||||
{canAccessDashboard && (
|
||||
<Link
|
||||
to="/dashboard"
|
||||
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-xl text-xs font-semibold bg-blue-600 hover:bg-blue-500 text-white transition cursor-pointer shadow-md shadow-blue-500/20"
|
||||
>
|
||||
<Home className="w-3.5 h-3.5" />
|
||||
<span>Dashboard</span>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export class ErrorBoundary extends React.Component<Props, State> {
|
||||
declare props: Props;
|
||||
declare state: State;
|
||||
@@ -38,36 +105,11 @@ export class ErrorBoundary extends React.Component<Props, State> {
|
||||
public render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div className="min-h-[400px] flex items-center justify-center p-6">
|
||||
<div className="max-w-md w-full bg-[#0f1523]/90 backdrop-blur-md rounded-2xl border border-rose-500/30 p-6 text-center shadow-2xl">
|
||||
<div className="w-12 h-12 rounded-2xl bg-rose-500/10 border border-rose-500/20 text-rose-400 flex items-center justify-center mx-auto mb-4">
|
||||
<AlertTriangle className="w-6 h-6" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-white mb-2">
|
||||
{this.props.fallbackTitle || 'Display Module Encountered an Issue'}
|
||||
</h3>
|
||||
<p className="text-xs text-slate-400 mb-6 leading-relaxed">
|
||||
An unexpected display error occurred while rendering this view. You can reload the view or return to the executive dashboard.
|
||||
</p>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={this.handleReset}
|
||||
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-xl text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-white border border-slate-700 transition cursor-pointer"
|
||||
>
|
||||
<RefreshCw className="w-3.5 h-3.5 text-blue-400" />
|
||||
<span>Reload View</span>
|
||||
</button>
|
||||
<a
|
||||
href="/dashboard"
|
||||
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-xl text-xs font-semibold bg-blue-600 hover:bg-blue-500 text-white transition cursor-pointer shadow-md shadow-blue-500/20"
|
||||
>
|
||||
<Home className="w-3.5 h-3.5" />
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ErrorBoundaryFallbackContent
|
||||
fallbackTitle={this.props.fallbackTitle}
|
||||
error={this.state.error}
|
||||
onReset={this.handleReset}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { ReactNode, useEffect } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { X } from 'lucide-react';
|
||||
import { HelpTooltip } from './HelpTooltip';
|
||||
|
||||
@@ -60,13 +61,18 @@ export const Modal: React.FC<ModalProps> = ({
|
||||
};
|
||||
const maxWidthClass = maxWidthClassMap[effectiveSize] || 'max-w-lg';
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm animate-in fade-in duration-150">
|
||||
const modalContent = (
|
||||
<div
|
||||
className="fixed inset-0 z-[9999] flex items-center justify-center p-4 bg-black/80 backdrop-blur-md animate-in fade-in duration-150 overflow-y-auto"
|
||||
onClick={onClose}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<div
|
||||
className={`relative w-full ${maxWidthClass} bg-[#0f1523] rounded-2xl shadow-2xl border border-slate-800/90 overflow-hidden flex flex-col max-h-[90vh] text-slate-200`}
|
||||
className={`relative w-full ${maxWidthClass} bg-[#0f1523] rounded-2xl shadow-2xl border border-slate-800/90 overflow-hidden flex flex-col max-h-[90vh] my-auto text-slate-200`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-start justify-between p-5 border-b border-slate-800/80 bg-[#0d121f]/90">
|
||||
<div className="flex items-start justify-between p-5 border-b border-slate-800/80 bg-[#0d121f]/90 shrink-0">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-lg font-semibold text-white tracking-tight">{title}</h3>
|
||||
@@ -82,14 +88,20 @@ export const Modal: React.FC<ModalProps> = ({
|
||||
{subtitle && <p className="text-xs text-slate-400 mt-0.5">{subtitle}</p>}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="rounded-xl p-1.5 text-slate-400 hover:text-white hover:bg-slate-800/60 transition cursor-pointer"
|
||||
aria-label="Close dialog"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-6 overflow-y-auto flex-1 bg-[#0f1523]">{children}</div>
|
||||
<div className="p-6 overflow-y-auto flex-1 bg-[#0f1523] min-w-0">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return typeof document !== 'undefined'
|
||||
? createPortal(modalContent, document.body)
|
||||
: modalContent;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user