@realtek/core-theme-live 0.0.470 → 0.0.471
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/{FileField-B0RTUe9w.js → FileField-CKLzJtMv.js} +3 -1
- package/dist-lib/FileField-CKLzJtMv.js.map +1 -0
- package/dist-lib/{FileField-4PXE0gsb.cjs → FileField-gA0dvBwj.cjs} +2 -2
- package/dist-lib/FileField-gA0dvBwj.cjs.map +1 -0
- package/dist-lib/{LinkedRecordFileField-BK4NXb-9.cjs → LinkedRecordFileField-BBlUevyr.cjs} +2 -2
- package/dist-lib/{LinkedRecordFileField-BK4NXb-9.cjs.map → LinkedRecordFileField-BBlUevyr.cjs.map} +1 -1
- package/dist-lib/{LinkedRecordFileField-CXaGgEV-.js → LinkedRecordFileField-BjGC33W9.js} +2 -2
- package/dist-lib/{LinkedRecordFileField-CXaGgEV-.js.map → LinkedRecordFileField-BjGC33W9.js.map} +1 -1
- package/dist-lib/{dropdownApi-BLfaVpEW.js → dropdownApi-C5cPN1-R.js} +2 -2
- package/dist-lib/{dropdownApi-BLfaVpEW.js.map → dropdownApi-C5cPN1-R.js.map} +1 -1
- package/dist-lib/{dropdownApi-BfOmJvJU.cjs → dropdownApi-CnNXP6kM.cjs} +2 -2
- package/dist-lib/{dropdownApi-BfOmJvJU.cjs.map → dropdownApi-CnNXP6kM.cjs.map} +1 -1
- package/dist-lib/index.cjs +7 -7
- package/dist-lib/index.cjs.map +1 -1
- package/dist-lib/index.js +5263 -4890
- package/dist-lib/index.js.map +1 -1
- package/package.json +1 -1
- package/dist-lib/FileField-4PXE0gsb.cjs.map +0 -1
- package/dist-lib/FileField-B0RTUe9w.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileField-gA0dvBwj.cjs","names":[],"sources":["../src/services/adminApi.js","../src/components/detailViewMode.js","../src/services/detailDefaults.js","../src/services/timezone.js","../src/utils/modulePath.js","../src/services/moduleDataApi.js","../src/services/documentApi.js","../src/components/form/savedValueValidation.js","../src/components/form/inputSecurity.js","../src/components/form/validationOverride.js","../src/components/form/optionMatching.js","../src/components/form/mountDefaultValue.js","../src/components/form/csvTags.js","../src/components/form/payloadTransformer.js","../src/components/detail/phoneDisplay.js","../src/components/detail/renderConfig.js","../src/components/form/repeatableRowShape.js","../src/components/AppButton.jsx","../src/components/detail/userNames.js","../src/utils/roleAllowsAction.js","../src/components/form/conditionalFieldLabel.js"],"sourcesContent":["import { fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL, SUBMISSIONS_URL } from './apiConfig';\n\nconst FIELD_CONFIG_PATH = '/admin/field-config';\n\nconst CUSTOMIZABLE_MODULES = [\n { key: 'clients', apiModule: 'clients' },\n { key: 'job', apiModule: 'jobs' },\n { key: 'candidate', apiModule: 'candidates' },\n { key: 'submissions', apiModule: 'submissions' },\n { key: 'onboarding', apiModule: 'onboarding' },\n { key: 'billing', apiModule: 'billing' },\n { key: 'payroll', apiModule: 'payroll' },\n];\n\n// ── Shared Utilities ──────────────────────────────────────────────────────────\n\nfunction firstArray(...values) {\n return values.find(Array.isArray) ?? [];\n}\n\nfunction firstValue(record, keys, fallback) {\n return keys.map((k) => record?.[k]).find((v) => v !== undefined && v !== null && v !== '') ?? fallback;\n}\n\nfunction normalizeTotal(payload, items) {\n return (\n payload?.total ?? payload?.totalCount ?? payload?.count ??\n payload?.recordsTotal ?? payload?.meta?.total ?? payload?.pagination?.total ?? items.length\n );\n}\n\nfunction normalizeModuleField(field, index) {\n const fieldName = firstValue(field, ['label', 'Label', 'fieldName', 'field_name', 'name', 'fieldLabel', 'value'], `Field ${index + 1}`);\n const fieldKey = firstValue(field, ['field', 'Field', 'fieldKey', 'field_key', 'key', 'value', 'fieldName', 'name'], fieldName);\n const showValue = firstValue(field, ['isVisible', 'is_visible', 'visible', 'show', 'is_show', 'isShow', 'enabled'], true);\n const mandatoryValue = firstValue(field, ['ismandatory', 'isMandatory', 'is_mandatory', 'mandatory', 'required', 'req'], false);\n const orderValue = firstValue(field, ['order', 'Order', 'sortOrder', 'position'], index);\n\n return {\n ...field,\n fieldKey,\n fieldName,\n type: firstValue(field, ['type', 'Type'], 'text'),\n isVisible: typeof showValue === 'string'\n ? !['false', '0', 'hide', 'hidden', 'no'].includes(showValue.toLowerCase())\n : Boolean(showValue),\n ismandatory: typeof mandatoryValue === 'string'\n ? ['true', '1', 'yes', 'required'].includes(mandatoryValue.toLowerCase())\n : Boolean(mandatoryValue),\n order: typeof orderValue === 'number' ? orderValue : index,\n isLink: Boolean(field.isLink),\n linkTemplate: field.linkTemplate ?? '',\n linkType: field.linkType ?? 'internal',\n linkTarget: field.linkTarget ?? '_self',\n action: field.action ?? 'navigate',\n allowedTabs: Array.isArray(field.allowedTabs) ? field.allowedTabs : [],\n secondaryField: field.secondaryField ?? '',\n secondaryLabel: field.secondaryLabel ?? '',\n secondaryFields: Array.isArray(field.secondaryFields) ? field.secondaryFields : [],\n secondarySeparator: field.secondarySeparator ?? '',\n lookup: field.lookup\n ? { ...field.lookup, projectFields: Array.isArray(field.lookup.projectFields) ? field.lookup.projectFields : [] }\n : null,\n derived: field.derived ?? null,\n computed: field.computed\n ? { ...field.computed, operands: Array.isArray(field.computed.operands) ? field.computed.operands : [] }\n : null,\n actionButtons: Array.isArray(field.actionButtons) ? field.actionButtons : [],\n displayRules: Array.isArray(field.displayRules) ? field.displayRules : [],\n renderType: field.renderType ?? '',\n renderConfig: field.renderConfig\n ? {\n ...field.renderConfig,\n treatZeroAsEmpty: Boolean(field.renderConfig.treatZeroAsEmpty),\n cleanEmptyTemplateSeparators: Boolean(field.renderConfig.cleanEmptyTemplateSeparators),\n }\n : null,\n valueStyles: Array.isArray(field.valueStyles) ? field.valueStyles : [],\n commonStyle: field.commonStyle ?? null,\n displayTemplate: field.displayTemplate ?? '',\n defaultValue: field.defaultValue ?? '',\n defaultValueField: field.defaultValueField ?? '',\n dataSource: field.dataSource ?? 'module',\n groupName: field.groupName ?? '',\n filterGroupFields: Array.isArray(field.filterGroupFields) ? field.filterGroupFields : [],\n filterGroupSeparator: field.filterGroupSeparator ?? ' ',\n filterArrayLabelField: field.filterArrayLabelField ?? '',\n filterArrayValueField: field.filterArrayValueField ?? '',\n };\n}\n\nfunction normalizeCustomizableModule(module) {\n if (typeof module === 'string') {\n return { key: module, apiModule: module };\n }\n\n const key = firstValue(\n module,\n ['key', 'moduleKey', 'value', 'module', 'apiModule', 'name', 'modulename', 'menuName'],\n ''\n );\n if (!key) return null;\n\n return {\n key,\n apiModule: firstValue(\n module,\n ['apiModule', 'api_module', 'module', 'value', 'key', 'name', 'modulename', 'menuName'],\n key\n ),\n };\n}\n\nfunction normalizeCustomizableModules(modules = CUSTOMIZABLE_MODULES) {\n const source = Array.isArray(modules) && modules.length ? modules : CUSTOMIZABLE_MODULES;\n const seen = new Set();\n return source\n .map(normalizeCustomizableModule)\n .filter(Boolean)\n .filter((module) => {\n if (seen.has(module.key)) return false;\n seen.add(module.key);\n return true;\n });\n}\n\n// ── Modules ───────────────────────────────────────────────────────────────────\n\nconst MODULES_PATH = '/admin/modules';\n\nexport async function getModules() {\n const json = await fetchJsonWithAuth(AUTH_URL, MODULES_PATH);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\n// createModule/updateModule let Admin define workflow modules (e.g. \"Source\n// Candidates\") that extend an existing module via baseModule, inheriting its\n// forms/columns/tabs/filters/actions wherever the new module hasn't\n// configured its own — see the backend's ModuleLookupChain.\nexport async function createModule({ key, label, collectionName, baseModule = '', order = 0 }) {\n return fetchJsonWithAuth(AUTH_URL, MODULES_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ key, label, collectionName, baseModule, order }),\n });\n}\n\nexport async function updateModule(key, { label, collectionName, baseModule = '', order = 0, isActive = true }) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULES_PATH}/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ label, collectionName, baseModule, order, isActive }),\n });\n}\n\n// getModuleAutomations/saveModuleAutomations edit a module's Gateway +\n// Strategies — the Admin \"Automations\" screen. This is the config-driven\n// replacement for logic legacy services used to hardcode in Go (reference-id\n// generation, duplicate checks, cross-module field snapshots, reporting-chain\n// hierarchy expansion, board updates, notifications, cache invalidation).\n// Separate endpoint from updateModule() above, so saving automations can\n// never touch label/forms/columns, and vice versa.\nexport async function getModuleAutomations(key) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${MODULES_PATH}/${encodeURIComponent(key)}/automations`);\n const data = json?.data ?? json ?? {};\n return { gateway: data.gateway ?? null, strategies: data.strategies ?? null };\n}\n\n// Platform metadata (workflows / resolvers / state-machines / applications) —\n// the event-driven behavior layer. kind ∈ 'workflows' | 'resolvers' |\n// 'state-machines' | 'applications'. Docs are keyed by their \"key\" field.\nexport async function listPlatformDocs(kind) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/platform/${kind}`);\n const data = json?.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\nexport async function savePlatformDoc(kind, doc) {\n return fetchJsonWithAuth(AUTH_URL, `/admin/platform/${kind}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(doc),\n });\n}\n\nexport async function deletePlatformDoc(kind, key) {\n return fetchJsonWithAuth(AUTH_URL, `/admin/platform/${kind}/${encodeURIComponent(key)}`, {\n method: 'DELETE',\n });\n}\n\nexport async function saveModuleAutomations(key, { gateway, strategies }) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULES_PATH}/${encodeURIComponent(key)}/automations`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ gateway, strategies }),\n });\n}\n\n// getAvailableModules() is the single source of truth for \"which modules can\n// be picked in an Admin configuration screen\" (Form Groups, Detail Groups,\n// Row Actions, Module Action Rules, Templates, Role Configuration). It's\n// driven by the curated Menus list (an admin explicitly adds/removes/disables\n// entries there) rather than the `modules` collection, which is really the\n// CRUD/gateway registry and accumulates every module ever wired for storage\n// (including disabled/legacy ones no admin actively curates).\n//\n// Falls back to getModules() when Menus has nothing configured yet, so a\n// project that hasn't set up Menus keeps working exactly as before.\nexport async function getAvailableModules() {\n let menus = [];\n try {\n menus = await getMenuModules();\n } catch {\n menus = [];\n }\n const active = (Array.isArray(menus) ? menus : []).filter((m) => (\n m?.isDeleted !== true\n && String(m?.status ?? 'active').toLowerCase() !== 'inactive'\n && String(m?.status ?? 'active').toLowerCase() !== 'disabled'\n ));\n if (active.length > 0) return active;\n return getModules();\n}\n\n// getModuleCollectionMap resolves module key → the Mongo collection that module\n// is stored in, straight from the module registry (`modules`, the CRUD/gateway\n// registry that owns `collectionName`). It exists so an Admin screen can DISPLAY\n// the collection a configuration will act on without ever asking the admin to\n// type a collection name — the collection is module configuration, and the\n// server resolves it authoritatively on every request regardless of what this\n// map says. If the registry is unreachable the map is simply empty and callers\n// fall back to \"resolved on the server\".\n//\n// Module names are matched loosely on purpose: config is stored sometimes under\n// the singular key and sometimes the plural one (the backend's own\n// ModuleAliasVariants exists for the same reason), so both spellings are\n// indexed here. Real registry entries always win over a generated alias, so an\n// alias can never shadow a module that genuinely exists.\nexport async function getModuleCollectionMap() {\n let modules;\n try {\n modules = await getModules();\n } catch {\n return {};\n }\n const map = {};\n const put = (key, collection, exact) => {\n const k = String(key ?? '').trim().toLowerCase();\n if (!k || !collection) return;\n if (exact || map[k] === undefined) map[k] = collection;\n };\n for (const m of Array.isArray(modules) ? modules : []) {\n const collection = String(m?.collectionName ?? '').trim();\n if (!collection) continue;\n put(m?.key, collection, true);\n }\n // Second pass so aliases never overwrite a registered key from pass one.\n for (const m of Array.isArray(modules) ? modules : []) {\n const collection = String(m?.collectionName ?? '').trim();\n const key = String(m?.key ?? '').trim().toLowerCase();\n if (!collection || !key) continue;\n if (key.endsWith('s')) put(key.slice(0, -1), collection, false);\n else put(`${key}s`, collection, false);\n }\n return map;\n}\n\n// ── Admin Form Groups ─────────────────────────────────────────────────────────\n\nconst FORM_GROUPS_PATH = '/admin/form-groups';\nconst CONFIG_DEFAULTS_PATH = '/admin/config-defaults';\n\nconst NIL_OBJECT_ID = '000000000000000000000000';\n\n/** True for an absent id or Go's zero-value ObjectID (24 zeros). */\nfunction isNilObjectId(value) {\n const id = String(value ?? '').trim();\n return id === '' || id === NIL_OBJECT_ID;\n}\n\nfunction formGroupScopeQuery(scope = {}) {\n const params = new URLSearchParams();\n if (scope.module) params.set('module', scope.module);\n if (scope.clientId) params.set('clientId', scope.clientId);\n if (scope.region) params.set('region', scope.region);\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n}\n\nexport async function getAdminFormGroups(module = '', scope = {}) {\n const query = formGroupScopeQuery({ ...scope, module: module || scope.module || '' });\n const path = `${FORM_GROUPS_PATH}${query}`;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const payload = json?.data;\n // Backend returns EITHER a flat array of groups, OR a wrapper object\n // { id, module, ..., groups: [...] }. Support both shapes.\n if (Array.isArray(payload)) return payload;\n if (payload && Array.isArray(payload.groups)) return payload.groups;\n if (Array.isArray(json)) return json;\n if (json && Array.isArray(json.groups)) return json.groups;\n return [];\n}\n\nexport async function getPreviewConfig(module = '', scope = {}) {\n const query = formGroupScopeQuery({ ...scope, module: module || scope.module || '' });\n const json = await fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/preview-config${query}`);\n return json?.data ?? json ?? {};\n}\n\nexport async function savePreviewConfig(module = '', config = {}, scope = {}) {\n const query = formGroupScopeQuery(scope);\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/preview-config${query}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module, ...config }),\n });\n}\n\nexport async function getAdminClients({ offset = 0, limit = 100, sortBy = 'new', searchvalue = '' } = {}) {\n const params = new URLSearchParams({\n offset: String(offset),\n limit: String(limit),\n sortBy,\n searchvalue,\n });\n // Scope the list to the active tenant/business/businessUnit captured from the\n // login token (authApi stores these on login). The backend uses them when\n // present, otherwise falls back to the token claims — so the dropdown lists\n // the right clients for the selected project, same as the legacy flow.\n const tenantId = localStorage.getItem('tenantId') || '';\n const businessId = localStorage.getItem('businessId') || '';\n const businessUnitId = localStorage.getItem('businessUnitId') || '';\n if (tenantId) params.set('tenantId', tenantId);\n if (businessId) params.set('businessId', businessId);\n if (businessUnitId) params.set('businessUnitId', businessUnitId);\n const json = await fetchJsonWithAuth(AUTH_URL, `/client/view?${params.toString()}`);\n const data = json?.data ?? json;\n return data?.clients ?? data?.Clients ?? data?.items ?? [];\n}\n\nexport async function getModuleFields(module) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/filter-dropdown-fields?module=${encodeURIComponent(module)}`);\n const fields = json?.data ?? json;\n return Array.isArray(fields) ? fields : [];\n}\n\nexport async function createAdminFormGroup(group, scope = {}) {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}${formGroupScopeQuery(scope)}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(group),\n });\n}\n\nexport async function updateAdminFormGroup(id, group, scope = {}) {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/${id}${formGroupScopeQuery(scope)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(group),\n });\n}\n\n// Scope-aware, like create/update: with a client selected the group is removed\n// from THAT client's list only. A client-scoped config is a fork of the module\n// default and keeps the same group ids, so an unscoped delete would clear the\n// group from the default and every other client at once.\nexport async function deleteAdminFormGroup(id, scope = {}) {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/${id}${formGroupScopeQuery(scope)}`, { method: 'DELETE' });\n}\n\nexport async function seedAdminFormGroups() {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/seed`, { method: 'POST' });\n}\n\nexport async function requestConfigOtp(scope, action) {\n return fetchJsonWithAuth(AUTH_URL, `${CONFIG_DEFAULTS_PATH}/request-otp`, {\n method: 'POST',\n body: JSON.stringify({ scope, action }),\n });\n}\n\nexport async function applyConfigDefault(scope, action, otp) {\n return fetchJsonWithAuth(AUTH_URL, `${CONFIG_DEFAULTS_PATH}/apply`, {\n method: 'POST',\n body: JSON.stringify({ scope, action, otp }),\n });\n}\n\n// ── Admin Detail-View Config ──────────────────────────────────────────────────\n// Per-module customization layered on top of the form groups, applied only by\n// the detail view (?view=detail). Shape: { module, fields:[…], merges:[…] }.\n\nconst DETAIL_CONFIG_PATH = '/admin/detail-config';\n\n// Client scope mirrors the form-group admin exactly: pass a clientId to read /\n// write THAT client's detail config, omit it for the module default. The backend\n// falls back to the default when the client has none of its own, so a client\n// with no overrides still renders.\nexport async function getDetailConfig(module, scope = {}) {\n const query = formGroupScopeQuery({ module, clientId: scope.clientId });\n const json = await fetchJsonWithAuth(AUTH_URL, `${DETAIL_CONFIG_PATH}${query}`);\n const data = json?.data ?? json ?? {};\n return {\n module: data.module ?? module,\n // Which scope the response belongs to — '' means the module default. Go\n // serialises an unset ObjectID as 24 zeros, which is NOT a client.\n clientId: isNilObjectId(data.clientId) ? '' : String(data.clientId),\n fields: Array.isArray(data.fields) ? data.fields : [],\n merges: Array.isArray(data.merges) ? data.merges : [],\n sourceFields: Array.isArray(data.sourceFields) ? data.sourceFields : [],\n // Detail-only group title overrides. Absent on an older backend, so default\n // to [] rather than undefined -- the editor treats undefined as \"unknown\"\n // and would refuse to send the key at all.\n groups: Array.isArray(data.groups) ? data.groups : [],\n };\n}\n\nexport async function saveDetailConfig({ module, fields = [], merges = [], sourceFields = [], groups = [], clientId = '' }) {\n return fetchJsonWithAuth(AUTH_URL, DETAIL_CONFIG_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n // clientId travels in the BODY (this is a JSON POST); the backend treats an\n // absent/zero id as \"the module default\".\n body: JSON.stringify({ module, fields, merges, sourceFields, groups, ...(clientId ? { clientId } : {}) }),\n });\n}\n\nexport async function discoverDetailFields({ module, clientId = '', otp }) {\n return fetchJsonWithAuth(AUTH_URL, `${DETAIL_CONFIG_PATH}/discover-fields`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module, otp, ...(clientId ? { clientId } : {}) }),\n });\n}\n\n// ── Detail-View Display Defaults ──────────────────────────────────────────────\n// Tenant-wide display settings (S3 base, date format, boolean/empty labels,\n// separator, document name order) — the configurable home of what used to be\n// hard-coded in fileUtils.js / FieldValue.jsx.\n\nconst DETAIL_DEFAULTS_PATH = '/admin/detail-defaults';\n\nexport async function getDetailDefaults() {\n const json = await fetchJsonWithAuth(AUTH_URL, DETAIL_DEFAULTS_PATH);\n return json?.data ?? json ?? {};\n}\n\nexport async function saveDetailDefaults(defaults) {\n return fetchJsonWithAuth(AUTH_URL, DETAIL_DEFAULTS_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(defaults),\n });\n}\n\n// ── AI Service registry (backends AI Actions can call) ─────────────────────\n\nconst AI_SERVICE_CONFIG_PATH = '/admin/ai-service-config';\n\nexport async function getAiServiceConfigs() {\n const json = await fetchJsonWithAuth(AUTH_URL, AI_SERVICE_CONFIG_PATH);\n return json?.data ?? json ?? [];\n}\n\nexport async function saveAiServiceConfig(config) {\n return fetchJsonWithAuth(AUTH_URL, AI_SERVICE_CONFIG_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n}\n\nexport async function deleteAiServiceConfig(serviceKey) {\n return fetchJsonWithAuth(AUTH_URL, `${AI_SERVICE_CONFIG_PATH}?serviceKey=${encodeURIComponent(serviceKey)}`, {\n method: 'DELETE',\n });\n}\n\n/**\n * Ask the SERVER whether it can reach an AI service base URL.\n *\n * Deliberately server-side. /ai-action is a server-to-server call made by the\n * Go backend, so only that process can answer the question that matters. A\n * browser-side probe would test the admin's laptop instead: an internal\n * address (http://pyops:7020) is reachable from the backend and not from a\n * remote admin, and a public URL can be reachable from a browser while blocked\n * by cluster egress rules. Testing from the wrong place proves nothing.\n *\n * Resolves with { reachable, status, latencyMs, target, detail, looksLikeWebApp }.\n * An unreachable service is a SUCCESSFUL test with reachable:false — the\n * detail is what the admin needs to see.\n */\nexport async function testAiServiceConnection({ baseUrl, authHeader, authValue }) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${AI_SERVICE_CONFIG_PATH}/test`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ baseUrl, authHeader, authValue }),\n });\n return json?.data ?? json;\n}\n\n// ── Background Check integration ───────────────────────────────────────────\n\nconst BGC_INTEGRATION_CONFIG_PATH = '/admin/bgc-integration-config';\n\nexport async function getBgcIntegrationConfig() {\n const json = await fetchJsonWithAuth(AUTH_URL, BGC_INTEGRATION_CONFIG_PATH);\n return json?.data ?? json ?? { updateMethod: 'MANUAL', enabled: true };\n}\n\nexport async function saveBgcIntegrationConfig(config) {\n return fetchJsonWithAuth(AUTH_URL, BGC_INTEGRATION_CONFIG_PATH, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n}\n\n// Safe runtime view for consumers such as the onboarding BGC stage. It never\n// contains credential values, only credentialsConfigured/webhookConfigured.\nexport async function getBgcIntegrationSummary() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/integration-config/bgc/summary');\n return json?.data ?? json ?? { updateMethod: 'MANUAL', enabled: true };\n}\n\nconst INTEGRATION_CONFIG_PATH = '/admin/integration-config';\n\nexport async function getIntegrationConfigs() {\n const json = await fetchJsonWithAuth(AUTH_URL, INTEGRATION_CONFIG_PATH);\n return json?.data ?? json ?? [];\n}\n\nexport async function getIntegrationConfig(integrationKey) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `${INTEGRATION_CONFIG_PATH}/${encodeURIComponent(integrationKey)}`,\n );\n return json?.data ?? json;\n}\n\nexport async function saveIntegrationConfig(config) {\n const key = config?.integrationKey;\n const path = key\n ? `${INTEGRATION_CONFIG_PATH}/${encodeURIComponent(key)}`\n : INTEGRATION_CONFIG_PATH;\n return fetchJsonWithAuth(AUTH_URL, path, {\n method: key ? 'PUT' : 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n}\n\nexport async function deleteIntegrationConfig(integrationKey) {\n return fetchJsonWithAuth(\n AUTH_URL,\n `${INTEGRATION_CONFIG_PATH}/${encodeURIComponent(integrationKey)}`,\n { method: 'DELETE' },\n );\n}\n\nexport async function testIntegrationConnection(config) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${INTEGRATION_CONFIG_PATH}/test`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n return json?.data ?? json;\n}\n\nexport async function getIntegrationSummary(integrationKey) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/integration-config/${encodeURIComponent(integrationKey)}/summary`,\n );\n return json?.data ?? json;\n}\n\nexport async function applyIntegrationStatus(integrationKey, recordId, providerResponse) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/integration-config/${encodeURIComponent(integrationKey)}/apply-status/${encodeURIComponent(recordId)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(providerResponse),\n },\n );\n return json?.data ?? json;\n}\n\n// getModuleDetailPreview fetches a record's detail-view groups WITH resolved\n// values (references → {id,value}, documents → full object) so the admin page\n// can preview exactly what the detail view will render.\nexport async function getModuleDetailPreview(module, id) {\n const path = `${FORM_GROUPS_PATH}?module=${encodeURIComponent(module)}&id=${encodeURIComponent(id)}&view=detail`;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const data = json?.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\n// ── Roles ─────────────────────────────────────────────────────────────────────\n\nfunction normalizeRole(record) {\n return {\n key: record.role_id ?? record.id,\n roleId: record.role_id ?? record.id,\n roleName: record.role_name ?? 'N/A',\n description: record.role_description ?? 'N/A',\n userCount: record.user_count ?? 0,\n roleType: record.role_type ?? 0,\n raw: record,\n };\n}\n\nexport function getRoleId(role) {\n return role?.raw?.role_id ?? role?.roleId ?? role?.key;\n}\n\nexport async function getAllRoles() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/get-roles');\n const payload = json.data ?? json;\n const defaultRoles = firstArray(payload?.zinnext_default).map(normalizeRole);\n const customRoles = firstArray(payload?.custom_roles).map(normalizeRole);\n const all = [...defaultRoles, ...customRoles];\n return { roles: all, total: all.length };\n}\n\n// ── Teams ─────────────────────────────────────────────────────────────────────\n\nfunction normalizeTeam(record) {\n return {\n key: record.teamId,\n teamId: record.teamId,\n teamName: record.teamName ?? 'N/A',\n manager: record.reportingManager ?? 'N/A',\n managerId: record.reportingManagerId ?? 0,\n memberCount: record.noOfTeamMembers ?? 0,\n members: firstArray(record.teamMembers),\n memberIds: firstArray(record.teamMembersIds),\n raw: record,\n };\n}\n\nexport function getTeamId(team) {\n return team?.raw?.teamId ?? team?.teamId ?? team?.key;\n}\n\nexport async function getAllTeams({ offset = 0, limit = 10, sortBy = 'new', search = '' } = {}) {\n const params = new URLSearchParams({ searchquery: search, sortBy, limit: String(limit), offset: String(offset) });\n const json = await fetchJsonWithAuth(AUTH_URL, `/get-teams?${params}`);\n const payload = json.data ?? json;\n const teams = firstArray(payload?.teams, payload, json?.teams);\n const total = payload?.totalCount ?? payload?.total ?? teams.length;\n return { teams: teams.map(normalizeTeam), total };\n}\n\nexport async function updateTeam(team, { teamName, managerId, memberIds = [] }) {\n const teamId = getTeamId(team);\n return fetchJsonWithAuth(AUTH_URL, `/team/edit?teamId=${teamId}`, {\n method: 'PUT',\n body: JSON.stringify({\n team_name: teamName,\n team_manager: managerId,\n other_team_members: memberIds,\n reporting_team_member: [],\n }),\n });\n}\n\n// ── Role / Team Field Config ──────────────────────────────────────────────────\n\nasync function fetchFieldConfigModule(apiModule, idParam, idValue, configType = 'listView', includeMeta = false) {\n const params = new URLSearchParams({ module: apiModule, userId: '0', [idParam]: String(idValue), configType });\n if (includeMeta) params.set('includeMeta', '1');\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}?${params}`);\n const payload = json.data ?? json;\n const fields = firstArray(payload, payload?.visibleFields, json?.visibleFields);\n return includeMeta\n ? {\n fields,\n dataVisibilityMode: payload?.dataVisibilityMode ?? json?.dataVisibilityMode ?? 'all',\n dataOwnerField: payload?.dataOwnerField ?? json?.dataOwnerField ?? 'createdBy',\n filterDisplayMode: payload?.filterDisplayMode === 'top' ? 'top' : 'popup',\n }\n : fields;\n}\n\nexport async function getRoleModuleFields(role, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const roleId = getRoleId(role);\n const customizableModules = normalizeCustomizableModules(modules);\n const entries = await Promise.all(\n customizableModules.map(async ({ key, apiModule }) => {\n const fields = await fetchFieldConfigModule(apiModule, 'roleId', roleId, configType);\n return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, roleId }))];\n })\n );\n return Object.fromEntries(entries);\n}\n\n\nexport async function getRoleModuleFieldsWithMeta(role, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const roleId = getRoleId(role);\n const customizableModules = normalizeCustomizableModules(modules);\n const entries = await Promise.all(\n customizableModules.map(async ({ key, apiModule }) => {\n const result = await fetchFieldConfigModule(apiModule, 'roleId', roleId, configType, true);\n const fields = result.fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, roleId }));\n return [key, {\n fields,\n dataVisibilityMode: result.dataVisibilityMode ?? 'all',\n dataOwnerField: result.dataOwnerField ?? 'createdBy',\n filterDisplayMode: result.filterDisplayMode ?? 'popup',\n }];\n })\n );\n return Object.fromEntries(entries);\n}\n\nexport async function getTeamModuleFields(team, configType = 'listView') {\n const teamId = getTeamId(team);\n const entries = await Promise.all(\n CUSTOMIZABLE_MODULES.map(async ({ key, apiModule }) => {\n const fields = await fetchFieldConfigModule(apiModule, 'teamId', teamId, configType);\n return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, teamId }))];\n })\n );\n return Object.fromEntries(entries);\n}\n\nfunction normalizeListViewLookup(field) {\n if (!field?.lookup) return null;\n\n const lookup = { ...field.lookup };\n const localField = String(lookup.localField ?? '').trim() || String(field.fieldKey ?? '').trim();\n const valueField = String(lookup.valueField ?? lookup.foreignField ?? '').trim() || '_id';\n const displayField = String(lookup.displayField ?? lookup.projectField ?? '').trim();\n const displayField2 = String(lookup.displayField2 ?? '').trim();\n\n let projectFields = Array.isArray(lookup.projectFields)\n ? lookup.projectFields.map((value) => String(value ?? '').trim()).filter(Boolean)\n : [];\n\n // The list API accepts displayField, projectField and projectFields. Persist\n // projectFields as well so old/new backend versions resolve the same config.\n if (projectFields.length === 0) {\n projectFields = [displayField, displayField2].filter(Boolean);\n }\n\n return {\n ...lookup,\n localField,\n valueField,\n displayField,\n ...(displayField2 ? { displayField2 } : {}),\n projectFields,\n };\n}\n\nfunction visibleFieldPayload(fields, configType) {\n const isListView = !configType || configType === 'listView';\n return fields.map((field, index) => {\n const base = {\n label: field.fieldName, field: field.fieldKey,\n isVisible: field.isVisible, type: field.type ?? 'text', order: field.order ?? index,\n };\n if (isListView) {\n Object.assign(base, {\n ...(typeof field.showByDefault === 'boolean' ? { showByDefault: field.showByDefault } : {}),\n allowedTabs: Array.isArray(field.allowedTabs) ? field.allowedTabs : [],\n isLink: field.isLink ?? false, linkTemplate: field.linkTemplate ?? '',\n linkType: field.linkType ?? 'internal', linkTarget: field.linkTarget ?? '_self',\n action: field.action ?? 'navigate',\n secondaryField: field.secondaryField ?? '', secondaryLabel: field.secondaryLabel ?? '',\n secondaryFields: Array.isArray(field.secondaryFields) ? field.secondaryFields : [],\n secondarySeparator: field.secondarySeparator ?? '',\n lookup: normalizeListViewLookup(field), actionButtons: field.actionButtons ?? [],\n derived: field.derived ?? null, computed: field.computed ?? null,\n displayRules: Array.isArray(field.displayRules) ? field.displayRules : [],\n renderType: field.renderType ?? '',\n valueStyles: Array.isArray(field.valueStyles) ? field.valueStyles : [],\n commonStyle: field.commonStyle ?? null, displayTemplate: field.displayTemplate ?? '',\n defaultValue: field.defaultValue ?? '',\n defaultValueField: field.defaultValueField ?? '',\n renderConfig: field.renderConfig\n ? {\n ...field.renderConfig,\n treatZeroAsEmpty: Boolean(field.renderConfig.treatZeroAsEmpty),\n cleanEmptyTemplateSeparators: Boolean(field.renderConfig.cleanEmptyTemplateSeparators),\n }\n : null,\n });\n } else {\n base.isEditable = field.isEditable ?? false;\n base.ismandatory = field.ismandatory ?? field.isMandatory ?? false;\n if (configType === 'filter') {\n base.filterInputType = field.filterInputType ?? 'dropdown';\n base.isDefaultFilter = Boolean(field.isDefaultFilter);\n base.dataSource = field.dataSource ?? 'module';\n if (field.dataSource === 'group') base.groupName = field.groupName ?? '';\n base.filterGroupFields = Array.isArray(field.filterGroupFields) ? field.filterGroupFields : [];\n base.filterGroupSeparator = field.filterGroupSeparator ?? ' ';\n base.filterArrayLabelField = field.filterArrayLabelField ?? '';\n base.filterArrayValueField = field.filterArrayValueField ?? '';\n if (field.lookup) base.lookup = field.lookup;\n } else {\n if (['select', 'radio', 'checkbox'].includes(field.type)) {\n base.dataSource = field.dataSource ?? 'module';\n if (field.dataSource === 'group') base.groupName = field.groupName ?? '';\n }\n }\n }\n return base;\n });\n}\n\nexport async function updateRoleModuleFieldConfig(role, module, fields, configType = 'listView', modules = CUSTOMIZABLE_MODULES, dataVisibilityMode = '', dataOwnerField = 'createdBy', updateAllRoles = false, filterDisplayMode = 'popup') {\n const roleId = getRoleId(role);\n const apiModule = normalizeCustomizableModules(modules).find((m) => m.key === module)?.apiModule ?? module;\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/role`, {\n method: 'PUT',\n body: JSON.stringify({\n module: apiModule, roleId, configType, updateAllRoles,\n ...(configType === 'filter' ? { filterDisplayMode } : {}),\n visibleFields: visibleFieldPayload(fields, configType),\n ...((!configType || configType === 'listView') && dataVisibilityMode\n ? { dataVisibilityMode, dataOwnerField: String(dataOwnerField || 'createdBy').trim() || 'createdBy' }\n : {}),\n }),\n });\n}\n\n// ── Role Configure → Form (derived from Form Groups) ─────────────────────────\n// Unlike getRoleModuleFields/updateRoleModuleFieldConfig above (an\n// independently-seeded flat field list), these back the Form tab's grouped\n// view: the backend derives groups/fields straight from the module's Form\n// Groups config and overlays this role's saved grants (GET\n// /admin/role-form-permissions), so Form Groups stays the single source of\n// truth and new/renamed/removed fields sync automatically. Saving still goes\n// through the existing PUT /admin/field-config/role (configType \"form\") —\n// the backend clamps it against Form Groups' global Show flags either way.\nconst ROLE_FORM_PERMISSIONS_PATH = '/admin/role-form-permissions';\n\n// Must match Be_Auth_DevOps models/roleFormPermissions.go's\n// roleGroupPermissionKey — the synthetic field-config key a group-level\n// ON/OFF toggle is persisted under, alongside real per-field grants, in the\n// same flat visibleFields list (no separate schema/endpoint needed).\nfunction roleGroupPermissionKey(groupName) {\n return `__group__:${groupName}`;\n}\n\n// scope: { clientId, region } — same client/region scope as\n// getAdminFormGroups/AddFormV1's effectiveClientId+effectiveRegion, so a\n// client- or region-scoped Form Groups override (e.g. a per-client Jobs\n// variant) is reflected here too, not just the tenant-wide default. Generic\n// for any module — GetFormGroupConfigScoped falls back to the default when\n// no scoped config exists for the given module, so this is always safe to pass.\nexport async function getRoleFormPermissions(role, module, scope = {}) {\n const roleId = getRoleId(role);\n const params = new URLSearchParams({ module, roleId: String(roleId ?? '') });\n if (scope.clientId) params.set('clientId', scope.clientId);\n if (scope.region) params.set('region', scope.region);\n const json = await fetchJsonWithAuth(AUTH_URL, `${ROLE_FORM_PERMISSIONS_PATH}?${params}`);\n const payload = json?.data ?? json;\n return Array.isArray(payload) ? payload : [];\n}\n\n// groups: [{ name, label, enabled, locked, fields: [{ field, label, enabled, locked, editable }] }]\n// as edited in the UI (see RoleFormPermissionsEditor). Group toggles are sent\n// as synthetic visibleFields rows so the existing role-save + clamp pipeline\n// needs no changes.\n//\n// A role's field-level grants are NOT stored per client/region — they're one\n// shared set overlaid onto whichever scoped Form Groups structure a caller\n// requests (getRoleFormPermissions). So any group/field that's `locked` in\n// the CURRENTLY VIEWED scope (globally disabled there, but possibly enabled\n// under the tenant-wide default or a different client's scope) is left out\n// of the payload entirely — sending its scope-computed `enabled: false`\n// would silently overwrite the role's real, scope-independent stored\n// preference the next time anyone loads a different scope. Locked items\n// aren't editable in this view anyway (their Switch is disabled), so there's\n// nothing this save is meant to change for them.\nexport async function saveRoleFormPermissions(role, module, groups) {\n const roleId = getRoleId(role);\n const visibleFields = [];\n let order = 0;\n for (const group of groups) {\n if (!group.locked) {\n visibleFields.push({\n label: group.label,\n field: roleGroupPermissionKey(group.name),\n // A group carries two independent role toggles: isVisible = shown,\n // isEditable = not-disabled (read-only when false). Default both true.\n isVisible: group.enabled,\n isEditable: group.editable !== false,\n order: order++,\n });\n }\n for (const field of group.fields ?? []) {\n if (field.locked) continue;\n visibleFields.push({\n label: field.label,\n field: field.field,\n isVisible: field.enabled,\n isEditable: field.editable !== false,\n order: order++,\n });\n }\n }\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/role`, {\n method: 'PUT',\n body: JSON.stringify({ module, roleId, configType: 'form', visibleFields }),\n });\n}\n\nexport async function updateTeamModuleFieldConfig(team, module, fields, configType = 'listView') {\n const teamId = getTeamId(team);\n const apiModule = CUSTOMIZABLE_MODULES.find((m) => m.key === module)?.apiModule ?? module;\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/team`, {\n method: 'PUT',\n body: JSON.stringify({ module: apiModule, teamId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n });\n}\n\n// ── Users ─────────────────────────────────────────────────────────────────────\n\nexport async function getAllUsers({ offset = 0, limit = 10, sortBy = 'new', searchQuery = '' } = {}) {\n const params = new URLSearchParams({ offset: String(offset), limit: String(limit), sortBy });\n if (searchQuery) params.set('searchquery', searchQuery);\n const json = await fetchJsonWithAuth(AUTH_URL, `/get-all-users?${params}`);\n const payload = json.data ?? json;\n const users = firstArray(\n payload, json.users, json.rows, json.items, json.records,\n payload?.users, payload?.data, payload?.rows, payload?.items,\n payload?.records, payload?.docs, payload?.result, payload?.results,\n );\n return { users, total: normalizeTotal({ ...json, ...payload }, users) };\n}\n\nexport function getUserId(user) {\n const value = firstValue(\n user?.raw ?? user,\n ['USER_ID', 'user_id', 'userId', 'id', '_id', 'uuid'],\n user?.key\n );\n const num = Number(value);\n return Number.isFinite(num) ? num : value;\n}\n\nexport function getUserRoleId(user) {\n const value = firstValue(user?.raw ?? user, ['ROLE_ID', 'role_id', 'roleId', 'ROLEID'], 0);\n const num = Number(value);\n return Number.isFinite(num) && num > 0 ? num : 0;\n}\n\nasync function getFieldConfigModule(apiModule, userId, roleId, configType) {\n const params = new URLSearchParams({ module: apiModule, userId: String(userId ?? ''), roleId: String(roleId ?? ''), configType });\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}?${params}`);\n const payload = json.data ?? json;\n return firstArray(payload, payload?.visibleFields, json?.visibleFields);\n}\n\nasync function getDropdownModuleFields(apiModule) {\n const json = await fetchJsonWithAuth(SUBMISSIONS_URL, `/filter-dropdown-fields?module=${encodeURIComponent(apiModule)}`);\n const payload = json.data ?? json;\n return firstArray(payload, payload?.fields, payload?.items, payload?.rows, json?.fields);\n}\n\nexport async function getUserModuleFields(user, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const userId = getUserId(user);\n const roleId = getUserRoleId(user);\n const customizableModules = normalizeCustomizableModules(modules);\n const entries = await Promise.all(\n customizableModules.map(async ({ key, apiModule }) => {\n let fields;\n try {\n fields = await getFieldConfigModule(apiModule, userId, roleId, configType);\n } catch {\n try { fields = await getDropdownModuleFields(apiModule); } catch { fields = []; }\n }\n if ((fields?.length ?? 0) === 0) {\n try { fields = await getDropdownModuleFields(apiModule); } catch { fields = []; }\n }\n return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, userId, roleId }))];\n })\n );\n return Object.fromEntries(entries);\n}\n\nexport async function hasUserFieldConfigData(user, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const userId = getUserId(user);\n const roleId = getUserRoleId(user);\n const customizableModules = normalizeCustomizableModules(modules);\n const results = await Promise.all(\n customizableModules.map(async ({ apiModule }) => {\n try {\n const fields = await getFieldConfigModule(apiModule, userId, roleId, configType);\n return (fields?.length ?? 0) > 0;\n } catch {\n return false;\n }\n })\n );\n return results.some(Boolean);\n}\n\nexport async function seedFieldConfig(collections = []) {\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/seed`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ collections }),\n });\n}\n\nexport async function getFieldConfigSeedStatus() {\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/seed-status`);\n const data = json?.data ?? json ?? {};\n return {\n modules: Array.isArray(data.modules) ? data.modules : [],\n seededModules: data.seededModules ?? {},\n };\n}\n\nexport async function updateUserModuleFieldConfig(user, module, fields, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const userId = getUserId(user);\n const roleId = getUserRoleId(user);\n const apiModule = normalizeCustomizableModules(modules).find((m) => m.key === module)?.apiModule ?? module;\n return fetchJsonWithAuth(AUTH_URL, FIELD_CONFIG_PATH, {\n method: 'PUT',\n body: JSON.stringify({ module: apiModule, userId, roleId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n });\n}\n\nexport async function updateUserModuleFields(user, moduleFields, configType = 'listView') {\n return Promise.all(\n Object.entries(moduleFields).map(([module, fields]) => updateUserModuleFieldConfig(user, module, fields, configType))\n );\n}\n\nexport async function uploadFormGroupIcon(file) {\n const token = localStorage.getItem('authToken');\n const formData = new FormData();\n formData.append('icon', file);\n const res = await fetch(`${AUTH_URL}/admin/form-groups/icon`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n if (!res.ok) { const e = new Error(`Upload failed: ${res.status}`); e.status = res.status; throw e; }\n const json = await res.json();\n return json.data ?? json;\n}\n\nexport async function uploadListViewActionIcon(file) {\n const token = localStorage.getItem('authToken');\n const formData = new FormData();\n formData.append('icon', file);\n const res = await fetch(`${AUTH_URL}/admin/field-config/action-icon`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n if (!res.ok) { const e = new Error(`Upload failed: ${res.status}`); e.status = res.status; throw e; }\n const json = await res.json();\n return json.data ?? json;\n}\n\n// Module options for the Actions admin screens: the configured menu/module\n// catalog PLUS every collection in this app's database, so row actions can be\n// configured for ANY module (onboarding, movements, a brand-new collection…)\n// without first registering it as a menu module. Resolves per app DB via\n// X-App-Id, so each project sees its own collections.\nexport async function getActionConfigModules() {\n const [modules, collections, existingConfigs] = await Promise.all([\n getAvailableModules().catch(() => []),\n getAvailableCollections().catch(() => []),\n getRowActionConfigs().catch(() => []),\n ]);\n const baseModules = Array.isArray(modules) ? modules : [];\n // Detail-view action variants: a module's DETAIL page can carry its own row\n // actions (e.g. an \"Open JD\" primary button on the submission detail view)\n // without those actions also appearing on the LIST view. The detail page\n // fetches actions under whatever key it passes as DetailHeaderCard's\n // customConfigName — \"submissiondetails\", \"submissionJob\" — which follows no\n // derivable rule. This used to GENERATE `${key}detail` names, which produced\n // keys nothing ever requests (\"submissionsdetail\" vs the real\n // \"submissiondetails\"), so configuring one silently changed nothing.\n // Offer the keys that actually exist in rowActionConfig instead.\n const variantKeys = existingConfigs\n .map((config) => String(config?.module ?? '').trim())\n .filter(Boolean);\n return [\n ...baseModules,\n ...(Array.isArray(collections) ? collections : []),\n ...variantKeys,\n ];\n}\n\n// Generic module CRUD — used by the Masters admin screen (locations/taxes),\n// but not specific to either: works for any module via the dynamic gateway.\nexport async function listModuleRecords(module, { page = 1, limit = 100 } = {}) {\n const params = new URLSearchParams({ module, page: String(page), limit: String(limit) });\n const json = await fetchJsonWithAuth(AUTH_URL, `/module/list?${params}`);\n const data = json?.data ?? json ?? {};\n return { items: Array.isArray(data.data) ? data.data : [], total: data.pagination?.total ?? 0 };\n}\n\nexport async function createModuleRecordGeneric(module, payload) {\n return fetchJsonWithAuth(AUTH_URL, `/module/create?module=${encodeURIComponent(module)}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n}\n\nexport async function updateModuleRecordGeneric(module, id, payload) {\n return fetchJsonWithAuth(AUTH_URL, `/module/update/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n}\n\nexport async function deleteModuleRecordGeneric(module, id) {\n return fetchJsonWithAuth(AUTH_URL, `/module/delete/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`, {\n method: 'DELETE',\n });\n}\n\n// Searchable \"City, State, Country\" options from the locationMasters collection\n// — the same source AddFormV1's `location` field type uses. Used by the Location\n// Tax Master screen to pick a real location instead of free-typing one.\nexport async function getLocationMasterOptions(search = '', limit = 50) {\n const params = new URLSearchParams({ search, limit: String(limit), offset: '0' });\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/location-dropdown-values?${params}`);\n const raw = json?.data ?? json ?? [];\n return (Array.isArray(raw) ? raw : [])\n .map((item) => {\n const label = typeof item === 'string' ? item : (item.label ?? item.value ?? '');\n return { label, value: label };\n })\n .filter((option) => option.label);\n}\n\n// Generic record options for any lookup-configured field — used by the Form\n// Groups editor's \"Conditional Default\" record picker (e.g. choose the default\n// Employer applied when Contract Type is W2). Returns { label, value } pairs\n// where value is the record's valueField (usually _id).\nexport async function getLookupRecordOptions(collection, displayField, valueField = '_id') {\n const params = new URLSearchParams({\n collection: String(collection),\n displayField: String(displayField),\n valueField: String(valueField),\n });\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/lookup-dropdown-values?${params}`);\n const raw = json?.data ?? json ?? [];\n return (Array.isArray(raw) ? raw : [])\n .map((item) => ({\n label: item.label ?? item.displayValue ?? item[displayField] ?? String(item.value ?? ''),\n value: String(item.value ?? item._id ?? item.id ?? ''),\n }))\n .filter((option) => option.value && option.label);\n}\n\n// Default Fields — per-module rules that LOCK a field in the Form Groups editor\n// and can optionally force it visible and/or required at render time.\n// getModuleDefaultFields is also called at runtime by AddFormV1/EditFormV1.\n// Saving is OTP-gated: request the code (requestConfigOtp('defaultFields','save'))\n// then pass it here.\n//\n// TWO WIRE SHAPES, and both must keep working:\n// \"jobTitle\" legacy — force show + force req + lock\n// { field, locked, forceShow, forceReq } each intent stated separately\n//\n// The legacy reading is not a courtesy: ~145 live fields are stored as bare\n// strings, and 29 of them (US unit) are configured differently from what\n// membership forces — 25 submission fields are configured req:0 yet required\n// today. Treating a string as \"lock only\" would quietly make those optional.\n// normalizeDefaultFieldRule is the ONE place that decision is made, so the\n// forms, the field editor and the admin panel cannot disagree about it.\nexport function normalizeDefaultFieldRule(entry) {\n if (typeof entry === 'string') {\n const field = entry.trim();\n return field ? { field, locked: true, forceShow: true, forceReq: true } : null;\n }\n if (!entry || typeof entry !== 'object') return null;\n const field = String(entry.field ?? '').trim();\n if (!field) return null;\n // Explicit === true, so a missing key is FALSE rather than truthy-by-accident.\n return {\n field,\n locked: entry.locked === true,\n forceShow: entry.forceShow === true,\n forceReq: entry.forceReq === true,\n };\n}\n\n// normalizeDefaultFieldRules -> Map(fieldKey -> rule). A Map because every\n// consumer wants \"what is the rule for THIS field\", which a Set of keys could\n// no longer answer once membership stopped implying all three intents.\nexport function normalizeDefaultFieldRules(list) {\n const out = new Map();\n (Array.isArray(list) ? list : []).forEach((entry) => {\n const rule = normalizeDefaultFieldRule(entry);\n if (rule && !out.has(rule.field)) out.set(rule.field, rule);\n });\n return out;\n}\n\nexport async function getModuleDefaultFields(module) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/default-fields?module=${encodeURIComponent(module)}`);\n const data = json?.data ?? json ?? {};\n return Array.isArray(data.fields) ? data.fields : [];\n}\n\n// getModuleDefaultFieldRules is what runtime consumers should call: it returns\n// the resolved Map, so no caller re-implements the legacy-string rule.\nexport async function getModuleDefaultFieldRules(module) {\n return normalizeDefaultFieldRules(await getModuleDefaultFields(module));\n}\n\nexport async function saveModuleDefaultFields(module, fields, otp) {\n return fetchJsonWithAuth(AUTH_URL, '/admin/default-fields', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module, fields, otp }),\n });\n}\n\n// Generic email verification — used by any admin-configured\n// field.verifyAction === \"email\" (AddFormV1/EditFormV1's VerifyFieldButton).\n//\n// The backend runs a layered check (syntax -> MX -> Mailgun -> SMTP mailbox\n// probe -> catch-all detection), so the verdict is richer than a boolean:\n//\n// valid keep/reject the address (unchanged meaning — the form gate)\n// result 'deliverable' | 'undeliverable' | 'risky' | 'unknown'\n// mailboxConfirmed the receiving server confirmed THIS mailbox specifically\n// catchAll the domain accepts every address, so the mailbox is unproven\n//\n// mailboxConfirmed is the one to trust for \"this person will actually get mail\".\n// A catch-all domain (Google Workspace default, many corporates) can never be\n// proven from outside — the UI says so rather than implying certainty.\nexport async function verifyEmail(email) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/email-verify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email }),\n });\n const data = json?.data ?? json ?? {};\n return {\n valid: Boolean(data.valid),\n reason: data.reason ?? '',\n result: data.result ?? '',\n mailboxConfirmed: Boolean(data.mailboxConfirmed),\n catchAll: Boolean(data.catchAll),\n disposable: Boolean(data.disposable),\n roleAddress: Boolean(data.roleAddress),\n risk: data.risk ?? '',\n checks: Array.isArray(data.checks) ? data.checks : [],\n };\n}\n\nexport async function getAvailableCollections() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/lookup/collections');\n const data = json.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\nexport async function getCollectionFields(collection) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/lookup/collection-fields?collection=${encodeURIComponent(collection)}`);\n const data = json.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\nexport async function getArraySubfields(module, field) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/field-array-subfields?module=${encodeURIComponent(module)}&field=${encodeURIComponent(field)}`);\n const data = json.data ?? json;\n return Array.isArray(data?.fields) ? data.fields : [];\n}\n\n// ── Menu Modules & Actions (master data management) ──────────────────────────\n\nexport async function getMenuModules() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-modules');\n return firstArray(json.data, json);\n}\n\nexport async function createMenuModule({ menuName, apiUrl = '', menuType = 'menu', parentMenuId = 0, displayOrder = 0 }) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-modules', {\n method: 'POST',\n body: JSON.stringify({ menuName, apiUrl, menuType, parentMenuId, displayOrder }),\n });\n return json.data ?? json;\n}\n\nexport async function updateMenuModule(id, { menuName }) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-modules/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ menuName }),\n });\n return json.data ?? json;\n}\n\nexport async function deleteMenuModule(id) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-modules/${id}`, { method: 'DELETE' });\n return json.data ?? json;\n}\n\nexport async function getMenuActions() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-actions');\n return firstArray(json.data, json);\n}\n\nexport async function createMenuAction({ permissionName, permissionKey }) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-actions', {\n method: 'POST',\n body: JSON.stringify({ permissionName, permissionKey }),\n });\n return json.data ?? json;\n}\n\nexport async function updateMenuAction(id, { permissionName }) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-actions/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ permissionName }),\n });\n return json.data ?? json;\n}\n\nexport async function deleteMenuAction(id) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-actions/${id}`, { method: 'DELETE' });\n return json.data ?? json;\n}\n\nfunction normalizeRolePermissionValue(value) {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return normalizeRolePermissionValue(\n value.value ?? value.access_value ?? value.accessValue ?? value.permissionValue ?? value.enabled,\n );\n }\n if (value === true || value === 1 || value === '1') return '1';\n const normalized = String(value ?? '').trim().toLowerCase();\n return normalized === 'true' || normalized === 'yes' || normalized === 'active' ? '1' : '0';\n}\n\nfunction normalizeRoleMenusForSave(menus = {}) {\n if (!menus || typeof menus !== 'object' || Array.isArray(menus)) return menus;\n return Object.fromEntries(Object.entries(menus).map(([menuName, menu]) => [\n menuName,\n {\n ...menu,\n menuId: menu?.menuId ?? menu?.menu_id ?? 0,\n permissions: Object.fromEntries(Object.entries(menu?.permissions ?? {}).map(([key, entry]) => [\n key,\n {\n ...(entry && typeof entry === 'object' && !Array.isArray(entry) ? entry : {}),\n id: entry?.id ?? entry?.permission_id ?? entry?.permissionId ?? 0,\n value: normalizeRolePermissionValue(entry),\n },\n ])),\n },\n ]));\n}\n\n// ── Permissions ───────────────────────────────────────────────────────────────\n\nexport async function getRolePermissions(roleId) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/get-role-details?roleid=${roleId}`);\n return (json?.data ?? json) ?? {};\n}\n\nexport async function updateRolePermissions(roleId, menus) {\n return fetchJsonWithAuth(AUTH_URL, '/edit-role-details', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ roleId, menus: normalizeRoleMenusForSave(menus) }),\n });\n}\n\n// Resolved permissions for the logged-in user's own role — moduleName ->\n// actionKey -> allowed (or the wildcard shape { \"*\": { \"*\": true } } for\n// full-access roles). Fetched once on login by PermissionContext.\nexport async function getMyPermissions() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/me/permissions');\n return (json?.data ?? json) ?? {};\n}\n\n// ── Custom Forms ──────────────────────────────────────────────────────────────\n\nconst CUSTOM_FORMS_PATH = '/admin/custom-forms';\n\nexport async function getCustomForms(module = '', type = '', action = '') {\n const params = new URLSearchParams();\n if (module) params.append('module', module);\n if (type) params.append('formType', type);\n if (action) params.append('action', action);\n const path = `${CUSTOM_FORMS_PATH}?${params.toString()}`;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\nexport async function createCustomForm(form) {\n return fetchJsonWithAuth(AUTH_URL, CUSTOM_FORMS_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(form),\n });\n}\n\nexport async function updateCustomForm(id, form) {\n return fetchJsonWithAuth(AUTH_URL, `${CUSTOM_FORMS_PATH}/${id}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(form),\n });\n}\n\nexport async function deleteCustomForm(id) {\n return fetchJsonWithAuth(AUTH_URL, `${CUSTOM_FORMS_PATH}/${id}`, { method: 'DELETE' });\n}\n\n// ── Row Action Config ─────────────────────────────────────────────────────────\n\nconst ROW_ACTION_CONFIG_PATH = '/admin/row-action-config';\n\n// Keep the module key written by Admin identical to the key used by ListView.\n// Module labels/keys supplied by the module catalog (or typed into the tags\n// selector) may contain casing and surrounding whitespace, while list routes\n// consistently request normalized keys.\nfunction normalizeRowActionModule(module) {\n return String(module ?? '').trim().toLowerCase();\n}\n\nexport async function getRowActionConfigs(module = '') {\n const normalizedModule = normalizeRowActionModule(module);\n const path = normalizedModule\n ? `${ROW_ACTION_CONFIG_PATH}?module=${encodeURIComponent(normalizedModule)}`\n : ROW_ACTION_CONFIG_PATH;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\nexport async function createRowActionConfig({ module, roleId = 0, rowActions = [] }) {\n const normalizedModule = normalizeRowActionModule(module);\n return fetchJsonWithAuth(AUTH_URL, ROW_ACTION_CONFIG_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module: normalizedModule, roleId, rowActions }),\n });\n}\n\nexport async function updateRowActionConfig(id, rowActions = []) {\n return fetchJsonWithAuth(AUTH_URL, `${ROW_ACTION_CONFIG_PATH}/${id}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ rowActions }),\n });\n}\n\nexport async function deleteRowActionConfig(id) {\n return fetchJsonWithAuth(AUTH_URL, `${ROW_ACTION_CONFIG_PATH}/${id}`, { method: 'DELETE' });\n}\n\nexport async function seedRowActionConfigs() {\n return fetchJsonWithAuth(AUTH_URL, `${ROW_ACTION_CONFIG_PATH}/seed`, { method: 'POST' });\n}\n\n// ── Module Action Rules ───────────────────────────────────────────────────────\n// Per-module configurable rules stored in auth repo, returned by module-data-list.\n// Frontend evaluates these rules per-row to show/hide row action buttons.\n\nconst MODULE_ACTION_RULES_PATH = '/admin/module-action-rules';\n\nexport async function getModuleActionRules(module) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}?module=${encodeURIComponent(module)}`);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\n// allowedActions is an ALLOW-LIST: when the rule fires, every action it does\n// not name is blocked, including actions added later. That is what makes\n// \"Inactive jobs — Edit and Delete only\" expressible as one rule instead of a\n// blockedActions list that has to be revisited every time an action is added.\n//\n// REQ-20260908-01 — it was missing from both of these functions. The admin UI\n// collects it, the Go model stores it, and the evaluator honours it; only the\n// two lines below dropped it, so an admin picked the actions, saw them in the\n// drawer, saved, and the field silently never left the browser. The reported\n// symptom was \"the admin screen does nothing\", and the whole gap was that this\n// key was absent from a destructure and a JSON body.\n//\n// On the empty case, which is the dangerous one: an allow-list of [] would mean\n// \"block every action\". Both ends already guard it — the controller stores nil\n// when the list is empty, and allowedActionsOf() treats an empty array as \"no\n// allow-list\" — so the admin form's default of [] round-trips as \"unset\" rather\n// than as a total lockout. The default here is undefined for programmatic\n// callers that omit the key entirely; it is not load-bearing, and the guard\n// that matters is on the other two layers.\nexport async function createModuleActionRule({ module, name, description = '', conditions = [], blockedActions = [], disabledActions = [], allowedActions = undefined, isActive = true }) {\n return fetchJsonWithAuth(AUTH_URL, MODULE_ACTION_RULES_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module, name, description, conditions, blockedActions, disabledActions, allowedActions, isActive }),\n });\n}\n\nexport async function updateModuleActionRule(id, { name, description = '', conditions = [], blockedActions = [], disabledActions = [], allowedActions = undefined, isActive = true }) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}/${id}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name, description, conditions, blockedActions, disabledActions, allowedActions, isActive }),\n });\n}\n\nexport async function deleteModuleActionRule(id) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}/${id}`, { method: 'DELETE' });\n}\n\n\nexport async function getRoleOnboardingTabs(role, module = 'onboarding') {\n const params = new URLSearchParams({ module, configType: 'tabs', userId: '0', roleId: String(getRoleId(role)) });\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}?${params}`);\n return json.data ?? json;\n}\n\nexport async function saveRoleOnboardingTabs(role, onboardingTabs, module = 'onboarding') {\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/role`, {\n method: 'PUT', body: JSON.stringify({ module, roleId: getRoleId(role), configType: 'tabs', onboardingTabs }),\n });\n}\n","// How a result row opens the record's detail view.\n//\n// Same vocabulary as the search form's action button (`actionButtonOpenAs` /\n// `OPEN_AS_OPTIONS` in searchBuilder/actionButtons.js) so an admin only learns\n// one set of words:\n//\n// self full route swap to /<module>/detailview/:id — the browser default,\n// and what every list did before this existed.\n// newTab the detail page in a new browser tab.\n// popup a Modal over the search page (/popup/<module>-detail/:id with a\n// `background` location) — the search stays mounted, so typed filters\n// survive. THE DEFAULT for the Sourcing search results.\n//\n// Options are defined inline (not imported from actionButtons.js) to avoid an\n// import cycle: services/detailDefaults.js imports this module.\nexport const DETAIL_VIEW_MODE_OPTIONS = [\n { value: 'self', label: 'Same page' },\n { value: 'newTab', label: 'New tab' },\n { value: 'popup', label: 'Popup' },\n];\n\nexport const DETAIL_VIEW_MODE_VALUES = DETAIL_VIEW_MODE_OPTIONS.map((o) => o.value);\n\n// Accepts any casing / spelling the config might carry (\"popUp\", \"POPUP\", \"\") —\n// anything unrecognised (including empty/undefined) resolves to 'popup', the\n// default. Only an explicit 'self' opts out of interception.\nexport function normalizeDetailViewMode(mode) {\n return { self: 'self', newtab: 'newTab', popup: 'popup' }[String(mode ?? '').trim().toLowerCase()] || 'popup';\n}\n\n// Route the popup mode navigates to. The host app registers exactly this path\n// (App.jsx, inside the `{background && <Routes>}` block). Fixed rather than\n// derived from moduleName: the search component runs as moduleName=\"candidates\"\n// (plural), and interpolating that produced \"/popup/candidates-detail/:id\",\n// which matched no route and silently rendered nothing.\nexport const DETAIL_VIEW_POPUP_PATH = '/popup/candidate-detail';\n\n// `href` is an already-resolved internal path, e.g. \"/candidates/detailview/<id>\"\n// (optionally with a query string / hash). Returns true when it has taken over\n// navigation; false means \"not handled — let the normal <Link>/navigate proceed\"\n// (which is the 'self' case).\n//\n// `location` is optional — the page to return to when the popup closes. When\n// omitted it is taken from window.location, so callers do not need useLocation().\nexport function runDetailViewOpen(mode, { href, navigate, location } = {}) {\n if (!href) return false;\n const resolved = normalizeDetailViewMode(mode);\n\n if (resolved === 'self') return false;\n\n if (resolved === 'newTab') {\n window.open(href, '_blank', 'noopener');\n return true;\n }\n\n // popup\n const str = String(href);\n const id = (str.split('/detailview/')[1] ?? str.split('/detail-view/')[1])?.split(/[/?#]/)[0];\n if (!id || typeof navigate !== 'function') return false;\n const background = location\n || (typeof window !== 'undefined'\n ? {\n pathname: window.location.pathname,\n search: window.location.search,\n hash: window.location.hash,\n }\n : undefined);\n navigate(`${DETAIL_VIEW_POPUP_PATH}/${id}`, { state: { background } });\n return true;\n}\n","// Central source-of-truth for detail-view DISPLAY defaults.\n//\n// These values used to be hard-coded inside components/detail/fileUtils.js and\n// components/detail/FieldValue.jsx. They now live here, are seeded with the same\n// built-in defaults, and are overridable from the admin page\n// (Admin → Detail Groups → Display Defaults), which persists them via\n// GET/POST /admin/detail-defaults.\n//\n// This is a plain module (not a React component / context) so fileUtils.js — a\n// non-component helper — can read it synchronously. Consumers read getDefaults()\n// at render time; loadDetailDefaults() fetches the saved values once and updates\n// the store (falling back to the built-ins on any failure).\n\nimport { getDetailDefaults as fetchDetailDefaults } from './adminApi';\nimport { DETAIL_VIEW_MODE_VALUES } from '../components/detailViewMode';\n\n// Built-in defaults — exactly the previous hard-coded behaviour. S3 base still\n// honours VITE_S3_BASE_URL as the env-level seed; the admin value overrides it.\nexport const BUILT_IN_DEFAULTS = Object.freeze({\n s3BaseUrl: (\n import.meta.env?.VITE_S3_BASE_URL ||\n 'https://zinnext-devlopment-ap-south-1.s3.ap-south-1.amazonaws.com'\n ).replace(/\\/+$/, ''),\n // A pure CALENDAR date — a date of birth, an expiry. No time, and no zone\n // suffix: stamping a clock on a birthday claims precision it does not have.\n dateFormat: 'DD MMM YYYY',\n // An INSTANT — created/updated stamps, interview slots. Carries the time and,\n // unless switched off below, the zone it is being shown in. Without the zone\n // a timestamp is ambiguous the moment two people in different countries read\n // it.\n dateTimeFormat: 'MMM DD, YYYY | hh:mm A',\n showTimeZoneLabel: true,\n // The tenant's clock. EMPTY means \"use the viewer's browser zone\", which is\n // exactly the behaviour before this setting existed — so nothing changes for\n // a tenant that never sets it. See services/timezone.js.\n timeZone: '',\n // Short labels for zones (\"Asia/Kolkata\" → \"IST\"). Abbreviations are NOT\n // valid IANA identifiers and are dangerously ambiguous as inputs, so they\n // exist only as display labels here.\n timeZoneAliases: {},\n // Which per-region rule profile form groups apply (\"US\"/\"UK\"/\"IND\"/'' for\n // the default). Carries no logic — it is a KEY into group.regionRules, which\n // is what lets a new market be added as config rather than as code.\n region: '',\n booleanTrueLabel: 'Yes',\n booleanFalseLabel: 'No',\n emptyPlaceholder: '-',\n // Per-render-type empty texts. The single `emptyPlaceholder` above could only\n // ever say one thing (\"-\"), which cannot distinguish \"no work experience\" from\n // \"no document\" and reads as a rendering fault rather than as information.\n // Built-in per-type defaults live in components/detail/emptyText.js; anything\n // set here overrides them, and `default` covers every unlisted type.\n emptyTexts: {},\n separator: ' - ',\n documentNameOrder: ['name', 'documentName', 'uploadName', 'uploadedFileName', 'uniqueName'],\n // Common phone-number format — one place, applied everywhere (forms + detail),\n // exactly like `dateFormat`. Groups of digit counts separated by a literal\n // separator: \"3-3-4\" → 999-878-3413 (total 10 digits). Change it here (or in\n // Admin → Display Defaults) and every phone field re-formats to match.\n phoneFormat: '3-3-4',\n // LAST-RESORT country code, used only when the record itself carries none\n // (see components/detail/phoneDisplay.js). Deliberately EMPTY: a tenant that\n // wants every unqualified number stamped \"+1\" sets it in Admin → Display\n // Defaults, but nothing invents a country for a record that never stated one\n // — that is exactly how every candidate ended up displayed as \"+1\".\n phoneCountryCode: '',\n // How a candidate row in the Sourcing search results opens the detail view:\n // 'popup' (a modal over the search — the default, so typed search filters\n // survive) | 'self' (full route swap) | 'newTab'. Same vocabulary as\n // actionButtonOpenAs; see components/detailViewMode.js. This is the tenant\n // FALLBACK — a per-search-form `detailViewOpenMode` wins over it. An admin\n // sets it in Admin > Detail Groups > Display Defaults.\n candidateDetailViewMode: 'popup',\n});\n\nlet current = { ...BUILT_IN_DEFAULTS };\nlet loadPromise = null;\n\n// getDefaults returns the live defaults (built-ins until loadDetailDefaults runs).\nexport function getDefaults() {\n return current;\n}\n\n// sanitize keeps only the keys that carry a real value, so a partial/empty saved\n// config never blanks out a built-in.\nfunction sanitize(d) {\n if (!d || typeof d !== 'object') return {};\n const out = {};\n if (d.s3BaseUrl) out.s3BaseUrl = String(d.s3BaseUrl).replace(/\\/+$/, '');\n if (d.dateFormat) out.dateFormat = d.dateFormat;\n if (d.dateTimeFormat) out.dateTimeFormat = d.dateTimeFormat;\n // A saved `false` is a real setting (\"never print the zone\"), so the key is\n // honoured whenever it is present as a boolean rather than only when truthy.\n if (typeof d.showTimeZoneLabel === 'boolean') out.showTimeZoneLabel = d.showTimeZoneLabel;\n // '' is meaningful (fall back to the browser zone), so any string is honoured.\n if (typeof d.timeZone === 'string') out.timeZone = d.timeZone.trim();\n // '' is meaningful: \"use each group's own defaults, no region profile\".\n if (typeof d.region === 'string') out.region = d.region.trim();\n if (d.timeZoneAliases && typeof d.timeZoneAliases === 'object' && !Array.isArray(d.timeZoneAliases)) {\n out.timeZoneAliases = { ...current.timeZoneAliases, ...d.timeZoneAliases };\n }\n if (d.booleanTrueLabel) out.booleanTrueLabel = d.booleanTrueLabel;\n if (d.booleanFalseLabel) out.booleanFalseLabel = d.booleanFalseLabel;\n if (d.emptyPlaceholder) out.emptyPlaceholder = d.emptyPlaceholder;\n // Objects merge key-wise rather than replacing wholesale, so configuring one\n // render type does not blank out the others.\n if (d.emptyTexts && typeof d.emptyTexts === 'object' && !Array.isArray(d.emptyTexts)) {\n const texts = {};\n Object.entries(d.emptyTexts).forEach(([key, value]) => {\n if (typeof value === 'string' && value.trim() !== '') texts[key] = value.trim();\n });\n if (Object.keys(texts).length) out.emptyTexts = { ...current.emptyTexts, ...texts };\n }\n if (typeof d.separator === 'string' && d.separator !== '') out.separator = d.separator;\n if (typeof d.phoneFormat === 'string' && d.phoneFormat.trim() !== '') out.phoneFormat = d.phoneFormat.trim();\n // Unlike the others, an EMPTY country code is a meaningful setting (\"stamp\n // nothing on a record that stated no country\"), so '' is honoured instead of\n // being treated as \"unset, keep the built-in\".\n if (typeof d.phoneCountryCode === 'string') out.phoneCountryCode = d.phoneCountryCode.trim();\n // A saved empty string is meaningful here (\"show no code at all\"), so unlike\n // the others this key is accepted whenever it is present as a string.\n if (typeof d.phoneCountryCode === 'string') out.phoneCountryCode = d.phoneCountryCode.trim();\n if (Array.isArray(d.documentNameOrder) && d.documentNameOrder.length) out.documentNameOrder = d.documentNameOrder;\n // Only a recognised value (self | newTab | popup) overrides the built-in;\n // anything else — empty, or a stale 'redirect' from an earlier build — is\n // ignored, so the store keeps the 'popup' default.\n if (DETAIL_VIEW_MODE_VALUES.includes(d.candidateDetailViewMode)) {\n out.candidateDetailViewMode = d.candidateDetailViewMode;\n }\n // Per-module format for FORM date pickers ({ candidates: 'MM/DD/YYYY' }).\n // Replaced wholesale — it is one admin-edited map, and {} is how it is cleared.\n if (d.formDateFormats && typeof d.formDateFormats === 'object' && !Array.isArray(d.formDateFormats)) {\n const formats = {};\n Object.entries(d.formDateFormats).forEach(([module, format]) => {\n if (typeof format === 'string' && format.trim() !== '') formats[module] = format.trim();\n });\n out.formDateFormats = formats;\n }\n return out;\n}\n\n// ── Phone number formatting (common, config-driven) ──────────────────────────\n// The format string is groups of digit counts joined by a literal separator,\n// e.g. \"3-3-4\" → [3,3,4] joined by \"-\". parsePhoneFormat returns { groups, sep,\n// total } so both the formatter and the length validation share one definition.\nexport function parsePhoneFormat(format = getPhoneFormat()) {\n const groups = (String(format).match(/\\d+/g) ?? ['3', '3', '4']).map(Number).filter((n) => n > 0);\n const sep = (String(format).match(/\\D+/)?.[0]) ?? '-';\n const safeGroups = groups.length ? groups : [3, 3, 4];\n return { groups: safeGroups, sep, total: safeGroups.reduce((a, b) => a + b, 0) };\n}\n\n// Live phone format from the same singleton the detail defaults use.\nexport function getPhoneFormat() {\n return current.phoneFormat || BUILT_IN_DEFAULTS.phoneFormat;\n}\n\n// The tenant-wide fallback country code — see BUILT_IN_DEFAULTS for why it is\n// empty by default. Consumed by components/detail/phoneDisplay.js as the LAST\n// resort, after the record's own code.\nexport function getPhoneCountryCode() {\n return current.phoneCountryCode ?? BUILT_IN_DEFAULTS.phoneCountryCode;\n}\n\n// Strips everything but digits, capped at the configured total (default 10).\nexport function phoneDigits(value, format = getPhoneFormat()) {\n const { total } = parsePhoneFormat(format);\n return String(value ?? '').replace(/\\D/g, '').slice(0, total);\n}\n\n// formatPhone turns any input into the configured mask as the user types:\n// \"9998783413\" → \"999-878-3413\". Partial input formats progressively\n// (\"99987\" → \"999-87\"); non-digits are ignored.\nexport function formatPhone(value, format = getPhoneFormat()) {\n const { groups, sep } = parsePhoneFormat(format);\n const digits = phoneDigits(value, format);\n if (!digits) return '';\n const chunks = [];\n let i = 0;\n for (const size of groups) {\n if (i >= digits.length) break;\n chunks.push(digits.slice(i, i + size));\n i += size;\n }\n return chunks.join(sep);\n}\n\n// applyDefaults merges a (partial) config over the current store immediately —\n// used by the admin page right after a successful save so the change is live\n// without a reload.\nexport function applyDefaults(partial) {\n const clean = sanitize(partial);\n Object.keys(clean).forEach((key) => configuredKeys.add(key));\n current = { ...current, ...clean };\n return current;\n}\n\n// Keys a tenant has actually SAVED, as opposed to built-ins. getDefaults()\n// cannot tell the two apart, and some consumers must: see\n// getConfiguredDateFormat.\nconst configuredKeys = new Set();\n\n// getConfiguredDateFormat — the tenant's saved `dateFormat`, or undefined when\n// the tenant never set one.\n//\n// Date PICKERS and the LIST VIEW read this instead of getDefaults().dateFormat\n// on purpose. Their behaviour predates the display defaults (antd's picker\n// default and the list's own layout), and the built-in 'DD MMM YYYY' was only\n// ever the DETAIL view's default. Falling back to it here would silently\n// re-format every picker and list column of every tenant that never asked for\n// it. So: a tenant that saves a date format (Admin → Display Defaults) gets it\n// everywhere; a tenant that does not sees exactly what it saw before.\nexport function getConfiguredDateFormat() {\n return configuredKeys.has('dateFormat') ? current.dateFormat : undefined;\n}\n\n// getFormDateFormat — the format a module's Add/Edit form DATE PICKERS use, from\n// Admin → Display Defaults → formDateFormats, or undefined when that module has\n// none (the caller then falls back to getConfiguredDateFormat). Module names\n// arrive as either \"candidate\" or \"candidates\", so both spellings match.\nexport function getFormDateFormat(moduleName) {\n const formats = current.formDateFormats;\n const key = String(moduleName ?? '').trim().toLowerCase();\n if (!formats || !key) return undefined;\n const base = key.endsWith('s') ? key.slice(0, -1) : key;\n const match = Object.keys(formats).find((module) => {\n const candidate = module.trim().toLowerCase();\n return candidate === key || candidate === base || candidate === `${base}s`;\n });\n return match ? formats[match] : undefined;\n}\n\n// loadDetailDefaults fetches the saved defaults once (cached). Safe to call from\n// anywhere — failures silently keep the built-ins. Pass force=true to refetch.\nexport function loadDetailDefaults(force = false) {\n if (loadPromise && !force) return loadPromise;\n // Promise.resolve().then(...) so a synchronous throw from fetchDetailDefaults\n // (e.g. a test that partially mocks adminApi and leaves it undefined) lands in\n // the .catch below and keeps the built-ins, exactly like a network failure.\n loadPromise = Promise.resolve()\n .then(() => fetchDetailDefaults())\n .then((d) => applyDefaults(d))\n .catch(() => current);\n return loadPromise;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// timezone — the application's clock.\n//\n// THE REQUIREMENT\n// \"We work with multiple locations, so we need to see the time based on\n// location. Set up a common time setup like GST, UTC, IST — list all the time\n// zones, and if I select the time zone the entire application has to work on\n// that time zone. It won't reflect the system time.\"\n//\n// So: one tenant-wide zone, chosen in admin, used for RENDERING every\n// date-time AND for interpreting what the user types — never the browser's.\n//\n// WHERE THE SETTING LIVES\n// detailViewDefaults.timeZone, alongside dateFormat / phoneFormat / the empty\n// texts. That store already exists, is already tenant-scoped, and is already\n// loaded once at startup — a second settings store would only create a second\n// thing to keep in sync.\n//\n// THE ZONE LIST IS NOT HARDCODED\n// It comes from Intl.supportedValuesOf('timeZone') — every IANA zone the\n// browser knows. The abbreviations the requirement names (IST, GST, UTC) are\n// not IANA identifiers, so they are provided as an admin-editable ALIAS map\n// (detailViewDefaults.timeZoneAliases) that labels the real zones. Nothing in\n// this file enumerates a country.\n//\n// ── THE CALENDAR-DATE TRAP (the important part) ──────────────────────────\n// A date of birth, a passport expiry, an education start date are CALENDAR\n// dates: \"7 July 2026\" means the same thing in Dubai and in New York. Passing\n// one through a timezone conversion shifts it by a day for half the world's\n// zones — silently corrupting data that was never about an instant in time.\n//\n// So conversion is OPT-IN, never blanket: only `datetime`/`time` fields, or a\n// field explicitly marked `tzAware`, are converted. Plain `date` fields keep\n// their calendar semantics. See shouldConvertToZone below.\n// ─────────────────────────────────────────────────────────────────────────\nimport dayjs from 'dayjs';\nimport utc from 'dayjs/plugin/utc';\nimport timezonePlugin from 'dayjs/plugin/timezone';\nimport { getDefaults } from './detailDefaults';\n\ndayjs.extend(utc);\ndayjs.extend(timezonePlugin);\n\n// The browser's own zone, used when the tenant has not chosen one. Resolved\n// lazily and cached: dayjs.tz.guess() reads Intl on every call.\nlet guessed = null;\nfunction browserZone() {\n if (guessed === null) {\n try {\n guessed = dayjs.tz.guess() || 'UTC';\n } catch {\n guessed = 'UTC';\n }\n }\n return guessed;\n}\n\n/**\n * isValidZone — is this a zone we are willing to run the application on?\n *\n * Deliberately STRICTER than Intl. ICU accepts bare abbreviations, but does so\n * inconsistently and with traps that would be invisible until a DST boundary:\n *\n * 'IST' → Asia/Calcutta (yet IST is equally Irish and Israel Standard Time)\n * 'EST' → America/Panama (a fixed -05:00 that NEVER shifts to EDT, so a\n * tenant picking \"EST\" would silently be an hour\n * wrong for two-thirds of the year)\n * 'GST' → rejected entirely\n *\n * So an accepted zone must be a real IANA identifier — \"Area/Location\", or the\n * one legitimate bare name, UTC. Abbreviations remain available to users as\n * LABELS through the alias map, where they are unambiguous because they point\n * at a specific IANA zone.\n */\nexport function isValidZone(zone) {\n const name = String(zone ?? '').trim();\n if (!name) return false;\n if (name !== 'UTC' && !name.includes('/')) return false;\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: name });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * getAppTimeZone — the zone everything renders and is interpreted in.\n *\n * An unset or unknown value falls back to the browser's zone, so the app keeps\n * working exactly as it did before anyone configured this. Written so a\n * per-user override can be layered in later without touching any call site.\n */\nexport function getAppTimeZone(defaults = getDefaults()) {\n const configured = String(defaults?.timeZone ?? '').trim();\n return isValidZone(configured) ? configured : browserZone();\n}\n\n/** appNow — \"now\", in the application's zone. */\nexport function appNow(defaults) {\n return dayjs().tz(getAppTimeZone(defaults));\n}\n\n/**\n * toApp — read an instant (ISO string, Date, dayjs, epoch ms) as it appears in\n * the application's zone. Invalid input returns an invalid dayjs, so callers\n * can keep using .isValid() exactly as they do now.\n */\nexport function toApp(value, defaults) {\n const parsed = dayjs(value?.$date ?? value);\n return parsed.isValid() ? parsed.tz(getAppTimeZone(defaults)) : parsed;\n}\n\n/**\n * formatApp — the one formatter. Falls back to the tenant's configured\n * dateFormat, so changing the format still happens in exactly one place.\n */\nexport function formatApp(value, format, defaults = getDefaults()) {\n const d = toApp(value, defaults);\n if (!d.isValid()) return '';\n return d.format(format || defaults?.dateFormat || 'DD MMM YYYY');\n}\n\n/**\n * tzShortLabel — the small suffix printed after a TIME so a reader knows which\n * clock they are looking at: \"IST\", \"GST\", or the zone's own name when nobody\n * has given it a short one.\n *\n * Deliberately NOT printed after a plain calendar date. A date of birth has no\n * time and no zone; stamping one on it claims a precision the value does not\n * have.\n */\nexport function tzShortLabel(defaults = getDefaults(), at) {\n const zone = getAppTimeZone(defaults);\n const alias = aliasFor(zone, defaults);\n // A zone that changes abbreviation across the year is stored as a PAIR\n // (\"EST/EDT\"), because the table cannot know which applies. Given the instant\n // being printed we can: a timestamp reading \"12:37 AM EST/EDT\" tells the\n // reader the two things it might be and leaves them to work out which, which\n // is precisely the ambiguity the suffix exists to remove.\n if (alias && alias.includes('/')) {\n const inEffect = zoneAbbreviation(zone, at);\n if (inEffect) {\n const halves = alias.split('/').map((half) => half.trim());\n const matched = halves.find((half) => half.toUpperCase() === inEffect.toUpperCase());\n return matched || inEffect;\n }\n // No abbreviation available: the pair is still more informative than the\n // raw zone name, so it stands.\n }\n return alias || zoneAbbreviation(zone, at) || zone;\n}\n\n/**\n * zoneAbbreviation — the short name a zone actually goes by AT a given instant\n * (\"EDT\" in August, \"EST\" in January), or '' when it has no letter form.\n *\n * Intl answers this correctly including the daylight-saving rules, which is why\n * it is asked rather than a lookup table: the rules change, and a table that\n * says \"EST/EDT\" is a table admitting it does not know.\n *\n * Zones with no common abbreviation come back as an offset (\"GMT+5:30\"). Those\n * are rejected here so the caller falls through to its configured alias — India\n * is written \"IST\", never \"GMT+5:30\".\n */\nexport function zoneAbbreviation(zone, at) {\n try {\n const when = at === undefined ? new Date() : new Date(dayjs(at?.$date ?? at).valueOf());\n if (Number.isNaN(when.valueOf())) return '';\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: zone,\n timeZoneName: 'short',\n }).formatToParts(when);\n const name = parts.find((part) => part.type === 'timeZoneName')?.value ?? '';\n // Letters only. \"GMT+5:30\" is an offset wearing a name badge.\n return /^[A-Za-z]+$/.test(name) ? name : '';\n } catch {\n // An unknown zone throws rather than guessing. The caller has a fallback.\n return '';\n }\n}\n\n/**\n * formatAppDateTime — an INSTANT, in the tenant's zone, with the zone named.\n *\n * \"Aug 06, 2026 | 07:03 PM IST\"\n *\n * The format and whether to show the zone are both config, so a tenant can\n * change either without a deploy. The zone suffix is what turns an ambiguous\n * timestamp into a fact: without it, a team spread across countries cannot tell\n * whether 07:03 PM is theirs or someone else's.\n */\nexport function formatAppDateTime(value, defaults = getDefaults()) {\n const shown = formatApp(value, defaults?.dateTimeFormat || 'MMM DD, YYYY | hh:mm A', defaults);\n if (!shown) return '';\n if (defaults?.showTimeZoneLabel === false) return shown;\n // The label is resolved FOR THIS INSTANT, so a summer timestamp reads \"EDT\"\n // and a winter one \"EST\" — rather than both reading \"EST/EDT\".\n const label = tzShortLabel(defaults, value);\n return label ? `${shown} ${label}` : shown;\n}\n\n/**\n * shouldConvertToZone — may this field's value be moved between zones?\n *\n * FALSE for plain calendar dates. This is the guard described in the header,\n * and it is deliberately conservative: a field must SAY it carries an instant\n * (type datetime/time) or opt in with `tzAware`, otherwise it is left alone.\n * Being wrong in this direction shows a time in the wrong zone; being wrong in\n * the other direction changes a stored date by a day.\n */\nexport function shouldConvertToZone(field) {\n if (!field) return false;\n if (field.tzAware === true) return true;\n if (field.tzAware === false) return false;\n const type = String(field.type ?? '').toLowerCase();\n return type === 'datetime' || type === 'time' || type === 'datetime-local';\n}\n\n/**\n * fromAppInput — a picker value the user entered MEANING the application's\n * zone, converted to the correct absolute instant for storage.\n *\n * A DatePicker hands back a dayjs in the BROWSER's zone. If the tenant zone is\n * Asia/Dubai and the user picks 09:00, they mean 09:00 in Dubai — storing the\n * browser's 09:00 would be a different moment entirely.\n */\nexport function fromAppInput(value, defaults) {\n // An EMPTY input is not a moment, and must never become one.\n //\n // dayjs(undefined) returns the CURRENT time and reports isValid() === true —\n // so without this guard an untouched picker was serialised as \"now\". On the\n // submission form that wrote the moment of saving into interviewAvailability\n // .startTime AND .endTime, giving every submission an interview slot the\n // recruiter never entered, with both ends identical. dayjs(null) and\n // dayjs('') are correctly invalid; only `undefined` has this behaviour, which\n // is exactly the shape a registered-but-empty antd picker hands back.\n if (value === undefined || value === null || value === '') return null;\n const d = dayjs(value);\n if (!d.isValid()) return null;\n const zone = getAppTimeZone(defaults);\n // Re-interpret the WALL-CLOCK reading in the target zone, rather than\n // converting the instant (which would keep the wrong moment and merely\n // relabel it).\n return dayjs.tz(d.format('YYYY-MM-DDTHH:mm:ss'), zone);\n}\n\n/**\n * zoneOffsetLabel — \"UTC+05:30\" for a zone, at the current moment.\n * Computed rather than tabulated, so it stays correct across DST.\n */\nexport function zoneOffsetLabel(zone = getAppTimeZone()) {\n try {\n const minutes = dayjs().tz(zone).utcOffset();\n const sign = minutes < 0 ? '-' : '+';\n const abs = Math.abs(minutes);\n const hh = String(Math.floor(abs / 60)).padStart(2, '0');\n const mm = String(abs % 60).padStart(2, '0');\n return `UTC${sign}${hh}:${mm}`;\n } catch {\n return '';\n }\n}\n\n/**\n * tzLabel — what a viewer sees next to a time so they know WHICH zone they are\n * reading: \"IST (UTC+05:30)\" when an alias names it, else\n * \"Asia/Kolkata (UTC+05:30)\".\n */\nexport function tzLabel(defaults = getDefaults()) {\n const zone = getAppTimeZone(defaults);\n const alias = aliasFor(zone, defaults);\n return `${alias || zone} (${zoneOffsetLabel(zone)})`;\n}\n\n// Built-in aliases covering the abbreviations the requirement names, plus the\n// common business zones. Admin-editable via detailViewDefaults.timeZoneAliases;\n// anything configured there wins, and unknown zones simply have no alias.\nexport const BUILT_IN_TZ_ALIASES = Object.freeze({\n UTC: 'UTC',\n 'Asia/Kolkata': 'IST',\n 'Asia/Calcutta': 'IST',\n 'Asia/Dubai': 'GST',\n 'America/New_York': 'EST/EDT',\n 'America/Chicago': 'CST/CDT',\n 'America/Denver': 'MST/MDT',\n 'America/Los_Angeles': 'PST/PDT',\n 'Europe/London': 'GMT/BST',\n 'Europe/Berlin': 'CET/CEST',\n 'Asia/Singapore': 'SGT',\n 'Asia/Tokyo': 'JST',\n 'Australia/Sydney': 'AEST/AEDT',\n});\n\n/** aliasFor — the short name for a zone, config first. */\nexport function aliasFor(zone, defaults = getDefaults()) {\n const configured = defaults?.timeZoneAliases ?? {};\n return configured[zone] ?? BUILT_IN_TZ_ALIASES[zone] ?? '';\n}\n\n/**\n * listTimeZones — every zone the runtime knows, labelled with its alias and\n * current offset, sorted by offset then name so the picker reads like a map\n * rather than an alphabetical wall.\n *\n * Returns [{ value, label, alias, offsetLabel, offsetMinutes }].\n */\nexport function listTimeZones(defaults = getDefaults()) {\n let enumerated = [];\n try {\n enumerated = Intl.supportedValuesOf('timeZone') ?? [];\n } catch {\n // Older runtimes cannot enumerate at all; the union below still yields the\n // named zones, so the picker is never empty.\n enumerated = [];\n }\n\n // UNION, not just the enumerated list. ICU builds disagree about which name\n // is canonical: this runtime enumerates \"Asia/Calcutta\" and omits both\n // \"Asia/Kolkata\" and \"UTC\", yet accepts all three. Listing only what is\n // enumerated would therefore hide IST and UTC — two of the three zones the\n // requirement names by hand — on some machines and not others.\n // Everything is validated, so an alias for a zone this runtime does not know\n // is dropped rather than offered and then failing at format time.\n const named = ['UTC', ...Object.keys(BUILT_IN_TZ_ALIASES), ...Object.keys(defaults?.timeZoneAliases ?? {})];\n const zones = [...new Set([...named, ...enumerated])].filter(isValidZone);\n\n return zones\n .map((zone) => {\n let offsetMinutes = 0;\n try {\n offsetMinutes = dayjs().tz(zone).utcOffset();\n } catch {\n return null;\n }\n const alias = aliasFor(zone, defaults);\n const offsetLabel = zoneOffsetLabel(zone);\n return {\n value: zone,\n alias,\n offsetLabel,\n offsetMinutes,\n label: `${alias ? `${alias} — ` : ''}${zone} (${offsetLabel})`,\n };\n })\n .filter(Boolean)\n .sort((a, b) => a.offsetMinutes - b.offsetMinutes || a.value.localeCompare(b.value));\n}\n","// Dot-notation module names: \"job.client.company\" means \"fetch the `job`\n// module, then display the data found at `client.company` inside each record\".\n// Only the segment before the first dot is a real backend module — every API\n// call (module-data-list, field-config, permissions) must use it, while the\n// remaining segments are resolved client-side against each fetched record.\n\nexport function parseModulePath(moduleName) {\n const raw = String(moduleName ?? '').trim();\n if (!raw) return { baseModule: '', nestedPath: '' };\n\n const [baseModule, ...rest] = raw.split('.');\n return {\n baseModule: baseModule.trim(),\n nestedPath: rest.map((part) => part.trim()).filter(Boolean).join('.'),\n };\n}\n\nexport function getBaseModuleName(moduleName) {\n return parseModulePath(moduleName).baseModule;\n}\n\nexport function getValueAtPath(source, path) {\n return String(path ?? '')\n .split('.')\n .filter(Boolean)\n .reduce((value, key) => (value == null ? undefined : value[key]), source);\n}\n","import { fetchJsonWithAuth, apiGetWithAuth } from './authApi';\nimport { AUTH_URL, SUBMISSIONS_URL } from './apiConfig';\nimport { normalizeDiceSkills, resolveDiceProfileId } from './diceCandidateMapper';\nimport { getBaseModuleName } from '../utils/modulePath';\n\nconst TEST_FLOW_MODULE = 'test-flows';\nconst LMS_FLOW_MODULE = 'lms-flows';\n\nconst TEST_FLOW_FIELDS = [\n { value: 'module', label: 'Module', isVisible: true, type: 'text' },\n { value: 'flow', label: 'Flow', isVisible: true, type: 'text' },\n { value: 'step_order', label: 'Step Order', isVisible: true, type: 'number' },\n { value: 'keyword', label: 'Action', isVisible: true, type: 'text' },\n { value: 'description', label: 'Description', isVisible: true, type: 'text' },\n { value: 'target', label: 'Key', isVisible: true, type: 'text' },\n { value: 'tags', label: 'Tags', isVisible: true, type: 'text' },\n { value: 'value', label: 'Value', isVisible: true, type: 'text' },\n { value: 'expected', label: 'Expected', isVisible: true, type: 'text' },\n];\n\nconst LMS_FLOW_FIELDS = [\n { value: 'module', label: 'Module', isVisible: true, type: 'text' },\n { value: 'flow', label: 'Flow', isVisible: true, type: 'text' },\n { value: 'step_order', label: 'Step Order', isVisible: true, type: 'number' },\n { value: 'keyword', label: 'Action', isVisible: true, type: 'text' },\n { value: 'description', label: 'Description', isVisible: true, type: 'text' },\n { value: 'target', label: 'Key', isVisible: true, type: 'text' },\n { value: 'tags', label: 'Tags', isVisible: true, type: 'text' },\n { value: 'value', label: 'Value', isVisible: true, type: 'text' },\n { value: 'expected', label: 'Expected', isVisible: true, type: 'text' },\n];\n\nfunction normalizeCandidateSearchSource(value) {\n const values = (Array.isArray(value) ? value : [value])\n .flat()\n .map((item) => {\n if (item && typeof item === 'object') {\n return item.value ?? item.label ?? item.name ?? '';\n }\n return item;\n })\n .map((item) => String(item ?? '').trim().toLowerCase())\n .filter(Boolean);\n\n if (values.includes('dice')) return 'dice';\n if (values.includes('internal')) return 'internal';\n return values[0] || '';\n}\n\nfunction firstFiniteNumber(...values) {\n for (const value of values) {\n const number = Number(value);\n if (Number.isFinite(number)) return number;\n }\n\n return undefined;\n}\n\nfunction extractResponseTotal(response, payload, rows, scope = '') {\n const responseCount = response?.count;\n const payloadCount = payload?.count;\n const normalizedScope = String(scope ?? '').trim().toLowerCase();\n\n if (responseCount && typeof responseCount === 'object') {\n const total = firstFiniteNumber(\n normalizedScope ? responseCount[normalizedScope] : undefined,\n responseCount.total,\n responseCount.searchCount,\n responseCount.count,\n );\n if (total !== undefined) return total;\n }\n\n if (payloadCount && typeof payloadCount === 'object') {\n const total = firstFiniteNumber(\n normalizedScope ? payloadCount[normalizedScope] : undefined,\n payloadCount.total,\n payloadCount.searchCount,\n payloadCount.count,\n );\n if (total !== undefined) return total;\n }\n\n return firstFiniteNumber(\n response?.total,\n typeof responseCount !== 'object' ? responseCount : undefined,\n response?.totalCount,\n payload?.total,\n typeof payloadCount !== 'object' ? payloadCount : undefined,\n payload?.totalCount,\n rows.length,\n ) ?? 0;\n}\n\nfunction extractResponseCounts(response, payload) {\n const counts = {};\n [response?.count, payload?.count].forEach((count) => {\n if (!count || typeof count !== 'object') return;\n\n Object.entries(count).forEach(([key, value]) => {\n const number = Number(value);\n if (Number.isFinite(number)) counts[String(key).trim().toLowerCase()] = number;\n });\n });\n\n return counts;\n}\n\nexport async function getDropdownFields(module) {\n if (module === TEST_FLOW_MODULE) return TEST_FLOW_FIELDS;\n if (module === LMS_FLOW_MODULE) return LMS_FLOW_FIELDS;\n // Dot-notation names (\"job.client\") target nested data client-side; the\n // backend only knows the base module.\n const baseModule = getBaseModuleName(module);\n return apiGetWithAuth(SUBMISSIONS_URL, `/filter-dropdown-fields?module=${encodeURIComponent(baseModule)}`);\n}\n\nexport async function getFieldConfig(module) {\n if (module === TEST_FLOW_MODULE) return TEST_FLOW_FIELDS;\n if (module === LMS_FLOW_MODULE) return LMS_FLOW_FIELDS;\n return apiGetWithAuth(AUTH_URL, `/admin/field-config?module=${encodeURIComponent(getBaseModuleName(module))}`);\n}\n\nexport async function getDropdownValues(module, field, search = '', limit = 50, offset = 0, meta = {}) {\n const params = new URLSearchParams({\n module: getBaseModuleName(module), field, value: search,\n limit: String(limit), offset: String(offset),\n });\n if (meta.dataSource) params.set('dataSource', meta.dataSource);\n if (meta.masterName) params.set('masterName', meta.masterName);\n if (meta.groupName) params.set('groupName', meta.groupName);\n if (meta.scope) params.set('scope', meta.scope);\n if (meta.arrayLabelField) params.set('arrayLabelField', meta.arrayLabelField);\n if (meta.arrayValueField) params.set('arrayValueField', meta.arrayValueField);\n // The Auth gateway owns the admin form configuration and understands\n // dataSource/masterName. Keeping this generic lets every configured master,\n // module and lookup field work without module-specific UI code.\n return apiGetWithAuth(AUTH_URL, `/filter-dropdown-values?${params}`);\n}\n\nexport async function getModuleDataList(module, limit = 10, offset = 0, options = {}) {\n const { scope = '', sort = '', sortDir = '', filters = [] } = options;\n\n if (module === TEST_FLOW_MODULE) {\n const { getTestFlows } = await import('./testingApi');\n const data = await getTestFlows({ limit, offset });\n const rows = Array.isArray(data?.items) ? data.items : [];\n const total = Number(data?.total) || 0;\n return { items: rows, total, limit, offset, tabs: [{ key: 'all', title: 'Test Cases', count: total }] };\n }\n\n if (module === LMS_FLOW_MODULE) {\n const { getLmsFlows } = await import('./testingApi');\n const data = await getLmsFlows({ limit, offset });\n const rows = Array.isArray(data?.items) ? data.items : [];\n const total = Number(data?.total) || 0;\n return { items: rows, total, limit, offset, tabs: [{ key: 'all', title: 'LMS Cases', count: total }] };\n }\n\n // const params = new URLSearchParams({ module, limit: String(limit), offset: String(offset) });\n // if (scope) params.set('scope', scope);\n const params = new URLSearchParams({\n module: getBaseModuleName(module), limit: String(limit), offset: String(offset),\n });\n const normalizedScope = String(scope ?? '').trim();\n if (normalizedScope) params.set('scope', normalizedScope);\n if (sort) params.set('sort', sort);\n if (sortDir) params.set('sortDir', sortDir);\n if (Array.isArray(filters) && filters.some((item) => item?.field)) {\n params.set('filters', JSON.stringify(filters));\n }\n\n return fetchJsonWithAuth(AUTH_URL, `/module-data-list?${params.toString()}`);\n}\n\n/**\n * getModuleRecordById — a SINGLE record from any module, by its Mongo _id.\n *\n * Generalises a fetch that already existed inline in more than one place\n * (jobConfig.js's job-assignment lookup, the finance-approval reads) with the\n * same defensive unwrapping each of them had to hand-roll: `/module/list`\n * hands back a one-element ARRAY for a list-shaped call, but the object\n * itself for some by-id reads. Reading only the array shape is the exact bug\n * that once hid the Candidates tab on a job that really was assigned to the\n * user — so both shapes are handled here, once, for every future caller.\n *\n * Resolves to `null` on any failure (bad id, network, 404, unexpected shape)\n * rather than throwing — a caller displaying a linked record's summary should\n * degrade to \"not available\" and not break the page it lives on.\n */\nexport async function getModuleRecordById(module, id) {\n if (!module || !id) return null;\n try {\n const response = await fetchJsonWithAuth(\n AUTH_URL,\n `/module/list?module=${encodeURIComponent(module)}&id=${encodeURIComponent(id)}`,\n );\n const payload = response?.data?.data ?? response?.data ?? response;\n const record = Array.isArray(payload) ? payload[0] : payload;\n return (record && typeof record === 'object') ? record : null;\n } catch {\n return null;\n }\n}\n\nexport async function searchModuleData(moduleName, searchParams = {}, pagination = {}, options = {}) {\n const { limit = 10, offset = 0 } = pagination;\n const { sort = '', sortDir = '', scope = '', filters = [] } = options;\n // `scope` is the ONE source being viewed; `selectedSource` may be a comma list\n // of every ticked source. Splitting the fallback keeps a list from being\n // normalized into a bogus scope like \"internal,dice\".\n const inferredScope = normalizeCandidateSearchSource(\n scope || String(searchParams.selectedSource ?? '').split(','),\n );\n\n const query = new URLSearchParams();\n query.append('module', getBaseModuleName(moduleName));\n query.append('limit', String(limit));\n query.append('offset', String(offset));\n\n if (inferredScope) {\n query.set('scope', inferredScope);\n console.log('[searchModuleData] Adding scope to query:', inferredScope);\n }\n if (sort) query.set('sort', sort);\n if (sortDir) query.set('sortDir', sortDir);\n // Same generic filter mechanism getModuleDataList already sends — reused\n // here (rather than a second convention) for search-form fields whose value\n // needs an operator (gte/lte for a date range) instead of a flat param.\n // No existing caller passes this today, so nothing changes for them.\n if (Array.isArray(filters) && filters.some((item) => item?.field)) {\n query.set('filters', JSON.stringify(filters));\n }\n\n console.log('[searchModuleData] Final query string:', query.toString());\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null || value === '') return;\n\n let normalizedValue = value;\n if (typeof value === 'object') {\n normalizedValue = value.value ?? value.label ?? value.name ?? String(value);\n } else {\n normalizedValue = String(value);\n }\n\n query.append(key, normalizedValue);\n });\n\n const response = await fetchJsonWithAuth(AUTH_URL, `/module-data-list?${query.toString()}`);\n // const response = await fetchJsonWithAuth(\"http://localhost:9009/v1\", `/module-data-list?${query.toString()}`);\n // response = { status, data: { actionRules, actions, columnActions, data: [...records] } }\n const payload = response?.data ?? response;\n const rows = Array.isArray(payload)\n ? payload\n : payload?.rows ?? payload?.records ?? payload?.items ?? payload?.list ?? payload?.data ?? [];\n\n const total = extractResponseTotal(response, payload, rows, inferredScope);\n const counts = extractResponseCounts(response, payload);\n if (inferredScope && counts[inferredScope] === undefined && Number.isFinite(total)) {\n counts[inferredScope] = total;\n }\n\n // Check if response contains Dice candidates and transform if needed\n const hasDiceCandidates = rows.some((record) => {\n const sourceFields = [\n record?.sourceType,\n record?.profileSource,\n record?.selectedSource,\n record?.customFields?.sourceType,\n record?.customFields?.profileSource,\n ].flat();\n return sourceFields.some(\n (value) => String(value ?? '').trim().toLowerCase() === 'dice',\n ) || Boolean(record?.customFields?.diceProfileData);\n });\n\n let normalizedRows = rows;\n if (hasDiceCandidates) {\n try {\n const { transformDiceResponseToInternalFormat } = await import('./diceCandidateMapper');\n const transformed = transformDiceResponseToInternalFormat(response, response);\n normalizedRows = transformed?.data?.data ?? rows;\n } catch (error) {\n console.error('[searchModuleData] Dice transformation failed:', error);\n normalizedRows = rows.map(normalizeCandidateSearchRow);\n }\n } else {\n normalizedRows = Array.isArray(rows) ? rows.map(normalizeCandidateSearchRow) : [];\n }\n\n return {\n rows: normalizedRows,\n total: Number(total) || 0,\n counts,\n tabs: response?.tabs ?? response?.tabList ?? payload?.tabs ?? payload?.tabList ?? [],\n fields: response?.fields ?? response?.fieldConfig ?? payload?.fields ?? payload?.fieldConfig ?? [],\n tabField: response?.tabField ?? payload?.tabField ?? '',\n actionRules: payload?.actionRules ?? [],\n actions: payload?.actions ?? [],\n columnActions: payload?.columnActions ?? [],\n };\n}\n\nfunction getNestedValue(record, path) {\n return String(path)\n .split('.')\n .reduce((value, key) => value?.[key], record);\n}\n\nfunction setNestedValue(record, path, value) {\n const keys = String(path).split('.').filter(Boolean);\n if (!keys.length) return record;\n\n const nextRecord = { ...record };\n let target = nextRecord;\n let source = record;\n\n keys.slice(0, -1).forEach((key) => {\n const currentValue = source?.[key];\n const nextValue = currentValue && typeof currentValue === 'object' && !Array.isArray(currentValue)\n ? { ...currentValue }\n : {};\n\n target[key] = nextValue;\n target = nextValue;\n source = currentValue;\n });\n\n target[keys[keys.length - 1]] = value;\n return nextRecord;\n}\n\nfunction normalizeCandidateSearchRow(row) {\n if (!row || typeof row !== 'object') return row;\n\n const skillPaths = [\n 'skills',\n 'technicalSkills',\n 'primarySkills',\n 'keySkills',\n 'customFields.diceProfileData.skills',\n ];\n\n let nextRow = row;\n let firstNormalizedSkills = null;\n\n skillPaths.forEach((path) => {\n const rawSkills = getNestedValue(nextRow, path);\n if (rawSkills === undefined || rawSkills === null) return;\n\n const mappedSkills = normalizeDiceSkills(rawSkills);\n if (mappedSkills.length === 0 && Array.isArray(rawSkills) && rawSkills.length > 0) return;\n\n firstNormalizedSkills = firstNormalizedSkills ?? mappedSkills;\n nextRow = setNestedValue(nextRow, path, mappedSkills);\n });\n\n if (firstNormalizedSkills && getNestedValue(nextRow, 'skills') === undefined) {\n nextRow = setNestedValue(nextRow, 'skills', firstNormalizedSkills);\n }\n\n const sourceFields = [\n nextRow?.sourceType,\n nextRow?.profileSource,\n nextRow?.selectedSource,\n nextRow?.customFields?.sourceType,\n nextRow?.customFields?.profileSource,\n ].flat();\n const isDiceCandidate = sourceFields.some(\n (value) => String(value ?? '').trim().toLowerCase() === 'dice',\n ) || Boolean(nextRow?.customFields?.diceProfileData);\n\n if (isDiceCandidate) {\n const diceProfileId = resolveDiceProfileId(nextRow);\n const candidateId = nextRow?.candidateId\n ?? nextRow?.customFields?.diceProfileData?.candidateId\n ?? '';\n\n nextRow = {\n ...nextRow,\n id: nextRow?.id ?? diceProfileId,\n diceId: diceProfileId,\n candidateId,\n };\n }\n\n return nextRow;\n}\n","import { ensureToken, fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL } from './apiConfig';\n\n// ── Common Module Documents API ───────────────────────────────────────────────\n// Module-agnostic document registry (Auth gateway \"documents\" collection, files\n// in S3). Every module's Add Form registers its uploads here AFTER the module\n// record is created — the module's Create API generates the reference id\n// (jobId / candidateId / submissionId), and that id is what links each document\n// row back to its record.\n\n// Strips the transport decorations collectFileParts may leave on a part key —\n// the moduleWrites routing prefix (\"__mw__<module>__\") and an addRow row-index\n// suffix (\"[0]\") — so the registered fieldName is the clean form-field key\n// (e.g. \"resume\", \"jobDescription\", \"offerLetter\").\nfunction cleanFieldName(formKey) {\n return String(formKey ?? '')\n .replace(/^__mw__.*?__/, '')\n .replace(/\\[\\d+\\]$/, '')\n .trim() || 'file';\n}\n\n/**\n * uploadModuleDocuments — registers uploaded files against a created module\n * record. Common across ALL modules: pass the module name and the record id\n * its Create API returned.\n *\n * @param moduleName the module key (e.g. \"jobs\", \"candidates\", \"submissions\")\n * @param refId the created record's _id (jobId / candidateId / …)\n * @param fileParts [{ formKey, file }] — the shape collectFileParts returns;\n * formKey is the form field the file was uploaded against\n * @param metadata optional { [fieldName]: {...} } extra metadata per field\n */\nexport async function uploadModuleDocuments(moduleName, refId, fileParts = [], metadata = {}) {\n if (!moduleName) throw new Error('moduleName is required to upload documents');\n if (!refId) throw new Error('refId (created record id) is required to upload documents');\n if (!fileParts.length) return { data: [], total: 0 };\n\n const token = await ensureToken();\n\n // NOTE: do NOT set Content-Type — the browser must add the multipart\n // boundary itself (same convention as createModuleRecord).\n const formData = new FormData();\n fileParts.forEach(({ formKey, file }) => {\n const fileName = file?.name ?? undefined;\n formData.append(cleanFieldName(formKey), file, fileName);\n });\n if (metadata && Object.keys(metadata).length > 0) {\n formData.append('metadata', JSON.stringify(metadata));\n }\n\n const params = new URLSearchParams({ module: moduleName, refId: String(refId) });\n let res;\n try {\n res = await fetch(`${AUTH_URL}/documents/upload?${params.toString()}`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n } catch (netErr) {\n const error = new Error('Unable to connect to the server. Please check your network connection.');\n error.originalError = netErr;\n throw error;\n }\n\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('application/json') ? await res.json() : await res.text();\n if (!res.ok) {\n const errorMsg = (typeof data === 'object' ? (data?.error || data?.message) : data) || `Server returned status ${res.status}`;\n const error = new Error(errorMsg);\n error.status = res.status;\n error.data = data;\n throw error;\n }\n return data?.data ?? data;\n}\n\n/**\n * getModuleDocuments — lists a module record's documents (newest first), each\n * carrying a 24h presigned S3 download URL as `fileUrl`.\n *\n * @param moduleName the module key (e.g. \"jobs\", \"candidates\", \"submissions\")\n * @param refId the module record's _id\n * @param fieldName optional — only documents uploaded against this form field\n */\nexport async function getModuleDocuments(moduleName, refId, fieldName = '') {\n if (!moduleName) throw new Error('moduleName is required to fetch documents');\n if (!refId) throw new Error('refId is required to fetch documents');\n const params = new URLSearchParams({ module: moduleName, refId: String(refId) });\n if (fieldName) params.set('fieldName', fieldName);\n const json = await fetchJsonWithAuth(AUTH_URL, `/documents?${params.toString()}`);\n const payload = json?.data ?? json ?? {};\n return {\n data: Array.isArray(payload?.data) ? payload.data : (Array.isArray(payload) ? payload : []),\n total: payload?.total ?? 0,\n };\n}\n\n/**\n * deleteModuleDocument — soft-delete one registry document.\n *\n * Needed because removing a document ROW from a record left the registry entry\n * behind, and EditFormV1 rebuilds an empty repeatable group FROM the registry.\n * The row therefore came back on the next load, which reads as \"the delete did\n * not save\". The registry has to be told, or it will keep resurrecting the file.\n *\n * Resolves to false rather than throwing when the id is missing or the call\n * fails: a registry that will not accept the delete must not block the record's\n * own update, which is the change the user actually asked for. The caller\n * decides whether to warn.\n */\nexport async function deleteModuleDocument(documentId) {\n if (!documentId) return false;\n try {\n await fetchJsonWithAuth(AUTH_URL, `/documents/${encodeURIComponent(String(documentId))}`, {\n method: 'DELETE',\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * generateModuleId — generates a valid 24-character hexadecimal MongoDB ObjectID\n * string on the client so documents can be uploaded and stored against `moduleId`\n * BEFORE the module record itself is created in the module collection.\n */\nexport function generateModuleId() {\n const timestampHex = Math.floor(Date.now() / 1000)\n .toString(16)\n .padStart(8, '0')\n .slice(0, 8);\n const randomBytes = new Uint8Array(8);\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n crypto.getRandomValues(randomBytes);\n } else {\n for (let i = 0; i < randomBytes.length; i += 1) {\n randomBytes[i] = Math.floor(Math.random() * 256);\n }\n }\n const randomHex = Array.from(randomBytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${timestampHex}${randomHex}`;\n}\n\n/**\n * extractDocumentIds — extracts created document IDs from the `/documents/upload` response.\n */\nexport function extractDocumentIds(uploadResponse) {\n const list = Array.isArray(uploadResponse)\n ? uploadResponse\n : Array.isArray(uploadResponse?.data)\n ? uploadResponse.data\n : [];\n return list\n .map((doc) => String(doc?.id ?? doc?._id ?? '').trim())\n .filter(Boolean);\n}\n\n/**\n * rollbackModuleDocuments — compensating rollback: deletes documents created in the\n * `documents` collection if the subsequent module creation call fails.\n */\nexport async function rollbackModuleDocuments(documentIds = []) {\n const ids = Array.from(new Set((documentIds || []).map((id) => String(id || '').trim()).filter(Boolean)));\n if (ids.length === 0) return;\n\n await Promise.allSettled(\n ids.map((id) => deleteModuleDocument(id)),\n );\n}\n","// Compare exact values, so even a one-character change restores every configured rule.\nexport const isSavedValue = (field, value) => field?._savedValueValidation === true\n && Object.is(value, field.value);\n\nexport function gateSavedValueRules(rules, field, name) {\n if (!field?._savedValueValidation) return rules;\n return rules.map(rule => form => isSavedValue(field, form.getFieldValue(name))\n ? {} : (typeof rule === 'function' ? rule(form) : rule));\n}\n","import { isSavedValue } from './savedValueValidation';\nimport DOMPurify from 'dompurify';\n\nconst RICH_TEXT_TAGS = [\n 'p', 'br', 'strong', 'b', 'em', 'i', 'u', 's', 'ul', 'ol', 'li',\n 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'a', 'code', 'pre',\n];\nconst RICH_TEXT_ATTRS = ['href', 'title', 'target', 'rel'];\nconst INVISIBLE_RE = /[\\u200B-\\u200D\\u2060\\uFEFF]/g;\n// Security normalization intentionally targets ASCII control characters.\n// eslint-disable-next-line no-control-regex\nconst CONTROL_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\nconst NAME_RE = /^[\\p{L}\\p{M}\\p{N} .'-]+$/u;\nconst PHONE_RE = /^\\+?[0-9 ()-]+$/;\nconst NUMBER_RE = /^-?(?:\\d+|\\d*\\.\\d+)$/;\n\n// The ONE definition of hostile input on the client, kept deliberately in step\n// with utils/inputsecurity/patterns.go on the server. When the two disagree the\n// user gets the worst possible experience: a form that refuses what the API\n// would have accepted, or accepts what the API then rejects.\n//\n// Split by whether the text could EVER be legitimate prose. This is an ATS:\n// job descriptions for database and DevOps roles contain \"DROP TABLE\" and\n// \"&&\", and refusing them blocks real work without closing any sink — React\n// escapes on render, and the server sends BSON values, never query strings.\n// Markup and script are refused, because those are only ever an attack.\n//\n// `plainOnly` marks a rule that applies to plain fields but not to a rich-text\n// editor, where markup is the entire point. This replaces an earlier\n// `slice(0, 3)` that depended on the ARRAY ORDER — reordering the list silently\n// changed what rich text allowed.\nconst REJECT_PATTERNS = [\n { re: /<\\s*\\/?\\s*(?:script|iframe|object|embed|svg|img|form|input|style|link|meta|base|applet|frame|frameset)\\b/i, message: 'HTML/script content is not allowed' },\n { re: /\\b(?:javascript|vbscript)\\s*:|\\bdata\\s*:\\s*text\\/html/i, message: 'Unsafe URL protocol is not allowed' },\n { re: /\\bon[a-z]+\\s*=/i, message: 'HTML event handlers are not allowed' },\n { re: /[\"']?\\$(?:where|gt|gte|lt|lte|ne|nin|regex|or|and|not|nor|expr|function|jsonSchema|accumulator)\\b/i, message: 'MongoDB operators are not allowed in input values' },\n // Refused rather than tolerated: dangerous anywhere a value becomes a\n // filename, and rare in recruiter prose. Narrower than it once was — the old\n // pattern also matched a bare \"%2f\", i.e. any URL-encoded slash.\n { re: /(?:\\.\\.[/\\\\])|(?:%2e%2e|%5c)/i, message: 'Path traversal is not allowed' },\n // Any other HTML-looking tag (\"<b>\", \"<div class=x>\", \"</span>\"). Plain\n // fields never legitimately contain markup; a text-editor field does, and is\n // sanitised through DOMPurify instead.\n { re: /<\\/?[a-zA-Z][a-zA-Z0-9]*(?:\\s[^<>]*)?>/, message: 'HTML tags are not allowed', plainOnly: true },\n];\n\n// Permitted as typed text. Kept compiled rather than deleted so the policy is\n// visible in one place, and so switching to strict-everywhere is a matter of\n// concatenating this list onto REJECT_PATTERNS.\nexport const ALLOWED_TECHNICAL_PATTERNS = [\n { re: /\\b(?:union\\s+(?:all\\s+)?select|drop\\s+table|delete\\s+from|insert\\s+into|xp_cmdshell)\\b/i, message: 'database command patterns' },\n { re: /(?:['\"]\\s*)?\\b(?:or|and)\\s+\\d+\\s*=\\s*\\d+/i, message: 'injection patterns' },\n { re: /&&|\\|\\||\\$\\(|\\$\\{|`|\\b(?:rm\\s+-rf|cat\\s+\\/etc\\/|whoami\\b)/i, message: 'command execution patterns' },\n];\n\nfunction decodeHtmlEntities(value) {\n if (typeof document === 'undefined') return value;\n const textarea = document.createElement('textarea');\n textarea.innerHTML = value;\n return textarea.value;\n}\n\nexport function canonicalizeForSecurityScan(value) {\n let result = String(value ?? '');\n for (let i = 0; i < 2; i += 1) {\n try {\n const decoded = decodeURIComponent(result);\n if (decoded === result) break;\n result = decoded;\n } catch { break; }\n }\n result = decodeHtmlEntities(result)\n .replace(/\\\\x([0-9a-f]{2})/gi, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)))\n .replace(/\\\\u([0-9a-f]{4})/gi, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)));\n return result.normalize('NFC');\n}\n\nexport function sanitizeRichText(value) {\n return DOMPurify.sanitize(String(value ?? ''), {\n ALLOWED_TAGS: RICH_TEXT_TAGS,\n ALLOWED_ATTR: RICH_TEXT_ATTRS,\n ALLOW_DATA_ATTR: false,\n FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', 'svg', 'img', 'form', 'input'],\n FORBID_ATTR: ['style', 'src', 'srcset'],\n });\n}\n\nfunction validationMax(field) {\n const rule = (field.validations ?? []).find((item) => item?.type === 'max');\n const configured = Number(field.validator?.maxLength ?? rule?.value ?? field.maxLength);\n if (Number.isFinite(configured) && configured > 0) return configured;\n if (field.type === 'email' || field.formatter === 'email') return 255;\n if (field.formatter === 'phone' || field.formatter === 'digits') return 32;\n if (field.formatter === 'name') return 100;\n if (field.type === 'text-editor') return 50000;\n if (field.type === 'textarea') return 5000;\n return 5000;\n}\n\nfunction isMultiline(field) {\n return field.type === 'textarea' || field.type === 'text-editor';\n}\n\nexport function normalizeSecureString(value, field = {}) {\n if (typeof value !== 'string') return value;\n if (field.type === 'text-editor') return sanitizeRichText(value.normalize('NFC'));\n\n let normalized = value.normalize('NFC')\n .replace(INVISIBLE_RE, '')\n .replace(/\\u00A0/g, ' ')\n .replace(CONTROL_RE, '');\n if (isMultiline(field)) {\n normalized = normalized.replace(/\\r\\n?/g, '\\n').replace(/[ \\t]{2,}/g, ' ').trim();\n } else {\n normalized = normalized.replace(/\\s+/g, ' ').trim();\n }\n return normalized;\n}\n\nexport function validateSecureString(value, field = {}) {\n if (isSavedValue(field, value)) field = {};\n if (typeof value !== 'string' || value === '') return null;\n const label = field.label || field.field || 'Field';\n const canonical = canonicalizeForSecurityScan(value);\n\n if (canonical.includes('\\0') || canonical.includes('\\u0000')) return `${label} contains a null byte`;\n const richText = field.type === 'text-editor';\n const attack = REJECT_PATTERNS.find(\n ({ re, plainOnly }) => (!plainOnly || !richText) && re.test(canonical),\n );\n if (attack) return `${label}: ${attack.message}`;\n\n const normalized = normalizeSecureString(value, field);\n if ([...normalized].length > validationMax(field)) return `${label} is too long`;\n if (field.formatter === 'name' && normalized && !NAME_RE.test(normalized)) {\n return `${label} allows only letters, numbers, spaces, apostrophes, hyphens and periods`;\n }\n if ((field.formatter === 'phone' || field.formatter === 'digits') && normalized && !PHONE_RE.test(normalized)) {\n return `${label} contains invalid phone characters`;\n }\n if (field.type === 'number' && normalized && !NUMBER_RE.test(normalized)) {\n return `${label} must contain only a valid number`;\n }\n if ((field.type === 'url' || field.formatter === 'url') && normalized && !/^https?:\\/\\//i.test(normalized)) {\n return `${label} must use http:// or https://`;\n }\n return null;\n}\n\nexport function securityValidationRule(field) {\n return {\n validator: (_, value) => {\n const values = Array.isArray(value) ? value : [value];\n const error = values.map((item) => validateSecureString(item, field)).find(Boolean);\n return error ? Promise.reject(new Error(error)) : Promise.resolve();\n },\n };\n}\n\nfunction policyMap(groups = []) {\n const policies = new Map();\n groups.forEach((group) => {\n (group.fields ?? []).forEach((field) => {\n if (field.type === 'file') return;\n const destination = field.payloadKey || field.field;\n if (!destination) return;\n const path = group.addRow ? `${group.payloadKey || group.name}[].${destination}` : destination;\n policies.set(path, field);\n });\n });\n return policies;\n}\n\nexport function securePayload(payload, groups = []) {\n const policies = policyMap(groups);\n const walk = (node, path = '') => {\n if (typeof node === 'string') {\n const field = policies.get(path) ?? {};\n const error = validateSecureString(node, field);\n if (error) throw new Error(error);\n return normalizeSecureString(node, field);\n }\n if (Array.isArray(node)) return node.map((item) => walk(item, `${path}[]`));\n if (node && typeof node === 'object') {\n return Object.fromEntries(Object.entries(node).map(([key, value]) => {\n if (key.startsWith('$') || key.includes('.') || key.includes('\\0')) {\n throw new Error(`Unsafe object key: ${key}`);\n }\n const childPath = path ? `${path}.${key}` : key;\n return [key, walk(value, childPath)];\n }));\n }\n return node;\n };\n return walk(payload);\n}\n","// validationOverride — an admin-configured \"I know, let it through\" checkbox\n// that suspends named validations on ONE field.\n//\n// WHY THIS EXISTS\n// A cross-field cap is right almost always and wrong occasionally. A submission\n// rate may not exceed the job's client rate — except when the account manager\n// has actually agreed a higher one, and the recruiter in front of the record\n// knows that and the config does not. Before this, the only ways out were to\n// weaken the rule for everyone or to edit the job. Both lose the signal.\n//\n// So the rule STAYS, and the exception becomes explicit, deliberate and\n// recorded: a checkbox beside the field's label, a confirmation that says\n// plainly what is being given up, and a stored flag on the record saying an\n// override was taken.\n//\n// Entirely config-driven (FormGroupField.overrideCheckbox). Nothing here names\n// a module, a field or a rate — any field may gate any of its own validation\n// types this way.\n//\n// overrideCheckbox: {\n// label: \"Rate agreed above the job's client rate\",\n// field: \"candidateRateOverride\", // where the flag is stored\n// skipValidations: [\"maxFieldWithFallback\"], // which rule TYPES it lifts\n// confirmTitle: \"…\", confirmBody: \"…\",\n// confirmOkText: \"…\", confirmCancelText: \"…\",\n// store: true, // persist the flag (default)\n// }\n\nconst truthy = (v) => v === true || v === 1 || v === '1' || v === 'true';\n\n/** The config block, or null when this field has no override checkbox. */\nexport function overrideConfig(field) {\n const cfg = field?.overrideCheckbox;\n if (!cfg || typeof cfg !== 'object') return null;\n // A checkbox that lifts nothing is not a checkbox — it would render a control\n // that visibly does nothing, which is worse than not rendering it.\n return overrideSkips(field).size > 0 ? cfg : null;\n}\n\n/**\n * The form/payload key holding the flag.\n *\n * Defaults to a deterministic key derived from the field's own key, so an admin\n * who only ticks \"add an override checkbox\" gets a working, collision-free flag\n * without having to invent a name. Dots become underscores: the flag is a flat\n * sibling, not a leaf inside the field's own container.\n */\nexport function overrideFieldKey(field) {\n const explicit = String(field?.overrideCheckbox?.field ?? '').trim();\n if (explicit) return explicit;\n return `${String(field?.field ?? '').replace(/\\./g, '_')}__override`;\n}\n\n/** Validation TYPES this checkbox suspends, as a Set. */\nexport function overrideSkips(field) {\n const raw = field?.overrideCheckbox?.skipValidations;\n const list = Array.isArray(raw)\n ? raw\n : String(raw ?? '').split(',');\n return new Set(list.map((t) => String(t ?? '').trim()).filter(Boolean));\n}\n\n/** Should the flag be written to the record? Defaults to YES — it is an audit fact. */\nexport function overrideIsStored(field) {\n return field?.overrideCheckbox?.store !== false;\n}\n\n/**\n * The absolute name path of the flag, scoped exactly like a cross-field rule's\n * referenced key: the same addRow row when the field sits in one, top level\n * otherwise. So a per-row override stays that row's own.\n */\nexport function overrideFlagPath(field, name, prefixFor) {\n const key = overrideFieldKey(field);\n // A row's own override, when the field sits in a repeatable group — ticking\n // it on one row must not lift the limit on every other row.\n if (typeof prefixFor === 'function') {\n const prefix = prefixFor(key);\n if (Array.isArray(prefix) && prefix.length) return [...prefix, key];\n }\n // prefixFor answers for the GROUP'S OWN field keys, and the flag is not one\n // of them, so it declines (null) even inside a row. Recover the row from the\n // field's absolute path instead: inside a Form.List it is\n // [listName, rowIndex, …].\n if (Array.isArray(name) && name.length >= 3 && typeof name[1] === 'number') {\n return [name[0], name[1], key];\n }\n // Deliberately NOT resolveScopedFieldPath's sibling fallback: for a dotted\n // field (\"candidateBudget.candidateBudgetStart\") that would bury the flag\n // INSIDE the field's own container — which, for an appendHistory container,\n // means inside each pushed history entry rather than on the record. The flag\n // is a flat sibling at the field's scope root, which is also exactly where\n // the forms register its hidden Form.Item.\n return [key];\n}\n\n/** Is the override currently taken, per live form state? */\nexport function overrideActive(form, field, name, prefixFor) {\n if (!overrideConfig(field)) return false;\n try {\n return truthy(form?.getFieldValue?.(overrideFlagPath(field, name, prefixFor)));\n } catch {\n return false;\n }\n}\n\n/**\n * gateRuleByOverride — wrap ONE built rule so it stands down while the override\n * is taken.\n *\n * Returned as antd's FUNCTION form of a rule, which antd re-evaluates on every\n * validation pass against the CURRENT form. That is what makes the suspension\n * live: ticking the box clears the error without re-rendering the rule list,\n * and unticking it brings the rule straight back. An object rule captured at\n * render time could not do either.\n *\n * Works for declarative rules ({pattern}, {max}, …) and validator rules alike —\n * neither is inspected, only returned or withheld.\n */\nexport function gateRuleByOverride(builtRule, flagPath) {\n return (formInstance) => {\n let active = false;\n try {\n active = truthy(formInstance?.getFieldValue?.(flagPath));\n } catch {\n active = false;\n }\n // `{}` is antd's no-op rule: it validates nothing and never rejects.\n return active ? {} : builtRule;\n };\n}\n\n/**\n * applyOverrideGating — the single call site each form's getRules makes.\n *\n * Takes the RAW admin validations (which still carry their `type`) and returns\n * a mapper that gates only the types this field's checkbox names, leaving every\n * other rule exactly as it was.\n */\nexport function overrideRuleMapper(field, name, prefixFor, buildRule) {\n const cfg = overrideConfig(field);\n if (!cfg) return buildRule;\n const skips = overrideSkips(field);\n const flagPath = overrideFlagPath(field, name, prefixFor);\n return (validation) => {\n const built = buildRule(validation);\n const type = typeof validation === 'string' ? validation : validation?.type;\n return skips.has(type) ? gateRuleByOverride(built, flagPath) : built;\n };\n}\n\n/** Dialog copy, with defaults that read as a deliberate, reversible decision. */\nexport function overrideConfirmCopy(field) {\n const cfg = overrideConfig(field) ?? {};\n const label = field?.label ?? field?.field ?? 'this field';\n return {\n tone: cfg.tone ?? 'overwrite',\n title: cfg.confirmTitle || 'Remove the validation on this field?',\n body: cfg.confirmBody\n || `The check that keeps ${label} within its allowed limit will be turned off for this record, `\n + 'and a value above that limit will be accepted. The override is recorded against the record. '\n + 'Do you want to continue?',\n okText: cfg.confirmOkText || 'Yes, remove the limit',\n cancelText: cfg.confirmCancelText || 'Keep the limit',\n };\n}\n\nexport default {\n overrideConfig,\n overrideFieldKey,\n overrideSkips,\n overrideIsStored,\n overrideFlagPath,\n overrideActive,\n gateRuleByOverride,\n overrideRuleMapper,\n overrideConfirmCopy,\n};\n","// optionMatching — snap an incoming value onto a field's CONFIGURED option.\n//\n// Any value that arrives from outside the form (an AI parse of a JD or resume,\n// an edit prefill, a cross-module prefill, an import) is free text. A select /\n// radio / checkbox control only selects when the value is character-for-\n// character one of its configured option values, so \"onsite\", \"ONSITE\",\n// \"On Site\" and \"on_site\" all silently failed to select the \"On-Site\" radio —\n// the parse looked like it had worked while the control sat empty.\n//\n// Matching is deliberately CONSERVATIVE and lossless:\n// • only fields that actually declare options are touched;\n// • an unmatched value is returned UNCHANGED, never blanked — a value we\n// cannot map is still shown to the user (and still saved) rather than\n// silently dropped;\n// • matching never invents a selection: it compares against the option's own\n// value and label only, plus whatever aliases the admin configured.\n//\n// Everything here is config-driven; no module, field or option name appears.\n\n// canonical — the comparison key. Case-folded, accent-folded and stripped of\n// every non-alphanumeric character, so \"On-Site\" / \"on site\" / \"ON_SITE\" /\n// \"onsite\" all collapse onto \"onsite\". Digits are kept so \"1\" ≠ \"10\".\nexport function canonical(value) {\n return String(value ?? '')\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '');\n}\n\n// optionEntries — the (canonicalKey → optionValue) pairs a field offers.\n// Both the option's value and its label are accepted as input, because a parse\n// far more often echoes the human label (\"Full Time\") than the stored value.\n// field.optionAliases lets an admin add synonyms the data uses but the option\n// list does not spell out, e.g. { \"WFH\": \"Remote\", \"work from home\": \"Remote\" }.\nexport function optionEntries(field) {\n const pairs = new Map();\n const add = (key, target) => {\n const k = canonical(key);\n // First writer wins: an earlier option keeps the key when a later option's\n // label happens to collapse onto the same string.\n if (k && !pairs.has(k)) pairs.set(k, target);\n };\n\n (Array.isArray(field?.options) ? field.options : []).forEach((option) => {\n if (option === null || option === undefined) return;\n const value = typeof option === 'object' ? option.value : option;\n if (value === undefined || value === null || value === '') return;\n add(value, value);\n if (typeof option === 'object' && option.label !== undefined) add(option.label, value);\n });\n\n Object.entries(field?.optionAliases ?? {}).forEach(([alias, target]) => {\n // An alias may only point AT a real option — otherwise a typo in config\n // would inject a value the control cannot select, which is the exact class\n // of bug this module exists to remove.\n const resolved = pairs.get(canonical(target));\n if (resolved !== undefined) add(alias, resolved);\n });\n\n return pairs;\n}\n\nconst hasOptions = (field) => Array.isArray(field?.options) && field.options.length > 0;\n\n// snapOne — map a single scalar onto an option value, or return it unchanged.\nfunction snapOne(pairs, value) {\n if (value === undefined || value === null || value === '') return value;\n // An object (a resolved reference like { id, value }) is not free text — the\n // reference resolver already owns it, so leave it entirely alone.\n if (typeof value === 'object') return value;\n const match = pairs.get(canonical(value));\n return match === undefined ? value : match;\n}\n\n// snapToOption — the entry point. Arrays map element-wise so a multi-select or\n// a checkbox group snaps every entry. Returns the input untouched for fields\n// with no options, which is most of them.\nexport function snapToOption(field, value) {\n if (!hasOptions(field)) return value;\n const pairs = optionEntries(field);\n if (pairs.size === 0) return value;\n if (Array.isArray(value)) return value.map((item) => snapOne(pairs, item));\n return snapOne(pairs, value);\n}\n\n// unmatchedOptionValues — the entries that could NOT be mapped onto an option.\n// Callers use it to tell the user what a parse failed to place, instead of\n// leaving a control looking mysteriously empty.\nexport function unmatchedOptionValues(field, value) {\n if (!hasOptions(field)) return [];\n const pairs = optionEntries(field);\n const list = Array.isArray(value) ? value : [value];\n return list.filter((item) => item !== undefined && item !== null && item !== ''\n && typeof item !== 'object'\n && !pairs.has(canonical(item)));\n}\n","import dayjs from 'dayjs';\n\n// mountDefaultValue — the value an Add-form control MOUNTS with from its\n// configured defaultValue. REQ-20260911-01 R11.\n//\n// For most controls that is simply the defaultValue. A checkbox GROUP (a\n// checkbox with options) is different: its value is the list of options that\n// are ticked, so a default naming none of its options is not a selection at\n// all. The Jobs priority field shows why this matters — one option (\"Mark as\n// High\" → \"High\"), with unticked meaning \"Low\". \"Low\" is a SAVE default, applied\n// by the payload engine (defaultValue on empty, then transformRule), and the\n// server's defaultOnServer. Mounting it into the control fed a string to a\n// component that expects an array, which is why the default had to be removed\n// from that field on 2026-09-08 and priority stopped being saved at all.\n//\n// A default that DOES name one of the options is returned exactly as\n// configured, so every existing checkbox default behaves as it did.\n//\n// A TIME control (REQ-20260911-04) holds a dayjs value, while an admin types a\n// default as text (\"09:00\", \"17:00:00\", \"5:00 PM\"). Handing the text straight\n// to antd's TimePicker throws on mount, which would take the whole form down —\n// so a time default is parsed into a dayjs value, and one that cannot be read\n// is dropped (the field simply mounts empty) rather than crashing the form.\nexport function mountDefaultValue(field = {}) {\n const value = field?.defaultValue;\n if (value === undefined || value === '') return undefined;\n if (field?.type === 'time') return parseTimeDefault(value);\n if (field?.type === 'date') return parseDateDefault(value);\n if (field?.type !== 'checkbox') return value;\n\n const options = Array.isArray(field.options) ? field.options : [];\n // A single boolean checkbox, or options that load at runtime: nothing to\n // check the default against, so it is left as configured.\n if (!options.length) return value;\n\n const optionValues = options.map((option) => String(\n option && typeof option === 'object' ? option.value : option,\n ));\n const wanted = Array.isArray(value) ? value : [value];\n return wanted.some((v) => optionValues.includes(String(v))) ? value : undefined;\n}\n\n// \"9:00\", \"09:00\", \"17:30:15\", \"5:00 PM\", \"5:00pm\", \"5:00 p.m.\"\nconst TIME_TEXT = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?\\s*([ap]\\.?\\s?m\\.?)?$/i;\n// A stored date-time used as a default: \"2026-09-11T09:00:00Z\", \"2026-09-11 09:00\".\nconst DATE_TIME_TEXT = /^\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}/;\n\n/**\n * parseTimeDefault — a configured time default as a dayjs value on today's\n * date, or undefined when it cannot be read. Never throws.\n */\nexport function parseTimeDefault(value) {\n if (value === undefined || value === null || value === '') return undefined;\n if (dayjs.isDayjs(value)) return value.isValid() ? value : undefined;\n if (typeof value !== 'string') return undefined;\n const text = value.trim();\n\n const match = text.match(TIME_TEXT);\n if (match) {\n let hour = Number(match[1]);\n const minute = Number(match[2]);\n const second = Number(match[3] ?? 0);\n const meridiem = match[4]?.replace(/[.\\s]/g, '').toLowerCase();\n if (meridiem) {\n if (hour < 1 || hour > 12) return undefined;\n hour = (hour % 12) + (meridiem === 'pm' ? 12 : 0);\n }\n if (hour > 23 || minute > 59 || second > 59) return undefined;\n return dayjs().hour(hour).minute(minute).second(second).millisecond(0);\n }\n\n if (DATE_TIME_TEXT.test(text)) {\n const parsed = dayjs(text);\n return parsed.isValid() ? parsed : undefined;\n }\n return undefined;\n}\n\n// \"today\", \"today+2\", \"today - 7\", \"TODAY+30\" — a date RELATIVE to the day the\n// form is opened. REQ-20260923-10: an interview slot defaults to two days out,\n// which no fixed date can express, and it must be recomputed for every new form.\nconst RELATIVE_DATE = /^today\\s*(?:([+-])\\s*(\\d{1,4}))?$/i;\n// A fixed calendar date: \"2026-09-25\", optionally with a time part.\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}(?:[T ].*)?$/;\n\n/**\n * parseDateDefault — a configured DATE default as a dayjs value (start of that\n * day), or undefined when it cannot be read. Never throws.\n *\n * Why this exists: antd's DatePicker holds a dayjs value. A text default handed\n * straight to it (\"today+2\", or even \"2026-09-25\") throws on mount and takes the\n * whole form down — the same failure parseTimeDefault prevents for time\n * controls. Unreadable text therefore mounts EMPTY rather than crashing.\n */\nexport function parseDateDefault(value, now = dayjs()) {\n if (value === undefined || value === null || value === '') return undefined;\n if (dayjs.isDayjs(value)) return value.isValid() ? value : undefined;\n if (value instanceof Date) {\n const d = dayjs(value);\n return d.isValid() ? d : undefined;\n }\n if (typeof value !== 'string') return undefined;\n const text = value.trim();\n\n const relative = text.match(RELATIVE_DATE);\n if (relative) {\n const days = relative[2] ? Number(relative[2]) * (relative[1] === '-' ? -1 : 1) : 0;\n return dayjs(now).startOf('day').add(days, 'day');\n }\n if (ISO_DATE.test(text)) {\n const parsed = dayjs(text);\n return parsed.isValid() ? parsed : undefined;\n }\n return undefined;\n}\n\nexport default mountDefaultValue;\n","// csvTags — the field type that is entered as CHIPS but stored as ONE\n// comma-separated string.\n//\n// Why a separate type: `tagify` stores an ARRAY. Some records keep a list as a\n// single string instead — a job's `competencies` is \"Core Java, MySQL\" on every\n// existing job, the Go model types it as a string, and the submission form's\n// /jobs/{id}/competencies endpoint splits that string on commas. Those fields\n// need the chip input without changing what is stored. Opt-in per field\n// (Admin → Form Groups → Type), so every existing tagify field keeps saving\n// arrays exactly as before.\n\nexport const CSV_TAGS_TYPE = 'csvTags';\nexport const DEFAULT_CSV_SEPARATOR = ', ';\n\nconst tagKey = (tag) => tag.toLowerCase();\n\n/**\n * csvToTags — the chips a stored value represents. Accepts the stored string,\n * an array of chips, or an array whose entries still contain commas (a parse,\n * a paste). Trimmed, empties dropped, duplicates removed case-insensitively\n * keeping the first spelling and the original order.\n */\nexport function csvToTags(value) {\n const list = Array.isArray(value) ? value : [value];\n const out = [];\n const seen = new Set();\n list.forEach((item) => {\n if (item === undefined || item === null || typeof item === 'object') return;\n String(item).split(',').forEach((part) => {\n const tag = part.trim().replace(/\\s+/g, ' ');\n if (!tag || seen.has(tagKey(tag))) return;\n seen.add(tagKey(tag));\n out.push(tag);\n });\n });\n return out;\n}\n\n/** tagsToCsv — the string that is saved: \"Core Java, MySQL\". Empty → \"\". */\nexport function tagsToCsv(value, separator = DEFAULT_CSV_SEPARATOR) {\n return csvToTags(value).join(separator || DEFAULT_CSV_SEPARATOR);\n}\n","/**\n * payloadTransformer — the single, configuration-driven payload engine shared by\n * AddFormV1 and EditFormV1.\n *\n * Goal: the Admin form-group config is the ONLY source of truth for how a form\n * value becomes an API payload value. There is ZERO field-name / module-specific\n * branching here. Every behaviour is driven by these per-field config keys:\n *\n * payloadKey output key (dot-notation allowed). Default: field.field\n * dataType auto | string | number | boolean | array | object | date | custom\n * elementType for dataType \"array\": coerce each element (e.g. \"number\")\n * payloadMode auto | value | label | object | custom | template | skip\n * valueKey which key holds the id/value on an option object (default: value/id/_id…)\n * displayKey which key holds the label on an option object (default: label/name…)\n * payloadTemplate object template for custom/template modes, with {{value}} {{label}} {{raw}} tokens\n * defaultValue value substituted when the form value is empty\n * transformRule { map: {...}, default, name } — value maps / named transforms\n * omitEmpty drop the key entirely when the final value is empty\n *\n * Nothing here knows about \"priority\", \"recruiters\", \"noticePeriod\", etc. Those\n * are expressed purely through the config above.\n *\n * Backward compatibility: when a field carries NONE of the new keys, the engine\n * falls back to the historical generic behaviour — dot-notation nesting, dayjs →\n * ISO string, scalar pass-through — so existing forms keep working unchanged.\n */\n\nimport dayjs from 'dayjs';\nimport { fromAppInput, shouldConvertToZone } from '../../services/timezone';\nimport { securePayload } from './inputSecurity';\nimport { overrideConfig, overrideFieldKey, overrideIsStored } from './validationOverride';\nimport { snapToOption } from './optionMatching';\nimport { parseDateDefault, parseTimeDefault } from './mountDefaultValue';\nimport { CSV_TAGS_TYPE, csvToTags, tagsToCsv } from './csvTags';\n\n// ── small shared predicates ───────────────────────────────────────────────────\n\nconst isEmpty = (v) =>\n v === undefined ||\n v === null ||\n v === '' ||\n (Array.isArray(v) && v.length === 0);\n\n// truthy tolerates the shapes a boolean config flag can arrive in (true/1/\"1\"),\n// matching how the forms read the same flags elsewhere.\nconst truthy = (v) => v === true || v === 1 || v === '1';\n\nconst isPlainObject = (v) =>\n v !== null && typeof v === 'object' && !Array.isArray(v) && !isDayjs(v);\n\nfunction isDayjs(v) {\n return (\n v &&\n typeof v === 'object' &&\n typeof v.format === 'function' &&\n typeof v.isValid === 'function'\n );\n}\n\nfunction isDateLike(v) {\n return isDayjs(v) || v instanceof Date;\n}\n\nfunction toISO(v) {\n if (v instanceof Date) return v.toISOString();\n if (isDayjs(v)) return v.isValid() ? v.toISOString() : null;\n return v;\n}\n\n// toStoredDate — serialise a picker value, honouring the tenant timezone for\n// fields that carry an actual INSTANT.\n//\n// A DatePicker/TimePicker hands back a value in the BROWSER's zone. On a tenant\n// configured to Asia/Dubai, a user choosing 09:00 means 09:00 in Dubai; storing\n// the browser's 09:00 would be a different moment entirely.\n//\n// The guard matters as much as the conversion: plain CALENDAR dates (date of\n// birth, passport expiry, education start) mean the same day in every zone, and\n// converting them shifts them by a day for half the world. shouldConvertToZone\n// only opts in datetime/time fields, or fields explicitly marked tzAware — see\n// services/timezone.js.\nfunction toStoredDate(value, field) {\n if (!shouldConvertToZone(field)) return toISO(value);\n const zoned = fromAppInput(value);\n return zoned ? zoned.toISOString() : toISO(value);\n}\n\n// ── dot-notation get / set ────────────────────────────────────────────────────\n\nexport function getDeep(obj, path) {\n if (!path) return undefined;\n const parts = String(path).split('.');\n let cur = obj;\n for (const part of parts) {\n if (cur == null) return undefined;\n cur = cur[part];\n }\n return cur;\n}\n\n// readFormValue reads a field value tolerating BOTH antd conventions used in this\n// codebase: a flat dotted key (\"experience.from\", as EditForm registers fields)\n// and a nested object (\"experience\": { from }, as AddForm registers fields).\nexport function readFormValue(values, path) {\n if (values && Object.prototype.hasOwnProperty.call(values, path)) return values[path];\n return getDeep(values, path);\n}\n\n// hasFormValue reports whether a submit actually carried this field (so EditForm\n// can leave untouched parts of the base record alone).\nexport function hasFormValue(values, path) {\n if (values && Object.prototype.hasOwnProperty.call(values, path)) return true;\n return getDeep(values, path) !== undefined;\n}\n\nexport function setDeep(target, path, value) {\n const parts = String(path).split('.');\n let cur = target;\n for (let i = 0; i < parts.length - 1; i += 1) {\n const key = parts[i];\n if (!isPlainObject(cur[key])) cur[key] = {};\n cur = cur[key];\n }\n cur[parts[parts.length - 1]] = value;\n return target;\n}\n\n/**\n * mergeDeepAt — setDeep, except that when BOTH the existing value at `path` and\n * the incoming one are plain objects the keys are merged, with the EXISTING\n * value winning every collision.\n *\n * A stored file container is written from two places: the file field carries\n * the whole container back (passport → {passportCopyLocation, passportUploadName,\n * passportNumber, …}) while its sibling fields write individual keys into the\n * same container (passport.passportNumber). Plain assignment would let whichever\n * ran last erase the other — including overwriting a number the user just edited\n * with the stale one from the carried snapshot. Existing-wins makes the result\n * independent of field order.\n */\nfunction mergeDeepAt(target, path, value) {\n const parts = String(path).split('.');\n let cur = target;\n for (let i = 0; i < parts.length - 1; i += 1) {\n const key = parts[i];\n if (!isPlainObject(cur[key])) cur[key] = {};\n cur = cur[key];\n }\n const last = parts[parts.length - 1];\n cur[last] = (isPlainObject(cur[last]) && isPlainObject(value))\n ? { ...value, ...cur[last] }\n : value;\n return target;\n}\n\n/**\n * carriedFileValue — the stored reference a file field must write back, read\n * from its antd fileList.\n *\n * Only entries EXPLICITLY marked by the prefill/edit helpers count: those carry\n * the original container on `stored`. Any other object in a file value is\n * treated exactly as before — leftover/stale file metadata that must not be\n * written back (see the \"strips stale file data\" case in the payload tests).\n * A fresh upload (originFileObj) means the multipart path owns this field and\n * the downstream handler writes its shape, so nothing is carried — mixing the\n * two would put a stale reference over it.\n */\nfunction carriedFileValue(value) {\n const list = Array.isArray(value) ? value : (value == null ? [] : [value]);\n if (!list.length) return undefined;\n if (list.some((item) => item?.originFileObj)) return undefined;\n const stored = list\n .map((item) => item?.stored)\n .filter((s) => s !== undefined && s !== null);\n if (!stored.length) return undefined;\n return stored.length === 1 && list.length === 1 ? stored[0] : stored;\n}\n\n/** Merge a plain object's keys into target at root (used by custom/template spread). */\nfunction mergeDeep(target, source) {\n Object.entries(source ?? {}).forEach(([k, v]) => {\n if (isPlainObject(v) && isPlainObject(target[k])) mergeDeep(target[k], v);\n else target[k] = v;\n });\n return target;\n}\n\n// ── option / value-label normalisation ────────────────────────────────────────\n\nfunction optionLabelFor(field, value) {\n const opts = field.options ?? field.values ?? [];\n for (const o of opts) {\n if (typeof o === 'string') {\n if (o === value) return o;\n } else if ((o.value ?? o.id ?? o.name ?? o.label) === value) {\n return o.label ?? o.name ?? o.value;\n }\n }\n return undefined;\n}\n\n/**\n * splitValueLabel — normalises a raw form value into { value, label }.\n * Handles antd labelInValue ({value,label}), {id,name}/{_id,…} option objects,\n * and plain primitives (label recovered from field.options when present).\n */\nfunction splitValueLabel(raw, field) {\n if (isPlainObject(raw)) {\n const value =\n (field.valueKey && raw[field.valueKey]) ??\n raw.value ??\n raw.id ??\n raw._id ??\n raw.key ??\n raw.code;\n const label =\n (field.displayKey && raw[field.displayKey]) ??\n raw.label ??\n raw.name ??\n raw.text ??\n raw.title ??\n value;\n return { value, label };\n }\n return { value: raw, label: optionLabelFor(field, raw) ?? raw };\n}\n\n// effectiveDataType — the dataType to apply. An explicit dataType always wins;\n// otherwise it is inferred from the field's input type / multiplicity so that a\n// plain `type: \"number\"` or a multi-select keeps coercing without the admin\n// having to set dataType on every field (keeps existing configs working).\nexport function effectiveDataType(field = {}) {\n if (field.dataType && field.dataType !== 'auto') return field.dataType;\n if (field.multiSelect || field.mode === 'multiple' || field.addRow) return 'array';\n // Chips in the form, ONE comma-separated string in the record (form/csvTags.js).\n if (field.type === CSV_TAGS_TYPE) return 'csv';\n switch (field.type) {\n case 'number':\n return 'number';\n case 'date':\n case 'time':\n return 'date';\n default:\n return 'auto';\n }\n}\n\n// ── data-type coercion ────────────────────────────────────────────────────────\n\nexport function coerceDataType(value, dataType, field = {}) {\n if (isDateLike(value)) {\n // Dates always serialise to ISO unless explicitly typed otherwise below.\n if (dataType === 'string') return toStoredDate(value, field);\n if (dataType === 'number') {\n const t = isDayjs(value) ? value.valueOf() : value.getTime();\n return Number.isNaN(t) ? null : t;\n }\n if (dataType === 'date' || dataType === 'auto' || !dataType) return toStoredDate(value, field);\n }\n\n switch (dataType) {\n case 'string':\n return value == null ? '' : String(value);\n\n case 'csv':\n return tagsToCsv(value, field.csvSeparator);\n\n case 'number': {\n if (isEmpty(value)) return null;\n const n = Number(value);\n return Number.isNaN(n) ? null : n;\n }\n\n case 'boolean':\n return value === true || value === 1 || value === '1' || value === 'true';\n\n case 'array': {\n let arr;\n if (Array.isArray(value)) arr = value;\n else if (isEmpty(value)) arr = [];\n else if (typeof value === 'string' && value.includes(','))\n arr = value.split(',').map((s) => s.trim()).filter(Boolean);\n else arr = [value];\n if (field.elementType) {\n return arr\n .map((el) => coerceDataType(el, field.elementType, {}))\n .filter((el) => el !== null && el !== undefined && el !== '');\n }\n return arr;\n }\n\n case 'object':\n return isPlainObject(value) ? value : value;\n\n case 'date':\n return toStoredDate(value, field);\n\n case 'custom':\n case 'auto':\n case undefined:\n case '':\n default:\n return value;\n }\n}\n\n// ── transformRule (value maps + named transforms) ─────────────────────────────\n\nconst NAMED_TRANSFORMS = {\n firstChecked: (v) => (Array.isArray(v) ? v[0] : v),\n csvToArray: (v) =>\n typeof v === 'string' ? v.split(',').map((s) => s.trim()).filter(Boolean) : v,\n arrayToCsv: (v) => (Array.isArray(v) ? v.join(',') : v),\n trim: (v) => (typeof v === 'string' ? v.trim() : v),\n upper: (v) => (typeof v === 'string' ? v.toUpperCase() : v),\n lower: (v) => (typeof v === 'string' ? v.toLowerCase() : v),\n};\n\nfunction applyTransformRule(value, transformRule) {\n if (!transformRule) return value;\n let rule = transformRule;\n if (typeof rule === 'string') {\n // Either a named transform or a JSON blob.\n if (NAMED_TRANSFORMS[rule]) return NAMED_TRANSFORMS[rule](value);\n try {\n rule = JSON.parse(rule);\n } catch {\n return value;\n }\n }\n\n let out = value;\n if (rule.name && NAMED_TRANSFORMS[rule.name]) out = NAMED_TRANSFORMS[rule.name](out);\n\n if (rule.map && typeof rule.map === 'object') {\n const key = Array.isArray(out) ? String(out[0]) : String(out);\n if (Object.prototype.hasOwnProperty.call(rule.map, key)) out = rule.map[key];\n else if (rule.default !== undefined) out = rule.default;\n }\n return out;\n}\n\n// ── template resolution (custom / template payload modes) ─────────────────────\n\nconst TOKEN_RE = /\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g;\n\nfunction resolveToken(token, ctx) {\n const [head, ...rest] = token.split('.');\n let base;\n if (head === 'value') base = ctx.value;\n else if (head === 'label') base = ctx.label;\n else if (head === 'raw') base = ctx.raw;\n else return undefined;\n return rest.length ? getDeep(base, rest.join('.')) : base;\n}\n\nfunction resolveTemplateNode(node, ctx) {\n if (typeof node === 'string') {\n // Whole-string single token → return the typed value (keep numbers numeric).\n const whole = node.match(/^\\{\\{\\s*([\\w.]+)\\s*\\}\\}$/);\n if (whole) return resolveToken(whole[1], ctx);\n return node.replace(TOKEN_RE, (_, tok) => {\n const v = resolveToken(tok, ctx);\n return v == null ? '' : String(v);\n });\n }\n if (Array.isArray(node)) return node.map((n) => resolveTemplateNode(n, ctx));\n if (isPlainObject(node)) {\n const out = {};\n Object.entries(node).forEach(([k, v]) => {\n out[k] = resolveTemplateNode(v, ctx);\n });\n return out;\n }\n return node;\n}\n\nfunction parseTemplate(template) {\n if (!template) return null;\n if (typeof template === 'string') {\n try {\n return JSON.parse(template);\n } catch {\n return null;\n }\n }\n return template;\n}\n\n// ── payloadMode shaping ───────────────────────────────────────────────────────\n\nfunction shapeSingle(field, raw) {\n const mode = field.payloadMode || 'auto';\n const dataType = effectiveDataType(field);\n const { value, label } = splitValueLabel(raw, field);\n\n switch (mode) {\n case 'value':\n return { kind: 'scalar', out: coerceDataType(value, dataType, field) };\n\n case 'label':\n return { kind: 'scalar', out: coerceDataType(label, field.dataType || 'string', field) };\n\n case 'object': {\n const vKey = field.valueKey || 'id';\n const dKey = field.displayKey || 'name';\n return {\n kind: 'scalar',\n out: {\n [vKey]: coerceDataType(value, field.elementType || 'auto', {}),\n [dKey]: label,\n },\n };\n }\n\n case 'custom':\n case 'template': {\n const tpl = parseTemplate(field.payloadTemplate);\n if (!tpl) return { kind: 'scalar', out: coerceDataType(value, dataType, field) };\n const ctx = { value: coerceDataType(value, field.elementType || 'auto', {}), label, raw };\n const resolved = resolveTemplateNode(tpl, ctx);\n // A template that yields a plain object spreads into the parent unless the\n // admin pinned an explicit payloadKey.\n return { kind: field.payloadKey ? 'scalar' : 'spread', out: resolved };\n }\n\n case 'auto':\n default:\n return { kind: 'scalar', out: coerceDataType(value, dataType, field) };\n }\n}\n\n/**\n * shapeFieldValue — turns one field's raw form value into its payload contribution.\n * Returns { kind: 'scalar'|'spread'|'skip', out }.\n * scalar → write `out` at the field's payloadKey\n * spread → merge `out` (a plain object) into the parent payload\n * skip → contribute nothing\n */\n/**\n * defaultForControl — a field's configured defaultValue in the shape its\n * CONTROL holds. For almost every type that is the value as configured; a time\n * control holds a dayjs value, so an admin's \"09:00\" is parsed (see\n * parseTimeDefault). Every path that puts a default into a form or a payload\n * goes through here — REQ-20260911-04: one path that did not (the edit form's\n * defaultOnEdit) handed the raw text to antd's TimePicker, which throws\n * `getUDayjs(...).isValid is not a function` and takes the form down.\n * Returns undefined when there is no usable default.\n */\nexport function defaultForControl(field = {}) {\n const value = field.defaultValue;\n if (value === undefined || value === '') return undefined;\n if (field.type === 'time') return parseTimeDefault(value);\n // A date control holds a dayjs value too, and a relative default (\"today+2\")\n // must be resolved on the day the form is used - REQ-20260923-10.\n if (field.type === 'date') return parseDateDefault(value);\n return value;\n}\n\nexport function shapeFieldValue(field, rawValue) {\n if (field.payloadMode === 'skip') return { kind: 'skip' };\n\n let raw = rawValue;\n if (isEmpty(raw)) {\n const fallback = defaultForControl(field);\n if (fallback !== undefined) raw = fallback;\n }\n\n raw = applyTransformRule(raw, field.transformRule);\n\n if (isEmpty(raw) && field.omitEmpty) return { kind: 'skip' };\n\n // Multi-value (multi-select / array source) with a per-element shape.\n const isMultiSource =\n Array.isArray(raw) &&\n (effectiveDataType(field) === 'array' || field.payloadMode === 'object');\n\n if (isMultiSource && field.payloadMode && field.payloadMode !== 'value' && field.payloadMode !== 'auto') {\n const out = raw.map((item) => shapeSingle(field, item).out);\n return { kind: 'scalar', out };\n }\n\n return shapeSingle(field, raw);\n}\n\nexport function usesLabeledSelectValue(field = {}) {\n return Boolean(field.richValue) || ['label', 'object', 'custom', 'template'].includes(field.payloadMode);\n}\n\nexport function toSelectControlValue(field, value, options = []) {\n if (!usesLabeledSelectValue(field) || value == null) return value;\n const toLabeled = (item) => {\n if (isPlainObject(item) && item.value !== undefined) return item;\n const match = options.find((option) =>\n String(option?.value ?? '') === String(item) || String(option?.label ?? '') === String(item));\n return {\n value: match?.value ?? item,\n label: match?.label ?? String(item),\n };\n };\n return Array.isArray(value) ? value.map(toLabeled) : toLabeled(value);\n}\n\n// ── showIf payload gating ─────────────────────────────────────────────────────\n//\n// A field hidden by its showIf condition must not STORE either: antd preserves\n// values of unmounted Form.Items, so \"check VMS Required, type a commission,\n// uncheck it\" would still submit the commission. showIfSatisfied evaluates the\n// same operators the forms render with, against the field's own scope (the row\n// object for an addRow row) falling back to the whole form values (a condition\n// field living in another group, e.g. contractType). writeField consults it and\n// writes null instead of the stale value, so an edit also clears what a\n// now-hidden field previously stored. Config-driven; no field names.\n// One leaf condition ({field,operator,value}) evaluated against a scope.\nfunction showIfLeafSatisfied(cond, scope, root) {\n if (!cond?.field) return true;\n const val = hasFormValue(scope, cond.field)\n ? readFormValue(scope, cond.field)\n : readFormValue(root ?? scope, cond.field);\n const list = () => String(cond.value ?? '').split(',').map((v) => v.trim());\n switch (cond.operator) {\n case 'eq': return String(val ?? '') === String(cond.value ?? '');\n case 'neq': return String(val ?? '') !== String(cond.value ?? '');\n case 'truthy': return val !== undefined && val !== null && val !== '' && val !== false;\n case 'falsy': return val === undefined || val === null || val === '' || val === false;\n case 'notEmpty': return Array.isArray(val) ? val.length > 0 : Boolean(val);\n case 'in': return list().includes(String(val ?? ''));\n case 'notIn': return !list().includes(String(val ?? ''));\n default: return true;\n }\n}\n\n// All field keys a Show Condition reads (single leaf + every conditions[] entry)\n// — used to walk transitive visibility across each gate field.\nexport function showIfConditionFields(showIf) {\n const keys = [];\n if (showIf?.field) keys.push(showIf.field);\n if (Array.isArray(showIf?.conditions)) {\n showIf.conditions.forEach((c) => { if (c?.field) keys.push(c.field); });\n }\n return keys;\n}\n\n// A Show Condition may be a single leaf OR a `conditions` array combined by\n// `logic` (\"and\" default | \"or\"), plus an optional leaf folded in with the same\n// logic — mirrors the form-side evaluateShowIf so payload gating matches render.\nexport function showIfSatisfied(showIf, scope, root) {\n if (!showIf) return true;\n const conditions = Array.isArray(showIf.conditions) ? showIf.conditions.filter((c) => c?.field) : [];\n if (conditions.length) {\n const results = conditions.map((c) => showIfLeafSatisfied(c, scope, root));\n const orLogic = String(showIf.logic ?? 'and').toLowerCase() === 'or';\n let combined = orLogic ? results.some(Boolean) : results.every(Boolean);\n if (showIf.field) {\n const leaf = showIfLeafSatisfied(showIf, scope, root);\n combined = orLogic ? (combined || leaf) : (combined && leaf);\n }\n return combined;\n }\n return showIfLeafSatisfied(showIf, scope, root);\n}\n\n// fieldVisibleForPayload — transitive showIf: a field is storable only when its\n// own condition passes AND the field its condition READS is itself storable\n// (e.g. vmsCommission is gated on isVMSRequired, which is gated on\n// contractType — on W2 all of them drop together even if stale values linger).\n// `index` maps field key → field config across every group; `seen` guards\n// against condition cycles.\nfunction fieldVisibleForPayload(field, scope, root, index, seen = new Set()) {\n const gateFields = showIfConditionFields(field?.showIf);\n if (!gateFields.length) return true;\n if (seen.has(field.field)) return true; // cycle — fail open\n seen.add(field.field);\n if (!showIfSatisfied(field.showIf, scope, root)) return false;\n // Transitive: EACH gate field this condition reads must itself be storable.\n return gateFields.every((key) => {\n const gate = index?.get?.(key);\n return gate ? fieldVisibleForPayload(gate, scope, root, index, seen) : true;\n });\n}\n\n// payloadFieldIndex — field key → field config across all groups, for the\n// transitive showIf walk above.\nfunction payloadFieldIndex(groups = []) {\n const index = new Map();\n groups.forEach((g) => (g.fields ?? []).forEach((f) => {\n if (f?.field && !index.has(f.field)) index.set(f.field, f);\n }));\n return index;\n}\n\n// rowDefaultsFor — the seed object for a brand-new addRow row: every non-file\n// field's configured defaultValue (e.g. VMS Commission 5.5, Rate Currency USD)\n// keyed by its form key. Used for a group's initial empty rows AND every row\n// the user adds, so admin defaults show inside repeatable groups too (top-level\n// fields already get theirs via Form.Item initialValue).\nexport function rowDefaultsFor(group = {}, { editing = false } = {}) {\n const seed = {};\n (group.fields ?? []).forEach((f) => {\n // Same alias-tolerant upload test used everywhere else — a \"document\"-typed\n // upload must not be seeded with a defaultValue any more than a \"file\" one.\n if (!f.field || isUploadField(f)) return;\n if (editing && !f.defaultOnEdit) return;\n if (f.defaultValue === undefined || f.defaultValue === '') return;\n // A time control needs a dayjs value, not the admin's text (\"09:00\") —\n // the same parse the top-level fields use (mountDefaultValue). An\n // unreadable time default seeds nothing rather than crashing the row.\n if (f.type === 'time') {\n const time = parseTimeDefault(f.defaultValue);\n if (time !== undefined) seed[f.field] = time;\n return;\n }\n // Same for a date control (\"today+2\", \"2026-09-25\") - REQ-20260923-10.\n if (f.type === 'date') {\n const date = parseDateDefault(f.defaultValue);\n if (date !== undefined) seed[f.field] = date;\n return;\n }\n seed[f.field] = f.defaultValue;\n });\n return seed;\n}\n\n// ── field flattening from groups ──────────────────────────────────────────────\n\n/** All leaf fields across groups, with their group context (for addRow arrays). */\nexport function flattenFields(groups = []) {\n const out = [];\n groups.forEach((g) => {\n (g.fields ?? []).forEach((f) => {\n out.push({ field: f, group: g });\n });\n });\n return out;\n}\n\n// UPLOAD_FIELD_TYPES — every field type that holds an uploaded file.\n//\n// \"file\" is the canonical type the form renderer uses, but admin config in the\n// wild (and the seeded module defaults) also carries \"document\", \"image\",\n// \"upload\" and \"attachment\" for the same intent. Those aliases used to be\n// invisible to the collector, so their files silently never reached the shared\n// `documents` collection — a module whose uploads simply never appeared in the\n// registry, with no error anywhere. Matching on intent instead of on one exact\n// spelling makes the central documents registry work for EVERY module by\n// default, whichever synonym the config happens to use.\n//\n// This is safe for a field that is not really an upload: the collector only\n// ever pushes actual File/Blob values (see push in collectFileParts), so a\n// non-upload field simply contributes nothing.\nexport const UPLOAD_FIELD_TYPES = ['file', 'document', 'image', 'upload', 'attachment'];\n\n// isUploadField is the SINGLE predicate every file-collection path uses, so the\n// set of upload types can never drift between them again.\nexport function isUploadField(field) {\n return UPLOAD_FIELD_TYPES.includes(String(field?.type ?? '').trim().toLowerCase());\n}\n\nexport function fileFields(groups = []) {\n return flattenFields(groups)\n .map(({ field }) => field)\n .filter(isUploadField);\n}\n\n/**\n * collectFileParts — gather the NEW files the user picked, ready to append to a\n * multipart request, so create AND edit upload through the same gateway endpoint.\n *\n * Returns [{ formKey, file }]. The part name is the field's `fileKey` if set\n * (e.g. jobs reads \"file\"), else the first segment of its path\n * (\"passport.passportUploadName\" → \"passport\"). addRow groups fan out across\n * every row. Only fresh uploads (antd `originFileObj`, or a raw File/Blob) are\n * included — existing/stored files (url only) are left alone. Config-driven; no\n * field-name or module-specific logic.\n *\n * opts.indexed — when true, files inside an addRow group are emitted under an\n * INDEXED part name (\"documents[0]\", \"documents[1]\", …) where the index is the\n * row's position. EditForm uses this because the candidates update handler reads\n * document files by indexed key and aligns each file to its metadata row by\n * index.\n *\n * opts.scope — 'all' (default) | 'flat' | 'addRow'. Lets a caller collect only\n * the non-repeatable (flat) file fields, or only the repeatable (addRow) ones.\n * AddForm uploads flat files in the create request, then attaches addRow files\n * (e.g. candidate documents) in a follow-up update — the create handler for\n * repeatable document files is unreliable, while the indexed update path is the\n * proven one. Config-driven (keyed on the group's addRow flag), no module names.\n */\n// normalizeModuleKey trims + lower-cases a module key so a group's Target\n// Collection can be compared against the form's own module case-insensitively.\n//\n// It deliberately does NOT singularize (strip a trailing \"s\"). The gateway is\n// the source of truth for collection identity: it folds a moduleWrites entry\n// back into the main record only when the target resolves to the SAME physical\n// collection (foldSameCollectionModuleWrites → isSameCollectionTarget in\n// moduleCrudController.go), and its NormalizeFormGroupModule does not blindly\n// singularize either. A naive \"trainers\" → \"trainer\" fold here diverged from\n// that and silently merged a genuinely-distinct collection (e.g. \"trainers\")\n// into the primary record (e.g. module \"trainer\") — the exact multi-collection\n// bug. Exact-match keeps the FE from over-folding; any real same-collection\n// alias/plural case is folded server-side where the collection names are known.\nfunction normalizeModuleKey(module) {\n return String(module ?? '').trim().toLowerCase();\n}\n\n// isSameModuleTarget — the group's Target Collection is the form's own module\n// (exact, case-insensitive), i.e. the group writes to the MAIN record rather\n// than a linked document. Plural/alias targets that still resolve to the main\n// collection are folded by the gateway, not guessed here.\nfunction isSameModuleTarget(group, primaryKey) {\n return Boolean(primaryKey) && normalizeModuleKey(group.moduleName) === primaryKey;\n}\n\nexport function collectFileParts(values, groups = [], opts = {}) {\n const parts = [];\n const scope = opts.scope ?? 'all';\n const primaryKey = normalizeModuleKey(opts.module);\n const push = (formKey, node) => {\n if (node == null) return;\n const list = Array.isArray(node) ? node : [node];\n list.forEach((file) => {\n const raw = file?.originFileObj || file;\n if (typeof File !== 'undefined' && raw instanceof File) parts.push({ formKey, file: raw });\n else if (typeof Blob !== 'undefined' && raw instanceof Blob) parts.push({ formKey, file: raw });\n });\n };\n (groups ?? []).forEach((group) => {\n const files = (group.fields ?? []).filter(isUploadField);\n if (!files.length) return;\n // A moduleWrites group's files must be routed to ITS collection, not the\n // primary record — prefixed so the gateway's applyModuleWrites can tell\n // them apart (see splitModuleWriteFiles / filesForModuleWrite server-side).\n // A group targeting the form's own module writes to the main record, so\n // its files stay unprefixed (mirrors buildPayload's targetFor).\n const routed = group.moduleName && !isSameModuleTarget(group, primaryKey);\n const keyFor = (baseKey) => (routed ? `__mw__${group.moduleName}__${baseKey}` : baseKey);\n if (group.addRow) {\n if (scope === 'flat') return;\n const rows = values[group.name];\n if (!Array.isArray(rows)) return;\n files.forEach((field) => {\n const baseKey = field.fileKey ?? String(field.field).split('.')[0];\n rows.forEach((row, rowIdx) => {\n const formKey = keyFor(opts.indexed ? `${baseKey}[${rowIdx}]` : baseKey);\n push(formKey, readFormValue(row, field.field));\n });\n });\n return;\n }\n if (scope === 'addRow') return;\n files.forEach((field) => {\n const formKey = keyFor(field.fileKey ?? String(field.field).split('.')[0]);\n push(formKey, readFormValue(values, field.field));\n });\n });\n return parts;\n}\n\n// ── the two public builders ───────────────────────────────────────────────────\n\n/**\n * buildPayload — config-driven payload object from antd form values.\n *\n * @param values the antd form values object (may contain dot-notation nesting,\n * Form.List arrays for addRow groups, and dayjs date objects)\n * @param groups normalised form groups (with the per-field payload config)\n * @param opts { base } optional base object to merge onto (edit keeps the\n * untouched parts of the original record)\n * @returns a plain payload object ready to JSON.stringify\n *\n * File-typed fields are skipped — the caller handles uploads separately.\n */\nexport function buildPayload(values, groups, opts = {}) {\n const payload = isPlainObject(opts.base) ? structuredClone(opts.base) : {};\n\n // Strip file fields out of any base so stale file arrays never ride along.\n fileFields(groups).forEach((f) => {\n if (f.payloadKey || f.field) {\n // best-effort removal at both the configured key and source key\n deleteDeep(payload, f.payloadKey || f.field);\n deleteDeep(payload, f.field);\n }\n });\n\n // moduleWrites — a group with `moduleName` set (Feature 1: Per-Group Target\n // Collection) is routed to a DIFFERENT collection than the default record,\n // so its fields build their OWN sub-payload instead of merging into\n // `payload`. Groups sharing the same moduleName merge into ONE entry — the\n // backend's applyModuleWrites (moduleCrudController.go) upserts them the\n // same way, into one secondary document per (primary record, moduleName).\n //\n // A target that IS the form's own module (opts.module) means \"the main\n // record\": routing it through moduleWrites would create a parentId-linked\n // TWIN document in the same collection, so it merges into `payload` instead.\n // The backend applies the same guard (foldSameCollectionModuleWrites).\n const primaryKey = normalizeModuleKey(opts.module);\n const fieldIndex = payloadFieldIndex(groups);\n const moduleWrites = new Map();\n const targetFor = (group) => {\n if (!group.moduleName || isSameModuleTarget(group, primaryKey)) return payload;\n if (!moduleWrites.has(group.moduleName)) moduleWrites.set(group.moduleName, {});\n return moduleWrites.get(group.moduleName);\n };\n\n (groups ?? []).forEach((group) => {\n const target = targetFor(group);\n const groupFields = group.fields ?? [];\n const groupToggleOn = group.toggleEnabled && group.toggleField && hasFormValue(values, group.toggleField)\n ? Boolean(readFormValue(values, group.toggleField))\n : false;\n if (group.toggleEnabled && group.toggleField && hasFormValue(values, group.toggleField)) {\n setDeep(target, group.toggleField, groupToggleOn);\n if (groupToggleOn && group.addRow) {\n setDeep(target, group.payloadKey || group.name, []);\n return;\n }\n }\n\n if (group.addRow) {\n // Repeatable group → ROW-oriented array of objects:\n // [{ fieldA: v, fieldB: v }, { ... }]\n // matching a Go []struct (e.g. workExperience, educationDetails, documents).\n // The output key is the group's payloadKey (the struct's array key, e.g.\n // \"workExperience\") falling back to the group name.\n const rows = values[group.name];\n // Not present in this submit (e.g. EditForm doesn't prefill Form.List) →\n // leave whatever the base record already has untouched.\n if (!Array.isArray(rows)) return;\n const outKey = group.payloadKey || group.name;\n const rowObjects = rows\n .map((row) => buildRowObject(groupFields, row, values, fieldIndex))\n .filter((obj) => obj && Object.keys(obj).length > 0);\n setDeep(target, outKey, rowObjects);\n return;\n }\n\n groupFields.forEach((field) => writeField(field, values, target, values, fieldIndex));\n });\n\n if (moduleWrites.size > 0) {\n payload.moduleWrites = Array.from(moduleWrites, ([moduleName, data]) => ({ moduleName, data }));\n }\n\n return securePayload(payload, groups);\n}\n\n// writeField — the single, shared rule for turning ONE field's value (read from\n// `scope`, which is the whole form values for a normal group or a single row\n// object for an addRow row) into its contribution on `target`. Used by both\n// buildPayload (top-level groups) and buildRowObject (addRow rows) so a field\n// behaves identically no matter how deeply it is nested — a repeatable\n// field-GROUP (subFields) or a repeatable scalar field (addRow) serialises to a\n// nested array the same way at any level. Config-driven; no field-name logic.\nfunction writeField(field, scope, target, root, fieldIndex) {\n // An admin-configured validation override (field.overrideCheckbox) writes its\n // own flag alongside the field, in whatever scope the field itself lives in\n // (flat payload or one addRow row). Recorded because \"someone deliberately\n // lifted this limit\" is an audit fact about the record, not a UI detail —\n // without it a value that breaks a documented rule looks like the rule failed.\n // `store: false` opts out for a purely transient override.\n const overrideCfg = overrideConfig(field);\n if (overrideCfg && overrideIsStored(field)) {\n const flagKey = overrideFieldKey(field);\n if (hasFormValue(scope, flagKey)) {\n setDeep(target, flagKey, Boolean(readFormValue(scope, flagKey)));\n }\n }\n\n // isUploadField, NOT `type === 'file'`. Admin config in the wild types an\n // upload as \"document\"/\"image\"/\"upload\"/\"attachment\" just as often, and those\n // aliases used to MISS this branch entirely: the antd fileList\n // ([{uid,name,stored,…}]) then fell through to the ordinary scalar path and\n // was written to the payload as the field's value, overwriting the stored file\n // container with UI junk — or, when the user had not touched the field and it\n // held an empty list, writing [] over it. That is the \"an uploaded document\n // disappears when you edit the record\" bug: the update wiped the container the\n // form never intended to change. Every upload type now takes the branch below,\n // whose contract is \"carry the stored reference back, or write NOTHING\" —\n // never null, never an empty array, in any scope (flat field, addRow row, or\n // indexed document row: buildRowObject routes through this same function).\n if (isUploadField(field)) {\n // A NEW upload rides the multipart request (collectFileParts) and the\n // downstream handler writes its stored shape — nothing to do here.\n // An ALREADY-STORED file has no File object to upload, so without carrying\n // its reference the record ends up with no document at all: that is why a\n // Quick Submit prefilled from a previous submission lost every document it\n // showed in the form. Write back the untouched original container, merged\n // so sibling scalars written from the same container (passport.number,\n // passport.expiry) survive regardless of field order.\n const carried = carriedFileValue(readFormValue(scope, field.field));\n if (carried !== undefined) {\n // At the top level the fileKey names the stored container (\"passport\",\n // \"resume\"); inside an addRow row it is the multipart part name for the\n // whole group, so only the row's own key applies there.\n const inRow = root !== undefined && root !== scope;\n const key = field.payloadKey || (inRow ? field.field : (field.fileKey || field.field));\n mergeDeepAt(target, key, carried);\n }\n return;\n }\n\n // showIf gating at PAYLOAD time: a field whose visibility condition fails is\n // not stored. antd preserves unmounted Form.Item values, so without this a\n // value typed before the condition flipped (e.g. VMS Commission after VMS\n // Required was unchecked) would silently ride along. Writing null (rather\n // than skipping) also clears the stale stored value on edit; inside an\n // addRow row the whole row array is rewritten anyway, so the key simply\n // drops out of the row object. Visibility is TRANSITIVE: a field whose gate\n // field is itself hidden drops too (fieldVisibleForPayload).\n if (showIfConditionFields(field.showIf).length && !fieldVisibleForPayload(field, scope, root, fieldIndex)) {\n // Top-level (scope === root): write null so an edit clears the stale\n // stored value. Row scope: the row array is rewritten wholesale, so simply\n // omitting the key removes it from the stored row.\n if ((root === undefined || root === scope) && hasFormValue(scope, field.field)) {\n setDeep(target, field.payloadKey || field.field, null);\n }\n return;\n }\n\n // Repeatable field-GROUP (subFields): each Form.List row is an object of the\n // sub-fields, so this field submits as an array of row objects —\n // [{question, answer}, …] — under its own payloadKey. Recurses so a nested\n // repeat group (e.g. inside an addRow row) is shaped element-by-element.\n if (Array.isArray(field.subFields) && field.subFields.length > 0) {\n if (!hasFormValue(scope, field.field)) return;\n const rows = readFormValue(scope, field.field);\n if (!Array.isArray(rows)) return;\n const rowObjects = rows\n .map((row) => buildRowObject(field.subFields, row, root, fieldIndex))\n .filter((obj) => obj && Object.keys(obj).length > 0);\n setDeep(target, field.payloadKey || field.field, rowObjects);\n return;\n }\n\n if (!hasFormValue(scope, field.field)) return; // not in this submit — leave base untouched\n\n // Repeatable SCALAR field (addRow, no subFields): each Form.List item is one\n // value, so normalise every element through the field's single-value shape\n // (dates → ISO, select → id) instead of relying on a blanket array coercion.\n // This makes a repeat field work INSIDE an addRow row (subjects[] per row) as\n // well as at the top level.\n if (truthy(field.addRow) && (!Array.isArray(field.subFields) || field.subFields.length === 0)) {\n const raw = readFormValue(scope, field.field);\n const arr = Array.isArray(raw) ? raw : isEmpty(raw) ? [] : [raw];\n const single = singleFieldOf(field);\n const out = arr\n .map((el) => shapeFieldValue(single, el))\n .filter((r) => r.kind !== 'skip')\n .map((r) => r.out);\n setDeep(target, field.payloadKey || field.field, out);\n return;\n }\n\n const raw = readFormValue(scope, field.field);\n const result = shapeFieldValue(field, raw);\n if (result.kind === 'skip') return;\n if (result.kind === 'spread' && isPlainObject(result.out)) {\n mergeDeep(target, result.out);\n return;\n }\n setDeep(target, field.payloadKey || field.field, result.out);\n}\n\n// buildRowObject — shape one addRow row into a plain object keyed by each\n// field's payloadKey/field (relative to the row). File fields are skipped (they\n// upload separately and their metadata is merged server-side).\nfunction buildRowObject(groupFields, row, root, fieldIndex) {\n const obj = {};\n groupFields.forEach((field) => writeField(field, row, obj, root ?? row, fieldIndex));\n return obj;\n}\n\nfunction deleteDeep(obj, path) {\n const parts = String(path).split('.');\n let cur = obj;\n for (let i = 0; i < parts.length - 1; i += 1) {\n if (!isPlainObject(cur[parts[i]])) return;\n cur = cur[parts[i]];\n }\n delete cur[parts[parts.length - 1]];\n}\n\n/**\n * buildInitialValue — reverse direction, for EditForm prefill.\n * Given a field config and the stored value (which may be a scalar, an\n * { id, value } reference, an { id, name } object, or an array of those),\n * returns the value the input control expects.\n *\n * - date/time fields → dayjs\n * - select/radio/checkbox → the id/value (single) or array of ids (multi)\n * - everything else → the scalar\n *\n * This replaces convertInitialValue's hardcoded field-name handling.\n */\n// Controls that render exactly one primitive. Deliberately excludes select /\n// lookup / reference / file types, whose values are legitimately objects.\nconst SCALAR_CONTROL_TYPES = new Set([\n 'text', 'textarea', 'email', 'phone', 'tel', 'number', 'date', 'time', 'datetime', 'password',\n]);\n\nexport function buildInitialValue(field, stored) {\n const configuredEmpty = (value) => {\n const emptyValues = Array.isArray(field.emptyValues) ? field.emptyValues : [];\n return emptyValues.some((v) => String(v) === String(value));\n };\n // defaultOnEdit fills an empty stored value — in the control's own shape.\n const fallbackEditDefault = () => (field.defaultOnEdit ? defaultForControl(field) : undefined);\n\n if (stored === undefined || stored === null) {\n return fallbackEditDefault() ?? stored;\n }\n if (configuredEmpty(stored)) {\n return fallbackEditDefault();\n }\n if (isEmpty(stored) && fallbackEditDefault() !== undefined) {\n return fallbackEditDefault();\n }\n\n // A text/number/date control can only show a scalar. When the stored value is\n // a whole sub-document — a field whose key names its own container, e.g.\n // \"address\" storing {addressLine, city, state} while its payloadKey is\n // \"address.addressLine\" — reduce it to the payloadKey's leaf. Without this the\n // input renders \"[object Object]\" and saving writes that string over the real\n // address. Falls through to undefined (blank control) when no leaf matches:\n // never destructive, always recoverable.\n if (SCALAR_CONTROL_TYPES.has(field.type) && isPlainObject(stored)) {\n const leaf = String(field.payloadKey || field.field || '').split('.').pop();\n const reduced = leaf ? stored[leaf] : undefined;\n if (reduced === undefined || isPlainObject(reduced)) return undefined;\n stored = reduced; // eslint-disable-line no-param-reassign\n }\n\n // The stored \"A, B\" opens as two chips, not one chip reading \"A, B\".\n if (field.type === CSV_TAGS_TYPE) return csvToTags(stored);\n\n if (field.type === 'date' || field.type === 'time') {\n if (!stored || stored === '0001-01-01T00:00:00Z') return null;\n const p = dayjs(stored);\n return p.isValid() ? p : null;\n }\n\n // Snap onto the field's configured option before the control sees it. A\n // select/radio only selects on an exact value match, so an incoming \"onsite\"\n // or \"ONSITE\" would leave an \"On-Site\" radio blank even though the value\n // arrived. Fields without options, and values that match nothing, pass\n // through untouched — this can correct a value but never discard one.\n stored = snapToOption(field, stored);\n\n const pickId = (item) => {\n if (isPlainObject(item)) {\n return (\n (field.valueKey && item[field.valueKey]) ??\n item.id ??\n item._id ??\n item.value ??\n item.userId ??\n item.recruiterId\n );\n }\n return item;\n };\n\n // Radio groups are ALWAYS single-select in the UI, even when the payload\n // dataType is \"array\" (e.g. jobRemoteStatus → []string). Prefill the scalar\n // so the selected radio shows; the payload engine re-wraps it to an array on\n // save via coerceDataType. Without this, a radio gets an array value and\n // renders with nothing selected.\n const isMulti = field.type === 'radio' ? false : effectiveDataType(field) === 'array';\n\n // A stored value that only differs from a static option by case (e.g. a\n // legacy \"active\" vs the configured option value \"Active\") would otherwise\n // match nothing and render as an unselected/empty control.\n const matchOptionCase = (v) => {\n if (v === undefined || v === null || v === '' || !Array.isArray(field.options)) return v;\n const match = field.options.find((option) => String(option?.value ?? '').toLowerCase() === String(v).toLowerCase());\n return match ? match.value : v;\n };\n\n if (field.type === 'select' || field.type === 'radio' || field.type === 'checkbox') {\n if (isMulti) {\n const arr = Array.isArray(stored) ? stored : [stored];\n return arr.map(pickId).map(matchOptionCase).filter((v) => v !== undefined && v !== null && v !== '' && !configuredEmpty(v));\n }\n const selected = matchOptionCase(Array.isArray(stored) ? pickId(stored[0]) : pickId(stored));\n return configuredEmpty(selected) ? fallbackEditDefault() : selected;\n }\n\n // checkbox group with a transformRule map (e.g. \"high\" → checked) is handled\n // by the caller via the same map; here we just pass the scalar through.\n return stored;\n}\n\n// singleFieldOf strips a repeatable (addRow) field down to its per-item shape:\n// each Form.List item holds ONE value, so multi/array coercion must NOT apply\n// when reading or rendering a single item. Preserves everything else (type,\n// options, datasource, validations) so the item control still behaves like the\n// field otherwise would.\nexport function singleFieldOf(field = {}) {\n return { ...field, addRow: false, multiSelect: false, mode: undefined, dataType: undefined };\n}\n\n// buildRepeatFieldInitial — the Form.List initialValue for a field-level repeat\n// (field.addRow). Unlike a repeatable GROUP (rows are objects), each item here\n// is a single scalar, so a stored array maps element-by-element through\n// buildInitialValue as if the field were single (see singleFieldOf). The result\n// is padded to minRows; when there is no stored value at all, initialRows\n// (falling back to minRows) empty inputs are shown so the user sees a starting\n// control instead of only a \"+\" button. Config keys mirror the group ones:\n// field.minRows / field.initialRows.\nexport function buildRepeatFieldInitial(field, stored) {\n const minRows = Math.max(0, Number(field.minRows ?? 0) || 0);\n const initialRows = field.initialRows !== undefined && field.initialRows !== ''\n ? Math.max(minRows, Math.max(0, Number(field.initialRows) || 0))\n : minRows;\n\n // Repeatable field-GROUP (subFields): each stored element is a row OBJECT, so\n // map every sub-field through buildInitialValue into a per-row object (mirrors\n // getAddRowInitialValue's group-addRow row mapping). Empty rows fall back to {}\n // so the sub-field controls still render.\n if (Array.isArray(field.subFields) && field.subFields.length > 0) {\n let rows = [];\n if (Array.isArray(stored)) {\n rows = stored.map((row) => {\n const out = {};\n field.subFields.forEach((sub) => {\n if (!sub.field || isUploadField(sub)) return;\n const v = getDeep(row ?? {}, sub.field) ?? (row ?? {})[sub.field];\n if (v !== undefined && v !== null) out[sub.field] = buildInitialValue(sub, v);\n });\n return out;\n });\n }\n const target = rows.length > 0 ? minRows : Math.max(minRows, initialRows);\n while (rows.length < target) rows.push({});\n return rows;\n }\n\n const singleField = singleFieldOf(field);\n let items = [];\n if (Array.isArray(stored)) {\n items = stored\n .map((v) => buildInitialValue(singleField, v))\n .filter((v) => v !== undefined && v !== null && v !== '');\n } else if (stored !== undefined && stored !== null && stored !== '') {\n const v = buildInitialValue(singleField, stored);\n if (v !== undefined && v !== null && v !== '') items = [v];\n }\n\n const target = items.length > 0 ? minRows : Math.max(minRows, initialRows);\n while (items.length < target) items.push(undefined);\n return items;\n}\n\nexport default {\n buildPayload,\n buildInitialValue,\n showIfSatisfied,\n rowDefaultsFor,\n buildRepeatFieldInitial,\n singleFieldOf,\n shapeFieldValue,\n usesLabeledSelectValue,\n toSelectControlValue,\n coerceDataType,\n getDeep,\n setDeep,\n flattenFields,\n fileFields,\n};\n","// ─────────────────────────────────────────────────────────────────────────\n// phoneDisplay — a contact number as the RECORD stores it, not as the tenant's\n// default happens to be set.\n//\n// The detail view formats every phone through one admin-configured mask\n// (detailDefaults.phoneFormat, e.g. \"3-3-4\"). That part is right and stays.\n// What was wrong is the country code: it came from a single global default, so\n// a candidate who stored +91 was displayed as \"+1 999-878-3413\" — a number that\n// does not exist, printed with total confidence.\n//\n// Resolution order, most specific first:\n// 1. a code embedded in the value itself (\"+91 9998783413\") — unambiguous\n// 2. `renderOptions.countryCodeField` — the Detail Groups admin naming the\n// sibling key that holds this record's code\n// 3. the conventional siblings (<field>CountryCode, countryCode,\n// phoneCountryCode, mobileCountryCode, …)\n// 4. the global default — ONLY when the record carries nothing, and it ships\n// empty so a missing code renders as no code instead of a wrong one.\n//\n// Nothing here knows a module or a field name: (2) is config and (3) is a\n// naming convention applied to whatever key the field itself has.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport { formatPhone, getPhoneCountryCode } from '../../services/detailDefaults';\n\nconst isEmpty = (v) => v === undefined || v === null || v === '';\n\n// getPath — dotted-path read, so a configured countryCodeField may point at a\n// nested container key (\"contact.countryCode\") as well as a flat one.\nfunction getPath(source, path) {\n return String(path ?? '')\n .split('.')\n .filter(Boolean)\n .reduce((current, key) => (current == null ? undefined : current[key]), source);\n}\n\n/**\n * normalizeDialCode — \"+91\" | \"91\" | 91 → \"+91\". Anything that is not a dial\n * code (a country NAME, an empty string, a stray label) resolves to '' so it is\n * never printed in front of a number.\n */\nexport function normalizeDialCode(value) {\n const raw = String(value ?? '').trim().replace(/[\\s()-]/g, '');\n if (!raw) return '';\n const digits = raw.replace(/^\\+/, '');\n return /^\\d{1,4}$/.test(digits) ? `+${digits}` : '';\n}\n\n/**\n * countryCodeKeys — the sibling keys to probe for a phone field's own code.\n * The field-derived candidates come first (\"mobileNumberCountryCode\",\n * \"mobileCountryCode\" for a `mobileNumber` field) so a record holding several\n * numbers keeps each one's code attached to the right number.\n */\nexport function countryCodeKeys(field) {\n const key = String(field?.field ?? '').trim();\n const base = key.replace(/(number|no|phone|mobile|contact)$/i, '');\n return [\n ...(key ? [`${key}CountryCode`, `${key}Code`] : []),\n ...(base && base !== key ? [`${base}CountryCode`] : []),\n 'countryCode',\n 'phoneCountryCode',\n 'mobileCountryCode',\n 'contactCountryCode',\n 'dialCode',\n 'isdCode',\n ];\n}\n\n/**\n * resolveCountryCode — this record's dial code for this field.\n * `record` is the flattened value map of the record being displayed; pass the\n * addRow ROW when formatting a repeatable row, so a row's own code wins.\n */\nexport function resolveCountryCode(field, record, fallback = '') {\n const configured = field?.renderOptions?.countryCodeField ?? field?.countryCodeField;\n const keys = configured ? [configured, ...countryCodeKeys(field)] : countryCodeKeys(field);\n for (const key of keys) {\n const code = normalizeDialCode(getPath(record, key));\n if (code) return code;\n }\n return normalizeDialCode(fallback);\n}\n\n/**\n * splitDialCode — separate a code the value already carries from the number.\n */\nexport function splitDialCode(raw) {\n const str = String(raw ?? '').trim();\n const match = str.match(/^(\\+\\d{1,4})[\\s-]*(.*)$/);\n return match ? { code: match[1], rest: match[2] } : { code: '', rest: str };\n}\n\n/**\n * displayPhone — the finished, maskable display string.\n * `countryCode` is whatever resolveCountryCode produced; a code embedded in the\n * value still wins, because that is the record speaking for itself.\n */\nexport function displayPhone(raw, countryCode = '') {\n if (isEmpty(raw)) return '';\n const { code, rest } = splitDialCode(raw);\n const cc = code || normalizeDialCode(countryCode);\n const formatted = formatPhone(rest) || rest;\n return `${cc ? `${cc} ` : ''}${formatted}`.trim();\n}\n\n/**\n * displayPhoneForField — the one call sites use: resolve the code off the\n * record, then format. Falls back to the global default only as a last resort.\n */\nexport function displayPhoneForField(field, value, record) {\n const code = resolveCountryCode(field, record, getPhoneCountryCode());\n return displayPhone(value, code);\n}\n","// renderConfig — non-component shared module for the detail-view render engine.\n// Holds the Admin-facing option lists and the file/document resolution helpers,\n// kept out of the .jsx engine so React Fast Refresh stays happy and so other\n// modules (DocumentLink, the admin screen) can reuse them.\n\nimport { getDefaults } from '../../services/detailDefaults';\n\n// ── Admin option lists ───────────────────────────────────────────────────────\nexport const RENDER_TYPES = [\n { value: 'auto', label: 'Default (auto)' },\n { value: 'text', label: 'Text' },\n { value: 'tag', label: 'Tag' },\n { value: 'tags', label: 'Multi Tag (Skills)' },\n { value: 'badge', label: 'Badge' },\n { value: 'chip', label: 'Chip' },\n { value: 'document', label: 'Document' },\n { value: 'link', label: 'Link' },\n { value: 'email', label: 'Email' },\n { value: 'phone', label: 'Phone' },\n { value: 'date', label: 'Date' },\n { value: 'currency', label: 'Currency' },\n { value: 'budget', label: 'Budget' },\n { value: 'html', label: 'HTML' },\n];\n\nexport const DOCUMENT_MODES = [\n { value: 'preview', label: 'Preview (viewer)' },\n { value: 'download', label: 'Download button' },\n { value: 'link', label: 'Link (filename)' },\n { value: 'tag', label: 'Tag' },\n { value: 'text', label: 'Text (filename)' },\n];\n\nexport const TEXT_TRANSFORMS = [\n { value: 'none', label: 'None' },\n { value: 'upper', label: 'UPPERCASE' },\n { value: 'lower', label: 'lowercase' },\n { value: 'title', label: 'Title Case' },\n];\n\n// ── file / document resolution ───────────────────────────────────────────────\nconst LOC_EXACT = ['location', 'path', 'url', 'key'];\nconst LOC_SUFFIX = ['location', 'filepath', 'path', 'url', 'key'];\nconst NAME_SUFFIX = ['uploadedfilename', 'uploadname', 'originalname', 'displayname', 'documentname', 'filename'];\nconst NAME_LAST = ['name'];\nconst lc = (s) => String(s).toLowerCase();\nconst isEmpty = (v) => v === undefined || v === null || v === '';\n\nfunction pickExact(obj, keys) {\n for (const key of keys) {\n const v = obj[key];\n if (typeof v === 'string' && v) return v;\n }\n return '';\n}\n\nfunction pickSuffix(obj, suffixes) {\n for (const suffix of suffixes) {\n for (const [key, val] of Object.entries(obj)) {\n if (typeof val === 'string' && val && lc(key).endsWith(suffix)) return val;\n }\n }\n return '';\n}\n\nexport function fileNameFromPath(path) {\n if (!path) return '';\n const clean = String(path).split('?')[0].split('#')[0];\n return decodeURIComponent(clean.split('/').pop() || '');\n}\n\n/**\n * buildFileUrl — resolves a file value to a directly-openable URL, or ''.\n *\n * IT NO LONGER BUILDS AN S3 URL FROM A STORED OBJECT KEY.\n *\n * It used to return `s3BaseUrl + key`. The bucket is PRIVATE, so that URL\n * carries no signature and S3 answers 403 — every resume in the product failed\n * to open with exactly that symptom. The key is not a URL; it is an identifier\n * that only the backend is allowed to resolve, because only the backend can\n * check that the caller is permitted to read that record.\n *\n * An absolute http(s) URL is still returned as-is — some fields genuinely hold\n * a public link (a company website, an externally hosted JD). But a bare\n * object key now returns '', which makes callers fall through to the\n * authenticated resolver (services/documentContentApi.js) instead of silently\n * producing a URL that 403s.\n *\n * Returning '' rather than throwing is deliberate: every caller already treats\n * '' as \"not directly linkable\", so this degrades into the resolver path\n * instead of turning a broken preview into a crashed page.\n */\nexport function buildFileUrl(location) {\n if (!location) return '';\n const loc = String(location);\n if (/^https?:\\/\\//i.test(loc)) return loc;\n return '';\n}\n\n/**\n * isStorageKey — true when a value is a stored object key rather than a URL,\n * i.e. it must be fetched through the authenticated document endpoint.\n */\nexport function isStorageKey(location) {\n const loc = String(location ?? '').trim();\n if (!loc) return false;\n if (/^https?:\\/\\//i.test(loc)) return false;\n if (loc.startsWith('blob:') || loc.startsWith('data:')) return false;\n return loc.includes('/');\n}\n\nexport function locationOf(doc) {\n if (typeof doc === 'string') return doc.includes('/') ? doc : '';\n if (!doc || typeof doc !== 'object') return '';\n return pickExact(doc, LOC_EXACT) || pickSuffix(doc, LOC_SUFFIX);\n}\n\nexport function docDisplayName(doc) {\n if (!doc) return 'Document';\n if (typeof doc === 'string') return fileNameFromPath(doc) || doc;\n if (typeof doc !== 'object') return 'Document';\n const order = getDefaults().documentNameOrder ?? [];\n return (\n pickExact(doc, order) ||\n pickSuffix(doc, NAME_SUFFIX) ||\n fileNameFromPath(locationOf(doc)) ||\n pickSuffix(doc, NAME_LAST) ||\n 'Document'\n );\n}\n\n// extractDocuments flattens any file-field value (array | object | string) into\n// a list of { location, name, url }.\nexport function extractDocuments(value) {\n if (isEmpty(value)) return [];\n const items = Array.isArray(value) ? value : [value];\n const out = [];\n for (const item of items) {\n if (isEmpty(item)) continue;\n if (typeof item === 'string') {\n const location = item.includes('/') ? item : '';\n out.push({\n location,\n name: fileNameFromPath(item) || item,\n url: buildFileUrl(location),\n needsAuthFetch: isStorageKey(location),\n });\n } else if (typeof item === 'object') {\n const location = locationOf(item);\n // A doc from the central `documents` collection already carries a 24h\n // presigned download URL (fileUrl) — prefer it over the raw location.\n const presigned = item.fileUrl || item.downloadUrl || item.signedUrl || '';\n out.push({\n location,\n name: docDisplayName(item),\n url: presigned || buildFileUrl(location),\n documentId: item.id || item._id || item.documentId || '',\n // `needsAuthFetch` marks a document whose bytes must come from the\n // authenticated backend because all we hold is a private-bucket object\n // key. Before this existed, callers built s3BaseUrl + key and got a\n // 403 — the key was indistinguishable from a usable URL once\n // buildFileUrl had concatenated it.\n needsAuthFetch: !presigned && isStorageKey(location),\n });\n }\n }\n return out;\n}\n","/**\n * normaliseRepeatableRows — accept the older shapes a repeatable group's data\n * can be stored in, without inventing anything.\n *\n * # Why this exists\n *\n * A repeatable group renders rows of `{ fieldA, fieldB, … }`. Records written\n * before the group gained its extra columns store something simpler — most\n * often a bare list of scalars. Measured on the live database: of 359 stored\n * language entries, 358 are the plain string \"English\" and one is\n * `{ language: \"Algerian\" }`. None carries the read/write/speak values the\n * current group asks for.\n *\n * The form cannot map a string onto `{ language, languageCanRead, … }`, so the\n * row renders empty and the user concludes their data is gone — the reported\n * \"R/W/S values are not mapped correctly for migrated candidates\".\n *\n * # What it deliberately does NOT do\n *\n * It fills in the field it can identify and leaves the rest EMPTY. It would be\n * easy to default a migrated \"English\" row to R/W/S all ticked, and it would be\n * wrong: nobody recorded that, and writing it would manufacture a fact about a\n * person's language ability. An empty field a recruiter can fill in is honest;\n * an invented one is not, and it is indistinguishable from a real answer once\n * saved.\n *\n * # Generic, not language-specific\n *\n * Nothing here knows about languages. Any repeatable group whose rows were once\n * scalars gets the same treatment, and the target field comes from the group's\n * own config.\n */\n\n/**\n * primaryFieldOf — the field a bare scalar belongs in.\n *\n * `group.primaryField` when configured; otherwise the group's first field,\n * which is the column a single-value list was collapsed from in every case\n * observed. Explicit config wins so a group whose first column is not the\n * identifying one can say so.\n */\nexport function primaryFieldOf(group) {\n if (group?.primaryField) return group.primaryField;\n const first = (group?.fields ?? []).find((f) => f?.field);\n return first?.field ?? null;\n}\n\n/**\n * Converts stored rows into the object shape the form renders.\n *\n * Returns the input unchanged when there is nothing to convert, so a group\n * already storing objects pays nothing and cannot be altered by this.\n */\nexport function normaliseRepeatableRows(group, rows) {\n if (!Array.isArray(rows) || rows.length === 0) return rows;\n\n const primary = primaryFieldOf(group);\n if (!primary) return rows;\n\n let changed = false;\n const normalised = rows.map((row) => {\n // Already the right shape — and an object is never rewritten, so a row\n // holding real R/W/S values passes through untouched.\n if (row !== null && typeof row === 'object' && !Array.isArray(row)) return row;\n\n // null / undefined / '' are not a language; they become an empty row rather\n // than a row whose primary field is the string \"null\".\n if (row === null || row === undefined || String(row).trim() === '') {\n changed = true;\n return {};\n }\n\n changed = true;\n // Only the field we can actually identify. Every other column stays absent,\n // which is what makes this a mapping rather than a guess.\n return { [primary]: row };\n });\n\n return changed ? normalised : rows;\n}\n","import { Button } from 'antd';\nimport { Link as RouterLink } from 'react-router-dom';\nimport '../styles/AppButton.css';\n\nexport default function AppButton({\n children,\n className = '',\n icon,\n to,\n variant = 'default',\n ...rest\n}) {\n const button = (\n <Button\n className={`app-button app-button--${variant} ${className}`.trim()}\n icon={icon}\n {...rest}\n >\n {children}\n </Button>\n );\n\n if (to) {\n return (\n <RouterLink className=\"app-button-link\" to={to}>\n {button}\n </RouterLink>\n );\n }\n\n return button;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// userNames — turning a stored `updatedBy` id into a person's name.\n//\n// Rate history (and any other audit trail) stores WHO changed a value as an id.\n// The detail view resolved it through one directory call — /module/list?\n// module=users — and indexed the result on `legacyUserId` ALONE. Ids stamped by\n// the auth service are auth user ids, so they missed that index and the history\n// row read \"User #123\": a number shown to a recruiter as if it meant something.\n//\n// Two fixes, both generic:\n// • index every id form a users row can carry (legacyUserId / userId / _id /\n// id) — the same person under all their identities;\n// • resolve a still-unknown id through the auth service's own user endpoint,\n// cached per id for the session so a long history costs one call per\n// distinct actor, not one per row.\n//\n// And when it is STILL unresolvable, render nothing. \"User #123\" is not\n// information; an empty attribution is honest.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport { fetchJsonWithAuth } from '../../services/authApi';\nimport { AUTH_URL } from '../../services/apiConfig';\n\n/**\n * userDisplayName — the same name-shape preference the finance panel uses\n * (financeApprovalData.userDisplayName), so one person reads identically on\n * every surface regardless of which endpoint produced the row.\n */\nexport function userDisplayName(user) {\n if (!user || typeof user !== 'object') return '';\n const firstName = user.first_name ?? user.firstName ?? user.FIRST_NAME;\n const lastName = user.last_name ?? user.lastName ?? user.LAST_NAME;\n return [firstName, lastName].filter(Boolean).join(' ').trim()\n || user.name\n || user.user_name\n || user.userName\n || user.username\n || user.email\n || '';\n}\n\n/**\n * indexUserRows — id → name for EVERY id a directory row carries, so an actor\n * stamped with an auth id and one stamped with a legacy id both resolve.\n */\nexport function indexUserRows(rows) {\n const map = {};\n (Array.isArray(rows) ? rows : []).forEach((user) => {\n const name = userDisplayName(user);\n if (!name) return;\n [user.legacyUserId, user.userId, user.user_id, user._id, user.id].forEach((id) => {\n if (id === undefined || id === null || id === '') return;\n const key = String(typeof id === 'object' ? (id.$oid ?? id.id ?? '') : id);\n if (key && map[key] === undefined) map[key] = name;\n });\n });\n return map;\n}\n\n// Session cache: id → Promise<string>. Shared across every detail view so\n// re-opening a record never re-asks for the same person.\nconst userNameCache = new Map();\n\n/**\n * fetchUserName — resolve one id through the auth service, '' when unknown.\n * Never throws: an unresolvable actor must degrade to no attribution, never to\n * a broken detail page.\n */\nexport function fetchUserName(userId) {\n const key = String(userId ?? '').trim();\n if (!key) return Promise.resolve('');\n if (userNameCache.has(key)) return userNameCache.get(key);\n const request = fetchJsonWithAuth(\n AUTH_URL,\n `/get-detailed-view?userId=${encodeURIComponent(key)}`,\n )\n .then((json) => userDisplayName(json?.data ?? json ?? null))\n .catch(() => '');\n userNameCache.set(key, request);\n return request;\n}\n\n// Test seam — lets a suite prime/clear the cache without a network call.\nexport function primeUserName(userId, name) {\n userNameCache.set(String(userId), Promise.resolve(name));\n}\nexport function clearUserNameCache() {\n userNameCache.clear();\n}\n","// Shared role/permission gate used by row actions (ListView, DetailHeaderCard)\n// and, via AddFormV1/EditFormV1, by field-level visiblePermission/editablePermission.\n// A permission value looks like \"<module>.<key>\" (e.g. \"submission.viewRate\").\n//\n// Two gates exist. checkPermission() resolves against usePermissions()/`can`\n// (GET /me/permissions) — but PermissionProvider is mounted nowhere, so it has\n// no live caller. roleAllowsAction() below, reading localStorage.menuPermission\n// (the login blob), is the gate that ACTUALLY runs: DetailHeaderCard, ListView,\n// ModuleRowActions and EditFormV1's field permissions all call it. An earlier\n// comment here called it deprecated with \"no remaining call sites\"; that was\n// wrong, and believing it is how its name-resolution bug went unfixed.\n\nexport function isActionValueAllowed(value) {\n if (value === undefined || value === null) return true;\n if (typeof value === 'object') {\n return isActionValueAllowed(value.permission ?? value.allowed ?? value.value);\n }\n return value !== false && value !== 0 && value !== '0';\n}\n\n// permission: \"<module>.<key>\" string from admin config (RowAction.permission,\n// field.visiblePermission/editablePermission). can: the `can` function returned\n// by usePermissions(). Falls back to the field/action's own moduleName when the\n// permission string has no module prefix (a bare key, e.g. \"edit\").\nexport function checkPermission(can, permission, moduleName) {\n if (!permission) return true;\n if (typeof can !== 'function') return true;\n const [configuredModule, configuredKey] = String(permission).split('.');\n const module = configuredKey ? configuredModule : (moduleName || configuredModule);\n const key = configuredKey ?? configuredModule;\n return can(module, key);\n}\n\n// resolveModulePermissions — find a module's entry in the login blob.\n//\n// menuPermission is keyed by MENU KEY (\"submissions\"), but admin config writes\n// the module part of a permission every which way: \"submission\", \"Submissions\",\n// \"Jobs\", \"candidate\". This used to be a chain of exact lookups ending in `{}`,\n// so any spelling that missed resolved to \"no entry\" and the action was ALLOWED.\n// On the submission detail page (config name \"submissiondetails\") that is why\n// \"submission.delete\" guarded nothing at all.\n//\n// Matching mirrors the server's modulePermissions() (controllers/\n// moduleDataController.go): case-insensitive, singular/plural tolerant. The two\n// sides of one permission system must agree on what a name means.\nfunction resolveModulePermissions(permissions, name) {\n const wanted = String(name ?? '').trim().toLowerCase();\n if (!wanted || !permissions || typeof permissions !== 'object') return undefined;\n for (const [key, value] of Object.entries(permissions)) {\n const candidate = String(key).trim().toLowerCase();\n if (candidate === wanted || candidate === `${wanted}s` || `${candidate}s` === wanted) {\n return value && typeof value === 'object' ? value : undefined;\n }\n }\n return undefined;\n}\n\n// resolveActionValue — the action flag, in either login-blob shape: the Mongo\n// path writes \"approve\", the MySQL-era path writes \"approveAccess\"\n// (companyController.go builds <PERMISSIONKEY>+\"Access\").\nfunction resolveActionValue(modulePermissions, key) {\n if (Object.prototype.hasOwnProperty.call(modulePermissions, key)) return modulePermissions[key];\n const legacy = `${key}Access`;\n if (Object.prototype.hasOwnProperty.call(modulePermissions, legacy)) return modulePermissions[legacy];\n return undefined;\n}\n\n// roleAllowsAction — the localStorage.menuPermission gate that actually runs.\n//\n// A permission is \"<module>.<key>\" (e.g. \"submissions.approve\"); a bare \"<key>\"\n// uses the caller's moduleName. The permission's own module is tried first,\n// then the caller's, so an explicit module always wins.\n//\n// DELIBERATELY STILL PERMISSIVE for a name it cannot resolve at all. Live config\n// carries permissions naming things the blob has no entry for — \"sendMail\" is\n// not a permission key on any menu, and \"Batch Management\" / \"quickSubmit\" are\n// not menus — and those actions work today only because an unresolvable name is\n// allowed. Failing closed would hide Send Mail on candidates, jobs and\n// onboarding for every user. What this fixes is resolution: a name that DOES\n// exist is now always found and enforced. Approve and reject do not rely on this\n// gate at all — the server enforces them (routes.RequirePermission).\nexport function roleAllowsAction(permission, moduleName) {\n if (!permission) return true;\n try {\n const permissions = JSON.parse(localStorage.getItem('menuPermission') || '{}');\n const [configuredModule, configuredKey] = String(permission).split('.');\n const key = configuredKey ?? configuredModule;\n const names = configuredKey ? [configuredModule, moduleName] : [moduleName];\n for (const name of names) {\n const modulePermissions = resolveModulePermissions(permissions, name);\n if (modulePermissions) return isActionValueAllowed(resolveActionValue(modulePermissions, key));\n }\n return true;\n } catch {\n return true;\n }\n}\n","function comparisonValues(value) {\n if (Array.isArray(value)) return value.map((item) => String(item ?? '').trim());\n return String(value ?? '').split(',').map((item) => item.trim());\n}\n\n// Matches the same generic operators supported by showIf. Keeping this helper\n// independent of React makes conditional labels usable in Add/Edit forms and\n// straightforward to test without module- or field-specific code.\nexport function conditionMatches(condition, value) {\n if (!condition?.field) return false;\n switch (condition.operator ?? 'eq') {\n case 'eq': return String(value ?? '') === String(condition.value ?? '');\n case 'neq': return String(value ?? '') !== String(condition.value ?? '');\n case 'truthy': return value !== undefined && value !== null && value !== '' && value !== false;\n case 'falsy': return value === undefined || value === null || value === '' || value === false;\n case 'notEmpty': return Array.isArray(value) ? value.length > 0 : Boolean(value);\n case 'in': return comparisonValues(condition.value).includes(String(value ?? ''));\n case 'notIn': return !comparisonValues(condition.value).includes(String(value ?? ''));\n default: return false;\n }\n}\n\nexport function configuredFieldLabel(field, watchedValue, combined = false) {\n const fallback = combined\n ? (field?.combineLabel ?? field?.label)\n : field?.label;\n const rule = field?.labelWhen;\n return conditionMatches(rule, watchedValue) && rule?.label\n ? rule.label\n : fallback;\n}\n\n// Flatten a showIf into its leaf conditions: the top-level {field,operator,value}\n// plus any \"add more\" entries in conditions[]. Mirrors how the forms read it.\nexport function showIfConditions(showIf) {\n if (!showIf) return [];\n const leaf = showIf.field ? [showIf] : [];\n return [...leaf, ...(showIf.conditions ?? []).filter((c) => c?.field)];\n}\n\n// Evaluate a showIf against an arbitrary value source. readValue(fieldKey) lets\n// the caller decide where values come from — live form state in the forms, or a\n// saved record in the detail view — so one condition definition drives both.\nexport function evaluateShowIfWith(showIf, readValue) {\n const conditions = showIfConditions(showIf);\n if (!conditions.length) return true;\n const results = conditions.map((c) => conditionMatches(c, readValue(c.field)));\n return showIf.logic === 'or' ? results.some(Boolean) : results.every(Boolean);\n}\n\n// colWhen — conditionally override a field's grid column span, so one field can\n// shrink to make room for a sibling that a showIf has just revealed (e.g. Work\n// Authorization narrows when its expiry date appears). Config is a rule, or a\n// list of rules, each {field, operator, value, col}; the first match wins.\n// Display only — the field key, payload mapping and validation are untouched.\nexport function colWhenRules(field) {\n const raw = field?.colWhen;\n if (!raw) return [];\n return (Array.isArray(raw) ? raw : [raw])\n .filter((rule) => rule?.field && Number(rule?.col) > 0);\n}\n\n// readValue(rule) resolves the watched value for one rule — the caller owns path\n// resolution so a rule can reference a sibling in the same addRow row or a\n// top-level field, exactly like showIf.\nexport function resolveColSpan(field, fallbackSpan, readValue) {\n for (const rule of colWhenRules(field)) {\n if (conditionMatches(rule, readValue(rule))) return Number(rule.col);\n }\n return fallbackSpan;\n}\n\nexport function conditionFieldPath(condition, siblingPrefix) {\n if (!condition?.field) return ['__noop_conditional_label__'];\n return siblingPrefix != null\n ? [...(Array.isArray(siblingPrefix) ? siblingPrefix : [siblingPrefix]), condition.field]\n : [condition.field];\n}\n"],"mappings":"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"}
|