Added dynamic sidebar
This commit is contained in:
parent
b79f3afc2e
commit
b3a666066c
@ -5,7 +5,7 @@ import { useEffect, useRef } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { AppDispatch } from "@/app/redux/types";
|
||||
import { validateAuth } from "./redux/auth/authSlice";
|
||||
|
||||
import { fetchMetadata } from "./redux/metadata/metadataSlice";
|
||||
export function AuthBootstrap() {
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const startedRef = useRef(false);
|
||||
@ -18,7 +18,7 @@ export function AuthBootstrap() {
|
||||
|
||||
// Initial validate on mount
|
||||
dispatch(validateAuth());
|
||||
|
||||
// dispatch(fetchMetadata());
|
||||
// Refresh on window focus (nice UX)
|
||||
const onFocus = () => dispatch(validateAuth());
|
||||
window.addEventListener("focus", onFocus);
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { cookies } from "next/headers";
|
||||
import { randomUUID } from "crypto";
|
||||
import { formatPhoneDisplay } from "@/app/features/UserRoles/utils";
|
||||
|
||||
const BE_BASE_URL = process.env.BE_BASE_URL || "http://localhost:8583";
|
||||
|
||||
@ -20,21 +20,13 @@ export async function GET(request: Request) {
|
||||
const url = new URL(request.url);
|
||||
const queryString = url.search ? url.search : "";
|
||||
|
||||
console.log(
|
||||
"[DEBUG] - URL",
|
||||
`${BE_BASE_URL}/api/v1/users/list${queryString}`
|
||||
);
|
||||
|
||||
const response = await fetch(
|
||||
`${BE_BASE_URL}/api/v1/users/list${queryString}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
cache: "no-store",
|
||||
}
|
||||
);
|
||||
const response = await fetch(`${BE_BASE_URL}/api/v1/users${queryString}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
console.log("[DEBUG] - Response", response);
|
||||
const data = await response.json();
|
||||
|
||||
48
app/api/metadata/route.ts
Normal file
48
app/api/metadata/route.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
// Proxy to backend metadata endpoint. Assumes BACKEND_BASE_URL is set.
|
||||
export async function GET() {
|
||||
const { cookies } = await import("next/headers");
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get("auth_token")?.value;
|
||||
const BE_BASE_URL = process.env.BE_BASE_URL || "http://localhost:5000";
|
||||
|
||||
if (!token) {
|
||||
return NextResponse.json(
|
||||
{ success: false, message: "No token found" },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const url = `${BE_BASE_URL.replace(/\/$/, "")}/api/v1/metadata`;
|
||||
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
console.log("metadata", data);
|
||||
|
||||
if (!res.ok) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
message: data?.message || "Failed to fetch metadata",
|
||||
},
|
||||
{ status: res.status }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(data);
|
||||
} catch (err) {
|
||||
const message = (err as Error)?.message || "Metadata proxy error";
|
||||
return NextResponse.json({ success: false, message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
// app/components/PageLinks/PageLinks.tsx
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { ISidebarLink } from "@/app/features/dashboard/sidebar/SidebarLink.interfaces"; // Keep this import
|
||||
import clsx from "clsx"; // Utility to merge class names
|
||||
|
||||
import { ISidebarLink } from "@/app/features/dashboard/sidebar/SidebarLink.interfaces"; // Keep this import
|
||||
import { resolveIcon } from "@/app/utils/iconMap";
|
||||
import "./PageLinks.scss"; // Keep this import
|
||||
|
||||
// Define the props interface for your PageLinks component
|
||||
@ -13,19 +13,11 @@ interface IPageLinksProps extends ISidebarLink {
|
||||
}
|
||||
|
||||
// PageLinks component
|
||||
export default function PageLinks({
|
||||
title,
|
||||
path,
|
||||
icon: Icon, // Destructure icon as Icon
|
||||
}: // isShowIcon, // If you plan to use this prop, uncomment it and add logic
|
||||
IPageLinksProps) {
|
||||
export default function PageLinks({ title, path, icon }: IPageLinksProps) {
|
||||
const Icon = resolveIcon(icon);
|
||||
console.log("Icon", Icon);
|
||||
return (
|
||||
// Corrected Link usage for Next.js 13/14 App Router:
|
||||
// - Removed `passHref` and `legacyBehavior`
|
||||
// - Applied `className` directly to the Link component
|
||||
// - Removed the nested `<a>` tag
|
||||
<Link href={path} className={clsx("page-link", "page-link__container")}>
|
||||
{/* Conditionally render Icon if it exists */}
|
||||
{Icon && <Icon />}
|
||||
<span className="page-link__text">{title}</span>
|
||||
</Link>
|
||||
|
||||
@ -49,11 +49,11 @@ export default async function BackOfficeUsersPage({
|
||||
|
||||
const data = await res.json();
|
||||
// Handle different response structures: could be array directly, or wrapped in data/users property
|
||||
// const users = Array.isArray(data) ? data : data.users || data.data || [];
|
||||
const users = Array.isArray(data) ? data : data.users || data.data || [];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Users users={data?.users} />
|
||||
<Users users={users} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
export interface IUser {
|
||||
id: string;
|
||||
email: string;
|
||||
role: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
username: string;
|
||||
|
||||
@ -7,13 +7,17 @@ import "./EditUser.scss";
|
||||
|
||||
const EditUser = ({ user }: { user: IUser }) => {
|
||||
const router = useRouter();
|
||||
const { username, lastName, email, authorities: roles, phone } = user;
|
||||
const { username, last_name, email, groups, phone } = user;
|
||||
const [form, setForm] = React.useState<IEditUserForm>({
|
||||
firstName: username || "",
|
||||
lastName: lastName || "",
|
||||
lastName: last_name || "",
|
||||
email: email || "",
|
||||
role: roles[0] || "",
|
||||
phone: phone || "",
|
||||
groups: groups || [],
|
||||
merchants: merchants || [],
|
||||
jobTitle: jobTitle || "",
|
||||
username: username || "",
|
||||
});
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@ -40,6 +44,10 @@ const EditUser = ({ user }: { user: IUser }) => {
|
||||
email: "",
|
||||
role: "",
|
||||
phone: "",
|
||||
groups: [],
|
||||
merchants: [],
|
||||
jobTitle: "",
|
||||
username: "",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -6,9 +6,10 @@ import {
|
||||
MenuItem,
|
||||
SelectChangeEvent,
|
||||
} from "@mui/material";
|
||||
import { PAGE_LINKS } from "@/app/features/dashboard/sidebar/SidebarLink.constants";
|
||||
import { ISidebarLink } from "@/app/features/dashboard/sidebar/SidebarLink.interfaces";
|
||||
import PageLinks from "../../../../components/PageLinks/PageLinks";
|
||||
import { SidebarItem } from "@/app/redux/metadata/metadataSlice";
|
||||
import { useSelector } from "react-redux";
|
||||
import { selectNavigationSidebar } from "@/app/redux/metadata/selectors";
|
||||
import "./DropDown.scss";
|
||||
|
||||
interface Props {
|
||||
@ -17,7 +18,7 @@ interface Props {
|
||||
|
||||
export default function SidebarDropdown({ onChange }: Props) {
|
||||
const [value, setValue] = React.useState("");
|
||||
|
||||
const sidebar = useSelector(selectNavigationSidebar)[0]?.links;
|
||||
const handleChange = (event: SelectChangeEvent<string>) => {
|
||||
setValue(event.target.value);
|
||||
onChange?.(event);
|
||||
@ -42,8 +43,13 @@ export default function SidebarDropdown({ onChange }: Props) {
|
||||
<em className="em">Select a page</em>
|
||||
<MenuItem value="" disabled></MenuItem>
|
||||
<div className="sidebar-dropdown__container">
|
||||
{PAGE_LINKS.map((link: ISidebarLink) => (
|
||||
<PageLinks key={link.path} title={link.title} path={link.path} />
|
||||
{sidebar?.map((link: SidebarItem) => (
|
||||
<PageLinks
|
||||
key={link.path}
|
||||
title={link.title}
|
||||
path={link.path}
|
||||
icon={link.icon as string}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Select>
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import DashboardIcon from "@mui/icons-material/Dashboard";
|
||||
import { useState } from "react";
|
||||
import { PAGE_LINKS } from "@/app/features/dashboard/sidebar/SidebarLink.constants";
|
||||
import { ElementType, useState } from "react";
|
||||
import PageLinks from "../../../components/PageLinks/PageLinks";
|
||||
import "./sideBar.scss";
|
||||
import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight";
|
||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
|
||||
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||
import { selectNavigationSidebar } from "@/app/redux/metadata/selectors";
|
||||
import { useSelector } from "react-redux";
|
||||
import { SidebarItem } from "@/app/redux/metadata/metadataSlice";
|
||||
import { resolveIcon } from "@/app/utils/iconMap";
|
||||
|
||||
interface SidebarProps {
|
||||
isOpen?: boolean;
|
||||
@ -17,7 +20,7 @@ interface SidebarProps {
|
||||
|
||||
const SideBar = ({ isOpen = true, onClose }: SidebarProps) => {
|
||||
const [openMenus, setOpenMenus] = useState<Record<string, boolean>>({});
|
||||
|
||||
const sidebar = useSelector(selectNavigationSidebar)[0]?.links;
|
||||
const toggleMenu = (title: string) => {
|
||||
setOpenMenus(prev => ({ ...prev, [title]: !prev[title] }));
|
||||
};
|
||||
@ -34,45 +37,52 @@ const SideBar = ({ isOpen = true, onClose }: SidebarProps) => {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{PAGE_LINKS.map(link =>
|
||||
link.children ? (
|
||||
<div key={link.title}>
|
||||
<button
|
||||
onClick={() => toggleMenu(link.title)}
|
||||
className="sidebar__dropdown-button"
|
||||
>
|
||||
{link.icon && <link.icon />}
|
||||
<span className="sidebar__text">{link.title}</span>
|
||||
<span className="sidebar__arrow">
|
||||
{!openMenus[link.title] ? (
|
||||
<KeyboardArrowRightIcon />
|
||||
) : (
|
||||
<KeyboardArrowDownIcon />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
{openMenus[link.title] && (
|
||||
<div className="sidebar__submenu">
|
||||
{link.children.map(child => (
|
||||
<PageLinks
|
||||
key={child.path}
|
||||
title={child.title}
|
||||
path={child.path}
|
||||
icon={child.icon}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
{sidebar?.map((link: SidebarItem) => {
|
||||
if (link.children) {
|
||||
const Icon = resolveIcon(link.icon as string);
|
||||
return (
|
||||
<div key={link.title}>
|
||||
<button
|
||||
onClick={() => toggleMenu(link.title)}
|
||||
className="sidebar__dropdown-button"
|
||||
>
|
||||
{Icon && <Icon />}
|
||||
<span className="sidebar__text">{link.title}</span>
|
||||
<span className="sidebar__arrow">
|
||||
{openMenus[link.title] ? (
|
||||
<KeyboardArrowDownIcon />
|
||||
) : (
|
||||
<KeyboardArrowRightIcon />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{openMenus[link.title] && (
|
||||
<div className="sidebar__submenu">
|
||||
{link.children.map(child => (
|
||||
<PageLinks
|
||||
key={child.path}
|
||||
title={child.title}
|
||||
path={child.path}
|
||||
icon={child.icon as ElementType}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Render simple links if no children
|
||||
return (
|
||||
<PageLinks
|
||||
key={link.path}
|
||||
title={link.title}
|
||||
path={link.path}
|
||||
icon={link.icon}
|
||||
icon={link.icon as ElementType}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
);
|
||||
})}
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
import HomeIcon from "@mui/icons-material/Home";
|
||||
import AccountBalanceWalletIcon from "@mui/icons-material/AccountBalanceWallet";
|
||||
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
import VerifiedUserIcon from "@mui/icons-material/VerifiedUser";
|
||||
import PeopleIcon from "@mui/icons-material/People";
|
||||
import GavelIcon from "@mui/icons-material/Gavel";
|
||||
import HubIcon from "@mui/icons-material/Hub";
|
||||
import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettings";
|
||||
import InsightsIcon from "@mui/icons-material/Insights";
|
||||
import ListAltIcon from "@mui/icons-material/ListAlt";
|
||||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
|
||||
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
|
||||
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
|
||||
import HistoryIcon from "@mui/icons-material/History";
|
||||
import FactCheckIcon from "@mui/icons-material/FactCheck";
|
||||
|
||||
import { ISidebarLink } from "@/app/features/dashboard/sidebar/SidebarLink.interfaces";
|
||||
|
||||
export const PAGE_LINKS: ISidebarLink[] = [
|
||||
{ title: "Home", path: "/dashboard", icon: HomeIcon },
|
||||
|
||||
{
|
||||
title: "Transaction",
|
||||
path: "/dashboard/transactions",
|
||||
icon: AccountBalanceWalletIcon,
|
||||
children: [
|
||||
{
|
||||
title: "Deposits",
|
||||
path: "/dashboard/transactions/deposits",
|
||||
icon: ArrowDownwardIcon,
|
||||
},
|
||||
{
|
||||
title: "Withdrawals",
|
||||
path: "/dashboard/transactions/withdrawals",
|
||||
icon: ArrowUpwardIcon,
|
||||
},
|
||||
{
|
||||
title: "All Transactions",
|
||||
path: "/dashboard/transactions/all",
|
||||
icon: HistoryIcon,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ title: "Approve", path: "/dashboard/approve", icon: CheckCircleIcon },
|
||||
{ title: "Investigate", path: "/dashboard/investigate", icon: SearchIcon },
|
||||
{ title: "KYC", path: "/dashboard/kyc", icon: VerifiedUserIcon },
|
||||
{
|
||||
title: "User Accounts",
|
||||
path: "/dashboard/user-accounts",
|
||||
icon: PeopleIcon,
|
||||
},
|
||||
// { title: 'Analytics', path: '/analytics', icon: BarChartIcon },
|
||||
{ title: "Rules", path: "/dashboard/rules", icon: GavelIcon },
|
||||
{ title: "Rules Hub", path: "/dashboard/rules-hub", icon: HubIcon },
|
||||
{
|
||||
title: "Admin",
|
||||
path: "/dashboard/admin",
|
||||
icon: AdminPanelSettingsIcon,
|
||||
children: [
|
||||
{
|
||||
title: "Manage Users",
|
||||
path: "/dashboard/admin/users",
|
||||
icon: PeopleIcon,
|
||||
},
|
||||
{
|
||||
title: "Transactions",
|
||||
path: "/dashboard/admin/transactions",
|
||||
icon: ListAltIcon,
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
path: "dashboard/admin/settings",
|
||||
icon: SettingsIcon,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ title: "Account IQ", path: "/dashboard/account-iq", icon: InsightsIcon },
|
||||
{ title: "Audits", path: "/dashboard/audits", icon: FactCheckIcon },
|
||||
// { title: 'Documentation', path: '/documentation', icon: DescriptionIcon },
|
||||
// { title: 'Support', path: '/support', icon: SupportAgentIcon },
|
||||
// { title: 'System Status', path: '/system-status', icon: WarningAmberIcon },
|
||||
];
|
||||
@ -3,6 +3,6 @@ import { ElementType } from "react";
|
||||
export interface ISidebarLink {
|
||||
title: string;
|
||||
path: string;
|
||||
icon?: ElementType;
|
||||
icon?: ElementType | string;
|
||||
children?: ISidebarLink[];
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { ThunkSuccess, ThunkError, IUserResponse } from "../types";
|
||||
import { IEditUserForm } from "../../features/UserRoles/User.interfaces";
|
||||
import { RootState } from "../store";
|
||||
import toast from "react-hot-toast";
|
||||
import { fetchMetadata } from "../metadata/metadataSlice";
|
||||
|
||||
interface TokenInfo {
|
||||
expiresAt: string;
|
||||
@ -168,6 +169,7 @@ export const validateAuth = createAsyncThunk<
|
||||
dispatch(autoLogout("Session invalid or expired"));
|
||||
return rejectWithValue("Session invalid or expired");
|
||||
}
|
||||
dispatch(fetchMetadata());
|
||||
|
||||
return {
|
||||
tokenInfo: data.tokenInfo || null,
|
||||
|
||||
90
app/redux/metadata/metadataSlice.ts
Normal file
90
app/redux/metadata/metadataSlice.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { RootState } from "../store";
|
||||
|
||||
export interface SidebarItem {
|
||||
id?: string;
|
||||
title: string;
|
||||
path: string;
|
||||
icon?: string; // icon name from backend; map client-side if needed
|
||||
permissions?: string[]; // required permissions for visibility
|
||||
children?: SidebarItem[];
|
||||
}
|
||||
|
||||
export interface AppMetadata {
|
||||
groups?: string[];
|
||||
job_titles?: string[];
|
||||
merchants?: string[];
|
||||
message?: string;
|
||||
sidebar?: SidebarItem[];
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
interface MetadataState {
|
||||
data: AppMetadata | null;
|
||||
status: "idle" | "loading" | "succeeded" | "failed";
|
||||
error: string | null;
|
||||
lastFetchedAt: number | null;
|
||||
}
|
||||
|
||||
const initialState: MetadataState = {
|
||||
data: null,
|
||||
status: "idle",
|
||||
error: null,
|
||||
lastFetchedAt: null,
|
||||
};
|
||||
|
||||
export const fetchMetadata = createAsyncThunk<
|
||||
AppMetadata,
|
||||
void,
|
||||
{ rejectValue: string }
|
||||
>("metadata/fetch", async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const res = await fetch("/api/metadata", {
|
||||
method: "GET",
|
||||
cache: "no-store",
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
return rejectWithValue(data?.message || "Failed to fetch metadata");
|
||||
}
|
||||
return data as AppMetadata;
|
||||
} catch (err) {
|
||||
return rejectWithValue((err as Error)?.message || "Network error");
|
||||
}
|
||||
});
|
||||
|
||||
const metadataSlice = createSlice({
|
||||
name: "metadata",
|
||||
initialState,
|
||||
reducers: {
|
||||
clearMetadata(state) {
|
||||
state.data = null;
|
||||
state.error = null;
|
||||
state.status = "idle";
|
||||
state.lastFetchedAt = null;
|
||||
},
|
||||
},
|
||||
extraReducers: builder => {
|
||||
builder
|
||||
.addCase(fetchMetadata.pending, state => {
|
||||
state.status = "loading";
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(
|
||||
fetchMetadata.fulfilled,
|
||||
(state, action: PayloadAction<AppMetadata>) => {
|
||||
state.status = "succeeded";
|
||||
state.data = action.payload;
|
||||
state.lastFetchedAt = Date.now();
|
||||
}
|
||||
)
|
||||
.addCase(fetchMetadata.rejected, (state, action) => {
|
||||
state.status = "failed";
|
||||
state.error = (action.payload as string) || "Unknown error";
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { clearMetadata } = metadataSlice.actions;
|
||||
|
||||
export default metadataSlice.reducer;
|
||||
7
app/redux/metadata/selectors.ts
Normal file
7
app/redux/metadata/selectors.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { RootState } from "../store";
|
||||
|
||||
// Selectors
|
||||
export const selectMetadataState = (state: RootState) => state.metadata;
|
||||
export const selectAppMetadata = (state: RootState) => state.metadata.data;
|
||||
export const selectNavigationSidebar = (state: RootState) =>
|
||||
state.metadata.data?.sidebar || [];
|
||||
@ -9,6 +9,7 @@ import { createEpicMiddleware, combineEpics } from "redux-observable";
|
||||
import advancedSearchReducer from "./advanedSearch/advancedSearchSlice";
|
||||
import authReducer from "./auth/authSlice";
|
||||
import uiReducer from "./ui/uiSlice";
|
||||
import metadataReducer from "./metadata/metadataSlice";
|
||||
import userEpics from "./user/epic";
|
||||
import authEpics from "./auth/epic";
|
||||
import uiEpics from "./ui/epic";
|
||||
@ -38,6 +39,7 @@ const persistConfig = {
|
||||
const rootReducer = combineReducers({
|
||||
advancedSearch: advancedSearchReducer,
|
||||
auth: authReducer,
|
||||
metadata: metadataReducer,
|
||||
ui: uiReducer,
|
||||
});
|
||||
|
||||
|
||||
50
app/utils/iconMap.ts
Normal file
50
app/utils/iconMap.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import type { ElementType } from "react";
|
||||
|
||||
// MUI icons used across the app; extend as needed
|
||||
import HomeIcon from "@mui/icons-material/Home";
|
||||
import AccountBalanceWalletIcon from "@mui/icons-material/AccountBalanceWallet";
|
||||
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
import VerifiedUserIcon from "@mui/icons-material/VerifiedUser";
|
||||
import PeopleIcon from "@mui/icons-material/People";
|
||||
import GavelIcon from "@mui/icons-material/Gavel";
|
||||
import HubIcon from "@mui/icons-material/Hub";
|
||||
import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettings";
|
||||
import InsightsIcon from "@mui/icons-material/Insights";
|
||||
import ListAltIcon from "@mui/icons-material/ListAlt";
|
||||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
|
||||
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
|
||||
import HistoryIcon from "@mui/icons-material/History";
|
||||
import FactCheckIcon from "@mui/icons-material/FactCheck";
|
||||
import WidgetsIcon from "@mui/icons-material/Widgets"; // fallback
|
||||
|
||||
// Map string keys from backend to actual Icon components
|
||||
const iconRegistry: Record<string, ElementType> = {
|
||||
HomeIcon,
|
||||
AccountBalanceWalletIcon,
|
||||
CheckCircleIcon,
|
||||
SearchIcon,
|
||||
VerifiedUserIcon,
|
||||
PeopleIcon,
|
||||
GavelIcon,
|
||||
HubIcon,
|
||||
AdminPanelSettingsIcon,
|
||||
InsightsIcon,
|
||||
ListAltIcon,
|
||||
SettingsIcon,
|
||||
ArrowDownwardIcon,
|
||||
ArrowUpwardIcon,
|
||||
HistoryIcon,
|
||||
FactCheckIcon,
|
||||
};
|
||||
|
||||
export function resolveIcon(
|
||||
icon?: ElementType | string
|
||||
): ElementType | undefined {
|
||||
if (!icon) return undefined;
|
||||
if (typeof icon === "string") {
|
||||
return iconRegistry[icon] || WidgetsIcon; // fallback to a generic icon
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user