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'; // Sidebar items that have real implementations (not the preview mock) const REAL_PAGES = ['my profile', 'my portfolio', 'verification']; 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[]; 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', '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; } 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[]): string[] { const base = ROLE_BASED_SIDEBAR[role] || ['My Dashboard', 'My Profile', 'Switch Services', 'Logout']; const fromRuntime = runtimeSidebar.filter(Boolean); const map = new Map(); for (const item of [...fromRuntime, ...base]) { const key = item.trim().toLowerCase(); if (!map.has(key)) map.set(key, item); } let merged = Array.from(map.values()); if (role === 'JOB_SEEKER') { merged = merged.filter((item) => item.trim().toLowerCase() !== 'credits'); } if (!merged.some((item) => item.trim().toLowerCase() === 'explore nxtgauge')) { const insertBefore = merged.findIndex((item) => item.trim().toLowerCase() === 'verification'); if (insertBefore >= 0) merged.splice(insertBefore, 0, 'Explore Nxtgauge'); else merged.push('Explore Nxtgauge'); } 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 || [])); 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(() => REAL_PAGES.includes(activeSidebar().toLowerCase()), ); 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;