feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
import { A , useParams , useSearchParams } from '@solidjs/router' ;
import { createMemo , createResource , createSignal , For , Show , createEffect } from 'solid-js' ;
2026-03-19 14:03:15 +01:00
2026-04-07 22:12:52 +02:00
const API = '' ;
2026-03-19 14:03:15 +01:00
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
// ── Types ──────────────────────────────────────────────────────────
type RoleType =
| 'COMPANY' | 'CANDIDATE' | 'CUSTOMER' | 'PHOTOGRAPHER' | 'MAKEUP_ARTIST'
| 'TUTOR' | 'DEVELOPER' | 'VIDEO_EDITOR' | 'GRAPHIC_DESIGNER'
| 'SOCIAL_MEDIA_MANAGER' | 'FITNESS_TRAINER' | 'CATERING_SERVICE'
| 'ADMIN' | 'UNKNOWN' ;
interface SubmissionData {
user : {
id : string ;
name? : string ;
email : string ;
phone? : string ;
status : string ;
email_verified : boolean ;
created_at : string ;
} ;
role_key? : string ;
onboarding ? : {
status : string ;
progress_json : Record < string , unknown > ;
completed_at? : string ;
updated_at : string ;
} | null ;
}
interface AdminRemark {
type : 'INFO' | 'CHANGES_REQUESTED' | 'MORE_DOCUMENTS_REQUESTED' | 'REJECTED' ;
comment : string ;
fields? : string [ ] ;
}
// ── Helpers ──────────────────────────────────────────────────────────
function inferRoleType ( roleKey? : string , name? : string ) : RoleType {
const raw = [ roleKey , name ] . filter ( Boolean ) . join ( ' ' ) . toLowerCase ( ) ;
if ( raw . includes ( 'photographer' ) ) return 'PHOTOGRAPHER' ;
if ( raw . includes ( 'makeup' ) ) return 'MAKEUP_ARTIST' ;
if ( raw . includes ( 'tutor' ) ) return 'TUTOR' ;
if ( raw . includes ( 'developer' ) ) return 'DEVELOPER' ;
if ( raw . includes ( 'video' ) || raw . includes ( 'video_editor' ) ) return 'VIDEO_EDITOR' ;
if ( raw . includes ( 'graphic' ) || raw . includes ( 'graphic_designer' ) ) return 'GRAPHIC_DESIGNER' ;
if ( raw . includes ( 'social' ) || raw . includes ( 'social_media' ) ) return 'SOCIAL_MEDIA_MANAGER' ;
if ( raw . includes ( 'fitness' ) || raw . includes ( 'fitness_trainer' ) ) return 'FITNESS_TRAINER' ;
if ( raw . includes ( 'catering' ) ) return 'CATERING_SERVICE' ;
if ( raw . includes ( 'customer' ) ) return 'CUSTOMER' ;
if ( raw . includes ( 'job_seeker' ) || raw . includes ( 'candidate' ) ) return 'CANDIDATE' ;
if ( raw . includes ( 'company' ) ) return 'COMPANY' ;
if ( raw . includes ( 'admin' ) || raw . includes ( 'employee' ) ) return 'ADMIN' ;
return 'UNKNOWN' ;
}
function managementDest ( roleType : RoleType ) : { label : string ; href : string } {
const map : Record < RoleType , { label : string ; href : string } > = {
COMPANY : { label : 'Company Management' , href : '/admin/company' } ,
CANDIDATE : { label : 'Candidate Management' , href : '/admin/candidate' } ,
CUSTOMER : { label : 'Customer Management' , href : '/admin/customer' } ,
PHOTOGRAPHER : { label : 'Photographer Management' , href : '/admin/photographer' } ,
MAKEUP_ARTIST : { label : 'Makeup Artist Management' , href : '/admin/makeup-artist' } ,
TUTOR : { label : 'Tutors Management' , href : '/admin/tutors' } ,
DEVELOPER : { label : 'Developers Management' , href : '/admin/developers' } ,
VIDEO_EDITOR : { label : 'Video Editor Management' , href : '/admin/video-editors' } ,
GRAPHIC_DESIGNER : { label : 'Graphics Designer Management' , href : '/admin/graphic-designers' } ,
SOCIAL_MEDIA_MANAGER : { label : 'Social Media Manager Management' , href : '/admin/social-media-managers' } ,
FITNESS_TRAINER : { label : 'Fitness Trainer Management' , href : '/admin/fitness-trainers' } ,
CATERING_SERVICE : { label : 'Catering Services Management' , href : '/admin/catering-services' } ,
ADMIN : { label : 'Employee Management' , href : '/admin/employees' } ,
UNKNOWN : { label : 'Users Management' , href : '/admin/users' } ,
} ;
return map [ roleType ] ? ? map . UNKNOWN ;
}
/** Flatten nested JSON into key→value pairs for display */
function flattenFields ( obj : Record < string , unknown > , prefix = '' ) : Array < { key : string ; value : string } > {
const result : Array < { key : string ; value : string } > = [ ] ;
for ( const [ k , v ] of Object . entries ( obj ) ) {
const label = prefix ? ` ${ prefix } . ${ k } ` : k ;
if ( v !== null && typeof v === 'object' && ! Array . isArray ( v ) ) {
result . push ( . . . flattenFields ( v as Record < string , unknown > , label ) ) ;
} else if ( Array . isArray ( v ) ) {
result . push ( { key : label , value : v.join ( ', ' ) } ) ;
} else {
result . push ( { key : label , value : String ( v ? ? '—' ) } ) ;
}
}
return result ;
}
/** Skip internal tracking fields — only show what the user actually submitted */
const SKIP_KEYS = new Set ( [ 'step' , 'total' , 'currentStep' , '__version' , '__schema' ] ) ;
function isSubmittedField ( key : string ) : boolean {
return ! SKIP_KEYS . has ( key ) && ! key . startsWith ( '_' ) ;
}
const ROLE_COLORS : Record < RoleType , string > = {
COMPANY : 'background:#dbeafe;color:#1d4ed8' ,
CANDIDATE : 'background:#e0e7ff;color:#4338ca' ,
CUSTOMER : 'background:#cffafe;color:#0e7490' ,
PHOTOGRAPHER : 'background:#ede9fe;color:#6d28d9' ,
MAKEUP_ARTIST : 'background:#fce7f3;color:#9d174d' ,
TUTOR : 'background:#d1fae5;color:#065f46' ,
DEVELOPER : 'background:#e0f2fe;color:#0369a1' ,
VIDEO_EDITOR : 'background:#fef3c7;color:#92400e' ,
GRAPHIC_DESIGNER : 'background:#f0fdf4;color:#166534' ,
SOCIAL_MEDIA_MANAGER : 'background:#fdf2f8;color:#86198f' ,
FITNESS_TRAINER : 'background:#fff7ed;color:#9a3412' ,
CATERING_SERVICE : 'background:#fefce8;color:#854d0e' ,
ADMIN : 'background:#f1f5f9;color:#334155' ,
UNKNOWN : 'background:#f8fafc;color:#64748b' ,
2026-03-19 14:03:15 +01:00
} ;
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
// ── Field type detection ──────────────────────────────────────────────────────────
type FieldKind = 'image' | 'pdf' | 'document' | 'url' | 'text' ;
function detectKind ( key : string , value : string ) : FieldKind {
const k = key . toLowerCase ( ) ;
const v = ( value || '' ) . toLowerCase ( ) ;
// Image: key hints OR common image extensions
if (
/photo|image|picture|avatar|selfie|headshot|thumbnail|profile_pic/i . test ( k ) ||
/\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?|$)/i . test ( v )
) return 'image' ;
// PDF
if ( /\.(pdf)(\?|$)/i . test ( v ) || /pdf|resume|cv\b/i . test ( k ) ) return 'pdf' ;
// Generic document/upload (not image/pdf)
if (
/upload|document|file|attachment|certificate|license|govt_id|aadhaar|pan|passport|degree|transcript|portfolio|id_proof/i . test ( k ) ||
/\.(doc|docx|xls|xlsx|ppt|pptx|zip|rar)(\?|$)/i . test ( v )
) return 'document' ;
// URL that isn't a file
if ( value . startsWith ( 'http' ) || value . startsWith ( '/' ) ) return 'url' ;
return 'text' ;
}
// ── Data loaders ──────────────────────────────────────────────────────────
async function loadSubmission ( args : { userId : string ; roleKey : string } ) : Promise < SubmissionData | null > {
if ( ! args . userId ) return null ;
2026-03-19 14:03:15 +01:00
try {
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
const qs = args . roleKey ? ` ?roleKey= ${ encodeURIComponent ( args . roleKey ) } ` : '' ;
const res = await fetch ( ` ${ API } /api/admin/approvals/submission/ ${ args . userId } ${ qs } ` ) ;
2026-03-19 14:03:15 +01:00
if ( ! res . ok ) return null ;
return res . json ( ) ;
} catch {
return null ;
}
}
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
// ── Page ──────────────────────────────────────────────────────────
2026-03-19 14:03:15 +01:00
export default function ApprovalDetailPage() {
const params = useParams ( ) ;
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
const [ searchParams ] = useSearchParams ( ) ;
// params.id can be either:
// - a user UUID → we load the submission directly
// - an old approval request ID → shown as legacy fallback
const userId = ( ) = > params . id ;
const roleKey = ( ) = > ( searchParams . roleKey as string ) || '' ;
const [ data ] = createResource (
( ) = > ( { userId : userId ( ) , roleKey : roleKey ( ) } ) ,
loadSubmission ,
) ;
2026-03-19 14:03:15 +01:00
const [ acting , setActing ] = createSignal ( '' ) ;
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
const [ actionError , setActionError ] = createSignal ( '' ) ;
const [ actionDone , setActionDone ] = createSignal ( '' ) ;
const roleType = createMemo ( ( ) = > inferRoleType ( data ( ) ? . role_key , undefined ) ) ;
const dest = createMemo ( ( ) = > managementDest ( roleType ( ) ) ) ;
// Flatten progress_json into displayable rows
const submittedRows = createMemo ( ( ) = > {
const pj = data ( ) ? . onboarding ? . progress_json ;
if ( ! pj || typeof pj !== 'object' ) return [ ] ;
return flattenFields ( pj as Record < string , unknown > )
. filter ( ( f ) = > isSubmittedField ( f . key ) ) ;
} ) ;
// ── Approve / Reject ──
// Routes: POST /api/admin/approvals/profiles/professional/{role_key}/{user_id}/approve
// POST /api/admin/approvals/profiles/company/{user_id}/approve
// POST /api/admin/approvals/profiles/customer/{user_id}/approve
const getApprovalPath = ( action : 'approve' | 'reject' ) = > {
const rk = ( roleKey ( ) || '' ) . toUpperCase ( ) ;
const uid = userId ( ) ;
if ( rk === 'COMPANY' ) return ` /api/admin/approvals/profiles/company/ ${ uid } / ${ action } ` ;
if ( rk === 'CUSTOMER' ) return ` /api/admin/approvals/profiles/customer/ ${ uid } / ${ action } ` ;
if ( rk ) return ` /api/admin/approvals/profiles/professional/ ${ rk } / ${ uid } / ${ action } ` ;
return null ;
} ;
2026-03-19 14:03:15 +01:00
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
const handleApprove = async ( ) = > {
if ( ! confirm ( 'Approve this profile submission?' ) ) return ;
const path = getApprovalPath ( 'approve' ) ;
if ( ! path ) { setActionError ( 'Cannot resolve approval endpoint — roleKey missing in URL' ) ; return ; }
try {
setActing ( 'APPROVE' ) ;
setActionError ( '' ) ;
const res = await fetch ( ` ${ API } ${ path } ` , { method : 'POST' } ) ;
if ( ! res . ok ) throw new Error ( 'Failed to approve' ) ;
setActionDone ( 'APPROVED' ) ;
} catch ( err : any ) {
setActionError ( err . message || 'Failed to approve' ) ;
} finally {
setActing ( '' ) ;
}
} ;
2026-03-19 14:03:15 +01:00
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
const handleReject = async ( ) = > {
const reason = prompt ( 'Rejection reason (required):' ) ;
if ( ! reason ? . trim ( ) ) return ;
const path = getApprovalPath ( 'reject' ) ;
if ( ! path ) { setActionError ( 'Cannot resolve rejection endpoint — roleKey missing in URL' ) ; return ; }
2026-03-19 14:03:15 +01:00
try {
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
setActing ( 'REJECT' ) ;
setActionError ( '' ) ;
const res = await fetch ( ` ${ API } ${ path } ` , {
method : 'POST' ,
2026-03-19 14:03:15 +01:00
headers : { 'Content-Type' : 'application/json' } ,
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
body : JSON.stringify ( { reason : reason.trim ( ) } ) ,
2026-03-19 14:03:15 +01:00
} ) ;
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
if ( ! res . ok ) throw new Error ( 'Failed to reject' ) ;
setActionDone ( 'REJECTED' ) ;
2026-03-19 14:03:15 +01:00
} catch ( err : any ) {
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
setActionError ( err . message || 'Failed to reject' ) ;
2026-03-19 14:03:15 +01:00
} finally {
setActing ( '' ) ;
}
} ;
return (
style: apply consistent page header pattern across all admin routes
- Replace text-2xl font-bold with text-xl font-semibold in all page headers
- Replace bg-[#fd6216] orange buttons with bg-[#0a1d37] navy buttons
- Wrap all pages in -mx-6 -mt-6 flex flex-col layout for edge-to-edge headers
- Replace .field/.actions CSS classes with explicit Tailwind utility classes
- Apply data-table/table-card shared CSS classes to remaining list pages
- Remove duplicate tab bar from roles/index.tsx (AdminShell TAB_SETS handles it)
- Move Create Internal Role button to page header in roles/index.tsx
Pages updated: applications, modules, responses, verification-status,
company/create, company/[id], employees/[id]/edit, users/[id]/edit,
users/details/[id], roles/index, roles/[id]/index, roles/[id]/edit,
role-ui-configs, runtime-roles/[roleKey], onboarding-schemas/*,
external/internal-dashboard-management, approval/[id], approval,
jobs/[id], leads/[id], photographer/[id], requirements/[id],
kb/articles/[id], kb/articles/[id]/edit, verification/[id],
verification-status/[id], help/[id], help/support-bridge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 07:37:02 +01:00
< div class = "flex flex-col -mx-6 -mt-6 min-h-full" >
< div class = "bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between" >
2026-03-19 14:03:15 +01:00
< div >
style: apply consistent page header pattern across all admin routes
- Replace text-2xl font-bold with text-xl font-semibold in all page headers
- Replace bg-[#fd6216] orange buttons with bg-[#0a1d37] navy buttons
- Wrap all pages in -mx-6 -mt-6 flex flex-col layout for edge-to-edge headers
- Replace .field/.actions CSS classes with explicit Tailwind utility classes
- Apply data-table/table-card shared CSS classes to remaining list pages
- Remove duplicate tab bar from roles/index.tsx (AdminShell TAB_SETS handles it)
- Move Create Internal Role button to page header in roles/index.tsx
Pages updated: applications, modules, responses, verification-status,
company/create, company/[id], employees/[id]/edit, users/[id]/edit,
users/details/[id], roles/index, roles/[id]/index, roles/[id]/edit,
role-ui-configs, runtime-roles/[roleKey], onboarding-schemas/*,
external/internal-dashboard-management, approval/[id], approval,
jobs/[id], leads/[id], photographer/[id], requirements/[id],
kb/articles/[id], kb/articles/[id]/edit, verification/[id],
verification-status/[id], help/[id], help/support-bridge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 07:37:02 +01:00
< h1 class = "text-xl font-semibold text-gray-900" > Submission Review < / h1 >
< p class = "text-sm text-gray-500 mt-0.5" > Review a user ' s onboarding form submission and take action . < / p >
2026-03-19 14:03:15 +01:00
< / div >
style: apply consistent page header pattern across all admin routes
- Replace text-2xl font-bold with text-xl font-semibold in all page headers
- Replace bg-[#fd6216] orange buttons with bg-[#0a1d37] navy buttons
- Wrap all pages in -mx-6 -mt-6 flex flex-col layout for edge-to-edge headers
- Replace .field/.actions CSS classes with explicit Tailwind utility classes
- Apply data-table/table-card shared CSS classes to remaining list pages
- Remove duplicate tab bar from roles/index.tsx (AdminShell TAB_SETS handles it)
- Move Create Internal Role button to page header in roles/index.tsx
Pages updated: applications, modules, responses, verification-status,
company/create, company/[id], employees/[id]/edit, users/[id]/edit,
users/details/[id], roles/index, roles/[id]/index, roles/[id]/edit,
role-ui-configs, runtime-roles/[roleKey], onboarding-schemas/*,
external/internal-dashboard-management, approval/[id], approval,
jobs/[id], leads/[id], photographer/[id], requirements/[id],
kb/articles/[id], kb/articles/[id]/edit, verification/[id],
verification-status/[id], help/[id], help/support-bridge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 07:37:02 +01:00
< A class = "rounded-lg border border-gray-200 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" href = "/admin/approval" > ← Back to Approvals < / A >
2026-03-19 14:03:15 +01:00
< / div >
style: apply consistent page header pattern across all admin routes
- Replace text-2xl font-bold with text-xl font-semibold in all page headers
- Replace bg-[#fd6216] orange buttons with bg-[#0a1d37] navy buttons
- Wrap all pages in -mx-6 -mt-6 flex flex-col layout for edge-to-edge headers
- Replace .field/.actions CSS classes with explicit Tailwind utility classes
- Apply data-table/table-card shared CSS classes to remaining list pages
- Remove duplicate tab bar from roles/index.tsx (AdminShell TAB_SETS handles it)
- Move Create Internal Role button to page header in roles/index.tsx
Pages updated: applications, modules, responses, verification-status,
company/create, company/[id], employees/[id]/edit, users/[id]/edit,
users/details/[id], roles/index, roles/[id]/index, roles/[id]/edit,
role-ui-configs, runtime-roles/[roleKey], onboarding-schemas/*,
external/internal-dashboard-management, approval/[id], approval,
jobs/[id], leads/[id], photographer/[id], requirements/[id],
kb/articles/[id], kb/articles/[id]/edit, verification/[id],
verification-status/[id], help/[id], help/support-bridge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 07:37:02 +01:00
< div class = "p-6 flex-1" >
2026-03-19 14:03:15 +01:00
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< Show when = { actionError ( ) } >
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "mb-4 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700" style = "margin-bottom:12px" > { actionError ( ) } < / div >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< / Show >
< Show when = { actionDone ( ) } >
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "rounded-xl border border-gray-200 bg-white shadow-sm" style = "margin-bottom:12px;border-left:4px solid #22c55e;padding:12px 16px" >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< p style = "margin:0;font-weight:600;color:#166534" >
{ actionDone ( ) === 'APPROVED' ? '✓ Profile approved successfully.' : '✕ Profile rejected.' }
< / p >
< A href = { dest ( ) . href } style = "font-size:13px;color:#15803d;text-decoration:underline" >
Open { dest ( ) . label } →
< / A >
< / div >
2026-03-19 14:03:15 +01:00
< / Show >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< Show when = { data . loading } >
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "rounded-xl border border-gray-200 bg-white shadow-sm" > < p class = "notice" > Loading submission . . . < / p > < / div >
2026-03-19 14:03:15 +01:00
< / Show >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< Show when = { ! data . loading && ! data ( ) } >
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "rounded-xl border border-gray-200 bg-white shadow-sm" >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< p class = "notice" > Submission not found or user does not have an onboarding record for this role . < / p >
< p style = "font-size:13px;color:#64748b;margin-top:8px" >
Make sure the URL includes < code > ? roleKey = PHOTOGRAPHER < / code > ( or the relevant role key ) .
< / p >
< / div >
2026-03-19 14:03:15 +01:00
< / Show >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< Show when = { data ( ) } >
{ /* ── Action bar ── */ }
< Show when = { ! actionDone ( ) } >
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "rounded-xl border border-gray-200 bg-white shadow-sm" style = "display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:16px;padding:12px 16px" >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< span style = { ` display:inline-block;padding:3px 10px;border-radius:999px;font-size:12px;font-weight:600; ${ ROLE_COLORS [ roleType ( ) ] } ` } >
{ ( roleKey ( ) || 'UNKNOWN' ) . replace ( /_/g , ' ' ) }
< / span >
< span style = "font-size:13px;color:#64748b" >
Onboarding : < strong style = { ` color: ${ data ( ) ! . onboarding ? . status === 'COMPLETED' ? '#15803d' : '#c2410c' } ` } >
{ data ( ) ! . onboarding ? . status ? ? 'NO DATA' }
< / strong >
< / span >
< div style = "flex:1" / >
< Show when = { data ( ) ! . onboarding ? . status === 'COMPLETED' && ! actionDone ( ) } >
< button
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
class = "inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors"
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
style = "background:#f0fdf4;color:#15803d;border-color:#bbf7d0"
disabled = { ! ! acting ( ) }
onClick = { handleApprove }
>
{ acting ( ) === 'APPROVE' ? 'Approving...' : '✓ Approve Profile' }
2026-03-19 14:03:15 +01:00
< / button >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< button
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
class = "inline-flex items-center rounded-lg border border-red-200 bg-red-50 px-4 py-2 text-sm font-medium text-red-600 hover:bg-red-100 transition-colors"
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
disabled = { ! ! acting ( ) }
onClick = { handleReject }
>
{ acting ( ) === 'REJECT' ? 'Rejecting...' : '✕ Reject Profile' }
2026-03-19 14:03:15 +01:00
< / button >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< / Show >
< / div >
< / Show >
< div style = "display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px" >
{ /* User info */ }
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "rounded-xl border border-gray-200 bg-white shadow-sm" >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< h3 style = "margin:0 0 12px;font-size:15px;font-weight:600;color:#0f172a" > User Info < / h3 >
< table style = "width:100%;border-collapse:collapse;font-size:13px" >
< tbody >
< tr > < td style = "color:#64748b;padding:5px 10px 5px 0;white-space:nowrap;width:40%" > Name < / td > < td style = "font-weight:500" > { data ( ) ! . user . name || '—' } < / td > < / tr >
< tr > < td style = "color:#64748b;padding:5px 10px 5px 0" > Email < / td > < td style = "color:#475569" > { data ( ) ! . user . email } < / td > < / tr >
< tr > < td style = "color:#64748b;padding:5px 10px 5px 0" > Phone < / td > < td style = "color:#475569" > { data ( ) ! . user . phone || '—' } < / td > < / tr >
< tr >
< td style = "color:#64748b;padding:5px 10px 5px 0" > Account Status < / td >
< td >
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< span class = { ` inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600 ${ data ( ) ! . user . status === 'ACTIVE' ? ' active' : '' } ` } >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
{ data ( ) ! . user . status }
< / span >
< / td >
< / tr >
< tr >
< td style = "color:#64748b;padding:5px 10px 5px 0" > Email Verified < / td >
< td style = { ` font-weight:500;color: ${ data ( ) ! . user . email_verified ? '#15803d' : '#b91c1c' } ` } >
{ data ( ) ! . user . email_verified ? '✓ Yes' : '✕ No' }
< / td >
< / tr >
< tr > < td style = "color:#64748b;padding:5px 10px 5px 0" > Registered < / td > < td style = "color:#475569" > { new Date ( data ( ) ! . user . created_at ) . toLocaleDateString ( ) } < / td > < / tr >
< tr > < td style = "color:#64748b;padding:5px 10px 5px 0" > User ID < / td > < td style = "font-family:ui-monospace,monospace;font-size:11px;color:#94a3b8" > { data ( ) ! . user . id } < / td > < / tr >
< / tbody >
< / table >
< / div >
{ /* Submission status */ }
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "rounded-xl border border-gray-200 bg-white shadow-sm" >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< h3 style = "margin:0 0 12px;font-size:15px;font-weight:600;color:#0f172a" > Submission Info < / h3 >
< Show when = { data ( ) ! . onboarding } fallback = {
< div style = "background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:14px" >
< p style = "margin:0;color:#b91c1c;font-weight:500" > No onboarding data found < / p >
< p style = "margin:6px 0 0;font-size:13px;color:#7f1d1d" >
This user has not started or submitted the onboarding form for role : < strong > { roleKey ( ) || 'unknown' } < / strong >
< / p >
< / div >
} >
< table style = "width:100%;border-collapse:collapse;font-size:13px" >
< tbody >
< tr > < td style = "color:#64748b;padding:5px 10px 5px 0;white-space:nowrap;width:40%" > Role < / td > < td style = "font-weight:500" > { ( roleKey ( ) || '—' ) . replace ( /_/g , ' ' ) } < / td > < / tr >
< tr >
< td style = "color:#64748b;padding:5px 10px 5px 0" > Status < / td >
< td >
< span style = { ` display:inline-block;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:600; ${ data ( ) ! . onboarding ! . status === 'COMPLETED' ? 'background:#dcfce7;color:#166534' : 'background:#fef9c3;color:#713f12' } ` } >
{ data ( ) ! . onboarding ! . status }
< / span >
< / td >
< / tr >
< tr > < td style = "color:#64748b;padding:5px 10px 5px 0" > Submitted < / td > < td style = "color:#475569" > { data ( ) ! . onboarding ! . completed_at ? new Date ( data ( ) ! . onboarding ! . completed_at ! ) . toLocaleString ( ) : '—' } < / td > < / tr >
< tr > < td style = "color:#64748b;padding:5px 10px 5px 0" > Last Updated < / td > < td style = "color:#475569" > { new Date ( data ( ) ! . onboarding ! . updated_at ) . toLocaleString ( ) } < / td > < / tr >
< tr > < td style = "color:#64748b;padding:5px 10px 5px 0" > Fields < / td > < td style = "color:#475569" > { submittedRows ( ) . length } fields submitted < / td > < / tr >
< / tbody >
< / table >
< / Show >
< / div >
< / div >
{ /* ── Submitted form answers + media viewer ── */ }
< Show when = { submittedRows ( ) . length > 0 } >
< SubmissionViewer rows = { submittedRows ( ) } / >
< / Show >
{ /* ── No form data fallback ── */ }
< Show when = { data ( ) ! . onboarding && submittedRows ( ) . length === 0 } >
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "rounded-xl border border-gray-200 bg-white shadow-sm" style = "margin-bottom:16px" >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< h3 style = "margin:0 0 10px;font-size:15px;font-weight:600;color:#0f172a" > Submitted Form Answers < / h3 >
< p class = "notice" > Onboarding state is present but contains no displayable field data . < / p >
< / div >
< / Show >
< / Show >
style: apply consistent page header pattern across all admin routes
- Replace text-2xl font-bold with text-xl font-semibold in all page headers
- Replace bg-[#fd6216] orange buttons with bg-[#0a1d37] navy buttons
- Wrap all pages in -mx-6 -mt-6 flex flex-col layout for edge-to-edge headers
- Replace .field/.actions CSS classes with explicit Tailwind utility classes
- Apply data-table/table-card shared CSS classes to remaining list pages
- Remove duplicate tab bar from roles/index.tsx (AdminShell TAB_SETS handles it)
- Move Create Internal Role button to page header in roles/index.tsx
Pages updated: applications, modules, responses, verification-status,
company/create, company/[id], employees/[id]/edit, users/[id]/edit,
users/details/[id], roles/index, roles/[id]/index, roles/[id]/edit,
role-ui-configs, runtime-roles/[roleKey], onboarding-schemas/*,
external/internal-dashboard-management, approval/[id], approval,
jobs/[id], leads/[id], photographer/[id], requirements/[id],
kb/articles/[id], kb/articles/[id]/edit, verification/[id],
verification-status/[id], help/[id], help/support-bridge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 07:37:02 +01:00
< / div >
< / div >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
) ;
}
// ────────────────────────────── SubmissionViewer ──────────────────────────────
function SubmissionViewer ( props : { rows : Array < { key : string ; value : string } > } ) {
const [ lightbox , setLightbox ] = createSignal < { src : string ; label : string } | null > ( null ) ;
const [ pdfViewer , setPdfViewer ] = createSignal < { src : string ; label : string } | null > ( null ) ;
// Split rows into text fields vs media
const textFields = createMemo ( ( ) = >
props . rows . filter ( ( r ) = > {
const kind = detectKind ( r . key , r . value ) ;
return kind === 'text' || kind === 'url' ;
} )
) ;
const mediaFields = createMemo ( ( ) = >
props . rows . filter ( ( r ) = > {
const kind = detectKind ( r . key , r . value ) ;
return kind === 'image' || kind === 'pdf' || kind === 'document' ;
} )
) ;
return (
< >
{ /* ── Text / data fields ── */ }
< Show when = { textFields ( ) . length > 0 } >
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "rounded-xl border border-gray-200 bg-white shadow-sm" style = "margin-bottom:16px" >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< h3 style = "margin:0 0 14px;font-size:15px;font-weight:600;color:#0f172a" >
Submitted Form Data
< span style = "margin-left:8px;font-size:12px;font-weight:400;color:#64748b" > { textFields ( ) . length } fields < / span >
< / h3 >
< div style = "display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:8px" >
< For each = { textFields ( ) } >
{ ( field ) = > {
const kind = detectKind ( field . key , field . value ) ;
return (
< div style = "background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:10px 12px" >
< div style = "font-size:10px;color:#94a3b8;margin-bottom:4px;font-weight:700;letter-spacing:0.05em" >
{ field . key . replace ( /_/g , ' ' ) . replace ( /\./g , ' › ' ) . toUpperCase ( ) }
< / div >
< Show when = { kind === 'url' } fallback = {
< div style = "font-size:13px;color:#0f172a;word-break:break-all;line-height:1.5" > { field . value || '—' } < / div >
} >
< a href = { field . value } target = "_blank" rel = "noreferrer"
style = "font-size:13px;color:#2563eb;word-break:break-all" >
🔗 { field . value }
< / a >
< / Show >
< / div >
) ;
} }
< / For >
< / div >
< / div >
< / Show >
{ /* ── Documents & Images ── */ }
< Show when = { mediaFields ( ) . length > 0 } >
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
< div class = "rounded-xl border border-gray-200 bg-white shadow-sm" style = "margin-bottom:16px" >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< h3 style = "margin:0 0 14px;font-size:15px;font-weight:600;color:#0f172a" >
Documents & Media
< span style = "margin-left:8px;font-size:12px;font-weight:400;color:#64748b" > { mediaFields ( ) . length } file { mediaFields ( ) . length !== 1 ? 's' : '' } < / span >
< / h3 >
< div style = "display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px" >
< For each = { mediaFields ( ) } >
{ ( field ) = > {
const kind = detectKind ( field . key , field . value ) ;
const label = field . key . replace ( /_/g , ' ' ) . replace ( /\./g , ' › ' ) ;
return (
< div style = "border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;background:#fff" >
{ /* Preview area */ }
< Show when = { kind === 'image' } >
< div
style = "background:#f1f5f9;cursor:pointer;position:relative;height:140px;display:flex;align-items:center;justify-content:center;overflow:hidden"
onClick = { ( ) = > setLightbox ( { src : field.value , label } ) }
>
< img
src = { field . value }
alt = { label }
style = "max-width:100%;max-height:140px;object-fit:contain"
onError = { ( e ) = > {
( e . target as HTMLImageElement ) . style . display = 'none' ;
( ( e . target as HTMLImageElement ) . nextElementSibling as HTMLElement ) ! . style . display = 'flex' ;
} }
/ >
< div style = "display:none;width:100%;height:100%;align-items:center;justify-content:center;flex-direction:column;gap:4px;color:#94a3b8" >
< span style = "font-size:32px" > 🖼 < / span >
< span style = "font-size:11px" > Preview unavailable < / span >
< / div >
< div style = "position:absolute;top:6px;right:6px;background:rgba(0,0,0,.5);color:#fff;border-radius:4px;font-size:10px;padding:2px 6px" >
🔍 Click to enlarge
< / div >
< / div >
< / Show >
< Show when = { kind === 'pdf' } >
< div
style = "background:#fef2f2;cursor:pointer;height:140px;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:6px"
onClick = { ( ) = > setPdfViewer ( { src : field.value , label } ) }
>
< span style = "font-size:40px" > 📄 < / span >
< span style = "font-size:12px;color:#b91c1c;font-weight:600" > PDF Document < / span >
< span style = "font-size:11px;color:#94a3b8" > Click to view < / span >
< / div >
< / Show >
< Show when = { kind === 'document' } >
< div style = "background:#eff6ff;height:140px;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:6px" >
< span style = "font-size:40px" > 📎 < / span >
< span style = "font-size:12px;color:#1d4ed8;font-weight:600" > Document < / span >
< a href = { field . value } target = "_blank" rel = "noreferrer"
style = "font-size:11px;color:#2563eb;text-decoration:underline"
onClick = { ( e ) = > e . stopPropagation ( ) } >
Download
< / a >
< / div >
< / Show >
{ /* Label + open link */ }
< div style = "padding:8px 10px;border-top:1px solid #e2e8f0" >
< div style = "font-size:11px;color:#0f172a;font-weight:600;margin-bottom:4px;text-transform:capitalize" >
{ label }
< / div >
< div style = "display:flex;gap:6px" >
< Show when = { kind === 'image' } >
< button
type = "button"
style = "font-size:11px;color:#2563eb;background:none;border:none;padding:0;cursor:pointer;text-decoration:underline"
onClick = { ( ) = > setLightbox ( { src : field.value , label } ) }
>
🔍 View Full
< / button >
< / Show >
< Show when = { kind === 'pdf' } >
< button
type = "button"
style = "font-size:11px;color:#b91c1c;background:none;border:none;padding:0;cursor:pointer;text-decoration:underline"
onClick = { ( ) = > setPdfViewer ( { src : field.value , label } ) }
>
📄 Open PDF
< / button >
< / Show >
< a href = { field . value } target = "_blank" rel = "noreferrer"
style = "font-size:11px;color:#64748b;text-decoration:underline" >
↗ Download
< / a >
< / div >
< / div >
< / div >
) ;
} }
< / For >
< / div >
< / div >
< / Show >
{ /* ── Image Lightbox ── */ }
< Show when = { lightbox ( ) } >
< div
style = "position:fixed;inset:0;background:rgba(0,0,0,.85);z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px"
onClick = { ( ) = > setLightbox ( null ) }
>
< div style = "display:flex;align-items:center;justify-content:space-between;width:100%;max-width:900px;margin-bottom:12px" >
< span style = "color:#f8fafc;font-size:14px;font-weight:600" > { lightbox ( ) ! . label } < / span >
< div style = "display:flex;gap:10px" >
< a href = { lightbox ( ) ! . src } target = "_blank" rel = "noreferrer"
style = "color:#93c5fd;font-size:13px;text-decoration:underline"
onClick = { ( e ) = > e . stopPropagation ( ) } >
↗ Open original
< / a >
< button
type = "button"
style = "color:#f8fafc;background:none;border:none;font-size:22px;cursor:pointer;line-height:1"
onClick = { ( ) = > setLightbox ( null ) }
>
✕
2026-03-19 14:03:15 +01:00
< / button >
< / div >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< / div >
< img
src = { lightbox ( ) ! . src }
alt = { lightbox ( ) ! . label }
style = "max-width:900px;max-height:80vh;object-fit:contain;border-radius:8px;box-shadow:0 0 40px rgba(0,0,0,.6)"
onClick = { ( e ) = > e . stopPropagation ( ) }
/ >
2026-03-19 14:03:15 +01:00
< / div >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< / Show >
2026-03-19 14:03:15 +01:00
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
{ /* ── PDF Viewer Modal ── */ }
< Show when = { pdfViewer ( ) } >
< div
style = "position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px"
>
< div style = "background:#fff;border-radius:12px;overflow:hidden;width:100%;max-width:900px;max-height:90vh;display:flex;flex-direction:column;box-shadow:0 25px 60px rgba(0,0,0,.5)" >
{ /* Header */ }
< div style = "display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc" >
< span style = "font-size:14px;font-weight:600;color:#0f172a" > 📄 { pdfViewer ( ) ! . label } < / span >
< div style = "display:flex;gap:8px;align-items:center" >
< a href = { pdfViewer ( ) ! . src } target = "_blank" rel = "noreferrer"
style = "font-size:12px;color:#2563eb;text-decoration:underline" >
↗ Open in new tab
< / a >
< button
type = "button"
feat(admin): Phase 0 — Tailwind v4 foundation, shell rewrite, modern dashboard
- Install Tailwind CSS v4 via @tailwindcss/vite; configure vite.config.ts
- Rewrite app.css: Tailwind base, Exo 2 font, brand tokens (orange #fd6216, navy #050026), scrollbar utility; fix @import order
- Rewrite AdminShell.tsx: fixed header, fixed inset body grid (sidebar + main), session check, sub-tab system, logout, admin avatar/name/role
- Rewrite AdminSidebar.tsx: collapsible w-64/w-20, orange active rail + badge/dot, CSS filter for SVG icon tinting, min-h-0 flex fix
- Replace 84 route stub CSS classes (page-title, card, btn, table-wrap, etc.) with Tailwind equivalents via safe class-attr-only regex script
- Rewrite admin dashboard: Lucide icons in colored chip backgrounds, 4-col KPI grid, Control Plane 6-module grid, hover lift animations
- Disable SSR (ssr: false) to fix Vinxi dev manifest error; clear stale .vinxi cache
- Add lucide-solid icon library
- Add scripts/cleanup-css.mjs for class migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:00:21 +01:00
class = "inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors"
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
style = "padding:4px 10px;font-size:13px"
onClick = { ( ) = > setPdfViewer ( null ) }
>
Close
< / button >
< / div >
< / div >
{ /* PDF embed */ }
< iframe
src = { ` ${ pdfViewer ( ) ! . src } #toolbar=1&view=FitH ` }
style = "flex:1;min-height:600px;width:100%;border:none"
title = { pdfViewer ( ) ! . label }
/ >
< / div >
< / div >
2026-03-19 14:03:15 +01:00
< / Show >
feat(admin-parity): approval management, submission viewer, role builder offline fallback
- Rebuilt approval.tsx: reject with reason prompt, request changes with field
keys, request more documents flow, RoleTypeBadge per role type, parsed
requestReason (ONBOARDING_SUBMISSION prefix), management page routing for
approved items, inline ApprovalDetailPanel with remarks timeline
- Rebuilt approval/[id].tsx: full onboarding submission viewer loading from
GET /api/admin/approvals/submission/{user_id}?roleKey=X, flattenFields
recursive flattener, detectKind image/pdf/document/url/text classifier,
image lightbox, PDF iframe modal, approve/reject per role type routing
- Added src/lib/admin-modules.ts: STATIC_PERMISSIONS fallback (39 modules x4
actions) for Internal Role Builder when backend is offline
- roles/create.tsx + roles/[id]/edit.tsx: use STATIC_PERMISSIONS when API
returns empty or fails, builder now works offline
- AdminShell + AdminSidebar: alignment/style fixes from parity review
- ExternalRoleForm: extended fields for external runtime role management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 00:34:38 +01:00
< / >
2026-03-19 14:03:15 +01:00
) ;
}