Files
emaui/apps/backoffice/src/app/features/user-management/UserManagementPage.tsx

207 lines
6.4 KiB
TypeScript

import Cookies from "js-cookie";
import { useCallback, useEffect, useRef } from "react";
import { createRoot, type Root } from "react-dom/client";
import { useNavigate } from "react-router-dom";
import { UserManagementApp } from "@tria-plc/iamui";
import { BASE_API_URL } from "@ema-platform/api";
import type {
DesignConfig,
UserManagementSessionOptions,
} from "@tria-plc/iamui";
import "@tria-plc/iamui/style.css";
const UM_OVERRIDES = `
.um-theme-light {
--background: #ffffff !important;
--foreground: #1f2937 !important;
--card: #ffffff !important;
--card-foreground: #1f2937 !important;
--popover: #ffffff !important;
--popover-foreground: #1f2937 !important;
--secondary: #f1f5f9 !important;
--secondary-foreground: #1e293b !important;
--muted: #f1f5f9 !important;
--muted-foreground: #64748b !important;
--accent: var(--primary) !important;
--accent-foreground: var(--primary-foreground) !important;
--border: #e2e8f0 !important;
--input: #e2e8f0 !important;
--sidebar: #ffffff !important;
--sidebar-foreground: #1e293b !important;
--sidebar-accent: #f1f5f9 !important;
--sidebar-accent-foreground: #1e293b !important;
--sidebar-border: #e2e8f0 !important;
--sidebar-ring: var(--primary) !important;
--color-amber-50: #eff6ff !important;
--color-amber-100: #dbeafe !important;
--color-yellow-50: #eff6ff !important;
--color-yellow-100: #dbeafe !important;
}
[class*="bg-[#f7efe8]"] { background-color: #f1f5f9 !important; }
[class*="bg-[#f2ece8]"] { background-color: #f1f5f9 !important; }
[class*="hover:bg-[#f7efe8]"]:hover { background-color: #f1f5f9 !important; }
[class*="hover:bg-[#f2ece8]"]:hover { background-color: #f1f5f9 !important; }
[class*="border-[#e6d8cc]"] { border-color: #e2e8f0 !important; }
[class*="bg-[#fffdfa]"] { background-color: #ffffff !important; }
[class*="bg-[#fcf7f3]"] { background-color: #f8fafc !important; }
[class*="bg-[#fffaf6]"] { background-color: #f8fafc !important; }
[class*="text-[#432319]"] { color: #1e293b !important; }
[class*="text-[#7a6a61]"] { color: #64748b !important; }
[class*="text-[#6b4a3d]"] { color: #475569 !important; }
.um-theme-light [class*="dark:border-gray-900"],
.um-theme-light [class*="dark:border-gray-800"],
.um-theme-light [class*="dark:border-gray-700"],
.um-theme-light [class*="dark:border-gray-600"],
.um-theme-light [class*="dark:border-slate-800"],
.um-theme-light [class*="dark:border-slate-700"] {
border-color: #e2e8f0 !important;
}
`;
const UM_CONFIG: DesignConfig = {
brand: {
appName: "Ethiopian Maritime Licence",
logoUrl: "/assets/emaLogo.jpg",
},
colors: {
primary: "#2563eb",
sidebar: "#ffffff",
background: "#f8fafc",
foreground: "#1e293b",
border: "#e2e8f0",
mutedForeground: "#94a3b8",
card: "#ffffff",
},
typography: {
fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif",
},
layout: {
userManagementView: "classic",
sidebarBrandLabel: "Ethiopian Maritime Authority",
sidebarBrandSublabel: "User Management",
sidebarBackground: "#ffffff",
sidebarColor: "#1e293b",
sidebarMutedColor: "#94a3b8",
sidebarActiveBackground: "#eff6ff",
sidebarActiveColor: "#2563eb",
sidebarHoverBackground: "#f8fafc",
sidebarBorder: "#e2e8f0",
sidebarWidth: "280px",
sidebarCollapsedWidth: "80px",
modalAccentColor: "#2563eb",
modalHeaderBackground: "#f8fafc",
modalHeaderEditBackground: "#eff6ff",
modalIconBackground: "#eff6ff",
modalIconColor: "#2563eb",
modalTitleColor: "#1e293b",
modalFocusColor: "#2563eb",
modalSurface: "#ffffff",
},
};
const UM_RUNTIME = {
basename: "/um",
// Keep the embedded IAM module on the same API as the backoffice client.
// When VITE_BASE_API_URL is absent locally, passing undefined makes iamui
// fall back to its remote development server, where the local JWT is
// rejected and the module redirects to its login page.
apiUrl: import.meta.env.VITE_BASE_API_URL ?? "http://localhost:3000/api",
};
const buttonStyle: React.CSSProperties = {
position: "fixed",
top: 12,
left: 12,
zIndex: 9999,
display: "flex",
alignItems: "center",
gap: 6,
padding: "8px 16px",
border: "1px solid #e2e8f0",
borderRadius: 8,
background: "#ffffff",
color: "#2563eb",
fontSize: 14,
fontWeight: 600,
cursor: "pointer",
fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif",
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
transition: "all 150ms ease",
};
export default function UserManagementPage() {
const containerRef = useRef<HTMLDivElement>(null);
const rootRef = useRef<Root | null>(null);
const navigate = useNavigate();
const handleReturn = useCallback(() => {
navigate("/dashboard");
}, [navigate]);
useEffect(() => {
if (!containerRef.current) return;
const style = document.createElement("style");
style.textContent = UM_OVERRIDES;
document.head.appendChild(style);
const token = Cookies.get("ema-backoffice-auth-token") ?? "";
const refreshToken = Cookies.get("ema-backoffice-refresh-token");
const session: UserManagementSessionOptions = {
initialSession: token ? { token, refreshToken, rememberMe: true } : null,
enableEmbeddedAuthBridge: false,
};
rootRef.current = createRoot(containerRef.current);
rootRef.current.render(
<UserManagementApp
config={UM_CONFIG}
runtime={UM_RUNTIME}
session={session}
/>,
);
return () => {
if (rootRef.current) {
rootRef.current.unmount();
rootRef.current = null;
}
style.remove();
};
}, []);
return (
<>
<button
onClick={handleReturn}
style={buttonStyle}
onMouseEnter={(e) => {
e.currentTarget.style.background = "#f8fafc";
e.currentTarget.style.boxShadow = "0 1px 6px rgba(0,0,0,0.12)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = "#ffffff";
e.currentTarget.style.boxShadow = "0 1px 3px rgba(0,0,0,0.08)";
}}>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<path d="m12 19-7-7 7-7" />
<path d="M19 12H5" />
</svg>
Return to EMA
</button>
<div ref={containerRef} style={{ position: "fixed", inset: 0 }} />
</>
);
}