diff --git a/docs/Nxtgauge-End-to-End-Testing-Blueprint.md b/docs/Nxtgauge-End-to-End-Testing-Blueprint.md index daa21b5..7dbb18d 100644 --- a/docs/Nxtgauge-End-to-End-Testing-Blueprint.md +++ b/docs/Nxtgauge-End-to-End-Testing-Blueprint.md @@ -1,9 +1,11 @@ +> Policy Update (2026-04-05): External onboarding management is deprecated and removed. After sign-up/login, users are routed directly by role key to role-specific dashboard/runtime modules. Any legacy references to /onboarding, /choose-role onboarding flows, onboarding schema management, or fallback data strategies should be treated as obsolete. Runtime config and canonical backend APIs are the only source of truth. + # Nxtgauge End-to-End Testing Blueprint ## 1. Scope - Validate full lifecycle for external users from sign-up to role approval and role-specific module insertion. - Validate admin-side verification and approval pipelines and state propagation into management modules. -- Validate runtime-config-driven onboarding/dashboard behavior and guard/redirect behavior. +- Validate runtime-config-driven role-key dashboard routing and guard/redirect behavior. - Validate notification and marketplace flows (customer requirement posting, professional discovery). - Validate UI parity and visual regressions for management modules using Playwright + pixelmatch + Storybook + VisBug. - Validate frontend (`vitest`, Playwright) and backend (`cargo test --workspace`) quality gates. @@ -16,7 +18,7 @@ - role-specific table insertion, - notification event. - Multi-role users are supported and role visibility must remain isolated by role/module. -- Runtime config must not silently mask missing onboarding/dashboard config. +- Runtime config must not silently mask missing role/dashboard config. ## 3. Environments and Preconditions - Frontend admin app: `/Users/ashwin/workspace/nxtgauge-admin-solid` @@ -27,13 +29,13 @@ - Preconditions: - `npm install` completed in admin repo. - Rust toolchain + dependencies available for backend repo. - - Seed users/roles configured in backend DB (or fallback data strategy used in UI where applicable). + - Seed users/roles configured in backend DB (no UI fallback data strategy). - Admin preview mode supported with `?_preview=1` for deterministic UI checks. ## 4. Test Data Matrix - External identities: - User without role. - - User with pending role onboarding. + - User with assigned role key and pending approval. - User with verified/pending approval role. - User with approved active role. - Multi-role user (2+ roles). @@ -54,10 +56,10 @@ - external identity blocked from admin login. - internal identity allowed and redirected to admin. - external-resolved session redirected to `/login?from=...`. -2. Onboarding: - - role-aware onboarding route loads via runtime config. - - missing config renders explicit error state. - - onboarding submissions persist and move to verification state. +2. Role Runtime: + - role-key based dashboard route resolves via runtime config. + - missing runtime config renders explicit error state. + - role state persists and moves to verification/approval state. 3. Dashboard: - role-specific sidebar and tabs render correctly. - guarded routes reject wrong portal/session. @@ -106,12 +108,12 @@ - Assertions: - lifecycle transitions emit expected payload shape/state. - role key propagation remains consistent across services. - - no silent fallback on missing config. + - no silent fallback on missing runtime config. ## 8. Database Validation Plan - Validate tables/events after each phase: - user record creation at sign-up. - - onboarding row/state creation. + - role state creation/update. - verification record creation/update. - approval decision and role activation state. - role-specific module table insertion on approval only. @@ -196,4 +198,3 @@ - Runtime config partial failures causing invisible fallback behavior. - Backend endpoint shape drift (`users`, `external-users`, `dashboard-config`) causing UI empty states. - Visual regressions in shared table controls/icons across management modules. - diff --git a/src/components/AdminShell.tsx b/src/components/AdminShell.tsx index 4d76f9a..d3153f8 100644 --- a/src/components/AdminShell.tsx +++ b/src/components/AdminShell.tsx @@ -20,8 +20,6 @@ const PAGE_TITLES: Array<{ prefix: string; label: string; exact?: boolean }> = [ { prefix: '/admin/roles', label: 'Internal Role Management' }, { prefix: '/admin/employees', label: 'Employee Management' }, { prefix: '/admin/external-roles', label: 'External Role Management' }, - { prefix: '/admin/onboarding-management', label: 'Onboarding Management' }, - { prefix: '/admin/onboarding-schemas', label: 'Onboarding Management' }, { prefix: '/admin/internal-dashboard-management', label: 'Internal Dashboard Management' }, { prefix: '/admin/external-dashboard-management', label: 'External Dashboard Management' }, { prefix: '/admin/role-ui-configs', label: 'External Dashboard Management' }, @@ -73,8 +71,6 @@ const ROUTE_MODULE_KEYS: Array<{ prefix: string; keys: string[] }> = [ { prefix: '/admin/roles', keys: ['INTERNAL_ROLE_MANAGEMENT', 'ROLES'] }, { prefix: '/admin/employees', keys: ['EMPLOYEE_MANAGEMENT', 'EMPLOYEES'] }, { prefix: '/admin/external-roles', keys: ['EXTERNAL_ROLE_MANAGEMENT', 'EXTERNAL_ROLES'] }, - { prefix: '/admin/onboarding-management', keys: ['EXTERNAL_ONBOARDING_MANAGEMENT', 'ONBOARDING_MANAGEMENT', 'ONBOARDING_SCHEMAS', 'ONBOARDING'] }, - { prefix: '/admin/onboarding-schemas', keys: ['EXTERNAL_ONBOARDING_MANAGEMENT', 'ONBOARDING_MANAGEMENT', 'ONBOARDING_SCHEMAS', 'ONBOARDING'] }, { prefix: '/admin/internal-dashboard-management', keys: ['INTERNAL_DASHBOARD_MANAGEMENT', 'INTERNAL_DASHBOARDS', 'INTERNAL_DASHBOARD_CONFIG'] }, { prefix: '/admin/external-dashboard-management', keys: ['DASHBOARD_CONFIG_MANAGEMENT', 'EXTERNAL_DASHBOARD_MANAGEMENT', 'EXTERNAL_DASHBOARDS', 'EXTERNAL_DASHBOARD_CONFIG', 'RUNTIME_ROLES'] }, { prefix: '/admin/role-ui-configs', keys: ['DASHBOARD_CONFIG_MANAGEMENT', 'EXTERNAL_DASHBOARD_MANAGEMENT', 'EXTERNAL_DASHBOARDS', 'EXTERNAL_DASHBOARD_CONFIG', 'RUNTIME_ROLES'] }, diff --git a/src/components/AdminSidebar.tsx b/src/components/AdminSidebar.tsx index 3b38371..a0281f1 100644 --- a/src/components/AdminSidebar.tsx +++ b/src/components/AdminSidebar.tsx @@ -30,7 +30,6 @@ const GROUPS: NavItem[][] = [ ], [ { href: '/admin/external-roles', label: 'External Role Management', icon: ShieldCheck, moduleKeys: ['EXTERNAL_ROLE_MANAGEMENT', 'EXTERNAL_ROLES'] }, - { href: '/admin/onboarding-management', label: 'External Onboarding Management', icon: FileText, aliasPrefix: '/admin/onboarding-schemas', moduleKeys: ['EXTERNAL_ONBOARDING_MANAGEMENT', 'ONBOARDING_MANAGEMENT', 'ONBOARDING_SCHEMAS', 'ONBOARDING'] }, { href: '/admin/internal-dashboard-management', label: 'Internal Dashboard Management', icon: LayoutDashboard, moduleKeys: ['INTERNAL_DASHBOARD_MANAGEMENT', 'INTERNAL_DASHBOARDS', 'INTERNAL_DASHBOARD_CONFIG'] }, { href: '/admin/external-dashboard-management', label: 'External Dashboard Management', icon: LayoutDashboard, aliasPrefix: '/admin/role-ui-configs', moduleKeys: ['DASHBOARD_CONFIG_MANAGEMENT', 'EXTERNAL_DASHBOARD_MANAGEMENT', 'EXTERNAL_DASHBOARDS', 'EXTERNAL_DASHBOARD_CONFIG', 'RUNTIME_ROLES'] }, ], diff --git a/src/routes/admin/[...module].tsx b/src/routes/admin/[...module].tsx index 406915a..2fc2558 100644 --- a/src/routes/admin/[...module].tsx +++ b/src/routes/admin/[...module].tsx @@ -22,7 +22,7 @@ function resolveLegacyPath(modulePath: string): string { case 'approvals': return '/approval'; case 'onboarding-management': - return '/onboarding-management'; + return '/'; case 'internal-dashboard-management': return '/internal-dashboard-management'; case 'external-dashboard-management': diff --git a/src/routes/admin/external-dashboard-management/index.tsx b/src/routes/admin/external-dashboard-management/index.tsx index ca2439d..bfd608c 100644 --- a/src/routes/admin/external-dashboard-management/index.tsx +++ b/src/routes/admin/external-dashboard-management/index.tsx @@ -364,42 +364,17 @@ export default function ExternalDashboardManagementPage() { fetch(`${API}/api/admin/roles?audience=EXTERNAL&per_page=200`, { headers: authHeaders(), credentials: 'include' }), ]); - const dashData = dashRes.ok ? await dashRes.json().catch(() => []) : []; - const roleData = rolesRes.ok ? await rolesRes.json().catch(() => []) : []; + if (!dashRes.ok) throw new Error(`Failed to load dashboard config (${dashRes.status})`); + if (!rolesRes.ok) throw new Error(`Failed to load external roles (${rolesRes.status})`); - const dashNeedsFallback = !dashRes.ok || looksLikeGateway404(dashData); - const rolesNeedFallback = !rolesRes.ok || looksLikeGateway404(roleData); - - let finalDashData: any = dashData; - let finalRoleData: any = roleData; - - if (dashNeedsFallback) { - const fallbackDashRes = await fetch(`${API}/api/config/dashboard`, { - headers: authHeaders(), - credentials: 'include', - }); - finalDashData = fallbackDashRes.ok ? await fallbackDashRes.json().catch(() => []) : []; + const dashData = await dashRes.json().catch(() => []); + const roleData = await rolesRes.json().catch(() => []); + if (looksLikeGateway404(dashData) || looksLikeGateway404(roleData)) { + throw new Error('Required admin runtime endpoints are unavailable.'); } - if (rolesNeedFallback) { - // Gateway no longer routes /api/admin/roles in some environments. - // Build role options from dashboard configs so external dashboard table remains usable. - const rawRows = Array.isArray(finalDashData) ? finalDashData : (finalDashData?.items || finalDashData?.configs || []); - const roleMap = new Map(); - for (const row of rawRows) { - const audience = String(row?.audience || '').toUpperCase(); - if (audience !== 'EXTERNAL') continue; - const roleId = String(row?.role_id || row?.roleId || '').trim(); - const roleKey = String(row?.role_key || row?.config_json?.role_key || '').toUpperCase().trim(); - if (!roleId) continue; - const name = normalizeRoleNameFromKey(roleKey || 'EXTERNAL_ROLE'); - roleMap.set(roleId, { id: roleId, key: roleKey || 'EXTERNAL_ROLE', name }); - } - finalRoleData = Array.from(roleMap.values()); - } - - const dashRows = Array.isArray(finalDashData) ? finalDashData : (finalDashData?.items || finalDashData?.configs || []); - const roleRows = Array.isArray(finalRoleData) ? finalRoleData : (finalRoleData?.roles || finalRoleData?.items || []); + const dashRows = Array.isArray(dashData) ? dashData : (dashData?.items || dashData?.configs || []); + const roleRows = Array.isArray(roleData) ? roleData : (roleData?.roles || roleData?.items || []); setRoles(roleRows .filter((r: any) => { diff --git a/src/routes/admin/external-roles.tsx b/src/routes/admin/external-roles.tsx index a53dd4c..09c92ba 100644 --- a/src/routes/admin/external-roles.tsx +++ b/src/routes/admin/external-roles.tsx @@ -93,6 +93,16 @@ const PERMISSION_ACTIONS = [ { key: 'approve', label: 'Approve' } ]; +function StatusBadge(props: { status: string }) { + const active = () => props.status === 'ACTIVE'; + return ( + + + {active() ? 'Active' : 'Inactive'} + + ); +} + const FALLBACK_LOGS = [ { id: 'l1', user: 'Admin Ashwin', action: 'Updated Permissions', target: 'Verified Company', date: '2026-03-27 10:30' }, { id: 'l2', user: 'Admin Ashwin', action: 'Changed Status', target: 'Professional Photographer', date: '2026-03-26 14:15' }, @@ -105,16 +115,6 @@ const FALLBACK_ASSIGNED_USERS = [ { id: 'u3', name: 'Alice Wong', email: 'alice@photo.me', joined: '2026-03-12' }, ]; -function StatusBadge(props: { status: string }) { - const active = () => props.status === 'ACTIVE'; - return ( - - - {active() ? 'Active' : 'Inactive'} - - ); -} - function FormInput(props: { label: string; required?: boolean; value: string; onInput: (v: string) => void; placeholder?: string; type?: string }) { return (