mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-30 05:18:13 +00:00
207 lines
6.4 KiB
TypeScript
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 }} />
|
|
</>
|
|
);
|
|
}
|