import { Match, Show, Switch, createEffect, createMemo, createResource, createSignal, onMount, } from "solid-js"; import { useNavigate } from "@solidjs/router"; import { useAuth, RequireAuth } from "~/lib/auth"; import DashboardDesignPreview from "~/components/admin/DashboardDesignPreview"; import DashboardShell from "~/components/DashboardShell"; import ProfilePage from "~/components/dashboard/ProfilePage"; import PortfolioPage from "~/components/dashboard/PortfolioPage"; import VerificationStatusPage from "~/components/dashboard/VerificationStatusPage"; import CompanyJobsPage from "~/components/dashboard/CompanyJobsPage"; import CompanyApplicationsPage from "~/components/dashboard/CompanyApplicationsPage"; import SettingsPage from "~/components/dashboard/SettingsPage"; import MyDashboardPage from "~/components/dashboard/MyDashboardPage"; import CustomerRequirementsPage from "~/components/dashboard/CustomerRequirementsPage"; import CustomerResponsesPage from "~/components/dashboard/CustomerResponsesPage"; import CompanyShortlistedCandidatesPage from "~/components/dashboard/CompanyShortlistedCandidatesPage"; import JobSeekerApplicationsPage from "~/components/dashboard/JobSeekerApplicationsPage"; import JobSeekerSavedJobsPage from "~/components/dashboard/JobSeekerSavedJobsPage"; import JobSeekerJobsPage from "~/components/dashboard/JobSeekerJobsPage"; import ProfessionalLeadsPage from "~/components/dashboard/ProfessionalLeadsPage"; import ProfessionalResponsesPage from "~/components/dashboard/ProfessionalResponsesPage"; import CreditsPage from "~/components/dashboard/CreditsPage"; import ExploreServicesPage from "~/components/dashboard/ExploreServicesPage"; import HelpCenterDashboardPage from "~/components/dashboard/HelpCenterDashboardPage"; import SwitchServicesPage from "~/components/dashboard/SwitchServicesPage"; import LogoutPage from "~/components/dashboard/LogoutPage"; import { PROFESSIONAL_ROLE_SET } from "~/components/dashboard/RoleDashboardShared"; // Sidebar items that have real data implementations (wired to backend APIs) // These show real components instead of DashboardDesignPreview mock const BASE_REAL_PAGES = ["my dashboard", "my profile", "my portfolio", "verification", "settings"]; const COMMON_REAL_PAGES = [ "credits", "explore nxtgauge", "help center", "switch services", "logout", ]; const COMPANY_REAL_PAGES = ["jobs", "applications", "shortlisted candidates"]; const CUSTOMER_REAL_PAGES = ["my requirements", "received responses", "shortlisted responses"]; const JOB_SEEKER_REAL_PAGES = ["jobs", "my applications", "saved jobs", "my portfolio"]; const PROFESSIONAL_REAL_PAGES = ["leads", "my responses"]; type RoleKey = | "COMPANY" | "CUSTOMER" | "JOB_SEEKER" | "PHOTOGRAPHER" | "MAKEUP_ARTIST" | "TUTOR" | "DEVELOPER" | "VIDEO_EDITOR" | "UGC_CONTENT_CREATOR" | "GRAPHIC_DESIGNER" | "SOCIAL_MEDIA_MANAGER" | "FITNESS_TRAINER" | "CATERING_SERVICES"; type RuntimeBundle = { role: RoleKey; status: "ACTIVE" | "INACTIVE"; sidebarItems: string[]; tabs: string[]; widgets: string[]; fields: string[]; verificationStatus?: string; source: "dashboard-config"; }; const API_GATEWAY = "/api/gateway"; const SERVER_API_BASE = (process.env.PUBLIC_API_URL || "http://localhost:8080/api").replace( /\/+$/, "" ); const ROLE_OPTIONS: RoleKey[] = [ "COMPANY", "CUSTOMER", "JOB_SEEKER", "PHOTOGRAPHER", "MAKEUP_ARTIST", "TUTOR", "DEVELOPER", "VIDEO_EDITOR", "UGC_CONTENT_CREATOR", "GRAPHIC_DESIGNER", "SOCIAL_MEDIA_MANAGER", "FITNESS_TRAINER", "CATERING_SERVICES", ]; const ROLE_BASED_SIDEBAR: Record = { PHOTOGRAPHER: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], MAKEUP_ARTIST: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], TUTOR: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], DEVELOPER: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], VIDEO_EDITOR: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], UGC_CONTENT_CREATOR: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], GRAPHIC_DESIGNER: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], SOCIAL_MEDIA_MANAGER: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], FITNESS_TRAINER: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], CATERING_SERVICES: [ "My Dashboard", "My Profile", "My Portfolio", "Leads", "My Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], COMPANY: [ "My Dashboard", "My Profile", "Jobs", "Applications", "Shortlisted Candidates", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], JOB_SEEKER: [ "My Dashboard", "My Profile", "My Portfolio", "Jobs", "My Applications", "Saved Jobs", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], CUSTOMER: [ "My Dashboard", "My Profile", "My Requirements", "Received Responses", "Shortlisted Responses", "Credits", "Explore Nxtgauge", "Verification", "Help Center", "Settings", "Switch Services", "Logout", ], }; const ROLE_PREFIXES: Record = { PHOTOGRAPHER: "photographers", MAKEUP_ARTIST: "makeup-artists", TUTOR: "tutors", DEVELOPER: "developers", VIDEO_EDITOR: "video-editors", GRAPHIC_DESIGNER: "graphic-designers", SOCIAL_MEDIA_MANAGER: "social-media-managers", FITNESS_TRAINER: "fitness-trainers", CATERING_SERVICES: "catering-services", UGC_CONTENT_CREATOR: "ugc-content-creators", CUSTOMER: "customers", COMPANY: "companies", JOB_SEEKER: "jobseeker", }; function getNameFromStorage(): string { if (typeof window === "undefined") return "User"; const keys = ["nxtgauge_signup_profile_v1", "nxtgauge_auth_user", "nxtgauge_user"]; for (const key of keys) { try { const raw = window.localStorage.getItem(key) || window.sessionStorage.getItem(key); if (!raw) continue; const parsed = JSON.parse(raw); const name = parsed?.name || parsed?.first_name || parsed?.user?.name || parsed?.user?.first_name; if (name) return String(name); } catch { /* ignore */ } } return "User"; } const EXPLORE_ROLES = [ { key: "PHOTOGRAPHER", name: "Photographer" }, { key: "MAKEUP_ARTIST", name: "Makeup Artist" }, { key: "TUTOR", name: "Tutor" }, { key: "DEVELOPER", name: "Developer" }, { key: "VIDEO_EDITOR", name: "Video Editor" }, { key: "UGC_CONTENT_CREATOR", name: "UGC Content Creator" }, { key: "GRAPHIC_DESIGNER", name: "Graphic Designer" }, { key: "SOCIAL_MEDIA_MANAGER", name: "Social Media Manager" }, { key: "FITNESS_TRAINER", name: "Fitness Trainer" }, { key: "CATERING_SERVICES", name: "Catering Services" }, ]; function normalizeRole(value: string): RoleKey { const up = String(value || "") .trim() .toUpperCase() .replace(/\s+/g, "_"); return (ROLE_OPTIONS.find((r) => r === up) || "JOB_SEEKER") as RoleKey; } function asStringArray(value: unknown): string[] { if (!Array.isArray(value)) return []; return value.map((item) => String(item || "").trim()).filter(Boolean); } function getInitialRoleFromStorage(): RoleKey { if (typeof window === "undefined") return "JOB_SEEKER"; const keys = ["nxtgauge_signup_profile_v1", "nxtgauge_auth_user", "nxtgauge_user"]; for (const key of keys) { try { const raw = window.localStorage.getItem(key) || window.sessionStorage.getItem(key); if (!raw) continue; const parsed = JSON.parse(raw); const found = normalizeRole( String( parsed?.roleKey || parsed?.role || parsed?.active_role || parsed?.user?.active_role || parsed?.user?.roles?.[0] || "" ) ); if (ROLE_OPTIONS.includes(found)) return found; } catch { // ignore invalid payload } } return "JOB_SEEKER"; } async function fetchJson(path: string): Promise { try { const isServer = typeof window === "undefined"; const target = isServer ? `${SERVER_API_BASE}${path}` : `${API_GATEWAY}${path}`; const res = await fetch(target, { credentials: "include" }); if (!res.ok) return null; return await res.json(); } catch { return null; } } async function loadRoleBundle(role: RoleKey): Promise { if (typeof window === "undefined") { return null; } const runtime = await fetchJson("/api/runtime-config"); if (runtime) { const runtimeRole = normalizeRole(String(runtime?.role || runtime?.user?.active_role || role)); const runtimeSidebar = asStringArray( runtime?.dashboard_config?.sidebar_items ?? runtime?.dashboard_config?.sidebarItems ?? runtime?.sidebar_items ?? runtime?.sidebarItems ); const runtimeTabs = asStringArray(runtime?.dashboard_config?.tabs ?? runtime?.tabs); const runtimeWidgetsRaw = Array.isArray(runtime?.dashboard_config?.widgets) ? runtime.dashboard_config.widgets : Array.isArray(runtime?.widgets) ? runtime.widgets : []; const runtimeWidgets = runtimeWidgetsRaw .map((item: any) => String(typeof item === "string" ? item : item?.key || item?.id || "").trim() ) .filter(Boolean); const runtimeFields = asStringArray(runtime?.dashboard_config?.fields ?? runtime?.fields); return { role: runtimeRole, status: "ACTIVE", sidebarItems: runtimeSidebar, tabs: runtimeTabs, widgets: runtimeWidgets, fields: runtimeFields, verificationStatus: String( runtime?.verification_status || runtime?.user?.verification_status || "" ).toUpperCase() || undefined, source: "dashboard-config", }; } let payload = await fetchJson( `/api/config/dashboard/by-key/${encodeURIComponent(role)}?audience=EXTERNAL` ); if (!payload) { const listPayload = await fetchJson("/api/admin/dashboard-config?audience=EXTERNAL"); const rows = Array.isArray(listPayload) ? listPayload : Array.isArray(listPayload?.items) ? listPayload.items : []; const matched = rows.find( (row: any) => String(row?.role_key || row?.config_json?.role_key || "").toUpperCase() === role ); if (matched) payload = matched; } const config = (payload?.config_json || payload || null) as Record | null; if (!config) return null; const sidebarItems = asStringArray( (config as any)?.sidebar_items ?? (config as any)?.sidebarItems ); const tabs = asStringArray((config as any)?.tabs); const widgetsRaw = Array.isArray((config as any)?.widgets) ? (config as any).widgets : []; const widgets = widgetsRaw .map((item: any) => String(typeof item === "string" ? item : item?.key || item?.id || "").trim() ) .filter(Boolean); const fields = asStringArray((config as any)?.fields); return { role, status: payload?.is_active === false ? "INACTIVE" : "ACTIVE", sidebarItems, tabs, widgets, fields, source: "dashboard-config", }; } function mergeSidebar( role: RoleKey, runtimeSidebar: string[], verificationStatus?: string ): string[] { const base = ROLE_BASED_SIDEBAR[role] || [ "My Dashboard", "My Profile", "Switch Services", "Logout", ]; const fromRuntime = runtimeSidebar.filter(Boolean); const source = fromRuntime.length > 0 ? fromRuntime : base; const map = new Map(); for (const item of source) { const key = item.trim().toLowerCase(); if (!map.has(key)) map.set(key, item); } let merged = Array.from(map.values()); const status = String(verificationStatus || "").toUpperCase(); const approved = status === "APPROVED"; if (!approved && status) { const restricted = new Set([ "my profile", "help center", "settings", "verification", "logout", ...(PROFESSIONAL_ROLE_SET.has(role) || role === "JOB_SEEKER" ? ["my portfolio", "credits"] : []), ]); merged = merged.filter((item) => restricted.has(item.trim().toLowerCase())); } return merged; } export default function RuntimeDashboardPage() { const navigate = useNavigate(); const auth = useAuth(); const [hydrated, setHydrated] = createSignal(false); const [role, setRole] = createSignal("JOB_SEEKER"); const [activeSidebar, setActiveSidebar] = createSignal("My Dashboard"); const [activeTab, setActiveTab] = createSignal("overview"); const [userName, setUserName] = createSignal("User"); const [userId, setUserId] = createSignal(""); onMount(() => { setHydrated(true); const storedRole = getInitialRoleFromStorage(); setRole(storedRole); setUserName(getNameFromStorage()); if (auth.user()) { const u = auth.user()!; if (u.full_name) setUserName(u.full_name); if (u.id) setUserId(u.id); if (u.active_role) setRole(normalizeRole(u.active_role)); } }); createEffect(() => { const u = auth.user(); if (u) { if (u.full_name && userName() === "User") setUserName(u.full_name); if (u.id && !userId()) setUserId(u.id); if (u.active_role) setRole(normalizeRole(u.active_role)); } }); const [bundle] = createResource(() => role(), loadRoleBundle); const sidebarItems = createMemo(() => mergeSidebar(role(), bundle()?.sidebarItems || [], bundle()?.verificationStatus) ); createEffect(() => { const runtimeRole = bundle()?.role; if (runtimeRole && runtimeRole !== role()) setRole(runtimeRole); }); createEffect(() => { const first = sidebarItems()[0] || "My Dashboard"; const current = activeSidebar(); const exists = sidebarItems().some((item) => item.toLowerCase() === current.toLowerCase()); if (!exists) setActiveSidebar(first); }); const tabs = createMemo(() => { const fromRuntime = bundle()?.tabs || []; return fromRuntime.length > 0 ? fromRuntime : ["overview"]; }); createEffect(() => { role(); const firstTab = tabs()[0] || "overview"; setActiveTab((prev) => (prev ? prev : firstTab)); }); const loading = createMemo(() => !hydrated() || bundle.loading); const ready = createMemo(() => hydrated() && !bundle.loading); const liveData = createMemo(() => { const prefix = ROLE_PREFIXES[role()]; if (!prefix) return undefined; return { userName: userName(), userId: userId(), rolePrefix: prefix }; }); const isRealPage = createMemo(() => { const key = activeSidebar().toLowerCase(); if (BASE_REAL_PAGES.includes(key)) return true; if (COMMON_REAL_PAGES.includes(key)) return true; if (role() === "COMPANY" && COMPANY_REAL_PAGES.includes(key)) return true; if (role() === "CUSTOMER" && CUSTOMER_REAL_PAGES.includes(key)) return true; if (role() === "JOB_SEEKER" && JOB_SEEKER_REAL_PAGES.includes(key)) return true; if (PROFESSIONAL_ROLE_SET.has(role()) && PROFESSIONAL_REAL_PAGES.includes(key)) return true; return false; }); return (
Loading dashboard…
{/* ── Real pages: DashboardShell + actual components ── */} {/* ── All other views: DashboardDesignPreview mock ── */}
); } const cardStyle = { border: "1px solid #E5E7EB", "border-radius": "12px", padding: "16px", background: "#fff", color: "#111827", } as const;