fix: MyDashboardPage reads role from URL directly, skips stale prop closure; loadRoleBundle uses auth; no fallback config fetch
This commit is contained in:
parent
f1e46e35e7
commit
188de040ae
3 changed files with 64 additions and 58 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { For, Show, createMemo, createSignal, onMount } from 'solid-js';
|
||||
import { BTN_GHOST, CARD } from '~/components/DashboardShell';
|
||||
import { PROFESSIONAL_ROLE_SET, ROLE_PREFIXES, type RoleKey } from './RoleDashboardShared';
|
||||
import { normalizeRole, PROFESSIONAL_ROLE_SET, ROLE_PREFIXES, type RoleKey } from './RoleDashboardShared';
|
||||
import { readJobSeekerProfile } from '~/lib/job-seeker-custom-data';
|
||||
|
||||
const API = '/api/gateway';
|
||||
|
|
@ -31,13 +31,29 @@ export default function MyDashboardPage(props: Props) {
|
|||
|
||||
const roleLabel = createMemo(() => String(props.roleKey || '').replace(/_/g, ' '));
|
||||
|
||||
const getEffectiveRole = (): RoleKey => {
|
||||
if (typeof window === 'undefined') return props.roleKey;
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const urlRole = urlParams.get('role');
|
||||
if (urlRole) {
|
||||
const normalized = normalizeRole(urlRole);
|
||||
if (normalized) return normalized;
|
||||
}
|
||||
return props.roleKey;
|
||||
};
|
||||
|
||||
const loadData = async () => {
|
||||
const effectiveRole = getEffectiveRole();
|
||||
console.log('[MyDashboardPage] loadData called, effectiveRole:', effectiveRole, 'props.roleKey:', props.roleKey);
|
||||
|
||||
setLoading(true);
|
||||
setErr('');
|
||||
const next: Metric[] = [];
|
||||
|
||||
const roleKey = effectiveRole;
|
||||
|
||||
try {
|
||||
if (props.roleKey === 'COMPANY') {
|
||||
if (roleKey === 'COMPANY') {
|
||||
const [jobsRes, appsRes] = await Promise.all([
|
||||
apiFetch('/api/companies/jobs?page=1&limit=100'),
|
||||
apiFetch('/api/companies/jobs?page=1&limit=1'),
|
||||
|
|
@ -52,7 +68,7 @@ export default function MyDashboardPage(props: Props) {
|
|||
{ title: 'Latest Sync', value: appsRes.ok ? 'Live' : 'Partial', hint: 'Dashboard data status' },
|
||||
);
|
||||
if (!jobsRes.ok && !appsRes.ok) setErr('Some company metrics could not be loaded.');
|
||||
} else if (props.roleKey === 'CUSTOMER') {
|
||||
} else if (roleKey === 'CUSTOMER') {
|
||||
const reqRes = await apiFetch('/api/customers/requirements?page=1&limit=100');
|
||||
const reqJson = await reqRes.json().catch(() => ({}));
|
||||
const reqs = Array.isArray(reqJson?.data) ? reqJson.data : [];
|
||||
|
|
@ -63,7 +79,7 @@ export default function MyDashboardPage(props: Props) {
|
|||
{ title: 'Drafts', value: String(reqs.filter((r: any) => String(r.status || '').toUpperCase() === 'DRAFT').length), hint: 'Not yet submitted' },
|
||||
);
|
||||
if (!reqRes.ok) setErr('Some customer metrics could not be loaded.');
|
||||
} else if (props.roleKey === 'JOB_SEEKER') {
|
||||
} else if (roleKey === 'JOB_SEEKER') {
|
||||
const [jobsRes, appsRes, profile] = await Promise.all([
|
||||
apiFetch('/api/jobseeker/jobs?page=1&limit=100'),
|
||||
apiFetch('/api/jobseeker/applications?page=1&limit=100'),
|
||||
|
|
@ -77,8 +93,8 @@ export default function MyDashboardPage(props: Props) {
|
|||
? (profile.custom_data as Record<string, unknown>)
|
||||
: {};
|
||||
const savedJobs = Array.isArray(customData.saved_jobs) ? customData.saved_jobs : [];
|
||||
const portfolio = (customData.job_seeker_portfolio && typeof customData.job_seeker_portfolio === 'object')
|
||||
? (customData.job_seeker_portfolio as Record<string, unknown>)
|
||||
const portfolio = (customData.job_seeker_portfolio && typeof profile.job_seeker_portfolio === 'object')
|
||||
? (profile.job_seeker_portfolio as Record<string, unknown>)
|
||||
: {};
|
||||
const profileStatus = String(profile?.status || 'NOT_SUBMITTED').replace(/_/g, ' ');
|
||||
const portfolioDone = Boolean(
|
||||
|
|
@ -96,8 +112,8 @@ export default function MyDashboardPage(props: Props) {
|
|||
{ title: 'Portfolio', value: portfolioDone ? 'Complete' : 'Incomplete', hint: 'Education/work/skills sections' },
|
||||
);
|
||||
if (!jobsRes.ok && !appsRes.ok && !profile) setErr('Some job seeker metrics could not be loaded.');
|
||||
} else if (PROFESSIONAL_ROLE_SET.has(props.roleKey)) {
|
||||
const prefix = ROLE_PREFIXES[props.roleKey];
|
||||
} else if (PROFESSIONAL_ROLE_SET.has(roleKey)) {
|
||||
const prefix = ROLE_PREFIXES[roleKey];
|
||||
const [marketRes, reqRes, walletRes] = await Promise.all([
|
||||
apiFetch(`/api/${prefix}/marketplace?page=1&limit=100`),
|
||||
apiFetch(`/api/${prefix}/leads/requests/me?page=1&limit=100`),
|
||||
|
|
@ -133,7 +149,9 @@ export default function MyDashboardPage(props: Props) {
|
|||
}
|
||||
};
|
||||
|
||||
onMount(loadData);
|
||||
onMount(() => {
|
||||
setTimeout(() => loadData(), 0);
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ display: 'grid', gap: '14px', 'max-width': '980px' }}>
|
||||
|
|
|
|||
|
|
@ -42,3 +42,27 @@ export const PROFESSIONAL_ROLE_SET = new Set<RoleKey>([
|
|||
'CATERING_SERVICES',
|
||||
]);
|
||||
|
||||
const ALL_ROLE_KEYS: RoleKey[] = [
|
||||
'COMPANY',
|
||||
'CUSTOMER',
|
||||
'JOB_SEEKER',
|
||||
'PHOTOGRAPHER',
|
||||
'MAKEUP_ARTIST',
|
||||
'TUTOR',
|
||||
'DEVELOPER',
|
||||
'VIDEO_EDITOR',
|
||||
'UGC_CONTENT_CREATOR',
|
||||
'GRAPHIC_DESIGNER',
|
||||
'SOCIAL_MEDIA_MANAGER',
|
||||
'FITNESS_TRAINER',
|
||||
'CATERING_SERVICES',
|
||||
];
|
||||
|
||||
export function normalizeRole(value: string): RoleKey {
|
||||
const up = String(value || '')
|
||||
.trim()
|
||||
.toUpperCase()
|
||||
.replace(/\s+/g, '_');
|
||||
return (ALL_ROLE_KEYS.find((r) => r === up) || 'JOB_SEEKER') as RoleKey;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -437,11 +437,16 @@ function resolveRoleForDashboard(rawRole: string | null | undefined): RoleKey {
|
|||
return normalized;
|
||||
}
|
||||
|
||||
async function fetchJson(path: string): Promise<any | null> {
|
||||
async function fetchJson(path: string, includeAuth = false): Promise<any | null> {
|
||||
try {
|
||||
const isServer = typeof window === "undefined";
|
||||
const target = isServer ? `${SERVER_API_BASE}${path}` : `${API_GATEWAY}${path}`;
|
||||
const res = await fetch(target, { credentials: "include" });
|
||||
const headers: Record<string, string> = { credentials: "include" } as any;
|
||||
if (includeAuth) {
|
||||
const token = typeof getToken === "function" ? getToken() : null;
|
||||
if (token) headers["Authorization"] = `Bearer ${token}`;
|
||||
}
|
||||
const res = await fetch(target, headers);
|
||||
if (!res.ok) return null;
|
||||
return await res.json();
|
||||
} catch {
|
||||
|
|
@ -449,12 +454,12 @@ async function fetchJson(path: string): Promise<any | null> {
|
|||
}
|
||||
}
|
||||
|
||||
async function loadRoleBundle(role: RoleKey): Promise<RuntimeBundle | null> {
|
||||
if (typeof window === "undefined") {
|
||||
return null;
|
||||
}
|
||||
const runtime = await fetchJson("/api/runtime-config");
|
||||
if (runtime) {
|
||||
async function loadRoleBundle(role: RoleKey): Promise<RuntimeBundle | null> {
|
||||
if (typeof window === "undefined") {
|
||||
return null;
|
||||
}
|
||||
const runtime = await fetchJson("/api/runtime-config", true);
|
||||
if (!runtime) return null;
|
||||
const runtimeRole = normalizeRole(String(runtime?.role || runtime?.user?.active_role || role));
|
||||
const runtimeRenderMode = String(
|
||||
runtime?.dashboard_config?.render_mode ?? runtime?.render_mode ?? ""
|
||||
|
|
@ -494,47 +499,6 @@ async function loadRoleBundle(role: RoleKey): Promise<RuntimeBundle | 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<string, unknown> | 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);
|
||||
const configRenderMode = String((config as any)?.render_mode || "").toLowerCase();
|
||||
return {
|
||||
role,
|
||||
status: payload?.is_active === false ? "INACTIVE" : "ACTIVE",
|
||||
sidebarItems,
|
||||
tabs,
|
||||
widgets,
|
||||
fields,
|
||||
source: "dashboard-config",
|
||||
renderMode: configRenderMode === "preview" ? "preview" : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function mergeSidebar(
|
||||
role: RoleKey,
|
||||
runtimeSidebar: string[],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue