refactor: remove convention-based identity field fallback
All checks were successful
build-and-release / build (push) Successful in 2m23s

Pre-fill is now purely schema-driven: only fields explicitly marked
identity_shared: true by an admin in the Onboarding Schema Editor are
pre-filled when registering a second role.  No implicit convention set.

SwitchServicesPage now passes the full existing profile to RoleWizard;
RoleWizard filters to identity_shared fields using the new role's schema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tracewebstudio Dev 2026-08-12 16:29:41 +02:00
parent 1df875ed36
commit 09a5c36310
2 changed files with 17 additions and 44 deletions

View file

@ -2,7 +2,7 @@ import { For, Show, createSignal, onMount } from 'solid-js';
import { RefreshCw, ArrowLeft } from 'lucide-solid';
import { BTN_GHOST, BTN_PRIMARY, CARD } from '~/components/DashboardShell';
import RoleWizard from '~/components/dashboard/RoleWizard';
import { roleKeyToPrefix, isIdentityField } from '~/lib/role-utils';
import { roleKeyToPrefix } from '~/lib/role-utils';
const API = '';
const NAVY = '#0D0D2A';
@ -48,29 +48,23 @@ const REGISTER_OPTIONS = [
];
/**
* Fetches the user's active role profile and extracts doc URLs for fields
* that are identity fields (shared across roles). These are passed as
* `prefilledDocs` to RoleWizard so the user doesn't have to re-upload
* Aadhaar / PAN / selfie etc. when registering a second role.
* Fetches the user's active role profile and returns all string-valued fields.
* RoleWizard will filter this down to only fields marked `identity_shared: true`
* in the new role's onboarding schema so the filtering is schema-driven, not
* convention-based.
*/
async function fetchSharedIdentityDocs(activeRoleKey: string): Promise<Record<string, string>> {
async function fetchExistingProfile(activeRoleKey: string): Promise<Record<string, string>> {
if (!activeRoleKey) return {};
try {
const res = await apiFetch(`/api/profile?roleKey=${activeRoleKey}`);
if (!res.ok) return {};
const data = await res.json().catch(() => ({}));
const profile: Record<string, unknown> = data?.profile_data ?? data ?? {};
// Keep only string values (URLs / field values) for fields whose key
// looks like an identity document — we don't know the schema here, so
// we use the convention-based check (adminFlagged = false).
const shared: Record<string, string> = {};
const result: Record<string, string> = {};
for (const [key, val] of Object.entries(profile)) {
if (typeof val === 'string' && val && isIdentityField(key, false)) {
shared[key] = val;
if (typeof val === 'string' && val) result[key] = val;
}
}
return shared;
return result;
} catch {
return {};
}
@ -136,8 +130,8 @@ export default function SwitchServicesPage() {
return;
}
// Fetch identity docs from the user's current active role to pre-fill
const shared = await fetchSharedIdentityDocs(activeRole());
// Fetch existing profile — RoleWizard filters to identity_shared fields
const shared = await fetchExistingProfile(activeRole());
setSharedDocs(shared);
await loadRoles();

View file

@ -22,34 +22,13 @@ export function roleKeyToPrefix(roleKey: string): string {
}
}
/**
* Field IDs that are considered "shared identity" documents by convention
* when the onboarding schema has not explicitly marked them with
* `identity_shared: true`. Admins can use these IDs to get automatic
* pre-fill from the user's active role or they can mark any field with
* `identity_shared: true` to opt into pre-fill explicitly.
*/
export const CONVENTION_IDENTITY_FIELD_IDS = new Set([
"aadhaar",
"aadhaar_card",
"aadhaar_number",
"aadhar",
"pan",
"pan_card",
"pan_number",
"passport",
"passport_number",
"selfie",
"profile_photo",
"photo_id",
"address_proof",
"government_id",
]);
/**
* Returns true when a field should be pre-filled from the user's existing
* profile across roles (either admin-flagged or matched by convention).
* profile across roles. Only fields explicitly marked `identity_shared: true`
* in the onboarding schema (by an admin in the Schema Editor) qualify
* there is no convention-based fallback so behaviour is always explicit and
* auditable.
*/
export function isIdentityField(fieldId: string, adminFlagged: boolean): boolean {
return adminFlagged || CONVENTION_IDENTITY_FIELD_IDS.has(fieldId);
export function isIdentityField(_fieldId: string, adminFlagged: boolean): boolean {
return adminFlagged;
}