feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
import { Component, Show, createEffect, For, createSignal, onMount } from 'solid-js';
|
|
|
|
|
import { useNavigate, A, useSearchParams } from '@solidjs/router';
|
|
|
|
|
import { authState, logout, switchRole, bootstrapAuth, setMockRuntimeConfig } from '~/lib/auth';
|
2026-03-19 00:58:29 +01:00
|
|
|
import { shouldShowRoleSwitcher } from '~/lib/auth-flow';
|
2026-03-19 02:35:19 +01:00
|
|
|
import {
|
|
|
|
|
getRoleTourStorageKey,
|
|
|
|
|
getWelcomeTourStorageKey,
|
|
|
|
|
pickGuidedTour,
|
|
|
|
|
readSeenRoleTours,
|
|
|
|
|
WELCOME_TOUR_VALUE,
|
|
|
|
|
writeSeenRoleTours,
|
|
|
|
|
} from '~/lib/guided-tour';
|
|
|
|
|
import type { GuidedTourKind } from '~/lib/guided-tour';
|
|
|
|
|
import {
|
|
|
|
|
type GuidedTourStep,
|
|
|
|
|
resolveRoleApprovedTourSteps,
|
|
|
|
|
resolveWelcomeTourSteps,
|
|
|
|
|
} from '~/lib/guided-tour-content';
|
2026-03-17 20:42:55 +01:00
|
|
|
|
|
|
|
|
// ── Icons (inline SVGs for zero deps) ─────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
const IconDashboard = () => (
|
|
|
|
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
|
|
|
<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/>
|
|
|
|
|
<rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
const IconJobs = () => (
|
|
|
|
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
|
|
|
<rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2"/>
|
|
|
|
|
<line x1="12" y1="12" x2="12" y2="17"/><line x1="9" y1="14.5" x2="15" y2="14.5"/>
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
const IconBell = () => (
|
|
|
|
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
|
|
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
const IconSettings = () => (
|
|
|
|
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
|
|
|
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
const IconLogout = () => (
|
|
|
|
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
|
|
|
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/>
|
|
|
|
|
<line x1="21" y1="12" x2="9" y2="12"/>
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
const IconCompass = () => (
|
|
|
|
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
|
|
|
<circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/>
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
const IconPortfolio = () => (
|
|
|
|
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
|
|
|
<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/>
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
const IconServices = () => (
|
|
|
|
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
|
|
|
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
const IconWallet = () => (
|
|
|
|
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
|
|
|
<path d="M20 12V8H6a2 2 0 0 1-2-2c0-1.1.9-2 2-2h12v4"/><path d="M4 6v12c0 1.1.9 2 2 2h14v-4"/><path d="M18 12a2 2 0 0 0-2 2c0 1.1.9 2 2 2h4v-4h-4z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
2026-03-17 20:42:55 +01:00
|
|
|
|
|
|
|
|
// ── Module → nav item mapping ─────────────────────────────────────────────────
|
|
|
|
|
|
2026-03-25 22:13:11 +01:00
|
|
|
const MODULE_NAV_MAP: Record<string, { label: string; href: string; icon: Component; tourId: string; order?: number }> = {
|
|
|
|
|
// Next.js role-dashboard module keys
|
|
|
|
|
dashboard: { label: 'Dashboard', href: '/users/dashboard', icon: IconDashboard, tourId: 'dashboard', order: 1 },
|
|
|
|
|
onboarding: { label: 'Get Started', href: '/users/onboarding', icon: IconCompass, tourId: 'onboarding', order: 2 },
|
|
|
|
|
role_selection: { label: 'Choose Your Role', href: '/users/onboarding/role-selection', icon: IconCompass, tourId: 'role-selection', order: 3 },
|
|
|
|
|
profile: { label: 'Profile', href: '/users/profile', icon: IconSettings, tourId: 'profile', order: 4 },
|
|
|
|
|
leads: { label: 'Leads', href: '/users/leads', icon: IconJobs, tourId: 'requests', order: 5 },
|
|
|
|
|
job_postings: { label: 'Job Postings', href: '/companies/job-postings', icon: IconJobs, tourId: 'jobs', order: 6 },
|
|
|
|
|
applications: { label: 'Applications', href: '/companies/applications', icon: IconJobs, tourId: 'applications', order: 7 },
|
|
|
|
|
portfolio: { label: 'Portfolio', href: '/users/professional/portfolio', icon: IconPortfolio, tourId: 'portfolio', order: 8 },
|
|
|
|
|
verification: { label: 'Verification Status', href: '/users/verification-status', icon: IconSettings, tourId: 'verification', order: 9 },
|
|
|
|
|
tracecoins: { label: 'Tracecoins', href: '/companies/tracecoins', icon: IconWallet, tourId: 'wallet', order: 10 },
|
|
|
|
|
feedback: { label: 'Feedback', href: '/companies/feedback', icon: IconBell, tourId: 'support', order: 11 },
|
|
|
|
|
notifications: { label: 'Notifications', href: '/users/notifications', icon: IconBell, tourId: 'notifications', order: 12 },
|
|
|
|
|
support: { label: 'Support', href: '/users/support', icon: IconBell, tourId: 'support', order: 13 },
|
|
|
|
|
settings: { label: 'Settings', href: '/users/settings', icon: IconSettings, tourId: 'settings', order: 14 },
|
|
|
|
|
|
|
|
|
|
// Existing Solid module keys (backward compatibility)
|
|
|
|
|
jobs: { label: 'Jobs', href: '/dashboard/jobs', icon: IconJobs, tourId: 'jobs' },
|
|
|
|
|
my_applications: { label: 'My Applications', href: '/dashboard/applications', icon: IconJobs, tourId: 'applications' },
|
|
|
|
|
browse_jobs: { label: 'Browse Jobs', href: '/dashboard/jobs', icon: IconJobs, tourId: 'jobs' },
|
|
|
|
|
requirements: { label: 'Requirements', href: '/dashboard/requirements', icon: IconJobs, tourId: 'requirements' },
|
|
|
|
|
marketplace: { label: 'Marketplace', href: '/dashboard/marketplace', icon: IconCompass, tourId: 'marketplace' },
|
|
|
|
|
services: { label: 'Services', href: '/dashboard/services', icon: IconServices, tourId: 'services' },
|
|
|
|
|
wallet: { label: 'Wallet', href: '/dashboard/wallet', icon: IconWallet, tourId: 'wallet' },
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
|
|
|
|
|
// Uppercase fallbacks
|
2026-03-25 22:13:11 +01:00
|
|
|
COMPANY_DASHBOARD: { label: 'Dashboard', href: '/dashboard', icon: IconDashboard, tourId: 'dashboard' },
|
|
|
|
|
JOBSEEKER_DASHBOARD: { label: 'Dashboard', href: '/dashboard', icon: IconDashboard, tourId: 'dashboard' },
|
|
|
|
|
CUSTOMER_DASHBOARD: { label: 'Dashboard', href: '/dashboard', icon: IconDashboard, tourId: 'dashboard' },
|
|
|
|
|
PROFESSIONAL_DASHBOARD: { label: 'Dashboard', href: '/dashboard', icon: IconDashboard, tourId: 'dashboard' },
|
|
|
|
|
JOBS: { label: 'Jobs', href: '/dashboard/jobs', icon: IconJobs, tourId: 'jobs' },
|
|
|
|
|
APPLICATIONS: { label: 'Applications', href: '/dashboard/applications', icon: IconJobs, tourId: 'applications' },
|
|
|
|
|
REQUIREMENTS: { label: 'Requirements', href: '/dashboard/requirements', icon: IconJobs, tourId: 'requirements' },
|
|
|
|
|
MARKETPLACE: { label: 'Marketplace', href: '/dashboard/marketplace', icon: IconCompass, tourId: 'marketplace' },
|
|
|
|
|
MY_REQUESTS: { label: 'Leads', href: '/dashboard/requests', icon: IconJobs, tourId: 'requests' },
|
|
|
|
|
ACCEPTED_LEADS: { label: 'Accepted Leads', href: '/dashboard/leads/accepted',icon: IconJobs, tourId: 'leads' },
|
|
|
|
|
PORTFOLIO: { label: 'Portfolio', href: '/dashboard/portfolio', icon: IconPortfolio, tourId: 'portfolio' },
|
|
|
|
|
SERVICES: { label: 'Services', href: '/dashboard/services', icon: IconServices, tourId: 'services' },
|
|
|
|
|
WALLET: { label: 'Wallet', href: '/dashboard/wallet', icon: IconWallet, tourId: 'wallet' },
|
|
|
|
|
NOTIFICATIONS: { label: 'Notifications', href: '/dashboard/notifications', icon: IconBell, tourId: 'notifications' },
|
|
|
|
|
SETTINGS: { label: 'Settings', href: '/dashboard/settings', icon: IconSettings, tourId: 'settings' },
|
|
|
|
|
EXPLORE_NXTGAUGE: { label: 'Explore', href: '/dashboard/explore', icon: IconCompass, tourId: 'explore' },
|
2026-03-17 20:42:55 +01:00
|
|
|
};
|
|
|
|
|
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
// ── Spotlight Tour Overlay ────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
function SpotlightOverlay(props: {
|
|
|
|
|
step: GuidedTourStep;
|
|
|
|
|
stepIndex: number;
|
|
|
|
|
total: number;
|
|
|
|
|
onNext: () => void;
|
|
|
|
|
onBack: () => void;
|
|
|
|
|
onSkip: () => void;
|
|
|
|
|
}) {
|
|
|
|
|
const PAD = 10;
|
|
|
|
|
const TW = 300; // tooltip width
|
|
|
|
|
const TH = 230; // tooltip approx height
|
|
|
|
|
|
|
|
|
|
type Rect = { x: number; y: number; w: number; h: number };
|
|
|
|
|
const [rect, setRect] = createSignal<Rect | null>(null);
|
|
|
|
|
|
|
|
|
|
// Re-measure spotlight target whenever the step changes
|
|
|
|
|
createEffect(() => {
|
|
|
|
|
const target = props.step.target;
|
|
|
|
|
void props.stepIndex; // track index changes too
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
if (!target) { setRect(null); return; }
|
|
|
|
|
const el = document.querySelector(target);
|
|
|
|
|
if (!el) { setRect(null); return; }
|
|
|
|
|
const r = el.getBoundingClientRect();
|
|
|
|
|
setRect({
|
|
|
|
|
x: Math.max(0, r.left - PAD),
|
|
|
|
|
y: Math.max(0, r.top - PAD),
|
|
|
|
|
w: r.width + PAD * 2,
|
|
|
|
|
h: r.height + PAD * 2,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const tooltipStyle = () => {
|
|
|
|
|
const r = rect();
|
|
|
|
|
const vw = window.innerWidth;
|
|
|
|
|
const vh = window.innerHeight;
|
|
|
|
|
if (!r) return { top: `${(vh - TH) / 2}px`, left: `${(vw - TW) / 2}px` };
|
|
|
|
|
const clampY = (y: number) => Math.max(12, Math.min(y, vh - TH - 12));
|
|
|
|
|
const clampX = (x: number) => Math.max(12, Math.min(x, vw - TW - 12));
|
|
|
|
|
// Right
|
|
|
|
|
if (r.x + r.w + TW + 20 <= vw)
|
|
|
|
|
return { top: `${clampY(r.y + r.h / 2 - TH / 2)}px`, left: `${r.x + r.w + 20}px` };
|
|
|
|
|
// Below
|
|
|
|
|
if (r.y + r.h + TH + 20 <= vh)
|
|
|
|
|
return { top: `${r.y + r.h + 20}px`, left: `${clampX(r.x + r.w / 2 - TW / 2)}px` };
|
|
|
|
|
// Above
|
|
|
|
|
if (r.y - TH - 20 >= 0)
|
|
|
|
|
return { top: `${r.y - TH - 20}px`, left: `${clampX(r.x + r.w / 2 - TW / 2)}px` };
|
|
|
|
|
// Left
|
|
|
|
|
return { top: `${clampY(r.y + r.h / 2 - TH / 2)}px`, left: `${clampX(r.x - TW - 20)}px` };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const progress = () => Math.round(((props.stepIndex + 1) / props.total) * 100);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{/* ── Dark overlay with spotlight cutout ── */}
|
|
|
|
|
<svg
|
|
|
|
|
style={{
|
|
|
|
|
position: 'fixed', inset: '0',
|
|
|
|
|
width: '100vw', height: '100vh',
|
|
|
|
|
'z-index': '9000', 'pointer-events': 'none',
|
|
|
|
|
transition: 'opacity 0.2s',
|
|
|
|
|
}}
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
|
|
|
>
|
|
|
|
|
<defs>
|
|
|
|
|
<mask id="tour-spotlight-mask">
|
|
|
|
|
<rect width="100%" height="100%" fill="white" />
|
|
|
|
|
<Show when={rect()}>
|
|
|
|
|
<rect
|
|
|
|
|
fill="black"
|
|
|
|
|
x={rect()!.x} y={rect()!.y}
|
|
|
|
|
width={rect()!.w} height={rect()!.h}
|
|
|
|
|
rx="10"
|
|
|
|
|
/>
|
|
|
|
|
</Show>
|
|
|
|
|
</mask>
|
|
|
|
|
</defs>
|
|
|
|
|
<rect width="100%" height="100%" fill="rgba(0,0,0,0.72)" mask="url(#tour-spotlight-mask)" />
|
|
|
|
|
{/* Orange border around spotlight */}
|
|
|
|
|
<Show when={rect()}>
|
|
|
|
|
<rect
|
|
|
|
|
x={rect()!.x} y={rect()!.y}
|
|
|
|
|
width={rect()!.w} height={rect()!.h}
|
|
|
|
|
rx="10" fill="none"
|
2026-03-25 22:13:11 +01:00
|
|
|
stroke="#fd6116" stroke-width="2.5" opacity="0.9"
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
/>
|
|
|
|
|
</Show>
|
|
|
|
|
</svg>
|
|
|
|
|
|
|
|
|
|
{/* ── Tooltip card ── */}
|
|
|
|
|
<div
|
|
|
|
|
role="dialog"
|
|
|
|
|
aria-modal="true"
|
|
|
|
|
aria-label="Guided Tour"
|
|
|
|
|
style={{
|
|
|
|
|
position: 'fixed',
|
|
|
|
|
'z-index': '9001',
|
|
|
|
|
width: `${TW}px`,
|
|
|
|
|
background: '#ffffff',
|
|
|
|
|
'border-radius': '16px',
|
|
|
|
|
padding: '22px 24px 20px',
|
|
|
|
|
'box-shadow': '0 24px 64px rgba(0,0,0,0.28), 0 0 0 1px rgba(0,0,0,0.06)',
|
|
|
|
|
...tooltipStyle(),
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{/* Header */}
|
|
|
|
|
<div style={{ display: 'flex', 'align-items': 'center', 'justify-content': 'space-between', 'margin-bottom': '10px' }}>
|
|
|
|
|
<span style={{
|
|
|
|
|
'font-size': '10px', 'font-weight': '800', 'text-transform': 'uppercase',
|
2026-03-25 22:13:11 +01:00
|
|
|
color: '#fd6116', 'letter-spacing': '0.1em',
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
}}>
|
|
|
|
|
Guided Tour
|
|
|
|
|
</span>
|
|
|
|
|
<span style={{ 'font-size': '11px', color: '#94a3b8', 'font-weight': '600' }}>
|
|
|
|
|
{props.stepIndex + 1} / {props.total}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Title */}
|
|
|
|
|
<h3 style={{
|
|
|
|
|
margin: '0 0 8px', 'font-size': '15px', 'font-weight': '800',
|
|
|
|
|
color: '#0f172a', 'line-height': '1.35',
|
|
|
|
|
}}>
|
|
|
|
|
{props.step.title}
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
{/* Body */}
|
|
|
|
|
<p style={{
|
|
|
|
|
margin: '0 0 16px', 'font-size': '13px',
|
|
|
|
|
color: '#475569', 'line-height': '1.65',
|
|
|
|
|
}}>
|
|
|
|
|
{props.step.body}
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
{/* Progress bar */}
|
|
|
|
|
<div style={{
|
|
|
|
|
height: '3px', background: '#f1f5f9',
|
|
|
|
|
'border-radius': '2px', 'margin-bottom': '16px', overflow: 'hidden',
|
|
|
|
|
}}>
|
|
|
|
|
<div style={{
|
2026-03-25 22:13:11 +01:00
|
|
|
height: '100%', background: '#fd6116', 'border-radius': '2px',
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
width: `${progress()}%`, transition: 'width 0.35s ease',
|
|
|
|
|
}} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Buttons */}
|
|
|
|
|
<div style={{ display: 'flex', gap: '8px', 'align-items': 'center' }}>
|
|
|
|
|
<button
|
|
|
|
|
style={{
|
|
|
|
|
padding: '8px 12px', border: '1.5px solid #e2e8f0', 'border-radius': '8px',
|
|
|
|
|
background: 'white', cursor: 'pointer', 'font-size': '12px', color: '#64748b',
|
|
|
|
|
'font-weight': '500',
|
|
|
|
|
}}
|
|
|
|
|
onClick={props.onSkip}
|
|
|
|
|
>
|
|
|
|
|
Skip
|
|
|
|
|
</button>
|
|
|
|
|
<Show when={props.stepIndex > 0}>
|
|
|
|
|
<button
|
|
|
|
|
style={{
|
|
|
|
|
padding: '8px 14px', border: '1.5px solid #e2e8f0', 'border-radius': '8px',
|
|
|
|
|
background: 'white', cursor: 'pointer', 'font-size': '12px', color: '#0f172a',
|
|
|
|
|
'font-weight': '600',
|
|
|
|
|
}}
|
|
|
|
|
onClick={props.onBack}
|
|
|
|
|
>
|
|
|
|
|
← Back
|
|
|
|
|
</button>
|
|
|
|
|
</Show>
|
|
|
|
|
<button
|
|
|
|
|
style={{
|
2026-03-25 22:13:11 +01:00
|
|
|
flex: '1', padding: '9px 14px', background: '#fd6116', color: '#ffffff',
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
border: 'none', 'border-radius': '8px', cursor: 'pointer',
|
|
|
|
|
'font-weight': '700', 'font-size': '13px', transition: 'opacity 0.15s',
|
|
|
|
|
}}
|
|
|
|
|
onClick={props.onNext}
|
|
|
|
|
>
|
|
|
|
|
{props.stepIndex >= props.total - 1 ? '✓ Done' : 'Next →'}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 20:42:55 +01:00
|
|
|
// ── Dashboard Layout ──────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export default function DashboardLayout(props: { children: any }) {
|
|
|
|
|
const navigate = useNavigate();
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
const [searchParams] = useSearchParams();
|
2026-03-19 00:53:36 +01:00
|
|
|
const [switchingRole, setSwitchingRole] = createSignal(false);
|
2026-03-19 02:35:19 +01:00
|
|
|
const [tourKind, setTourKind] = createSignal<GuidedTourKind | null>(null);
|
|
|
|
|
const [tourStepIndex, setTourStepIndex] = createSignal(0);
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
const [authReady, setAuthReady] = createSignal(false);
|
|
|
|
|
|
|
|
|
|
// Restore session or inject mock preview config
|
|
|
|
|
onMount(async () => {
|
|
|
|
|
const preview = String(searchParams._preview || '').toUpperCase();
|
|
|
|
|
if (preview) {
|
|
|
|
|
setMockRuntimeConfig(preview);
|
|
|
|
|
} else {
|
|
|
|
|
await bootstrapAuth();
|
|
|
|
|
}
|
|
|
|
|
setAuthReady(true);
|
|
|
|
|
});
|
2026-03-17 20:42:55 +01:00
|
|
|
|
|
|
|
|
createEffect(() => {
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
if (!authReady()) return;
|
2026-03-17 20:42:55 +01:00
|
|
|
const s = authState();
|
|
|
|
|
if (s.runtime_config?.onboarding_required) {
|
|
|
|
|
navigate('/onboarding', { replace: true });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const rc = () => authState().runtime_config;
|
2026-03-19 00:53:36 +01:00
|
|
|
const roleOptions = () => rc()?.user?.roles ?? [];
|
|
|
|
|
const activeRole = () => rc()?.user?.active_role ?? rc()?.role ?? '';
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
|
2026-03-19 02:35:19 +01:00
|
|
|
const activeTourSteps = (): GuidedTourStep[] => {
|
|
|
|
|
const kind = tourKind();
|
|
|
|
|
if (!kind) return [];
|
|
|
|
|
if (kind === 'welcome') return resolveWelcomeTourSteps(rc()?.guided_tours ?? null);
|
|
|
|
|
return resolveRoleApprovedTourSteps(activeRole(), rc()?.guided_tours ?? null);
|
|
|
|
|
};
|
|
|
|
|
const tourStep = () => activeTourSteps()[tourStepIndex()];
|
2026-03-17 20:42:55 +01:00
|
|
|
|
|
|
|
|
const navItems = () => {
|
2026-03-25 22:13:11 +01:00
|
|
|
const role = String(activeRole() || rc()?.role || '').toUpperCase();
|
|
|
|
|
const moduleSet = new Set(
|
|
|
|
|
(Array.isArray(rc()?.enabled_modules) ? rc()!.enabled_modules : [])
|
|
|
|
|
.map((moduleKey) => String(moduleKey || '').toLowerCase()),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (role !== 'USER') {
|
|
|
|
|
moduleSet.add('dashboard');
|
2026-03-19 00:30:33 +01:00
|
|
|
}
|
2026-03-25 22:13:11 +01:00
|
|
|
|
|
|
|
|
const modules = Array.from(moduleSet);
|
2026-03-17 20:42:55 +01:00
|
|
|
return modules
|
2026-03-25 22:13:11 +01:00
|
|
|
.map((m) => {
|
|
|
|
|
const base = MODULE_NAV_MAP[m];
|
|
|
|
|
if (!base) return null;
|
|
|
|
|
|
|
|
|
|
// Match Next.js role override: customer "leads" is "Post Requirement"
|
|
|
|
|
if (m === 'leads' && role === 'CUSTOMER') {
|
|
|
|
|
return { ...base, label: 'Post Requirement', href: '/users/requirements/new', tourId: 'requirements' };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Match Next.js company route overrides.
|
|
|
|
|
if (role === 'COMPANY') {
|
|
|
|
|
if (m === 'profile') return { ...base, href: '/companies/profile' };
|
|
|
|
|
if (m === 'support') return { ...base, href: '/companies/support' };
|
|
|
|
|
if (m === 'settings') return { ...base, href: '/companies/settings' };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return base;
|
|
|
|
|
})
|
|
|
|
|
.filter((item): item is NonNullable<typeof item> => Boolean(item))
|
|
|
|
|
.sort((left, right) => (left.order ?? 999) - (right.order ?? 999));
|
2026-03-17 20:42:55 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function handleLogout() {
|
|
|
|
|
await logout();
|
|
|
|
|
navigate('/auth/login', { replace: true });
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 00:53:36 +01:00
|
|
|
async function handleRoleSwitch(nextRole: string) {
|
|
|
|
|
const normalized = String(nextRole || '').trim().toUpperCase();
|
|
|
|
|
if (!normalized || normalized === activeRole()) return;
|
|
|
|
|
setSwitchingRole(true);
|
|
|
|
|
try {
|
|
|
|
|
await switchRole(normalized);
|
|
|
|
|
navigate('/dashboard', { replace: true });
|
|
|
|
|
} finally {
|
|
|
|
|
setSwitchingRole(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 02:35:19 +01:00
|
|
|
function finishTour() {
|
|
|
|
|
if (typeof window === 'undefined') return;
|
|
|
|
|
const userId = rc()?.user?.id;
|
|
|
|
|
if (!userId || !tourKind()) {
|
|
|
|
|
setTourKind(null);
|
|
|
|
|
setTourStepIndex(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (tourKind() === 'welcome') {
|
|
|
|
|
window.localStorage.setItem(getWelcomeTourStorageKey(userId), WELCOME_TOUR_VALUE);
|
|
|
|
|
} else {
|
|
|
|
|
const key = getRoleTourStorageKey(userId);
|
|
|
|
|
const seen = readSeenRoleTours(window.localStorage.getItem(key));
|
|
|
|
|
seen.add(activeRole());
|
|
|
|
|
window.localStorage.setItem(key, writeSeenRoleTours(seen));
|
|
|
|
|
}
|
|
|
|
|
setTourKind(null);
|
|
|
|
|
setTourStepIndex(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function nextTourStep() {
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
if (tourStepIndex() >= activeTourSteps().length - 1) { finishTour(); return; }
|
|
|
|
|
setTourStepIndex(i => i + 1);
|
2026-03-19 02:35:19 +01:00
|
|
|
}
|
|
|
|
|
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
// Auto-start guided tour when runtime config loads
|
2026-03-19 02:35:19 +01:00
|
|
|
createEffect(() => {
|
|
|
|
|
if (typeof window === 'undefined') return;
|
|
|
|
|
const runtime = rc();
|
|
|
|
|
const userId = runtime?.user?.id;
|
|
|
|
|
if (!runtime || !userId) return;
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
const welcomeSeen = window.localStorage.getItem(getWelcomeTourStorageKey(userId)) === WELCOME_TOUR_VALUE;
|
|
|
|
|
const seenRoles = readSeenRoleTours(window.localStorage.getItem(getRoleTourStorageKey(userId)));
|
|
|
|
|
const next = pickGuidedTour({
|
2026-03-19 02:35:19 +01:00
|
|
|
userId,
|
|
|
|
|
activeRole: runtime?.user?.active_role ?? runtime?.role,
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
welcomeTourSeen: welcomeSeen,
|
|
|
|
|
seenRoleTours: seenRoles,
|
2026-03-19 02:35:19 +01:00
|
|
|
});
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
if (next && next !== tourKind()) {
|
|
|
|
|
setTourKind(next);
|
2026-03-19 02:35:19 +01:00
|
|
|
setTourStepIndex(0);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-17 20:42:55 +01:00
|
|
|
return (
|
|
|
|
|
<div class="dashboard-shell">
|
|
|
|
|
{/* ── Sidebar ── */}
|
|
|
|
|
<aside class="sidebar">
|
|
|
|
|
<div class="sidebar-logo">
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
<A href="/dashboard"><span class="logo-text">NXTGAUGE</span></A>
|
2026-03-17 20:42:55 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="sidebar-role-badge">
|
|
|
|
|
<span class="role-badge">{rc()?.user?.active_role ?? 'Loading...'}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<nav class="sidebar-nav">
|
|
|
|
|
<For each={navItems()}>
|
|
|
|
|
{(item) => (
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
<A
|
|
|
|
|
href={item.href}
|
|
|
|
|
class="nav-item"
|
|
|
|
|
activeClass="nav-item-active"
|
|
|
|
|
data-tour-id={item.tourId}
|
|
|
|
|
>
|
2026-03-17 20:42:55 +01:00
|
|
|
<item.icon />
|
|
|
|
|
<span>{item.label}</span>
|
|
|
|
|
</A>
|
|
|
|
|
)}
|
|
|
|
|
</For>
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
<div class="sidebar-footer">
|
|
|
|
|
<button class="nav-item nav-item-logout" onClick={handleLogout}>
|
|
|
|
|
<IconLogout />
|
|
|
|
|
<span>Logout</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
{/* ── Main Content ── */}
|
|
|
|
|
<div class="dashboard-main">
|
|
|
|
|
<header class="dashboard-topbar">
|
|
|
|
|
<div class="topbar-title"> </div>
|
|
|
|
|
<div class="topbar-right">
|
2026-03-19 00:58:29 +01:00
|
|
|
<Show when={shouldShowRoleSwitcher(roleOptions())}>
|
2026-03-19 00:53:36 +01:00
|
|
|
<select
|
|
|
|
|
class="input"
|
|
|
|
|
style={{ width: '210px', 'font-size': '13px', padding: '8px 10px' }}
|
|
|
|
|
value={activeRole()}
|
|
|
|
|
disabled={switchingRole()}
|
|
|
|
|
onChange={(e) => handleRoleSwitch(e.currentTarget.value)}
|
|
|
|
|
title="Switch your dashboard view"
|
|
|
|
|
>
|
|
|
|
|
<For each={roleOptions()}>
|
|
|
|
|
{(role) => <option value={role}>{role.replaceAll('_', ' ')}</option>}
|
|
|
|
|
</For>
|
|
|
|
|
</select>
|
|
|
|
|
</Show>
|
|
|
|
|
<A href="/choose-role" class="btn btn-sm" style={{ 'text-decoration': 'none' }}>
|
|
|
|
|
Choose What You Need
|
|
|
|
|
</A>
|
2026-03-17 20:42:55 +01:00
|
|
|
<A href="/dashboard/notifications" class="topbar-icon-btn" title="Notifications">
|
|
|
|
|
<IconBell />
|
|
|
|
|
</A>
|
|
|
|
|
<div class="topbar-user">
|
|
|
|
|
<span class="topbar-name">{rc()?.user?.full_name ?? 'User'}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<main class="dashboard-content">
|
|
|
|
|
<Show when={rc()} fallback={<div class="loading-spinner">Loading...</div>}>
|
|
|
|
|
{props.children}
|
|
|
|
|
</Show>
|
|
|
|
|
</main>
|
|
|
|
|
</div>
|
2026-03-19 02:35:19 +01:00
|
|
|
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
{/* ── Spotlight Guided Tour ── */}
|
2026-03-19 02:35:19 +01:00
|
|
|
<Show when={tourKind() && tourStep()}>
|
feat(dashboard-parity): DashboardLayout runtime-config nav, auth hardening, onboarding UX
- DashboardLayout.tsx: fully runtime-config driven sidebar nav using
MODULE_NAV_MAP, role switcher, guided tour spotlight overlay, responsive
mobile drawer
- auth.ts: hardened JWT access token handling, httpOnly cookie refresh flow
- guided-tour-content.ts: expanded per-role tour steps for all modules
- gateway.ts: improved proxy with Set-Cookie forwarding for refresh token
- onboarding/complete.ts + schema.ts: refined onboarding completion flow
- login.ts + register.ts: cleaner error handling and response forwarding
- dashboard/index.tsx: role-based KPI cards from runtime-config
- jobs/index.tsx: status filters, post job action gated by requiresJobApproval
- marketplace/index.tsx + [id].tsx: leads browsing with tracecoin hold display
- requirements/index.tsx + [id].tsx: post requirement with profession-specific
conditional fields, budget/timeline/mode, resubmit support
- portfolio/index.tsx: CRUD for photographer portfolio projects
- services/index.tsx: service management for marketplace professionals
- applications/index.tsx: jobseeker applied jobs list
- notifications.tsx: all/unread tabs, mark read, deep link routing
- settings.tsx: change password form
- wallet/index.tsx + ledger.tsx: tracecoin balance and transaction history
- onboarding.tsx: multi-step onboarding form with profession branching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:45:51 +01:00
|
|
|
<SpotlightOverlay
|
|
|
|
|
step={tourStep()!}
|
|
|
|
|
stepIndex={tourStepIndex()}
|
|
|
|
|
total={activeTourSteps().length}
|
|
|
|
|
onNext={nextTourStep}
|
|
|
|
|
onBack={() => setTourStepIndex(i => Math.max(0, i - 1))}
|
|
|
|
|
onSkip={finishTour}
|
|
|
|
|
/>
|
2026-03-19 02:35:19 +01:00
|
|
|
</Show>
|
2026-03-17 20:42:55 +01:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|