@realtek/core-theme 0.0.291 → 0.0.293
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/{Breadcrumb-B53cuO_P.cjs → Breadcrumb-CEMaPPLj.cjs} +2 -2
- package/dist-lib/{Breadcrumb-B53cuO_P.cjs.map → Breadcrumb-CEMaPPLj.cjs.map} +1 -1
- package/dist-lib/{Breadcrumb-DubLK7aU.js → Breadcrumb-DMMhMHcG.js} +964 -946
- package/dist-lib/{Breadcrumb-DubLK7aU.js.map → Breadcrumb-DMMhMHcG.js.map} +1 -1
- package/dist-lib/index.cjs +6 -6
- package/dist-lib/index.cjs.map +1 -1
- package/dist-lib/index.js +950 -947
- package/dist-lib/index.js.map +1 -1
- package/dist-lib/style.css +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Breadcrumb-DubLK7aU.js","names":[],"sources":["../src/services/adminApi.js","../src/services/detailDefaults.js","../src/services/timezone.js","../src/theme/colors/colors.js","../src/components/typography/Typography.jsx","../src/components/TipTapEditor.jsx","../src/components/DocumentViewer.jsx","../src/utils/modulePath.js","../src/services/moduleDataApi.js","../src/services/addFormApi.js","../src/services/documentApi.js","../src/services/detailedViewApi.js","../src/utils/roleFormPermissionGate.js","../src/components/form/inputSecurity.js","../src/components/form/optionMatching.js","../src/components/form/payloadTransformer.js","../src/components/form/linkedAddRowGroups.js","../src/components/form/formDecisionDialog.jsx","../src/services/aiActionApi.js","../src/components/detail/phoneDisplay.js","../src/components/detail/renderConfig.js","../src/components/form/applyGroupValues.js","../src/components/form/useAiActions.js","../src/components/form/AiActionButtons.jsx","../src/components/form/emailValidator.js","../src/components/form/inputValidator.js","../src/components/form/uniqueFieldValidation.js","../src/services/uniqueValidationApi.js","../src/components/form/uploadAccept.js","../src/components/form/quickActionLabels.js","../src/components/AppButton.jsx","../src/components/detail/userNames.js","../src/components/form/quickCreateNotice.js","../src/components/form/QuickCreateEditField.jsx","../src/components/form/crossFieldRules.js","../src/components/form/maxCeiling.js","../src/components/form/contextPrefill.js","../src/components/form/prefillWhenRules.js","../src/components/form/optionConstraints.js","../src/components/form/optionRowFilters.js","../src/components/form/fieldTooltip.js","../src/utils/roleAllowsAction.js","../src/utils/afterSubmitNav.js","../src/utils/backNav.js","../src/components/form/educationRules.js","../src/components/form/clearGroupOnChange.js","../src/components/form/resolveStoredOptions.js","../src/components/form/dateRules.js","../src/utils/submitMessages.js","../src/utils/scrollToFirstFormError.js","../src/components/form/conditionalFieldLabel.js","../src/components/Breadcrumb.jsx"],"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 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 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 dataSource: field.dataSource ?? 'module',\n groupName: field.groupName ?? '',\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 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 };\n}\n\nexport async function saveDetailConfig({ module, fields = [], merges = [], 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, ...(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// ── 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') {\n const params = new URLSearchParams({ module: apiModule, userId: '0', [idParam]: String(idValue), 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\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\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 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 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: field.lookup ?? null, actionButtons: field.actionButtons ?? [],\n derived: field.derived ?? null, computed: field.computed ?? null,\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 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 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) {\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({ module: apiModule, roleId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\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 field keys forced always-shown + always-required.\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.\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\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\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 }),\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\nexport async function createModuleActionRule({ module, name, description = '', conditions = [], blockedActions = [], disabledActions = [], 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, isActive }),\n });\n}\n\nexport async function updateModuleActionRule(id, { name, description = '', conditions = [], blockedActions = [], disabledActions = [], 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, isActive }),\n });\n}\n\nexport async function deleteModuleActionRule(id) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}/${id}`, { method: 'DELETE' });\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';\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});\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 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 current = { ...current, ...sanitize(partial) };\n return current;\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 loadPromise = 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 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","export const colors = {\n brand: '#0053a5',\n brandDark: '#1d4ed8',\n brandDarker: '#1e40af',\n brandHover: '#004f85',\n brandSoft: '#dfedf7',\n brandSofter: '#e7f2fa',\n brandSubtle: '#edf8fe',\n\n textPrimary: '#111827',\n textSecondary: '#4b5563',\n textMuted: '#6b7280',\n textSubtle: '#010306',\n textHeading: '#142235',\n textDark: '#232a31',\n textPlaceholder: '#a7b0bb',\n textInverse: '#ffffff',\n textLink: '#0053a5',\n\n surfacePage: '#f5f6fa',\n surfaceSoft: '#f8fafc',\n surfaceSofter: '#f3f8fb',\n surfaceCard: '#ffffff',\n surfaceHover: '#eff6ff',\n surfaceHoverLight: '#f8fcff',\n surfaceHoverSoft: '#f5fbff',\n surfaceSelected: '#dbeafe',\n surfaceControl: '#f4f8fb',\n surfaceRowAlt: '#fbfdff',\n\n border: '#e6f0ff',\n borderLight: '#eef3f8',\n borderSofter: '#f0f0f0',\n borderMuted: '#d8e4ef',\n borderInput: '#d5dde5',\n borderFocus: '#7dbce6',\n borderHover: '#99c7e8',\n controlBorder: '#77abd0',\n controlBorderMuted: '#e7edf3',\n controlAccent: '#4f9ac7',\n\n iconMuted: '#d1d5db',\n iconSubtle: '#9aa6b2',\n iconNeutral: '#8c8c8c',\n iconSoft: '#bbbbbb',\n scrollbarThumb: '#c4ccd8',\n scrollbarThumbLight: '#d1d5db',\n danger: '#dc2626',\n dangerSoft: '#ef4444',\n dangerStrong: '#e11d24',\n success: '#15803d',\n successSoft: '#16a34a',\n warning: '#f97316',\n info: '#3b82f6',\n transparent: 'transparent',\n\n statusNeutralBg: '#f1f3ee',\n statusNeutralText: '#717b36',\n statusProcessingBg: '#f6f4f7',\n statusProcessingText: '#273048',\n statusProcessingBorder: '#d4d8dd',\n statusWarningBg: '#fff7ea',\n statusWarningText: '#bf7328',\n\n shadowMenu: 'rgba(15, 35, 55, 0.08)',\n shadowBadge: 'rgba(26, 95, 145, 0.08)',\n shadowTag: 'rgba(39, 51, 70, 0.05)',\n shadowAvatar: 'rgba(57, 77, 103, 0.12)',\n shadowDropdown: '0 6px 16px 0 rgba(0, 0, 0, .08), 0 3px 6px -4px rgba(0, 0, 0, .12), 0 9px 28px 8px rgba(0, 0, 0, .05)',\n avatarBlueBg: '#eaf3ff',\n avatarBlueText: '#142235',\n avatarPurpleBg: '#f2eaff',\n avatarMoreBg: '#eef6ff',\n avatarMoreText: '#0053a5',\n\n avatarNeutralBg: '#d9d9d9',\n avatarNeutralText: '#555555',\n avatarIndigo: '#6366f1',\n avatarPurple: '#8b5cf6',\n avatarBlue: '#3b82f6',\n avatarGreen: '#10b981',\n linkedIn: '#0077b5',\n};\n\nexport const typographyColors = {\n primary: colors.textPrimary,\n secondary: colors.textSecondary,\n muted: colors.textMuted,\n subtle: colors.textSubtle,\n inverse: colors.textInverse,\n link: colors.textLink,\n danger: colors.danger,\n success: colors.success,\n};\n\nexport const onboardingStageToneColors = {\n approved: {\n background: '#f2f2e8',\n text: '#79772d',\n },\n danger: {\n background: '#fde7e9',\n text: colors.danger,\n },\n issued: {\n background: '#e9f2fb',\n text: colors.textLink,\n },\n neutral: {\n background: colors.surfaceSoft,\n text: colors.textSecondary,\n },\n success: {\n background: '#e7f5ee',\n text: colors.success,\n },\n warning: {\n background: '#fbf0e7',\n text: colors.warning,\n },\n};\n\nexport const colorVars = {\n brand: 'var(--color-brand)',\n brandDark: 'var(--color-brand-dark)',\n brandDarker: 'var(--color-brand-darker)',\n brandHover: 'var(--color-brand-hover)',\n brandSoft: 'var(--color-brand-soft)',\n brandSofter: 'var(--color-brand-softer)',\n brandSubtle: 'var(--color-brand-subtle)',\n\n textPrimary: 'var(--color-text-primary)',\n textSecondary: 'var(--color-text-secondary)',\n textMuted: 'var(--color-text-muted)',\n textSubtle: 'var(--color-text-subtle)',\n textHeading: 'var(--color-text-heading)',\n textDark: 'var(--color-text-dark)',\n textPlaceholder: 'var(--color-text-placeholder)',\n textInverse: 'var(--color-text-inverse)',\n textLink: 'var(--color-text-link)',\n\n surfacePage: 'var(--color-surface-page)',\n surfaceSoft: 'var(--color-surface-soft)',\n surfaceSofter: 'var(--color-surface-softer)',\n surfaceCard: 'var(--color-surface-card)',\n surfaceHover: 'var(--color-surface-hover)',\n surfaceHoverLight: 'var(--color-surface-hover-light)',\n surfaceHoverSoft: 'var(--color-surface-hover-soft)',\n surfaceSelected: 'var(--color-surface-selected)',\n surfaceControl: 'var(--color-surface-control)',\n surfaceRowAlt: 'var(--color-surface-row-alt)',\n\n border: 'var(--color-border)',\n borderLight: 'var(--color-border-light)',\n borderSofter: 'var(--color-border-softer)',\n borderMuted: 'var(--color-border-muted)',\n borderInput: 'var(--color-border-input)',\n borderFocus: 'var(--color-border-focus)',\n borderHover: 'var(--color-border-hover)',\n controlBorder: 'var(--color-control-border)',\n controlBorderMuted: 'var(--color-control-border-muted)',\n controlAccent: 'var(--color-control-accent)',\n\n iconMuted: 'var(--color-icon-muted)',\n iconSubtle: 'var(--color-icon-subtle)',\n iconNeutral: 'var(--color-icon-neutral)',\n iconSoft: 'var(--color-icon-soft)',\n scrollbarThumb: 'var(--color-scrollbar-thumb)',\n scrollbarThumbLight: 'var(--color-scrollbar-thumb-light)',\n danger: 'var(--color-danger)',\n dangerSoft: 'var(--color-danger-soft)',\n dangerStrong: 'var(--color-danger-strong)',\n success: 'var(--color-success)',\n successSoft: 'var(--color-success-soft)',\n warning: 'var(--color-warning)',\n info: 'var(--color-info)',\n transparent: 'var(--color-transparent)',\n linkedIn: 'var(--color-linkedin)',\n\n statusNeutralBg: 'var(--color-status-neutral-bg)',\n statusNeutralText: 'var(--color-status-neutral-text)',\n statusProcessingBg: 'var(--color-status-processing-bg)',\n statusProcessingText: 'var(--color-status-processing-text)',\n statusProcessingBorder: 'var(--color-status-processing-border)',\n statusWarningBg: 'var(--color-status-warning-bg)',\n statusWarningText: 'var(--color-status-warning-text)',\n\n shadowMenu: 'var(--color-shadow-menu)',\n shadowBadge: 'var(--color-shadow-badge)',\n shadowTag: 'var(--color-shadow-tag)',\n shadowAvatar: 'var(--color-shadow-avatar)',\n shadowDropdown: 'var(--color-shadow-dropdown)',\n avatarBlueBg: 'var(--color-avatar-blue-bg)',\n avatarBlueText: 'var(--color-avatar-blue-text)',\n avatarPurpleBg: 'var(--color-avatar-purple-bg)',\n avatarMoreBg: 'var(--color-avatar-more-bg)',\n avatarMoreText: 'var(--color-avatar-more-text)',\n};\n","import { Typography } from 'antd';\nimport { colorVars } from '../../theme/colors/colors';\n\nconst { Text, Title, Paragraph, Link } = Typography;\n\nconst defaultElementByVariant = {\n display: 'h1',\n h1: 'h1',\n h2: 'h2',\n h3: 'h3',\n h4: 'h4',\n h5: 'h5',\n 'section-title': 'h3',\n 'card-title': 'h4',\n subtitle: 'span',\n body: 'span',\n 'body-strong': 'span',\n label: 'span',\n caption: 'span',\n meta: 'span',\n metric: 'span',\n helper: 'span',\n link: 'a',\n};\n\nconst namedSizes = {\n xs: 'var(--font-size-xs)',\n sm: 'var(--font-size-sm)',\n md: 'var(--font-size-md)',\n lg: 'var(--font-size-lg)',\n xl: 'var(--font-size-xl)',\n '2xl': 'var(--font-size-2xl)',\n '3xl': 'var(--font-size-3xl)',\n '4xl': 'var(--font-size-4xl)',\n};\n\nconst namedWeights = {\n regular: 'var(--font-weight-regular)',\n medium: 'var(--font-weight-medium)',\n semibold: 'var(--font-weight-semibold)',\n bold: 'var(--font-weight-bold)',\n extrabold: 'var(--font-weight-extrabold)',\n};\n\nconst namedLineHeights = {\n tight: 'var(--line-height-tight)',\n snug: 'var(--line-height-snug)',\n normal: 'var(--line-height-normal)',\n relaxed: 'var(--line-height-relaxed)',\n};\n\nconst namedColors = {\n primary: colorVars.textPrimary,\n secondary: colorVars.textSecondary,\n muted: colorVars.textMuted,\n subtle: colorVars.textSubtle,\n inverse: colorVars.textInverse,\n link: colorVars.textLink,\n danger: colorVars.danger,\n success: colorVars.success,\n};\n\nfunction cx(...classes) {\n return classes.filter(Boolean).join(' ');\n}\n\nfunction tokenValue(value, tokens) {\n if (value === undefined || value === null) return undefined;\n return tokens[value] || value;\n}\n\nfunction getAntTypographyComponent(tag, variant) {\n if (variant === 'link' || tag === 'a') return Link;\n if (tag === 'p') return Paragraph;\n if (['h1', 'h2', 'h3', 'h4', 'h5'].includes(tag)) return Title;\n return Text;\n}\n\nfunction getTitleLevel(tag, variant) {\n const resolvedTag = tag || defaultElementByVariant[variant];\n if (!resolvedTag?.startsWith('h')) return undefined;\n return Number(resolvedTag.slice(1));\n}\n\nexport default function AppTypography({\n as,\n tag,\n variant = 'body',\n color,\n size,\n weight,\n lineHeight,\n align,\n truncate = false,\n display,\n className,\n style,\n children,\n ...props\n}) {\n const resolvedTag = tag || as || defaultElementByVariant[variant] || 'span';\n const Component = getAntTypographyComponent(resolvedTag, variant);\n const titleLevel = getTitleLevel(resolvedTag, variant);\n const dynamicStyle = {\n color: tokenValue(color, namedColors),\n fontSize: tokenValue(size, namedSizes),\n fontWeight: tokenValue(weight, namedWeights),\n lineHeight: tokenValue(lineHeight, namedLineHeights),\n display,\n ...style,\n };\n\n return (\n <Component\n {...(titleLevel ? { level: titleLevel } : {})}\n className={cx(\n 'app-typography',\n `app-typography--${variant}`,\n color && namedColors[color] && `app-typography--${color}`,\n align && `app-typography--${align}`,\n truncate && 'app-typography--truncate',\n className,\n )}\n style={dynamicStyle}\n {...props}\n >\n {children}\n </Component>\n );\n}\n","/**\n * TipTapEditor — Rich text editor component\n * Drop-in replacement for ReactQuill in EditFormV1\n *\n * Props:\n * value string — HTML string (controlled)\n * onChange function — called with HTML string on every change\n * disabled boolean — makes editor read-only\n * placeholder string — placeholder text\n */\n\nimport { useEffect, useRef } from 'react';\nimport { useEditor, EditorContent } from '@tiptap/react';\nimport StarterKit from '@tiptap/starter-kit';\nimport Link from '@tiptap/extension-link';\nimport Underline from '@tiptap/extension-underline';\nimport '../styles/TipTapEditor.css';\n\n// ─── Toolbar button ───────────────────────────────────────────────────────────\n\nfunction ToolbarButton({ onClick, active, disabled, title, children }) {\n return (\n <button\n type=\"button\"\n title={title}\n disabled={disabled}\n className={`tte-btn${active ? ' tte-btn--active' : ''}`}\n onMouseDown={(e) => {\n e.preventDefault(); // prevent editor losing focus\n onClick?.();\n }}\n >\n {children}\n </button>\n );\n}\n\n// ─── Toolbar ─────────────────────────────────────────────────────────────────\n\nfunction Toolbar({ editor, disabled }) {\n if (!editor) return null;\n\n const setLink = () => {\n const url = window.prompt('Enter URL');\n if (!url) {\n editor.chain().focus().unsetLink().run();\n return;\n }\n editor.chain().focus().setLink({ href: url }).run();\n };\n\n return (\n <div className={`tte-toolbar${disabled ? ' tte-toolbar--disabled' : ''}`}>\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Bold\"\n disabled={disabled}\n active={editor.isActive('bold')}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <strong>B</strong>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Italic\"\n disabled={disabled}\n active={editor.isActive('italic')}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <em>I</em>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Underline\"\n disabled={disabled}\n active={editor.isActive('underline')}\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n >\n <span style={{ textDecoration: 'underline' }}>U</span>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Strikethrough\"\n disabled={disabled}\n active={editor.isActive('strike')}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <s>S</s>\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Bullet List\"\n disabled={disabled}\n active={editor.isActive('bulletList')}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n ≡\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Ordered List\"\n disabled={disabled}\n active={editor.isActive('orderedList')}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n 1.\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Link\"\n disabled={disabled}\n active={editor.isActive('link')}\n onClick={setLink}\n >\n 🔗\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Clear formatting\"\n disabled={disabled}\n onClick={() => editor.chain().focus().clearNodes().unsetAllMarks().run()}\n >\n ✕\n </ToolbarButton>\n </div>\n </div>\n );\n}\n\n// ─── Main editor ──────────────────────────────────────────────────────────────\n\nexport default function TipTapEditor({\n value = '',\n onChange,\n disabled = false,\n placeholder = '',\n}) {\n // Tracks the last value this editor itself produced (via onUpdate) or was\n // last told to show (via the sync effect below) — NOT the editor's live\n // getHTML(), which can drift from `value` after a round-trip through\n // TipTap's HTML serializer (e.g. plain AI-generated text with no <p> tags\n // never equals its own wrapped-in-<p> serialization, which previously made\n // the old getHTML()-based comparison useless as a \"did this come from us\"\n // check and caused an external update to fight with a stale echo).\n const lastKnownValueRef = useRef(value ?? '');\n\n const editor = useEditor({\n extensions: [\n StarterKit,\n Underline,\n Link.configure({\n openOnClick: false,\n HTMLAttributes: { rel: 'noopener noreferrer' },\n }),\n ],\n content: value, // set initial content correctly on mount\n editable: !disabled,\n editorProps: {\n attributes: {\n class: 'tte-content',\n },\n },\n onUpdate: ({ editor }) => {\n const html = editor.getHTML();\n const next = html === '<p></p>' ? '' : html;\n lastKnownValueRef.current = next;\n onChange?.(next);\n },\n });\n\n // Sync value from outside — handles setFieldsValue from Ant Design form\n // (e.g. an AI Action replacing the content). Skips only when the incoming\n // value is exactly what this editor itself last emitted, so a genuine\n // external update always applies even if it differs from getHTML() purely\n // due to HTML serialization (missing <p> wrapper, entity encoding, etc).\n useEffect(() => {\n if (!editor || editor.isDestroyed) return;\n const nextValue = value || '';\n if (nextValue === (lastKnownValueRef.current || '')) return;\n lastKnownValueRef.current = nextValue;\n editor.commands.setContent(nextValue, false);\n }, [value, editor]);\n\n // Sync disabled state\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(!disabled);\n }, [disabled, editor]);\n\n return (\n <div className={`tte-wrapper${disabled ? ' tte-wrapper--disabled' : ''}`}>\n <Toolbar editor={editor} disabled={disabled} />\n <EditorContent editor={editor} />\n {!value && !editor?.isFocused && placeholder && (\n <div className=\"tte-placeholder\">{placeholder}</div>\n )}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { Modal, Spin, Tooltip, message } from 'antd';\nimport {\n CloseOutlined,\n DownloadOutlined,\n FileUnknownOutlined,\n LeftOutlined,\n RightOutlined,\n ZoomInOutlined,\n ZoomOutOutlined,\n} from '@ant-design/icons';\nimport { Document, Page, pdfjs } from 'react-pdf';\nimport { renderAsync } from 'docx-preview';\nimport DOMPurify from 'dompurify';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\nimport '../styles/DocumentViewer.css';\n\n// pdf.js needs a web worker; Vite resolves this URL at build time so it works\n// in dev and production without copying files into /public.\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n 'pdfjs-dist/build/pdf.worker.min.mjs',\n import.meta.url,\n).toString();\n\nconst IMAGE_EXT = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'avif', 'ico'];\nconst TEXT_EXT = ['txt', 'csv', 'log', 'json', 'md', 'xml'];\nconst VIDEO_EXT = ['mp4', 'webm', 'ogg', 'ogv', 'mov', 'm4v'];\nconst ZOOM_STEP = 0.2;\nconst ZOOM_MIN = 0.4;\nconst ZOOM_MAX = 3;\n\nfunction fileNameFromUrl(url) {\n if (!url) return 'Document';\n const clean = String(url).split('?')[0].split('#')[0];\n return decodeURIComponent(clean.split('/').pop() || clean) || 'Document';\n}\n\nfunction extOf(nameOrUrl) {\n const clean = String(nameOrUrl || '').split('?')[0].split('#')[0];\n const dot = clean.lastIndexOf('.');\n return dot === -1 ? '' : clean.slice(dot + 1).toLowerCase();\n}\n\nconst KNOWN_BUCKETS = ['pdf', 'docx', 'image', 'text', 'video', 'html'];\n\n// bucketFromExt maps a bare extension (no dot) to a renderer bucket, or null.\nfunction bucketFromExt(ext) {\n if (!ext) return null;\n if (ext === 'pdf') return 'pdf';\n if (ext === 'doc' || ext === 'docx') return 'docx';\n if (IMAGE_EXT.includes(ext)) return 'image';\n if (TEXT_EXT.includes(ext)) return 'text';\n if (VIDEO_EXT.includes(ext)) return 'video';\n return null;\n}\n\n// kindOf maps a document to a renderer bucket: pdf | docx | image | text |\n// video | html | unknown.\n//\n// `doc.type` is normalized rather than trusted verbatim, because it arrives\n// in different shapes depending on the caller: an already-correct bucket\n// name (\"image\", inline html), a bare file extension as the backend upload\n// handler stores it (\"jpg\", \"png\", \"pdf\" — see timesheetController.go's\n// `ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(...), \".\"))`), or a\n// MIME type (\"image/jpeg\"). Returning a bare extension straight through only\n// happened to work for \"pdf\"/\"docx\" because those extensions equal their own\n// bucket name — every image extension (\"jpg\", \"png\", …) doesn't equal\n// \"image\", so it silently fell through to the \"no preview available\"\n// fallback. This maps all three shapes through the same extension table.\nfunction kindOf(doc) {\n const rawType = String(doc.type || '').toLowerCase().trim();\n if (KNOWN_BUCKETS.includes(rawType)) return rawType;\n\n const mimeExt = rawType.includes('/') ? rawType.split('/').pop() : rawType;\n const fromType = bucketFromExt(mimeExt);\n if (fromType) return fromType;\n\n const fromName = bucketFromExt(extOf(doc.name || doc.url));\n if (fromName) return fromName;\n\n return 'unknown';\n}\n\n// normalizeDocs accepts either bare URL strings, file objects, or inline\n// document objects ({ name, type: 'text'|'html', content }).\nfunction normalizeDocs(documents) {\n return (documents || [])\n .map((d, i) => {\n if (typeof d === 'string') {\n return { url: d, name: fileNameFromUrl(d), key: String(i) };\n }\n const url = d.url || d.location || d.path || '';\n return {\n url,\n name: d.name || fileNameFromUrl(url),\n type: d.type,\n content: typeof d.content === 'string' ? d.content : '',\n key: String(d.id ?? d._id ?? i),\n };\n })\n .filter((d) => d.url || d.content);\n}\n\n/* ------------------------------- renderers ------------------------------- */\n\nfunction PdfRenderer({ url, scale, onNativeFallback }) {\n const wrapRef = useRef(null);\n const [numPages, setNumPages] = useState(0);\n const [width, setWidth] = useState(0);\n const [error, setError] = useState(false);\n\n useEffect(() => {\n const el = wrapRef.current;\n if (!el) return undefined;\n const update = () => setWidth(el.clientWidth);\n update();\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n // pdf.js streams the file via its own fetch, which fails cross-origin on\n // storage URLs the bucket's CORS policy doesn't allowlist this origin for\n // (common — see the S3 bucket's CORS config, not something fixable here) —\n // distinct from a plain navigation (an <iframe> load), which is NOT subject\n // to CORS at all and works regardless. Fall back to that rather than\n // dead-ending the preview; the native viewer brings its own zoom/controls,\n // so the toolbar hides its (now inert) zoom buttons via onNativeFallback.\n //\n // This intentionally points the iframe at the raw presigned `url`, not a\n // fetched blob — fetching it would hit the same CORS wall react-pdf just\n // did. Whether this renders inline vs. downloads depends entirely on the\n // `Content-Disposition` the presigned URL responds with; that's set\n // server-side (GetPresignedURLInline in Be_Auth_DevOps) rather than worked\n // around here, so every consumer of the URL — this iframe, a plain link,\n // anything — gets the same correct inline behavior.\n if (error) {\n return <iframe title=\"PDF preview\" src={url} className=\"dv-pdf-native\" />;\n }\n\n return (\n <div ref={wrapRef} className=\"dv-pdf-wrap\">\n <Document\n file={url}\n loading={<Spin />}\n error={<FallbackStage label=\"This PDF could not be displayed.\" />}\n onLoadSuccess={({ numPages: n }) => setNumPages(n)}\n onLoadError={() => {\n setError(true);\n onNativeFallback?.();\n }}\n >\n {Array.from({ length: numPages }, (_, i) => (\n <Page\n key={`page-${i + 1}`}\n pageNumber={i + 1}\n width={width ? width * scale : undefined}\n className=\"dv-pdf-page\"\n renderTextLayer\n renderAnnotationLayer\n />\n ))}\n </Document>\n </div>\n );\n}\n\nPdfRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n scale: PropTypes.number.isRequired,\n onNativeFallback: PropTypes.func,\n};\n\nfunction DocxRenderer({ url }) {\n const ref = useRef(null);\n const [status, setStatus] = useState('loading'); // loading | ready | error\n\n useEffect(() => {\n let cancelled = false;\n\n fetch(url)\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return r.blob();\n })\n .then((blob) => {\n if (cancelled || !ref.current) return undefined;\n ref.current.innerHTML = '';\n return renderAsync(blob, ref.current, undefined, {\n className: 'dv-docx',\n inWrapper: true,\n ignoreWidth: false,\n ignoreHeight: false,\n });\n })\n .then(() => {\n if (!cancelled) setStatus('ready');\n })\n .catch(() => {\n if (!cancelled) setStatus('error');\n });\n\n return () => {\n cancelled = true;\n };\n }, [url]);\n\n if (status === 'error') {\n return <FallbackStage label=\"This document could not be displayed.\" />;\n }\n\n return (\n <div className=\"dv-docx-scroll\">\n {status === 'loading' && <div className=\"dv-center\"><Spin /></div>}\n <div ref={ref} style={{ visibility: status === 'ready' ? 'visible' : 'hidden' }} />\n </div>\n );\n}\n\nDocxRenderer.propTypes = { url: PropTypes.string.isRequired };\n\nfunction TextRenderer({ url, content = '' }) {\n const [text, setText] = useState(null);\n const [status, setStatus] = useState('loading');\n\n useEffect(() => {\n if (content) return undefined;\n\n let cancelled = false;\n fetch(url)\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return r.text();\n })\n .then((t) => {\n if (!cancelled) {\n setText(t);\n setStatus('ready');\n }\n })\n .catch(() => !cancelled && setStatus('error'));\n return () => {\n cancelled = true;\n };\n }, [content, url]);\n\n if (content) return <pre className=\"dv-text\">{content}</pre>;\n if (status === 'loading') return <div className=\"dv-center\"><Spin /></div>;\n if (status === 'error') return <FallbackStage label=\"This file could not be displayed.\" />;\n return <pre className=\"dv-text\">{text}</pre>;\n}\n\nTextRenderer.propTypes = {\n url: PropTypes.string,\n content: PropTypes.string,\n};\n\nfunction HtmlRenderer({ content }) {\n const safeHtml = useMemo(() => DOMPurify.sanitize(content), [content]);\n return <div className=\"dv-html\" dangerouslySetInnerHTML={{ __html: safeHtml }} />;\n}\n\nHtmlRenderer.propTypes = { content: PropTypes.string.isRequired };\n\nfunction ImageRenderer({ url, name, scale }) {\n const [error, setError] = useState(false);\n if (error) return <FallbackStage label=\"This image could not be displayed.\" />;\n return (\n <div className=\"dv-image-wrap\">\n <img\n className=\"dv-image\"\n src={url}\n alt={name}\n style={{ transform: `scale(${scale})` }}\n onError={() => setError(true)}\n />\n </div>\n );\n}\n\nImageRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n scale: PropTypes.number.isRequired,\n};\n\nfunction VideoRenderer({ url, name }) {\n const [error, setError] = useState(false);\n if (error) return <FallbackStage label=\"This video could not be played.\" />;\n return (\n <div className=\"dv-video-wrap\">\n <video\n className=\"dv-video\"\n src={url}\n title={name}\n controls\n preload=\"metadata\"\n controlsList=\"nodownload\"\n onError={() => setError(true)}\n />\n </div>\n );\n}\n\nVideoRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n};\n\nfunction FallbackStage({ label }) {\n return (\n <div className=\"dv-center dv-fallback\">\n <FileUnknownOutlined className=\"dv-fallback-icon\" />\n <p>{label}</p>\n <span className=\"dv-fallback-hint\">Use the download button to open it.</span>\n </div>\n );\n}\n\nFallbackStage.propTypes = { label: PropTypes.string.isRequired };\n\n/* ----------------------------- main component ---------------------------- */\n\n// ViewerBody holds the per-session state (active index + zoom). It lives inside\n// the Modal body, which is destroyed on close (destroyOnHidden), so it remounts\n// fresh on every open — no manual \"reset on open\" effects required.\nfunction ViewerBody({ docs, initialIndex, onClose }) {\n const total = docs.length;\n const safeInitial = Math.min(Math.max(initialIndex, 0), Math.max(0, total - 1));\n const [index, setIndex] = useState(safeInitial);\n const [scale, setScale] = useState(1);\n // Set when a PDF falls back to the browser's native viewer (see\n // PdfRenderer) — that viewer has its own zoom UI, so ours would sit there\n // doing nothing if left visible.\n const [pdfNativeFallback, setPdfNativeFallback] = useState(false);\n\n const current = docs[index];\n const kind = current ? kindOf(current) : 'unknown';\n const zoomable = (kind === 'pdf' && !pdfNativeFallback) || kind === 'image';\n\n // setActive changes the document and resets zoom/fallback in one event\n // handler, so we never have to reset them from an effect.\n const setActive = useCallback((next) => {\n setIndex(next);\n setScale(1);\n setPdfNativeFallback(false);\n }, []);\n\n const goPrev = useCallback(() => setActive(Math.max(0, index - 1)), [index, setActive]);\n const goNext = useCallback(\n () => setActive(Math.min(total - 1, index + 1)),\n [index, total, setActive],\n );\n\n // Keyboard navigation.\n useEffect(() => {\n const onKey = (e) => {\n // A focused <video> uses arrow keys to seek — don't switch documents.\n if (e.target?.tagName === 'VIDEO') return;\n if (e.key === 'ArrowLeft') goPrev();\n else if (e.key === 'ArrowRight') goNext();\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [goPrev, goNext]);\n\n const download = useCallback(async (doc) => {\n if (!doc) return;\n try {\n let blob;\n if (doc.content) {\n const mimeType = doc.type === 'html' ? 'text/html;charset=utf-8' : 'text/plain;charset=utf-8';\n blob = new Blob([doc.content], { type: mimeType });\n } else {\n const res = await fetch(doc.url);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n blob = await res.blob();\n }\n const objUrl = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = objUrl;\n a.download = doc.name || 'document';\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(objUrl);\n } catch {\n // Cross-origin without CORS: fall back to opening in a new tab.\n message.info('Opening file in a new tab…');\n if (doc.url) window.open(doc.url, '_blank', 'noopener,noreferrer');\n }\n }, []);\n\n function renderStage() {\n if (!current) {\n return <FallbackStage label=\"No document to preview.\" />;\n }\n switch (kind) {\n case 'pdf':\n return (\n <PdfRenderer\n key={current.key}\n url={current.url}\n scale={scale}\n onNativeFallback={() => setPdfNativeFallback(true)}\n />\n );\n case 'docx':\n return <DocxRenderer key={current.key} url={current.url} />;\n case 'image':\n return (\n <ImageRenderer key={current.key} url={current.url} name={current.name} scale={scale} />\n );\n case 'text':\n return <TextRenderer key={current.key} url={current.url} content={current.content} />;\n case 'html':\n return <HtmlRenderer key={current.key} content={current.content} />;\n case 'video':\n return <VideoRenderer key={current.key} url={current.url} name={current.name} />;\n default:\n return <FallbackStage label=\"Preview is not available for this file type.\" />;\n }\n }\n\n return (\n <div className=\"dv-root\">\n {/* toolbar */}\n <div className=\"dv-toolbar\">\n <div className=\"dv-title\" title={current?.name}>\n <span className=\"dv-title-text\">{current?.name || 'Document'}</span>\n {total > 1 && <span className=\"dv-counter\">{index + 1} / {total}</span>}\n </div>\n <div className=\"dv-actions\">\n {zoomable && (\n <>\n <Tooltip title=\"Zoom out\">\n <button\n type=\"button\"\n className=\"dv-icon-btn\"\n onClick={() => setScale((s) => Math.max(ZOOM_MIN, +(s - ZOOM_STEP).toFixed(2)))}\n disabled={scale <= ZOOM_MIN}\n >\n <ZoomOutOutlined />\n </button>\n </Tooltip>\n <span className=\"dv-zoom-label\">{Math.round(scale * 100)}%</span>\n <Tooltip title=\"Zoom in\">\n <button\n type=\"button\"\n className=\"dv-icon-btn\"\n onClick={() => setScale((s) => Math.min(ZOOM_MAX, +(s + ZOOM_STEP).toFixed(2)))}\n disabled={scale >= ZOOM_MAX}\n >\n <ZoomInOutlined />\n </button>\n </Tooltip>\n <span className=\"dv-divider\" />\n </>\n )}\n <Tooltip title=\"Download\">\n <button type=\"button\" className=\"dv-icon-btn dv-download\" onClick={() => download(current)}>\n <DownloadOutlined />\n </button>\n </Tooltip>\n <Tooltip title=\"Close\">\n <button type=\"button\" className=\"dv-icon-btn\" onClick={onClose}>\n <CloseOutlined />\n </button>\n </Tooltip>\n </div>\n </div>\n\n {/* stage + slider arrows */}\n <div className=\"dv-stage\">\n {total > 1 && (\n <button\n type=\"button\"\n className=\"dv-nav dv-nav--prev\"\n onClick={goPrev}\n disabled={index === 0}\n aria-label=\"Previous document\"\n >\n <LeftOutlined />\n </button>\n )}\n\n <div className=\"dv-canvas\">{renderStage()}</div>\n\n {total > 1 && (\n <button\n type=\"button\"\n className=\"dv-nav dv-nav--next\"\n onClick={goNext}\n disabled={index === total - 1}\n aria-label=\"Next document\"\n >\n <RightOutlined />\n </button>\n )}\n </div>\n\n {/* dots */}\n {total > 1 && (\n <div className=\"dv-dots\">\n {docs.map((d, i) => (\n <button\n key={d.key}\n type=\"button\"\n className={`dv-dot${i === index ? ' dv-dot--active' : ''}`}\n onClick={() => setActive(i)}\n aria-label={`Go to document ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nViewerBody.propTypes = {\n docs: PropTypes.arrayOf(PropTypes.object).isRequired,\n initialIndex: PropTypes.number.isRequired,\n onClose: PropTypes.func.isRequired,\n};\n\n// DocumentViewer is the public component: a Modal shell that mounts ViewerBody\n// only while open. `documents` may be URL strings or document objects.\n//\n// Pass `inline` to render ViewerBody directly with no Modal — used by the timesheet\n// manager review, which shows the uploaded screenshot side-by-side with the calendar\n// rather than in a popup. Everything ViewerBody already does (pdf/image/docx render,\n// zoom, download, multi-document navigation) comes along for free.\nexport default function DocumentViewer({ documents, open, onClose, initialIndex = 0, inline = false }) {\n const docs = useMemo(() => normalizeDocs(documents), [documents]);\n\n if (inline) {\n return (\n <div className=\"dv-root--inline\">\n <ViewerBody docs={docs} initialIndex={initialIndex} onClose={onClose ?? (() => {})} />\n </div>\n );\n }\n\n return (\n <Modal\n open={open}\n onCancel={onClose}\n footer={null}\n title={null}\n closable={false}\n centered\n width=\"min(1100px, 94vw)\"\n className=\"dv-modal\"\n styles={{ content: { padding: 0, overflow: 'hidden', borderRadius: 14 }, body: { padding: 0 } }}\n destroyOnHidden\n >\n <ViewerBody docs={docs} initialIndex={initialIndex} onClose={onClose} />\n </Modal>\n );\n}\n\nDocumentViewer.propTypes = {\n // URL strings, file objects, or inline objects ({ content, type: 'text'|'html' }).\n documents: PropTypes.arrayOf(\n PropTypes.oneOfType([PropTypes.string, PropTypes.object]),\n ).isRequired,\n // Required for the Modal shell; unused (and optional) when `inline` is set.\n open: PropTypes.bool,\n onClose: PropTypes.func,\n initialIndex: PropTypes.number,\n // Render the viewer body directly, with no Modal wrapper.\n inline: PropTypes.bool,\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 // 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\nexport async function searchModuleData(moduleName, searchParams = {}, pagination = {}, options = {}) {\n const { limit = 10, offset = 0 } = pagination;\n const { sort = '', sortDir = '', scope = '' } = options;\n const inferredScope = normalizeCandidateSearchSource(scope || searchParams.selectedSource);\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\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, JOBS_URL } from './apiConfig';\n\n// ── Form Groups ───────────────────────────────────────────────────────────────\n\nfunction extractFormGroups(json) {\n const payload = json?.data ?? json;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(payload?.groups)) return payload.groups;\n if (Array.isArray(json?.groups)) return json.groups;\n return [];\n}\n\nexport async function getFormGroups({ module, id, action, clientId, region, group } = {}) {\n if (!module) throw new Error('module is required to fetch form groups');\n const token = await ensureToken();\n const params = new URLSearchParams({ module });\n if (id) params.set('id', id);\n if (action) params.set('action', action);\n if (clientId) params.set('clientId', clientId);\n if (region) params.set('region', region);\n if (group) params.set('group', group);\n const res = await fetch(`${AUTH_URL}/admin/form-groups?${params.toString()}`, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);\n const json = await res.json();\n return extractFormGroups(json);\n}\n\nexport async function getCandidateFormGroups(options = {}) {\n return getFormGroups({ module: 'candidates', ...options });\n}\n\nexport async function getMentionUsers(search = '') {\n const { apiGetWithAuth } = await import('./authApi');\n const params = search ? `?search=${encodeURIComponent(search)}` : '';\n return apiGetWithAuth(JOBS_URL, `/users/any${params}`);\n}\n\n// ── Create Records ────────────────────────────────────────────────────────────\n// Routes through the Auth gateway which injects tenant context from JWT\n// and proxies to the correct downstream service based on module name.\n\nexport async function createModuleRecord(moduleName, formData) {\n // ensureToken (not getStoredToken) so a missing/expired session throws a\n // clear \"Authentication required\" error instead of silently sending the\n // request with no Authorization header (which the gateway rejects as 401).\n // NOTE: do NOT set Content-Type here — the browser must add the multipart\n // boundary itself. Only the Authorization header is set manually.\n const token = await ensureToken();\n\n const res = await fetch(\n `${AUTH_URL}/module/create?module=${encodeURIComponent(moduleName)}`,\n { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData },\n );\n\n const { logout } = await import('./authApi');\n if (res.status === 401) logout();\n\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('application/json') ? await res.json() : await res.text();\n\n if (!res.ok) {\n const error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\n error.status = res.status;\n error.data = data;\n throw error;\n }\n\n return data;\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 const res = await fetch(`${AUTH_URL}/documents/upload?${params.toString()}`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\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 error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\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","import { ensureToken, fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL, JOBS_URL, CANDIDATES_URL, SUBMISSIONS_URL } from './apiConfig';\n\n// ── Module Detail & Record Edit ───────────────────────────────────────────────\n\n// fetchUrl — direct downstream (read-only, no tenant injection needed)\n// updateUrl is no longer used; updates go through the Auth gateway.\nconst MODULE_EDIT_CONFIG = {\n job: {\n fetchUrl: (id) => `${JOBS_URL}/edit/detailed-view/${encodeURIComponent(id)}`,\n },\n candidate: {\n fetchUrl: (id) => `${CANDIDATES_URL}/edit/detailed-view?_id=${encodeURIComponent(id)}`,\n },\n candidates: {\n fetchUrl: (id) => `${CANDIDATES_URL}/edit/detailed-view?_id=${encodeURIComponent(id)}`,\n },\n submission: {\n fetchUrl: (id) => `${SUBMISSIONS_URL}/edit/detailed-view?_id=${encodeURIComponent(id)}`,\n },\n};\n\nexport async function getModuleDataDetail(module, id) {\n const normalizedModule = String(module ?? '').trim().toLowerCase();\n\n if (normalizedModule === 'job' || normalizedModule === 'jobs') {\n const { getJobDetailView } = await import('./jobsApi');\n return getJobDetailView(id);\n }\n\n if (normalizedModule === 'candidate' || normalizedModule === 'candidates') {\n return getRecordForEdit(normalizedModule === 'candidate' ? 'candidate' : 'candidates', id);\n }\n\n const params = new URLSearchParams({ module: String(module ?? ''), id: String(id) });\n return fetchJsonWithAuth(AUTH_URL, `/module-data-detail?${params.toString()}`);\n}\n\nexport async function getModuleActions(module) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/module-actions?module=${encodeURIComponent(module)}`,\n );\n const data = json?.data ?? json ?? {};\n return {\n actions: Array.isArray(data.actions) ? data.actions : [],\n actionRules: Array.isArray(data.actionRules) ? data.actionRules : [],\n };\n}\n\nexport async function getRecordForEdit(module, id) {\n if (!module) throw new Error('module is required');\n if (!id) throw new Error('id is required');\n\n const config = MODULE_EDIT_CONFIG[module];\n if (!config) throw new Error(`No edit config for module: ${module}`);\n\n const token = await ensureToken();\n const res = await fetch(config.fetchUrl(id), {\n method: 'GET',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);\n const json = await res.json();\n return json.data ?? json;\n}\n\nexport async function updateRecord(module, id, payload, fileParts = []) {\n if (!module) throw new Error('module is required');\n if (!id) throw new Error('id is required');\n\n const token = await ensureToken();\n const formData = new FormData();\n formData.append('json', JSON.stringify(payload));\n // New file uploads ride along in the SAME multipart, under the part name the\n // downstream update handler reads (jobs → \"file\", candidates → resume/passport/\n // documents/…). The gateway forwards them and the downstream stores + returns\n // their name/location. Existing files (no originFileObj) are not re-sent.\n (fileParts ?? []).forEach(({ formKey, file }) => formData.append(formKey, file));\n\n const res = await fetch(\n `${AUTH_URL}/module/update/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`,\n { method: 'PUT', headers: { Authorization: `Bearer ${token}` }, body: formData },\n );\n\n if (!res.ok) {\n const errorText = await res.text();\n console.error('[updateRecord] Error response:', errorText);\n const error = new Error(serverErrorMessage(errorText) || `API ${res.status}: ${res.statusText}`);\n // The MESSAGE stays exactly what it was, but the structured body rides along\n // now. A duplicate-value 409 carries `errors[]` — including the group and row\n // index of a repeatable-group field — and flattening it to a string was\n // throwing that away, leaving the form to guess the field from the message\n // text and with no way at all to know WHICH ROW was rejected.\n error.status = res.status;\n error.response = parseErrorBody(errorText);\n throw error;\n }\n\n const json = await res.json();\n return json.data ?? json;\n}\n\n// parseErrorBody returns the parsed JSON envelope, or null for a non-JSON body\n// (a proxy/gateway HTML error page). Never throws.\nfunction parseErrorBody(body) {\n const text = String(body ?? '').trim();\n if (!text.startsWith('{')) return null;\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\n// The gateway reports failures as {status:false, error:\"…\"} (utils.ERROR). A\n// rejected write is often something the user can act on — \"amount received\n// exceeds the remaining balance\" — and submitErrorMessage surfaces this text\n// verbatim in the toast, so hand it the message rather than the JSON envelope.\n// Non-JSON bodies (proxy/gateway HTML) fall through unchanged.\nfunction serverErrorMessage(body) {\n const text = String(body ?? '').trim();\n if (!text.startsWith('{')) return text;\n try {\n const parsed = JSON.parse(text);\n const message = parsed?.error ?? parsed?.message;\n return typeof message === 'string' && message.trim() ? message.trim() : text;\n } catch {\n return text;\n }\n}\n\n// ── Activity & Notes ──────────────────────────────────────────────────────────\n\nconst unwrap = (json) => json?.data ?? json;\n\nexport async function getActivity(module, id) {\n if (!module || !id) return [];\n const params = new URLSearchParams({ module, id: String(id) });\n const json = await fetchJsonWithAuth(AUTH_URL, `/module-activity?${params.toString()}`);\n const data = unwrap(json);\n return Array.isArray(data) ? data : [];\n}\n\nexport async function getNotes(relatedId) {\n if (!relatedId) return [];\n const json = await fetchJsonWithAuth(AUTH_URL, `/notes?relatedId=${encodeURIComponent(relatedId)}`);\n const data = unwrap(json);\n return Array.isArray(data) ? data : [];\n}\n\nexport async function createNote({ relatedId, notes, title, notesFor }) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/notes', {\n method: 'POST',\n body: JSON.stringify({ relatedId, notes, title, notesFor }),\n });\n return unwrap(json);\n}\n\nexport async function uploadJobAttachment(recordId, file) {\n const { ensureToken } = await import('./authApi');\n const token = await ensureToken();\n\n const formData = new FormData();\n formData.append('file', file);\n formData.append('jobId', recordId);\n\n const res = await fetch(`${JOBS_URL}/jobs/${recordId}/attachment`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n\n if (!res.ok) {\n const errorText = await res.text();\n throw new Error(errorText || `API ${res.status}: ${res.statusText}`);\n }\n\n const json = await res.json();\n return json.data ?? json;\n}\n","// Runtime gate consulted by AddFormV1/EditFormV1 so a role's Form\n// Configuration (Role Configure → Form) actually restricts the real add/edit\n// form, not just the admin preview. rolePerms is the array returned by\n// GET /admin/role-form-permissions (see adminApi.js getRoleFormPermissions):\n// [{ name, enabled, locked, fields: [{ field, enabled, locked }] }].\n//\n// Fails OPEN (returns true) whenever rolePerms is null/empty/not-yet-loaded —\n// a module or role with no derived permissions behaves exactly as before\n// (gated only by Form Groups' own show/visiblePermission), so this is purely\n// additive and cannot hide a field that used to render.\nexport function groupAllowedByRole(rolePerms, groupName) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n return group.enabled !== false;\n}\n\nexport function fieldAllowedByRole(rolePerms, groupName, fieldKey) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n const field = group.fields?.find((f) => f.field === fieldKey);\n if (!field) return true;\n return field.enabled !== false;\n}\n\n// Editability gate — the \"disable\" side of Role Configure → Form. A group or\n// field marked editable:false is still SHOWN but rendered read-only. Fails\n// OPEN (returns true = editable) when nothing is configured, so a role/module\n// with no derived permissions stays fully editable, exactly as before.\nexport function groupEditableByRole(rolePerms, groupName) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n return group.editable !== false;\n}\n\nexport function fieldEditableByRole(rolePerms, groupName, fieldKey) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n if (group.editable === false) return false; // group disabled → every field read-only\n const field = group.fields?.find((f) => f.field === fieldKey);\n if (!field) return true;\n return field.editable !== false;\n}\n","import 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\nconst ATTACK_PATTERNS = [\n { re: /<\\s*\\/?\\s*(?:script|iframe|object|embed|svg|img|form|input|style|link|meta)\\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: /\\bunion\\s+(?:all\\s+)?select\\b|\\b(?:drop\\s+table|delete\\s+from|insert\\s+into|xp_cmdshell)\\b/i, message: 'Database command patterns are not allowed' },\n { re: /(?:['\"]\\s*)?\\b(?:or|and)\\s+\\d+\\s*=\\s*\\d+/i, message: 'Injection patterns are not allowed' },\n { re: /[\"']?\\$(?:where|gt|gte|lt|lte|ne|regex|or|and|expr|function)\\b/i, message: 'MongoDB operators are not allowed in input values' },\n { re: /(?:\\.\\.[/\\\\])|(?:%2e|%2f|%5c)/i, message: 'Path traversal patterns are not allowed' },\n { re: /&&|\\|\\||\\$\\(|\\$\\{|`|\\b(?:rm\\s+-rf|cat\\s+\\/etc\\/|whoami\\b|curl\\s+https?:\\/\\/|wget\\s+https?:\\/\\/)/i, message: 'Command execution patterns are not allowed' },\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 (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 if (field.type !== 'text-editor') {\n const attack = ATTACK_PATTERNS.find(({ re }) => re.test(canonical));\n if (attack) return `${label}: ${attack.message}`;\n } else {\n const dangerousRichText = ATTACK_PATTERNS.slice(0, 3).find(({ re }) => re.test(canonical));\n if (dangerousRichText) return `${label}: ${dangerousRichText.message}`;\n }\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","// 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","/**\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 { snapToOption } from './optionMatching';\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 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 '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 */\nexport function shapeFieldValue(field, rawValue) {\n if (field.payloadMode === 'skip') return { kind: 'skip' };\n\n let raw = rawValue;\n if (isEmpty(raw) && field.defaultValue !== undefined && field.defaultValue !== '') {\n raw = field.defaultValue;\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 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 // 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 const fallbackEditDefault = () =>\n field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== ''\n ? field.defaultValue\n : 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) && field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== '') {\n return field.defaultValue;\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 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","// linkedAddRowGroups.js\n//\n// Pure helpers for group.linkGroup — an opt-in mechanism that keeps two or\n// more addRow groups' rows synchronized (same add/remove, same row count)\n// while each group keeps rendering as its own Card and submitting its own\n// independent payload array (buildPayload / buildAddRowInitial are untouched\n// and stay fully per-group — see payloadTransformer.js / applyGroupValues.js).\n//\n// A group only participates when BOTH group.addRow is true AND group.linkGroup\n// is a non-empty string shared by at least one other group. Every group that\n// predates this field (i.e. every group in production today) has no\n// linkGroup, so these helpers are a strict no-op for it.\n\nconst truthy = (value) => value === true || value === 1 || value === '1';\n\n// computeLinkedSets(groups) -> Map<linkGroupKey, { leaderName, memberNames: string[] }>\n//\n// `groups` is expected already order-sorted (normalizeGroups sorts by `order`\n// before render), so the first member encountered per key is the leader —\n// Array.prototype.sort is stable, so this matches the existing order\n// convention with no extra tie-break logic needed. A \"set\" of size 1 (nothing\n// else shares that linkGroup value) is dropped — a lone linkGroup value isn't\n// meaningfully linked to anything, and the group renders as if unlinked.\nexport function computeLinkedSets(groups = []) {\n const byKey = new Map();\n groups.forEach((group) => {\n const key = group?.linkGroup;\n if (!truthy(group?.addRow) || !key) return;\n if (!byKey.has(key)) byKey.set(key, []);\n byKey.get(key).push(group.name);\n });\n\n const sets = new Map();\n byKey.forEach((memberNames, key) => {\n if (memberNames.length < 2) return;\n sets.set(key, { leaderName: memberNames[0], memberNames });\n });\n return sets;\n}\n\n// findLinkedSet(linkedSets, groupName) -> { leaderName, memberNames } | null\n// Cheap membership lookup against an already-computed sets Map (compute once\n// per render via computeLinkedSets, look up per group here — avoids\n// recomputing the whole map on every group).\nexport function findLinkedSet(linkedSets, groupName) {\n for (const set of linkedSets.values()) {\n if (set.memberNames.includes(groupName)) return set;\n }\n return null;\n}\n\n// padLinkedInitialValues(linkedSets, initialValuesByGroupName) -> a new\n// { [groupName]: rows[] } object where every member of a linked set is padded\n// with {} placeholder rows up to the set's max length. Guards against\n// pre-existing data where two linked groups' stored `rows` happen to differ\n// in length (undefined territory otherwise — buildAddRowInitial/\n// getAddRowInitialValue compute each group's rows fully independently). A\n// no-op passthrough for any group not in a real (>=2 member) linked set.\nexport function padLinkedInitialValues(linkedSets, initialValuesByGroupName = {}) {\n const next = { ...initialValuesByGroupName };\n linkedSets.forEach(({ memberNames }) => {\n const maxLen = Math.max(...memberNames.map((name) => (next[name] ?? []).length));\n memberNames.forEach((name) => {\n const rows = next[name] ?? [];\n if (rows.length < maxLen) {\n next[name] = [...rows, ...Array.from({ length: maxLen - rows.length }, () => ({}))];\n }\n });\n });\n return next;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// formDecisionDialog — the popups the Add form asks questions through.\n//\n// WHAT WAS WRONG WITH THE OLD ONES\n// They were bare Modal.confirm calls: a title, one run-on sentence, \"OK\" and\n// \"Cancel\". Three problems, all of them the same problem — the user could not\n// see what they were deciding about:\n//\n// • the FACTS were buried in prose (\"…— Asha · Rao — last updated about 2\n// months ago. Continue to update…\"), so the two things that actually decide\n// the answer (who, how stale) had to be read out of a paragraph;\n// • \"OK\" names no outcome, and neither choice here is obviously the default;\n// • nothing distinguished \"we found a possible duplicate\" from \"this will\n// overwrite your work\" — different stakes, identical dialog.\n//\n// WHAT THIS DOES INSTEAD\n// A tone-coloured icon states the kind of decision at a glance; the heading is\n// clearly heavier than the body; the facts sit in a scannable card ABOVE the\n// prose; and the buttons name their outcomes. The tone drives colour only —\n// never meaning on its own, so it still reads correctly in monochrome.\n//\n// Imperative on purpose: it is called from hooks and event handlers that need\n// to await an answer, so it returns a Promise<boolean> exactly like\n// Modal.confirm did, and every existing call site keeps its shape.\n// ─────────────────────────────────────────────────────────────────────────\nimport { Modal } from 'antd';\nimport {\n ExclamationCircleFilled,\n InfoCircleFilled,\n FileTextOutlined,\n UserOutlined,\n} from '@ant-design/icons';\nimport './formDecisionDialog.css';\n\nconst TONES = {\n // \"We think these are the same person\" — a judgement, not a failure.\n duplicate: { className: 'fdd-tone-amber', Icon: UserOutlined },\n // \"This will replace what you typed\" — a real risk to work already done.\n overwrite: { className: 'fdd-tone-amber', Icon: ExclamationCircleFilled },\n // \"Shall I tidy this for you?\" — no stakes at all.\n suggestion: { className: 'fdd-tone-blue', Icon: InfoCircleFilled },\n file: { className: 'fdd-tone-blue', Icon: FileTextOutlined },\n};\n\n/**\n * openFormDecision — ask a question and resolve to the user's answer.\n *\n * @param {object} opts\n * @param {string} opts.tone duplicate | overwrite | suggestion | file\n * @param {string} opts.title the heading — say the SITUATION, not \"Are you sure?\"\n * @param {string} opts.body one or two sentences of context\n * @param {Array} [opts.facts] [{ label, value }] — shown as a scannable card\n * @param {string} opts.okText names the outcome, never \"OK\"\n * @param {string} opts.cancelText\n * @param {boolean} [opts.danger] style the primary action as destructive\n * @param {string} [opts.footnote] a quiet line under the buttons\n * @returns {Promise<boolean>}\n */\nexport function openFormDecision({\n tone = 'suggestion',\n title,\n body,\n facts = [],\n okText = 'Continue',\n cancelText = 'Cancel',\n danger = false,\n footnote,\n} = {}) {\n const { className, Icon } = TONES[tone] ?? TONES.suggestion;\n const usableFacts = facts.filter((f) => f && f.value !== undefined && f.value !== null && String(f.value).trim() !== '');\n\n return new Promise((resolve) => {\n Modal.confirm({\n // antd's own icon is suppressed: this dialog renders its own, sized and\n // coloured with the heading rather than floating beside the body.\n icon: null,\n centered: true,\n width: 480,\n className: `fdd-modal ${className}`,\n okText,\n cancelText,\n okButtonProps: { danger, size: 'large' },\n cancelButtonProps: { size: 'large' },\n content: (\n <div className=\"fdd\">\n <div className=\"fdd-head\">\n <span className=\"fdd-icon\" aria-hidden=\"true\"><Icon /></span>\n <h3 className=\"fdd-title\">{title}</h3>\n </div>\n\n {/* The facts come FIRST and are scannable. This is the part that\n actually answers \"is this the same person?\" — reading it out of a\n sentence is work the reader should not have to do. */}\n {usableFacts.length > 0 && (\n <dl className=\"fdd-facts\">\n {usableFacts.map((f) => (\n <div className=\"fdd-fact\" key={f.label}>\n <dt>{f.label}</dt>\n <dd>{f.value}</dd>\n </div>\n ))}\n </dl>\n )}\n\n {body && <p className=\"fdd-body\">{body}</p>}\n {footnote && <p className=\"fdd-footnote\">{footnote}</p>}\n </div>\n ),\n onOk: () => resolve(true),\n onCancel: () => resolve(false),\n });\n });\n}\n\n/**\n * openFormNotice — a one-way message (the module refuses to continue).\n * Same shell, single action, so a block and a choice look like relatives\n * rather than two unrelated dialogs.\n */\nexport function openFormNotice({ tone = 'duplicate', title, body, facts = [], okText = 'Go back' } = {}) {\n const { className, Icon } = TONES[tone] ?? TONES.suggestion;\n const usableFacts = facts.filter((f) => f && String(f.value ?? '').trim() !== '');\n\n return new Promise((resolve) => {\n Modal.warning({\n icon: null,\n centered: true,\n width: 480,\n className: `fdd-modal ${className}`,\n okText,\n okButtonProps: { size: 'large' },\n content: (\n <div className=\"fdd\">\n <div className=\"fdd-head\">\n <span className=\"fdd-icon\" aria-hidden=\"true\"><Icon /></span>\n <h3 className=\"fdd-title\">{title}</h3>\n </div>\n {usableFacts.length > 0 && (\n <dl className=\"fdd-facts\">\n {usableFacts.map((f) => (\n <div className=\"fdd-fact\" key={f.label}>\n <dt>{f.label}</dt>\n <dd>{f.value}</dd>\n </div>\n ))}\n </dl>\n )}\n {body && <p className=\"fdd-body\">{body}</p>}\n </div>\n ),\n onOk: () => resolve(true),\n });\n });\n}\n","import { ensureToken } from './authApi';\nimport { AUTH_URL } from './apiConfig';\n\n// ── AI Actions ──────────────────────────────────────────────────────────────\n// Generic, config-driven \"call an AI/automation service and get a JSON\n// response back\" call. The gateway resolves WHICH service/endpoint to hit\n// entirely from admin config (module/group/field/action) — this file never\n// carries a service URL, module name, or field name as a hardcoded value.\n\n/**\n * @param {Object} args\n * @param {string} args.module\n * @param {string} args.group - FormGroup.name the field lives in\n * @param {string} args.field - FormGroupField.field the action is attached to\n * @param {string} args.actionKey - AiActionConfig.key to run\n * @param {Object} [args.inputs] - { [param]: textValue } for this action's text inputs\n * @param {Object} [args.files] - { [param]: File } for this action's file inputs\n * @returns {Promise<{ raw: any, actionKey: string, responseMappings: any[] }>}\n */\nexport async function runAiAction({ module, group, field, actionKey, inputs = {}, files = {} }) {\n if (!module || !group || !field || !actionKey) {\n throw new Error('module, group, field and actionKey are required to run an AI action');\n }\n const token = await ensureToken();\n\n const formData = new FormData();\n formData.append('json', JSON.stringify({ inputs }));\n Object.entries(files).forEach(([param, file]) => {\n if (file) formData.append(param, file);\n });\n\n const params = new URLSearchParams({ module, group, field, action: actionKey });\n const res = await fetch(`${AUTH_URL}/ai-action?${params.toString()}`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n\n const { logout } = await import('./authApi');\n if (res.status === 401) logout();\n\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('application/json') ? await res.json() : await res.text();\n\n if (!res.ok) {\n const error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\n error.status = res.status;\n error.data = data;\n throw error;\n }\n\n return data?.data ?? data;\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\nexport function buildFileUrl(location) {\n if (!location) return '';\n const loc = String(location);\n if (/^https?:\\/\\//i.test(loc)) return loc;\n const base = getDefaults().s3BaseUrl || '';\n return base ? `${base}/${loc.replace(/^\\/+/, '')}` : '';\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({ location, name: fileNameFromPath(item) || item, url: buildFileUrl(location) });\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 rebuilding from the\n // S3 base, so private-bucket files open correctly.\n const presigned = item.fileUrl || item.downloadUrl || item.signedUrl || '';\n out.push({ location, name: docDisplayName(item), url: presigned || buildFileUrl(location) });\n }\n }\n return out;\n}\n","// applyGroupValues — the SINGLE place that turns a `groups` array (each field\n// carrying `.value`, each addRow group carrying `.rows`) into antd form state.\n//\n// This is the exact mechanism Edit-prefill has always used (getFormGroups with\n// an `id` embeds field.value/group.rows server-side via AttachFieldValues).\n// It's extracted here so any OTHER source of the same shape — notably an AI\n// Action's response, which the gateway resolves through the identical\n// AttachFieldValues engine — can be applied to the form with the same code,\n// instead of maintaining a second, parallel field-mapping implementation.\nimport { buildInitialValue, buildRepeatFieldInitial, getDeep, rowDefaultsFor } from './payloadTransformer';\nimport { splitDialCode, normalizeDialCode } from '../detail/phoneDisplay';\nimport { snapToOption } from './optionMatching';\n\n// truthy tolerates the boolean-ish shapes a config flag can arrive in.\nconst isTruthyFlag = (v) => v === true || v === 1 || v === '1';\nimport { buildFileUrl } from '../detail/renderConfig';\n\nconst empty = (v) => v === undefined || v === null || v === '';\n\nexport function namePathFromString(path) {\n return String(path ?? '').split('.').map((part) => part.trim()).filter(Boolean);\n}\n\nexport function writeFormValue(target, path, value) {\n const parts = Array.isArray(path) ? path : namePathFromString(path);\n if (!parts.length) return target;\n let cursor = target;\n for (let i = 0; i < parts.length - 1; i += 1) {\n const key = parts[i];\n if (!cursor[key] || typeof cursor[key] !== 'object' || Array.isArray(cursor[key])) cursor[key] = {};\n cursor = cursor[key];\n }\n cursor[parts[parts.length - 1]] = value;\n return target;\n}\n\nfunction firstConfiguredValue(source, keys = []) {\n if (!source || typeof source !== 'object') return undefined;\n for (const key of keys) {\n if (!key) continue;\n const val = getDeep(source, key);\n if (!empty(val)) return val;\n }\n return undefined;\n}\n\nfunction firstMatchingValue(source, pattern) {\n if (!source || typeof source !== 'object') return undefined;\n const entries = Object.entries(source);\n const direct = entries.find(([key, val]) => pattern.test(key) && !empty(val));\n if (direct) return direct[1];\n for (const [, val] of entries) {\n if (val && typeof val === 'object' && !Array.isArray(val)) {\n const nested = firstMatchingValue(val, pattern);\n if (!empty(nested)) return nested;\n }\n }\n return undefined;\n}\n\n// fileListFromValue — turn a file field's embedded value (from getFormGroups,\n// or an AI response resolved the same way) into the antd fileList the file\n// control expects. The value may be a full document object ({ location,\n// uploadName/name, uniqueName }), a bare original filename string, or an\n// array of either. Returns undefined when empty.\nexport function fileListFromValue(stored, field) {\n const list = Array.isArray(stored) ? stored : (stored ? [stored] : []);\n if (!list.length) return undefined;\n const mapped = list.map((f, i) => {\n if (typeof f === 'string') {\n return { uid: `${field.field}-${i}`, name: f, status: 'done' };\n }\n if (!f || typeof f !== 'object') return null;\n const rawUrl = firstConfiguredValue(f, [field.fileUrlKey, field.locationKey])\n ?? firstMatchingValue(f, /(location|url|path)$/i)\n ?? '';\n const fileName = firstConfiguredValue(f, [field.fileNameKey])\n ?? firstMatchingValue(f, /(uploadName|uploadedFileName|fileName|name)$/i)\n ?? '';\n // A stored object with no usable url AND no real filename carries no actual\n // file (e.g. an all-null document sub-object from a parsed AI response) —\n // skip it rather than showing a bogus \"file\" chip with nothing behind it.\n if (!rawUrl && !fileName) return null;\n const fullUrl = rawUrl && !String(rawUrl).startsWith('http')\n ? buildFileUrl(rawUrl)\n : rawUrl;\n return {\n uid: String(f.id ?? f._id ?? f.uniqueName ?? f.documentCopyUniqueName ?? `${field.field}-${i}`),\n name: fileName || 'file',\n status: 'done',\n existing: true,\n url: fullUrl || undefined,\n // The ORIGINAL stored container, kept so the payload engine can write an\n // untouched file straight back (see carriedFileValue) instead of the\n // record silently losing a document it displayed.\n stored: f,\n };\n }).filter(Boolean);\n return mapped.length ? mapped : undefined;\n}\n\n// buildRowFileList — turn an addRow row's stored file metadata into the antd\n// fileList. Returns undefined when the row has no file.\nexport function buildRowFileList(row, field) {\n if (!row || typeof row !== 'object') return undefined;\n const name = firstConfiguredValue(row, [field.fileNameKey, field.field])\n ?? firstMatchingValue(row, /(uploadName|uploadedFileName|fileName|name)$/i);\n const rawUrl = firstConfiguredValue(row, [field.fileUrlKey, field.locationKey])\n ?? firstMatchingValue(row, /(location|url|path)$/i)\n ?? '';\n if (!name && !rawUrl) return undefined;\n const fullUrl = rawUrl && !String(rawUrl).startsWith('http')\n ? buildFileUrl(rawUrl)\n : rawUrl;\n const uid = row.documentCopyUniqueName ?? row.uniqueName ?? name ?? `${field.field}-0`;\n return [{\n uid: String(uid),\n name: name ?? 'file',\n status: 'done',\n existing: true,\n url: fullUrl || undefined,\n }];\n}\n\n// seedVerifyCarriers — carry a row's VERIFY BOOKKEEPING keys into the form even\n// when they are not declared as fields of the group.\n//\n// Why this exists: mapRow below prefills strictly from `group.fields`, which is\n// correct for anything the user can see or edit. But a verify-enabled field\n// names its bookkeeping keys in its OWN config — verifyResultField (\"isValid\"),\n// verifyTimestampField (\"validatedAt\") and the lock keys (\"isAccountCreate\") —\n// and those are only prefilled if someone ALSO remembered to declare each of\n// them as a separate hidden field on the group.\n//\n// That coupling is invisible and environment-specific: the row itself carries\n// isValid/isAccountCreate in every environment, but a config that omits the\n// hidden field declarations silently drops them at prefill. The symptom is a\n// recruiter whose email really is validated (and whose login really exists)\n// still rendering a \"Verify Email\" button, because the button watches a value\n// that never made it into the form — while Name/Email/Contact prefill fine and\n// make the row look perfectly healthy.\n//\n// So: the field that DECLARES a verify key is the authority for prefilling it.\n// Only keys already present on the stored row are copied, nothing is invented,\n// and an existing configured value is never overwritten.\nfunction seedVerifyCarriers(group, row, rowVals, readScalar) {\n (group.fields ?? []).forEach((field) => {\n if (!field?.verifyAction) return;\n const keys = [\n field.verifyResultField,\n field.verifyTimestampField,\n ...String(field.verifyLockedWhenFields ?? '').split(/[,\\s]+/),\n ].map((key) => String(key ?? '').trim()).filter(Boolean);\n\n keys.forEach((key) => {\n if (getDeep(rowVals, key) !== undefined) return; // already prefilled as a real field\n const stored = readScalar(row, { field: key });\n if (stored === undefined || stored === null) return;\n writeFormValue(rowVals, key, stored);\n });\n });\n}\n\n// buildAddRowInitial — turn a repeatable (addRow) group's backend-embedded\n// stored rows (group.rows) into the Form.List initialValue shape: an array\n// with ONE object per stored row, each mapped through buildInitialValue (file\n// fields via the row's sibling keys). Returns [] when the group has no stored\n// rows, padded to group.minRows.\nexport function buildAddRowInitial(group) {\n const storedRows = Array.isArray(group.rows) ? group.rows : null;\n const minRows = Math.max(0, Number(group.minRows ?? 0) || 0);\n const mapRow = (row, readFile, readScalar) => {\n const rowVals = {};\n (group.fields ?? []).forEach((field) => {\n if (!field.field) return;\n if (field.type === 'file') {\n const fileList = readFile(row, field);\n if (fileList) writeFormValue(rowVals, field.field, fileList);\n return;\n }\n const stored = readScalar(row, field);\n if (stored === undefined || stored === null) {\n // Row has no stored value — an admin defaultOnEdit default still shows\n // (e.g. VMS Commission 5.5 on rows saved before the field existed).\n if (field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== '') {\n writeFormValue(rowVals, field.field, field.defaultValue);\n }\n return;\n }\n // A repeat field inside the row (nested Form.List) hydrates as an array,\n // each element normalised for its control; a plain field as a single value.\n writeFormValue(rowVals, field.field, isTruthyFlag(field.addRow)\n ? buildRepeatFieldInitial(field, stored)\n : buildInitialValue(field, stored));\n });\n seedVerifyCarriers(group, row, rowVals, readScalar);\n return rowVals;\n };\n\n const padSeed = rowDefaultsFor(group, { editing: true });\n if (storedRows && storedRows.length > 0) {\n const rows = storedRows.map((row) => mapRow(\n row,\n (r, field) => buildRowFileList(r, field),\n (r, field) => getDeep(r, field.field),\n ));\n while (rows.length < minRows) rows.push({ ...padSeed });\n return rows;\n }\n\n // Fallback for an older backend that embeds only per-field field.value (row 0).\n const rowVals = mapRow(\n group,\n (_g, field) => fileListFromValue(field.value, field),\n (_g, field) => field.value,\n );\n const rows = Object.keys(rowVals).length > 0 ? [rowVals] : [];\n while (rows.length < minRows) rows.push({ ...padSeed });\n return rows;\n}\n\n// applyScalarFieldValues — walk every NON-addRow group's fields, read each\n// field's embedded `.value`, shape it (buildInitialValue / fileListFromValue /\n// checkbox-transformRule reversal), and set it on the form in one batch.\n// AddRow groups are skipped — their rows only apply correctly as a Form.List\n// `initialValue` at mount (or via an explicit remount for a post-mount\n// update), never via setFieldsValue.\nexport function applyScalarFieldValues(form, groups = []) {\n const values = {};\n\n groups.forEach((group) => {\n if (group.toggleEnabled && group.toggleField) {\n writeFormValue(values, group.toggleField, Boolean(group.toggleValue));\n }\n if (group.addRow) return;\n\n (group.fields ?? []).forEach((field) => {\n if (!field.field) return;\n // A repeatable (addRow) field prefills via its own Form.List initialValue\n // at mount (buildRepeatFieldInitial) — setFieldsValue on a Form.List that\n // mounted empty only keeps the last item, so skip it here.\n if (field.addRow) return;\n const stored = field.value;\n const name = field.field;\n\n if (field.type === 'file') {\n const fileList = fileListFromValue(stored, field);\n if (fileList) writeFormValue(values, name, fileList);\n return;\n }\n\n if (stored === undefined || stored === null) return;\n\n if (field.type === 'checkbox' && Array.isArray(field.options) && field.options.length > 0) {\n const map = field.transformRule?.map;\n if (map) {\n const match = Object.entries(map).find(([, to]) => String(to) === String(stored));\n writeFormValue(values, name, match ? [match[0]] : []);\n } else {\n // Checkbox groups bypass buildInitialValue, so they snap here — a\n // parsed \"high\"/\"HIGH\" must still tick a \"High\" box.\n writeFormValue(values, name, snapToOption(field, Array.isArray(stored) ? stored : [stored]));\n }\n return;\n }\n\n // ── dial code split ──────────────────────────────────────────────────\n // A parsed contact number arrives as one string that often carries its\n // country code (\"+91 99887 76655\"). Written whole into the number field,\n // the code is stripped by the phone formatter and the separate code field\n // keeps whatever default it had — which is how every parsed candidate\n // ended up as \"+1\" regardless of the résumé.\n //\n // Config, not a field name: `splitDialCodeInto` names the sibling that\n // should receive the code. Reuses the SAME splitter the detail view uses\n // (components/detail/phoneDisplay.js), so form and detail agree on what a\n // dial code is.\n const codeTarget = field.splitDialCodeInto;\n if (codeTarget && typeof stored === 'string') {\n const { code, rest } = splitDialCode(stored);\n const normalized = normalizeDialCode(code);\n // A parse that carried NO code leaves the code field untouched rather\n // than blanking or defaulting it — inventing a country is the bug.\n if (normalized) writeFormValue(values, codeTarget, normalized);\n writeFormValue(values, name, buildInitialValue(field, rest || stored));\n return;\n }\n\n writeFormValue(values, name, buildInitialValue(field, stored));\n });\n });\n\n if (Object.keys(values).length > 0) form.setFieldsValue(values);\n return values;\n}\n","import { useCallback, useState } from 'react';\nimport { Form, message } from 'antd';\nimport { openFormDecision } from './formDecisionDialog';\nimport { runAiAction } from '../../services/aiActionApi';\nimport { applyScalarFieldValues, buildAddRowInitial } from './applyGroupValues';\n\nfunction stripHtml(html) {\n const div = document.createElement('div');\n div.innerHTML = String(html ?? '');\n return div.textContent || div.innerText || '';\n}\n\nfunction applyTransform(value, transform) {\n if (!transform) return value;\n if (transform === 'stripHtml') return stripHtml(value);\n return value;\n}\n\nfunction wordCount(text) {\n return String(text ?? '').trim().split(/\\s+/).filter(Boolean).length;\n}\n\nfunction rowHasData(row) {\n return Object.values(row || {}).some((v) => v !== undefined && v !== null && v !== '');\n}\n\n// isInputSatisfied — an input \"counts\" once it has real content: a file\n// present for kind \"file\", or non-empty (post-transform) text for kind\n// \"text\" that also meets the input's own MinWords, if configured.\nfunction isInputSatisfied(input, rawValue) {\n if (input.kind === 'file') {\n return Array.isArray(rawValue) ? rawValue.length > 0 : Boolean(rawValue);\n }\n const text = applyTransform(rawValue, input.transform);\n const str = String(text ?? '').trim();\n if (!str) return false;\n const minWords = Number(input.minWords) || 0;\n return minWords > 0 ? wordCount(str) >= minWords : true;\n}\n\n// computeActionEnabled — an action is disabled until every GATED input is\n// satisfied. An input gates the button when it's marked Required, or has a\n// MinWords requirement (>0) — a plain optional input never gates. Inputs\n// sharing a Group (e.g. \"either a JD file or JD text\") gate as one unit: the\n// group is satisfied once ANY member of it is satisfied.\nfunction computeActionEnabled(action, getValue) {\n const inputs = action.inputs || [];\n if (inputs.length === 0) return true;\n\n const groupsMap = new Map();\n inputs.forEach((input, i) => {\n const key = input.group || `__solo_${i}`;\n if (!groupsMap.has(key)) groupsMap.set(key, []);\n groupsMap.get(key).push(input);\n });\n\n for (const groupInputs of groupsMap.values()) {\n const gates = groupInputs.some((i) => i.required || Number(i.minWords) > 0);\n if (!gates) continue;\n const satisfied = groupInputs.some((i) => isInputSatisfied(i, getValue(i.sourceField)));\n if (!satisfied) return false;\n }\n return true;\n}\n\n// targetFieldsOf — every FORM FIELD an action's response would write into.\n// Derived from the response itself (the {groups} shape the parser returns), so\n// it needs no per-action configuration and stays correct as an action's output\n// changes.\nexport function targetFieldsOf(responseGroups = []) {\n const keys = [];\n responseGroups.forEach((group) => {\n if (group?.addRow) {\n if (group.name) keys.push(group.name);\n return;\n }\n (group?.fields ?? []).forEach((f) => {\n const key = f?.field ?? f?.name;\n if (key) keys.push(key);\n });\n });\n return keys;\n}\n\n// hasExistingData — would applying this response OVERWRITE something the user\n// already typed? Used to decide whether to ask first.\nexport function hasExistingData(form, responseGroups) {\n return targetFieldsOf(responseGroups).some((key) => {\n const value = form.getFieldValue(key);\n if (Array.isArray(value)) return value.some(rowHasData);\n return value !== undefined && value !== null && value !== '';\n });\n}\n\n/**\n * formHasUserData — has anyone typed anything into this form yet, ignoring the\n * field that triggered the action?\n *\n * Used for UPLOAD-triggered actions, where the question has to be answered\n * BEFORE the file is sent anywhere. At that point the response does not exist,\n * so the precise \"would this overwrite a target field?\" test cannot be run —\n * but \"the form is still blank\" is knowable, and it is the case that matters:\n * an empty form can be filled in silently, a form someone has worked on cannot.\n */\n/**\n * countUserEntries — how much the user has actually filled in, ignoring the\n * field that triggered the action.\n *\n * The overwrite prompt needs to state what is AT RISK. \"Already has details\n * entered\" is not a fact — it is a restatement of why the dialog opened, which\n * tells the reader nothing they did not already know. A count does: it is the\n * difference between \"I typed one thing by accident\" and \"I have filled in half\n * this form\".\n */\nexport function countUserEntries(form, excludeFields = []) {\n const values = form.getFieldsValue(true) ?? {};\n const skip = new Set([excludeFields].flat().filter(Boolean));\n const touched = typeof form.isFieldTouched === 'function'\n ? (key) => form.isFieldTouched(key)\n : () => true;\n\n let count = 0;\n Object.entries(values).forEach(([key, value]) => {\n // Counted on the SAME basis the prompt is shown on. Counting untouched\n // defaults here would say \"you have filled in 4 answers\" to someone who has\n // filled in one.\n if (skip.has(key) || !touched(key) || !hasRealValue(value)) return;\n if (Array.isArray(value)) {\n count += value.filter((row) => (row && typeof row === 'object' ? rowHasData(row) : Boolean(row))).length;\n return;\n }\n count += 1;\n });\n return count;\n}\n\n// hasRealValue — is there something here the user would mind losing?\nfunction hasRealValue(value) {\n if (value === undefined || value === null || value === '') return false;\n if (Array.isArray(value)) {\n // A Form.List that mounted with one blank row is not \"user data\".\n return value.some((row) => (row && typeof row === 'object' ? rowHasData(row) : Boolean(row)));\n }\n if (typeof value === 'object') {\n return Object.values(value).some((v) => v !== undefined && v !== null && v !== '');\n }\n return true;\n}\n\nexport function formHasUserData(form, excludeFields = []) {\n const values = form.getFieldsValue(true) ?? {};\n const skip = new Set([excludeFields].flat().filter(Boolean));\n const keys = Object.keys(values).filter((key) => !skip.has(key));\n\n // TOUCHED, not merely non-empty.\n //\n // A brand-new candidate form is NOT blank: four fields already carry values\n // from their configured defaults (VMS commission 5.5, VMS type \"Recurring\",\n // rate currency, rate unit). Judging by emptiness alone therefore reported\n // \"the user has filled things in\" on a form nobody had typed into, so the\n // very first résumé upload — the one that should just work — stopped to ask\n // permission to overwrite defaults the user had never seen.\n //\n // antd sets `touched` on USER interaction only: neither Form.Item\n // initialValue nor a programmatic setFieldsValue marks a field touched, which\n // is exactly the distinction needed. A field still has to hold something too,\n // so typing into a box and then clearing it does not count.\n if (typeof form.isFieldTouched === 'function') {\n return keys.some((key) => form.isFieldTouched(key) && hasRealValue(values[key]));\n }\n\n // No touch tracking available (a bare form object): fall back to emptiness.\n return keys.some((key) => hasRealValue(values[key]));\n}\n\n/**\n * parsedPayload — the scalar values a response carries, as a flat record.\n *\n * The duplicate check needs an email and a phone number, and after a résumé is\n * read those exist in the RESPONSE, not yet in the form. Checking the form here\n * would ask \"is this person already on file?\" about the blank page the user is\n * still looking at.\n *\n * Repeatable groups are skipped: nothing identifies a person by their third job.\n */\nexport function parsedPayload(responseGroups = []) {\n const out = {};\n (responseGroups ?? []).forEach((group) => {\n if (group?.addRow) return;\n (group?.fields ?? []).forEach((f) => {\n const key = f?.field ?? f?.name;\n if (!key) return;\n const value = f?.value;\n if (value === undefined || value === null || value === '') return;\n out[key] = value;\n });\n });\n return out;\n}\n\n// shouldConfirmApply — the admin-configured overwrite policy for an action.\n//\n// 'targetsFilled' (default for upload-triggered parses) — ask only when the\n// user has already filled something the parse would replace.\n// An upload BEFORE typing stays silent (the fast path); an\n// upload AFTER typing always asks, which is exactly the\n// requirement.\n// 'always' — ask every time.\n// 'never' — apply silently (the behaviour before this existed).\nexport function shouldConfirmApply(action, form, responseGroups) {\n const mode = action?.confirmWhen ?? 'never';\n if (mode === 'never') return false;\n if (mode === 'always') return true;\n return hasExistingData(form, responseGroups);\n}\n\nconst AI_ACTION_POSITIONS = ['top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'];\nconst DEFAULT_AI_ACTION_POSITION = 'bottom-left';\n\n// groupActionsByPosition — buckets a field's click-triggered AI actions by\n// their admin-configured Position (blank/unknown falls back to the original\n// \"bottom-left\" layout), so the caller can render each bucket in its own\n// slot around the field with the right alignment.\nexport function groupActionsByPosition(actions = []) {\n const buckets = {};\n actions.forEach((action) => {\n const pos = AI_ACTION_POSITIONS.includes(action.position) ? action.position : DEFAULT_AI_ACTION_POSITION;\n (buckets[pos] ??= []).push(action);\n });\n return buckets;\n}\n\nexport { AI_ACTION_POSITIONS };\n\n/**\n * useAiActions — shared logic behind config-driven \"AI Action\" buttons\n * (Generate/Parse-style: send some form fields to an admin-configured\n * service, get back the SAME `{groups: [...]}` shape getFormGroups returns\n * for Edit prefill, and apply it to the form via the same shared functions).\n * Used identically by AddFormV1 and EditFormV1 so the apply logic isn't\n * duplicated.\n *\n * @param {Object} args\n * @param {import('antd').FormInstance} args.form\n * @param {Array} args.groups - the module's form groups (for input lookups)\n * @param {string} args.module\n */\nexport default function useAiActions({ form, groups, module, onApplied, onParsed }) {\n const [loadingKey, setLoadingKey] = useState(null);\n // What the spinner SAYS. A bare \"Loading…\" over a form that has just been\n // taken away from the user tells them nothing about how long to wait or why.\n // Admin-configured per action (action.loadingText); the fallback still names\n // the action rather than the mechanism.\n const [loadingText, setLoadingText] = useState('');\n // Re-renders whenever ANY field changes, so isActionEnabled below reflects\n // live typing/upload without needing to know in advance which field names\n // any given action reads from (fully config-driven, no hardcoded paths).\n const watchedValues = Form.useWatch((values) => values, form);\n\n const isActionEnabled = useCallback(\n (action) => computeActionEnabled(action, (sourceField) => (watchedValues || {})[sourceField]),\n [watchedValues],\n );\n\n // Returns whether anything was actually applied to the form, so the caller\n // can show an accurate success vs. \"nothing found\" message — generic to any\n // action/module, since it only looks at what the response itself contained.\n const applyResponse = useCallback((responseGroups) => {\n if (!Array.isArray(responseGroups) || responseGroups.length === 0) return false;\n\n const scalarValues = applyScalarFieldValues(form, responseGroups.filter((g) => !g.addRow));\n let appliedAny = Object.keys(scalarValues).length > 0;\n\n responseGroups.forEach((group) => {\n if (!group.addRow) return;\n if (!Array.isArray(group.rows) || group.rows.length === 0) return;\n\n const shapedRows = buildAddRowInitial(group);\n if (shapedRows.length === 0) return;\n appliedAny = true;\n\n // A mounted Form.List's field name is already registered in the Form's\n // store (even with an empty array) — setFieldsValue is what antd\n // documents for a post-mount update; it re-renders the List with the\n // new rows directly, no remount trick needed.\n const applyRows = () => form.setFieldsValue({ [group.name]: shapedRows });\n\n const existingRows = form.getFieldValue(group.name) || [];\n const filled = existingRows.filter(rowHasData);\n if (filled.length) {\n const sectionName = group.label || group.name;\n openFormDecision({\n tone: 'overwrite',\n title: `Replace what's in ${sectionName}?`,\n facts: [\n { label: 'Section', value: sectionName },\n { label: 'Rows you filled in', value: filled.length },\n { label: 'Rows in the file', value: shapedRows.length },\n ],\n body: `Everything currently in ${sectionName} will be removed and replaced with what the file says. `\n + 'The rest of the form is not affected.',\n okText: 'Replace them',\n cancelText: 'Keep mine',\n danger: true,\n }).then((confirmed) => { if (confirmed) applyRows(); });\n } else {\n applyRows();\n }\n });\n\n return appliedAny;\n }, [form]);\n\n const runAction = useCallback(async (groupName, field, action, extraFile) => {\n // ── ask BEFORE sending the file anywhere ────────────────────────────────\n // For an upload action the question is \"shall I read this and fill the\n // form in?\", and it has to be asked before the call, not after: calling\n // first wastes a round-trip on a file the user may not want parsed, and it\n // sends their document to a service for nothing.\n const mode = action?.confirmWhen ?? 'never';\n if (action?.trigger === 'upload' && mode !== 'never') {\n const dirty = mode === 'always' || formHasUserData(form, [field?.field]);\n if (dirty) {\n const msgs = action.confirmMessages ?? {};\n // Naming the FILE matters: by this point the user has picked something,\n // and \"this file\" only reassures if they can see it is the one they\n // meant. The count says what is at risk — see countUserEntries.\n const fileName = extraFile?.name\n || form.getFieldValue(field?.field)?.slice?.(-1)?.[0]?.name;\n const entries = countUserEntries(form, [field?.field]);\n const proceed = await openFormDecision({\n tone: 'overwrite',\n title: msgs.title || 'Read this file and fill the form in?',\n facts: [\n { label: 'File', value: fileName },\n {\n label: 'You have filled in',\n value: entries ? `${entries} ${entries === 1 ? 'answer' : 'answers'}` : undefined,\n },\n ],\n // Present/future tense: nothing has been read yet. The confirm now\n // runs BEFORE the file is sent anywhere, so past-tense copy (\"we read\n // the details…\") describes something that has not happened.\n body: msgs.body\n || 'We can read the details out of this file and fill the form in for you. '\n + 'Where the file has an answer, it replaces what is currently in that box. '\n + 'Anything the file does not mention is left as you typed it.',\n okText: msgs.ok || 'Read it and fill in',\n cancelText: msgs.cancel || 'Keep what I typed',\n });\n if (!proceed) return undefined;\n }\n }\n\n setLoadingKey(action.key);\n setLoadingText(action.loadingText || `Reading ${action.label || 'the file'}…`);\n try {\n const inputs = {};\n const files = {};\n\n (action.inputs || []).forEach((input) => {\n if (input.kind === 'file') {\n const fromFileList = form.getFieldValue(input.sourceField);\n const file = extraFile ?? fromFileList?.[fromFileList.length - 1]?.originFileObj;\n if (file) files[input.param] = file;\n } else {\n const raw = form.getFieldValue(input.sourceField);\n if (raw !== undefined && raw !== null && raw !== '') {\n inputs[input.param] = applyTransform(raw, input.transform);\n }\n }\n });\n // Within a shared input Group (e.g. \"either a JD file or JD text\"), a\n // file input wins — drop the sibling text input if both are present.\n (action.inputs || []).forEach((input) => {\n if (!input.group || input.kind !== 'text') return;\n const fileSiblingProvided = (action.inputs || [])\n .some((i) => i.group === input.group && i.kind === 'file' && files[i.param]);\n if (fileSiblingProvided) delete inputs[input.param];\n });\n\n const result = await runAiAction({\n module, group: groupName, field: field.field, actionKey: action.key, inputs, files,\n });\n const label = action.label || 'AI action';\n const responseGroups = result?.groups;\n\n // Click actions still ask AFTER the call, because only the response says\n // which fields they would touch. Upload actions have already asked above.\n if (action?.trigger !== 'upload' && shouldConfirmApply(action, form, responseGroups)) {\n const msgs = action.confirmMessages ?? {};\n // A click action knows exactly which boxes it would overwrite, because\n // the response has already come back — so it can say so.\n const targets = targetFieldsOf(responseGroups).length;\n const confirmed = await openFormDecision({\n tone: 'overwrite',\n title: msgs.title || `Fill the form in from ${label}?`,\n facts: [\n { label: 'Source', value: label },\n { label: 'Boxes it would fill', value: targets || undefined },\n ],\n body: msgs.body\n || 'We found details you can use. Applying them will replace what you have '\n + 'already entered in those boxes.',\n okText: msgs.ok || 'Use these details',\n cancelText: msgs.cancel || 'Keep what I typed',\n });\n if (!confirmed) {\n message.info(`${label} cancelled — your entries were kept.`);\n return result;\n }\n }\n\n // ── between reading and applying ──────────────────────────────────\n // The host gets to veto BEFORE any value lands on the form. For a résumé\n // this is where \"do we already have this person?\" is asked: the parsed\n // email and phone exist now, and if the answer is yes there is no point\n // filling in a form the user is about to abandon.\n //\n // A veto returns the result unapplied — the host has already told the\n // user why and decided what to do with the file.\n if (onParsed) {\n const verdict = await onParsed({\n action,\n field,\n groupName,\n responseGroups,\n payload: parsedPayload(responseGroups),\n });\n if (verdict === false || verdict === 'abort') return result;\n }\n\n const appliedAny = applyResponse(responseGroups);\n if (appliedAny) {\n message.success(`${label} completed successfully.`);\n } else {\n message.info(`${label} completed, but no matching details were found.`);\n }\n // After the form is filled — a second, cheaper check that also covers\n // anything the user had already typed.\n if (appliedAny && onApplied) await onApplied({ action, field, groupName });\n return result;\n } catch (error) {\n message.error(error?.message || `${action.label || 'AI action'} failed`);\n return undefined;\n } finally {\n setLoadingKey(null);\n setLoadingText('');\n }\n }, [form, module, applyResponse, onApplied, onParsed]);\n\n return { runAction, loadingKey, loadingText, busy: loadingKey !== null, isActionEnabled };\n}\n","import { Button, Space } from 'antd';\n\n// AiActionButtonGroup — renders one position bucket (\"top-left\", \"bottom-right\",\n// etc.) of a field's AI action buttons, admin-configured entirely via each\n// action's `position`. Horizontal alignment follows the position's own\n// left/center/right suffix; the caller places top vs. bottom buckets around\n// the field itself.\nexport default function AiActionButtonGroup({ position, actions, aiActions, groupName, field }) {\n if (!actions || actions.length === 0) return null;\n\n const justifyContent = position.endsWith('center')\n ? 'center'\n : position.endsWith('right')\n ? 'flex-end'\n : 'flex-start';\n\n return (\n <Space size={8} className=\"v1-ai-action-buttons\" style={{ width: '100%', justifyContent }}>\n {actions.map((action) => (\n <Button\n key={action.key}\n size=\"small\"\n loading={aiActions.loadingKey === action.key}\n disabled={aiActions.busy || !aiActions.isActionEnabled(action)}\n onClick={() => aiActions.runAction(groupName, field, action)}\n >\n {action.label}\n </Button>\n ))}\n </Space>\n );\n}\n","// Generic email rule used by Add/Edit forms and the real-time input validator.\n// The rule is selected through the DB validation type `email`; it contains no\n// module or field-name assumptions.\nexport function isValidConfiguredEmail(value) {\n const email = String(value ?? '').trim();\n const match = /^([^\\s@]+)@([^\\s@]+)\\.([A-Za-z]{2,})$/.exec(email);\n if (!match) return false;\n\n const [, localPart, domainHost] = match;\n return /[A-Za-z]/.test(localPart) && /[A-Za-z]/.test(domainHost);\n}\n\nexport function configuredEmailRule(label = 'Email', message) {\n return {\n validator: (_, value) => {\n if (value === undefined || value === null || value === '') return Promise.resolve();\n return isValidConfiguredEmail(value)\n ? Promise.resolve()\n : Promise.reject(new Error(message ?? `Enter a valid ${label}; numeric-only email addresses are not allowed`));\n },\n };\n}\n","// inputValidator.js — config-driven real-time input restriction engine.\n// Driven entirely by field.validator in formGroupConfig — no hardcoding.\n// Used by FieldControl in AddFormV1 and EditFormV1.\n\nimport { isValidConfiguredEmail } from './emailValidator';\n\nexport const INPUT_VALIDATOR_TYPES = [\n { label: 'Only Numbers', value: 'onlyNumber' },\n { label: 'Only Letters', value: 'onlyLetter' },\n { label: 'Only Letters & Spaces', value: 'onlyLettersAndSpace' },\n { label: 'Only Alphanumeric', value: 'onlyAlphanumeric' },\n { label: 'Letters, Numbers & Hyphen', value: 'onlyLettersNumberAndHyphen' },\n { label: 'Letters, Numbers, Hyphen & Dot', value: 'onlyLettersNumberHyphenAndDot' },\n { label: 'Contact Number (digits, (), -)', value: 'contactNumber' },\n { label: 'Email (reject numeric-only address)', value: 'email' },\n { label: 'Job Title (letters, nums, symbols)', value: 'jobTitle' },\n { label: 'MSP / Ref ID (alphanumeric only)', value: 'mspRefId' },\n { label: 'Location (letters, nums, , . -)', value: 'locationValidation' },\n { label: 'Decimal / Range (number + dot)', value: 'decimalRange' },\n { label: 'Budget (numbers, must be > 0)', value: 'budgetValidation' },\n { label: 'Experience (numbers only)', value: 'experience' },\n { label: 'Numeric — 2 digits max', value: 'numericTwoDigits' },\n { label: 'Job Description (character count)', value: 'jobDescription' },\n { label: 'Website URL', value: 'websiteUrl' },\n];\n\n// onKeyPress allowlist patterns — used to block invalid chars before they appear.\n// null means no per-key blocking for that type.\nconst KEY_PATTERNS = {\n onlyNumber: /^[0-9]$/,\n experience: /^[0-9]$/,\n numericTwoDigits: /^[0-9]$/,\n budgetValidation: /^[0-9]$/,\n onlyLetter: /^[a-zA-Z\\s]$/,\n onlyLettersAndSpace: /^[a-zA-Z\\s]$/,\n onlyAlphanumeric: /^[a-zA-Z0-9]$/,\n mspRefId: /^[a-zA-Z0-9]$/,\n onlyLettersNumberAndHyphen: /^[a-zA-Z0-9\\s-]$/,\n locationValidation: /^[a-zA-Z0-9\\s\\-.,]$/,\n jobTitle: /^[a-zA-Z0-9\\s\\-.,/&+()*'\"#@]$/,\n decimalRange: /^[0-9.]$/,\n contactNumber: /^[0-9()\\-+\\s]$/,\n};\n\nexport function getKeyPattern(type) {\n return KEY_PATTERNS[type] ?? null;\n}\n\n// applyInputValidator — clean a raw input value according to the validator config.\n// Returns { cleaned: string, error: string|null }.\n// eventType 'blur' triggers trim; 'change' only strips leading whitespace.\nexport function applyInputValidator(rawValue, config = {}, eventType = 'change') {\n if (!config?.type || rawValue === undefined || rawValue === null) {\n return { cleaned: rawValue ?? '', error: null };\n }\n\n const value = String(rawValue);\n const { type, maxLength, maxChars } = config;\n const title = config.title || 'Field';\n let cleaned;\n let error = null;\n\n const trimStart = (s) => (eventType === 'blur' ? s.trim() : s.replace(/^\\s+/, ''));\n\n switch (type) {\n case 'onlyNumber':\n case 'experience':\n case 'numericTwoDigits': {\n const limit = Number(maxLength ?? (type === 'numericTwoDigits' ? 2 : 10));\n cleaned = value.replace(/[^0-9]/g, '');\n if (value !== cleaned) error = `${title} allows only numbers.`;\n if (cleaned.length > limit) {\n error = `${title} cannot exceed ${limit} characters.`;\n cleaned = cleaned.slice(0, limit);\n }\n break;\n }\n\n case 'onlyLetter': {\n const limit = Number(maxLength ?? 50);\n cleaned = value.replace(/[^a-zA-Z]/g, '');\n if (value !== cleaned) error = `${title} allows only letters.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersAndSpace': {\n const limit = Number(maxLength ?? 55);\n cleaned = trimStart(value.replace(/[^a-zA-Z\\s]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters and spaces.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyAlphanumeric':\n case 'mspRefId': {\n const limit = Number(maxLength ?? 50);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters and numbers.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersNumberAndHyphen': {\n const limit = Number(maxLength ?? 55);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s-]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers and hyphens.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersNumberHyphenAndDot': {\n const limit = Number(maxLength ?? 100);\n cleaned = value.replace(/[^a-zA-Z0-9\\-./#+[\\]{}()\\s]/g, '').slice(0, limit);\n if (value !== cleaned) error = `${title} allows only letters, numbers, hyphens and dots.`;\n break;\n }\n\n case 'contactNumber': {\n const limit = Number(maxLength ?? 15);\n cleaned = value.replace(/[^0-9()\\-+\\s]/g, '').slice(0, limit);\n const re = /^(\\+?[0-9]{1,3}[- ]?)?(\\(?\\d{1,4}\\)?[- ]?)?[\\d\\-\\s]{3,15}$/;\n if (value !== cleaned) error = `${title} allows only digits, parentheses () and hyphens.`;\n else if (cleaned && !re.test(cleaned)) error = `${title} is not a valid phone number format.`;\n break;\n }\n\n case 'locationValidation': {\n const limit = Number(maxLength ?? 100);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s\\-.,]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers, spaces, commas, dots and hyphens.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'jobTitle': {\n const limit = Number(maxLength ?? 80);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s\\-/&.,()+#@'\"*]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers, spaces and common symbols.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'decimalRange': {\n let c = value.replace(/[^0-9.]/g, '');\n const dotCount = (c.match(/\\./g) || []).length;\n if (dotCount > 1) {\n const di = c.indexOf('.');\n c = c.slice(0, di + 1) + c.slice(di + 1).replace(/\\./g, '');\n error = `${title} can have only one decimal point.`;\n }\n const digits = c.replace(/\\./g, '');\n const limit = Number(maxLength ?? 10);\n if (digits.length > limit) {\n c = c.slice(0, limit + (c.includes('.') ? 1 : 0));\n error = `${title} cannot exceed ${limit} digits.`;\n }\n cleaned = c;\n if (!error && value !== cleaned) error = `${title} allows only numbers and one dot.`;\n break;\n }\n\n case 'budgetValidation': {\n const limit = Number(maxLength ?? 10);\n cleaned = value.replace(/[^0-9]/g, '').slice(0, limit);\n const num = Number(cleaned);\n if (value !== cleaned) error = `${title} allows only numbers.`;\n else if (cleaned && num <= 0) error = `${title} must be greater than 0.`;\n break;\n }\n\n case 'jobDescription': {\n const limit = Number(maxChars ?? maxLength ?? 1500);\n const plain = value\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n if (plain.length > limit) error = `${title} cannot exceed ${limit} characters.`;\n cleaned = value; // never truncate HTML content\n break;\n }\n\n case 'email': {\n cleaned = eventType === 'blur' ? value.trim() : value;\n if (cleaned && !isValidConfiguredEmail(cleaned)) {\n error = `${title} must be a valid email address; numeric-only email addresses are not allowed.`;\n }\n break;\n }\n\n case 'websiteUrl': {\n cleaned = eventType === 'blur' ? value.trim() : value;\n const urlRe = /^(https?:\\/\\/)?([\\da-z.-]+)\\.([a-z.]{2,6})([/\\w .-]*)*\\/?$/i;\n if (cleaned && !urlRe.test(cleaned)) error = `${title} must be a valid URL.`;\n break;\n }\n\n default:\n cleaned = value;\n }\n\n return { cleaned, error };\n}\n","// Config-driven field uniqueness — the shared, pure runtime shared by\n// AddFormV1 and EditFormV1.\n//\n// An admin marks a field unique in Form Groups → Field → Validations; the\n// stored entry is an ordinary validations[] row:\n//\n// { type: 'unique', message: 'Email already present',\n// value: { collectionField, normalizer, skipBlank, validateOn } }\n//\n// Everything module-specific comes from that config. NOTHING in this file (or\n// in the two form engines) may name a module, a collection or a field — the\n// server resolves the collection, the stored field and the tenant scope from\n// the module + the caller's claims.\n//\n// This module owns ALL of the uniqueness behaviour so neither form duplicates\n// it: rule extraction, blank handling, normalization, the per-session cache,\n// the stale-response guard, the pending-request counter that blocks submit, the\n// antd rule factory (incl. \"only after the synchronous rules pass\") and the\n// mapping of a submit-time 409 back onto form fields.\n\nexport const UNIQUE_VALIDATION_TYPE = 'unique';\nexport const ALREADY_PRESENT_CODE = 'already_present';\nexport const DUPLICATE_VALUE_CODE = 'DUPLICATE_VALUE';\n\nconst DEFAULT_MESSAGE = 'This value is already present';\n\n// ── Rule extraction ──────────────────────────────────────────────────────────\n\nfunction validationType(validation) {\n if (typeof validation === 'string') return validation;\n return validation?.type ?? validation?.rule ?? validation?.name;\n}\n\n// The admin editor stores the unique options as an OBJECT under `value` (the\n// declared valueType is 'object', so the save path leaves it untouched). Older\n// or hand-written config may put the same keys flat on the validation itself,\n// so both shapes are accepted.\nexport function parseUniqueValidation(validation) {\n if (validationType(validation) !== UNIQUE_VALIDATION_TYPE) return null;\n if (typeof validation === 'string') {\n return { message: DEFAULT_MESSAGE, normalizer: 'trim', skipBlank: true, validateOn: 'blur' };\n }\n\n const raw = (validation.value && typeof validation.value === 'object') ? validation.value : validation;\n const validateOn = String(raw.validateOn ?? 'blur').toLowerCase();\n\n return {\n message: (typeof validation.message === 'string' && validation.message.trim())\n ? validation.message.trim()\n : DEFAULT_MESSAGE,\n // Kept only so the admin's configured target is inspectable client-side;\n // it is NEVER sent to the server (the server resolves the stored field).\n collectionField: raw.collectionField ?? '',\n normalizer: String(raw.normalizer ?? 'trim'),\n // skipBlank defaults to true; only an explicit `false` turns it off.\n skipBlank: raw.skipBlank !== false,\n validateOn: ['blur', 'change', 'submit'].includes(validateOn) ? validateOn : 'blur',\n };\n}\n\nexport function getUniqueRule(field) {\n const validations = field?.validations ?? field?.validation ?? field?.rules ?? [];\n if (!Array.isArray(validations)) return null;\n for (const validation of validations) {\n const rule = parseUniqueValidation(validation);\n if (rule) return rule;\n }\n return null;\n}\n\n// Every unique-configured field in the form, flattened across groups. Used to\n// map a submit-time 409 back onto a field when the transport lost the\n// structured body (see extractDuplicateFieldErrors).\nexport function collectUniqueRules(groups = []) {\n const collected = [];\n (Array.isArray(groups) ? groups : []).forEach((group) => {\n (group?.fields ?? []).forEach((field) => {\n const rule = getUniqueRule(field);\n if (!rule || !field?.field) return;\n collected.push({ field: field.field, label: field.label ?? field.field, rule });\n });\n });\n return collected;\n}\n\n// ── Blank + normalization ────────────────────────────────────────────────────\n\n// Blank = missing / null / empty / whitespace-only / empty list.\n// Numeric ZERO and boolean FALSE are REAL values, not blanks — a \"0\" employee\n// code or a `false` flag must still be uniqueness-checked.\nexport function isBlankUniqueValue(value) {\n if (value === undefined || value === null) return true;\n if (typeof value === 'number') return Number.isNaN(value);\n if (typeof value === 'boolean') return false;\n if (Array.isArray(value)) return value.length === 0;\n return String(value).trim() === '';\n}\n\nexport function normalizeUniqueValue(value, normalizer = 'trim') {\n if (value === undefined || value === null) return '';\n const text = String(value);\n switch (String(normalizer)) {\n case 'exact': return text;\n case 'lower':\n case 'trimLower':\n case 'email': return text.trim().toLowerCase();\n case 'digitsOnly': return text.replace(/\\D+/g, '');\n case 'trim':\n default: return text.trim();\n }\n}\n\nexport function shouldCheckUniqueValue(rule, value) {\n if (!rule) return false;\n if (rule.skipBlank !== false && isBlankUniqueValue(value)) return false;\n return true;\n}\n\n// antd/rc-field-form filters rules by trigger. `[]` matches no trigger at all,\n// so a submit-only rule runs exclusively inside form.validateFields().\nexport function uniqueValidateTriggers(rule) {\n if (!rule) return [];\n if (rule.validateOn === 'submit') return [];\n if (rule.validateOn === 'change') return ['onChange', 'onBlur'];\n return ['onBlur'];\n}\n\n// ── The checker (cache + staleness + pending) ────────────────────────────────\n\nexport const UNIQUE_STATUS = {\n SKIPPED: 'skipped',\n AVAILABLE: 'available',\n DUPLICATE: 'duplicate',\n STALE: 'stale',\n ERROR: 'error',\n};\n\nfunction cacheKey({ module, field, recordId, normalized }) {\n return `${module}\u0000${field}\u0000${recordId ?? ''}\u0000${normalized}`;\n}\n\n/**\n * One checker per form session. Both form engines create exactly one and pass\n * it into getRules; it is the only place a uniqueness request is ever made.\n *\n * @param checkFieldUnique the API function (injected so it can be mocked)\n * @param onPendingChange (pendingCount) => void — drives the submit button\n */\nexport function createUniqueChecker({ checkFieldUnique, onPendingChange } = {}) {\n // Normalized-value cache: the same normalized value is never re-checked in\n // one form session (blur → submit → blur again is one request, not three).\n // Only DEFINITIVE outcomes are cached — a failed request must be retried.\n const cache = new Map();\n // Monotonic sequence. Every request takes the next number and records itself\n // as its field's latest; when a response comes back with a number that is no\n // longer the latest for that field, the user has typed on and the answer\n // describes an OLD value — it is dropped instead of overwriting the new one.\n const latestSeq = new Map();\n let seq = 0;\n let pending = 0;\n\n function setPending(next) {\n pending = next;\n if (typeof onPendingChange === 'function') onPendingChange(pending);\n }\n\n async function check({ module, field, rule, value, recordId, clientId, region } = {}) {\n if (!rule || !module || !field || typeof checkFieldUnique !== 'function') {\n return { status: UNIQUE_STATUS.SKIPPED };\n }\n if (!shouldCheckUniqueValue(rule, value)) {\n return { status: UNIQUE_STATUS.SKIPPED };\n }\n\n const normalized = normalizeUniqueValue(value, rule.normalizer);\n const key = cacheKey({ module, field, recordId, normalized });\n if (cache.has(key)) return cache.get(key);\n\n seq += 1;\n const mySeq = seq;\n latestSeq.set(field, mySeq);\n setPending(pending + 1);\n\n try {\n const result = await checkFieldUnique({\n module,\n field,\n value,\n // recordId is passed straight through: EditFormV1 supplies it (so the\n // record does not collide with itself), AddFormV1 never does.\n recordId,\n clientId,\n region,\n });\n\n if (latestSeq.get(field) !== mySeq) return { status: UNIQUE_STATUS.STALE };\n\n const outcome = result?.available === false\n ? {\n status: UNIQUE_STATUS.DUPLICATE,\n message: firstDuplicateMessage(result) || rule.message || DEFAULT_MESSAGE,\n }\n : { status: UNIQUE_STATUS.AVAILABLE };\n\n cache.set(key, outcome);\n return outcome;\n } catch (err) {\n if (latestSeq.get(field) !== mySeq) return { status: UNIQUE_STATUS.STALE };\n // FAIL SAFE. A network/server failure must never block a user from\n // typing or submitting — submit-time enforcement on the server is the\n // authoritative check, and it still runs. Not cached, so the next blur\n // retries.\n return { status: UNIQUE_STATUS.ERROR, error: err };\n } finally {\n setPending(Math.max(0, pending - 1));\n }\n }\n\n return {\n check,\n isPending: () => pending > 0,\n pendingCount: () => pending,\n // Exposed for tests / a form that reloads its config mid-session.\n reset: () => { cache.clear(); latestSeq.clear(); },\n };\n}\n\nfunction firstDuplicateMessage(result) {\n const fromErrors = (result?.errors ?? []).find((item) => item?.message)?.message;\n return fromErrors || result?.message || '';\n}\n\n// ── antd rule factory ────────────────────────────────────────────────────────\n\n// Marker so getRules can find the unique rules again after the array is built\n// (see attachUniqueSyncGuards).\nconst UNIQUE_RULE_FLAG = '__uniqueRule';\n\n/**\n * Build the async antd rule for one `unique` validations entry.\n * `context` is supplied by the form engine: { checker, module, recordId,\n * clientId, region }. With no context (test harnesses, other callers of\n * getRules) the rule degrades to a no-op instead of throwing.\n */\nexport function buildUniqueValidationRule({ field, validation, context }) {\n const rule = parseUniqueValidation(validation);\n if (!rule) return { validator: () => Promise.resolve() };\n\n const fieldKey = field?.field ?? '';\n if (!context?.checker || !context?.module || !fieldKey) {\n return { validator: () => Promise.resolve() };\n }\n\n return {\n [UNIQUE_RULE_FLAG]: rule,\n validateTrigger: uniqueValidateTriggers(rule),\n validator: async (_, value) => {\n const outcome = await context.checker.check({\n module: context.module,\n field: fieldKey,\n rule,\n value,\n recordId: context.recordId,\n clientId: context.clientId,\n region: context.region,\n });\n if (outcome.status === UNIQUE_STATUS.DUPLICATE) {\n return Promise.reject(new Error(outcome.message || rule.message));\n }\n // skipped / available / stale / error all pass: a stale answer describes\n // a value the user has already replaced, and an error is handled by the\n // authoritative server-side check at submit.\n return Promise.resolve();\n },\n };\n}\n\n/**\n * The uniqueness call must never fire for a value that is ALREADY known\n * invalid (blank required field, malformed email, failed pattern) — that would\n * waste a round trip and stack a confusing second error under the field.\n *\n * antd runs a field's rules in parallel, so ordering alone cannot express\n * \"after the synchronous rules\". Instead each unique rule is re-wrapped with a\n * guard that first evaluates its sibling rules against the same value and\n * resolves immediately if any of them fails.\n */\nexport function attachUniqueSyncGuards(rules = []) {\n const list = Array.isArray(rules) ? rules : [];\n if (!list.some((rule) => rule && rule[UNIQUE_RULE_FLAG])) return list;\n\n const siblings = list.filter((rule) => rule && !rule[UNIQUE_RULE_FLAG]);\n return list.map((rule) => {\n if (!rule || !rule[UNIQUE_RULE_FLAG]) return rule;\n const inner = rule.validator;\n return {\n ...rule,\n validator: async (ruleArg, value) => {\n if (await hasSyncRuleError(siblings, value, ruleArg)) return Promise.resolve();\n return inner(ruleArg, value);\n },\n };\n });\n}\n\n// Minimal evaluator for the rule shapes this form engine actually produces:\n// { required }, { pattern }, { len }, { type:'url' } and custom { validator }.\n// Any rule it cannot interpret is treated as passing — the guard exists to\n// suppress a redundant request, never to invent a failure.\nexport async function hasSyncRuleError(rules = [], value, ruleArg = {}) {\n for (const rule of rules) {\n if (!rule || typeof rule === 'string') continue;\n if (rule.required && isBlankUniqueValue(value)) return true;\n if (!isBlankUniqueValue(value)) {\n if (rule.pattern instanceof RegExp && !new RegExp(rule.pattern.source, rule.pattern.flags).test(String(value))) return true;\n if (rule.len != null && String(value).length !== Number(rule.len)) return true;\n }\n if (typeof rule.validator === 'function') {\n try {\n await rule.validator(ruleArg, value);\n } catch {\n return true;\n }\n }\n }\n return false;\n}\n\n// ── Submit-time 409 → field errors ───────────────────────────────────────────\n\nfunction parseMaybeJson(text) {\n const trimmed = String(text ?? '').trim();\n if (!trimmed.startsWith('{')) return null;\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n}\n\nfunction duplicateBody(source) {\n if (!source) return null;\n if (typeof source === 'string') return parseMaybeJson(source);\n // An Error thrown by the create/update services: the structured body rides\n // on `.data` (createModuleRecord) or `.response` (fetchJsonWithAuth).\n const candidates = [source.data, source.response, source, parseMaybeJson(source.message)];\n for (const candidate of candidates) {\n if (!candidate || typeof candidate !== 'object') continue;\n if (Array.isArray(candidate.errors) || candidate.code === DUPLICATE_VALUE_CODE) return candidate;\n }\n return null;\n}\n\n/**\n * The antd form path a duplicate error must be attached to.\n *\n * A field inside a REPEATABLE (addRow) group is registered under\n * [groupName, rowIndex, fieldKey] — its Form.List is named after the group — so\n * an error reported with only the field key would either land nowhere or, worse,\n * on a same-named field elsewhere in the form. The server sends `group` and\n * `rowIndex` alongside `field` for exactly this case; both are absent for an\n * ordinary field, which keeps the historical plain-string name.\n */\nexport function duplicateErrorName({ field, group, rowIndex }) {\n const key = String(field ?? '');\n const row = Number(rowIndex);\n if (group && Number.isInteger(row) && row >= 0) {\n // A row field's own key may itself be a dotted path within the row object.\n return [String(group), row, ...key.split('.')];\n }\n return key;\n}\n\n/**\n * Map a failed create/update into per-field duplicate errors.\n *\n * The `field` in the response is the FRONTEND field key — which is not always\n * the stored Mongo field (a form field `altEmail` may be stored as\n * `alternateEmail`) — so the returned name is always taken from the response,\n * never from the configured collectionField.\n *\n * `uniqueRules` (from collectUniqueRules) is the fallback path: one of the\n * update services flattens an error body down to its message string, which\n * loses `errors[]`. When the surviving message is exactly the message an admin\n * configured for a unique field, that identifies the field unambiguously\n * without any module or field name being hardcoded here.\n *\n * Returns [] when the failure is not a duplicate, so callers keep their normal\n * error handling.\n */\nexport function extractDuplicateFieldErrors(source, { uniqueRules = [] } = {}) {\n const body = duplicateBody(source);\n if (body) {\n const errors = (Array.isArray(body.errors) ? body.errors : [])\n .filter((item) => item?.field)\n .map((item) => {\n const mapped = {\n field: item.field,\n // `name` is what form.setFields needs; `field` stays for callers (and\n // tests) that only care which field key was reported.\n name: duplicateErrorName(item),\n message: item.message || body.message || DEFAULT_MESSAGE,\n };\n // Only present for a repeatable-group duplicate — never emitted as\n // `undefined`, so an ordinary duplicate is the exact object it always was\n // plus `name`.\n if (Array.isArray(mapped.name)) {\n mapped.group = String(item.group);\n mapped.rowIndex = Number(item.rowIndex);\n }\n return mapped;\n });\n if (errors.length) return errors;\n }\n\n const message = String(\n (body && (body.message || body.error))\n ?? (typeof source === 'string' ? source : source?.message)\n ?? '',\n ).trim();\n if (!message) return [];\n\n const isDuplicateStatus = source?.status === 409 || body?.code === DUPLICATE_VALUE_CODE;\n const matched = uniqueRules.filter(\n (entry) => String(entry?.rule?.message ?? '').trim().toLowerCase() === message.toLowerCase(),\n );\n if (matched.length && (isDuplicateStatus || matched.length === 1)) {\n // Fallback path: the structured body was flattened to a message, so the row\n // index is gone. The field is still identified, and the error lands on the\n // field rather than nowhere — see the note in collectUniqueRules.\n return matched.map((entry) => ({ field: entry.field, name: entry.field, message }));\n }\n return [];\n}\n\nexport default createUniqueChecker;\n","// Config-driven field uniqueness — the single preflight API call.\n//\n// The endpoint is deliberately narrow: the client names a MODULE and a FORM\n// FIELD, never a collection, a collection field or a tenant id. The server\n// resolves the target collection, the stored field and the tenant scope from\n// the caller's claims + the module's Form Groups config. That is a security\n// boundary — do not widen this payload.\n//\n// POST (never GET) so the value never lands in a URL, browser history or an\n// access log.\n//\n// 200 -> { status: true, available: true }\n// 409 -> { status: false, available: false, code: 'DUPLICATE_VALUE',\n// message, errors: [{ field, code: 'already_present', message }] }\n//\n// A 409 is a NORMAL structured outcome here, not a transport failure:\n// fetchJsonWithAuth throws on every non-2xx, so it is caught and converted back\n// into a plain result object. Anything else (network down, 500, gateway HTML)\n// is re-thrown — the caller decides how to fail safe.\nimport { fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL } from './apiConfig';\n\nexport const DUPLICATE_VALUE_CODE = 'DUPLICATE_VALUE';\n\nexport async function checkFieldUnique({\n module,\n field,\n value,\n recordId,\n clientId,\n region,\n} = {}) {\n if (!module || !field) {\n throw new Error('module and field are required to check uniqueness');\n }\n\n // Only the four scope keys the contract allows, and only when they carry a\n // value — an explicit `recordId: undefined` on the Add form must not become a\n // `\"recordId\": null` the server could read as \"exclude nothing / something\".\n const body = { field, value: value ?? '' };\n if (recordId) body.recordId = String(recordId);\n if (clientId) body.clientId = String(clientId);\n if (region) body.region = String(region);\n\n try {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/module/validate-unique?module=${encodeURIComponent(module)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n },\n );\n\n // Treat anything that is not an explicit `available: false` as available;\n // an older/partial server response must not invent a duplicate.\n const available = json?.available !== false;\n return {\n available,\n duplicate: !available,\n message: json?.message ?? '',\n errors: Array.isArray(json?.errors) ? json.errors : [],\n response: json,\n };\n } catch (err) {\n if (err?.status === 409) {\n const body409 = err.response ?? err.data ?? {};\n return {\n available: false,\n duplicate: true,\n message: body409?.message ?? err.message ?? '',\n errors: Array.isArray(body409?.errors) ? body409.errors : [],\n response: body409,\n };\n }\n throw err;\n }\n}\n\nexport default checkFieldUnique;\n\n// ── staged duplicate preflight ───────────────────────────────────────────────\n// POST /module/duplicate-check?module=X body: { payload, excludeId }\n//\n// Asked before a create, so the user can be offered the EXISTING record instead\n// of silently creating a second copy of the same person. Which fields are\n// compared, in what order, and what happens on a hit are all module config —\n// see models.DuplicateCheckStrategy. POST, not GET: the body carries personal\n// data (email, phone) that must not reach a URL or a proxy log.\n//\n// Fails OPEN: a check that cannot run must never block a legitimate create.\nexport async function checkDuplicateRecord({ module, payload, excludeId } = {}) {\n if (!module || !payload) return { duplicate: false };\n try {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/module/duplicate-check?module=${encodeURIComponent(module)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ payload, ...(excludeId ? { excludeId } : {}) }),\n },\n );\n return json?.data ?? json ?? { duplicate: false };\n } catch {\n return { duplicate: false };\n }\n}\n","// Admin-configurable upload content categories. The Form Groups admin stores a\n// category key on a file field (`field.accept`); AddFormV1/EditFormV1 expand it\n// to the browser `accept` attribute and a beforeUpload extension check, so a\n// file outside the category is rejected client-side with a clear message.\n// A field without a category (or 'any') falls back to the fine-grained\n// `fileType` validation rule, preserving existing behaviour.\n\nconst IMAGE_EXTS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'avif'];\n// Mirrors DocumentViewer's VIDEO_EXT so anything uploadable is also previewable.\nconst VIDEO_EXTS = ['mp4', 'webm', 'ogg', 'ogv', 'mov', 'm4v'];\nconst DOC_EXTS = ['pdf', 'doc', 'docx'];\n\nexport const UPLOAD_ACCEPT_OPTIONS = [\n { label: 'Any file', value: 'any' },\n { label: 'Documents & Images (pdf, doc, images)', value: 'documents' },\n { label: 'Images only', value: 'images' },\n { label: 'Videos only', value: 'videos' },\n { label: 'Images & Videos', value: 'imagesAndVideos' },\n];\n\nexport const UPLOAD_ACCEPT_CATEGORIES = {\n documents: {\n exts: [...DOC_EXTS, ...IMAGE_EXTS],\n hint: 'PDF, DOC or image files',\n error: 'Only document (PDF/DOC) or image files are allowed',\n },\n images: {\n exts: IMAGE_EXTS,\n hint: 'Image files',\n error: 'Only image files are allowed',\n },\n videos: {\n exts: VIDEO_EXTS,\n hint: 'Video files',\n error: 'Only video files are allowed',\n },\n imagesAndVideos: {\n exts: [...IMAGE_EXTS, ...VIDEO_EXTS],\n hint: 'Image or video files',\n error: 'Only image or video files are allowed',\n },\n};\n\n// uploadAcceptCategory resolves a field's configured category, or null when the\n// field accepts any file ('any', unset, or an unknown key).\nexport function uploadAcceptCategory(field) {\n return UPLOAD_ACCEPT_CATEGORIES[String(field?.accept ?? '').trim()] ?? null;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// quickActionLabels — what the buttons inside a dropdown actually say.\n//\n// THE PROBLEM\n// Every quick-action button read \"Add More\", in the dropdown AND as the modal\n// title. \"Add More\" tells the user nothing: more of what? And on the Edit\n// button it was actively wrong — the stored config had `quickEditLabel:\n// \"Add More\"` too, so clicking \"Add More\" opened an EDIT form.\n//\n// A button should name the thing it acts on: \"Add New Employer\", \"Edit\n// Employer\". That is what the user is thinking, and it is the difference\n// between a control you have to try and one you can read.\n//\n// HOW THE NAME IS FOUND\n// From the TARGET MODULE the button opens, turned into a readable singular:\n// employers → Employer\n// locationMasters → Location Master\n// client_contacts → Client Contact\n// An admin can still type an explicit label; this only decides what happens\n// when they have not. No module name is hardcoded here.\n// ─────────────────────────────────────────────────────────────────────────\n\n// Words that should not be title-cased into nonsense when they appear inside\n// a module key. Deliberately tiny — this is a display nicety, not a dictionary.\nconst LOWER_WORDS = new Set(['of', 'and', 'the', 'to', 'for', 'in', 'a', 'an']);\n\n// Irregular plurals worth knowing, because the naive \"drop the s\" rule turns\n// them into something visibly wrong on a button.\nconst IRREGULAR_SINGULARS = {\n addresses: 'address',\n branches: 'branch',\n batches: 'batch',\n categories: 'category',\n companies: 'company',\n countries: 'country',\n entries: 'entry',\n people: 'person',\n statuses: 'status',\n};\n\n/**\n * singularize — a module key's singular form.\n *\n * Conservative on purpose: an unknown word that does not clearly look plural\n * is left ALONE. Printing \"Addres\" or \"Statu\" on a button is worse than\n * printing a plural, so the rule only fires where it is safe.\n */\nexport function singularize(word) {\n const w = String(word ?? '').trim();\n if (!w) return '';\n // An ALL-CAPS acronym is never a plural: \"VMS\" is a name, and stripping its\n // trailing S produces \"VM\" — a different thing entirely.\n if (w.length > 1 && w === w.toUpperCase() && /[A-Z]/.test(w)) return w;\n const lower = w.toLowerCase();\n if (IRREGULAR_SINGULARS[lower]) return IRREGULAR_SINGULARS[lower];\n // \"-ies\" → \"-y\" (categories → category)\n if (/[^aeiou]ies$/i.test(w)) return w.slice(0, -3) + 'y';\n // \"-ses\"/\"-xes\"/\"-zes\"/\"-ches\"/\"-shes\" → drop \"es\"\n if (/(s|x|z|ch|sh)es$/i.test(w)) return w.slice(0, -2);\n // A plain trailing \"s\", but never \"ss\" (address) and never a bare \"s\".\n if (/[^s]s$/i.test(w)) return w.slice(0, -1);\n return w;\n}\n\n/**\n * humanizeModule — a module key as a person would write it.\n * \"locationMasters\" → \"Location Master\"\n */\nexport function humanizeModule(moduleKey) {\n const raw = String(moduleKey ?? '').trim();\n if (!raw) return '';\n const words = raw\n // An acronym RUN followed by a word: \"MSPRequests\" → \"MSP Requests\".\n // Must run before the camelCase split, which cannot see this boundary\n // because there is no lowercase letter in front of the capital.\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n // camelCase → camel Case\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n // snake_case / kebab-case / dots → spaces\n .replace(/[_\\-.]+/g, ' ')\n .split(/\\s+/)\n .filter(Boolean);\n if (!words.length) return '';\n\n const singularLast = singularize(words[words.length - 1]);\n const all = [...words.slice(0, -1), singularLast];\n\n return all\n .map((word, index) => {\n const lower = word.toLowerCase();\n // Preserve an ALL-CAPS acronym an admin deliberately used (VMS, MSP).\n if (word.length > 1 && word === word.toUpperCase()) return word;\n if (index > 0 && LOWER_WORDS.has(lower)) return lower;\n return lower.charAt(0).toUpperCase() + lower.slice(1);\n })\n .join(' ');\n}\n\n/**\n * quickCreateLabel — the \"add\" button's text.\n * An explicitly configured label always wins.\n */\nexport function quickCreateLabel(field, fallbackModule) {\n const configured = String(field?.quickCreateLabel ?? '').trim();\n // \"Add More\" is treated as UNSET rather than as a choice: it is the old\n // default that this function exists to replace, and it is stored on live\n // config. Honouring it literally would mean the fix silently did nothing.\n if (configured && configured.toLowerCase() !== 'add more') return configured;\n const name = humanizeModule(field?.quickCreateModule || fallbackModule);\n return name ? `Add New ${name}` : 'Add New';\n}\n\n/**\n * quickEditLabel — the \"edit\" button's text.\n */\nexport function quickEditLabel(field, fallbackModule) {\n const configured = String(field?.quickEditLabel ?? '').trim();\n // Same reasoning, and here it also fixes a real bug: the stored config had\n // \"Add More\" on the EDIT button, so clicking \"Add More\" opened an edit form.\n if (configured && configured.toLowerCase() !== 'add more') return configured;\n const name = humanizeModule(field?.quickEditModule || fallbackModule);\n return name ? `Edit ${name}` : 'Edit';\n}\n\n/**\n * quickModalTitle — the popup's heading.\n *\n * The requirement asks for the popup to match the button (\"same in the popup\n * also\"), so it uses the same resolved name. The heading is allowed to be\n * slightly fuller than the button, because a dialog title has room and the\n * user has just left the context behind.\n */\nexport function quickModalTitle(mode, field, fallbackModule) {\n const name = humanizeModule(\n (mode === 'create' ? field?.quickCreateModule : field?.quickEditModule) || fallbackModule,\n );\n if (mode === 'create') {\n const configured = String(field?.quickCreateTitle ?? '').trim();\n if (configured) return configured;\n return name ? `Add New ${name}` : quickCreateLabel(field, fallbackModule);\n }\n const configured = String(field?.quickEditTitle ?? '').trim();\n if (configured) return configured;\n return name ? `Edit ${name}` : quickEditLabel(field, fallbackModule);\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","// ─────────────────────────────────────────────────────────────────────────\n// quickCreateNotice — config + resolution logic for the \"you just created a\n// brand-new record\" popup shown after an in-dropdown quick-create.\n//\n// WHY this lives in its own module rather than inside QuickCreateEditField:\n// anything that imports AddFormV1/EditFormV1 (which QuickCreateEditField does,\n// lazily, and which the admin screen does statically) cannot be rendered under\n// vitest — react-pdf's DocumentViewer needs DOMMatrix and jsdom has none. That\n// is a pre-existing trap in this repo. Keeping the *decisions* (is the notice\n// on? what is the record called? who created it?) in a dependency-free module\n// means the part that can actually be wrong is unit-testable, and the React\n// file is left with nothing but rendering.\n//\n// Everything here is generic: no module name, no field name, no collection is\n// special-cased. A field opts in through the admin config key\n// `quickCreateNotice`.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport { fetchJsonWithAuth, getStoredUser } from '../../services/authApi';\nimport { AUTH_URL } from '../../services/apiConfig';\nimport { fetchUserName, userDisplayName } from '../detail/userNames';\n\n/**\n * The product-owner-approved default. It is the DEFAULT rather than something\n * the admin must type, so that a field which merely switches the notice on\n * still shows the correct warning. Admin config overrides it verbatim.\n */\nexport const DEFAULT_QUICK_CREATE_NOTICE_MESSAGE =\n 'A new client has been created successfully. Please contact your Organization Admin to '\n + 'configure the required forms, workflows, permissions, and client-specific details before '\n + 'proceeding with further operations.';\n\nexport const DEFAULT_QUICK_CREATE_NOTICE_TITLE = 'New record created';\nexport const DEFAULT_QUICK_CREATE_RECORD_LABEL = 'Record name';\nexport const DEFAULT_QUICK_CREATE_CREATOR_LABEL = 'Created by';\n\nconst str = (v) => (v === undefined || v === null ? '' : String(v).trim());\n\n/**\n * normalizeQuickCreateNotice — field config → the resolved notice settings, or\n * null when this field has not opted in.\n *\n * `enabled` defaults to TRUE when the key is present at all: an admin who took\n * the trouble to author a `quickCreateNotice` object meant to switch it on, and\n * a config object that silently does nothing is the worst of the two failure\n * modes. Only an explicit `enabled: false` turns it off.\n */\nexport function normalizeQuickCreateNotice(field) {\n const raw = field?.quickCreateNotice;\n if (!raw || typeof raw !== 'object') return null;\n if (raw.enabled === false) return null;\n\n return {\n enabled: true,\n title: str(raw.title) || DEFAULT_QUICK_CREATE_NOTICE_TITLE,\n message: str(raw.message) || DEFAULT_QUICK_CREATE_NOTICE_MESSAGE,\n // Both traceability lines are ON unless explicitly disabled — same reasoning\n // as `enabled` above.\n showCreator: raw.showCreator !== false,\n showRecordName: raw.showRecordName !== false,\n recordNameField: str(raw.recordNameField),\n recordLabel: str(raw.recordLabel) || DEFAULT_QUICK_CREATE_RECORD_LABEL,\n creatorLabel: str(raw.creatorLabel) || DEFAULT_QUICK_CREATE_CREATOR_LABEL,\n };\n}\n\n// Keys that commonly hold a record's human name, tried in order when the admin\n// configured no recordNameField and the field carries no lookup displayField.\nconst NAME_LIKE_KEYS = [\n 'clientName', 'companyName', 'name', 'title', 'fullName',\n 'displayName', 'label', 'employerName', 'vendorName',\n];\n\nconst readPath = (record, key) => {\n if (!record || !key) return undefined;\n return String(key).split('.').reduce((acc, part) => (\n acc && typeof acc === 'object' ? acc[part] : undefined\n ), record);\n};\n\nconst scalarName = (v) => {\n if (typeof v === 'string' || typeof v === 'number') return str(v);\n // Labeled-select / reference shapes ({label,value}) show up on records too.\n if (v && typeof v === 'object') return str(v.label ?? v.name ?? v.title ?? '');\n return '';\n};\n\n/**\n * pickRecordName — the record's display name, by the configured key first, then\n * the field's lookup displayField, then well-known name-like keys, then ANY\n * string key whose name ends in \"name\". Purely best-effort: an empty result is\n * a legitimate outcome and must not stop the notice from appearing.\n */\nexport function pickRecordName(record, { recordNameField = '', displayField = '' } = {}) {\n if (!record || typeof record !== 'object') return '';\n for (const key of [recordNameField, displayField, ...NAME_LIKE_KEYS]) {\n if (!key) continue;\n const value = scalarName(readPath(record, key));\n if (value) return value;\n }\n const loose = Object.keys(record).find((k) => (\n /name$/i.test(k) && typeof record[k] === 'string' && record[k].trim()\n ));\n return loose ? str(record[loose]) : '';\n}\n\n/**\n * pickCreatorId — the id of whoever the stored record credits with its\n * creation, across the several shapes the gateway's audit stamp can take\n * (plain id, ObjectId wrapper, nested user object).\n */\nexport function pickCreatorId(record) {\n if (!record || typeof record !== 'object') return '';\n const candidates = [\n record.createdBy, record.created_by, record.createdById,\n record.createdUserId, record.recordMeta?.createdBy, record.ownerId,\n ];\n for (const candidate of candidates) {\n if (candidate === undefined || candidate === null || candidate === '') continue;\n if (typeof candidate === 'object') {\n const nested = candidate.$oid ?? candidate.userId ?? candidate._id ?? candidate.id;\n const value = str(nested);\n if (value) return value;\n continue;\n }\n const value = str(candidate);\n if (value) return value;\n }\n return '';\n}\n\n/** currentUserName — the signed-in user's display name, '' when unknowable. */\nexport function currentUserName(getUser = getStoredUser) {\n try {\n return userDisplayName(getUser()) || '';\n } catch {\n return '';\n }\n}\n\nconst defaultFetchRecord = (module, recordId) => fetchJsonWithAuth(\n AUTH_URL,\n `/module/list?module=${encodeURIComponent(module)}&id=${encodeURIComponent(recordId)}`,\n).then((res) => {\n const rows = res?.data?.data ?? res?.data ?? [];\n return Array.isArray(rows) ? rows[0] : rows;\n});\n\n/**\n * resolveQuickCreateNoticeDetails — the two traceability lookups.\n *\n * FAIL OPEN, deliberately and on every branch: the warning message is the point\n * of this popup, the name and the creator are garnish. A directory that is down,\n * a record the list endpoint cannot return, a module name that does not resolve\n * — none of it may suppress the notice or throw into the form. Every await is\n * individually caught and degrades to an empty string.\n *\n * The creator falls back to the signed-in user because in this flow the creator\n * IS the current user: they pressed \"Add More\" seconds ago. That fallback is\n * therefore accurate, not a guess.\n *\n * Dependencies are injected (with real defaults) so this is testable without a\n * network or a browser.\n */\nexport async function resolveQuickCreateNoticeDetails({\n module,\n recordId,\n notice,\n displayField = '',\n deps = {},\n} = {}) {\n const {\n fetchRecord = defaultFetchRecord,\n fetchUserNameFn = fetchUserName,\n getUser = getStoredUser,\n } = deps;\n\n let record = null;\n if (module && recordId) {\n try {\n record = await fetchRecord(module, recordId);\n } catch {\n record = null; // fail open\n }\n }\n\n const recordName = notice?.showRecordName === false\n ? ''\n : pickRecordName(record, { recordNameField: notice?.recordNameField, displayField });\n\n let creatorName = '';\n if (notice?.showCreator !== false) {\n const creatorId = pickCreatorId(record);\n if (creatorId) {\n try {\n creatorName = str(await fetchUserNameFn(creatorId));\n } catch {\n creatorName = ''; // fail open\n }\n }\n if (!creatorName) creatorName = currentUserName(getUser);\n }\n\n return { recordName, creatorName };\n}\n","// QuickCreateEditField — the \"Add More\" / \"Edit\" affordance inside a select\n// dropdown. Fully admin-config driven (FormGroupField.quickCreate / quickEdit,\n// see the Go struct). When a select carries quickCreate, its dropdown grows an\n// \"Add More\" button that opens the target module's Add form in a modal; on a\n// successful create the host field refetches its options and selects the new\n// record. quickEdit adds an \"Edit\" button that opens the target module's Edit\n// form for a resolved record id (this field's own value, or a sibling field via\n// quickEditIdFrom — e.g. \"edit the SELECTED CLIENT to add a contact\" from the\n// job's Contact Person field). No module or field name is hardcoded here.\n//\n// A field may also carry `quickCreateNotice` (see ./quickCreateNotice.js): a\n// popup shown the instant a record is created HERE, warning that a brand-new\n// record is not yet configured. Because only a real quick-create reaches that\n// code path, the notice cannot fire for an existing record that was merely\n// selected.\n//\n// AddFormV1/EditFormV1 are pulled in with React.lazy so this module can be\n// imported by those same files without a static circular dependency.\nimport React, { Suspense, lazy, useMemo, useState } from 'react';\nimport { quickCreateLabel, quickEditLabel, quickModalTitle, humanizeModule } from './quickActionLabels';\nimport { Modal, Space, Spin, Button } from 'antd';\nimport { PlusOutlined, EditOutlined, ExclamationCircleFilled } from '@ant-design/icons';\nimport AppButton from '../AppButton';\nimport { normalizeQuickCreateNotice, resolveQuickCreateNoticeDetails } from './quickCreateNotice';\n\nconst AddFormV1 = lazy(() => import('../AddFormV1'));\nconst EditFormV1 = lazy(() => import('../EditFormV1'));\n\nconst empty = (v) => v === undefined || v === null || v === '';\nconst pathOf = (key) => (typeof key === 'string' && key.includes('.') ? key.split('.') : [key]);\n\n// Resolve the record id a quick-edit should open. Priority:\n// 1. field.quickEditIdFrom → a sibling field's value (row-scoped inside an\n// addRow row, else top-level; falls back to scopeValues, e.g. clientId).\n// 2. this field's own selected value (labeled selects carry {value}).\nfunction resolveEditId(field, form, name, scopeValues, ownValue) {\n const idFrom = field.quickEditIdFrom;\n if (idFrom) {\n const insideRow = Array.isArray(name) && name.length >= 3 && typeof name[1] === 'number';\n const abs = insideRow ? [...name.slice(0, 2), ...pathOf(idFrom)] : pathOf(idFrom);\n let v = form?.getFieldValue?.(abs);\n if (empty(v)) v = scopeValues?.[idFrom];\n return empty(v) ? null : v;\n }\n if (ownValue && typeof ownValue === 'object') return ownValue.value ?? null;\n return empty(ownValue) ? null : ownValue;\n}\n\n/**\n * useQuickField — returns { footer, modal } for a select FieldControl.\n * footer: JSX to append inside the Select's dropdownRender (the action buttons)\n * modal: JSX to render alongside the Select (the create/edit Modal)\n * onDone(kind, recordId) fires after a successful create/edit so the caller can\n * refetch options (and, for create, select the new record).\n */\nexport function useQuickField({ field, form, name, moduleName, scopeValues, ownValue, onDone }) {\n const [modal, setModal] = useState(null); // { mode: 'create'|'edit', module, recordId }\n // The post-create notice (see handleCreated below). Held here — above the\n // early return — because hooks may not be called conditionally.\n const [notice, setNotice] = useState(null); // { title, message, recordLabel, ... , recordName, creatorName }\n\n const quickCreate = Boolean(field?.quickCreate);\n const quickEdit = Boolean(field?.quickEdit);\n const createModule = field?.quickCreateModule || field?.lookupCollection || '';\n const editModule = field?.quickEditModule || field?.lookupCollection || '';\n\n const editId = useMemo(\n () => (quickEdit ? resolveEditId(field, form, name, scopeValues, ownValue) : null),\n // ownValue / sibling changes should re-resolve\n [quickEdit, field, form, name, scopeValues, ownValue],\n );\n\n if (!quickCreate && !quickEdit) return { footer: null, modal: null };\n\n const close = () => setModal(null);\n // quickCreateOnClick (opt-in): call a plain local function instead of\n // opening the generic module's Add form or navigating — for a create\n // flow the page already has its own custom modal/logic for. Checked\n // before quickCreateRoute; existing fields without either stay on\n // today's default modal behavior.\n const openCreate = () => {\n if (field?.quickCreateOnClick) {\n field.quickCreateOnClick();\n // This bypass opens the caller's own external modal, not the built-in\n // in-dropdown create flow — so unlike that flow, there's no reason to\n // keep the Select's dropdown open underneath it. Close it immediately\n // instead of letting it linger until the new modal's mask steals focus.\n document.activeElement?.blur?.();\n return;\n }\n if (field?.quickCreateRoute) {\n window.open(field.quickCreateRoute, '_blank', 'noopener,noreferrer');\n return;\n }\n createModule && setModal({ mode: 'create', module: createModule });\n };\n const openEdit = () => editModule && editId && setModal({ mode: 'edit', module: editModule, recordId: String(editId) });\n\n // ── The \"brand-new record\" notice ────────────────────────────────────────\n // WHY it hooks the quick-create success path and nothing else:\n // reaching this callback is only possible by having just SAVED a record\n // through the in-dropdown Add form. Selecting an existing option never\n // travels through here at all. That makes \"new records only\" a STRUCTURAL\n // guarantee of where the code sits, not a runtime test we have to keep\n // true. A heuristic (\"does this id look new?\", \"was it absent from the\n // options list?\") would be a second source of truth that drifts the first\n // time options are cached, paginated or server-filtered — so we\n // deliberately do NOT add one.\n //\n // WHY it fires now instead of after the outer form is submitted: the stated\n // purpose is to stop the recruiter proceeding as though a brand-new client\n // were fully configured. A warning shown after they finished the submission\n // is a receipt, not a guard. It must land while the decision to continue is\n // still ahead of them.\n const handleCreated = (newId) => {\n const config = normalizeQuickCreateNotice(field);\n if (!config) return;\n // Open immediately with whatever we know (nothing yet). The two lookups\n // below only ever ENRICH this; they can never delay or cancel it, which\n // is what \"fail open\" means here.\n setNotice({ ...config, recordName: '', creatorName: '' });\n resolveQuickCreateNoticeDetails({\n module: createModule,\n recordId: newId,\n notice: config,\n displayField: field?.displayField || '',\n })\n .then(({ recordName, creatorName }) => {\n setNotice((prev) => (prev ? { ...prev, recordName, creatorName } : prev));\n })\n .catch(() => { /* already open; the message is the important part */ });\n };\n\n const footer = (\n <div\n className=\"v1-quick-actions\"\n role=\"presentation\"\n onMouseDown={(e) => e.preventDefault()} // keep the select open while clicking\n style={{ display: 'flex', gap: 8, padding: '6px 8px', borderTop: '1px solid rgba(0,0,0,0.06)' }}\n >\n <Space size={8}>\n {quickCreate && (\n <Button type=\"link\" size=\"small\" icon={<PlusOutlined />} onClick={openCreate} style={{ paddingLeft: 0 }}>\n {quickCreateLabel(field, createModule)}\n </Button>\n )}\n {quickEdit && (\n <Button\n type=\"link\"\n size=\"small\"\n icon={<EditOutlined />}\n onClick={openEdit}\n disabled={!editId}\n title={!editId ? `Choose a ${humanizeModule(editModule) || 'record'} above first, then edit it here` : undefined}\n >\n {quickEditLabel(field, editModule)}\n </Button>\n )}\n </Space>\n </div>\n );\n\n const modalNode = modal ? (\n <Modal\n open\n title={quickModalTitle(modal.mode, field, modal.mode === 'create' ? createModule : editModule)}\n width=\"min(1080px, 96vw)\"\n footer={null}\n destroyOnClose\n maskClosable={false}\n onCancel={close}\n styles={{ body: { maxHeight: '78vh', overflowY: 'auto' } }}\n >\n <Suspense fallback={<div style={{ padding: 48, textAlign: 'center' }}><Spin /></div>}>\n {modal.mode === 'create' ? (\n <AddFormV1\n moduleName={modal.module}\n embedded\n breadcrumbItems={[]}\n onCancel={close}\n onSuccess={(newId) => { close(); handleCreated(newId); onDone?.('create', newId); }}\n />\n ) : (\n <EditFormV1\n moduleName={modal.module}\n recordId={modal.recordId}\n embedded\n breadcrumbItems={[]}\n onCancel={close}\n onSuccess={(rid) => { close(); onDone?.('edit', rid); }}\n />\n )}\n </Suspense>\n </Modal>\n ) : null;\n\n // Informational/warning notice. Rendered as a sibling of the create modal\n // (never nested inside it) so it survives that modal being destroyed on\n // close — the create form unmounts the moment it succeeds.\n const noticeNode = notice ? (\n <Modal\n open\n title={(\n <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>\n <ExclamationCircleFilled style={{ color: '#faad14' }} />\n {notice.title}\n </span>\n )}\n width=\"min(520px, 94vw)\"\n maskClosable={false}\n onCancel={() => setNotice(null)}\n footer={(\n <AppButton variant=\"primary\" type=\"primary\" onClick={() => setNotice(null)}>\n OK\n </AppButton>\n )}\n >\n <p style={{ marginTop: 0, marginBottom: 16 }}>{notice.message}</p>\n {/* Traceability pair. Each line is dropped when its lookup produced\n nothing — an empty \"Created by:\" is noise, not information (the\n same rule detail/userNames.js applies to unresolvable actors). */}\n {(notice.recordName || notice.creatorName) && (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'auto 1fr',\n gap: '6px 12px',\n padding: '10px 12px',\n borderRadius: 6,\n background: 'rgba(0,0,0,0.03)',\n }}\n >\n {notice.recordName && (\n <>\n <span style={{ color: 'rgba(0,0,0,0.55)' }}>{notice.recordLabel}</span>\n <strong>{notice.recordName}</strong>\n </>\n )}\n {notice.creatorName && (\n <>\n <span style={{ color: 'rgba(0,0,0,0.55)' }}>{notice.creatorLabel}</span>\n <strong>{notice.creatorName}</strong>\n </>\n )}\n </div>\n )}\n </Modal>\n ) : null;\n\n return {\n footer,\n modal: (modalNode || noticeNode) ? (<>{modalNode}{noticeNode}</>) : null,\n };\n}\n","// crossFieldRules — validation rules that compare a field against ANOTHER field,\n// shared by AddFormV1 and EditFormV1 so both forms enforce them identically.\n//\n// The historical cross-field rules (greaterThanField, dateAfterField, …) live\n// inline in each form and resolve the referenced key as a SIBLING: the same\n// addRow row when the rule sits inside one, the top level otherwise. That is\n// wrong for a rule that must reach OUT of its row — e.g. \"the experience typed\n// on a relevant-skill row may not exceed the candidate's overall experience\",\n// where the row field points at a top-level field.\n//\n// resolveScopedFieldPath fixes that by using the SAME scoping rule the render\n// side already applies to `showIf`: renderField builds a `prefixFor(key)`\n// closure that returns the row prefix ([listName, rowIndex]) only when the\n// referenced key is one of the row's own fields (opts.rowFieldKeys), and null\n// for anything else — which resolves to the top-level path. Passing that same\n// closure in here means a rule and a Show Condition can never disagree about\n// which field a key refers to.\n\n// Local emptiness check — mirrors the forms' own `empty` helper. Kept here so\n// this module has no import back into either form (they both import from it).\nconst isBlank = (v) => v === undefined || v === null || v === ''\n || (Array.isArray(v) && v.length === 0);\n\n/**\n * resolveScopedFieldPath — the antd name path a rule's referenced field key\n * resolves to.\n *\n * @param {Array|string} name the CURRENT field's absolute name path\n * @param {string} key the referenced field key (dot notation allowed)\n * @param {Function} [prefixFor] renderField's showIf prefix resolver\n * @returns {Array} absolute name path\n */\nexport function resolveScopedFieldPath(name, key, prefixFor) {\n const parts = String(key ?? '').includes('.') ? String(key).split('.') : [key];\n if (typeof prefixFor === 'function') {\n const prefix = prefixFor(key);\n return Array.isArray(prefix) && prefix.length ? [...prefix, ...parts] : parts;\n }\n // No prefix resolver (e.g. a repeat-scalar item): keep the historical\n // sibling-first behaviour so nothing that worked before changes.\n return Array.isArray(name) && name.length > 1 ? [...name.slice(0, -1), ...parts] : parts;\n}\n\n/**\n * maxFromFieldRule — `{ type: 'maxFromField', field: '<otherFieldKey>', message }`\n *\n * The value may not exceed the CURRENT value of another field. Generic: which\n * field caps which is admin config (Form Groups → field → validations), never\n * code. Fails OPEN whenever either side is blank or non-numeric — a validation\n * rule must never block a save it cannot actually evaluate.\n */\nexport function maxFromFieldRule({ label, message, rule = {}, value, form, name, prefixFor }) {\n const referencedKey = rule.field ?? rule.compareField ?? value;\n return {\n validator: async (_, input) => {\n if (!referencedKey || isBlank(input)) return Promise.resolve();\n const other = form?.getFieldValue?.(resolveScopedFieldPath(name, referencedKey, prefixFor));\n if (isBlank(other)) return Promise.resolve();\n const maximum = Number(other);\n const current = Number(input);\n if (Number.isNaN(maximum) || Number.isNaN(current)) return Promise.resolve();\n if (current <= maximum) return Promise.resolve();\n return Promise.reject(new Error(message ?? `${label} must not exceed ${referencedKey}`));\n },\n };\n}\n\n/**\n * maxFromFieldDependency — the absolute name path a maxFromField rule depends\n * on, so antd re-runs the rule when the referenced field changes.\n */\nexport function maxFromFieldDependency(rule = {}, name, prefixFor) {\n const referencedKey = rule.field ?? rule.compareField ?? rule.value;\n if (!referencedKey) return null;\n return resolveScopedFieldPath(name, referencedKey, prefixFor);\n}\n\nexport default { resolveScopedFieldPath, maxFromFieldRule, maxFromFieldDependency };\n","/**\n * maxCeiling — the pure core of the `maxFieldWithFallback` validation.\n *\n * The rule caps a numeric field against the FIRST usable value in an ordered,\n * admin-authored list of other fields (\"cap at the To value, else the From\n * value\"). Both AddFormV1 and EditFormV1 hold a thin antd validator around\n * these functions; everything decision-shaped lives here so it can be unit\n * tested without rendering a form (importing either form component pulls in\n * react-pdf, which dies on jsdom's missing DOMMatrix).\n *\n * Nothing here knows a module, group or field name — the list of compare\n * fields comes from the validation entry in Form Groups config.\n */\n\n/**\n * Resolve a compare-field reference to an absolute form name path, relative to\n * the field being validated. Kept identical to the behaviour both forms had\n * inline (they now delegate here):\n *\n * • DOTTED reference (\"jobClientRate.clientRateTo\") → ABSOLUTE path, i.e. it\n * reaches out of the validated field's own container. This is what lets a\n * field inside one container (candidateBudget.*) be capped by a value\n * parked in a different one — inside a Form.List row the row prefix is\n * preserved instead, so a per-row rule still resolves within its row.\n * • BARE reference (\"clientRateTo\") → sibling of the validated field, i.e.\n * the last path segment is swapped. A bare name can therefore never see a\n * top-level field from inside a container — use a dotted reference for that.\n */\nexport function resolveCompareFieldName(currentName, compareField) {\n const compareParts = String(compareField).split('.').filter(Boolean);\n if (!Array.isArray(currentName)) return compareParts.length > 1 ? compareParts : compareField;\n\n if (compareParts.length > 1) {\n // A DB rule may use `endDate` or `work_experience.endDate`. Preserve\n // the current Form.List row and avoid repeating the group segment.\n if (currentName.length >= 3 && typeof currentName[1] === 'number') {\n const listPrefix = currentName.slice(0, 2);\n const relativeParts = compareParts[0] === String(currentName[0])\n ? compareParts.slice(1)\n : compareParts;\n return [...listPrefix, ...relativeParts];\n }\n return compareParts;\n }\n\n const nextName = [...currentName];\n nextName[nextName.length - 1] = compareParts[0];\n return nextName;\n}\n\n/** \"a, b ,, c\" → ['a','b','c'] — the admin writes the list comma-separated. */\nexport function parseCompareFields(value) {\n return String(value ?? '')\n .split(',')\n .map((item) => item.trim())\n .filter(Boolean);\n}\n\n/**\n * Pick the ceiling: the first compare field that carries a USABLE maximum.\n *\n * Usable means \"a positive, finite number\". Empty/blank falls through (the\n * long-standing behaviour) and so does ZERO or a negative number, which is the\n * subtle part:\n *\n * A 0 ceiling means \"no ceiling configured\", NOT \"nothing is allowed\".\n * Rate ranges in this system are routinely persisted with the open end at 0\n * (submissions store candidateBudgetEnd: 0 on nearly every record; jobs with\n * a single-point budget leave clientBudgetEnd unset or 0). Treating that 0 as\n * a real maximum would reject EVERY value the user could type, with an error\n * they have no way to satisfy. The rest of the codebase already reads these\n * pairs the same way — see popups/definitions/others-assign/config.js\n * (`end > 0 ? end : start`) and financeApprovalEngine's `clientBudgetEnd ||\n * clientBudgetStart`. So a non-positive candidate is skipped and the next\n * entry in the list is tried; if none qualifies there is simply no cap.\n *\n * @param {string[]} compareFields ordered field references\n * @param {(field: string) => any} readValue reads the current value of one\n * @returns {{ field: string, maximum: number } | null}\n */\nexport function selectCeiling(compareFields, readValue) {\n for (const field of compareFields ?? []) {\n const raw = readValue(field);\n if (raw === undefined || raw === null || raw === '') continue;\n const maximum = Number(raw);\n if (!Number.isFinite(maximum) || maximum <= 0) continue;\n return { field, maximum };\n }\n return null;\n}\n\n/**\n * The whole decision: is `input` within the first usable ceiling?\n * Fails open (ok:true) for a blank input, a non-numeric input, and when no\n * compare field carries a usable ceiling.\n *\n * @returns {{ ok: boolean, field: string|null, maximum: number|null }}\n */\nexport function checkMaxWithFallback({ input, compareFields, readValue }) {\n const pass = { ok: true, field: null, maximum: null };\n if (input === undefined || input === null || input === '') return pass;\n\n const ceiling = selectCeiling(compareFields, readValue);\n if (!ceiling) return pass;\n\n const inputNumber = Number(input);\n if (!Number.isFinite(inputNumber)) return pass;\n\n return {\n ok: inputNumber <= ceiling.maximum,\n field: ceiling.field,\n maximum: ceiling.maximum,\n };\n}\n","/**\n * contextPrefill — pure helpers for the cross-module context prefill\n * (`field.prefillFromModule` + `field.prefillFrom`).\n *\n * A field can inherit its value from a DIFFERENT module's record than the form\n * is editing/creating — e.g. a submission field fed by the JOB it is raised\n * against. The two forms differ only in where the linked record's id comes\n * from:\n *\n * AddFormV1 the URL linkage (?jobId= / ?candidateId= on Quick Submit)\n * EditFormV1 the record's OWN stored ids, surfaced by getFormGroups as\n * `recordMeta` (every top-level \"*Id\" key of the record)\n *\n * Both shapes are plain { someId: value } maps, so one resolver serves both.\n * No module or field name is hardcoded in either form — the alias table below\n * is the single place where a module's conventional id key is spelled out.\n */\n\nimport { getDeep } from './payloadTransformer';\n\n// Extra id keys a module is known by, beyond the derived `<module>Id` /\n// `<singular>Id`. Submissions/candidates historically link by \"applicantId\".\nconst EXTRA_ID_KEYS = {\n candidates: ['applicantId'],\n};\n\n/** Candidate id keys for a (normalized, plural) module key, most specific first. */\nexport function contextIdKeys(moduleKey) {\n const key = String(moduleKey ?? '').trim();\n if (!key) return [];\n const singular = key.endsWith('s') ? key.slice(0, -1) : key;\n return [...new Set([`${singular}Id`, `${key}Id`, ...(EXTRA_ID_KEYS[key] ?? [])])];\n}\n\n/**\n * Resolve the linked record id for `moduleKey` from a linkage/recordMeta map.\n * Values may be plain ids or { id } / { value } objects (recordMeta serializes\n * ObjectIDs as hex strings, but detail payloads sometimes carry objects).\n */\nexport function contextRecordId(moduleKey, source) {\n if (!source) return '';\n const idOf = (v) => (v && typeof v === 'object' ? (v.id ?? v.value ?? '') : (v ?? ''));\n for (const key of contextIdKeys(moduleKey)) {\n const id = String(idOf(source[key]) || '');\n if (id) return id;\n }\n return '';\n}\n\n/** Distinct normalized modules referenced by any field's prefillFromModule. */\nexport function contextPrefillModules(groups, normalizeModule) {\n const wanted = new Set();\n (groups ?? []).forEach((group) => (group.fields ?? []).forEach((field) => {\n if (field?.prefillFromModule) wanted.add(normalizeModule(field.prefillFromModule));\n }));\n return [...wanted];\n}\n\n/**\n * EDIT-side gate: which fields may a cross-module prefill write on an EXISTING\n * record?\n *\n * Only NON-PERSISTENT CARRIER fields (payloadMode \"skip\"). A carrier holds a\n * value that is never stored on the record, so there is nothing on the record\n * for it to contradict — it has to be re-derived on every edit or the rule it\n * feeds would be silently inert there. A field that IS stored keeps whatever\n * the record holds: re-pulling the source module's current value on edit would\n * silently overwrite what the user saved (e.g. a rate currency deliberately\n * changed after the job was raised). Config-driven, no field names.\n */\nexport function isNonPersistentCarrier(field) {\n return field?.payloadMode === 'skip';\n}\n\n/** getFormGroups responses come back in a few envelopes — normalize to an array. */\nexport function extractContextGroups(response) {\n if (Array.isArray(response)) return response;\n if (Array.isArray(response?.groups)) return response.groups;\n if (Array.isArray(response?.data)) return response.data;\n if (Array.isArray(response?.data?.groups)) return response.data.groups;\n return [];\n}\n\n/**\n * Flatten a form-groups response (scalar fields carry their stored `value`)\n * into { fieldKey: value }, indexed by both field key and payloadKey.\n */\nexport function flattenContextRecord(groups) {\n const record = {};\n (groups ?? []).forEach((group) => {\n if (group?.addRow) return;\n (group?.fields ?? []).forEach((field) => {\n if (!field?.field || field.value === undefined || field.value === null) return;\n record[field.field] = field.value;\n if (field.payloadKey && field.payloadKey !== field.field) record[field.payloadKey] = field.value;\n });\n });\n return record;\n}\n\n/** Read a field's value out of a context record: prefillFrom → field → payloadKey. */\nexport function pickContextValue(record, field) {\n if (!record || !field) return undefined;\n for (const path of [field.prefillFrom, field.field, field.payloadKey]) {\n if (!path) continue;\n let value = record[path];\n if (value === undefined || value === null) value = getDeep(record, path);\n if (value !== undefined && value !== null) return value;\n }\n return undefined;\n}\n","// prefillWhenRules — the pure decision engine behind `field.prefillWhen`\n// (\"Conditional Default\" in Form Groups), shared by AddFormV1 and EditFormV1 so\n// both forms can never drift apart on what a rule means.\n//\n// A prefillWhen rule says \"when the field named `field` holds `value`, put\n// `setValue` into THIS field\" — e.g. contractType=\"W2\" drops the default\n// employer record id into employerId. Two admin-config extensions live here:\n//\n// • rule.clearValue — a matching rule CLEARS this field instead of setting\n// it (e.g. contractType=\"C2C\" must not keep the W2 default employer\n// sitting there). Absent/false = today's set behaviour, so every rule ever\n// saved keeps working untouched.\n// • clearOnMismatch — field-level (`field.prefillWhenReset`): once NO rule\n// matches any more, revert this field to empty rather than stranding the\n// default a rule previously applied.\n//\n// WHY the lastApplied bookkeeping: the form store cannot tell \"this id is the\n// W2 default we injected\" from \"the user deliberately picked this employer\" —\n// both are just a string in the field. So the caller remembers the exact value\n// the last matching rule wrote, and a mismatch-clear only fires while the field\n// STILL holds precisely that value. The moment the user overrides it, the value\n// is theirs and we never touch it again. TRADEOFF, deliberately documented: a\n// user who manually re-picks the very same record the rule had set is\n// indistinguishable from the rule's own write, and that value WILL be cleared\n// on mismatch. Clearing an identical value is the benign side of the trade —\n// the alternative (never clearing) is the bug this exists to fix.\n//\n// WHY clears are suppressed on the FIRST evaluation: on the edit form the very\n// first tick sees the record's own stored contractType. If that value matches a\n// clearValue rule, an unguarded clear would wipe a stored employer the user\n// never touched, purely from opening the form. A clear must always be the\n// consequence of the user CHANGING the watched field, never of a page load.\n// (On the add form the field is empty at that point, so nothing is lost.)\n//\n// Nothing here knows any module, field or value name — all of it is admin data.\n\nconst asKey = (v) => String(v ?? '');\n\n/**\n * prefillWhenWatchFields — the distinct field keys a rule set references, in\n * rule order. Rules may point at DIFFERENT fields, so callers must watch each\n * one, not just the first rule's.\n *\n * @param {Array} rules field.prefillWhen\n * @returns {string[]}\n */\nexport function prefillWhenWatchFields(rules) {\n const seen = new Set();\n const out = [];\n (Array.isArray(rules) ? rules : []).forEach((r) => {\n const key = r?.field;\n if (!key || seen.has(key)) return;\n seen.add(key);\n out.push(key);\n });\n return out;\n}\n\n/**\n * prefillWhenWatchPaths — resolves each referenced field key to an absolute\n * antd name path using the SAME row-vs-top-level scoping `showIf` uses.\n *\n * Inside an addRow group, a rule referencing one of the group's OWN fields\n * resolves to [listName, rowIndex, key]; a rule referencing anything else (the\n * top-level contractType read from inside the repeatable employer rows) must\n * resolve to the top-level path. renderField's `prefixFor(key)` closure already\n * encodes exactly that decision — passing it in means a Conditional Default and\n * a Show Condition can never disagree about which field a key names. Getting\n * this wrong is what made \"W2/C2C not working\" the first time round.\n *\n * @param {Array} rules field.prefillWhen\n * @param {Array|string} name this field's own absolute name path\n * @param {Function} resolvePath (name, key, prefixFor) => absolute path\n * @param {Function} [prefixFor] renderField's showIf prefix resolver\n * @returns {{field: string, path: Array}[]}\n */\nexport function prefillWhenWatchPaths(rules, name, resolvePath, prefixFor) {\n return prefillWhenWatchFields(rules).map((field) => ({\n field,\n path: resolvePath(name, field, prefixFor),\n }));\n}\n\n/**\n * prefillWhenMatches — does THIS rule's condition hold right now?\n *\n * The single definition of \"a rule matches\", exported so the option side\n * (`prefillWhenExclusive`, see optionConstraints.js) can never drift from the\n * value side: an option is offered on exactly the ticks the rule would fire.\n *\n * @param {object} rule one field.prefillWhen entry\n * @param {object} watched { [referencedFieldKey]: liveValue }\n * @returns {boolean}\n */\nexport function prefillWhenMatches(rule, watched = {}) {\n if (!rule || !rule.field) return false;\n return asKey(watched[rule.field]) === asKey(rule.value);\n}\n\n/**\n * resolvePrefillWhen — decides what should happen to the target field on this\n * tick. Pure: it never touches the antd store, the caller applies the outcome.\n *\n * @param {object} args\n * @param {Array} args.rules field.prefillWhen\n * @param {object} args.watched { [referencedFieldKey]: liveValue }\n * @param {*} args.current the target field's live value\n * @param {*} args.lastApplied value the last matching SET rule wrote (undefined = none)\n * @param {boolean} args.clearOnMismatch field.prefillWhenReset (default on)\n * @param {boolean} args.initial true on the very first evaluation\n * @returns {{action: 'set'|'clear'|'none', value?: *}}\n */\nexport function resolvePrefillWhen({\n rules,\n watched = {},\n current,\n lastApplied,\n clearOnMismatch = true,\n initial = false,\n} = {}) {\n const list = Array.isArray(rules) ? rules : [];\n // First matching rule wins — same precedence the single-path watcher had.\n const match = list.find((r) => prefillWhenMatches(r, watched));\n\n if (match) {\n if (match.clearValue) {\n // A clear rule that fires on page load would delete stored data (see the\n // header comment), so the first tick only ever arms the watcher.\n return initial ? { action: 'none' } : { action: 'clear' };\n }\n return { action: 'set', value: match.setValue };\n }\n\n // No rule matches any more. Only revert a value WE put there.\n if (clearOnMismatch && lastApplied !== undefined && asKey(current) === asKey(lastApplied)) {\n return { action: 'clear' };\n }\n return { action: 'none' };\n}\n","// optionConstraints — config-driven narrowing of a select's options by the live\n// value of ANOTHER field, shared by AddFormV1 and EditFormV1.\n//\n// Config key (NEW — must exist on the Go FormField struct or the admin save API\n// drops it): `field.optionsFromField: \"<otherFieldKey>\"`.\n//\n// Use case it was built for: a candidate's rate UNIT may not differ from the\n// unit the JOB was raised in. The job's unit is already brought onto the form by\n// the existing `prefillFromModule` mechanism, so with this constraint the whole\n// restriction is config — no module, field or value name is written in code.\n//\n// The referenced field is resolved with the SAME row-vs-top-level scoping the\n// render side uses for `showIf` (row first, top level as fallback) — see\n// FieldControl, which watches both paths and prefers the row value when set.\n\nimport { prefillWhenMatches } from './prefillWhenRules';\n\nconst asKey = (v) => String(v ?? '').trim().toLowerCase();\n\n// Every comparable form of a constraint value: a plain scalar, an antd\n// labelInValue object ({value,label}) or a reference object ({id,name}), and\n// arrays of any of those (a multi-select constraint narrows to a SET).\nfunction constraintKeys(constraintValue) {\n const list = Array.isArray(constraintValue) ? constraintValue : [constraintValue];\n const keys = [];\n list.forEach((item) => {\n if (item === undefined || item === null || item === '') return;\n if (typeof item === 'object') {\n [item.value, item.id, item._id, item.label, item.name].forEach((v) => {\n if (v !== undefined && v !== null && v !== '') keys.push(asKey(v));\n });\n return;\n }\n keys.push(asKey(item));\n });\n return keys;\n}\n\n/**\n * narrowOptionsByConstraint — the options a select may offer given the current\n * value of the field named by `optionsFromField`.\n *\n * Matching is case-insensitive on the option's value OR its label, so a stored\n * \"hr\" narrows to the option labelled \"Hourly\" with value \"hr\" either way.\n *\n * FAILS OPEN in both directions:\n * • no constraint configured / constraint field still empty → all options\n * • the constraint matches NO option → all options\n * A narrowing that resolves to an empty dropdown would leave the user unable to\n * fill a (possibly mandatory) field because of a data mismatch they cannot see\n * or fix, which is strictly worse than showing the unrestricted list.\n */\nexport function narrowOptionsByConstraint(options = [], constraintValue) {\n const keys = constraintKeys(constraintValue);\n if (!keys.length || !Array.isArray(options) || options.length === 0) return options;\n const wanted = new Set(keys);\n const narrowed = options.filter((option) => {\n if (option === null || option === undefined) return false;\n if (typeof option !== 'object') return wanted.has(asKey(option));\n return wanted.has(asKey(option.value)) || wanted.has(asKey(option.label));\n });\n return narrowed.length > 0 ? narrowed : options;\n}\n\n// ---------------------------------------------------------------------------\n// prefillWhenExclusive — \"a value a Conditional Default would SET is exclusive\n// to that rule's condition\".\n//\n// Config key (NEW — must exist on the Go FormField struct or the admin save API\n// drops it): `field.prefillWhenExclusive: true`. Default OFF, so every field\n// configured before this existed offers exactly the options it does today.\n//\n// It reuses the EXISTING `field.prefillWhen` rules rather than introducing a\n// second list, so the value that must be hidden can never fall out of step with\n// the value that gets auto-filled — there is only one copy of it in config.\n//\n// prefillWhen: [{ field: 'contractType', value: 'W2', setValue: '<id>' }]\n// contractType = W2 → the rule matches → that option IS offered (and the\n// existing watcher auto-selects it, as today)\n// contractType = C2C → no rule matches → that option is REMOVED\n// contractType = empty → no rule matches → that option is REMOVED\n//\n// The use case: the value a W2 rule injects is the internal/own company; on any\n// other contract type the employer must be an external vendor, so offering the\n// internal one is wrong — not merely a bad default. Nothing here knows that:\n// the rule, the field and the value are all admin data.\n//\n// DELIBERATE DIVERGENCE from narrowOptionsByConstraint's fail-open rule: this\n// does NOT restore the full list when the exclusion empties it. Failing open on\n// a NARROWING is right (a data mismatch the user cannot see must not block a\n// mandatory field), but failing open on an EXCLUSION would re-offer precisely\n// the value the admin declared unofferable, i.e. reintroduce the bug. An empty\n// list is the honest answer — and these lookups carry a quick-create button, so\n// the user still has a way forward. Config-level fail-open is kept: flag off,\n// no rules, or a rule with no setValue all leave the options untouched.\n\n// Every option key one rule claims: the value it sets and, when the admin UI\n// stored one, its human label — so a rule whose setValue is a record id still\n// matches an option carrying that record's label, and vice versa. Mirrors\n// constraintKeys' value-OR-label matching above.\nfunction ruleOptionKeys(rule) {\n const keys = [];\n [rule?.setValue, rule?.setValueLabel].forEach((v) => {\n if (v === undefined || v === null || v === '') return;\n keys.push(asKey(v));\n });\n return keys;\n}\n\n/**\n * exclusivePrefillBlockedKeys — the option keys that must NOT be offered right\n * now, given the rule set and the live values of the fields it references.\n *\n * A key claimed by a rule that DOES match is always allowed, even if another\n * (non-matching) rule claims the same key — one live reason to offer a value is\n * enough.\n *\n * Rules that CLEAR (`rule.clearValue`) set no value at all, so they claim\n * nothing and never hide an option.\n *\n * @param {Array} rules field.prefillWhen\n * @param {object} watched { [referencedFieldKey]: liveValue }\n * @returns {string[]}\n */\nexport function exclusivePrefillBlockedKeys(rules, watched = {}) {\n const list = Array.isArray(rules) ? rules : [];\n const blocked = new Set();\n const allowed = new Set();\n list.forEach((rule) => {\n if (!rule || rule.clearValue) return;\n const keys = ruleOptionKeys(rule);\n if (!keys.length) return;\n const target = prefillWhenMatches(rule, watched) ? allowed : blocked;\n keys.forEach((key) => target.add(key));\n });\n allowed.forEach((key) => blocked.delete(key));\n return [...blocked];\n}\n\n/**\n * stripExclusivePrefillOptions — the options a control may offer once every\n * currently-inapplicable Conditional Default value has been removed.\n *\n * Case-insensitive on the option's value OR its label, exactly like\n * narrowOptionsByConstraint, so it works whether the option list is a lookup\n * (value = record id) or a static list (value = label).\n *\n * Composes with narrowOptionsByConstraint — pass its output in.\n *\n * @param {Array} options the already-narrowed option list\n * @param {Array} rules field.prefillWhen (only when the flag is on)\n * @param {object} watched { [referencedFieldKey]: liveValue }\n */\nexport function stripExclusivePrefillOptions(options = [], rules, watched) {\n const blocked = exclusivePrefillBlockedKeys(rules, watched);\n if (!blocked.length || !Array.isArray(options) || options.length === 0) return options;\n const deny = new Set(blocked);\n return options.filter((option) => {\n if (option === null || option === undefined) return true;\n if (typeof option !== 'object') return !deny.has(asKey(option));\n return !(deny.has(asKey(option.value)) || deny.has(asKey(option.label)));\n });\n}\n\nexport default {\n narrowOptionsByConstraint,\n exclusivePrefillBlockedKeys,\n stripExclusivePrefillOptions,\n};\n","// optionRowFilters — config-driven, CLIENT-SIDE narrowing of the RAW rows a\n// lookup dropdown returned, shared by AddFormV1 and EditFormV1.\n//\n// It runs on the raw `/admin/lookup-dropdown-values` rows ({label, value,\n// extraData}) BEFORE they are normalized into antd options, because every\n// decision here is made from `extraData` — the values the backend was asked to\n// carry alongside each row via `extraFields`.\n//\n// ── Why client-side at all ───────────────────────────────────────────────────\n// The proper mechanism is `field.lookupFilters` (multi-condition, evaluated\n// server-side). It IS configured on the Jobs \"Assign To\" field, but the\n// currently DEPLOYED backend predates that key and drops it while reading the\n// config, so the dropdown falls back to \"everybody\". `extraFields` however IS\n// supported by that build (the employer→recruiter `optionsRequireExtraField`\n// feature uses it live), so the same restriction can be expressed as data on\n// each row and applied in the browser until the backend ships.\n//\n// BOTH filters will be active once the backend ships. They are deliberately\n// written to express the SAME rule, so the result is identical rather than\n// contradictory:\n//\n// server lookupFilters client keys (this module)\n// ------------------------------------------ -----------------------------\n// roleId in (roles where roleName in [ROLES]) optionsRequireExtraField:\n// \"roleId\"\n// optionsRequireExtraValue:\n// \"<those roleIds>\"\n// legacyUserId in (users whose reportingId optionsHierarchyParentField:\n// chains up to currentUserId, recursive) \"reportingId\"\n// optionsHierarchySelfFrom:\n// \"currentUserId\"\n//\n// Both narrow to the same set, and an AND of a set with itself is that set —\n// so the dropdown shows the same rows whether one or both are in force. The\n// seeder (cmd/wire-jobs-assignto-lookup) writes both sides from ONE role list\n// so they can never drift apart in config either.\n//\n// ── Config keys (all must exist on the Go FormField struct or the admin save\n// API silently drops them) ─────────────────────────────────────────────────\n// optionsRequireExtraField — extraData key to test (e.g. \"roleId\")\n// optionsRequireExtraValue — NEW. Allowed value(s), single or\n// comma-separated. ABSENT ⇒ today's exact\n// `=== true` semantics, unchanged.\n// optionsHierarchyParentField — NEW. extraData key holding each row's PARENT\n// id in the same id space as the row's value\n// (e.g. \"reportingId\").\n// optionsHierarchySelfFrom — NEW. Identity token naming whose downline to\n// keep (e.g. \"currentUserId\").\n//\n// Nothing here knows about jobs, assignedTo, recruiters, roleId or reportingId:\n// every name above arrives as config.\n\n// Depth cap for the parent walk. Well beyond any real org chart, and the\n// visited-set below already stops cycles — this is a second, unconditional\n// backstop so a malformed chain can never spin.\nconst MAX_HIERARCHY_DEPTH = 64;\n\n// Identity tokens resolvable in the BROWSER. The server-side lookupFilters\n// resolve `currentUserId` from the authenticated request; client-side the same\n// value comes from localStorage `userId` — the identical source\n// ZINNEXT-V2's jobConfig.js / useVisibleTabs use for \"is this me?\".\n// An unknown token is NOT silently treated as \"no filter applied without\n// saying so\": resolveIdentity returns '' and the caller warns and skips.\nexport function resolveIdentity(token) {\n const key = String(token ?? '').trim();\n if (key !== 'currentUserId') return '';\n try {\n if (typeof localStorage === 'undefined') return '';\n return String(localStorage.getItem('userId') ?? '').trim();\n } catch {\n return '';\n }\n}\n\nconst asKey = (v) => String(v ?? '').trim().toLowerCase();\nconst filled = (v) => v !== undefined && v !== null && String(v).trim() !== '';\n\n/** The allowed-value set from a comma-separated (or array) config value. */\nfunction allowedValueKeys(raw) {\n const list = Array.isArray(raw) ? raw : String(raw ?? '').split(',');\n return new Set(list.map(asKey).filter((v) => v !== ''));\n}\n\n/**\n * extraDataArrived — did the backend actually send this key?\n *\n * TRUE when at least one row carries a non-empty value at `key`. This is the\n * fail-safe pivot (see filterLookupOptionRows): a deployed build that ignores\n * `extraFields` returns rows with no extraData at all, and filtering on data\n * that never arrived would empty the dropdown for a reason no admin can see.\n * \"Present but nothing matches\" is a legitimate empty and IS honoured.\n */\nexport function extraDataArrived(rows, key) {\n if (!key || !Array.isArray(rows) || rows.length === 0) return false;\n return rows.some((row) => filled(row?.extraData?.[key]));\n}\n\n/**\n * matchesRequiredExtra — the value test for ONE row.\n *\n * With no `optionsRequireExtraValue` this is byte-for-byte the old behaviour:\n * strictly `=== true` (used live by \"only list a VERIFIED recruiter\").\n * With one, it is a LOOSE string comparison so a stored numeric 10 matches a\n * configured \"10\" — the lookup API may hand back either, depending on whether\n * the value survived an aggregation projection as a number or a string.\n */\nexport function matchesRequiredExtra(row, key, allowedRaw) {\n const actual = row?.extraData?.[key];\n if (!filled(allowedRaw)) return actual === true;\n const allowed = allowedValueKeys(allowedRaw);\n if (allowed.size === 0) return actual === true;\n return allowed.has(asKey(actual));\n}\n\n/**\n * buildParentMap — { rowValueKey: parentValueKey } from the returned rows.\n *\n * The map is built from the ROWS THEMSELVES, so a transitive chain can only be\n * proven through people who are in the returned page of results. That is the\n * intended semantics here: the option list is what we are filtering.\n */\nexport function buildParentMap(rows, parentField) {\n const map = new Map();\n (Array.isArray(rows) ? rows : []).forEach((row) => {\n const self = asKey(row?.value);\n if (!self) return;\n map.set(self, asKey(row?.extraData?.[parentField]));\n });\n return map;\n}\n\n/**\n * isInDownline — does walking parent links from `startValue` reach `selfId`?\n *\n * CYCLE GUARD: two independent stops.\n * 1. `seen` — a node revisited means the chain looped (A→B→A, or a\n * self-referencing reportingId pointing at its own row); return false.\n * 2. MAX_HIERARCHY_DEPTH — an unconditional iteration cap, so even a map\n * mutated mid-walk or an unforeseen shape cannot spin the browser.\n *\n * SELF-INCLUSION — DECIDED: the signed-in user does NOT appear in their own\n * list. The walk starts at the row's PARENT, so a row whose value equals\n * selfId only survives if it also reports (transitively) to itself, which the\n * cycle guard rejects. Rationale: the configured rule is \"users who report to\n * me\", and I do not report to myself; a manager assigning work picks from\n * their team. It also keeps this filter identical to the server-side\n * `lookupFilters` condition (`reportingId` chains up from currentUserId),\n * which likewise never yields the signed-in user's own row — the two filters\n * must agree exactly or the union/intersection of the two would differ by one\n * row depending on which backend is deployed.\n */\nexport function isInDownline(startValue, parentMap, selfId) {\n const self = asKey(selfId);\n if (!self) return false;\n const seen = new Set();\n let current = asKey(startValue);\n if (!current) return false;\n seen.add(current);\n for (let depth = 0; depth < MAX_HIERARCHY_DEPTH; depth += 1) {\n const parent = parentMap.get(current);\n if (!parent) return false;\n if (parent === self) return true;\n if (seen.has(parent)) return false; // cycle\n seen.add(parent);\n current = parent;\n }\n return false;\n}\n\n/**\n * lookupExtraFieldKeys — every extraData key the request must ask for.\n *\n * The caller passes this to the `extraFields` query param instead of\n * `field.extraFields` alone, so configuring a filter key is enough: the admin\n * cannot forget to also list it under extraFields and get a silently empty (or\n * silently unfiltered) dropdown. Explicit `field.extraFields` entries (used by\n * autofillFrom) are preserved and come first; order is stable and de-duped.\n */\nexport function lookupExtraFieldKeys(field = {}) {\n const keys = [];\n const push = (k) => {\n const key = String(k ?? '').trim();\n if (key && !keys.includes(key)) keys.push(key);\n };\n (Array.isArray(field.extraFields) ? field.extraFields : []).forEach(push);\n push(field.optionsRequireExtraField);\n push(field.optionsHierarchyParentField);\n return keys;\n}\n\n/** Does this field configure any row filter at all? */\nexport function hasOptionRowFilters(field = {}) {\n return Boolean(field.optionsRequireExtraField || field.optionsHierarchyParentField);\n}\n\n/**\n * filterLookupOptionRows — the whole client-side restriction, in order:\n * A. value match on one extraData key\n * B. keep only the signed-in user's downline\n * Both are optional and independent; configuring neither returns `rows` as-is.\n *\n * FAIL-SAFE (deliberate asymmetry, see the requirement it was built for):\n * • data NEVER ARRIVED (no row carries the configured key, or the identity\n * token cannot be resolved) → SKIP that filter and log ONE warning naming\n * the field. An admin must be able to tell \"nobody reports to me\"\n * (legitimate empty) from \"the deployed build ignored extraFields\"\n * (broken), and an unexplained empty dropdown hides a mandatory field\n * behind a data problem the user cannot see or fix.\n * • data ARRIVED and simply nothing matches → return the empty list\n * faithfully. Failing open there would re-offer exactly the rows the admin\n * declared off-limits, i.e. reintroduce the bug.\n *\n * @param {Array} rows raw lookup rows ({label, value, extraData})\n * @param {object} field the field config\n * @param {object} opts { source: 'AddFormV1' | 'EditFormV1' } for the warning\n * @returns {Array} the rows to keep\n */\nexport function filterLookupOptionRows(rows, field = {}, opts = {}) {\n if (!Array.isArray(rows) || rows.length === 0) return rows;\n const where = opts.source ? `[${opts.source}]` : '[optionRowFilters]';\n const name = field.field ?? field.label ?? '(unnamed field)';\n const warn = typeof opts.warn === 'function'\n ? opts.warn\n : (msg) => { if (typeof console !== 'undefined') console.warn(msg); };\n let out = rows;\n\n // A — value match.\n const valueKey = String(field.optionsRequireExtraField ?? '').trim();\n if (valueKey) {\n if (!extraDataArrived(out, valueKey)) {\n warn(`${where} \"${name}\": option filter SKIPPED — no option carried extraData[\"${valueKey}\"], `\n + 'so the value filter could not be applied (the lookup API returned no such extra field). '\n + 'Showing the unfiltered list; this is NOT \"nothing matched\".');\n } else {\n out = out.filter((row) => matchesRequiredExtra(row, valueKey, field.optionsRequireExtraValue));\n }\n }\n\n // B — hierarchy (downline of the signed-in user).\n const parentField = String(field.optionsHierarchyParentField ?? '').trim();\n if (parentField) {\n const token = String(field.optionsHierarchySelfFrom ?? '').trim() || 'currentUserId';\n const selfId = resolveIdentity(token);\n if (!selfId) {\n warn(`${where} \"${name}\": hierarchy filter SKIPPED — identity \"${token}\" could not be resolved `\n + '(no signed-in user id available). Showing the list unrestricted by reporting line.');\n } else if (!extraDataArrived(rows, parentField)) {\n warn(`${where} \"${name}\": hierarchy filter SKIPPED — no option carried extraData[\"${parentField}\"], `\n + 'so the reporting chain could not be walked (the lookup API returned no such extra field). '\n + 'Showing the list unrestricted by reporting line; this is NOT \"nobody reports to you\".');\n } else {\n // The chain is walked over ALL returned rows, not the value-filtered\n // ones: an intermediate manager may hold a role the value filter\n // excludes (a recruiter reporting to a LEAD RECRUITER reporting to me),\n // and dropping that link first would sever a chain that genuinely\n // reaches me. This also matches the server-side condition, which\n // evaluates the two conditions independently over the whole collection.\n const parentMap = buildParentMap(rows, parentField);\n out = out.filter((row) => isInDownline(row?.value, parentMap, selfId));\n }\n }\n\n return out;\n}\n\nexport default {\n filterLookupOptionRows,\n lookupExtraFieldKeys,\n hasOptionRowFilters,\n matchesRequiredExtra,\n extraDataArrived,\n buildParentMap,\n isInDownline,\n resolveIdentity,\n};\n","// fieldTooltip — the single resolver for a field's admin-set help tooltip,\n// shared by AddFormV1 and EditFormV1.\n//\n// Two config shapes exist and both are honoured:\n// • `field.tooltip` (NEW key — one string, always shown)\n// • `field.infoEnabled` + `field.infoText` (the original toggle + text pair)\n//\n// Before this, tooltip text only ever reached the screen through FieldLabel, so\n// a field rendered WITHOUT a label (an inline/combined box, or a group that\n// prints one shared header row) could never carry one. Both forms now fall back\n// to wrapping the control itself for those, so ANY control type can get an\n// admin-set tooltip — which is the whole point: the text is config, the\n// capability is code.\n\nconst blank = (v) => v === undefined || v === null || String(v).trim() === '';\nconst truthy = (v) => v === true || v === 1 || v === '1' || v === 'true';\n\n/** @returns {string} the tooltip text for a field, or '' when it has none. */\nexport function fieldTooltipText(field) {\n if (!blank(field?.tooltip)) return String(field.tooltip);\n if (truthy(field?.infoEnabled) && !blank(field?.infoText)) return String(field.infoText);\n return '';\n}\n\nexport default { fieldTooltipText };\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// checkPermission() is the CURRENT gate — it resolves against the `can`\n// function from src/hooks/usePermissions.js (backed by GET /me/permissions,\n// see src/contexts/PermissionContext.jsx), which every consumer must call\n// usePermissions() to obtain and pass in. roleAllowsAction() below is the\n// OLD, localStorage.menuPermission-based gate — kept only for isActionValueAllowed's\n// row-level (record.actionPermission.<key>) use, which is a separate,\n// per-record mechanism unrelated to role permissions.\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// Deprecated — localStorage.menuPermission-based gate, superseded by\n// checkPermission()/usePermissions(). No remaining call sites; kept only so a\n// stray import doesn't break until it's confirmed unused everywhere.\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 moduleKey = configuredModule || String(moduleName || '').replace(/s$/i, '');\n const modulePermissions = permissions[moduleKey]\n ?? permissions[String(moduleName || '')]\n ?? permissions[String(moduleName || '').replace(/s$/i, '')]\n ?? {};\n return isActionValueAllowed(modulePermissions[configuredKey]);\n } catch {\n return true;\n }\n}\n","// Shared \"After Submit\" navigation resolver used by AddFormV1/EditFormV1.\n// Admin-configured per FormGroup (group.afterSubmit — see FormGroupsSection.jsx),\n// generic across every module/project: no module name is ever referenced here.\n\n// Picks the configured behavior from the group with the lowest `order` that\n// declares one. Returns null when no group configures it, so callers fall\n// back to their existing (pre-feature) navigation — unchanged behavior.\nexport function resolveAfterSubmit(groups) {\n const withConfig = (groups ?? [])\n .filter((g) => g?.afterSubmit?.mode)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n return withConfig[0]?.afterSubmit ?? null;\n}\n\n// Resolves a configured route template (e.g. \"/trainers/:id\") against the\n// created/updated record id and the submitted field values, and rejects\n// anything that isn't a safe in-app path (blocks open-redirect / javascript:\n// / data: payloads an admin could otherwise paste into the target field).\nexport function safeNavTarget(template, recordId, values) {\n const raw = String(template ?? '').trim();\n if (!raw || !raw.startsWith('/') || raw.startsWith('//')) return null;\n if (/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null; // any \"scheme:\" prefix, e.g. javascript:, data:\n return raw.replace(/:([A-Za-z_][\\w]*)/g, (_, key) => {\n const value = key === 'id' ? recordId : values?.[key];\n return encodeURIComponent(value ?? '');\n });\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// backNav — \"take me back to where I actually was\".\n//\n// THE BUG THIS FIXES\n// Every edit page in the consuming app passes a `cancelPath` naming the module\n// LIST (\"/candidates\", \"/jobs\", \"/employers\", …). EditFormV1 preferred that\n// path over history, so opening Edit *from a detail view* and cancelling threw\n// the user out to the list — losing the record they were looking at, its tab,\n// its scroll position and any filter behind it.\n//\n// THE RULE\n// History wins. `cancelPath` is demoted to what it is genuinely good for: a\n// fallback for a COLD entry (a deep-linked /candidate/edit/:id opened in a\n// fresh tab), where navigate(-1) would walk out of the application entirely.\n//\n// A caller that really must force a destination can still say so explicitly\n// with `cancelPathPriority` — but that is now an opt-in exception rather than\n// the accidental default.\n//\n// Nothing here knows a module, a route or a project: it only answers\n// \"is there in-app history behind me?\".\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * hasInAppHistory — is there a previous entry belonging to THIS app?\n *\n * react-router (v6, history v5) stamps a monotonically increasing `idx` on\n * window.history.state for every entry it pushes. `idx > 0` therefore means\n * \"the app itself navigated here\", i.e. going back lands on one of our own\n * screens rather than on whatever the tab showed before.\n *\n * `window.history.length` is deliberately NOT used: it counts entries from\n * before the app was loaded, so a fresh tab opened from a bookmark can report\n * a length of 2+ and send the user out to an unrelated site.\n */\nexport function hasInAppHistory(win = typeof window !== 'undefined' ? window : undefined) {\n const idx = win?.history?.state?.idx;\n return typeof idx === 'number' && idx > 0;\n}\n\n/**\n * resolveCancelTarget — what a Cancel/Back control should do.\n *\n * Returns either { back: true } (call navigate(-1)) or { path } (call\n * navigate(path)), so the caller stays in charge of the actual navigation and\n * this module stays router-agnostic and testable.\n *\n * @param {object} opts\n * @param {string} [opts.cancelPath] fallback route for a cold entry\n * @param {boolean}[opts.cancelPathPriority] force cancelPath over history\n * @param {string} [opts.fallbackPath] last resort when there is neither\n * @param {Window} [opts.win] injectable for tests\n */\nexport function resolveCancelTarget({\n cancelPath,\n cancelPathPriority = false,\n fallbackPath = '/',\n win = typeof window !== 'undefined' ? window : undefined,\n} = {}) {\n if (cancelPathPriority && cancelPath) return { path: cancelPath };\n if (hasInAppHistory(win)) return { back: true };\n if (cancelPath) return { path: cancelPath };\n return { path: fallbackPath };\n}\n\n/**\n * goBackOrTo — the one-liner most call sites want. Applies\n * resolveCancelTarget with the caller's `navigate`.\n */\nexport function goBackOrTo(navigate, opts = {}) {\n const target = resolveCancelTarget(opts);\n if (target.back) navigate(-1);\n else navigate(target.path);\n return target;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// educationRules — region profiles, recency ordering and level hierarchy for\n// repeatable groups.\n//\n// THE REQUIREMENTS THIS SERVES\n//\n// 1. \"In the admin create a region US / UK / IND / common at the top. From\n// this it has to trigger the components … when I click US and UK and apply\n// the changes it has to render all.\"\n//\n// 2. \"For US and UK it can proceed with currently pursuing\" (a candidate may\n// apply mid-degree) \"but in case of India only after pursuing will a\n// company let you apply.\"\n//\n// 3. \"If I click currently pursuing in the middle of the rows it has to show\n// a popup — 'as you are mentioning currently pursuing, since this seems to\n// be a recent education can I make this the 1st?' If the user clicks yes\n// then it has to be at the top, and it has to be in the order using the\n// start date and end date, recent first.\"\n//\n// 4. \"In the education, if I type Masters in the latest and go to the next\n// group it has to show the error 'you added only the PG, the UG degree is\n// mandatory'.\"\n//\n// NOTHING HERE NAMES A REGION, A DEGREE OR A MODULE. A region is a key into a\n// config map; a degree's rank comes from master data. That is what lets the\n// same code serve a market nobody has thought of yet.\n// ─────────────────────────────────────────────────────────────────────────\nimport dayjs from 'dayjs';\n\nconst text = (v) => (v === null || v === undefined ? '' : String(v).trim());\n\n// ── 1. region profiles ───────────────────────────────────────────────────\n\n/**\n * resolveRegionRules — merge a group's `regionRules[<active region>]` over its\n * base config.\n *\n * A group declares its defaults normally and overrides only what differs per\n * region, so switching region re-renders every affected component with no\n * branch in any component and no second copy of the config.\n *\n * An unknown/absent region falls back to `default`, then to the base group —\n * so a tenant that never sets a region behaves exactly as before.\n */\nexport function resolveRegionRules(group, region) {\n const rules = group?.regionRules;\n if (!rules || typeof rules !== 'object') return group ?? {};\n const key = text(region);\n const applied = rules[key] ?? rules[key.toUpperCase()] ?? rules.default;\n if (!applied || typeof applied !== 'object') return group ?? {};\n return { ...group, ...applied };\n}\n\n// ── 2. \"currently pursuing\" allowed by region ────────────────────────────\n\n/**\n * inProgressAllowed — may a row be marked as still in progress?\n *\n * Defaults to TRUE: blocking is the exceptional rule (India), and a group that\n * never configures this must not suddenly start rejecting rows.\n */\nexport function inProgressAllowed(group) {\n return group?.allowInProgress !== false;\n}\n\n/**\n * inProgressError — the configured message for a disallowed in-progress row,\n * or '' when it is allowed.\n */\nexport function inProgressError(group) {\n if (inProgressAllowed(group)) return '';\n if (inProgressWarns(group)) {\n return group?.inProgressWarningMessage\n || group?.inProgressMessage\n || 'In this region an unfinished qualification is not usually accepted when a '\n + 'candidate is put forward for a role. You can still record it here \\u2014 just '\n + 'be aware it will have to be completed before they can be submitted.';\n }\n return group?.inProgressMessage\n || 'This one needs to be finished before the application can go ahead. '\n + 'Please untick \\u201cstill ongoing\\u201d, or remove the row.';\n}\n\n/**\n * inProgressSeverity \\u2014 HOW HARD a region's restriction bites.\n *\n * The same fact does not carry the same weight everywhere it appears. Recording\n * a candidate is record-keeping: someone mid-degree in India is a real person\n * whose details are worth having on file, and refusing to record them throws\n * away information the business wanted. Putting that candidate FORWARD is a\n * commitment to a client who will not accept an unfinished qualification \\u2014 and\n * there the same fact has to stop the submit.\n *\n * 'allow' no restriction (the default everywhere)\n * 'warn' say so, and let it through\n * 'block' refuse \\u2014 the tick is reversed and the form will not submit\n *\n * Defaults to 'block' when allowInProgress is false, so a group configured\n * before this key existed behaves exactly as it did.\n */\nexport function inProgressSeverity(group) {\n if (inProgressAllowed(group)) return 'allow';\n const configured = text(group?.inProgressSeverity).toLowerCase();\n return configured === 'warn' ? 'warn' : 'block';\n}\n\n/** Does this group refuse an in-progress row outright? */\nexport function inProgressBlocks(group) {\n return inProgressSeverity(group) === 'block';\n}\n\n/** Does this group merely caution about one? */\nexport function inProgressWarns(group) {\n return inProgressSeverity(group) === 'warn';\n}\n\n/**\n * buildInProgressValidator \\u2014 the SUBMIT-time half of a blocking rule.\n *\n * Intercepting the tick-box is not enough on its own. A submission's rows are\n * prefilled from the candidate, and the candidate is allowed to carry an\n * in-progress entry \\u2014 so a blocked row arrives without anybody having clicked\n * anything. Without this the form would accept it, and the rule would hold only\n * against users who happened to tick the box by hand.\n *\n * Returns null when the group does not block, so no rule is attached at all.\n */\nexport function buildInProgressValidator(group) {\n if (!inProgressBlocks(group)) return null;\n const message = inProgressError(group);\n return {\n validator: (_, value) => (value === true\n ? Promise.reject(new Error(message))\n : Promise.resolve()),\n };\n}\n\n// ── 3. recency ordering ──────────────────────────────────────────────────\n\n/**\n * rowSortKey — the instant a row is ordered by. End date first (a finished\n * qualification is placed by when it finished), falling back to start date.\n * Returns null when the row carries no usable date, so undated rows can be\n * kept where they are rather than being shuffled to an arbitrary end.\n */\nexport function rowSortKey(row, cfg = {}) {\n const endField = cfg.tieBreak ?? 'endDate';\n const startField = cfg.field ?? 'startDate';\n for (const key of [endField, startField]) {\n const value = row?.[key];\n if (value === undefined || value === null || value === '') continue;\n const d = dayjs(value);\n if (d.isValid()) return d.valueOf();\n }\n return null;\n}\n\n/**\n * isInProgressRow — is this row flagged as ongoing?\n */\nexport function isInProgressRow(row, cfg = {}) {\n const field = cfg.inProgressField ?? 'currentStudyingHere';\n return Boolean(row?.[field]);\n}\n\n/**\n * orderedRowIndexes — the indexes the rows SHOULD appear in.\n *\n * Most recent first. An in-progress row sorts above every completed one when\n * `inProgressFirst` is set — it is by definition the latest, and it usually has\n * no end date to sort on. Undated rows keep their relative position at the end\n * rather than being flung to the top by a null comparing as zero.\n */\nexport function orderedRowIndexes(rows = [], cfg = {}) {\n const desc = (cfg.direction ?? 'desc') === 'desc';\n const decorated = rows.map((row, index) => ({\n index,\n key: rowSortKey(row, cfg),\n ongoing: cfg.inProgressFirst !== false && isInProgressRow(row, cfg),\n }));\n\n return decorated\n .slice()\n .sort((a, b) => {\n if (a.ongoing !== b.ongoing) return a.ongoing ? -1 : 1;\n // Undated rows sink, and hold their original order among themselves.\n if (a.key === null && b.key === null) return a.index - b.index;\n if (a.key === null) return 1;\n if (b.key === null) return -1;\n if (a.key === b.key) return a.index - b.index;\n return desc ? b.key - a.key : a.key - b.key;\n })\n .map((d) => d.index);\n}\n\n/**\n * misplacedRow — where does the row the user just edited actually belong?\n *\n * Returns null when it is already in the right place, else\n * { from, to, reason } where reason is 'inProgress' or 'outOfOrder' — which is\n * what lets the caller pick between the requirement's two different prompts.\n */\nexport function misplacedRow(rows, changedIndex, cfg = {}) {\n if (!Array.isArray(rows) || rows.length < 2) return null;\n if (changedIndex == null || changedIndex < 0 || changedIndex >= rows.length) return null;\n\n const order = orderedRowIndexes(rows, cfg);\n const to = order.indexOf(changedIndex);\n if (to === -1 || to === changedIndex) return null;\n\n return {\n from: changedIndex,\n to,\n reason: isInProgressRow(rows[changedIndex], cfg) ? 'inProgress' : 'outOfOrder',\n };\n}\n\n// Default prompt wording. Both sentences are the ones the requirement asks for,\n// and both are overridable per group via `orderBy.messages`.\nexport const DEFAULT_ORDER_MESSAGES = Object.freeze({\n inProgress: 'You\\u2019ve marked this one as still ongoing, so it\\u2019s the most recent. '\n + 'Shall we move it to the top? This list reads best newest first.',\n outOfOrder: 'These dates make this the most recent one. '\n + 'Shall we move it to the top? This list reads best newest first.',\n ok: 'Yes, move it up',\n keep: 'No, leave it here',\n});\n\n/**\n * orderPromptMessage — the sentence for a given misplacement.\n */\nexport function orderPromptMessage(reason, cfg = {}) {\n const messages = { ...DEFAULT_ORDER_MESSAGES, ...(cfg.messages ?? {}) };\n return messages[reason] ?? messages.outOfOrder;\n}\n\n/**\n * moveRow — pure reorder, so the caller can preview or test it without antd.\n */\nexport function moveRow(rows, from, to) {\n const next = [...(rows ?? [])];\n if (from < 0 || from >= next.length || to < 0 || to >= next.length) return next;\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n return next;\n}\n\n// ── 4. degree hierarchy ──────────────────────────────────────────────────\n\n/**\n * levelOf — a row's rank, resolved from master data rather than from the\n * degree's NAME. `levels` maps a stored value to a numeric rank\n * ({ \"UG\": 4, \"PG\": 5, … }), which is what keeps \"Masters\", \"M.Tech\" and\n * \"PG\" configurable as the same rank without any of them appearing here.\n */\nexport function levelOf(row, cfg = {}) {\n const field = cfg.levelField ?? 'qualification';\n const raw = text(row?.[field]);\n if (!raw) return null;\n const levels = cfg.levels ?? {};\n const direct = levels[raw] ?? levels[raw.toLowerCase()] ?? levels[raw.toUpperCase()];\n return Number.isFinite(Number(direct)) ? Number(direct) : null;\n}\n\n/**\n * missingRequiredLevels — which mandatory levels BELOW the highest entered one\n * are absent.\n *\n * \"You added only the PG; the UG degree is mandatory\" is exactly this: the\n * highest level present is PG (5), UG (4) is configured as required, and no row\n * carries it.\n *\n * Returns [] when the rule is not configured, when nothing has been entered\n * yet, or when `requireBelow` is off (US/UK, where applying mid-degree is\n * normal) — so it never fires on a form the rule was not meant for.\n */\nexport function missingRequiredLevels(rows = [], cfg = {}) {\n if (!cfg || cfg.requireBelow === false) return [];\n const levels = cfg.levels ?? {};\n const required = cfg.requiredLevels ?? [];\n if (!required.length) return [];\n\n const present = rows.map((r) => levelOf(r, cfg)).filter((n) => n !== null);\n if (!present.length) return [];\n const highest = Math.max(...present);\n\n const missing = [];\n required.forEach((entry) => {\n // An entry may be a label (\"UG\") or {label, level}.\n const label = typeof entry === 'object' ? entry.label : entry;\n const rank = typeof entry === 'object' && Number.isFinite(Number(entry.level))\n ? Number(entry.level)\n : Number(levels[label]);\n if (!Number.isFinite(rank)) return;\n // Only levels BELOW what the candidate claims are required: someone whose\n // highest entry is 12th grade must not be asked for a degree.\n if (rank >= highest) return;\n if (!present.includes(rank)) missing.push(label);\n });\n return missing;\n}\n\n/**\n * levelRuleError — the finished message, or '' when the rule is satisfied.\n */\nexport function levelRuleError(rows, cfg = {}) {\n const missing = missingRequiredLevels(rows, cfg);\n if (!missing.length) return '';\n const list = missing.join(', ');\n const template = cfg.message\n || 'You\\u2019ve added a higher qualification but not the {missing} below it. '\n + 'Please add that too.';\n return template.replace('{missing}', list);\n}\n\n/**\n * promptRowMove — ask whether to move a row that is now out of order, and do it.\n *\n * Called after the user changes something that affects a row's position: the\n * dates, or the \"still ongoing\" tick-box. If the row belongs somewhere else,\n * they are asked; nothing is ever reordered behind their back, because a list\n * that rearranges itself while you are typing in it is disorienting.\n *\n * Returns true when a move happened, so the caller can skip any follow-up work.\n *\n * @param {object} opts\n * @param {object} opts.group the group config (already region-resolved)\n * @param {Array} opts.rows the group's current rows\n * @param {number} opts.rowIndex the row the user just edited\n * @param {function} opts.move Form.List's move(from, to)\n * @param {function} opts.confirm ({title, body, okText, cancelText}) => Promise<boolean>\n */\nexport async function promptRowMove({ group, rows, rowIndex, move, confirm }) {\n const cfg = group?.orderBy;\n if (!cfg || cfg.confirmMove === false || typeof move !== 'function') return false;\n\n const misplaced = misplacedRow(rows, rowIndex, cfg);\n if (!misplaced) return false;\n\n const messages = { ...DEFAULT_ORDER_MESSAGES, ...(cfg.messages ?? {}) };\n const agreed = await confirm({\n reason: misplaced.reason,\n title: misplaced.reason === 'inProgress'\n ? 'This looks like the most recent one'\n : 'These dates make this the most recent one',\n body: orderPromptMessage(misplaced.reason, cfg),\n okText: messages.ok,\n cancelText: messages.keep,\n from: misplaced.from,\n to: misplaced.to,\n });\n if (!agreed) return false;\n\n move(misplaced.from, misplaced.to);\n return true;\n}\n\n// ── 5. the cross-module gate ─────────────────────────────────────────────\n\n/**\n * groupInProgressConfig — a group's ongoing-flag settings, region-resolved.\n *\n * Returns null for a group that has no ongoing flag at all, so a caller can\n * simply skip it. `rowsKey` is where the group's rows live on a stored record\n * (payloadKey, falling back to the group name) — the popup needs that to read a\n * candidate it did not render.\n */\nexport function groupInProgressConfig(group, region) {\n const effective = resolveRegionRules(group, region);\n const inProgressField = effective?.orderBy?.inProgressField;\n if (!inProgressField) return null;\n return {\n name: effective.name,\n label: effective.label ?? effective.name,\n rowsKey: effective.payloadKey || effective.name,\n inProgressField,\n severity: inProgressSeverity(effective),\n message: inProgressError(effective),\n };\n}\n\n/**\n * inProgressVerdict — what a MODULE's rules say about a RECORD.\n *\n * This is the single question both sides of the flow ask, and the reason it\n * lives here rather than in either of them: Quick Submit has to refuse exactly\n * what the submission form would refuse. Two implementations of \"does this\n * candidate have an unfinished qualification\" would drift, and the failure mode\n * is the worst kind — the popup lets someone through to a form that then will\n * not submit, with no way back.\n *\n * groups the TARGET module's form groups (submissions, when gating a\n * submit) — never the source record's own module\n * region the tenant's configured region\n * record the record being judged (a candidate), whose rows are read by\n * each group's rowsKey\n *\n * Returns { severity, message, group } — severity 'allow' when nothing\n * objects, so a caller can treat any other value as \"say something\".\n */\nexport function inProgressVerdict(groups = [], region, record) {\n if (!record) return { severity: 'allow', message: '', group: null };\n\n let warning = null;\n for (const group of groups) {\n const cfg = groupInProgressConfig(group, region);\n if (!cfg || cfg.severity === 'allow') continue;\n\n const rows = readRows(record, cfg.rowsKey);\n if (!rows.some((row) => isInProgressRow(row, cfg))) continue;\n\n // A block is final; keep looking only while all we have is a warning, so\n // one group that merely cautions never masks another that refuses.\n if (cfg.severity === 'block') {\n return { severity: 'block', message: cfg.message, group: cfg };\n }\n warning = warning ?? { severity: 'warn', message: cfg.message, group: cfg };\n }\n return warning ?? { severity: 'allow', message: '', group: null };\n}\n\n/**\n * readRows pulls a group's rows off a stored record.\n *\n * Tolerates the three shapes one arrives in: the plain array a record carries,\n * the `{ rows }` wrapper getFormGroups embeds for edit-prefill, and a missing\n * key. Anything else yields no rows — which means \"nothing to object to\", the\n * safe answer for a shape we do not understand.\n */\nfunction readRows(record, key) {\n const raw = record?.[key];\n if (Array.isArray(raw)) return raw;\n if (Array.isArray(raw?.rows)) return raw.rows;\n return [];\n}\n\n// ── 6. the level rule at SUBMIT time ─────────────────────────────────────\n\n/**\n * levelRuleSeverity — how hard a missing lower qualification bites.\n *\n * 'warn' (default) ask, and let the user go ahead\n * 'block' refuse the submit\n *\n * Defaults to WARN, and deliberately so. This rule has never actually fired —\n * the config and the functions existed with nothing calling them — so switching\n * it on as a hard block would start rejecting submissions that have always been\n * accepted, for a reason nobody has seen before. A question the user can answer\n * introduces the same rule without that.\n *\n * A record is also not always wrong: someone genuinely may hold a Master's from\n * a system that never recorded the Bachelor's, and a recruiter looking at the\n * CV knows that better than a config does.\n */\nexport function levelRuleSeverity(cfg) {\n return String(cfg?.severity ?? '').toLowerCase() === 'block' ? 'block' : 'warn';\n}\n\n/**\n * levelRuleVerdict — check EVERY group that has a level rule, for one form's\n * values.\n *\n * groups the module's form groups (region-resolved by the caller)\n * values the submitted form values; each group's rows are read from its\n * own name, falling back to its payloadKey\n *\n * Returns { severity, message, group } with severity 'allow' when nothing\n * objects. A BLOCK anywhere wins over a warning, so a group that merely\n * cautions can never mask one that refuses.\n */\nexport function levelRuleVerdict(groups = [], values = {}) {\n let warning = null;\n for (const group of groups) {\n const cfg = group?.levelRule;\n if (!cfg) continue;\n const rows = rowsForGroup(values, group);\n if (!rows.length) continue;\n const message = levelRuleError(rows, cfg);\n if (!message) continue;\n if (levelRuleSeverity(cfg) === 'block') {\n return { severity: 'block', message, group };\n }\n warning = warning ?? { severity: 'warn', message, group };\n }\n return warning ?? { severity: 'allow', message: '', group: null };\n}\n\n/** A group's submitted rows, under its name or its payloadKey. */\nfunction rowsForGroup(values, group) {\n for (const key of [group?.name, group?.payloadKey]) {\n if (!key) continue;\n const raw = values?.[key];\n if (Array.isArray(raw)) return raw;\n }\n return [];\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// clearGroupOnChange — \"when THIS field changes, the rest of its group no\n// longer describes anything\".\n//\n// THE REQUIREMENT\n// \"After the new employer is added by clicking Add More in the submission and\n// candidate form, after successful add it selects the employer name. If the\n// employer name changes, reset all the other fields in the employer group.\"\n//\n// The employer group's other fields — recruiter, email, contact code, contact,\n// VMS %, tax — all describe the PREVIOUSLY selected employer. Leaving them\n// behind after the employer changes silently attaches one company's recruiter\n// and phone number to another company, which is worse than a blank form\n// because it looks filled in and correct.\n//\n// `linkedClearField` already existed but clears exactly ONE named sibling and\n// only from a checkbox. This generalises it: any field may declare\n// `clearGroupOnChange: true` to clear every OTHER field of its own group, and\n// `linkedClearField` may now name several fields.\n//\n// Row-scoped inside a repeatable group: clearing employer row 2 must not touch\n// row 1.\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * siblingPath — the form path of `key` as a sibling of the field at `name`.\n * Inside a Form.List row, `name` is [listName, rowIndex, fieldKey], so the\n * sibling shares the first two segments and only the last changes.\n */\nexport function siblingPath(name, key) {\n return Array.isArray(name) && name.length > 1\n ? [...name.slice(0, -1), key]\n : [key];\n}\n\n/**\n * fieldsToClear — which keys this change should blank.\n *\n * @param {object} field the field that changed\n * @param {Set|Array} groupKeys every field key in the field's group\n * @returns {string[]} keys to clear, never including the field itself\n */\nexport function fieldsToClear(field, groupKeys) {\n const own = field?.field;\n const keys = [];\n\n if (field?.clearGroupOnChange) {\n const all = groupKeys instanceof Set ? [...groupKeys] : (groupKeys ?? []);\n all.forEach((key) => { if (key && key !== own) keys.push(key); });\n }\n\n // linkedClearField now accepts one key or several. A field named here is\n // cleared even when it is NOT part of the group, which is what makes it\n // usable for a cross-group dependency.\n const linked = field?.linkedClearField;\n if (Array.isArray(linked)) {\n linked.forEach((key) => { if (key && key !== own) keys.push(key); });\n } else if (typeof linked === 'string' && linked.trim() && linked.trim() !== own) {\n keys.push(linked.trim());\n }\n\n return [...new Set(keys)];\n}\n\n/**\n * applyGroupClear — perform the clear.\n *\n * Values are set to `null` rather than deleted: antd keeps a Form.Item\n * registered either way, and null is what every other clear path in these forms\n * writes, so a subsequent payload build treats it identically.\n *\n * Returns the paths cleared, so a caller can re-validate or test.\n */\nexport function applyGroupClear(form, field, name, groupKeys) {\n const keys = fieldsToClear(field, groupKeys);\n const paths = keys.map((key) => siblingPath(name, key));\n paths.forEach((path) => form.setFieldValue(path, null));\n return paths;\n}\n","import { AUTH_URL } from '../../services/apiConfig';\nimport { fetchJsonWithAuth } from '../../services/authApi';\n\n// =============================================================================\n// Stored values that fall outside a filtered dropdown\n// -----------------------------------------------------------------------------\n// A lookup field's `lookupFilters` answer \"who may I PICK?\" — the jobs\n// Assign-to picker lists only recruiters who report to the signed-in user. They\n// were also, accidentally, answering \"whose name may I SEE?\": a value already on\n// the record but outside that filtered set had no matching option, so antd fell\n// back to printing the raw stored value. The edit form showed \"149\" and \"1\"\n// where it should have shown two people's names.\n//\n// That is not a cosmetic problem. The user cannot tell who the job is assigned\n// to, cannot verify it, and cannot even tell whether removing the chip is safe.\n// And it is invisible to whoever configured the filter, because it only shows up\n// for records assigned before the filter existed, or by somebody higher up the\n// reporting chain.\n//\n// So: options stay filtered, and any value ALREADY STORED is resolved\n// separately and added to the list. Nothing here names a module or a field.\n// =============================================================================\n\n/** The values a select currently holds, flattened to plain scalars. */\nexport function selectedValues(value) {\n const list = Array.isArray(value) ? value : [value];\n return list\n .map((item) => (item && typeof item === 'object' ? (item.value ?? item.key ?? item.id) : item))\n .filter((item) => item !== undefined && item !== null && item !== '');\n}\n\n/**\n * missingOptionValues — stored values with no option to render them.\n *\n * Compared as STRINGS: a legacyUserId is the number 149 on the record and may\n * arrive as \"149\" from the option list. Comparing them raw would report every\n * value as missing and re-fetch on every render.\n */\nexport function missingOptionValues(value, options = []) {\n const known = new Set((options ?? []).map((opt) => String(opt?.value)));\n const missing = [];\n for (const v of selectedValues(value)) {\n const key = String(v);\n if (!known.has(key) && !missing.includes(key)) missing.push(key);\n }\n return missing;\n}\n\n/**\n * fetchLookupLabels — labels for specific stored values, unfiltered.\n *\n * Uses the SAME endpoint the options come from, in its `values` mode, so the\n * label a resolved chip shows is built by the same displayField/displayField2\n * the dropdown itself uses — a separately-built label would drift from the list\n * the moment an admin changed either.\n */\nexport async function fetchLookupLabels(field, values) {\n if (!field?.lookupCollection || !values?.length) return [];\n const params = new URLSearchParams({\n collection: field.lookupCollection,\n displayField: field.displayField ?? '',\n valueField: field.valueField ?? '_id',\n values: values.join(','),\n });\n if (field.displayField2) params.set('displayField2', field.displayField2);\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/lookup-dropdown-values?${params}`);\n const rows = json?.data ?? json ?? [];\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * mergeResolvedOptions — the filtered list plus the resolved stragglers.\n *\n * Resolved entries are marked `resolvedOnly` so a caller can tell them apart.\n * They are NOT disabled: the value is on the record, and the user must be able\n * to remove it. What they cannot do is add it back once removed — which is\n * exactly what the filter is there to prevent, and is now the only thing it\n * prevents.\n */\nexport function mergeResolvedOptions(options = [], resolved = []) {\n if (!resolved.length) return options;\n const known = new Set((options ?? []).map((opt) => String(opt?.value)));\n const extra = resolved\n .filter((row) => !known.has(String(row?.value)))\n .map((row) => ({ ...row, resolvedOnly: true }));\n return extra.length ? [...options, ...extra] : options;\n}\n\n/**\n * labelForMissingValue — the last resort when even the lookup finds nothing.\n *\n * A deleted user, or an id that never existed. Showing the bare number implies\n * it is a name; this says plainly that it could not be resolved while keeping\n * the id visible, because the id is the only thing left to investigate with.\n */\nexport function labelForMissingValue(value) {\n return `Unknown (${value})`;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// dateRules — the ONE implementation of every date-comparison rule, shared by\n// AddFormV1 and EditFormV1.\n//\n// WHY THIS MODULE EXISTS\n// The rule engine was duplicated verbatim in both forms. That is how a fix\n// lands on Add and silently misses Edit — exactly what had already happened to\n// `minLength3` (defined only in AddFormV1, a no-op on the edit form). A\n// requirement phrased as \"candidate AND submission, add AND edit must behave\n// the same\" cannot be satisfied by two copies that merely look alike, so the\n// logic lives here and both forms import it.\n//\n// WHAT A RULE LOOKS LIKE (all options optional, absent === previous behaviour)\n//\n// { type: 'dateAfterField', // this field must be AFTER…\n// value: 'startDate', // …this sibling\n// strict: true, // equal dates are INVALID\n// minGap: { value: 6, unit: 'month' }, // and at least 6 months apart\n// minGapFrom: 'duration', // …or read the gap from a sibling\n// maxGap: { value: 40, unit: 'year' },\n// message: 'End Date must be after Start Date' }\n//\n// `dateBeforeField` is the mirror image. Writing BOTH (start declares\n// dateBeforeField(end), end declares dateAfterField(start)) is what makes the\n// constraint show up in BOTH pickers: pick 7 July as Start and the End picker\n// greys out everything up to and including 7 July, and vice-versa. The two\n// rules are generated from one table in the seeder so they cannot drift.\n//\n// Every rule drives BOTH the submit-time validator and the picker's\n// `disabledDate` from the same object — a greyed-out calendar that disagrees\n// with the error message is worse than either alone.\n// ─────────────────────────────────────────────────────────────────────────\nimport dayjs from 'dayjs';\nimport { appNow } from '../../services/timezone';\n\nexport const isEmptyValue = (v) => v === undefined || v === null || v === '';\n\n/** Milliseconds for an instant-comparable value (dayjs, Date, ISO string). */\nexport function getComparableDateTime(value) {\n if (!value) return null;\n if (typeof value.valueOf === 'function') {\n const time = value.valueOf();\n return Number.isNaN(time) ? null : time;\n }\n const time = new Date(value).getTime();\n return Number.isNaN(time) ? null : time;\n}\n\n/** Milliseconds at the START of the value's day — the unit date rules compare in. */\nexport function getComparableDateDay(value) {\n if (!value) return null;\n const date = dayjs(value);\n return date.isValid() ? date.startOf('day').valueOf() : null;\n}\n\n/** Minutes since midnight. TimePicker values share a day, so startOf('day') would make every time equal. */\nexport function getComparableTimeOfDay(value) {\n if (!value) return null;\n const time = dayjs(value);\n return time.isValid() ? time.hour() * 60 + time.minute() : null;\n}\n\n// Units dayjs accepts for add/subtract, mapped from what an admin might type.\nconst GAP_UNITS = {\n d: 'day', day: 'day', days: 'day',\n w: 'week', week: 'week', weeks: 'week',\n m: 'month', mo: 'month', month: 'month', months: 'month',\n y: 'year', yr: 'year', year: 'year', years: 'year',\n};\n\n/**\n * parseGap — normalise every shape a gap can arrive in into {value, unit}.\n *\n * It has to be forgiving because one of the sources is a DROPDOWN whose options\n * are admin-authored master data: \"6 months\", \"6m\", \"6\", {value:6,unit:'month'}\n * all have to mean the same thing, or the config screen becomes a trap where\n * the wrong-but-reasonable spelling silently disables the rule.\n *\n * A bare number means months — the unit the duration dropdown is expressed in.\n * Returns null for anything that carries no usable magnitude.\n */\nexport function parseGap(raw) {\n if (raw === null || raw === undefined || raw === '') return null;\n\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n const value = Number(raw.value ?? raw.count ?? raw.amount);\n if (!Number.isFinite(value) || value <= 0) return null;\n return { value, unit: GAP_UNITS[String(raw.unit ?? 'month').toLowerCase()] ?? 'month' };\n }\n\n const text = String(raw).trim().toLowerCase();\n if (!text) return null;\n const match = text.match(/^(\\d+(?:\\.\\d+)?)\\s*([a-z]*)$/);\n if (!match) return null;\n const value = Number(match[1]);\n if (!Number.isFinite(value) || value <= 0) return null;\n return { value, unit: GAP_UNITS[match[2]] ?? 'month' };\n}\n\n/**\n * resolveGap — the gap actually in force for this field right now.\n *\n * `minGapFrom` names a sibling whose CURRENT value supplies the gap, which is\n * how the \"select 6 months and the pickers tighten to 6 months\" dropdown works\n * without any rule rewriting. It wins over a static `minGap` when it holds a\n * usable value, and falls back to the static one when the dropdown is empty.\n */\nexport function resolveGap(rule, readSibling) {\n const fromField = rule?.minGapFrom ?? rule?.gapFrom;\n if (fromField && typeof readSibling === 'function') {\n const dynamic = parseGap(readSibling(fromField));\n if (dynamic) return dynamic;\n }\n return parseGap(rule?.minGap);\n}\n\n/**\n * compareBoundary — the earliest (dateAfterField) or latest (dateBeforeField)\n * day this field may hold, given the other end of the pair.\n *\n * Returns a day-start timestamp, or null when there is nothing to constrain.\n */\nexport function compareBoundary(type, compareDay, rule, readSibling) {\n if (compareDay === null) return null;\n const gap = resolveGap(rule, readSibling);\n let boundary = dayjs(compareDay);\n if (gap) {\n boundary = type === 'dateAfterField'\n ? boundary.add(gap.value, gap.unit)\n : boundary.subtract(gap.value, gap.unit);\n }\n return boundary.startOf('day').valueOf();\n}\n\n/**\n * violatesPair — is `inputDay` on the wrong side of the boundary?\n *\n * `strict` is what makes \"the end date can't be the same day as the start date\"\n * work: without it the rule reads \"on or after\", which is the behaviour that\n * let 7 July → 7 July through.\n */\nexport function violatesPair(type, inputDay, compareDay, rule, readSibling) {\n if (inputDay === null || compareDay === null) return false;\n const boundary = compareBoundary(type, compareDay, rule, readSibling);\n if (boundary === null) return false;\n const strict = Boolean(rule?.strict) && !resolveGap(rule, readSibling);\n if (type === 'dateAfterField') {\n return strict ? inputDay <= boundary : inputDay < boundary;\n }\n return strict ? inputDay >= boundary : inputDay > boundary;\n}\n\n/**\n * pairMessage — the error text, falling back to something that names the real\n * constraint rather than a generic \"invalid date\".\n */\nexport function pairMessage(type, label, rule, readSibling) {\n if (rule?.message) return rule.message;\n const other = rule?.value ?? rule?.compareField ?? rule?.field ?? 'the paired date';\n const gap = resolveGap(rule, readSibling);\n if (gap) {\n const unit = gap.value === 1 ? gap.unit : `${gap.unit}s`;\n return type === 'dateAfterField'\n ? `${label} must be at least ${gap.value} ${unit} after ${other}`\n : `${label} must be at least ${gap.value} ${unit} before ${other}`;\n }\n if (rule?.strict) {\n return type === 'dateAfterField'\n ? `${label} must be after ${other}`\n : `${label} must be before ${other}`;\n }\n return type === 'dateAfterField'\n ? `${label} must be on or after ${other}`\n : `${label} must be on or before ${other}`;\n}\n\n/**\n * buildPairValidator — the antd rule object for dateAfterField/dateBeforeField.\n *\n * `readSibling(fieldName)` resolves a sibling's CURRENT value with the caller's\n * own row-vs-top-level scoping, so this module never needs to know it is inside\n * a repeatable group.\n */\nexport function buildPairValidator({ type, label, rule, readSibling }) {\n return {\n validator: async (_, input) => {\n const compareField = rule?.value ?? rule?.compareField ?? rule?.field;\n // An empty value on EITHER side is the `required` rule's business —\n // otherwise an optional date pair becomes mandatory the moment one half\n // is filled in.\n if (!compareField || isEmptyValue(input)) return Promise.resolve();\n const compareValue = readSibling(compareField);\n if (isEmptyValue(compareValue)) return Promise.resolve();\n\n const inputDay = getComparableDateDay(input);\n const compareDay = getComparableDateDay(compareValue);\n if (inputDay === null || compareDay === null) return Promise.resolve();\n\n return violatesPair(type, inputDay, compareDay, rule, readSibling)\n ? Promise.reject(new Error(pairMessage(type, label, rule, readSibling)))\n : Promise.resolve();\n },\n };\n}\n\n/**\n * buildDisabledDate — the DatePicker `disabledDate` predicate for a field,\n * assembled from every date rule it declares.\n *\n * `now` is injected so the \"today\" boundary can come from the application's\n * configured timezone rather than the browser's (see services/timezone.js) —\n * a user in another zone would otherwise have noFutureDate reject their own\n * today, or accept a tomorrow.\n */\n/**\n * violatesRowHierarchy — does this date clash with an ADJACENT ROW?\n *\n * A newest-first list (education, work experience) says something the per-row\n * rules cannot: row 2 happened BEFORE row 1. So once row 1 has a start date,\n * every date at or after it is impossible for row 2 — you cannot have finished\n * a later qualification before starting an earlier one.\n *\n * Constraining the CALENDAR rather than only erroring on submit is the point:\n * the user sees which dates are available while choosing, instead of being told\n * afterwards that the one they picked was wrong.\n *\n * @param {number} day candidate day (start-of-day ms)\n * @param {object} cfg the field's `rowHierarchy` config\n * @param {object} rowsAccess { rows, rowIndex } — every row of the group + this row's index\n */\nexport function violatesRowHierarchy(day, cfg, { rows, rowIndex } = {}) {\n if (day === null || !cfg || !Array.isArray(rows) || rowIndex == null) return false;\n // 'newestFirst' is the only shape today; naming it keeps an 'oldestFirst'\n // list addable as config rather than as a second code path.\n if ((cfg.mode ?? 'newestFirst') !== 'newestFirst') return false;\n\n const startKey = cfg.startField ?? 'startDate';\n const endKey = cfg.endField ?? 'endDate';\n\n // The row ABOVE is more recent, so this row must end before that row began.\n const above = rows[rowIndex - 1];\n if (above) {\n const ceiling = getComparableDateDay(above[startKey]);\n if (ceiling !== null && day >= ceiling) return true;\n }\n\n // The row BELOW is older, so this row must not start before that row ended.\n const below = rows[rowIndex + 1];\n if (below) {\n const floor = getComparableDateDay(below[endKey]) ?? getComparableDateDay(below[startKey]);\n if (floor !== null && day <= floor) return true;\n }\n\n return false;\n}\n\n/**\n * rowHierarchyMessage — why a date was refused, in terms of the OTHER row.\n * \"Overlaps the entry above\" is actionable; \"invalid date\" is not.\n */\nexport function rowHierarchyMessage(cfg, position = 'above') {\n const messages = cfg?.messages ?? {};\n if (position === 'below') {\n return messages.below\n || 'This starts before the entry below it finished. Entries are listed newest first, so they cannot overlap.';\n }\n return messages.above\n || 'This overlaps the entry above it. Entries are listed newest first, so this one must finish before that one started.';\n}\n\nexport function buildDisabledDate({ field, readSibling, rows, rowIndex, now = appNow }) {\n const rules = field?.validations ?? field?.validation ?? field?.rules ?? [];\n const predicates = [];\n\n for (const raw of rules) {\n const rule = typeof raw === 'string' ? { type: raw } : raw;\n const type = rule?.type;\n\n if (type === 'noPastDate') {\n predicates.push((current) => {\n if (!current) return false;\n return getComparableDateDay(current) < now().startOf('day').valueOf();\n });\n }\n\n if (type === 'noFutureDate') {\n predicates.push((current) => {\n if (!current) return false;\n return getComparableDateDay(current) > now().startOf('day').valueOf();\n });\n }\n\n if (type === 'minAge') {\n const years = Number(rule?.value ?? 18);\n predicates.push((current) => {\n if (!current) return false;\n return getComparableDateDay(current) > now().subtract(years, 'year').startOf('day').valueOf();\n });\n }\n\n if (type === 'dateAfterField' || type === 'dateBeforeField') {\n const compareField = rule?.value ?? rule?.compareField ?? rule?.field;\n if (!compareField) continue;\n predicates.push((current) => {\n if (!current) return false;\n const compareValue = readSibling(compareField);\n if (isEmptyValue(compareValue)) return false;\n return violatesPair(\n type,\n getComparableDateDay(current),\n getComparableDateDay(compareValue),\n rule,\n readSibling,\n );\n });\n }\n }\n\n // Cross-row constraint. Declared on the FIELD (`rowHierarchy`) but evaluated\n // against the whole group, which is why buildDisabledDate needs `rows` and\n // `rowIndex` — a per-row `readSibling` cannot see the neighbouring rows.\n if (field?.rowHierarchy && Array.isArray(rows) && rowIndex != null) {\n predicates.push((current) => {\n if (!current) return false;\n return violatesRowHierarchy(getComparableDateDay(current), field.rowHierarchy, { rows, rowIndex });\n });\n }\n\n return predicates.length\n ? (current) => predicates.some((predicate) => predicate(current))\n : undefined;\n}\n\n/**\n * defaultPickerValue — which month the calendar opens on, so a Date of Birth\n * field does not open on today and make the user page back 30 years.\n */\nexport function defaultPickerValue(field, now = appNow) {\n const rules = field?.validations ?? field?.validation ?? field?.rules ?? [];\n for (const raw of rules) {\n const rule = typeof raw === 'string' ? { type: raw } : raw;\n if (rule?.type === 'minAge') return now().subtract(Number(rule?.value ?? 18), 'year');\n if (rule?.type === 'noFutureDate' || rule?.type === 'noPastDate') return now();\n }\n return undefined;\n}\n\n/**\n * buildRowHierarchyValidator — the submit-time counterpart of the greyed-out\n * calendar.\n *\n * The picker stops a date being CHOSEN; this stops one that arrived another way\n * — typed, pasted, prefilled from a résumé, or entered before the neighbouring\n * row was filled in. A calendar constraint with no validator behind it is a\n * suggestion.\n */\nexport function buildRowHierarchyValidator({ field, rows, rowIndex }) {\n const cfg = field?.rowHierarchy;\n return {\n validator: async (_, input) => {\n if (!cfg || isEmptyValue(input) || !Array.isArray(rows) || rowIndex == null) {\n return Promise.resolve();\n }\n const day = getComparableDateDay(input);\n if (day === null) return Promise.resolve();\n if (!violatesRowHierarchy(day, cfg, { rows, rowIndex })) return Promise.resolve();\n\n // Name WHICH neighbour it clashes with, so the fix is obvious.\n const startKey = cfg.startField ?? 'startDate';\n const above = rows[rowIndex - 1];\n const ceiling = above ? getComparableDateDay(above[startKey]) : null;\n const position = (ceiling !== null && day >= ceiling) ? 'above' : 'below';\n return Promise.reject(new Error(rowHierarchyMessage(cfg, position)));\n },\n };\n}\n","// Shared submit-toast resolver used by AddFormV1/EditFormV1.\n// Admin-configured per FormGroup (group.submitMessages — see FormGroupsSection),\n// generic across every module/project: no module name is ever referenced here.\n//\n// Defaults (no configuration anywhere, including brand-new modules):\n// add → \"<Module> has added successfully\"\n// edit → \"<Module> has Updated successfully\"\n// An admin can override any template (with ${module} substitution) or set\n// suppress to skip the success toast entirely.\n\n// Picks the configured messages from the group with the lowest `order` that\n// declares any — the same resolution rule afterSubmitNav uses.\nexport function resolveSubmitMessages(groups) {\n const withConfig = (groups ?? [])\n .filter((g) => g?.submitMessages && typeof g.submitMessages === 'object')\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n return withConfig[0]?.submitMessages ?? null;\n}\n\n// \"jobs\" → \"Jobs\", \"locationTaxMaster\" → \"LocationTaxMaster\" (first letter only —\n// the module key is the admin-facing name across the platform).\nfunction moduleLabel(moduleName) {\n const raw = String(moduleName ?? '').trim();\n return raw ? raw.charAt(0).toUpperCase() + raw.slice(1) : 'Record';\n}\n\nfunction fillTemplate(template, moduleName) {\n return String(template).replaceAll('${module}', moduleLabel(moduleName))\n .replaceAll('${moduleName}', moduleLabel(moduleName));\n}\n\n// Returns the success toast text for a finished submit, or null when the admin\n// suppressed success toasts for this module. kind: 'add' | 'edit'.\nexport function submitSuccessMessage(groups, moduleName, kind) {\n const config = resolveSubmitMessages(groups);\n if (config?.suppress) return null;\n const template = kind === 'edit' ? config?.editSuccess : config?.addSuccess;\n if (template && String(template).trim()) return fillTemplate(template, moduleName);\n return kind === 'edit'\n ? `${moduleLabel(moduleName)} has Updated successfully`\n : `${moduleLabel(moduleName)} has added successfully`;\n}\n\n// Returns the error toast text: admin override first, then the real server\n// error (most actionable), then a generic fallback. Never null — a failed\n// submit must always surface.\nexport function submitErrorMessage(groups, moduleName, kind, serverText) {\n const config = resolveSubmitMessages(groups);\n const template = kind === 'edit' ? config?.editError : config?.addError;\n if (template && String(template).trim()) return fillTemplate(template, moduleName);\n if (serverText && String(serverText).trim()) return String(serverText);\n return kind === 'edit'\n ? `Failed to update ${moduleLabel(moduleName)}`\n : `Failed to create ${moduleLabel(moduleName)}`;\n}\n","// Shared \"scroll to the first invalid field\" handler for AddFormV1/EditFormV1.\n// Wire it from the antd Form's onFinishFailed: when Create/Save is clicked with\n// validation errors, the screen scrolls to the first errored field, focuses it\n// (cursor placed in the input) and pulses a red halo so the user sees exactly\n// which field failed. Generic — works for every module's add & edit form.\n//\n// Robustness notes:\n// - antd applies the .ant-form-item-has-error classes AFTER onFinishFailed\n// fires, so location runs on a short delay.\n// - The DOM query + scrollIntoView is the PRIMARY mechanism and always runs.\n// form.scrollToField is only a best-effort extra: antd locates fields by\n// DOM id, which custom field controls don't always forward — relying on it\n// alone silently scrolls nothing.\n// - Callers should expand any collapsed sections BEFORE calling this (a field\n// inside a display:none section can be neither scrolled to nor focused) —\n// both form engines do so in their onFinishFailed wrapper.\nexport function scrollToFirstFormError({ errorFields } = {}, form) {\n if (!errorFields?.length) return;\n const firstName = errorFields[0]?.name;\n\n setTimeout(() => {\n if (form?.scrollToField && firstName !== undefined) {\n try {\n form.scrollToField(firstName, { behavior: 'smooth', block: 'center' });\n } catch { /* best-effort only — the DOM scroll below always runs */ }\n }\n\n const errorFormItem = document.querySelector('.ant-form-item-has-error');\n if (!errorFormItem) return;\n const input = errorFormItem.querySelector('input, textarea, select, .ProseMirror');\n const target = input ?? errorFormItem;\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n\n setTimeout(() => {\n input?.focus?.({ preventScroll: true });\n target.style.boxShadow = '0 0 0 3px rgba(255, 77, 79, 0.3)';\n setTimeout(() => { target.style.boxShadow = ''; }, 1200);\n }, 350);\n }, 60);\n}\n\nexport default scrollToFirstFormError;\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","import { Breadcrumb } from 'antd';\nimport { DownOutlined } from '@ant-design/icons';\nimport { Link } from 'react-router-dom';\nimport AppTypography from './typography/Typography';\n\nexport default function AppBreadcrumb({ items = [] }) {\n const breadcrumbItems = items.map((item, index) => {\n const isLast = index === items.length - 1;\n return {\n title: isLast ? (\n <AppTypography variant=\"body\" weight=\"medium\" color=\"link\">\n {item.label}\n </AppTypography>\n ) : (\n <Link to={item.href}>\n <AppTypography variant=\"body\" color=\"secondary\">\n {item.label}\n </AppTypography>\n {item.dropdown && <DownOutlined />}\n </Link>\n ),\n };\n });\n\n return <Breadcrumb items={breadcrumbItems} />;\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Breadcrumb-DMMhMHcG.js","names":[],"sources":["../src/services/adminApi.js","../src/services/detailDefaults.js","../src/services/timezone.js","../src/theme/colors/colors.js","../src/components/typography/Typography.jsx","../src/components/TipTapEditor.jsx","../src/components/DocumentViewer.jsx","../src/utils/modulePath.js","../src/services/moduleDataApi.js","../src/services/addFormApi.js","../src/services/documentApi.js","../src/services/detailedViewApi.js","../src/utils/roleFormPermissionGate.js","../src/components/form/inputSecurity.js","../src/components/form/optionMatching.js","../src/components/form/payloadTransformer.js","../src/components/form/linkedAddRowGroups.js","../src/components/form/formDecisionDialog.jsx","../src/services/aiActionApi.js","../src/components/detail/phoneDisplay.js","../src/components/detail/renderConfig.js","../src/components/form/applyGroupValues.js","../src/components/form/useAiActions.js","../src/components/form/AiActionButtons.jsx","../src/components/form/emailValidator.js","../src/components/form/inputValidator.js","../src/components/form/uniqueFieldValidation.js","../src/services/uniqueValidationApi.js","../src/components/form/uploadAccept.js","../src/components/form/quickActionLabels.js","../src/components/AppButton.jsx","../src/components/detail/userNames.js","../src/components/form/quickCreateNotice.js","../src/components/form/QuickCreateEditField.jsx","../src/components/form/crossFieldRules.js","../src/components/form/maxCeiling.js","../src/components/form/contextPrefill.js","../src/components/form/prefillWhenRules.js","../src/components/form/optionConstraints.js","../src/components/form/optionRowFilters.js","../src/components/form/fieldTooltip.js","../src/utils/roleAllowsAction.js","../src/utils/afterSubmitNav.js","../src/utils/backNav.js","../src/components/form/educationRules.js","../src/components/form/clearGroupOnChange.js","../src/components/form/resolveStoredOptions.js","../src/components/form/dateRules.js","../src/utils/submitMessages.js","../src/utils/scrollToFirstFormError.js","../src/components/form/conditionalFieldLabel.js","../src/components/Breadcrumb.jsx"],"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 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 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 dataSource: field.dataSource ?? 'module',\n groupName: field.groupName ?? '',\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 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 };\n}\n\nexport async function saveDetailConfig({ module, fields = [], merges = [], 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, ...(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// ── 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') {\n const params = new URLSearchParams({ module: apiModule, userId: '0', [idParam]: String(idValue), 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\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\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 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 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: field.lookup ?? null, actionButtons: field.actionButtons ?? [],\n derived: field.derived ?? null, computed: field.computed ?? null,\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 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 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) {\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({ module: apiModule, roleId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\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 field keys forced always-shown + always-required.\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.\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\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\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 }),\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\nexport async function createModuleActionRule({ module, name, description = '', conditions = [], blockedActions = [], disabledActions = [], 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, isActive }),\n });\n}\n\nexport async function updateModuleActionRule(id, { name, description = '', conditions = [], blockedActions = [], disabledActions = [], 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, isActive }),\n });\n}\n\nexport async function deleteModuleActionRule(id) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}/${id}`, { method: 'DELETE' });\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';\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});\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 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 current = { ...current, ...sanitize(partial) };\n return current;\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 loadPromise = 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 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","export const colors = {\n brand: '#0053a5',\n brandDark: '#1d4ed8',\n brandDarker: '#1e40af',\n brandHover: '#004f85',\n brandSoft: '#dfedf7',\n brandSofter: '#e7f2fa',\n brandSubtle: '#edf8fe',\n\n textPrimary: '#111827',\n textSecondary: '#4b5563',\n textMuted: '#6b7280',\n textSubtle: '#010306',\n textHeading: '#142235',\n textDark: '#232a31',\n textPlaceholder: '#a7b0bb',\n textInverse: '#ffffff',\n textLink: '#0053a5',\n\n surfacePage: '#f5f6fa',\n surfaceSoft: '#f8fafc',\n surfaceSofter: '#f3f8fb',\n surfaceCard: '#ffffff',\n surfaceHover: '#eff6ff',\n surfaceHoverLight: '#f8fcff',\n surfaceHoverSoft: '#f5fbff',\n surfaceSelected: '#dbeafe',\n surfaceControl: '#f4f8fb',\n surfaceRowAlt: '#fbfdff',\n\n border: '#e6f0ff',\n borderLight: '#eef3f8',\n borderSofter: '#f0f0f0',\n borderMuted: '#d8e4ef',\n borderInput: '#d5dde5',\n borderFocus: '#7dbce6',\n borderHover: '#99c7e8',\n controlBorder: '#77abd0',\n controlBorderMuted: '#e7edf3',\n controlAccent: '#4f9ac7',\n\n iconMuted: '#d1d5db',\n iconSubtle: '#9aa6b2',\n iconNeutral: '#8c8c8c',\n iconSoft: '#bbbbbb',\n scrollbarThumb: '#c4ccd8',\n scrollbarThumbLight: '#d1d5db',\n danger: '#dc2626',\n dangerSoft: '#ef4444',\n dangerStrong: '#e11d24',\n success: '#15803d',\n successSoft: '#16a34a',\n warning: '#f97316',\n info: '#3b82f6',\n transparent: 'transparent',\n\n statusNeutralBg: '#f1f3ee',\n statusNeutralText: '#717b36',\n statusProcessingBg: '#f6f4f7',\n statusProcessingText: '#273048',\n statusProcessingBorder: '#d4d8dd',\n statusWarningBg: '#fff7ea',\n statusWarningText: '#bf7328',\n\n shadowMenu: 'rgba(15, 35, 55, 0.08)',\n shadowBadge: 'rgba(26, 95, 145, 0.08)',\n shadowTag: 'rgba(39, 51, 70, 0.05)',\n shadowAvatar: 'rgba(57, 77, 103, 0.12)',\n shadowDropdown: '0 6px 16px 0 rgba(0, 0, 0, .08), 0 3px 6px -4px rgba(0, 0, 0, .12), 0 9px 28px 8px rgba(0, 0, 0, .05)',\n avatarBlueBg: '#eaf3ff',\n avatarBlueText: '#142235',\n avatarPurpleBg: '#f2eaff',\n avatarMoreBg: '#eef6ff',\n avatarMoreText: '#0053a5',\n\n avatarNeutralBg: '#d9d9d9',\n avatarNeutralText: '#555555',\n avatarIndigo: '#6366f1',\n avatarPurple: '#8b5cf6',\n avatarBlue: '#3b82f6',\n avatarGreen: '#10b981',\n linkedIn: '#0077b5',\n};\n\nexport const typographyColors = {\n primary: colors.textPrimary,\n secondary: colors.textSecondary,\n muted: colors.textMuted,\n subtle: colors.textSubtle,\n inverse: colors.textInverse,\n link: colors.textLink,\n danger: colors.danger,\n success: colors.success,\n};\n\nexport const onboardingStageToneColors = {\n approved: {\n background: '#f2f2e8',\n text: '#79772d',\n },\n danger: {\n background: '#fde7e9',\n text: colors.danger,\n },\n issued: {\n background: '#e9f2fb',\n text: colors.textLink,\n },\n neutral: {\n background: colors.surfaceSoft,\n text: colors.textSecondary,\n },\n success: {\n background: '#e7f5ee',\n text: colors.success,\n },\n warning: {\n background: '#fbf0e7',\n text: colors.warning,\n },\n};\n\nexport const colorVars = {\n brand: 'var(--color-brand)',\n brandDark: 'var(--color-brand-dark)',\n brandDarker: 'var(--color-brand-darker)',\n brandHover: 'var(--color-brand-hover)',\n brandSoft: 'var(--color-brand-soft)',\n brandSofter: 'var(--color-brand-softer)',\n brandSubtle: 'var(--color-brand-subtle)',\n\n textPrimary: 'var(--color-text-primary)',\n textSecondary: 'var(--color-text-secondary)',\n textMuted: 'var(--color-text-muted)',\n textSubtle: 'var(--color-text-subtle)',\n textHeading: 'var(--color-text-heading)',\n textDark: 'var(--color-text-dark)',\n textPlaceholder: 'var(--color-text-placeholder)',\n textInverse: 'var(--color-text-inverse)',\n textLink: 'var(--color-text-link)',\n\n surfacePage: 'var(--color-surface-page)',\n surfaceSoft: 'var(--color-surface-soft)',\n surfaceSofter: 'var(--color-surface-softer)',\n surfaceCard: 'var(--color-surface-card)',\n surfaceHover: 'var(--color-surface-hover)',\n surfaceHoverLight: 'var(--color-surface-hover-light)',\n surfaceHoverSoft: 'var(--color-surface-hover-soft)',\n surfaceSelected: 'var(--color-surface-selected)',\n surfaceControl: 'var(--color-surface-control)',\n surfaceRowAlt: 'var(--color-surface-row-alt)',\n\n border: 'var(--color-border)',\n borderLight: 'var(--color-border-light)',\n borderSofter: 'var(--color-border-softer)',\n borderMuted: 'var(--color-border-muted)',\n borderInput: 'var(--color-border-input)',\n borderFocus: 'var(--color-border-focus)',\n borderHover: 'var(--color-border-hover)',\n controlBorder: 'var(--color-control-border)',\n controlBorderMuted: 'var(--color-control-border-muted)',\n controlAccent: 'var(--color-control-accent)',\n\n iconMuted: 'var(--color-icon-muted)',\n iconSubtle: 'var(--color-icon-subtle)',\n iconNeutral: 'var(--color-icon-neutral)',\n iconSoft: 'var(--color-icon-soft)',\n scrollbarThumb: 'var(--color-scrollbar-thumb)',\n scrollbarThumbLight: 'var(--color-scrollbar-thumb-light)',\n danger: 'var(--color-danger)',\n dangerSoft: 'var(--color-danger-soft)',\n dangerStrong: 'var(--color-danger-strong)',\n success: 'var(--color-success)',\n successSoft: 'var(--color-success-soft)',\n warning: 'var(--color-warning)',\n info: 'var(--color-info)',\n transparent: 'var(--color-transparent)',\n linkedIn: 'var(--color-linkedin)',\n\n statusNeutralBg: 'var(--color-status-neutral-bg)',\n statusNeutralText: 'var(--color-status-neutral-text)',\n statusProcessingBg: 'var(--color-status-processing-bg)',\n statusProcessingText: 'var(--color-status-processing-text)',\n statusProcessingBorder: 'var(--color-status-processing-border)',\n statusWarningBg: 'var(--color-status-warning-bg)',\n statusWarningText: 'var(--color-status-warning-text)',\n\n shadowMenu: 'var(--color-shadow-menu)',\n shadowBadge: 'var(--color-shadow-badge)',\n shadowTag: 'var(--color-shadow-tag)',\n shadowAvatar: 'var(--color-shadow-avatar)',\n shadowDropdown: 'var(--color-shadow-dropdown)',\n avatarBlueBg: 'var(--color-avatar-blue-bg)',\n avatarBlueText: 'var(--color-avatar-blue-text)',\n avatarPurpleBg: 'var(--color-avatar-purple-bg)',\n avatarMoreBg: 'var(--color-avatar-more-bg)',\n avatarMoreText: 'var(--color-avatar-more-text)',\n};\n","import { Typography } from 'antd';\nimport { colorVars } from '../../theme/colors/colors';\n\nconst { Text, Title, Paragraph, Link } = Typography;\n\nconst defaultElementByVariant = {\n display: 'h1',\n h1: 'h1',\n h2: 'h2',\n h3: 'h3',\n h4: 'h4',\n h5: 'h5',\n 'section-title': 'h3',\n 'card-title': 'h4',\n subtitle: 'span',\n body: 'span',\n 'body-strong': 'span',\n label: 'span',\n caption: 'span',\n meta: 'span',\n metric: 'span',\n helper: 'span',\n link: 'a',\n};\n\nconst namedSizes = {\n xs: 'var(--font-size-xs)',\n sm: 'var(--font-size-sm)',\n md: 'var(--font-size-md)',\n lg: 'var(--font-size-lg)',\n xl: 'var(--font-size-xl)',\n '2xl': 'var(--font-size-2xl)',\n '3xl': 'var(--font-size-3xl)',\n '4xl': 'var(--font-size-4xl)',\n};\n\nconst namedWeights = {\n regular: 'var(--font-weight-regular)',\n medium: 'var(--font-weight-medium)',\n semibold: 'var(--font-weight-semibold)',\n bold: 'var(--font-weight-bold)',\n extrabold: 'var(--font-weight-extrabold)',\n};\n\nconst namedLineHeights = {\n tight: 'var(--line-height-tight)',\n snug: 'var(--line-height-snug)',\n normal: 'var(--line-height-normal)',\n relaxed: 'var(--line-height-relaxed)',\n};\n\nconst namedColors = {\n primary: colorVars.textPrimary,\n secondary: colorVars.textSecondary,\n muted: colorVars.textMuted,\n subtle: colorVars.textSubtle,\n inverse: colorVars.textInverse,\n link: colorVars.textLink,\n danger: colorVars.danger,\n success: colorVars.success,\n};\n\nfunction cx(...classes) {\n return classes.filter(Boolean).join(' ');\n}\n\nfunction tokenValue(value, tokens) {\n if (value === undefined || value === null) return undefined;\n return tokens[value] || value;\n}\n\nfunction getAntTypographyComponent(tag, variant) {\n if (variant === 'link' || tag === 'a') return Link;\n if (tag === 'p') return Paragraph;\n if (['h1', 'h2', 'h3', 'h4', 'h5'].includes(tag)) return Title;\n return Text;\n}\n\nfunction getTitleLevel(tag, variant) {\n const resolvedTag = tag || defaultElementByVariant[variant];\n if (!resolvedTag?.startsWith('h')) return undefined;\n return Number(resolvedTag.slice(1));\n}\n\nexport default function AppTypography({\n as,\n tag,\n variant = 'body',\n color,\n size,\n weight,\n lineHeight,\n align,\n truncate = false,\n display,\n className,\n style,\n children,\n ...props\n}) {\n const resolvedTag = tag || as || defaultElementByVariant[variant] || 'span';\n const Component = getAntTypographyComponent(resolvedTag, variant);\n const titleLevel = getTitleLevel(resolvedTag, variant);\n const dynamicStyle = {\n color: tokenValue(color, namedColors),\n fontSize: tokenValue(size, namedSizes),\n fontWeight: tokenValue(weight, namedWeights),\n lineHeight: tokenValue(lineHeight, namedLineHeights),\n display,\n ...style,\n };\n\n return (\n <Component\n {...(titleLevel ? { level: titleLevel } : {})}\n className={cx(\n 'app-typography',\n `app-typography--${variant}`,\n color && namedColors[color] && `app-typography--${color}`,\n align && `app-typography--${align}`,\n truncate && 'app-typography--truncate',\n className,\n )}\n style={dynamicStyle}\n {...props}\n >\n {children}\n </Component>\n );\n}\n","/**\n * TipTapEditor — Rich text editor component\n * Drop-in replacement for ReactQuill in EditFormV1\n *\n * Props:\n * value string — HTML string (controlled)\n * onChange function — called with HTML string on every change\n * disabled boolean — makes editor read-only\n * placeholder string — placeholder text\n */\n\nimport { useEffect, useRef } from 'react';\nimport { useEditor, EditorContent } from '@tiptap/react';\nimport StarterKit from '@tiptap/starter-kit';\nimport Link from '@tiptap/extension-link';\nimport Underline from '@tiptap/extension-underline';\nimport '../styles/TipTapEditor.css';\n\n// ─── Toolbar button ───────────────────────────────────────────────────────────\n\nfunction ToolbarButton({ onClick, active, disabled, title, children }) {\n return (\n <button\n type=\"button\"\n title={title}\n disabled={disabled}\n className={`tte-btn${active ? ' tte-btn--active' : ''}`}\n onMouseDown={(e) => {\n e.preventDefault(); // prevent editor losing focus\n onClick?.();\n }}\n >\n {children}\n </button>\n );\n}\n\n// ─── Toolbar ─────────────────────────────────────────────────────────────────\n\nfunction Toolbar({ editor, disabled }) {\n if (!editor) return null;\n\n const setLink = () => {\n const url = window.prompt('Enter URL');\n if (!url) {\n editor.chain().focus().unsetLink().run();\n return;\n }\n editor.chain().focus().setLink({ href: url }).run();\n };\n\n return (\n <div className={`tte-toolbar${disabled ? ' tte-toolbar--disabled' : ''}`}>\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Bold\"\n disabled={disabled}\n active={editor.isActive('bold')}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <strong>B</strong>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Italic\"\n disabled={disabled}\n active={editor.isActive('italic')}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <em>I</em>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Underline\"\n disabled={disabled}\n active={editor.isActive('underline')}\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n >\n <span style={{ textDecoration: 'underline' }}>U</span>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Strikethrough\"\n disabled={disabled}\n active={editor.isActive('strike')}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <s>S</s>\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Bullet List\"\n disabled={disabled}\n active={editor.isActive('bulletList')}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n ≡\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Ordered List\"\n disabled={disabled}\n active={editor.isActive('orderedList')}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n 1.\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Link\"\n disabled={disabled}\n active={editor.isActive('link')}\n onClick={setLink}\n >\n 🔗\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Clear formatting\"\n disabled={disabled}\n onClick={() => editor.chain().focus().clearNodes().unsetAllMarks().run()}\n >\n ✕\n </ToolbarButton>\n </div>\n </div>\n );\n}\n\n// ─── Main editor ──────────────────────────────────────────────────────────────\n\nexport default function TipTapEditor({\n value = '',\n onChange,\n disabled = false,\n placeholder = '',\n}) {\n // Tracks the last value this editor itself produced (via onUpdate) or was\n // last told to show (via the sync effect below) — NOT the editor's live\n // getHTML(), which can drift from `value` after a round-trip through\n // TipTap's HTML serializer (e.g. plain AI-generated text with no <p> tags\n // never equals its own wrapped-in-<p> serialization, which previously made\n // the old getHTML()-based comparison useless as a \"did this come from us\"\n // check and caused an external update to fight with a stale echo).\n const lastKnownValueRef = useRef(value ?? '');\n\n const editor = useEditor({\n extensions: [\n StarterKit,\n Underline,\n Link.configure({\n openOnClick: false,\n HTMLAttributes: { rel: 'noopener noreferrer' },\n }),\n ],\n content: value, // set initial content correctly on mount\n editable: !disabled,\n editorProps: {\n attributes: {\n class: 'tte-content',\n },\n },\n onUpdate: ({ editor }) => {\n const html = editor.getHTML();\n const next = html === '<p></p>' ? '' : html;\n lastKnownValueRef.current = next;\n onChange?.(next);\n },\n });\n\n // Sync value from outside — handles setFieldsValue from Ant Design form\n // (e.g. an AI Action replacing the content). Skips only when the incoming\n // value is exactly what this editor itself last emitted, so a genuine\n // external update always applies even if it differs from getHTML() purely\n // due to HTML serialization (missing <p> wrapper, entity encoding, etc).\n useEffect(() => {\n if (!editor || editor.isDestroyed) return;\n const nextValue = value || '';\n if (nextValue === (lastKnownValueRef.current || '')) return;\n lastKnownValueRef.current = nextValue;\n editor.commands.setContent(nextValue, false);\n }, [value, editor]);\n\n // Sync disabled state\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(!disabled);\n }, [disabled, editor]);\n\n return (\n <div className={`tte-wrapper${disabled ? ' tte-wrapper--disabled' : ''}`}>\n <Toolbar editor={editor} disabled={disabled} />\n <EditorContent editor={editor} />\n {!value && !editor?.isFocused && placeholder && (\n <div className=\"tte-placeholder\">{placeholder}</div>\n )}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { Modal, Spin, Tooltip, message } from 'antd';\nimport {\n CloseOutlined,\n DownloadOutlined,\n FileUnknownOutlined,\n LeftOutlined,\n RightOutlined,\n ZoomInOutlined,\n ZoomOutOutlined,\n} from '@ant-design/icons';\nimport { Document, Page, pdfjs } from 'react-pdf';\nimport { renderAsync } from 'docx-preview';\nimport DOMPurify from 'dompurify';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\nimport '../styles/DocumentViewer.css';\n\n// pdf.js needs a web worker; Vite resolves this URL at build time so it works\n// in dev and production without copying files into /public.\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n 'pdfjs-dist/build/pdf.worker.min.mjs',\n import.meta.url,\n).toString();\n\nconst IMAGE_EXT = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'avif', 'ico'];\nconst TEXT_EXT = ['txt', 'csv', 'log', 'json', 'md', 'xml'];\nconst VIDEO_EXT = ['mp4', 'webm', 'ogg', 'ogv', 'mov', 'm4v'];\nconst ZOOM_STEP = 0.2;\nconst ZOOM_MIN = 0.4;\nconst ZOOM_MAX = 3;\n\nfunction fileNameFromUrl(url) {\n if (!url) return 'Document';\n const clean = String(url).split('?')[0].split('#')[0];\n return decodeURIComponent(clean.split('/').pop() || clean) || 'Document';\n}\n\nfunction extOf(nameOrUrl) {\n const clean = String(nameOrUrl || '').split('?')[0].split('#')[0];\n const dot = clean.lastIndexOf('.');\n return dot === -1 ? '' : clean.slice(dot + 1).toLowerCase();\n}\n\nconst KNOWN_BUCKETS = ['pdf', 'docx', 'image', 'text', 'video', 'html'];\n\n// bucketFromExt maps a bare extension (no dot) to a renderer bucket, or null.\nfunction bucketFromExt(ext) {\n if (!ext) return null;\n if (ext === 'pdf') return 'pdf';\n if (ext === 'doc' || ext === 'docx') return 'docx';\n if (IMAGE_EXT.includes(ext)) return 'image';\n if (TEXT_EXT.includes(ext)) return 'text';\n if (VIDEO_EXT.includes(ext)) return 'video';\n return null;\n}\n\n// kindOf maps a document to a renderer bucket: pdf | docx | image | text |\n// video | html | unknown.\n//\n// `doc.type` is normalized rather than trusted verbatim, because it arrives\n// in different shapes depending on the caller: an already-correct bucket\n// name (\"image\", inline html), a bare file extension as the backend upload\n// handler stores it (\"jpg\", \"png\", \"pdf\" — see timesheetController.go's\n// `ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(...), \".\"))`), or a\n// MIME type (\"image/jpeg\"). Returning a bare extension straight through only\n// happened to work for \"pdf\"/\"docx\" because those extensions equal their own\n// bucket name — every image extension (\"jpg\", \"png\", …) doesn't equal\n// \"image\", so it silently fell through to the \"no preview available\"\n// fallback. This maps all three shapes through the same extension table.\nfunction kindOf(doc) {\n const rawType = String(doc.type || '').toLowerCase().trim();\n if (KNOWN_BUCKETS.includes(rawType)) return rawType;\n\n const mimeExt = rawType.includes('/') ? rawType.split('/').pop() : rawType;\n const fromType = bucketFromExt(mimeExt);\n if (fromType) return fromType;\n\n const fromName = bucketFromExt(extOf(doc.name || doc.url));\n if (fromName) return fromName;\n\n return 'unknown';\n}\n\n// normalizeDocs accepts either bare URL strings, file objects, or inline\n// document objects ({ name, type: 'text'|'html', content }).\nfunction normalizeDocs(documents) {\n return (documents || [])\n .map((d, i) => {\n if (typeof d === 'string') {\n return { url: d, name: fileNameFromUrl(d), key: String(i) };\n }\n const url = d.url || d.location || d.path || '';\n return {\n url,\n name: d.name || fileNameFromUrl(url),\n type: d.type,\n content: typeof d.content === 'string' ? d.content : '',\n key: String(d.id ?? d._id ?? i),\n };\n })\n .filter((d) => d.url || d.content);\n}\n\n/* ------------------------------- renderers ------------------------------- */\n\nfunction PdfRenderer({ url, scale, onNativeFallback }) {\n const wrapRef = useRef(null);\n const [numPages, setNumPages] = useState(0);\n const [width, setWidth] = useState(0);\n const [error, setError] = useState(false);\n\n useEffect(() => {\n const el = wrapRef.current;\n if (!el) return undefined;\n const update = () => setWidth(el.clientWidth);\n update();\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n // pdf.js streams the file via its own fetch, which fails cross-origin on\n // storage URLs the bucket's CORS policy doesn't allowlist this origin for\n // (common — see the S3 bucket's CORS config, not something fixable here) —\n // distinct from a plain navigation (an <iframe> load), which is NOT subject\n // to CORS at all and works regardless. Fall back to that rather than\n // dead-ending the preview; the native viewer brings its own zoom/controls,\n // so the toolbar hides its (now inert) zoom buttons via onNativeFallback.\n //\n // This intentionally points the iframe at the raw presigned `url`, not a\n // fetched blob — fetching it would hit the same CORS wall react-pdf just\n // did. Whether this renders inline vs. downloads depends entirely on the\n // `Content-Disposition` the presigned URL responds with; that's set\n // server-side (GetPresignedURLInline in Be_Auth_DevOps) rather than worked\n // around here, so every consumer of the URL — this iframe, a plain link,\n // anything — gets the same correct inline behavior.\n if (error) {\n return <iframe title=\"PDF preview\" src={url} className=\"dv-pdf-native\" />;\n }\n\n return (\n <div ref={wrapRef} className=\"dv-pdf-wrap\">\n <Document\n file={url}\n loading={<Spin />}\n error={<FallbackStage label=\"This PDF could not be displayed.\" />}\n onLoadSuccess={({ numPages: n }) => setNumPages(n)}\n onLoadError={() => {\n setError(true);\n onNativeFallback?.();\n }}\n >\n {Array.from({ length: numPages }, (_, i) => (\n <Page\n key={`page-${i + 1}`}\n pageNumber={i + 1}\n width={width ? width * scale : undefined}\n className=\"dv-pdf-page\"\n renderTextLayer\n renderAnnotationLayer\n />\n ))}\n </Document>\n </div>\n );\n}\n\nPdfRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n scale: PropTypes.number.isRequired,\n onNativeFallback: PropTypes.func,\n};\n\nfunction DocxRenderer({ url }) {\n const ref = useRef(null);\n const [status, setStatus] = useState('loading'); // loading | ready | error\n\n useEffect(() => {\n let cancelled = false;\n\n fetch(url)\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return r.blob();\n })\n .then((blob) => {\n if (cancelled || !ref.current) return undefined;\n ref.current.innerHTML = '';\n return renderAsync(blob, ref.current, undefined, {\n className: 'dv-docx',\n inWrapper: true,\n ignoreWidth: false,\n ignoreHeight: false,\n });\n })\n .then(() => {\n if (!cancelled) setStatus('ready');\n })\n .catch(() => {\n if (!cancelled) setStatus('error');\n });\n\n return () => {\n cancelled = true;\n };\n }, [url]);\n\n if (status === 'error') {\n return <FallbackStage label=\"This document could not be displayed.\" />;\n }\n\n return (\n <div className=\"dv-docx-scroll\">\n {status === 'loading' && <div className=\"dv-center\"><Spin /></div>}\n <div ref={ref} style={{ visibility: status === 'ready' ? 'visible' : 'hidden' }} />\n </div>\n );\n}\n\nDocxRenderer.propTypes = { url: PropTypes.string.isRequired };\n\nfunction TextRenderer({ url, content = '' }) {\n const [text, setText] = useState(null);\n const [status, setStatus] = useState('loading');\n\n useEffect(() => {\n if (content) return undefined;\n\n let cancelled = false;\n fetch(url)\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return r.text();\n })\n .then((t) => {\n if (!cancelled) {\n setText(t);\n setStatus('ready');\n }\n })\n .catch(() => !cancelled && setStatus('error'));\n return () => {\n cancelled = true;\n };\n }, [content, url]);\n\n if (content) return <pre className=\"dv-text\">{content}</pre>;\n if (status === 'loading') return <div className=\"dv-center\"><Spin /></div>;\n if (status === 'error') return <FallbackStage label=\"This file could not be displayed.\" />;\n return <pre className=\"dv-text\">{text}</pre>;\n}\n\nTextRenderer.propTypes = {\n url: PropTypes.string,\n content: PropTypes.string,\n};\n\nfunction HtmlRenderer({ content }) {\n const safeHtml = useMemo(() => DOMPurify.sanitize(content), [content]);\n return <div className=\"dv-html\" dangerouslySetInnerHTML={{ __html: safeHtml }} />;\n}\n\nHtmlRenderer.propTypes = { content: PropTypes.string.isRequired };\n\nfunction ImageRenderer({ url, name, scale }) {\n const [error, setError] = useState(false);\n if (error) return <FallbackStage label=\"This image could not be displayed.\" />;\n return (\n <div className=\"dv-image-wrap\">\n <img\n className=\"dv-image\"\n src={url}\n alt={name}\n style={{ transform: `scale(${scale})` }}\n onError={() => setError(true)}\n />\n </div>\n );\n}\n\nImageRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n scale: PropTypes.number.isRequired,\n};\n\nfunction VideoRenderer({ url, name }) {\n const [error, setError] = useState(false);\n if (error) return <FallbackStage label=\"This video could not be played.\" />;\n return (\n <div className=\"dv-video-wrap\">\n <video\n className=\"dv-video\"\n src={url}\n title={name}\n controls\n preload=\"metadata\"\n controlsList=\"nodownload\"\n onError={() => setError(true)}\n />\n </div>\n );\n}\n\nVideoRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n};\n\nfunction FallbackStage({ label }) {\n return (\n <div className=\"dv-center dv-fallback\">\n <FileUnknownOutlined className=\"dv-fallback-icon\" />\n <p>{label}</p>\n <span className=\"dv-fallback-hint\">Use the download button to open it.</span>\n </div>\n );\n}\n\nFallbackStage.propTypes = { label: PropTypes.string.isRequired };\n\n/* ----------------------------- main component ---------------------------- */\n\n// ViewerBody holds the per-session state (active index + zoom). It lives inside\n// the Modal body, which is destroyed on close (destroyOnHidden), so it remounts\n// fresh on every open — no manual \"reset on open\" effects required.\nfunction ViewerBody({ docs, initialIndex, onClose }) {\n const total = docs.length;\n const safeInitial = Math.min(Math.max(initialIndex, 0), Math.max(0, total - 1));\n const [index, setIndex] = useState(safeInitial);\n const [scale, setScale] = useState(1);\n // Set when a PDF falls back to the browser's native viewer (see\n // PdfRenderer) — that viewer has its own zoom UI, so ours would sit there\n // doing nothing if left visible.\n const [pdfNativeFallback, setPdfNativeFallback] = useState(false);\n\n const current = docs[index];\n const kind = current ? kindOf(current) : 'unknown';\n const zoomable = (kind === 'pdf' && !pdfNativeFallback) || kind === 'image';\n\n // setActive changes the document and resets zoom/fallback in one event\n // handler, so we never have to reset them from an effect.\n const setActive = useCallback((next) => {\n setIndex(next);\n setScale(1);\n setPdfNativeFallback(false);\n }, []);\n\n const goPrev = useCallback(() => setActive(Math.max(0, index - 1)), [index, setActive]);\n const goNext = useCallback(\n () => setActive(Math.min(total - 1, index + 1)),\n [index, total, setActive],\n );\n\n // Keyboard navigation.\n useEffect(() => {\n const onKey = (e) => {\n // A focused <video> uses arrow keys to seek — don't switch documents.\n if (e.target?.tagName === 'VIDEO') return;\n if (e.key === 'ArrowLeft') goPrev();\n else if (e.key === 'ArrowRight') goNext();\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [goPrev, goNext]);\n\n const download = useCallback(async (doc) => {\n if (!doc) return;\n try {\n let blob;\n if (doc.content) {\n const mimeType = doc.type === 'html' ? 'text/html;charset=utf-8' : 'text/plain;charset=utf-8';\n blob = new Blob([doc.content], { type: mimeType });\n } else {\n const res = await fetch(doc.url);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n blob = await res.blob();\n }\n const objUrl = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = objUrl;\n a.download = doc.name || 'document';\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(objUrl);\n } catch {\n // Cross-origin without CORS: fall back to opening in a new tab.\n message.info('Opening file in a new tab…');\n if (doc.url) window.open(doc.url, '_blank', 'noopener,noreferrer');\n }\n }, []);\n\n function renderStage() {\n if (!current) {\n return <FallbackStage label=\"No document to preview.\" />;\n }\n switch (kind) {\n case 'pdf':\n return (\n <PdfRenderer\n key={current.key}\n url={current.url}\n scale={scale}\n onNativeFallback={() => setPdfNativeFallback(true)}\n />\n );\n case 'docx':\n return <DocxRenderer key={current.key} url={current.url} />;\n case 'image':\n return (\n <ImageRenderer key={current.key} url={current.url} name={current.name} scale={scale} />\n );\n case 'text':\n return <TextRenderer key={current.key} url={current.url} content={current.content} />;\n case 'html':\n return <HtmlRenderer key={current.key} content={current.content} />;\n case 'video':\n return <VideoRenderer key={current.key} url={current.url} name={current.name} />;\n default:\n return <FallbackStage label=\"Preview is not available for this file type.\" />;\n }\n }\n\n return (\n <div className=\"dv-root\">\n {/* toolbar */}\n <div className=\"dv-toolbar\">\n <div className=\"dv-title\" title={current?.name}>\n <span className=\"dv-title-text\">{current?.name || 'Document'}</span>\n {total > 1 && <span className=\"dv-counter\">{index + 1} / {total}</span>}\n </div>\n <div className=\"dv-actions\">\n {zoomable && (\n <>\n <Tooltip title=\"Zoom out\">\n <button\n type=\"button\"\n className=\"dv-icon-btn\"\n onClick={() => setScale((s) => Math.max(ZOOM_MIN, +(s - ZOOM_STEP).toFixed(2)))}\n disabled={scale <= ZOOM_MIN}\n >\n <ZoomOutOutlined />\n </button>\n </Tooltip>\n <span className=\"dv-zoom-label\">{Math.round(scale * 100)}%</span>\n <Tooltip title=\"Zoom in\">\n <button\n type=\"button\"\n className=\"dv-icon-btn\"\n onClick={() => setScale((s) => Math.min(ZOOM_MAX, +(s + ZOOM_STEP).toFixed(2)))}\n disabled={scale >= ZOOM_MAX}\n >\n <ZoomInOutlined />\n </button>\n </Tooltip>\n <span className=\"dv-divider\" />\n </>\n )}\n <Tooltip title=\"Download\">\n <button type=\"button\" className=\"dv-icon-btn dv-download\" onClick={() => download(current)}>\n <DownloadOutlined />\n </button>\n </Tooltip>\n <Tooltip title=\"Close\">\n <button type=\"button\" className=\"dv-icon-btn\" onClick={onClose}>\n <CloseOutlined />\n </button>\n </Tooltip>\n </div>\n </div>\n\n {/* stage + slider arrows */}\n <div className=\"dv-stage\">\n {total > 1 && (\n <button\n type=\"button\"\n className=\"dv-nav dv-nav--prev\"\n onClick={goPrev}\n disabled={index === 0}\n aria-label=\"Previous document\"\n >\n <LeftOutlined />\n </button>\n )}\n\n <div className=\"dv-canvas\">{renderStage()}</div>\n\n {total > 1 && (\n <button\n type=\"button\"\n className=\"dv-nav dv-nav--next\"\n onClick={goNext}\n disabled={index === total - 1}\n aria-label=\"Next document\"\n >\n <RightOutlined />\n </button>\n )}\n </div>\n\n {/* dots */}\n {total > 1 && (\n <div className=\"dv-dots\">\n {docs.map((d, i) => (\n <button\n key={d.key}\n type=\"button\"\n className={`dv-dot${i === index ? ' dv-dot--active' : ''}`}\n onClick={() => setActive(i)}\n aria-label={`Go to document ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nViewerBody.propTypes = {\n docs: PropTypes.arrayOf(PropTypes.object).isRequired,\n initialIndex: PropTypes.number.isRequired,\n onClose: PropTypes.func.isRequired,\n};\n\n// DocumentViewer is the public component: a Modal shell that mounts ViewerBody\n// only while open. `documents` may be URL strings or document objects.\n//\n// Pass `inline` to render ViewerBody directly with no Modal — used by the timesheet\n// manager review, which shows the uploaded screenshot side-by-side with the calendar\n// rather than in a popup. Everything ViewerBody already does (pdf/image/docx render,\n// zoom, download, multi-document navigation) comes along for free.\nexport default function DocumentViewer({ documents, open, onClose, initialIndex = 0, inline = false }) {\n const docs = useMemo(() => normalizeDocs(documents), [documents]);\n\n if (inline) {\n return (\n <div className=\"dv-root--inline\">\n <ViewerBody docs={docs} initialIndex={initialIndex} onClose={onClose ?? (() => {})} />\n </div>\n );\n }\n\n return (\n <Modal\n open={open}\n onCancel={onClose}\n footer={null}\n title={null}\n closable={false}\n centered\n width=\"min(1100px, 94vw)\"\n className=\"dv-modal\"\n styles={{ content: { padding: 0, overflow: 'hidden', borderRadius: 14 }, body: { padding: 0 } }}\n destroyOnHidden\n >\n <ViewerBody docs={docs} initialIndex={initialIndex} onClose={onClose} />\n </Modal>\n );\n}\n\nDocumentViewer.propTypes = {\n // URL strings, file objects, or inline objects ({ content, type: 'text'|'html' }).\n documents: PropTypes.arrayOf(\n PropTypes.oneOfType([PropTypes.string, PropTypes.object]),\n ).isRequired,\n // Required for the Modal shell; unused (and optional) when `inline` is set.\n open: PropTypes.bool,\n onClose: PropTypes.func,\n initialIndex: PropTypes.number,\n // Render the viewer body directly, with no Modal wrapper.\n inline: PropTypes.bool,\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 // 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\nexport async function searchModuleData(moduleName, searchParams = {}, pagination = {}, options = {}) {\n const { limit = 10, offset = 0 } = pagination;\n const { sort = '', sortDir = '', scope = '' } = options;\n const inferredScope = normalizeCandidateSearchSource(scope || searchParams.selectedSource);\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\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, JOBS_URL } from './apiConfig';\n\n// ── Form Groups ───────────────────────────────────────────────────────────────\n\nfunction extractFormGroups(json) {\n const payload = json?.data ?? json;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(payload?.groups)) return payload.groups;\n if (Array.isArray(json?.groups)) return json.groups;\n return [];\n}\n\nexport async function getFormGroups({ module, id, action, clientId, region, group } = {}) {\n if (!module) throw new Error('module is required to fetch form groups');\n const token = await ensureToken();\n const params = new URLSearchParams({ module });\n if (id) params.set('id', id);\n if (action) params.set('action', action);\n if (clientId) params.set('clientId', clientId);\n if (region) params.set('region', region);\n if (group) params.set('group', group);\n const res = await fetch(`${AUTH_URL}/admin/form-groups?${params.toString()}`, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);\n const json = await res.json();\n return extractFormGroups(json);\n}\n\nexport async function getCandidateFormGroups(options = {}) {\n return getFormGroups({ module: 'candidates', ...options });\n}\n\nexport async function getMentionUsers(search = '') {\n const { apiGetWithAuth } = await import('./authApi');\n const params = search ? `?search=${encodeURIComponent(search)}` : '';\n return apiGetWithAuth(JOBS_URL, `/users/any${params}`);\n}\n\n// ── Create Records ────────────────────────────────────────────────────────────\n// Routes through the Auth gateway which injects tenant context from JWT\n// and proxies to the correct downstream service based on module name.\n\nexport async function createModuleRecord(moduleName, formData) {\n // ensureToken (not getStoredToken) so a missing/expired session throws a\n // clear \"Authentication required\" error instead of silently sending the\n // request with no Authorization header (which the gateway rejects as 401).\n // NOTE: do NOT set Content-Type here — the browser must add the multipart\n // boundary itself. Only the Authorization header is set manually.\n const token = await ensureToken();\n\n const res = await fetch(\n `${AUTH_URL}/module/create?module=${encodeURIComponent(moduleName)}`,\n { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData },\n );\n\n const { logout } = await import('./authApi');\n if (res.status === 401) logout();\n\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('application/json') ? await res.json() : await res.text();\n\n if (!res.ok) {\n const error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\n error.status = res.status;\n error.data = data;\n throw error;\n }\n\n return data;\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 const res = await fetch(`${AUTH_URL}/documents/upload?${params.toString()}`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\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 error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\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","import { ensureToken, fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL, JOBS_URL, CANDIDATES_URL, SUBMISSIONS_URL } from './apiConfig';\n\n// ── Module Detail & Record Edit ───────────────────────────────────────────────\n\n// fetchUrl — direct downstream (read-only, no tenant injection needed)\n// updateUrl is no longer used; updates go through the Auth gateway.\nconst MODULE_EDIT_CONFIG = {\n job: {\n fetchUrl: (id) => `${JOBS_URL}/edit/detailed-view/${encodeURIComponent(id)}`,\n },\n candidate: {\n fetchUrl: (id) => `${CANDIDATES_URL}/edit/detailed-view?_id=${encodeURIComponent(id)}`,\n },\n candidates: {\n fetchUrl: (id) => `${CANDIDATES_URL}/edit/detailed-view?_id=${encodeURIComponent(id)}`,\n },\n submission: {\n fetchUrl: (id) => `${SUBMISSIONS_URL}/edit/detailed-view?_id=${encodeURIComponent(id)}`,\n },\n};\n\nexport async function getModuleDataDetail(module, id) {\n const normalizedModule = String(module ?? '').trim().toLowerCase();\n\n if (normalizedModule === 'job' || normalizedModule === 'jobs') {\n const { getJobDetailView } = await import('./jobsApi');\n return getJobDetailView(id);\n }\n\n if (normalizedModule === 'candidate' || normalizedModule === 'candidates') {\n return getRecordForEdit(normalizedModule === 'candidate' ? 'candidate' : 'candidates', id);\n }\n\n const params = new URLSearchParams({ module: String(module ?? ''), id: String(id) });\n return fetchJsonWithAuth(AUTH_URL, `/module-data-detail?${params.toString()}`);\n}\n\nexport async function getModuleActions(module) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/module-actions?module=${encodeURIComponent(module)}`,\n );\n const data = json?.data ?? json ?? {};\n return {\n actions: Array.isArray(data.actions) ? data.actions : [],\n actionRules: Array.isArray(data.actionRules) ? data.actionRules : [],\n };\n}\n\nexport async function getRecordForEdit(module, id) {\n if (!module) throw new Error('module is required');\n if (!id) throw new Error('id is required');\n\n const config = MODULE_EDIT_CONFIG[module];\n if (!config) throw new Error(`No edit config for module: ${module}`);\n\n const token = await ensureToken();\n const res = await fetch(config.fetchUrl(id), {\n method: 'GET',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);\n const json = await res.json();\n return json.data ?? json;\n}\n\nexport async function updateRecord(module, id, payload, fileParts = []) {\n if (!module) throw new Error('module is required');\n if (!id) throw new Error('id is required');\n\n const token = await ensureToken();\n const formData = new FormData();\n formData.append('json', JSON.stringify(payload));\n // New file uploads ride along in the SAME multipart, under the part name the\n // downstream update handler reads (jobs → \"file\", candidates → resume/passport/\n // documents/…). The gateway forwards them and the downstream stores + returns\n // their name/location. Existing files (no originFileObj) are not re-sent.\n (fileParts ?? []).forEach(({ formKey, file }) => formData.append(formKey, file));\n\n const res = await fetch(\n `${AUTH_URL}/module/update/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`,\n { method: 'PUT', headers: { Authorization: `Bearer ${token}` }, body: formData },\n );\n\n if (!res.ok) {\n const errorText = await res.text();\n console.error('[updateRecord] Error response:', errorText);\n const error = new Error(serverErrorMessage(errorText) || `API ${res.status}: ${res.statusText}`);\n // The MESSAGE stays exactly what it was, but the structured body rides along\n // now. A duplicate-value 409 carries `errors[]` — including the group and row\n // index of a repeatable-group field — and flattening it to a string was\n // throwing that away, leaving the form to guess the field from the message\n // text and with no way at all to know WHICH ROW was rejected.\n error.status = res.status;\n error.response = parseErrorBody(errorText);\n throw error;\n }\n\n const json = await res.json();\n return json.data ?? json;\n}\n\n// parseErrorBody returns the parsed JSON envelope, or null for a non-JSON body\n// (a proxy/gateway HTML error page). Never throws.\nfunction parseErrorBody(body) {\n const text = String(body ?? '').trim();\n if (!text.startsWith('{')) return null;\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\n// The gateway reports failures as {status:false, error:\"…\"} (utils.ERROR). A\n// rejected write is often something the user can act on — \"amount received\n// exceeds the remaining balance\" — and submitErrorMessage surfaces this text\n// verbatim in the toast, so hand it the message rather than the JSON envelope.\n// Non-JSON bodies (proxy/gateway HTML) fall through unchanged.\nfunction serverErrorMessage(body) {\n const text = String(body ?? '').trim();\n if (!text.startsWith('{')) return text;\n try {\n const parsed = JSON.parse(text);\n const message = parsed?.error ?? parsed?.message;\n return typeof message === 'string' && message.trim() ? message.trim() : text;\n } catch {\n return text;\n }\n}\n\n// ── Activity & Notes ──────────────────────────────────────────────────────────\n\nconst unwrap = (json) => json?.data ?? json;\n\nexport async function getActivity(module, id) {\n if (!module || !id) return [];\n const params = new URLSearchParams({ module, id: String(id) });\n const json = await fetchJsonWithAuth(AUTH_URL, `/module-activity?${params.toString()}`);\n const data = unwrap(json);\n return Array.isArray(data) ? data : [];\n}\n\nexport async function getNotes(relatedId) {\n if (!relatedId) return [];\n const json = await fetchJsonWithAuth(AUTH_URL, `/notes?relatedId=${encodeURIComponent(relatedId)}`);\n const data = unwrap(json);\n return Array.isArray(data) ? data : [];\n}\n\nexport async function createNote({ relatedId, notes, title, notesFor }) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/notes', {\n method: 'POST',\n body: JSON.stringify({ relatedId, notes, title, notesFor }),\n });\n return unwrap(json);\n}\n\nexport async function uploadJobAttachment(recordId, file) {\n const { ensureToken } = await import('./authApi');\n const token = await ensureToken();\n\n const formData = new FormData();\n formData.append('file', file);\n formData.append('jobId', recordId);\n\n const res = await fetch(`${JOBS_URL}/jobs/${recordId}/attachment`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n\n if (!res.ok) {\n const errorText = await res.text();\n throw new Error(errorText || `API ${res.status}: ${res.statusText}`);\n }\n\n const json = await res.json();\n return json.data ?? json;\n}\n","// Runtime gate consulted by AddFormV1/EditFormV1 so a role's Form\n// Configuration (Role Configure → Form) actually restricts the real add/edit\n// form, not just the admin preview. rolePerms is the array returned by\n// GET /admin/role-form-permissions (see adminApi.js getRoleFormPermissions):\n// [{ name, enabled, locked, fields: [{ field, enabled, locked }] }].\n//\n// Fails OPEN (returns true) whenever rolePerms is null/empty/not-yet-loaded —\n// a module or role with no derived permissions behaves exactly as before\n// (gated only by Form Groups' own show/visiblePermission), so this is purely\n// additive and cannot hide a field that used to render.\nexport function groupAllowedByRole(rolePerms, groupName) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n return group.enabled !== false;\n}\n\nexport function fieldAllowedByRole(rolePerms, groupName, fieldKey) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n const field = group.fields?.find((f) => f.field === fieldKey);\n if (!field) return true;\n return field.enabled !== false;\n}\n\n// Editability gate — the \"disable\" side of Role Configure → Form. A group or\n// field marked editable:false is still SHOWN but rendered read-only. Fails\n// OPEN (returns true = editable) when nothing is configured, so a role/module\n// with no derived permissions stays fully editable, exactly as before.\nexport function groupEditableByRole(rolePerms, groupName) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n return group.editable !== false;\n}\n\nexport function fieldEditableByRole(rolePerms, groupName, fieldKey) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n if (group.editable === false) return false; // group disabled → every field read-only\n const field = group.fields?.find((f) => f.field === fieldKey);\n if (!field) return true;\n return field.editable !== false;\n}\n","import 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\nconst ATTACK_PATTERNS = [\n { re: /<\\s*\\/?\\s*(?:script|iframe|object|embed|svg|img|form|input|style|link|meta)\\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: /\\bunion\\s+(?:all\\s+)?select\\b|\\b(?:drop\\s+table|delete\\s+from|insert\\s+into|xp_cmdshell)\\b/i, message: 'Database command patterns are not allowed' },\n { re: /(?:['\"]\\s*)?\\b(?:or|and)\\s+\\d+\\s*=\\s*\\d+/i, message: 'Injection patterns are not allowed' },\n { re: /[\"']?\\$(?:where|gt|gte|lt|lte|ne|regex|or|and|expr|function)\\b/i, message: 'MongoDB operators are not allowed in input values' },\n { re: /(?:\\.\\.[/\\\\])|(?:%2e|%2f|%5c)/i, message: 'Path traversal patterns are not allowed' },\n { re: /&&|\\|\\||\\$\\(|\\$\\{|`|\\b(?:rm\\s+-rf|cat\\s+\\/etc\\/|whoami\\b|curl\\s+https?:\\/\\/|wget\\s+https?:\\/\\/)/i, message: 'Command execution patterns are not allowed' },\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 (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 if (field.type !== 'text-editor') {\n const attack = ATTACK_PATTERNS.find(({ re }) => re.test(canonical));\n if (attack) return `${label}: ${attack.message}`;\n } else {\n const dangerousRichText = ATTACK_PATTERNS.slice(0, 3).find(({ re }) => re.test(canonical));\n if (dangerousRichText) return `${label}: ${dangerousRichText.message}`;\n }\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","// 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","/**\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 { snapToOption } from './optionMatching';\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 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 '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 */\nexport function shapeFieldValue(field, rawValue) {\n if (field.payloadMode === 'skip') return { kind: 'skip' };\n\n let raw = rawValue;\n if (isEmpty(raw) && field.defaultValue !== undefined && field.defaultValue !== '') {\n raw = field.defaultValue;\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 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 // 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 const fallbackEditDefault = () =>\n field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== ''\n ? field.defaultValue\n : 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) && field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== '') {\n return field.defaultValue;\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 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","// linkedAddRowGroups.js\n//\n// Pure helpers for group.linkGroup — an opt-in mechanism that keeps two or\n// more addRow groups' rows synchronized (same add/remove, same row count)\n// while each group keeps rendering as its own Card and submitting its own\n// independent payload array (buildPayload / buildAddRowInitial are untouched\n// and stay fully per-group — see payloadTransformer.js / applyGroupValues.js).\n//\n// A group only participates when BOTH group.addRow is true AND group.linkGroup\n// is a non-empty string shared by at least one other group. Every group that\n// predates this field (i.e. every group in production today) has no\n// linkGroup, so these helpers are a strict no-op for it.\n\nconst truthy = (value) => value === true || value === 1 || value === '1';\n\n// computeLinkedSets(groups) -> Map<linkGroupKey, { leaderName, memberNames: string[] }>\n//\n// `groups` is expected already order-sorted (normalizeGroups sorts by `order`\n// before render), so the first member encountered per key is the leader —\n// Array.prototype.sort is stable, so this matches the existing order\n// convention with no extra tie-break logic needed. A \"set\" of size 1 (nothing\n// else shares that linkGroup value) is dropped — a lone linkGroup value isn't\n// meaningfully linked to anything, and the group renders as if unlinked.\nexport function computeLinkedSets(groups = []) {\n const byKey = new Map();\n groups.forEach((group) => {\n const key = group?.linkGroup;\n if (!truthy(group?.addRow) || !key) return;\n if (!byKey.has(key)) byKey.set(key, []);\n byKey.get(key).push(group.name);\n });\n\n const sets = new Map();\n byKey.forEach((memberNames, key) => {\n if (memberNames.length < 2) return;\n sets.set(key, { leaderName: memberNames[0], memberNames });\n });\n return sets;\n}\n\n// findLinkedSet(linkedSets, groupName) -> { leaderName, memberNames } | null\n// Cheap membership lookup against an already-computed sets Map (compute once\n// per render via computeLinkedSets, look up per group here — avoids\n// recomputing the whole map on every group).\nexport function findLinkedSet(linkedSets, groupName) {\n for (const set of linkedSets.values()) {\n if (set.memberNames.includes(groupName)) return set;\n }\n return null;\n}\n\n// padLinkedInitialValues(linkedSets, initialValuesByGroupName) -> a new\n// { [groupName]: rows[] } object where every member of a linked set is padded\n// with {} placeholder rows up to the set's max length. Guards against\n// pre-existing data where two linked groups' stored `rows` happen to differ\n// in length (undefined territory otherwise — buildAddRowInitial/\n// getAddRowInitialValue compute each group's rows fully independently). A\n// no-op passthrough for any group not in a real (>=2 member) linked set.\nexport function padLinkedInitialValues(linkedSets, initialValuesByGroupName = {}) {\n const next = { ...initialValuesByGroupName };\n linkedSets.forEach(({ memberNames }) => {\n const maxLen = Math.max(...memberNames.map((name) => (next[name] ?? []).length));\n memberNames.forEach((name) => {\n const rows = next[name] ?? [];\n if (rows.length < maxLen) {\n next[name] = [...rows, ...Array.from({ length: maxLen - rows.length }, () => ({}))];\n }\n });\n });\n return next;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// formDecisionDialog — the popups the Add form asks questions through.\n//\n// WHAT WAS WRONG WITH THE OLD ONES\n// They were bare Modal.confirm calls: a title, one run-on sentence, \"OK\" and\n// \"Cancel\". Three problems, all of them the same problem — the user could not\n// see what they were deciding about:\n//\n// • the FACTS were buried in prose (\"…— Asha · Rao — last updated about 2\n// months ago. Continue to update…\"), so the two things that actually decide\n// the answer (who, how stale) had to be read out of a paragraph;\n// • \"OK\" names no outcome, and neither choice here is obviously the default;\n// • nothing distinguished \"we found a possible duplicate\" from \"this will\n// overwrite your work\" — different stakes, identical dialog.\n//\n// WHAT THIS DOES INSTEAD\n// A tone-coloured icon states the kind of decision at a glance; the heading is\n// clearly heavier than the body; the facts sit in a scannable card ABOVE the\n// prose; and the buttons name their outcomes. The tone drives colour only —\n// never meaning on its own, so it still reads correctly in monochrome.\n//\n// Imperative on purpose: it is called from hooks and event handlers that need\n// to await an answer, so it returns a Promise<boolean> exactly like\n// Modal.confirm did, and every existing call site keeps its shape.\n// ─────────────────────────────────────────────────────────────────────────\nimport { Modal } from 'antd';\nimport {\n ExclamationCircleFilled,\n InfoCircleFilled,\n FileTextOutlined,\n UserOutlined,\n} from '@ant-design/icons';\nimport './formDecisionDialog.css';\n\nconst TONES = {\n // \"We think these are the same person\" — a judgement, not a failure.\n duplicate: { className: 'fdd-tone-amber', Icon: UserOutlined },\n // \"This will replace what you typed\" — a real risk to work already done.\n overwrite: { className: 'fdd-tone-amber', Icon: ExclamationCircleFilled },\n // \"Shall I tidy this for you?\" — no stakes at all.\n suggestion: { className: 'fdd-tone-blue', Icon: InfoCircleFilled },\n file: { className: 'fdd-tone-blue', Icon: FileTextOutlined },\n};\n\n/**\n * openFormDecision — ask a question and resolve to the user's answer.\n *\n * @param {object} opts\n * @param {string} opts.tone duplicate | overwrite | suggestion | file\n * @param {string} opts.title the heading — say the SITUATION, not \"Are you sure?\"\n * @param {string} opts.body one or two sentences of context\n * @param {Array} [opts.facts] [{ label, value }] — shown as a scannable card\n * @param {string} opts.okText names the outcome, never \"OK\"\n * @param {string} opts.cancelText\n * @param {boolean} [opts.danger] style the primary action as destructive\n * @param {string} [opts.footnote] a quiet line under the buttons\n * @returns {Promise<boolean>}\n */\nexport function openFormDecision({\n tone = 'suggestion',\n title,\n body,\n facts = [],\n okText = 'Continue',\n cancelText = 'Cancel',\n danger = false,\n footnote,\n} = {}) {\n const { className, Icon } = TONES[tone] ?? TONES.suggestion;\n const usableFacts = facts.filter((f) => f && f.value !== undefined && f.value !== null && String(f.value).trim() !== '');\n\n return new Promise((resolve) => {\n Modal.confirm({\n // antd's own icon is suppressed: this dialog renders its own, sized and\n // coloured with the heading rather than floating beside the body.\n icon: null,\n centered: true,\n width: 480,\n className: `fdd-modal ${className}`,\n okText,\n cancelText,\n okButtonProps: { danger, size: 'large' },\n cancelButtonProps: { size: 'large' },\n content: (\n <div className=\"fdd\">\n <div className=\"fdd-head\">\n <span className=\"fdd-icon\" aria-hidden=\"true\"><Icon /></span>\n <h3 className=\"fdd-title\">{title}</h3>\n </div>\n\n {/* The facts come FIRST and are scannable. This is the part that\n actually answers \"is this the same person?\" — reading it out of a\n sentence is work the reader should not have to do. */}\n {usableFacts.length > 0 && (\n <dl className=\"fdd-facts\">\n {usableFacts.map((f) => (\n <div className=\"fdd-fact\" key={f.label}>\n <dt>{f.label}</dt>\n <dd>{f.value}</dd>\n </div>\n ))}\n </dl>\n )}\n\n {body && <p className=\"fdd-body\">{body}</p>}\n {footnote && <p className=\"fdd-footnote\">{footnote}</p>}\n </div>\n ),\n onOk: () => resolve(true),\n onCancel: () => resolve(false),\n });\n });\n}\n\n/**\n * openFormNotice — a one-way message (the module refuses to continue).\n * Same shell, single action, so a block and a choice look like relatives\n * rather than two unrelated dialogs.\n */\nexport function openFormNotice({ tone = 'duplicate', title, body, facts = [], okText = 'Go back' } = {}) {\n const { className, Icon } = TONES[tone] ?? TONES.suggestion;\n const usableFacts = facts.filter((f) => f && String(f.value ?? '').trim() !== '');\n\n return new Promise((resolve) => {\n Modal.warning({\n icon: null,\n centered: true,\n width: 480,\n className: `fdd-modal ${className}`,\n okText,\n okButtonProps: { size: 'large' },\n content: (\n <div className=\"fdd\">\n <div className=\"fdd-head\">\n <span className=\"fdd-icon\" aria-hidden=\"true\"><Icon /></span>\n <h3 className=\"fdd-title\">{title}</h3>\n </div>\n {usableFacts.length > 0 && (\n <dl className=\"fdd-facts\">\n {usableFacts.map((f) => (\n <div className=\"fdd-fact\" key={f.label}>\n <dt>{f.label}</dt>\n <dd>{f.value}</dd>\n </div>\n ))}\n </dl>\n )}\n {body && <p className=\"fdd-body\">{body}</p>}\n </div>\n ),\n onOk: () => resolve(true),\n });\n });\n}\n","import { ensureToken } from './authApi';\nimport { AUTH_URL } from './apiConfig';\n\n// ── AI Actions ──────────────────────────────────────────────────────────────\n// Generic, config-driven \"call an AI/automation service and get a JSON\n// response back\" call. The gateway resolves WHICH service/endpoint to hit\n// entirely from admin config (module/group/field/action) — this file never\n// carries a service URL, module name, or field name as a hardcoded value.\n\n/**\n * @param {Object} args\n * @param {string} args.module\n * @param {string} args.group - FormGroup.name the field lives in\n * @param {string} args.field - FormGroupField.field the action is attached to\n * @param {string} args.actionKey - AiActionConfig.key to run\n * @param {Object} [args.inputs] - { [param]: textValue } for this action's text inputs\n * @param {Object} [args.files] - { [param]: File } for this action's file inputs\n * @returns {Promise<{ raw: any, actionKey: string, responseMappings: any[] }>}\n */\nexport async function runAiAction({ module, group, field, actionKey, inputs = {}, files = {} }) {\n if (!module || !group || !field || !actionKey) {\n throw new Error('module, group, field and actionKey are required to run an AI action');\n }\n const token = await ensureToken();\n\n const formData = new FormData();\n formData.append('json', JSON.stringify({ inputs }));\n Object.entries(files).forEach(([param, file]) => {\n if (file) formData.append(param, file);\n });\n\n const params = new URLSearchParams({ module, group, field, action: actionKey });\n const res = await fetch(`${AUTH_URL}/ai-action?${params.toString()}`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n\n const { logout } = await import('./authApi');\n if (res.status === 401) logout();\n\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('application/json') ? await res.json() : await res.text();\n\n if (!res.ok) {\n const error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\n error.status = res.status;\n error.data = data;\n throw error;\n }\n\n return data?.data ?? data;\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\nexport function buildFileUrl(location) {\n if (!location) return '';\n const loc = String(location);\n if (/^https?:\\/\\//i.test(loc)) return loc;\n const base = getDefaults().s3BaseUrl || '';\n return base ? `${base}/${loc.replace(/^\\/+/, '')}` : '';\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({ location, name: fileNameFromPath(item) || item, url: buildFileUrl(location) });\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 rebuilding from the\n // S3 base, so private-bucket files open correctly.\n const presigned = item.fileUrl || item.downloadUrl || item.signedUrl || '';\n out.push({ location, name: docDisplayName(item), url: presigned || buildFileUrl(location) });\n }\n }\n return out;\n}\n","// applyGroupValues — the SINGLE place that turns a `groups` array (each field\n// carrying `.value`, each addRow group carrying `.rows`) into antd form state.\n//\n// This is the exact mechanism Edit-prefill has always used (getFormGroups with\n// an `id` embeds field.value/group.rows server-side via AttachFieldValues).\n// It's extracted here so any OTHER source of the same shape — notably an AI\n// Action's response, which the gateway resolves through the identical\n// AttachFieldValues engine — can be applied to the form with the same code,\n// instead of maintaining a second, parallel field-mapping implementation.\nimport { buildInitialValue, buildRepeatFieldInitial, getDeep, rowDefaultsFor } from './payloadTransformer';\nimport { splitDialCode, normalizeDialCode } from '../detail/phoneDisplay';\nimport { snapToOption } from './optionMatching';\n\n// truthy tolerates the boolean-ish shapes a config flag can arrive in.\nconst isTruthyFlag = (v) => v === true || v === 1 || v === '1';\nimport { buildFileUrl } from '../detail/renderConfig';\n\nconst empty = (v) => v === undefined || v === null || v === '';\n\nexport function namePathFromString(path) {\n return String(path ?? '').split('.').map((part) => part.trim()).filter(Boolean);\n}\n\nexport function writeFormValue(target, path, value) {\n const parts = Array.isArray(path) ? path : namePathFromString(path);\n if (!parts.length) return target;\n let cursor = target;\n for (let i = 0; i < parts.length - 1; i += 1) {\n const key = parts[i];\n if (!cursor[key] || typeof cursor[key] !== 'object' || Array.isArray(cursor[key])) cursor[key] = {};\n cursor = cursor[key];\n }\n cursor[parts[parts.length - 1]] = value;\n return target;\n}\n\nfunction firstConfiguredValue(source, keys = []) {\n if (!source || typeof source !== 'object') return undefined;\n for (const key of keys) {\n if (!key) continue;\n const val = getDeep(source, key);\n if (!empty(val)) return val;\n }\n return undefined;\n}\n\nfunction firstMatchingValue(source, pattern) {\n if (!source || typeof source !== 'object') return undefined;\n const entries = Object.entries(source);\n const direct = entries.find(([key, val]) => pattern.test(key) && !empty(val));\n if (direct) return direct[1];\n for (const [, val] of entries) {\n if (val && typeof val === 'object' && !Array.isArray(val)) {\n const nested = firstMatchingValue(val, pattern);\n if (!empty(nested)) return nested;\n }\n }\n return undefined;\n}\n\n// fileListFromValue — turn a file field's embedded value (from getFormGroups,\n// or an AI response resolved the same way) into the antd fileList the file\n// control expects. The value may be a full document object ({ location,\n// uploadName/name, uniqueName }), a bare original filename string, or an\n// array of either. Returns undefined when empty.\nexport function fileListFromValue(stored, field) {\n const list = Array.isArray(stored) ? stored : (stored ? [stored] : []);\n if (!list.length) return undefined;\n const mapped = list.map((f, i) => {\n if (typeof f === 'string') {\n return { uid: `${field.field}-${i}`, name: f, status: 'done' };\n }\n if (!f || typeof f !== 'object') return null;\n const rawUrl = firstConfiguredValue(f, [field.fileUrlKey, field.locationKey])\n ?? firstMatchingValue(f, /(location|url|path)$/i)\n ?? '';\n const fileName = firstConfiguredValue(f, [field.fileNameKey])\n ?? firstMatchingValue(f, /(uploadName|uploadedFileName|fileName|name)$/i)\n ?? '';\n // A stored object with no usable url AND no real filename carries no actual\n // file (e.g. an all-null document sub-object from a parsed AI response) —\n // skip it rather than showing a bogus \"file\" chip with nothing behind it.\n if (!rawUrl && !fileName) return null;\n const fullUrl = rawUrl && !String(rawUrl).startsWith('http')\n ? buildFileUrl(rawUrl)\n : rawUrl;\n return {\n uid: String(f.id ?? f._id ?? f.uniqueName ?? f.documentCopyUniqueName ?? `${field.field}-${i}`),\n name: fileName || 'file',\n status: 'done',\n existing: true,\n url: fullUrl || undefined,\n // The ORIGINAL stored container, kept so the payload engine can write an\n // untouched file straight back (see carriedFileValue) instead of the\n // record silently losing a document it displayed.\n stored: f,\n };\n }).filter(Boolean);\n return mapped.length ? mapped : undefined;\n}\n\n// buildRowFileList — turn an addRow row's stored file metadata into the antd\n// fileList. Returns undefined when the row has no file.\nexport function buildRowFileList(row, field) {\n if (!row || typeof row !== 'object') return undefined;\n const name = firstConfiguredValue(row, [field.fileNameKey, field.field])\n ?? firstMatchingValue(row, /(uploadName|uploadedFileName|fileName|name)$/i);\n const rawUrl = firstConfiguredValue(row, [field.fileUrlKey, field.locationKey])\n ?? firstMatchingValue(row, /(location|url|path)$/i)\n ?? '';\n if (!name && !rawUrl) return undefined;\n const fullUrl = rawUrl && !String(rawUrl).startsWith('http')\n ? buildFileUrl(rawUrl)\n : rawUrl;\n const uid = row.documentCopyUniqueName ?? row.uniqueName ?? name ?? `${field.field}-0`;\n return [{\n uid: String(uid),\n name: name ?? 'file',\n status: 'done',\n existing: true,\n url: fullUrl || undefined,\n }];\n}\n\n// seedVerifyCarriers — carry a row's VERIFY BOOKKEEPING keys into the form even\n// when they are not declared as fields of the group.\n//\n// Why this exists: mapRow below prefills strictly from `group.fields`, which is\n// correct for anything the user can see or edit. But a verify-enabled field\n// names its bookkeeping keys in its OWN config — verifyResultField (\"isValid\"),\n// verifyTimestampField (\"validatedAt\") and the lock keys (\"isAccountCreate\") —\n// and those are only prefilled if someone ALSO remembered to declare each of\n// them as a separate hidden field on the group.\n//\n// That coupling is invisible and environment-specific: the row itself carries\n// isValid/isAccountCreate in every environment, but a config that omits the\n// hidden field declarations silently drops them at prefill. The symptom is a\n// recruiter whose email really is validated (and whose login really exists)\n// still rendering a \"Verify Email\" button, because the button watches a value\n// that never made it into the form — while Name/Email/Contact prefill fine and\n// make the row look perfectly healthy.\n//\n// So: the field that DECLARES a verify key is the authority for prefilling it.\n// Only keys already present on the stored row are copied, nothing is invented,\n// and an existing configured value is never overwritten.\nfunction seedVerifyCarriers(group, row, rowVals, readScalar) {\n (group.fields ?? []).forEach((field) => {\n if (!field?.verifyAction) return;\n const keys = [\n field.verifyResultField,\n field.verifyTimestampField,\n ...String(field.verifyLockedWhenFields ?? '').split(/[,\\s]+/),\n ].map((key) => String(key ?? '').trim()).filter(Boolean);\n\n keys.forEach((key) => {\n if (getDeep(rowVals, key) !== undefined) return; // already prefilled as a real field\n const stored = readScalar(row, { field: key });\n if (stored === undefined || stored === null) return;\n writeFormValue(rowVals, key, stored);\n });\n });\n}\n\n// buildAddRowInitial — turn a repeatable (addRow) group's backend-embedded\n// stored rows (group.rows) into the Form.List initialValue shape: an array\n// with ONE object per stored row, each mapped through buildInitialValue (file\n// fields via the row's sibling keys). Returns [] when the group has no stored\n// rows, padded to group.minRows.\nexport function buildAddRowInitial(group) {\n const storedRows = Array.isArray(group.rows) ? group.rows : null;\n const minRows = Math.max(0, Number(group.minRows ?? 0) || 0);\n const mapRow = (row, readFile, readScalar) => {\n const rowVals = {};\n (group.fields ?? []).forEach((field) => {\n if (!field.field) return;\n if (field.type === 'file') {\n const fileList = readFile(row, field);\n if (fileList) writeFormValue(rowVals, field.field, fileList);\n return;\n }\n const stored = readScalar(row, field);\n if (stored === undefined || stored === null) {\n // Row has no stored value — an admin defaultOnEdit default still shows\n // (e.g. VMS Commission 5.5 on rows saved before the field existed).\n if (field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== '') {\n writeFormValue(rowVals, field.field, field.defaultValue);\n }\n return;\n }\n // A repeat field inside the row (nested Form.List) hydrates as an array,\n // each element normalised for its control; a plain field as a single value.\n writeFormValue(rowVals, field.field, isTruthyFlag(field.addRow)\n ? buildRepeatFieldInitial(field, stored)\n : buildInitialValue(field, stored));\n });\n seedVerifyCarriers(group, row, rowVals, readScalar);\n return rowVals;\n };\n\n const padSeed = rowDefaultsFor(group, { editing: true });\n if (storedRows && storedRows.length > 0) {\n const rows = storedRows.map((row) => mapRow(\n row,\n (r, field) => buildRowFileList(r, field),\n (r, field) => getDeep(r, field.field),\n ));\n while (rows.length < minRows) rows.push({ ...padSeed });\n return rows;\n }\n\n // Fallback for an older backend that embeds only per-field field.value (row 0).\n const rowVals = mapRow(\n group,\n (_g, field) => fileListFromValue(field.value, field),\n (_g, field) => field.value,\n );\n const rows = Object.keys(rowVals).length > 0 ? [rowVals] : [];\n while (rows.length < minRows) rows.push({ ...padSeed });\n return rows;\n}\n\n// applyScalarFieldValues — walk every NON-addRow group's fields, read each\n// field's embedded `.value`, shape it (buildInitialValue / fileListFromValue /\n// checkbox-transformRule reversal), and set it on the form in one batch.\n// AddRow groups are skipped — their rows only apply correctly as a Form.List\n// `initialValue` at mount (or via an explicit remount for a post-mount\n// update), never via setFieldsValue.\nexport function applyScalarFieldValues(form, groups = []) {\n const values = {};\n\n groups.forEach((group) => {\n if (group.toggleEnabled && group.toggleField) {\n writeFormValue(values, group.toggleField, Boolean(group.toggleValue));\n }\n if (group.addRow) return;\n\n (group.fields ?? []).forEach((field) => {\n if (!field.field) return;\n // A repeatable (addRow) field prefills via its own Form.List initialValue\n // at mount (buildRepeatFieldInitial) — setFieldsValue on a Form.List that\n // mounted empty only keeps the last item, so skip it here.\n if (field.addRow) return;\n const stored = field.value;\n const name = field.field;\n\n if (field.type === 'file') {\n const fileList = fileListFromValue(stored, field);\n if (fileList) writeFormValue(values, name, fileList);\n return;\n }\n\n if (stored === undefined || stored === null) return;\n\n if (field.type === 'checkbox' && Array.isArray(field.options) && field.options.length > 0) {\n const map = field.transformRule?.map;\n if (map) {\n const match = Object.entries(map).find(([, to]) => String(to) === String(stored));\n writeFormValue(values, name, match ? [match[0]] : []);\n } else {\n // Checkbox groups bypass buildInitialValue, so they snap here — a\n // parsed \"high\"/\"HIGH\" must still tick a \"High\" box.\n writeFormValue(values, name, snapToOption(field, Array.isArray(stored) ? stored : [stored]));\n }\n return;\n }\n\n // ── dial code split ──────────────────────────────────────────────────\n // A parsed contact number arrives as one string that often carries its\n // country code (\"+91 99887 76655\"). Written whole into the number field,\n // the code is stripped by the phone formatter and the separate code field\n // keeps whatever default it had — which is how every parsed candidate\n // ended up as \"+1\" regardless of the résumé.\n //\n // Config, not a field name: `splitDialCodeInto` names the sibling that\n // should receive the code. Reuses the SAME splitter the detail view uses\n // (components/detail/phoneDisplay.js), so form and detail agree on what a\n // dial code is.\n const codeTarget = field.splitDialCodeInto;\n if (codeTarget && typeof stored === 'string') {\n const { code, rest } = splitDialCode(stored);\n const normalized = normalizeDialCode(code);\n // A parse that carried NO code leaves the code field untouched rather\n // than blanking or defaulting it — inventing a country is the bug.\n if (normalized) writeFormValue(values, codeTarget, normalized);\n writeFormValue(values, name, buildInitialValue(field, rest || stored));\n return;\n }\n\n writeFormValue(values, name, buildInitialValue(field, stored));\n });\n });\n\n if (Object.keys(values).length > 0) form.setFieldsValue(values);\n return values;\n}\n","import { useCallback, useState } from 'react';\nimport { Form, message } from 'antd';\nimport { openFormDecision } from './formDecisionDialog';\nimport { runAiAction } from '../../services/aiActionApi';\nimport { applyScalarFieldValues, buildAddRowInitial } from './applyGroupValues';\n\nfunction stripHtml(html) {\n const div = document.createElement('div');\n div.innerHTML = String(html ?? '');\n return div.textContent || div.innerText || '';\n}\n\nfunction applyTransform(value, transform) {\n if (!transform) return value;\n if (transform === 'stripHtml') return stripHtml(value);\n return value;\n}\n\nfunction wordCount(text) {\n return String(text ?? '').trim().split(/\\s+/).filter(Boolean).length;\n}\n\nfunction rowHasData(row) {\n return Object.values(row || {}).some((v) => v !== undefined && v !== null && v !== '');\n}\n\n// isInputSatisfied — an input \"counts\" once it has real content: a file\n// present for kind \"file\", or non-empty (post-transform) text for kind\n// \"text\" that also meets the input's own MinWords, if configured.\nfunction isInputSatisfied(input, rawValue) {\n if (input.kind === 'file') {\n return Array.isArray(rawValue) ? rawValue.length > 0 : Boolean(rawValue);\n }\n const text = applyTransform(rawValue, input.transform);\n const str = String(text ?? '').trim();\n if (!str) return false;\n const minWords = Number(input.minWords) || 0;\n return minWords > 0 ? wordCount(str) >= minWords : true;\n}\n\n// computeActionEnabled — an action is disabled until every GATED input is\n// satisfied. An input gates the button when it's marked Required, or has a\n// MinWords requirement (>0) — a plain optional input never gates. Inputs\n// sharing a Group (e.g. \"either a JD file or JD text\") gate as one unit: the\n// group is satisfied once ANY member of it is satisfied.\nfunction computeActionEnabled(action, getValue) {\n const inputs = action.inputs || [];\n if (inputs.length === 0) return true;\n\n const groupsMap = new Map();\n inputs.forEach((input, i) => {\n const key = input.group || `__solo_${i}`;\n if (!groupsMap.has(key)) groupsMap.set(key, []);\n groupsMap.get(key).push(input);\n });\n\n for (const groupInputs of groupsMap.values()) {\n const gates = groupInputs.some((i) => i.required || Number(i.minWords) > 0);\n if (!gates) continue;\n const satisfied = groupInputs.some((i) => isInputSatisfied(i, getValue(i.sourceField)));\n if (!satisfied) return false;\n }\n return true;\n}\n\n// targetFieldsOf — every FORM FIELD an action's response would write into.\n// Derived from the response itself (the {groups} shape the parser returns), so\n// it needs no per-action configuration and stays correct as an action's output\n// changes.\nexport function targetFieldsOf(responseGroups = []) {\n const keys = [];\n responseGroups.forEach((group) => {\n if (group?.addRow) {\n if (group.name) keys.push(group.name);\n return;\n }\n (group?.fields ?? []).forEach((f) => {\n const key = f?.field ?? f?.name;\n if (key) keys.push(key);\n });\n });\n return keys;\n}\n\n// hasExistingData — would applying this response OVERWRITE something the user\n// already typed? Used to decide whether to ask first.\nexport function hasExistingData(form, responseGroups) {\n return targetFieldsOf(responseGroups).some((key) => {\n const value = form.getFieldValue(key);\n if (Array.isArray(value)) return value.some(rowHasData);\n return value !== undefined && value !== null && value !== '';\n });\n}\n\n/**\n * formHasUserData — has anyone typed anything into this form yet, ignoring the\n * field that triggered the action?\n *\n * Used for UPLOAD-triggered actions, where the question has to be answered\n * BEFORE the file is sent anywhere. At that point the response does not exist,\n * so the precise \"would this overwrite a target field?\" test cannot be run —\n * but \"the form is still blank\" is knowable, and it is the case that matters:\n * an empty form can be filled in silently, a form someone has worked on cannot.\n */\n/**\n * countUserEntries — how much the user has actually filled in, ignoring the\n * field that triggered the action.\n *\n * The overwrite prompt needs to state what is AT RISK. \"Already has details\n * entered\" is not a fact — it is a restatement of why the dialog opened, which\n * tells the reader nothing they did not already know. A count does: it is the\n * difference between \"I typed one thing by accident\" and \"I have filled in half\n * this form\".\n */\nexport function countUserEntries(form, excludeFields = []) {\n const values = form.getFieldsValue(true) ?? {};\n const skip = new Set([excludeFields].flat().filter(Boolean));\n const touched = typeof form.isFieldTouched === 'function'\n ? (key) => form.isFieldTouched(key)\n : () => true;\n\n let count = 0;\n Object.entries(values).forEach(([key, value]) => {\n // Counted on the SAME basis the prompt is shown on. Counting untouched\n // defaults here would say \"you have filled in 4 answers\" to someone who has\n // filled in one.\n if (skip.has(key) || !touched(key) || !hasRealValue(value)) return;\n if (Array.isArray(value)) {\n count += value.filter((row) => (row && typeof row === 'object' ? rowHasData(row) : Boolean(row))).length;\n return;\n }\n count += 1;\n });\n return count;\n}\n\n// hasRealValue — is there something here the user would mind losing?\nfunction hasRealValue(value) {\n if (value === undefined || value === null || value === '') return false;\n if (Array.isArray(value)) {\n // A Form.List that mounted with one blank row is not \"user data\".\n return value.some((row) => (row && typeof row === 'object' ? rowHasData(row) : Boolean(row)));\n }\n if (typeof value === 'object') {\n return Object.values(value).some((v) => v !== undefined && v !== null && v !== '');\n }\n return true;\n}\n\nexport function formHasUserData(form, excludeFields = []) {\n const values = form.getFieldsValue(true) ?? {};\n const skip = new Set([excludeFields].flat().filter(Boolean));\n const keys = Object.keys(values).filter((key) => !skip.has(key));\n\n // TOUCHED, not merely non-empty.\n //\n // A brand-new candidate form is NOT blank: four fields already carry values\n // from their configured defaults (VMS commission 5.5, VMS type \"Recurring\",\n // rate currency, rate unit). Judging by emptiness alone therefore reported\n // \"the user has filled things in\" on a form nobody had typed into, so the\n // very first résumé upload — the one that should just work — stopped to ask\n // permission to overwrite defaults the user had never seen.\n //\n // antd sets `touched` on USER interaction only: neither Form.Item\n // initialValue nor a programmatic setFieldsValue marks a field touched, which\n // is exactly the distinction needed. A field still has to hold something too,\n // so typing into a box and then clearing it does not count.\n if (typeof form.isFieldTouched === 'function') {\n return keys.some((key) => form.isFieldTouched(key) && hasRealValue(values[key]));\n }\n\n // No touch tracking available (a bare form object): fall back to emptiness.\n return keys.some((key) => hasRealValue(values[key]));\n}\n\n/**\n * parsedPayload — the scalar values a response carries, as a flat record.\n *\n * The duplicate check needs an email and a phone number, and after a résumé is\n * read those exist in the RESPONSE, not yet in the form. Checking the form here\n * would ask \"is this person already on file?\" about the blank page the user is\n * still looking at.\n *\n * Repeatable groups are skipped: nothing identifies a person by their third job.\n */\nexport function parsedPayload(responseGroups = []) {\n const out = {};\n (responseGroups ?? []).forEach((group) => {\n if (group?.addRow) return;\n (group?.fields ?? []).forEach((f) => {\n const key = f?.field ?? f?.name;\n if (!key) return;\n const value = f?.value;\n if (value === undefined || value === null || value === '') return;\n out[key] = value;\n });\n });\n return out;\n}\n\n// shouldConfirmApply — the admin-configured overwrite policy for an action.\n//\n// 'targetsFilled' (default for upload-triggered parses) — ask only when the\n// user has already filled something the parse would replace.\n// An upload BEFORE typing stays silent (the fast path); an\n// upload AFTER typing always asks, which is exactly the\n// requirement.\n// 'always' — ask every time.\n// 'never' — apply silently (the behaviour before this existed).\nexport function shouldConfirmApply(action, form, responseGroups) {\n const mode = action?.confirmWhen ?? 'never';\n if (mode === 'never') return false;\n if (mode === 'always') return true;\n return hasExistingData(form, responseGroups);\n}\n\nconst AI_ACTION_POSITIONS = ['top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'];\nconst DEFAULT_AI_ACTION_POSITION = 'bottom-left';\n\n// groupActionsByPosition — buckets a field's click-triggered AI actions by\n// their admin-configured Position (blank/unknown falls back to the original\n// \"bottom-left\" layout), so the caller can render each bucket in its own\n// slot around the field with the right alignment.\nexport function groupActionsByPosition(actions = []) {\n const buckets = {};\n actions.forEach((action) => {\n const pos = AI_ACTION_POSITIONS.includes(action.position) ? action.position : DEFAULT_AI_ACTION_POSITION;\n (buckets[pos] ??= []).push(action);\n });\n return buckets;\n}\n\nexport { AI_ACTION_POSITIONS };\n\n/**\n * useAiActions — shared logic behind config-driven \"AI Action\" buttons\n * (Generate/Parse-style: send some form fields to an admin-configured\n * service, get back the SAME `{groups: [...]}` shape getFormGroups returns\n * for Edit prefill, and apply it to the form via the same shared functions).\n * Used identically by AddFormV1 and EditFormV1 so the apply logic isn't\n * duplicated.\n *\n * @param {Object} args\n * @param {import('antd').FormInstance} args.form\n * @param {Array} args.groups - the module's form groups (for input lookups)\n * @param {string} args.module\n */\nexport default function useAiActions({ form, groups, module, onApplied, onParsed }) {\n const [loadingKey, setLoadingKey] = useState(null);\n // What the spinner SAYS. A bare \"Loading…\" over a form that has just been\n // taken away from the user tells them nothing about how long to wait or why.\n // Admin-configured per action (action.loadingText); the fallback still names\n // the action rather than the mechanism.\n const [loadingText, setLoadingText] = useState('');\n // Re-renders whenever ANY field changes, so isActionEnabled below reflects\n // live typing/upload without needing to know in advance which field names\n // any given action reads from (fully config-driven, no hardcoded paths).\n const watchedValues = Form.useWatch((values) => values, form);\n\n const isActionEnabled = useCallback(\n (action) => computeActionEnabled(action, (sourceField) => (watchedValues || {})[sourceField]),\n [watchedValues],\n );\n\n // Returns whether anything was actually applied to the form, so the caller\n // can show an accurate success vs. \"nothing found\" message — generic to any\n // action/module, since it only looks at what the response itself contained.\n const applyResponse = useCallback((responseGroups) => {\n if (!Array.isArray(responseGroups) || responseGroups.length === 0) return false;\n\n const scalarValues = applyScalarFieldValues(form, responseGroups.filter((g) => !g.addRow));\n let appliedAny = Object.keys(scalarValues).length > 0;\n\n responseGroups.forEach((group) => {\n if (!group.addRow) return;\n if (!Array.isArray(group.rows) || group.rows.length === 0) return;\n\n const shapedRows = buildAddRowInitial(group);\n if (shapedRows.length === 0) return;\n appliedAny = true;\n\n // A mounted Form.List's field name is already registered in the Form's\n // store (even with an empty array) — setFieldsValue is what antd\n // documents for a post-mount update; it re-renders the List with the\n // new rows directly, no remount trick needed.\n const applyRows = () => form.setFieldsValue({ [group.name]: shapedRows });\n\n const existingRows = form.getFieldValue(group.name) || [];\n const filled = existingRows.filter(rowHasData);\n if (filled.length) {\n const sectionName = group.label || group.name;\n openFormDecision({\n tone: 'overwrite',\n title: `Replace what's in ${sectionName}?`,\n facts: [\n { label: 'Section', value: sectionName },\n { label: 'Rows you filled in', value: filled.length },\n { label: 'Rows in the file', value: shapedRows.length },\n ],\n body: `Everything currently in ${sectionName} will be removed and replaced with what the file says. `\n + 'The rest of the form is not affected.',\n okText: 'Replace them',\n cancelText: 'Keep mine',\n danger: true,\n }).then((confirmed) => { if (confirmed) applyRows(); });\n } else {\n applyRows();\n }\n });\n\n return appliedAny;\n }, [form]);\n\n const runAction = useCallback(async (groupName, field, action, extraFile) => {\n // ── ask BEFORE sending the file anywhere ────────────────────────────────\n // For an upload action the question is \"shall I read this and fill the\n // form in?\", and it has to be asked before the call, not after: calling\n // first wastes a round-trip on a file the user may not want parsed, and it\n // sends their document to a service for nothing.\n const mode = action?.confirmWhen ?? 'never';\n if (action?.trigger === 'upload' && mode !== 'never') {\n const dirty = mode === 'always' || formHasUserData(form, [field?.field]);\n if (dirty) {\n const msgs = action.confirmMessages ?? {};\n // Naming the FILE matters: by this point the user has picked something,\n // and \"this file\" only reassures if they can see it is the one they\n // meant. The count says what is at risk — see countUserEntries.\n const fileName = extraFile?.name\n || form.getFieldValue(field?.field)?.slice?.(-1)?.[0]?.name;\n const entries = countUserEntries(form, [field?.field]);\n const proceed = await openFormDecision({\n tone: 'overwrite',\n title: msgs.title || 'Read this file and fill the form in?',\n facts: [\n { label: 'File', value: fileName },\n {\n label: 'You have filled in',\n value: entries ? `${entries} ${entries === 1 ? 'answer' : 'answers'}` : undefined,\n },\n ],\n // Present/future tense: nothing has been read yet. The confirm now\n // runs BEFORE the file is sent anywhere, so past-tense copy (\"we read\n // the details…\") describes something that has not happened.\n body: msgs.body\n || 'We can read the details out of this file and fill the form in for you. '\n + 'Where the file has an answer, it replaces what is currently in that box. '\n + 'Anything the file does not mention is left as you typed it.',\n okText: msgs.ok || 'Read it and fill in',\n cancelText: msgs.cancel || 'Keep what I typed',\n });\n if (!proceed) return undefined;\n }\n }\n\n setLoadingKey(action.key);\n setLoadingText(action.loadingText || `Reading ${action.label || 'the file'}…`);\n try {\n const inputs = {};\n const files = {};\n\n (action.inputs || []).forEach((input) => {\n if (input.kind === 'file') {\n const fromFileList = form.getFieldValue(input.sourceField);\n const file = extraFile ?? fromFileList?.[fromFileList.length - 1]?.originFileObj;\n if (file) files[input.param] = file;\n } else {\n const raw = form.getFieldValue(input.sourceField);\n if (raw !== undefined && raw !== null && raw !== '') {\n inputs[input.param] = applyTransform(raw, input.transform);\n }\n }\n });\n // Within a shared input Group (e.g. \"either a JD file or JD text\"), a\n // file input wins — drop the sibling text input if both are present.\n (action.inputs || []).forEach((input) => {\n if (!input.group || input.kind !== 'text') return;\n const fileSiblingProvided = (action.inputs || [])\n .some((i) => i.group === input.group && i.kind === 'file' && files[i.param]);\n if (fileSiblingProvided) delete inputs[input.param];\n });\n\n const result = await runAiAction({\n module, group: groupName, field: field.field, actionKey: action.key, inputs, files,\n });\n const label = action.label || 'AI action';\n const responseGroups = result?.groups;\n\n // Click actions still ask AFTER the call, because only the response says\n // which fields they would touch. Upload actions have already asked above.\n if (action?.trigger !== 'upload' && shouldConfirmApply(action, form, responseGroups)) {\n const msgs = action.confirmMessages ?? {};\n // A click action knows exactly which boxes it would overwrite, because\n // the response has already come back — so it can say so.\n const targets = targetFieldsOf(responseGroups).length;\n const confirmed = await openFormDecision({\n tone: 'overwrite',\n title: msgs.title || `Fill the form in from ${label}?`,\n facts: [\n { label: 'Source', value: label },\n { label: 'Boxes it would fill', value: targets || undefined },\n ],\n body: msgs.body\n || 'We found details you can use. Applying them will replace what you have '\n + 'already entered in those boxes.',\n okText: msgs.ok || 'Use these details',\n cancelText: msgs.cancel || 'Keep what I typed',\n });\n if (!confirmed) {\n message.info(`${label} cancelled — your entries were kept.`);\n return result;\n }\n }\n\n // ── between reading and applying ──────────────────────────────────\n // The host gets to veto BEFORE any value lands on the form. For a résumé\n // this is where \"do we already have this person?\" is asked: the parsed\n // email and phone exist now, and if the answer is yes there is no point\n // filling in a form the user is about to abandon.\n //\n // A veto returns the result unapplied — the host has already told the\n // user why and decided what to do with the file.\n if (onParsed) {\n const verdict = await onParsed({\n action,\n field,\n groupName,\n responseGroups,\n payload: parsedPayload(responseGroups),\n });\n if (verdict === false || verdict === 'abort') return result;\n }\n\n const appliedAny = applyResponse(responseGroups);\n if (appliedAny) {\n message.success(`${label} completed successfully.`);\n } else {\n message.info(`${label} completed, but no matching details were found.`);\n }\n // After the form is filled — a second, cheaper check that also covers\n // anything the user had already typed.\n if (appliedAny && onApplied) await onApplied({ action, field, groupName });\n return result;\n } catch (error) {\n message.error(error?.message || `${action.label || 'AI action'} failed`);\n return undefined;\n } finally {\n setLoadingKey(null);\n setLoadingText('');\n }\n }, [form, module, applyResponse, onApplied, onParsed]);\n\n return { runAction, loadingKey, loadingText, busy: loadingKey !== null, isActionEnabled };\n}\n","import { Button, Space } from 'antd';\n\n// AiActionButtonGroup — renders one position bucket (\"top-left\", \"bottom-right\",\n// etc.) of a field's AI action buttons, admin-configured entirely via each\n// action's `position`. Horizontal alignment follows the position's own\n// left/center/right suffix; the caller places top vs. bottom buckets around\n// the field itself.\nexport default function AiActionButtonGroup({ position, actions, aiActions, groupName, field }) {\n if (!actions || actions.length === 0) return null;\n\n const justifyContent = position.endsWith('center')\n ? 'center'\n : position.endsWith('right')\n ? 'flex-end'\n : 'flex-start';\n\n return (\n <Space size={8} className=\"v1-ai-action-buttons\" style={{ width: '100%', justifyContent }}>\n {actions.map((action) => (\n <Button\n key={action.key}\n size=\"small\"\n loading={aiActions.loadingKey === action.key}\n disabled={aiActions.busy || !aiActions.isActionEnabled(action)}\n onClick={() => aiActions.runAction(groupName, field, action)}\n >\n {action.label}\n </Button>\n ))}\n </Space>\n );\n}\n","// Generic email rule used by Add/Edit forms and the real-time input validator.\n// The rule is selected through the DB validation type `email`; it contains no\n// module or field-name assumptions.\nexport function isValidConfiguredEmail(value) {\n const email = String(value ?? '').trim();\n const match = /^([^\\s@]+)@([^\\s@]+)\\.([A-Za-z]{2,})$/.exec(email);\n if (!match) return false;\n\n const [, localPart, domainHost] = match;\n return /[A-Za-z]/.test(localPart) && /[A-Za-z]/.test(domainHost);\n}\n\nexport function configuredEmailRule(label = 'Email', message) {\n return {\n validator: (_, value) => {\n if (value === undefined || value === null || value === '') return Promise.resolve();\n return isValidConfiguredEmail(value)\n ? Promise.resolve()\n : Promise.reject(new Error(message ?? `Enter a valid ${label}; numeric-only email addresses are not allowed`));\n },\n };\n}\n","// inputValidator.js — config-driven real-time input restriction engine.\n// Driven entirely by field.validator in formGroupConfig — no hardcoding.\n// Used by FieldControl in AddFormV1 and EditFormV1.\n\nimport { isValidConfiguredEmail } from './emailValidator';\n\nexport const INPUT_VALIDATOR_TYPES = [\n { label: 'Only Numbers', value: 'onlyNumber' },\n { label: 'Only Letters', value: 'onlyLetter' },\n { label: 'Only Letters & Spaces', value: 'onlyLettersAndSpace' },\n { label: 'Only Alphanumeric', value: 'onlyAlphanumeric' },\n { label: 'Letters, Numbers & Hyphen', value: 'onlyLettersNumberAndHyphen' },\n { label: 'Letters, Numbers, Hyphen & Dot', value: 'onlyLettersNumberHyphenAndDot' },\n { label: 'Contact Number (digits, (), -)', value: 'contactNumber' },\n { label: 'Email (reject numeric-only address)', value: 'email' },\n { label: 'Job Title (letters, nums, symbols)', value: 'jobTitle' },\n { label: 'MSP / Ref ID (alphanumeric only)', value: 'mspRefId' },\n { label: 'Location (letters, nums, , . -)', value: 'locationValidation' },\n { label: 'Decimal / Range (number + dot)', value: 'decimalRange' },\n { label: 'Budget (numbers, must be > 0)', value: 'budgetValidation' },\n { label: 'Experience (numbers only)', value: 'experience' },\n { label: 'Numeric — 2 digits max', value: 'numericTwoDigits' },\n { label: 'Job Description (character count)', value: 'jobDescription' },\n { label: 'Website URL', value: 'websiteUrl' },\n];\n\n// onKeyPress allowlist patterns — used to block invalid chars before they appear.\n// null means no per-key blocking for that type.\nconst KEY_PATTERNS = {\n onlyNumber: /^[0-9]$/,\n experience: /^[0-9]$/,\n numericTwoDigits: /^[0-9]$/,\n budgetValidation: /^[0-9]$/,\n onlyLetter: /^[a-zA-Z\\s]$/,\n onlyLettersAndSpace: /^[a-zA-Z\\s]$/,\n onlyAlphanumeric: /^[a-zA-Z0-9]$/,\n mspRefId: /^[a-zA-Z0-9]$/,\n onlyLettersNumberAndHyphen: /^[a-zA-Z0-9\\s-]$/,\n locationValidation: /^[a-zA-Z0-9\\s\\-.,]$/,\n jobTitle: /^[a-zA-Z0-9\\s\\-.,/&+()*'\"#@]$/,\n decimalRange: /^[0-9.]$/,\n contactNumber: /^[0-9()\\-+\\s]$/,\n};\n\nexport function getKeyPattern(type) {\n return KEY_PATTERNS[type] ?? null;\n}\n\n// applyInputValidator — clean a raw input value according to the validator config.\n// Returns { cleaned: string, error: string|null }.\n// eventType 'blur' triggers trim; 'change' only strips leading whitespace.\nexport function applyInputValidator(rawValue, config = {}, eventType = 'change') {\n if (!config?.type || rawValue === undefined || rawValue === null) {\n return { cleaned: rawValue ?? '', error: null };\n }\n\n const value = String(rawValue);\n const { type, maxLength, maxChars } = config;\n const title = config.title || 'Field';\n let cleaned;\n let error = null;\n\n const trimStart = (s) => (eventType === 'blur' ? s.trim() : s.replace(/^\\s+/, ''));\n\n switch (type) {\n case 'onlyNumber':\n case 'experience':\n case 'numericTwoDigits': {\n const limit = Number(maxLength ?? (type === 'numericTwoDigits' ? 2 : 10));\n cleaned = value.replace(/[^0-9]/g, '');\n if (value !== cleaned) error = `${title} allows only numbers.`;\n if (cleaned.length > limit) {\n error = `${title} cannot exceed ${limit} characters.`;\n cleaned = cleaned.slice(0, limit);\n }\n break;\n }\n\n case 'onlyLetter': {\n const limit = Number(maxLength ?? 50);\n cleaned = value.replace(/[^a-zA-Z]/g, '');\n if (value !== cleaned) error = `${title} allows only letters.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersAndSpace': {\n const limit = Number(maxLength ?? 55);\n cleaned = trimStart(value.replace(/[^a-zA-Z\\s]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters and spaces.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyAlphanumeric':\n case 'mspRefId': {\n const limit = Number(maxLength ?? 50);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters and numbers.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersNumberAndHyphen': {\n const limit = Number(maxLength ?? 55);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s-]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers and hyphens.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersNumberHyphenAndDot': {\n const limit = Number(maxLength ?? 100);\n cleaned = value.replace(/[^a-zA-Z0-9\\-./#+[\\]{}()\\s]/g, '').slice(0, limit);\n if (value !== cleaned) error = `${title} allows only letters, numbers, hyphens and dots.`;\n break;\n }\n\n case 'contactNumber': {\n const limit = Number(maxLength ?? 15);\n cleaned = value.replace(/[^0-9()\\-+\\s]/g, '').slice(0, limit);\n const re = /^(\\+?[0-9]{1,3}[- ]?)?(\\(?\\d{1,4}\\)?[- ]?)?[\\d\\-\\s]{3,15}$/;\n if (value !== cleaned) error = `${title} allows only digits, parentheses () and hyphens.`;\n else if (cleaned && !re.test(cleaned)) error = `${title} is not a valid phone number format.`;\n break;\n }\n\n case 'locationValidation': {\n const limit = Number(maxLength ?? 100);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s\\-.,]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers, spaces, commas, dots and hyphens.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'jobTitle': {\n const limit = Number(maxLength ?? 80);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s\\-/&.,()+#@'\"*]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers, spaces and common symbols.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'decimalRange': {\n let c = value.replace(/[^0-9.]/g, '');\n const dotCount = (c.match(/\\./g) || []).length;\n if (dotCount > 1) {\n const di = c.indexOf('.');\n c = c.slice(0, di + 1) + c.slice(di + 1).replace(/\\./g, '');\n error = `${title} can have only one decimal point.`;\n }\n const digits = c.replace(/\\./g, '');\n const limit = Number(maxLength ?? 10);\n if (digits.length > limit) {\n c = c.slice(0, limit + (c.includes('.') ? 1 : 0));\n error = `${title} cannot exceed ${limit} digits.`;\n }\n cleaned = c;\n if (!error && value !== cleaned) error = `${title} allows only numbers and one dot.`;\n break;\n }\n\n case 'budgetValidation': {\n const limit = Number(maxLength ?? 10);\n cleaned = value.replace(/[^0-9]/g, '').slice(0, limit);\n const num = Number(cleaned);\n if (value !== cleaned) error = `${title} allows only numbers.`;\n else if (cleaned && num <= 0) error = `${title} must be greater than 0.`;\n break;\n }\n\n case 'jobDescription': {\n const limit = Number(maxChars ?? maxLength ?? 1500);\n const plain = value\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n if (plain.length > limit) error = `${title} cannot exceed ${limit} characters.`;\n cleaned = value; // never truncate HTML content\n break;\n }\n\n case 'email': {\n cleaned = eventType === 'blur' ? value.trim() : value;\n if (cleaned && !isValidConfiguredEmail(cleaned)) {\n error = `${title} must be a valid email address; numeric-only email addresses are not allowed.`;\n }\n break;\n }\n\n case 'websiteUrl': {\n cleaned = eventType === 'blur' ? value.trim() : value;\n const urlRe = /^(https?:\\/\\/)?([\\da-z.-]+)\\.([a-z.]{2,6})([/\\w .-]*)*\\/?$/i;\n if (cleaned && !urlRe.test(cleaned)) error = `${title} must be a valid URL.`;\n break;\n }\n\n default:\n cleaned = value;\n }\n\n return { cleaned, error };\n}\n","// Config-driven field uniqueness — the shared, pure runtime shared by\n// AddFormV1 and EditFormV1.\n//\n// An admin marks a field unique in Form Groups → Field → Validations; the\n// stored entry is an ordinary validations[] row:\n//\n// { type: 'unique', message: 'Email already present',\n// value: { collectionField, normalizer, skipBlank, validateOn } }\n//\n// Everything module-specific comes from that config. NOTHING in this file (or\n// in the two form engines) may name a module, a collection or a field — the\n// server resolves the collection, the stored field and the tenant scope from\n// the module + the caller's claims.\n//\n// This module owns ALL of the uniqueness behaviour so neither form duplicates\n// it: rule extraction, blank handling, normalization, the per-session cache,\n// the stale-response guard, the pending-request counter that blocks submit, the\n// antd rule factory (incl. \"only after the synchronous rules pass\") and the\n// mapping of a submit-time 409 back onto form fields.\n\nexport const UNIQUE_VALIDATION_TYPE = 'unique';\nexport const ALREADY_PRESENT_CODE = 'already_present';\nexport const DUPLICATE_VALUE_CODE = 'DUPLICATE_VALUE';\n\nconst DEFAULT_MESSAGE = 'This value is already present';\n\n// ── Rule extraction ──────────────────────────────────────────────────────────\n\nfunction validationType(validation) {\n if (typeof validation === 'string') return validation;\n return validation?.type ?? validation?.rule ?? validation?.name;\n}\n\n// The admin editor stores the unique options as an OBJECT under `value` (the\n// declared valueType is 'object', so the save path leaves it untouched). Older\n// or hand-written config may put the same keys flat on the validation itself,\n// so both shapes are accepted.\nexport function parseUniqueValidation(validation) {\n if (validationType(validation) !== UNIQUE_VALIDATION_TYPE) return null;\n if (typeof validation === 'string') {\n return { message: DEFAULT_MESSAGE, normalizer: 'trim', skipBlank: true, validateOn: 'blur' };\n }\n\n const raw = (validation.value && typeof validation.value === 'object') ? validation.value : validation;\n const validateOn = String(raw.validateOn ?? 'blur').toLowerCase();\n\n return {\n message: (typeof validation.message === 'string' && validation.message.trim())\n ? validation.message.trim()\n : DEFAULT_MESSAGE,\n // Kept only so the admin's configured target is inspectable client-side;\n // it is NEVER sent to the server (the server resolves the stored field).\n collectionField: raw.collectionField ?? '',\n normalizer: String(raw.normalizer ?? 'trim'),\n // skipBlank defaults to true; only an explicit `false` turns it off.\n skipBlank: raw.skipBlank !== false,\n validateOn: ['blur', 'change', 'submit'].includes(validateOn) ? validateOn : 'blur',\n };\n}\n\nexport function getUniqueRule(field) {\n const validations = field?.validations ?? field?.validation ?? field?.rules ?? [];\n if (!Array.isArray(validations)) return null;\n for (const validation of validations) {\n const rule = parseUniqueValidation(validation);\n if (rule) return rule;\n }\n return null;\n}\n\n// Every unique-configured field in the form, flattened across groups. Used to\n// map a submit-time 409 back onto a field when the transport lost the\n// structured body (see extractDuplicateFieldErrors).\nexport function collectUniqueRules(groups = []) {\n const collected = [];\n (Array.isArray(groups) ? groups : []).forEach((group) => {\n (group?.fields ?? []).forEach((field) => {\n const rule = getUniqueRule(field);\n if (!rule || !field?.field) return;\n collected.push({ field: field.field, label: field.label ?? field.field, rule });\n });\n });\n return collected;\n}\n\n// ── Blank + normalization ────────────────────────────────────────────────────\n\n// Blank = missing / null / empty / whitespace-only / empty list.\n// Numeric ZERO and boolean FALSE are REAL values, not blanks — a \"0\" employee\n// code or a `false` flag must still be uniqueness-checked.\nexport function isBlankUniqueValue(value) {\n if (value === undefined || value === null) return true;\n if (typeof value === 'number') return Number.isNaN(value);\n if (typeof value === 'boolean') return false;\n if (Array.isArray(value)) return value.length === 0;\n return String(value).trim() === '';\n}\n\nexport function normalizeUniqueValue(value, normalizer = 'trim') {\n if (value === undefined || value === null) return '';\n const text = String(value);\n switch (String(normalizer)) {\n case 'exact': return text;\n case 'lower':\n case 'trimLower':\n case 'email': return text.trim().toLowerCase();\n case 'digitsOnly': return text.replace(/\\D+/g, '');\n case 'trim':\n default: return text.trim();\n }\n}\n\nexport function shouldCheckUniqueValue(rule, value) {\n if (!rule) return false;\n if (rule.skipBlank !== false && isBlankUniqueValue(value)) return false;\n return true;\n}\n\n// antd/rc-field-form filters rules by trigger. `[]` matches no trigger at all,\n// so a submit-only rule runs exclusively inside form.validateFields().\nexport function uniqueValidateTriggers(rule) {\n if (!rule) return [];\n if (rule.validateOn === 'submit') return [];\n if (rule.validateOn === 'change') return ['onChange', 'onBlur'];\n return ['onBlur'];\n}\n\n// ── The checker (cache + staleness + pending) ────────────────────────────────\n\nexport const UNIQUE_STATUS = {\n SKIPPED: 'skipped',\n AVAILABLE: 'available',\n DUPLICATE: 'duplicate',\n STALE: 'stale',\n ERROR: 'error',\n};\n\nfunction cacheKey({ module, field, recordId, normalized }) {\n return `${module}\u0000${field}\u0000${recordId ?? ''}\u0000${normalized}`;\n}\n\n/**\n * One checker per form session. Both form engines create exactly one and pass\n * it into getRules; it is the only place a uniqueness request is ever made.\n *\n * @param checkFieldUnique the API function (injected so it can be mocked)\n * @param onPendingChange (pendingCount) => void — drives the submit button\n */\nexport function createUniqueChecker({ checkFieldUnique, onPendingChange } = {}) {\n // Normalized-value cache: the same normalized value is never re-checked in\n // one form session (blur → submit → blur again is one request, not three).\n // Only DEFINITIVE outcomes are cached — a failed request must be retried.\n const cache = new Map();\n // Monotonic sequence. Every request takes the next number and records itself\n // as its field's latest; when a response comes back with a number that is no\n // longer the latest for that field, the user has typed on and the answer\n // describes an OLD value — it is dropped instead of overwriting the new one.\n const latestSeq = new Map();\n let seq = 0;\n let pending = 0;\n\n function setPending(next) {\n pending = next;\n if (typeof onPendingChange === 'function') onPendingChange(pending);\n }\n\n async function check({ module, field, rule, value, recordId, clientId, region } = {}) {\n if (!rule || !module || !field || typeof checkFieldUnique !== 'function') {\n return { status: UNIQUE_STATUS.SKIPPED };\n }\n if (!shouldCheckUniqueValue(rule, value)) {\n return { status: UNIQUE_STATUS.SKIPPED };\n }\n\n const normalized = normalizeUniqueValue(value, rule.normalizer);\n const key = cacheKey({ module, field, recordId, normalized });\n if (cache.has(key)) return cache.get(key);\n\n seq += 1;\n const mySeq = seq;\n latestSeq.set(field, mySeq);\n setPending(pending + 1);\n\n try {\n const result = await checkFieldUnique({\n module,\n field,\n value,\n // recordId is passed straight through: EditFormV1 supplies it (so the\n // record does not collide with itself), AddFormV1 never does.\n recordId,\n clientId,\n region,\n });\n\n if (latestSeq.get(field) !== mySeq) return { status: UNIQUE_STATUS.STALE };\n\n const outcome = result?.available === false\n ? {\n status: UNIQUE_STATUS.DUPLICATE,\n message: firstDuplicateMessage(result) || rule.message || DEFAULT_MESSAGE,\n }\n : { status: UNIQUE_STATUS.AVAILABLE };\n\n cache.set(key, outcome);\n return outcome;\n } catch (err) {\n if (latestSeq.get(field) !== mySeq) return { status: UNIQUE_STATUS.STALE };\n // FAIL SAFE. A network/server failure must never block a user from\n // typing or submitting — submit-time enforcement on the server is the\n // authoritative check, and it still runs. Not cached, so the next blur\n // retries.\n return { status: UNIQUE_STATUS.ERROR, error: err };\n } finally {\n setPending(Math.max(0, pending - 1));\n }\n }\n\n return {\n check,\n isPending: () => pending > 0,\n pendingCount: () => pending,\n // Exposed for tests / a form that reloads its config mid-session.\n reset: () => { cache.clear(); latestSeq.clear(); },\n };\n}\n\nfunction firstDuplicateMessage(result) {\n const fromErrors = (result?.errors ?? []).find((item) => item?.message)?.message;\n return fromErrors || result?.message || '';\n}\n\n// ── antd rule factory ────────────────────────────────────────────────────────\n\n// Marker so getRules can find the unique rules again after the array is built\n// (see attachUniqueSyncGuards).\nconst UNIQUE_RULE_FLAG = '__uniqueRule';\n\n/**\n * Build the async antd rule for one `unique` validations entry.\n * `context` is supplied by the form engine: { checker, module, recordId,\n * clientId, region }. With no context (test harnesses, other callers of\n * getRules) the rule degrades to a no-op instead of throwing.\n */\nexport function buildUniqueValidationRule({ field, validation, context }) {\n const rule = parseUniqueValidation(validation);\n if (!rule) return { validator: () => Promise.resolve() };\n\n const fieldKey = field?.field ?? '';\n if (!context?.checker || !context?.module || !fieldKey) {\n return { validator: () => Promise.resolve() };\n }\n\n return {\n [UNIQUE_RULE_FLAG]: rule,\n validateTrigger: uniqueValidateTriggers(rule),\n validator: async (_, value) => {\n const outcome = await context.checker.check({\n module: context.module,\n field: fieldKey,\n rule,\n value,\n recordId: context.recordId,\n clientId: context.clientId,\n region: context.region,\n });\n if (outcome.status === UNIQUE_STATUS.DUPLICATE) {\n return Promise.reject(new Error(outcome.message || rule.message));\n }\n // skipped / available / stale / error all pass: a stale answer describes\n // a value the user has already replaced, and an error is handled by the\n // authoritative server-side check at submit.\n return Promise.resolve();\n },\n };\n}\n\n/**\n * The uniqueness call must never fire for a value that is ALREADY known\n * invalid (blank required field, malformed email, failed pattern) — that would\n * waste a round trip and stack a confusing second error under the field.\n *\n * antd runs a field's rules in parallel, so ordering alone cannot express\n * \"after the synchronous rules\". Instead each unique rule is re-wrapped with a\n * guard that first evaluates its sibling rules against the same value and\n * resolves immediately if any of them fails.\n */\nexport function attachUniqueSyncGuards(rules = []) {\n const list = Array.isArray(rules) ? rules : [];\n if (!list.some((rule) => rule && rule[UNIQUE_RULE_FLAG])) return list;\n\n const siblings = list.filter((rule) => rule && !rule[UNIQUE_RULE_FLAG]);\n return list.map((rule) => {\n if (!rule || !rule[UNIQUE_RULE_FLAG]) return rule;\n const inner = rule.validator;\n return {\n ...rule,\n validator: async (ruleArg, value) => {\n if (await hasSyncRuleError(siblings, value, ruleArg)) return Promise.resolve();\n return inner(ruleArg, value);\n },\n };\n });\n}\n\n// Minimal evaluator for the rule shapes this form engine actually produces:\n// { required }, { pattern }, { len }, { type:'url' } and custom { validator }.\n// Any rule it cannot interpret is treated as passing — the guard exists to\n// suppress a redundant request, never to invent a failure.\nexport async function hasSyncRuleError(rules = [], value, ruleArg = {}) {\n for (const rule of rules) {\n if (!rule || typeof rule === 'string') continue;\n if (rule.required && isBlankUniqueValue(value)) return true;\n if (!isBlankUniqueValue(value)) {\n if (rule.pattern instanceof RegExp && !new RegExp(rule.pattern.source, rule.pattern.flags).test(String(value))) return true;\n if (rule.len != null && String(value).length !== Number(rule.len)) return true;\n }\n if (typeof rule.validator === 'function') {\n try {\n await rule.validator(ruleArg, value);\n } catch {\n return true;\n }\n }\n }\n return false;\n}\n\n// ── Submit-time 409 → field errors ───────────────────────────────────────────\n\nfunction parseMaybeJson(text) {\n const trimmed = String(text ?? '').trim();\n if (!trimmed.startsWith('{')) return null;\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n}\n\nfunction duplicateBody(source) {\n if (!source) return null;\n if (typeof source === 'string') return parseMaybeJson(source);\n // An Error thrown by the create/update services: the structured body rides\n // on `.data` (createModuleRecord) or `.response` (fetchJsonWithAuth).\n const candidates = [source.data, source.response, source, parseMaybeJson(source.message)];\n for (const candidate of candidates) {\n if (!candidate || typeof candidate !== 'object') continue;\n if (Array.isArray(candidate.errors) || candidate.code === DUPLICATE_VALUE_CODE) return candidate;\n }\n return null;\n}\n\n/**\n * The antd form path a duplicate error must be attached to.\n *\n * A field inside a REPEATABLE (addRow) group is registered under\n * [groupName, rowIndex, fieldKey] — its Form.List is named after the group — so\n * an error reported with only the field key would either land nowhere or, worse,\n * on a same-named field elsewhere in the form. The server sends `group` and\n * `rowIndex` alongside `field` for exactly this case; both are absent for an\n * ordinary field, which keeps the historical plain-string name.\n */\nexport function duplicateErrorName({ field, group, rowIndex }) {\n const key = String(field ?? '');\n const row = Number(rowIndex);\n if (group && Number.isInteger(row) && row >= 0) {\n // A row field's own key may itself be a dotted path within the row object.\n return [String(group), row, ...key.split('.')];\n }\n return key;\n}\n\n/**\n * Map a failed create/update into per-field duplicate errors.\n *\n * The `field` in the response is the FRONTEND field key — which is not always\n * the stored Mongo field (a form field `altEmail` may be stored as\n * `alternateEmail`) — so the returned name is always taken from the response,\n * never from the configured collectionField.\n *\n * `uniqueRules` (from collectUniqueRules) is the fallback path: one of the\n * update services flattens an error body down to its message string, which\n * loses `errors[]`. When the surviving message is exactly the message an admin\n * configured for a unique field, that identifies the field unambiguously\n * without any module or field name being hardcoded here.\n *\n * Returns [] when the failure is not a duplicate, so callers keep their normal\n * error handling.\n */\nexport function extractDuplicateFieldErrors(source, { uniqueRules = [] } = {}) {\n const body = duplicateBody(source);\n if (body) {\n const errors = (Array.isArray(body.errors) ? body.errors : [])\n .filter((item) => item?.field)\n .map((item) => {\n const mapped = {\n field: item.field,\n // `name` is what form.setFields needs; `field` stays for callers (and\n // tests) that only care which field key was reported.\n name: duplicateErrorName(item),\n message: item.message || body.message || DEFAULT_MESSAGE,\n };\n // Only present for a repeatable-group duplicate — never emitted as\n // `undefined`, so an ordinary duplicate is the exact object it always was\n // plus `name`.\n if (Array.isArray(mapped.name)) {\n mapped.group = String(item.group);\n mapped.rowIndex = Number(item.rowIndex);\n }\n return mapped;\n });\n if (errors.length) return errors;\n }\n\n const message = String(\n (body && (body.message || body.error))\n ?? (typeof source === 'string' ? source : source?.message)\n ?? '',\n ).trim();\n if (!message) return [];\n\n const isDuplicateStatus = source?.status === 409 || body?.code === DUPLICATE_VALUE_CODE;\n const matched = uniqueRules.filter(\n (entry) => String(entry?.rule?.message ?? '').trim().toLowerCase() === message.toLowerCase(),\n );\n if (matched.length && (isDuplicateStatus || matched.length === 1)) {\n // Fallback path: the structured body was flattened to a message, so the row\n // index is gone. The field is still identified, and the error lands on the\n // field rather than nowhere — see the note in collectUniqueRules.\n return matched.map((entry) => ({ field: entry.field, name: entry.field, message }));\n }\n return [];\n}\n\nexport default createUniqueChecker;\n","// Config-driven field uniqueness — the single preflight API call.\n//\n// The endpoint is deliberately narrow: the client names a MODULE and a FORM\n// FIELD, never a collection, a collection field or a tenant id. The server\n// resolves the target collection, the stored field and the tenant scope from\n// the caller's claims + the module's Form Groups config. That is a security\n// boundary — do not widen this payload.\n//\n// POST (never GET) so the value never lands in a URL, browser history or an\n// access log.\n//\n// 200 -> { status: true, available: true }\n// 409 -> { status: false, available: false, code: 'DUPLICATE_VALUE',\n// message, errors: [{ field, code: 'already_present', message }] }\n//\n// A 409 is a NORMAL structured outcome here, not a transport failure:\n// fetchJsonWithAuth throws on every non-2xx, so it is caught and converted back\n// into a plain result object. Anything else (network down, 500, gateway HTML)\n// is re-thrown — the caller decides how to fail safe.\nimport { fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL } from './apiConfig';\n\nexport const DUPLICATE_VALUE_CODE = 'DUPLICATE_VALUE';\n\nexport async function checkFieldUnique({\n module,\n field,\n value,\n recordId,\n clientId,\n region,\n} = {}) {\n if (!module || !field) {\n throw new Error('module and field are required to check uniqueness');\n }\n\n // Only the four scope keys the contract allows, and only when they carry a\n // value — an explicit `recordId: undefined` on the Add form must not become a\n // `\"recordId\": null` the server could read as \"exclude nothing / something\".\n const body = { field, value: value ?? '' };\n if (recordId) body.recordId = String(recordId);\n if (clientId) body.clientId = String(clientId);\n if (region) body.region = String(region);\n\n try {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/module/validate-unique?module=${encodeURIComponent(module)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n },\n );\n\n // Treat anything that is not an explicit `available: false` as available;\n // an older/partial server response must not invent a duplicate.\n const available = json?.available !== false;\n return {\n available,\n duplicate: !available,\n message: json?.message ?? '',\n errors: Array.isArray(json?.errors) ? json.errors : [],\n response: json,\n };\n } catch (err) {\n if (err?.status === 409) {\n const body409 = err.response ?? err.data ?? {};\n return {\n available: false,\n duplicate: true,\n message: body409?.message ?? err.message ?? '',\n errors: Array.isArray(body409?.errors) ? body409.errors : [],\n response: body409,\n };\n }\n throw err;\n }\n}\n\nexport default checkFieldUnique;\n\n// ── staged duplicate preflight ───────────────────────────────────────────────\n// POST /module/duplicate-check?module=X body: { payload, excludeId }\n//\n// Asked before a create, so the user can be offered the EXISTING record instead\n// of silently creating a second copy of the same person. Which fields are\n// compared, in what order, and what happens on a hit are all module config —\n// see models.DuplicateCheckStrategy. POST, not GET: the body carries personal\n// data (email, phone) that must not reach a URL or a proxy log.\n//\n// Fails OPEN: a check that cannot run must never block a legitimate create.\nexport async function checkDuplicateRecord({ module, payload, excludeId } = {}) {\n if (!module || !payload) return { duplicate: false };\n try {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/module/duplicate-check?module=${encodeURIComponent(module)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ payload, ...(excludeId ? { excludeId } : {}) }),\n },\n );\n return json?.data ?? json ?? { duplicate: false };\n } catch {\n return { duplicate: false };\n }\n}\n","// Admin-configurable upload content categories. The Form Groups admin stores a\n// category key on a file field (`field.accept`); AddFormV1/EditFormV1 expand it\n// to the browser `accept` attribute and a beforeUpload extension check, so a\n// file outside the category is rejected client-side with a clear message.\n// A field without a category (or 'any') falls back to the fine-grained\n// `fileType` validation rule, preserving existing behaviour.\n\nconst IMAGE_EXTS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'avif'];\n// Mirrors DocumentViewer's VIDEO_EXT so anything uploadable is also previewable.\nconst VIDEO_EXTS = ['mp4', 'webm', 'ogg', 'ogv', 'mov', 'm4v'];\nconst DOC_EXTS = ['pdf', 'doc', 'docx'];\n\nexport const UPLOAD_ACCEPT_OPTIONS = [\n { label: 'Any file', value: 'any' },\n { label: 'Documents & Images (pdf, doc, images)', value: 'documents' },\n { label: 'Images only', value: 'images' },\n { label: 'Videos only', value: 'videos' },\n { label: 'Images & Videos', value: 'imagesAndVideos' },\n];\n\nexport const UPLOAD_ACCEPT_CATEGORIES = {\n documents: {\n exts: [...DOC_EXTS, ...IMAGE_EXTS],\n hint: 'PDF, DOC or image files',\n error: 'Only document (PDF/DOC) or image files are allowed',\n },\n images: {\n exts: IMAGE_EXTS,\n hint: 'Image files',\n error: 'Only image files are allowed',\n },\n videos: {\n exts: VIDEO_EXTS,\n hint: 'Video files',\n error: 'Only video files are allowed',\n },\n imagesAndVideos: {\n exts: [...IMAGE_EXTS, ...VIDEO_EXTS],\n hint: 'Image or video files',\n error: 'Only image or video files are allowed',\n },\n};\n\n// uploadAcceptCategory resolves a field's configured category, or null when the\n// field accepts any file ('any', unset, or an unknown key).\nexport function uploadAcceptCategory(field) {\n return UPLOAD_ACCEPT_CATEGORIES[String(field?.accept ?? '').trim()] ?? null;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// quickActionLabels — what the buttons inside a dropdown actually say.\n//\n// THE PROBLEM\n// Every quick-action button read \"Add More\", in the dropdown AND as the modal\n// title. \"Add More\" tells the user nothing: more of what? And on the Edit\n// button it was actively wrong — the stored config had `quickEditLabel:\n// \"Add More\"` too, so clicking \"Add More\" opened an EDIT form.\n//\n// A button should name the thing it acts on: \"Add New Employer\", \"Edit\n// Employer\". That is what the user is thinking, and it is the difference\n// between a control you have to try and one you can read.\n//\n// HOW THE NAME IS FOUND\n// From the TARGET MODULE the button opens, turned into a readable singular:\n// employers → Employer\n// locationMasters → Location Master\n// client_contacts → Client Contact\n// An admin can still type an explicit label; this only decides what happens\n// when they have not. No module name is hardcoded here.\n// ─────────────────────────────────────────────────────────────────────────\n\n// Words that should not be title-cased into nonsense when they appear inside\n// a module key. Deliberately tiny — this is a display nicety, not a dictionary.\nconst LOWER_WORDS = new Set(['of', 'and', 'the', 'to', 'for', 'in', 'a', 'an']);\n\n// Irregular plurals worth knowing, because the naive \"drop the s\" rule turns\n// them into something visibly wrong on a button.\nconst IRREGULAR_SINGULARS = {\n addresses: 'address',\n branches: 'branch',\n batches: 'batch',\n categories: 'category',\n companies: 'company',\n countries: 'country',\n entries: 'entry',\n people: 'person',\n statuses: 'status',\n};\n\n/**\n * singularize — a module key's singular form.\n *\n * Conservative on purpose: an unknown word that does not clearly look plural\n * is left ALONE. Printing \"Addres\" or \"Statu\" on a button is worse than\n * printing a plural, so the rule only fires where it is safe.\n */\nexport function singularize(word) {\n const w = String(word ?? '').trim();\n if (!w) return '';\n // An ALL-CAPS acronym is never a plural: \"VMS\" is a name, and stripping its\n // trailing S produces \"VM\" — a different thing entirely.\n if (w.length > 1 && w === w.toUpperCase() && /[A-Z]/.test(w)) return w;\n const lower = w.toLowerCase();\n if (IRREGULAR_SINGULARS[lower]) return IRREGULAR_SINGULARS[lower];\n // \"-ies\" → \"-y\" (categories → category)\n if (/[^aeiou]ies$/i.test(w)) return w.slice(0, -3) + 'y';\n // \"-ses\"/\"-xes\"/\"-zes\"/\"-ches\"/\"-shes\" → drop \"es\"\n if (/(s|x|z|ch|sh)es$/i.test(w)) return w.slice(0, -2);\n // A plain trailing \"s\", but never \"ss\" (address) and never a bare \"s\".\n if (/[^s]s$/i.test(w)) return w.slice(0, -1);\n return w;\n}\n\n/**\n * humanizeModule — a module key as a person would write it.\n * \"locationMasters\" → \"Location Master\"\n */\nexport function humanizeModule(moduleKey) {\n const raw = String(moduleKey ?? '').trim();\n if (!raw) return '';\n const words = raw\n // An acronym RUN followed by a word: \"MSPRequests\" → \"MSP Requests\".\n // Must run before the camelCase split, which cannot see this boundary\n // because there is no lowercase letter in front of the capital.\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n // camelCase → camel Case\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n // snake_case / kebab-case / dots → spaces\n .replace(/[_\\-.]+/g, ' ')\n .split(/\\s+/)\n .filter(Boolean);\n if (!words.length) return '';\n\n const singularLast = singularize(words[words.length - 1]);\n const all = [...words.slice(0, -1), singularLast];\n\n return all\n .map((word, index) => {\n const lower = word.toLowerCase();\n // Preserve an ALL-CAPS acronym an admin deliberately used (VMS, MSP).\n if (word.length > 1 && word === word.toUpperCase()) return word;\n if (index > 0 && LOWER_WORDS.has(lower)) return lower;\n return lower.charAt(0).toUpperCase() + lower.slice(1);\n })\n .join(' ');\n}\n\n/**\n * quickCreateLabel — the \"add\" button's text.\n * An explicitly configured label always wins.\n */\nexport function quickCreateLabel(field, fallbackModule) {\n const configured = String(field?.quickCreateLabel ?? '').trim();\n // \"Add More\" is treated as UNSET rather than as a choice: it is the old\n // default that this function exists to replace, and it is stored on live\n // config. Honouring it literally would mean the fix silently did nothing.\n if (configured && configured.toLowerCase() !== 'add more') return configured;\n const name = humanizeModule(field?.quickCreateModule || fallbackModule);\n return name ? `Add New ${name}` : 'Add New';\n}\n\n/**\n * quickEditLabel — the \"edit\" button's text.\n */\nexport function quickEditLabel(field, fallbackModule) {\n const configured = String(field?.quickEditLabel ?? '').trim();\n // Same reasoning, and here it also fixes a real bug: the stored config had\n // \"Add More\" on the EDIT button, so clicking \"Add More\" opened an edit form.\n if (configured && configured.toLowerCase() !== 'add more') return configured;\n const name = humanizeModule(field?.quickEditModule || fallbackModule);\n return name ? `Edit ${name}` : 'Edit';\n}\n\n/**\n * quickModalTitle — the popup's heading.\n *\n * The requirement asks for the popup to match the button (\"same in the popup\n * also\"), so it uses the same resolved name. The heading is allowed to be\n * slightly fuller than the button, because a dialog title has room and the\n * user has just left the context behind.\n */\nexport function quickModalTitle(mode, field, fallbackModule) {\n const name = humanizeModule(\n (mode === 'create' ? field?.quickCreateModule : field?.quickEditModule) || fallbackModule,\n );\n if (mode === 'create') {\n const configured = String(field?.quickCreateTitle ?? '').trim();\n if (configured) return configured;\n return name ? `Add New ${name}` : quickCreateLabel(field, fallbackModule);\n }\n const configured = String(field?.quickEditTitle ?? '').trim();\n if (configured) return configured;\n return name ? `Edit ${name}` : quickEditLabel(field, fallbackModule);\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","// ─────────────────────────────────────────────────────────────────────────\n// quickCreateNotice — config + resolution logic for the \"you just created a\n// brand-new record\" popup shown after an in-dropdown quick-create.\n//\n// WHY this lives in its own module rather than inside QuickCreateEditField:\n// anything that imports AddFormV1/EditFormV1 (which QuickCreateEditField does,\n// lazily, and which the admin screen does statically) cannot be rendered under\n// vitest — react-pdf's DocumentViewer needs DOMMatrix and jsdom has none. That\n// is a pre-existing trap in this repo. Keeping the *decisions* (is the notice\n// on? what is the record called? who created it?) in a dependency-free module\n// means the part that can actually be wrong is unit-testable, and the React\n// file is left with nothing but rendering.\n//\n// Everything here is generic: no module name, no field name, no collection is\n// special-cased. A field opts in through the admin config key\n// `quickCreateNotice`.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport { fetchJsonWithAuth, getStoredUser } from '../../services/authApi';\nimport { AUTH_URL } from '../../services/apiConfig';\nimport { fetchUserName, userDisplayName } from '../detail/userNames';\n\n/**\n * The product-owner-approved default. It is the DEFAULT rather than something\n * the admin must type, so that a field which merely switches the notice on\n * still shows the correct warning. Admin config overrides it verbatim.\n */\nexport const DEFAULT_QUICK_CREATE_NOTICE_MESSAGE =\n 'A new client has been created successfully. Please contact your Organization Admin to '\n + 'configure the required forms, workflows, permissions, and client-specific details before '\n + 'proceeding with further operations.';\n\nexport const DEFAULT_QUICK_CREATE_NOTICE_TITLE = 'New record created';\nexport const DEFAULT_QUICK_CREATE_RECORD_LABEL = 'Record name';\nexport const DEFAULT_QUICK_CREATE_CREATOR_LABEL = 'Created by';\n\nconst str = (v) => (v === undefined || v === null ? '' : String(v).trim());\n\n/**\n * normalizeQuickCreateNotice — field config → the resolved notice settings, or\n * null when this field has not opted in.\n *\n * `enabled` defaults to TRUE when the key is present at all: an admin who took\n * the trouble to author a `quickCreateNotice` object meant to switch it on, and\n * a config object that silently does nothing is the worst of the two failure\n * modes. Only an explicit `enabled: false` turns it off.\n */\nexport function normalizeQuickCreateNotice(field) {\n const raw = field?.quickCreateNotice;\n if (!raw || typeof raw !== 'object') return null;\n if (raw.enabled === false) return null;\n\n return {\n enabled: true,\n title: str(raw.title) || DEFAULT_QUICK_CREATE_NOTICE_TITLE,\n message: str(raw.message) || DEFAULT_QUICK_CREATE_NOTICE_MESSAGE,\n // Both traceability lines are ON unless explicitly disabled — same reasoning\n // as `enabled` above.\n showCreator: raw.showCreator !== false,\n showRecordName: raw.showRecordName !== false,\n recordNameField: str(raw.recordNameField),\n recordLabel: str(raw.recordLabel) || DEFAULT_QUICK_CREATE_RECORD_LABEL,\n creatorLabel: str(raw.creatorLabel) || DEFAULT_QUICK_CREATE_CREATOR_LABEL,\n };\n}\n\n// Keys that commonly hold a record's human name, tried in order when the admin\n// configured no recordNameField and the field carries no lookup displayField.\nconst NAME_LIKE_KEYS = [\n 'clientName', 'companyName', 'name', 'title', 'fullName',\n 'displayName', 'label', 'employerName', 'vendorName',\n];\n\nconst readPath = (record, key) => {\n if (!record || !key) return undefined;\n return String(key).split('.').reduce((acc, part) => (\n acc && typeof acc === 'object' ? acc[part] : undefined\n ), record);\n};\n\nconst scalarName = (v) => {\n if (typeof v === 'string' || typeof v === 'number') return str(v);\n // Labeled-select / reference shapes ({label,value}) show up on records too.\n if (v && typeof v === 'object') return str(v.label ?? v.name ?? v.title ?? '');\n return '';\n};\n\n/**\n * pickRecordName — the record's display name, by the configured key first, then\n * the field's lookup displayField, then well-known name-like keys, then ANY\n * string key whose name ends in \"name\". Purely best-effort: an empty result is\n * a legitimate outcome and must not stop the notice from appearing.\n */\nexport function pickRecordName(record, { recordNameField = '', displayField = '' } = {}) {\n if (!record || typeof record !== 'object') return '';\n for (const key of [recordNameField, displayField, ...NAME_LIKE_KEYS]) {\n if (!key) continue;\n const value = scalarName(readPath(record, key));\n if (value) return value;\n }\n const loose = Object.keys(record).find((k) => (\n /name$/i.test(k) && typeof record[k] === 'string' && record[k].trim()\n ));\n return loose ? str(record[loose]) : '';\n}\n\n/**\n * pickCreatorId — the id of whoever the stored record credits with its\n * creation, across the several shapes the gateway's audit stamp can take\n * (plain id, ObjectId wrapper, nested user object).\n */\nexport function pickCreatorId(record) {\n if (!record || typeof record !== 'object') return '';\n const candidates = [\n record.createdBy, record.created_by, record.createdById,\n record.createdUserId, record.recordMeta?.createdBy, record.ownerId,\n ];\n for (const candidate of candidates) {\n if (candidate === undefined || candidate === null || candidate === '') continue;\n if (typeof candidate === 'object') {\n const nested = candidate.$oid ?? candidate.userId ?? candidate._id ?? candidate.id;\n const value = str(nested);\n if (value) return value;\n continue;\n }\n const value = str(candidate);\n if (value) return value;\n }\n return '';\n}\n\n/** currentUserName — the signed-in user's display name, '' when unknowable. */\nexport function currentUserName(getUser = getStoredUser) {\n try {\n return userDisplayName(getUser()) || '';\n } catch {\n return '';\n }\n}\n\nconst defaultFetchRecord = (module, recordId) => fetchJsonWithAuth(\n AUTH_URL,\n `/module/list?module=${encodeURIComponent(module)}&id=${encodeURIComponent(recordId)}`,\n).then((res) => {\n const rows = res?.data?.data ?? res?.data ?? [];\n return Array.isArray(rows) ? rows[0] : rows;\n});\n\n/**\n * resolveQuickCreateNoticeDetails — the two traceability lookups.\n *\n * FAIL OPEN, deliberately and on every branch: the warning message is the point\n * of this popup, the name and the creator are garnish. A directory that is down,\n * a record the list endpoint cannot return, a module name that does not resolve\n * — none of it may suppress the notice or throw into the form. Every await is\n * individually caught and degrades to an empty string.\n *\n * The creator falls back to the signed-in user because in this flow the creator\n * IS the current user: they pressed \"Add More\" seconds ago. That fallback is\n * therefore accurate, not a guess.\n *\n * Dependencies are injected (with real defaults) so this is testable without a\n * network or a browser.\n */\nexport async function resolveQuickCreateNoticeDetails({\n module,\n recordId,\n notice,\n displayField = '',\n deps = {},\n} = {}) {\n const {\n fetchRecord = defaultFetchRecord,\n fetchUserNameFn = fetchUserName,\n getUser = getStoredUser,\n } = deps;\n\n let record = null;\n if (module && recordId) {\n try {\n record = await fetchRecord(module, recordId);\n } catch {\n record = null; // fail open\n }\n }\n\n const recordName = notice?.showRecordName === false\n ? ''\n : pickRecordName(record, { recordNameField: notice?.recordNameField, displayField });\n\n let creatorName = '';\n if (notice?.showCreator !== false) {\n const creatorId = pickCreatorId(record);\n if (creatorId) {\n try {\n creatorName = str(await fetchUserNameFn(creatorId));\n } catch {\n creatorName = ''; // fail open\n }\n }\n if (!creatorName) creatorName = currentUserName(getUser);\n }\n\n return { recordName, creatorName };\n}\n","// QuickCreateEditField — the \"Add More\" / \"Edit\" affordance inside a select\n// dropdown. Fully admin-config driven (FormGroupField.quickCreate / quickEdit,\n// see the Go struct). When a select carries quickCreate, its dropdown grows an\n// \"Add More\" button that opens the target module's Add form in a modal; on a\n// successful create the host field refetches its options and selects the new\n// record. quickEdit adds an \"Edit\" button that opens the target module's Edit\n// form for a resolved record id (this field's own value, or a sibling field via\n// quickEditIdFrom — e.g. \"edit the SELECTED CLIENT to add a contact\" from the\n// job's Contact Person field). No module or field name is hardcoded here.\n//\n// A field may also carry `quickCreateNotice` (see ./quickCreateNotice.js): a\n// popup shown the instant a record is created HERE, warning that a brand-new\n// record is not yet configured. Because only a real quick-create reaches that\n// code path, the notice cannot fire for an existing record that was merely\n// selected.\n//\n// AddFormV1/EditFormV1 are pulled in with React.lazy so this module can be\n// imported by those same files without a static circular dependency.\nimport React, { Suspense, lazy, useMemo, useState } from 'react';\nimport { quickCreateLabel, quickEditLabel, quickModalTitle, humanizeModule } from './quickActionLabels';\nimport { Modal, Space, Spin, Button } from 'antd';\nimport { PlusOutlined, EditOutlined, ExclamationCircleFilled } from '@ant-design/icons';\nimport AppButton from '../AppButton';\nimport { normalizeQuickCreateNotice, resolveQuickCreateNoticeDetails } from './quickCreateNotice';\n\nconst AddFormV1 = lazy(() => import('../AddFormV1'));\nconst EditFormV1 = lazy(() => import('../EditFormV1'));\n\nconst empty = (v) => v === undefined || v === null || v === '';\nconst pathOf = (key) => (typeof key === 'string' && key.includes('.') ? key.split('.') : [key]);\n\n// Resolve the record id a quick-edit should open. Priority:\n// 1. field.quickEditIdFrom → a sibling field's value (row-scoped inside an\n// addRow row, else top-level; falls back to scopeValues, e.g. clientId).\n// 2. this field's own selected value (labeled selects carry {value}).\nfunction resolveEditId(field, form, name, scopeValues, ownValue) {\n const idFrom = field.quickEditIdFrom;\n if (idFrom) {\n const insideRow = Array.isArray(name) && name.length >= 3 && typeof name[1] === 'number';\n const abs = insideRow ? [...name.slice(0, 2), ...pathOf(idFrom)] : pathOf(idFrom);\n let v = form?.getFieldValue?.(abs);\n if (empty(v)) v = scopeValues?.[idFrom];\n return empty(v) ? null : v;\n }\n if (ownValue && typeof ownValue === 'object') return ownValue.value ?? null;\n return empty(ownValue) ? null : ownValue;\n}\n\n/**\n * useQuickField — returns { footer, modal } for a select FieldControl.\n * footer: JSX to append inside the Select's dropdownRender (the action buttons)\n * modal: JSX to render alongside the Select (the create/edit Modal)\n * onDone(kind, recordId) fires after a successful create/edit so the caller can\n * refetch options (and, for create, select the new record).\n */\nexport function useQuickField({ field, form, name, moduleName, scopeValues, ownValue, onDone }) {\n const [modal, setModal] = useState(null); // { mode: 'create'|'edit', module, recordId }\n // The post-create notice (see handleCreated below). Held here — above the\n // early return — because hooks may not be called conditionally.\n const [notice, setNotice] = useState(null); // { title, message, recordLabel, ... , recordName, creatorName }\n\n const quickCreate = Boolean(field?.quickCreate);\n const quickEdit = Boolean(field?.quickEdit);\n const createModule = field?.quickCreateModule || field?.lookupCollection || '';\n const editModule = field?.quickEditModule || field?.lookupCollection || '';\n\n const editId = useMemo(\n () => (quickEdit ? resolveEditId(field, form, name, scopeValues, ownValue) : null),\n // ownValue / sibling changes should re-resolve\n [quickEdit, field, form, name, scopeValues, ownValue],\n );\n\n if (!quickCreate && !quickEdit) return { footer: null, modal: null };\n\n const close = () => setModal(null);\n // quickCreateOnClick (opt-in): call a plain local function instead of\n // opening the generic module's Add form or navigating — for a create\n // flow the page already has its own custom modal/logic for. Checked\n // before quickCreateRoute; existing fields without either stay on\n // today's default modal behavior.\n const openCreate = () => {\n if (field?.quickCreateOnClick) {\n field.quickCreateOnClick();\n // This bypass opens the caller's own external modal, not the built-in\n // in-dropdown create flow — so unlike that flow, there's no reason to\n // keep the Select's dropdown open underneath it. Close it immediately\n // instead of letting it linger until the new modal's mask steals focus.\n document.activeElement?.blur?.();\n return;\n }\n if (field?.quickCreateRoute) {\n window.open(field.quickCreateRoute, '_blank', 'noopener,noreferrer');\n return;\n }\n createModule && setModal({ mode: 'create', module: createModule });\n };\n const openEdit = () => editModule && editId && setModal({ mode: 'edit', module: editModule, recordId: String(editId) });\n\n // ── The \"brand-new record\" notice ────────────────────────────────────────\n // WHY it hooks the quick-create success path and nothing else:\n // reaching this callback is only possible by having just SAVED a record\n // through the in-dropdown Add form. Selecting an existing option never\n // travels through here at all. That makes \"new records only\" a STRUCTURAL\n // guarantee of where the code sits, not a runtime test we have to keep\n // true. A heuristic (\"does this id look new?\", \"was it absent from the\n // options list?\") would be a second source of truth that drifts the first\n // time options are cached, paginated or server-filtered — so we\n // deliberately do NOT add one.\n //\n // WHY it fires now instead of after the outer form is submitted: the stated\n // purpose is to stop the recruiter proceeding as though a brand-new client\n // were fully configured. A warning shown after they finished the submission\n // is a receipt, not a guard. It must land while the decision to continue is\n // still ahead of them.\n const handleCreated = (newId) => {\n const config = normalizeQuickCreateNotice(field);\n if (!config) return;\n // Open immediately with whatever we know (nothing yet). The two lookups\n // below only ever ENRICH this; they can never delay or cancel it, which\n // is what \"fail open\" means here.\n setNotice({ ...config, recordName: '', creatorName: '' });\n resolveQuickCreateNoticeDetails({\n module: createModule,\n recordId: newId,\n notice: config,\n displayField: field?.displayField || '',\n })\n .then(({ recordName, creatorName }) => {\n setNotice((prev) => (prev ? { ...prev, recordName, creatorName } : prev));\n })\n .catch(() => { /* already open; the message is the important part */ });\n };\n\n const footer = (\n <div\n className=\"v1-quick-actions\"\n role=\"presentation\"\n onMouseDown={(e) => e.preventDefault()} // keep the select open while clicking\n style={{ display: 'flex', gap: 8, padding: '6px 8px', borderTop: '1px solid rgba(0,0,0,0.06)' }}\n >\n <Space size={8}>\n {quickCreate && (\n <Button type=\"link\" size=\"small\" icon={<PlusOutlined />} onClick={openCreate} style={{ paddingLeft: 0 }}>\n {quickCreateLabel(field, createModule)}\n </Button>\n )}\n {quickEdit && (\n <Button\n type=\"link\"\n size=\"small\"\n icon={<EditOutlined />}\n onClick={openEdit}\n disabled={!editId}\n title={!editId ? `Choose a ${humanizeModule(editModule) || 'record'} above first, then edit it here` : undefined}\n >\n {quickEditLabel(field, editModule)}\n </Button>\n )}\n </Space>\n </div>\n );\n\n const modalNode = modal ? (\n <Modal\n open\n title={quickModalTitle(modal.mode, field, modal.mode === 'create' ? createModule : editModule)}\n width=\"min(1080px, 96vw)\"\n footer={null}\n destroyOnClose\n maskClosable={false}\n onCancel={close}\n styles={{ body: { maxHeight: '78vh', overflowY: 'auto' } }}\n >\n <Suspense fallback={<div style={{ padding: 48, textAlign: 'center' }}><Spin /></div>}>\n {modal.mode === 'create' ? (\n <AddFormV1\n moduleName={modal.module}\n embedded\n breadcrumbItems={[]}\n onCancel={close}\n onSuccess={(newId) => { close(); handleCreated(newId); onDone?.('create', newId); }}\n />\n ) : (\n <EditFormV1\n moduleName={modal.module}\n recordId={modal.recordId}\n embedded\n breadcrumbItems={[]}\n onCancel={close}\n onSuccess={(rid) => { close(); onDone?.('edit', rid); }}\n />\n )}\n </Suspense>\n </Modal>\n ) : null;\n\n // Informational/warning notice. Rendered as a sibling of the create modal\n // (never nested inside it) so it survives that modal being destroyed on\n // close — the create form unmounts the moment it succeeds.\n const noticeNode = notice ? (\n <Modal\n open\n title={(\n <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>\n <ExclamationCircleFilled style={{ color: '#faad14' }} />\n {notice.title}\n </span>\n )}\n width=\"min(520px, 94vw)\"\n maskClosable={false}\n onCancel={() => setNotice(null)}\n footer={(\n <AppButton variant=\"primary\" type=\"primary\" onClick={() => setNotice(null)}>\n OK\n </AppButton>\n )}\n >\n <p style={{ marginTop: 0, marginBottom: 16 }}>{notice.message}</p>\n {/* Traceability pair. Each line is dropped when its lookup produced\n nothing — an empty \"Created by:\" is noise, not information (the\n same rule detail/userNames.js applies to unresolvable actors). */}\n {(notice.recordName || notice.creatorName) && (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'auto 1fr',\n gap: '6px 12px',\n padding: '10px 12px',\n borderRadius: 6,\n background: 'rgba(0,0,0,0.03)',\n }}\n >\n {notice.recordName && (\n <>\n <span style={{ color: 'rgba(0,0,0,0.55)' }}>{notice.recordLabel}</span>\n <strong>{notice.recordName}</strong>\n </>\n )}\n {notice.creatorName && (\n <>\n <span style={{ color: 'rgba(0,0,0,0.55)' }}>{notice.creatorLabel}</span>\n <strong>{notice.creatorName}</strong>\n </>\n )}\n </div>\n )}\n </Modal>\n ) : null;\n\n return {\n footer,\n modal: (modalNode || noticeNode) ? (<>{modalNode}{noticeNode}</>) : null,\n };\n}\n","// crossFieldRules — validation rules that compare a field against ANOTHER field,\n// shared by AddFormV1 and EditFormV1 so both forms enforce them identically.\n//\n// The historical cross-field rules (greaterThanField, dateAfterField, …) live\n// inline in each form and resolve the referenced key as a SIBLING: the same\n// addRow row when the rule sits inside one, the top level otherwise. That is\n// wrong for a rule that must reach OUT of its row — e.g. \"the experience typed\n// on a relevant-skill row may not exceed the candidate's overall experience\",\n// where the row field points at a top-level field.\n//\n// resolveScopedFieldPath fixes that by using the SAME scoping rule the render\n// side already applies to `showIf`: renderField builds a `prefixFor(key)`\n// closure that returns the row prefix ([listName, rowIndex]) only when the\n// referenced key is one of the row's own fields (opts.rowFieldKeys), and null\n// for anything else — which resolves to the top-level path. Passing that same\n// closure in here means a rule and a Show Condition can never disagree about\n// which field a key refers to.\n\n// Local emptiness check — mirrors the forms' own `empty` helper. Kept here so\n// this module has no import back into either form (they both import from it).\nconst isBlank = (v) => v === undefined || v === null || v === ''\n || (Array.isArray(v) && v.length === 0);\n\n/**\n * resolveScopedFieldPath — the antd name path a rule's referenced field key\n * resolves to.\n *\n * @param {Array|string} name the CURRENT field's absolute name path\n * @param {string} key the referenced field key (dot notation allowed)\n * @param {Function} [prefixFor] renderField's showIf prefix resolver\n * @returns {Array} absolute name path\n */\nexport function resolveScopedFieldPath(name, key, prefixFor) {\n const parts = String(key ?? '').includes('.') ? String(key).split('.') : [key];\n if (typeof prefixFor === 'function') {\n const prefix = prefixFor(key);\n return Array.isArray(prefix) && prefix.length ? [...prefix, ...parts] : parts;\n }\n // No prefix resolver (e.g. a repeat-scalar item): keep the historical\n // sibling-first behaviour so nothing that worked before changes.\n return Array.isArray(name) && name.length > 1 ? [...name.slice(0, -1), ...parts] : parts;\n}\n\n/**\n * maxFromFieldRule — `{ type: 'maxFromField', field: '<otherFieldKey>', message }`\n *\n * The value may not exceed the CURRENT value of another field. Generic: which\n * field caps which is admin config (Form Groups → field → validations), never\n * code. Fails OPEN whenever either side is blank or non-numeric — a validation\n * rule must never block a save it cannot actually evaluate.\n */\nexport function maxFromFieldRule({ label, message, rule = {}, value, form, name, prefixFor }) {\n const referencedKey = rule.field ?? rule.compareField ?? value;\n return {\n validator: async (_, input) => {\n if (!referencedKey || isBlank(input)) return Promise.resolve();\n const other = form?.getFieldValue?.(resolveScopedFieldPath(name, referencedKey, prefixFor));\n if (isBlank(other)) return Promise.resolve();\n const maximum = Number(other);\n const current = Number(input);\n if (Number.isNaN(maximum) || Number.isNaN(current)) return Promise.resolve();\n if (current <= maximum) return Promise.resolve();\n return Promise.reject(new Error(message ?? `${label} must not exceed ${referencedKey}`));\n },\n };\n}\n\n/**\n * maxFromFieldDependency — the absolute name path a maxFromField rule depends\n * on, so antd re-runs the rule when the referenced field changes.\n */\nexport function maxFromFieldDependency(rule = {}, name, prefixFor) {\n const referencedKey = rule.field ?? rule.compareField ?? rule.value;\n if (!referencedKey) return null;\n return resolveScopedFieldPath(name, referencedKey, prefixFor);\n}\n\nexport default { resolveScopedFieldPath, maxFromFieldRule, maxFromFieldDependency };\n","/**\n * maxCeiling — the pure core of the `maxFieldWithFallback` validation.\n *\n * The rule caps a numeric field against the FIRST usable value in an ordered,\n * admin-authored list of other fields (\"cap at the To value, else the From\n * value\"). Both AddFormV1 and EditFormV1 hold a thin antd validator around\n * these functions; everything decision-shaped lives here so it can be unit\n * tested without rendering a form (importing either form component pulls in\n * react-pdf, which dies on jsdom's missing DOMMatrix).\n *\n * Nothing here knows a module, group or field name — the list of compare\n * fields comes from the validation entry in Form Groups config.\n */\n\n/**\n * Resolve a compare-field reference to an absolute form name path, relative to\n * the field being validated. Kept identical to the behaviour both forms had\n * inline (they now delegate here):\n *\n * • DOTTED reference (\"jobClientRate.clientRateTo\") → ABSOLUTE path, i.e. it\n * reaches out of the validated field's own container. This is what lets a\n * field inside one container (candidateBudget.*) be capped by a value\n * parked in a different one — inside a Form.List row the row prefix is\n * preserved instead, so a per-row rule still resolves within its row.\n * • BARE reference (\"clientRateTo\") → sibling of the validated field, i.e.\n * the last path segment is swapped. A bare name can therefore never see a\n * top-level field from inside a container — use a dotted reference for that.\n */\nexport function resolveCompareFieldName(currentName, compareField) {\n const compareParts = String(compareField).split('.').filter(Boolean);\n if (!Array.isArray(currentName)) return compareParts.length > 1 ? compareParts : compareField;\n\n if (compareParts.length > 1) {\n // A DB rule may use `endDate` or `work_experience.endDate`. Preserve\n // the current Form.List row and avoid repeating the group segment.\n if (currentName.length >= 3 && typeof currentName[1] === 'number') {\n const listPrefix = currentName.slice(0, 2);\n const relativeParts = compareParts[0] === String(currentName[0])\n ? compareParts.slice(1)\n : compareParts;\n return [...listPrefix, ...relativeParts];\n }\n return compareParts;\n }\n\n const nextName = [...currentName];\n nextName[nextName.length - 1] = compareParts[0];\n return nextName;\n}\n\n/** \"a, b ,, c\" → ['a','b','c'] — the admin writes the list comma-separated. */\nexport function parseCompareFields(value) {\n return String(value ?? '')\n .split(',')\n .map((item) => item.trim())\n .filter(Boolean);\n}\n\n/**\n * Pick the ceiling: the first compare field that carries a USABLE maximum.\n *\n * Usable means \"a positive, finite number\". Empty/blank falls through (the\n * long-standing behaviour) and so does ZERO or a negative number, which is the\n * subtle part:\n *\n * A 0 ceiling means \"no ceiling configured\", NOT \"nothing is allowed\".\n * Rate ranges in this system are routinely persisted with the open end at 0\n * (submissions store candidateBudgetEnd: 0 on nearly every record; jobs with\n * a single-point budget leave clientBudgetEnd unset or 0). Treating that 0 as\n * a real maximum would reject EVERY value the user could type, with an error\n * they have no way to satisfy. The rest of the codebase already reads these\n * pairs the same way — see popups/definitions/others-assign/config.js\n * (`end > 0 ? end : start`) and financeApprovalEngine's `clientBudgetEnd ||\n * clientBudgetStart`. So a non-positive candidate is skipped and the next\n * entry in the list is tried; if none qualifies there is simply no cap.\n *\n * @param {string[]} compareFields ordered field references\n * @param {(field: string) => any} readValue reads the current value of one\n * @returns {{ field: string, maximum: number } | null}\n */\nexport function selectCeiling(compareFields, readValue) {\n for (const field of compareFields ?? []) {\n const raw = readValue(field);\n if (raw === undefined || raw === null || raw === '') continue;\n const maximum = Number(raw);\n if (!Number.isFinite(maximum) || maximum <= 0) continue;\n return { field, maximum };\n }\n return null;\n}\n\n/**\n * The whole decision: is `input` within the first usable ceiling?\n * Fails open (ok:true) for a blank input, a non-numeric input, and when no\n * compare field carries a usable ceiling.\n *\n * @returns {{ ok: boolean, field: string|null, maximum: number|null }}\n */\nexport function checkMaxWithFallback({ input, compareFields, readValue }) {\n const pass = { ok: true, field: null, maximum: null };\n if (input === undefined || input === null || input === '') return pass;\n\n const ceiling = selectCeiling(compareFields, readValue);\n if (!ceiling) return pass;\n\n const inputNumber = Number(input);\n if (!Number.isFinite(inputNumber)) return pass;\n\n return {\n ok: inputNumber <= ceiling.maximum,\n field: ceiling.field,\n maximum: ceiling.maximum,\n };\n}\n","/**\n * contextPrefill — pure helpers for the cross-module context prefill\n * (`field.prefillFromModule` + `field.prefillFrom`).\n *\n * A field can inherit its value from a DIFFERENT module's record than the form\n * is editing/creating — e.g. a submission field fed by the JOB it is raised\n * against. The two forms differ only in where the linked record's id comes\n * from:\n *\n * AddFormV1 the URL linkage (?jobId= / ?candidateId= on Quick Submit)\n * EditFormV1 the record's OWN stored ids, surfaced by getFormGroups as\n * `recordMeta` (every top-level \"*Id\" key of the record)\n *\n * Both shapes are plain { someId: value } maps, so one resolver serves both.\n * No module or field name is hardcoded in either form — the alias table below\n * is the single place where a module's conventional id key is spelled out.\n */\n\nimport { getDeep } from './payloadTransformer';\n\n// Extra id keys a module is known by, beyond the derived `<module>Id` /\n// `<singular>Id`. Submissions/candidates historically link by \"applicantId\".\nconst EXTRA_ID_KEYS = {\n candidates: ['applicantId'],\n};\n\n/** Candidate id keys for a (normalized, plural) module key, most specific first. */\nexport function contextIdKeys(moduleKey) {\n const key = String(moduleKey ?? '').trim();\n if (!key) return [];\n const singular = key.endsWith('s') ? key.slice(0, -1) : key;\n return [...new Set([`${singular}Id`, `${key}Id`, ...(EXTRA_ID_KEYS[key] ?? [])])];\n}\n\n/**\n * Resolve the linked record id for `moduleKey` from a linkage/recordMeta map.\n * Values may be plain ids or { id } / { value } objects (recordMeta serializes\n * ObjectIDs as hex strings, but detail payloads sometimes carry objects).\n */\nexport function contextRecordId(moduleKey, source) {\n if (!source) return '';\n const idOf = (v) => (v && typeof v === 'object' ? (v.id ?? v.value ?? '') : (v ?? ''));\n for (const key of contextIdKeys(moduleKey)) {\n const id = String(idOf(source[key]) || '');\n if (id) return id;\n }\n return '';\n}\n\n/** Distinct normalized modules referenced by any field's prefillFromModule. */\nexport function contextPrefillModules(groups, normalizeModule) {\n const wanted = new Set();\n (groups ?? []).forEach((group) => (group.fields ?? []).forEach((field) => {\n if (field?.prefillFromModule) wanted.add(normalizeModule(field.prefillFromModule));\n }));\n return [...wanted];\n}\n\n/**\n * EDIT-side gate: which fields may a cross-module prefill write on an EXISTING\n * record?\n *\n * Only NON-PERSISTENT CARRIER fields (payloadMode \"skip\"). A carrier holds a\n * value that is never stored on the record, so there is nothing on the record\n * for it to contradict — it has to be re-derived on every edit or the rule it\n * feeds would be silently inert there. A field that IS stored keeps whatever\n * the record holds: re-pulling the source module's current value on edit would\n * silently overwrite what the user saved (e.g. a rate currency deliberately\n * changed after the job was raised). Config-driven, no field names.\n */\nexport function isNonPersistentCarrier(field) {\n return field?.payloadMode === 'skip';\n}\n\n/** getFormGroups responses come back in a few envelopes — normalize to an array. */\nexport function extractContextGroups(response) {\n if (Array.isArray(response)) return response;\n if (Array.isArray(response?.groups)) return response.groups;\n if (Array.isArray(response?.data)) return response.data;\n if (Array.isArray(response?.data?.groups)) return response.data.groups;\n return [];\n}\n\n/**\n * Flatten a form-groups response (scalar fields carry their stored `value`)\n * into { fieldKey: value }, indexed by both field key and payloadKey.\n */\nexport function flattenContextRecord(groups) {\n const record = {};\n (groups ?? []).forEach((group) => {\n if (group?.addRow) return;\n (group?.fields ?? []).forEach((field) => {\n if (!field?.field || field.value === undefined || field.value === null) return;\n record[field.field] = field.value;\n if (field.payloadKey && field.payloadKey !== field.field) record[field.payloadKey] = field.value;\n });\n });\n return record;\n}\n\n/** Read a field's value out of a context record: prefillFrom → field → payloadKey. */\nexport function pickContextValue(record, field) {\n if (!record || !field) return undefined;\n for (const path of [field.prefillFrom, field.field, field.payloadKey]) {\n if (!path) continue;\n let value = record[path];\n if (value === undefined || value === null) value = getDeep(record, path);\n if (value !== undefined && value !== null) return value;\n }\n return undefined;\n}\n","// prefillWhenRules — the pure decision engine behind `field.prefillWhen`\n// (\"Conditional Default\" in Form Groups), shared by AddFormV1 and EditFormV1 so\n// both forms can never drift apart on what a rule means.\n//\n// A prefillWhen rule says \"when the field named `field` holds `value`, put\n// `setValue` into THIS field\" — e.g. contractType=\"W2\" drops the default\n// employer record id into employerId. Two admin-config extensions live here:\n//\n// • rule.clearValue — a matching rule CLEARS this field instead of setting\n// it (e.g. contractType=\"C2C\" must not keep the W2 default employer\n// sitting there). Absent/false = today's set behaviour, so every rule ever\n// saved keeps working untouched.\n// • clearOnMismatch — field-level (`field.prefillWhenReset`): once NO rule\n// matches any more, revert this field to empty rather than stranding the\n// default a rule previously applied.\n//\n// WHY the lastApplied bookkeeping: the form store cannot tell \"this id is the\n// W2 default we injected\" from \"the user deliberately picked this employer\" —\n// both are just a string in the field. So the caller remembers the exact value\n// the last matching rule wrote, and a mismatch-clear only fires while the field\n// STILL holds precisely that value. The moment the user overrides it, the value\n// is theirs and we never touch it again. TRADEOFF, deliberately documented: a\n// user who manually re-picks the very same record the rule had set is\n// indistinguishable from the rule's own write, and that value WILL be cleared\n// on mismatch. Clearing an identical value is the benign side of the trade —\n// the alternative (never clearing) is the bug this exists to fix.\n//\n// WHY clears are suppressed on the FIRST evaluation: on the edit form the very\n// first tick sees the record's own stored contractType. If that value matches a\n// clearValue rule, an unguarded clear would wipe a stored employer the user\n// never touched, purely from opening the form. A clear must always be the\n// consequence of the user CHANGING the watched field, never of a page load.\n// (On the add form the field is empty at that point, so nothing is lost.)\n//\n// Nothing here knows any module, field or value name — all of it is admin data.\n\nconst asKey = (v) => String(v ?? '');\n\n/**\n * prefillWhenWatchFields — the distinct field keys a rule set references, in\n * rule order. Rules may point at DIFFERENT fields, so callers must watch each\n * one, not just the first rule's.\n *\n * @param {Array} rules field.prefillWhen\n * @returns {string[]}\n */\nexport function prefillWhenWatchFields(rules) {\n const seen = new Set();\n const out = [];\n (Array.isArray(rules) ? rules : []).forEach((r) => {\n const key = r?.field;\n if (!key || seen.has(key)) return;\n seen.add(key);\n out.push(key);\n });\n return out;\n}\n\n/**\n * prefillWhenWatchPaths — resolves each referenced field key to an absolute\n * antd name path using the SAME row-vs-top-level scoping `showIf` uses.\n *\n * Inside an addRow group, a rule referencing one of the group's OWN fields\n * resolves to [listName, rowIndex, key]; a rule referencing anything else (the\n * top-level contractType read from inside the repeatable employer rows) must\n * resolve to the top-level path. renderField's `prefixFor(key)` closure already\n * encodes exactly that decision — passing it in means a Conditional Default and\n * a Show Condition can never disagree about which field a key names. Getting\n * this wrong is what made \"W2/C2C not working\" the first time round.\n *\n * @param {Array} rules field.prefillWhen\n * @param {Array|string} name this field's own absolute name path\n * @param {Function} resolvePath (name, key, prefixFor) => absolute path\n * @param {Function} [prefixFor] renderField's showIf prefix resolver\n * @returns {{field: string, path: Array}[]}\n */\nexport function prefillWhenWatchPaths(rules, name, resolvePath, prefixFor) {\n return prefillWhenWatchFields(rules).map((field) => ({\n field,\n path: resolvePath(name, field, prefixFor),\n }));\n}\n\n/**\n * prefillWhenMatches — does THIS rule's condition hold right now?\n *\n * The single definition of \"a rule matches\", exported so the option side\n * (`prefillWhenExclusive`, see optionConstraints.js) can never drift from the\n * value side: an option is offered on exactly the ticks the rule would fire.\n *\n * @param {object} rule one field.prefillWhen entry\n * @param {object} watched { [referencedFieldKey]: liveValue }\n * @returns {boolean}\n */\nexport function prefillWhenMatches(rule, watched = {}) {\n if (!rule || !rule.field) return false;\n return asKey(watched[rule.field]) === asKey(rule.value);\n}\n\n/**\n * resolvePrefillWhen — decides what should happen to the target field on this\n * tick. Pure: it never touches the antd store, the caller applies the outcome.\n *\n * @param {object} args\n * @param {Array} args.rules field.prefillWhen\n * @param {object} args.watched { [referencedFieldKey]: liveValue }\n * @param {*} args.current the target field's live value\n * @param {*} args.lastApplied value the last matching SET rule wrote (undefined = none)\n * @param {boolean} args.clearOnMismatch field.prefillWhenReset (default on)\n * @param {boolean} args.initial true on the very first evaluation\n * @returns {{action: 'set'|'clear'|'none', value?: *}}\n */\nexport function resolvePrefillWhen({\n rules,\n watched = {},\n current,\n lastApplied,\n clearOnMismatch = true,\n initial = false,\n} = {}) {\n const list = Array.isArray(rules) ? rules : [];\n // First matching rule wins — same precedence the single-path watcher had.\n const match = list.find((r) => prefillWhenMatches(r, watched));\n\n if (match) {\n if (match.clearValue) {\n // A clear rule that fires on page load would delete stored data (see the\n // header comment), so the first tick only ever arms the watcher.\n return initial ? { action: 'none' } : { action: 'clear' };\n }\n return { action: 'set', value: match.setValue };\n }\n\n // No rule matches any more. Only revert a value WE put there.\n if (clearOnMismatch && lastApplied !== undefined && asKey(current) === asKey(lastApplied)) {\n return { action: 'clear' };\n }\n return { action: 'none' };\n}\n","// optionConstraints — config-driven narrowing of a select's options by the live\n// value of ANOTHER field, shared by AddFormV1 and EditFormV1.\n//\n// Config key (NEW — must exist on the Go FormField struct or the admin save API\n// drops it): `field.optionsFromField: \"<otherFieldKey>\"`.\n//\n// Use case it was built for: a candidate's rate UNIT may not differ from the\n// unit the JOB was raised in. The job's unit is already brought onto the form by\n// the existing `prefillFromModule` mechanism, so with this constraint the whole\n// restriction is config — no module, field or value name is written in code.\n//\n// The referenced field is resolved with the SAME row-vs-top-level scoping the\n// render side uses for `showIf` (row first, top level as fallback) — see\n// FieldControl, which watches both paths and prefers the row value when set.\n\nimport { prefillWhenMatches } from './prefillWhenRules';\n\nconst asKey = (v) => String(v ?? '').trim().toLowerCase();\n\n// Every comparable form of a constraint value: a plain scalar, an antd\n// labelInValue object ({value,label}) or a reference object ({id,name}), and\n// arrays of any of those (a multi-select constraint narrows to a SET).\nfunction constraintKeys(constraintValue) {\n const list = Array.isArray(constraintValue) ? constraintValue : [constraintValue];\n const keys = [];\n list.forEach((item) => {\n if (item === undefined || item === null || item === '') return;\n if (typeof item === 'object') {\n [item.value, item.id, item._id, item.label, item.name].forEach((v) => {\n if (v !== undefined && v !== null && v !== '') keys.push(asKey(v));\n });\n return;\n }\n keys.push(asKey(item));\n });\n return keys;\n}\n\n/**\n * narrowOptionsByConstraint — the options a select may offer given the current\n * value of the field named by `optionsFromField`.\n *\n * Matching is case-insensitive on the option's value OR its label, so a stored\n * \"hr\" narrows to the option labelled \"Hourly\" with value \"hr\" either way.\n *\n * FAILS OPEN in both directions:\n * • no constraint configured / constraint field still empty → all options\n * • the constraint matches NO option → all options\n * A narrowing that resolves to an empty dropdown would leave the user unable to\n * fill a (possibly mandatory) field because of a data mismatch they cannot see\n * or fix, which is strictly worse than showing the unrestricted list.\n */\nexport function narrowOptionsByConstraint(options = [], constraintValue) {\n const keys = constraintKeys(constraintValue);\n if (!keys.length || !Array.isArray(options) || options.length === 0) return options;\n const wanted = new Set(keys);\n const narrowed = options.filter((option) => {\n if (option === null || option === undefined) return false;\n if (typeof option !== 'object') return wanted.has(asKey(option));\n return wanted.has(asKey(option.value)) || wanted.has(asKey(option.label));\n });\n return narrowed.length > 0 ? narrowed : options;\n}\n\n// ---------------------------------------------------------------------------\n// prefillWhenExclusive — \"a value a Conditional Default would SET is exclusive\n// to that rule's condition\".\n//\n// Config key (NEW — must exist on the Go FormField struct or the admin save API\n// drops it): `field.prefillWhenExclusive: true`. Default OFF, so every field\n// configured before this existed offers exactly the options it does today.\n//\n// It reuses the EXISTING `field.prefillWhen` rules rather than introducing a\n// second list, so the value that must be hidden can never fall out of step with\n// the value that gets auto-filled — there is only one copy of it in config.\n//\n// prefillWhen: [{ field: 'contractType', value: 'W2', setValue: '<id>' }]\n// contractType = W2 → the rule matches → that option IS offered (and the\n// existing watcher auto-selects it, as today)\n// contractType = C2C → no rule matches → that option is REMOVED\n// contractType = empty → no rule matches → that option is REMOVED\n//\n// The use case: the value a W2 rule injects is the internal/own company; on any\n// other contract type the employer must be an external vendor, so offering the\n// internal one is wrong — not merely a bad default. Nothing here knows that:\n// the rule, the field and the value are all admin data.\n//\n// DELIBERATE DIVERGENCE from narrowOptionsByConstraint's fail-open rule: this\n// does NOT restore the full list when the exclusion empties it. Failing open on\n// a NARROWING is right (a data mismatch the user cannot see must not block a\n// mandatory field), but failing open on an EXCLUSION would re-offer precisely\n// the value the admin declared unofferable, i.e. reintroduce the bug. An empty\n// list is the honest answer — and these lookups carry a quick-create button, so\n// the user still has a way forward. Config-level fail-open is kept: flag off,\n// no rules, or a rule with no setValue all leave the options untouched.\n\n// Every option key one rule claims: the value it sets and, when the admin UI\n// stored one, its human label — so a rule whose setValue is a record id still\n// matches an option carrying that record's label, and vice versa. Mirrors\n// constraintKeys' value-OR-label matching above.\nfunction ruleOptionKeys(rule) {\n const keys = [];\n [rule?.setValue, rule?.setValueLabel].forEach((v) => {\n if (v === undefined || v === null || v === '') return;\n keys.push(asKey(v));\n });\n return keys;\n}\n\n/**\n * exclusivePrefillBlockedKeys — the option keys that must NOT be offered right\n * now, given the rule set and the live values of the fields it references.\n *\n * A key claimed by a rule that DOES match is always allowed, even if another\n * (non-matching) rule claims the same key — one live reason to offer a value is\n * enough.\n *\n * Rules that CLEAR (`rule.clearValue`) set no value at all, so they claim\n * nothing and never hide an option.\n *\n * @param {Array} rules field.prefillWhen\n * @param {object} watched { [referencedFieldKey]: liveValue }\n * @returns {string[]}\n */\nexport function exclusivePrefillBlockedKeys(rules, watched = {}) {\n const list = Array.isArray(rules) ? rules : [];\n const blocked = new Set();\n const allowed = new Set();\n list.forEach((rule) => {\n if (!rule || rule.clearValue) return;\n const keys = ruleOptionKeys(rule);\n if (!keys.length) return;\n const target = prefillWhenMatches(rule, watched) ? allowed : blocked;\n keys.forEach((key) => target.add(key));\n });\n allowed.forEach((key) => blocked.delete(key));\n return [...blocked];\n}\n\n/**\n * stripExclusivePrefillOptions — the options a control may offer once every\n * currently-inapplicable Conditional Default value has been removed.\n *\n * Case-insensitive on the option's value OR its label, exactly like\n * narrowOptionsByConstraint, so it works whether the option list is a lookup\n * (value = record id) or a static list (value = label).\n *\n * Composes with narrowOptionsByConstraint — pass its output in.\n *\n * @param {Array} options the already-narrowed option list\n * @param {Array} rules field.prefillWhen (only when the flag is on)\n * @param {object} watched { [referencedFieldKey]: liveValue }\n */\nexport function stripExclusivePrefillOptions(options = [], rules, watched) {\n const blocked = exclusivePrefillBlockedKeys(rules, watched);\n if (!blocked.length || !Array.isArray(options) || options.length === 0) return options;\n const deny = new Set(blocked);\n return options.filter((option) => {\n if (option === null || option === undefined) return true;\n if (typeof option !== 'object') return !deny.has(asKey(option));\n return !(deny.has(asKey(option.value)) || deny.has(asKey(option.label)));\n });\n}\n\nexport default {\n narrowOptionsByConstraint,\n exclusivePrefillBlockedKeys,\n stripExclusivePrefillOptions,\n};\n","// optionRowFilters — config-driven, CLIENT-SIDE narrowing of the RAW rows a\n// lookup dropdown returned, shared by AddFormV1 and EditFormV1.\n//\n// It runs on the raw `/admin/lookup-dropdown-values` rows ({label, value,\n// extraData}) BEFORE they are normalized into antd options, because every\n// decision here is made from `extraData` — the values the backend was asked to\n// carry alongside each row via `extraFields`.\n//\n// ── Why client-side at all ───────────────────────────────────────────────────\n// The proper mechanism is `field.lookupFilters` (multi-condition, evaluated\n// server-side). It IS configured on the Jobs \"Assign To\" field, but the\n// currently DEPLOYED backend predates that key and drops it while reading the\n// config, so the dropdown falls back to \"everybody\". `extraFields` however IS\n// supported by that build (the employer→recruiter `optionsRequireExtraField`\n// feature uses it live), so the same restriction can be expressed as data on\n// each row and applied in the browser until the backend ships.\n//\n// BOTH filters will be active once the backend ships. They are deliberately\n// written to express the SAME rule, so the result is identical rather than\n// contradictory:\n//\n// server lookupFilters client keys (this module)\n// ------------------------------------------ -----------------------------\n// roleId in (roles where roleName in [ROLES]) optionsRequireExtraField:\n// \"roleId\"\n// optionsRequireExtraValue:\n// \"<those roleIds>\"\n// legacyUserId in (users whose reportingId optionsHierarchyParentField:\n// chains up to currentUserId, recursive) \"reportingId\"\n// optionsHierarchySelfFrom:\n// \"currentUserId\"\n//\n// Both narrow to the same set, and an AND of a set with itself is that set —\n// so the dropdown shows the same rows whether one or both are in force. The\n// seeder (cmd/wire-jobs-assignto-lookup) writes both sides from ONE role list\n// so they can never drift apart in config either.\n//\n// ── Config keys (all must exist on the Go FormField struct or the admin save\n// API silently drops them) ─────────────────────────────────────────────────\n// optionsRequireExtraField — extraData key to test (e.g. \"roleId\")\n// optionsRequireExtraValue — NEW. Allowed value(s), single or\n// comma-separated. ABSENT ⇒ today's exact\n// `=== true` semantics, unchanged.\n// optionsHierarchyParentField — NEW. extraData key holding each row's PARENT\n// id in the same id space as the row's value\n// (e.g. \"reportingId\").\n// optionsHierarchySelfFrom — NEW. Identity token naming whose downline to\n// keep (e.g. \"currentUserId\").\n//\n// Nothing here knows about jobs, assignedTo, recruiters, roleId or reportingId:\n// every name above arrives as config.\n\n// Depth cap for the parent walk. Well beyond any real org chart, and the\n// visited-set below already stops cycles — this is a second, unconditional\n// backstop so a malformed chain can never spin.\nconst MAX_HIERARCHY_DEPTH = 64;\n\n// Identity tokens resolvable in the BROWSER. The server-side lookupFilters\n// resolve `currentUserId` from the authenticated request; client-side the same\n// value comes from localStorage `userId` — the identical source\n// ZINNEXT-V2's jobConfig.js / useVisibleTabs use for \"is this me?\".\n// An unknown token is NOT silently treated as \"no filter applied without\n// saying so\": resolveIdentity returns '' and the caller warns and skips.\nexport function resolveIdentity(token) {\n const key = String(token ?? '').trim();\n if (key !== 'currentUserId') return '';\n try {\n if (typeof localStorage === 'undefined') return '';\n return String(localStorage.getItem('userId') ?? '').trim();\n } catch {\n return '';\n }\n}\n\nconst asKey = (v) => String(v ?? '').trim().toLowerCase();\nconst filled = (v) => v !== undefined && v !== null && String(v).trim() !== '';\n\n/** The allowed-value set from a comma-separated (or array) config value. */\nfunction allowedValueKeys(raw) {\n const list = Array.isArray(raw) ? raw : String(raw ?? '').split(',');\n return new Set(list.map(asKey).filter((v) => v !== ''));\n}\n\n/**\n * extraDataArrived — did the backend actually send this key?\n *\n * TRUE when at least one row carries a non-empty value at `key`. This is the\n * fail-safe pivot (see filterLookupOptionRows): a deployed build that ignores\n * `extraFields` returns rows with no extraData at all, and filtering on data\n * that never arrived would empty the dropdown for a reason no admin can see.\n * \"Present but nothing matches\" is a legitimate empty and IS honoured.\n */\nexport function extraDataArrived(rows, key) {\n if (!key || !Array.isArray(rows) || rows.length === 0) return false;\n return rows.some((row) => filled(row?.extraData?.[key]));\n}\n\n/**\n * matchesRequiredExtra — the value test for ONE row.\n *\n * With no `optionsRequireExtraValue` this is byte-for-byte the old behaviour:\n * strictly `=== true` (used live by \"only list a VERIFIED recruiter\").\n * With one, it is a LOOSE string comparison so a stored numeric 10 matches a\n * configured \"10\" — the lookup API may hand back either, depending on whether\n * the value survived an aggregation projection as a number or a string.\n */\nexport function matchesRequiredExtra(row, key, allowedRaw) {\n const actual = row?.extraData?.[key];\n if (!filled(allowedRaw)) return actual === true;\n const allowed = allowedValueKeys(allowedRaw);\n if (allowed.size === 0) return actual === true;\n return allowed.has(asKey(actual));\n}\n\n/**\n * buildParentMap — { rowValueKey: parentValueKey } from the returned rows.\n *\n * The map is built from the ROWS THEMSELVES, so a transitive chain can only be\n * proven through people who are in the returned page of results. That is the\n * intended semantics here: the option list is what we are filtering.\n */\nexport function buildParentMap(rows, parentField) {\n const map = new Map();\n (Array.isArray(rows) ? rows : []).forEach((row) => {\n const self = asKey(row?.value);\n if (!self) return;\n map.set(self, asKey(row?.extraData?.[parentField]));\n });\n return map;\n}\n\n/**\n * isInDownline — does walking parent links from `startValue` reach `selfId`?\n *\n * CYCLE GUARD: two independent stops.\n * 1. `seen` — a node revisited means the chain looped (A→B→A, or a\n * self-referencing reportingId pointing at its own row); return false.\n * 2. MAX_HIERARCHY_DEPTH — an unconditional iteration cap, so even a map\n * mutated mid-walk or an unforeseen shape cannot spin the browser.\n *\n * SELF-INCLUSION — DECIDED: the signed-in user does NOT appear in their own\n * list. The walk starts at the row's PARENT, so a row whose value equals\n * selfId only survives if it also reports (transitively) to itself, which the\n * cycle guard rejects. Rationale: the configured rule is \"users who report to\n * me\", and I do not report to myself; a manager assigning work picks from\n * their team. It also keeps this filter identical to the server-side\n * `lookupFilters` condition (`reportingId` chains up from currentUserId),\n * which likewise never yields the signed-in user's own row — the two filters\n * must agree exactly or the union/intersection of the two would differ by one\n * row depending on which backend is deployed.\n */\nexport function isInDownline(startValue, parentMap, selfId) {\n const self = asKey(selfId);\n if (!self) return false;\n const seen = new Set();\n let current = asKey(startValue);\n if (!current) return false;\n seen.add(current);\n for (let depth = 0; depth < MAX_HIERARCHY_DEPTH; depth += 1) {\n const parent = parentMap.get(current);\n if (!parent) return false;\n if (parent === self) return true;\n if (seen.has(parent)) return false; // cycle\n seen.add(parent);\n current = parent;\n }\n return false;\n}\n\n/**\n * lookupExtraFieldKeys — every extraData key the request must ask for.\n *\n * The caller passes this to the `extraFields` query param instead of\n * `field.extraFields` alone, so configuring a filter key is enough: the admin\n * cannot forget to also list it under extraFields and get a silently empty (or\n * silently unfiltered) dropdown. Explicit `field.extraFields` entries (used by\n * autofillFrom) are preserved and come first; order is stable and de-duped.\n */\nexport function lookupExtraFieldKeys(field = {}) {\n const keys = [];\n const push = (k) => {\n const key = String(k ?? '').trim();\n if (key && !keys.includes(key)) keys.push(key);\n };\n (Array.isArray(field.extraFields) ? field.extraFields : []).forEach(push);\n push(field.optionsRequireExtraField);\n push(field.optionsHierarchyParentField);\n return keys;\n}\n\n/** Does this field configure any row filter at all? */\nexport function hasOptionRowFilters(field = {}) {\n return Boolean(field.optionsRequireExtraField || field.optionsHierarchyParentField);\n}\n\n/**\n * filterLookupOptionRows — the whole client-side restriction, in order:\n * A. value match on one extraData key\n * B. keep only the signed-in user's downline\n * Both are optional and independent; configuring neither returns `rows` as-is.\n *\n * FAIL-SAFE (deliberate asymmetry, see the requirement it was built for):\n * • data NEVER ARRIVED (no row carries the configured key, or the identity\n * token cannot be resolved) → SKIP that filter and log ONE warning naming\n * the field. An admin must be able to tell \"nobody reports to me\"\n * (legitimate empty) from \"the deployed build ignored extraFields\"\n * (broken), and an unexplained empty dropdown hides a mandatory field\n * behind a data problem the user cannot see or fix.\n * • data ARRIVED and simply nothing matches → return the empty list\n * faithfully. Failing open there would re-offer exactly the rows the admin\n * declared off-limits, i.e. reintroduce the bug.\n *\n * @param {Array} rows raw lookup rows ({label, value, extraData})\n * @param {object} field the field config\n * @param {object} opts { source: 'AddFormV1' | 'EditFormV1' } for the warning\n * @returns {Array} the rows to keep\n */\nexport function filterLookupOptionRows(rows, field = {}, opts = {}) {\n if (!Array.isArray(rows) || rows.length === 0) return rows;\n const where = opts.source ? `[${opts.source}]` : '[optionRowFilters]';\n const name = field.field ?? field.label ?? '(unnamed field)';\n const warn = typeof opts.warn === 'function'\n ? opts.warn\n : (msg) => { if (typeof console !== 'undefined') console.warn(msg); };\n let out = rows;\n\n // A — value match.\n const valueKey = String(field.optionsRequireExtraField ?? '').trim();\n if (valueKey) {\n if (!extraDataArrived(out, valueKey)) {\n warn(`${where} \"${name}\": option filter SKIPPED — no option carried extraData[\"${valueKey}\"], `\n + 'so the value filter could not be applied (the lookup API returned no such extra field). '\n + 'Showing the unfiltered list; this is NOT \"nothing matched\".');\n } else {\n out = out.filter((row) => matchesRequiredExtra(row, valueKey, field.optionsRequireExtraValue));\n }\n }\n\n // B — hierarchy (downline of the signed-in user).\n const parentField = String(field.optionsHierarchyParentField ?? '').trim();\n if (parentField) {\n const token = String(field.optionsHierarchySelfFrom ?? '').trim() || 'currentUserId';\n const selfId = resolveIdentity(token);\n if (!selfId) {\n warn(`${where} \"${name}\": hierarchy filter SKIPPED — identity \"${token}\" could not be resolved `\n + '(no signed-in user id available). Showing the list unrestricted by reporting line.');\n } else if (!extraDataArrived(rows, parentField)) {\n warn(`${where} \"${name}\": hierarchy filter SKIPPED — no option carried extraData[\"${parentField}\"], `\n + 'so the reporting chain could not be walked (the lookup API returned no such extra field). '\n + 'Showing the list unrestricted by reporting line; this is NOT \"nobody reports to you\".');\n } else {\n // The chain is walked over ALL returned rows, not the value-filtered\n // ones: an intermediate manager may hold a role the value filter\n // excludes (a recruiter reporting to a LEAD RECRUITER reporting to me),\n // and dropping that link first would sever a chain that genuinely\n // reaches me. This also matches the server-side condition, which\n // evaluates the two conditions independently over the whole collection.\n const parentMap = buildParentMap(rows, parentField);\n out = out.filter((row) => isInDownline(row?.value, parentMap, selfId));\n }\n }\n\n return out;\n}\n\nexport default {\n filterLookupOptionRows,\n lookupExtraFieldKeys,\n hasOptionRowFilters,\n matchesRequiredExtra,\n extraDataArrived,\n buildParentMap,\n isInDownline,\n resolveIdentity,\n};\n","// fieldTooltip — the single resolver for a field's admin-set help tooltip,\n// shared by AddFormV1 and EditFormV1.\n//\n// Two config shapes exist and both are honoured:\n// • `field.tooltip` (NEW key — one string, always shown)\n// • `field.infoEnabled` + `field.infoText` (the original toggle + text pair)\n//\n// Before this, tooltip text only ever reached the screen through FieldLabel, so\n// a field rendered WITHOUT a label (an inline/combined box, or a group that\n// prints one shared header row) could never carry one. Both forms now fall back\n// to wrapping the control itself for those, so ANY control type can get an\n// admin-set tooltip — which is the whole point: the text is config, the\n// capability is code.\n\nconst blank = (v) => v === undefined || v === null || String(v).trim() === '';\nconst truthy = (v) => v === true || v === 1 || v === '1' || v === 'true';\n\n/** @returns {string} the tooltip text for a field, or '' when it has none. */\nexport function fieldTooltipText(field) {\n if (!blank(field?.tooltip)) return String(field.tooltip);\n if (truthy(field?.infoEnabled) && !blank(field?.infoText)) return String(field.infoText);\n return '';\n}\n\nexport default { fieldTooltipText };\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// checkPermission() is the CURRENT gate — it resolves against the `can`\n// function from src/hooks/usePermissions.js (backed by GET /me/permissions,\n// see src/contexts/PermissionContext.jsx), which every consumer must call\n// usePermissions() to obtain and pass in. roleAllowsAction() below is the\n// OLD, localStorage.menuPermission-based gate — kept only for isActionValueAllowed's\n// row-level (record.actionPermission.<key>) use, which is a separate,\n// per-record mechanism unrelated to role permissions.\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// Deprecated — localStorage.menuPermission-based gate, superseded by\n// checkPermission()/usePermissions(). No remaining call sites; kept only so a\n// stray import doesn't break until it's confirmed unused everywhere.\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 moduleKey = configuredModule || String(moduleName || '').replace(/s$/i, '');\n const modulePermissions = permissions[moduleKey]\n ?? permissions[String(moduleName || '')]\n ?? permissions[String(moduleName || '').replace(/s$/i, '')]\n ?? {};\n return isActionValueAllowed(modulePermissions[configuredKey]);\n } catch {\n return true;\n }\n}\n","// Shared \"After Submit\" navigation resolver used by AddFormV1/EditFormV1.\n// Admin-configured per FormGroup (group.afterSubmit — see FormGroupsSection.jsx),\n// generic across every module/project: no module name is ever referenced here.\n\n// Picks the configured behavior from the group with the lowest `order` that\n// declares one. Returns null when no group configures it, so callers fall\n// back to their existing (pre-feature) navigation — unchanged behavior.\nexport function resolveAfterSubmit(groups) {\n const withConfig = (groups ?? [])\n .filter((g) => g?.afterSubmit?.mode)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n return withConfig[0]?.afterSubmit ?? null;\n}\n\n// Resolves a configured route template (e.g. \"/trainers/:id\") against the\n// created/updated record id and the submitted field values, and rejects\n// anything that isn't a safe in-app path (blocks open-redirect / javascript:\n// / data: payloads an admin could otherwise paste into the target field).\nexport function safeNavTarget(template, recordId, values) {\n const raw = String(template ?? '').trim();\n if (!raw || !raw.startsWith('/') || raw.startsWith('//')) return null;\n if (/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null; // any \"scheme:\" prefix, e.g. javascript:, data:\n return raw.replace(/:([A-Za-z_][\\w]*)/g, (_, key) => {\n const value = key === 'id' ? recordId : values?.[key];\n return encodeURIComponent(value ?? '');\n });\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// backNav — \"take me back to where I actually was\".\n//\n// THE BUG THIS FIXES\n// Every edit page in the consuming app passes a `cancelPath` naming the module\n// LIST (\"/candidates\", \"/jobs\", \"/employers\", …). EditFormV1 preferred that\n// path over history, so opening Edit *from a detail view* and cancelling threw\n// the user out to the list — losing the record they were looking at, its tab,\n// its scroll position and any filter behind it.\n//\n// THE RULE\n// History wins. `cancelPath` is demoted to what it is genuinely good for: a\n// fallback for a COLD entry (a deep-linked /candidate/edit/:id opened in a\n// fresh tab), where navigate(-1) would walk out of the application entirely.\n//\n// A caller that really must force a destination can still say so explicitly\n// with `cancelPathPriority` — but that is now an opt-in exception rather than\n// the accidental default.\n//\n// Nothing here knows a module, a route or a project: it only answers\n// \"is there in-app history behind me?\".\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * hasInAppHistory — is there a previous entry belonging to THIS app?\n *\n * react-router (v6, history v5) stamps a monotonically increasing `idx` on\n * window.history.state for every entry it pushes. `idx > 0` therefore means\n * \"the app itself navigated here\", i.e. going back lands on one of our own\n * screens rather than on whatever the tab showed before.\n *\n * `window.history.length` is deliberately NOT used: it counts entries from\n * before the app was loaded, so a fresh tab opened from a bookmark can report\n * a length of 2+ and send the user out to an unrelated site.\n */\nexport function hasInAppHistory(win = typeof window !== 'undefined' ? window : undefined) {\n const idx = win?.history?.state?.idx;\n return typeof idx === 'number' && idx > 0;\n}\n\n/**\n * resolveCancelTarget — what a Cancel/Back control should do.\n *\n * Returns either { back: true } (call navigate(-1)) or { path } (call\n * navigate(path)), so the caller stays in charge of the actual navigation and\n * this module stays router-agnostic and testable.\n *\n * @param {object} opts\n * @param {string} [opts.cancelPath] fallback route for a cold entry\n * @param {boolean}[opts.cancelPathPriority] force cancelPath over history\n * @param {string} [opts.fallbackPath] last resort when there is neither\n * @param {Window} [opts.win] injectable for tests\n */\nexport function resolveCancelTarget({\n cancelPath,\n cancelPathPriority = false,\n fallbackPath = '/',\n win = typeof window !== 'undefined' ? window : undefined,\n} = {}) {\n if (cancelPathPriority && cancelPath) return { path: cancelPath };\n if (hasInAppHistory(win)) return { back: true };\n if (cancelPath) return { path: cancelPath };\n return { path: fallbackPath };\n}\n\n/**\n * goBackOrTo — the one-liner most call sites want. Applies\n * resolveCancelTarget with the caller's `navigate`.\n */\nexport function goBackOrTo(navigate, opts = {}) {\n const target = resolveCancelTarget(opts);\n if (target.back) navigate(-1);\n else navigate(target.path);\n return target;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// educationRules — region profiles, recency ordering and level hierarchy for\n// repeatable groups.\n//\n// THE REQUIREMENTS THIS SERVES\n//\n// 1. \"In the admin create a region US / UK / IND / common at the top. From\n// this it has to trigger the components … when I click US and UK and apply\n// the changes it has to render all.\"\n//\n// 2. \"For US and UK it can proceed with currently pursuing\" (a candidate may\n// apply mid-degree) \"but in case of India only after pursuing will a\n// company let you apply.\"\n//\n// 3. \"If I click currently pursuing in the middle of the rows it has to show\n// a popup — 'as you are mentioning currently pursuing, since this seems to\n// be a recent education can I make this the 1st?' If the user clicks yes\n// then it has to be at the top, and it has to be in the order using the\n// start date and end date, recent first.\"\n//\n// 4. \"In the education, if I type Masters in the latest and go to the next\n// group it has to show the error 'you added only the PG, the UG degree is\n// mandatory'.\"\n//\n// NOTHING HERE NAMES A REGION, A DEGREE OR A MODULE. A region is a key into a\n// config map; a degree's rank comes from master data. That is what lets the\n// same code serve a market nobody has thought of yet.\n// ─────────────────────────────────────────────────────────────────────────\nimport dayjs from 'dayjs';\n\nconst text = (v) => (v === null || v === undefined ? '' : String(v).trim());\n\n// ── 1. region profiles ───────────────────────────────────────────────────\n\n/**\n * resolveRegionRules — merge a group's `regionRules[<active region>]` over its\n * base config.\n *\n * A group declares its defaults normally and overrides only what differs per\n * region, so switching region re-renders every affected component with no\n * branch in any component and no second copy of the config.\n *\n * An unknown/absent region falls back to `default`, then to the base group —\n * so a tenant that never sets a region behaves exactly as before.\n */\nexport function resolveRegionRules(group, region) {\n const rules = group?.regionRules;\n if (!rules || typeof rules !== 'object') return group ?? {};\n const key = text(region);\n const applied = rules[key] ?? rules[key.toUpperCase()] ?? rules.default;\n if (!applied || typeof applied !== 'object') return group ?? {};\n return { ...group, ...applied };\n}\n\n// ── 2. \"currently pursuing\" allowed by region ────────────────────────────\n\n/**\n * inProgressAllowed — may a row be marked as still in progress?\n *\n * Defaults to TRUE: blocking is the exceptional rule (India), and a group that\n * never configures this must not suddenly start rejecting rows.\n */\nexport function inProgressAllowed(group) {\n return group?.allowInProgress !== false;\n}\n\n/**\n * inProgressError — the configured message for a disallowed in-progress row,\n * or '' when it is allowed.\n */\nexport function inProgressError(group) {\n if (inProgressAllowed(group)) return '';\n if (inProgressWarns(group)) {\n return group?.inProgressWarningMessage\n || group?.inProgressMessage\n || 'In this region an unfinished qualification is not usually accepted when a '\n + 'candidate is put forward for a role. You can still record it here \\u2014 just '\n + 'be aware it will have to be completed before they can be submitted.';\n }\n return group?.inProgressMessage\n || 'This one needs to be finished before the application can go ahead. '\n + 'Please untick \\u201cstill ongoing\\u201d, or remove the row.';\n}\n\n/**\n * inProgressSeverity \\u2014 HOW HARD a region's restriction bites.\n *\n * The same fact does not carry the same weight everywhere it appears. Recording\n * a candidate is record-keeping: someone mid-degree in India is a real person\n * whose details are worth having on file, and refusing to record them throws\n * away information the business wanted. Putting that candidate FORWARD is a\n * commitment to a client who will not accept an unfinished qualification \\u2014 and\n * there the same fact has to stop the submit.\n *\n * 'allow' no restriction (the default everywhere)\n * 'warn' say so, and let it through\n * 'block' refuse \\u2014 the tick is reversed and the form will not submit\n *\n * Defaults to 'block' when allowInProgress is false, so a group configured\n * before this key existed behaves exactly as it did.\n */\nexport function inProgressSeverity(group) {\n if (inProgressAllowed(group)) return 'allow';\n const configured = text(group?.inProgressSeverity).toLowerCase();\n return configured === 'warn' ? 'warn' : 'block';\n}\n\n/** Does this group refuse an in-progress row outright? */\nexport function inProgressBlocks(group) {\n return inProgressSeverity(group) === 'block';\n}\n\n/** Does this group merely caution about one? */\nexport function inProgressWarns(group) {\n return inProgressSeverity(group) === 'warn';\n}\n\n/**\n * buildInProgressValidator \\u2014 the SUBMIT-time half of a blocking rule.\n *\n * Intercepting the tick-box is not enough on its own. A submission's rows are\n * prefilled from the candidate, and the candidate is allowed to carry an\n * in-progress entry \\u2014 so a blocked row arrives without anybody having clicked\n * anything. Without this the form would accept it, and the rule would hold only\n * against users who happened to tick the box by hand.\n *\n * Returns null when the group does not block, so no rule is attached at all.\n */\nexport function buildInProgressValidator(group) {\n if (!inProgressBlocks(group)) return null;\n const message = inProgressError(group);\n return {\n validator: (_, value) => (value === true\n ? Promise.reject(new Error(message))\n : Promise.resolve()),\n };\n}\n\n// ── 3. recency ordering ──────────────────────────────────────────────────\n\n/**\n * rowSortKey — the instant a row is ordered by. End date first (a finished\n * qualification is placed by when it finished), falling back to start date.\n * Returns null when the row carries no usable date, so undated rows can be\n * kept where they are rather than being shuffled to an arbitrary end.\n */\nexport function rowSortKey(row, cfg = {}) {\n const endField = cfg.tieBreak ?? 'endDate';\n const startField = cfg.field ?? 'startDate';\n for (const key of [endField, startField]) {\n const value = row?.[key];\n if (value === undefined || value === null || value === '') continue;\n const d = dayjs(value);\n if (d.isValid()) return d.valueOf();\n }\n return null;\n}\n\n/**\n * isInProgressRow — is this row flagged as ongoing?\n */\nexport function isInProgressRow(row, cfg = {}) {\n const field = cfg.inProgressField ?? 'currentStudyingHere';\n return Boolean(row?.[field]);\n}\n\n/**\n * orderedRowIndexes — the indexes the rows SHOULD appear in.\n *\n * Most recent first. An in-progress row sorts above every completed one when\n * `inProgressFirst` is set — it is by definition the latest, and it usually has\n * no end date to sort on. Undated rows keep their relative position at the end\n * rather than being flung to the top by a null comparing as zero.\n */\nexport function orderedRowIndexes(rows = [], cfg = {}) {\n const desc = (cfg.direction ?? 'desc') === 'desc';\n const decorated = rows.map((row, index) => ({\n index,\n key: rowSortKey(row, cfg),\n ongoing: cfg.inProgressFirst !== false && isInProgressRow(row, cfg),\n }));\n\n return decorated\n .slice()\n .sort((a, b) => {\n if (a.ongoing !== b.ongoing) return a.ongoing ? -1 : 1;\n // Undated rows sink, and hold their original order among themselves.\n if (a.key === null && b.key === null) return a.index - b.index;\n if (a.key === null) return 1;\n if (b.key === null) return -1;\n if (a.key === b.key) return a.index - b.index;\n return desc ? b.key - a.key : a.key - b.key;\n })\n .map((d) => d.index);\n}\n\n/**\n * misplacedRow — where does the row the user just edited actually belong?\n *\n * Returns null when it is already in the right place, else\n * { from, to, reason } where reason is 'inProgress' or 'outOfOrder' — which is\n * what lets the caller pick between the requirement's two different prompts.\n */\nexport function misplacedRow(rows, changedIndex, cfg = {}) {\n if (!Array.isArray(rows) || rows.length < 2) return null;\n if (changedIndex == null || changedIndex < 0 || changedIndex >= rows.length) return null;\n\n const order = orderedRowIndexes(rows, cfg);\n const to = order.indexOf(changedIndex);\n if (to === -1 || to === changedIndex) return null;\n\n return {\n from: changedIndex,\n to,\n reason: isInProgressRow(rows[changedIndex], cfg) ? 'inProgress' : 'outOfOrder',\n };\n}\n\n// Default prompt wording. Both sentences are the ones the requirement asks for,\n// and both are overridable per group via `orderBy.messages`.\nexport const DEFAULT_ORDER_MESSAGES = Object.freeze({\n inProgress: 'You\\u2019ve marked this one as still ongoing, so it\\u2019s the most recent. '\n + 'Shall we move it to the top? This list reads best newest first.',\n outOfOrder: 'These dates make this the most recent one. '\n + 'Shall we move it to the top? This list reads best newest first.',\n ok: 'Yes, move it up',\n keep: 'No, leave it here',\n});\n\n/**\n * orderPromptMessage — the sentence for a given misplacement.\n */\nexport function orderPromptMessage(reason, cfg = {}) {\n const messages = { ...DEFAULT_ORDER_MESSAGES, ...(cfg.messages ?? {}) };\n return messages[reason] ?? messages.outOfOrder;\n}\n\n/**\n * moveRow — pure reorder, so the caller can preview or test it without antd.\n */\nexport function moveRow(rows, from, to) {\n const next = [...(rows ?? [])];\n if (from < 0 || from >= next.length || to < 0 || to >= next.length) return next;\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n return next;\n}\n\n// ── 4. degree hierarchy ──────────────────────────────────────────────────\n\n/**\n * levelOf — a row's rank, resolved from master data rather than from the\n * degree's NAME. `levels` maps a stored value to a numeric rank\n * ({ \"UG\": 4, \"PG\": 5, … }), which is what keeps \"Masters\", \"M.Tech\" and\n * \"PG\" configurable as the same rank without any of them appearing here.\n */\nexport function levelOf(row, cfg = {}) {\n const field = cfg.levelField ?? 'qualification';\n const raw = text(row?.[field]);\n if (!raw) return null;\n const levels = cfg.levels ?? {};\n const direct = levels[raw] ?? levels[raw.toLowerCase()] ?? levels[raw.toUpperCase()];\n if (Number.isFinite(Number(direct))) return Number(direct);\n return patternLevel(raw, cfg);\n}\n\n/**\n * patternLevel — match a FREE-TEXT qualification to a level by keyword.\n *\n * The exact `levels` map above works when the stored value is a short code\n * (\"UG\", \"PG\"). It is useless against the real data: the qualification master\n * holds ~111 spelled-out names — \"MASTER OF SCIENCE\", \"BACHELOR OF ENGINEERING\",\n * \"M.SC.\", \"MBBS\", \"Diploma\" — so a record saying \"Master of Science in Computer\n * Science\" matched no key, resolved to no level, and the whole hierarchy rule\n * quietly evaluated to nothing.\n *\n * Enumerating all 111 in the map is not a fix: it breaks the day somebody adds\n * a degree, and it breaks silently, in the same way.\n *\n * So `levelPatterns` is admin config — an ORDERED list of { match, level },\n * first match wins, tested case-insensitively. Order is the admin's lever: put\n * the more specific rungs first, because \"Bachelor of Engineering and Master of\n * Science\" legitimately matches two.\n *\n * A malformed pattern is skipped, not thrown: one bad regex in config must not\n * take the submit button down with it.\n */\nfunction patternLevel(raw, cfg) {\n const patterns = Array.isArray(cfg?.levelPatterns) ? cfg.levelPatterns : [];\n const subject = raw.toLowerCase();\n for (const entry of patterns) {\n const pattern = text(entry?.match);\n if (!pattern) continue;\n let matcher;\n try {\n matcher = new RegExp(pattern, 'i');\n } catch {\n // A malformed pattern is SKIPPED, never thrown: one bad regex in config\n // must not take the submit button down with it.\n continue;\n }\n if (matcher.test(subject) && Number.isFinite(Number(entry.level))) {\n return Number(entry.level);\n }\n }\n return null;\n}\n\n/**\n * levelLabel — how a level is NAMED to the user.\n *\n * The rule's keys are codes (\"UG\"), and a message reading \"there is no UG\n * recorded\" tells a recruiter nothing. `levelLabels` maps each code to the words\n * a person uses. Falls back to the code, so a level nobody has labelled still\n * names itself rather than disappearing.\n */\nexport function levelLabel(code, cfg = {}) {\n const labels = cfg?.levelLabels ?? {};\n return text(labels[code]) || text(code);\n}\n\n/**\n * missingRequiredLevels — which mandatory levels BELOW the highest entered one\n * are absent.\n *\n * \"You added only the PG; the UG degree is mandatory\" is exactly this: the\n * highest level present is PG (5), UG (4) is configured as required, and no row\n * carries it.\n *\n * Returns [] when the rule is not configured, when nothing has been entered\n * yet, or when `requireBelow` is off (US/UK, where applying mid-degree is\n * normal) — so it never fires on a form the rule was not meant for.\n */\nexport function missingRequiredLevels(rows = [], cfg = {}) {\n if (!cfg || cfg.requireBelow === false) return [];\n const levels = cfg.levels ?? {};\n const required = cfg.requiredLevels ?? [];\n if (!required.length) return [];\n\n const present = rows.map((r) => levelOf(r, cfg)).filter((n) => n !== null);\n if (!present.length) return [];\n const highest = Math.max(...present);\n\n const missing = [];\n required.forEach((entry) => {\n // An entry may be a label (\"UG\") or {label, level}.\n const label = typeof entry === 'object' ? entry.label : entry;\n const rank = typeof entry === 'object' && Number.isFinite(Number(entry.level))\n ? Number(entry.level)\n : Number(levels[label]);\n if (!Number.isFinite(rank)) return;\n // Only levels BELOW what the candidate claims are required: someone whose\n // highest entry is 12th grade must not be asked for a degree.\n if (rank >= highest) return;\n if (!present.includes(rank)) missing.push(label);\n });\n return missing;\n}\n\n/**\n * levelRuleError — the finished message, or '' when the rule is satisfied.\n */\nexport function levelRuleError(rows, cfg = {}) {\n const missing = missingRequiredLevels(rows, cfg);\n if (!missing.length) return '';\n // Named the way a person would say it, not by the config's code.\n const list = missing.map((code) => levelLabel(code, cfg)).join(', ');\n const template = cfg.message\n || 'You\\u2019ve added a higher qualification but not the {missing} below it. '\n + 'Please add that too.';\n return template.replace('{missing}', list);\n}\n\n/**\n * promptRowMove — ask whether to move a row that is now out of order, and do it.\n *\n * Called after the user changes something that affects a row's position: the\n * dates, or the \"still ongoing\" tick-box. If the row belongs somewhere else,\n * they are asked; nothing is ever reordered behind their back, because a list\n * that rearranges itself while you are typing in it is disorienting.\n *\n * Returns true when a move happened, so the caller can skip any follow-up work.\n *\n * @param {object} opts\n * @param {object} opts.group the group config (already region-resolved)\n * @param {Array} opts.rows the group's current rows\n * @param {number} opts.rowIndex the row the user just edited\n * @param {function} opts.move Form.List's move(from, to)\n * @param {function} opts.confirm ({title, body, okText, cancelText}) => Promise<boolean>\n */\nexport async function promptRowMove({ group, rows, rowIndex, move, confirm }) {\n const cfg = group?.orderBy;\n if (!cfg || cfg.confirmMove === false || typeof move !== 'function') return false;\n\n const misplaced = misplacedRow(rows, rowIndex, cfg);\n if (!misplaced) return false;\n\n const messages = { ...DEFAULT_ORDER_MESSAGES, ...(cfg.messages ?? {}) };\n const agreed = await confirm({\n reason: misplaced.reason,\n title: misplaced.reason === 'inProgress'\n ? 'This looks like the most recent one'\n : 'These dates make this the most recent one',\n body: orderPromptMessage(misplaced.reason, cfg),\n okText: messages.ok,\n cancelText: messages.keep,\n from: misplaced.from,\n to: misplaced.to,\n });\n if (!agreed) return false;\n\n move(misplaced.from, misplaced.to);\n return true;\n}\n\n// ── 5. the cross-module gate ─────────────────────────────────────────────\n\n/**\n * groupInProgressConfig — a group's ongoing-flag settings, region-resolved.\n *\n * Returns null for a group that has no ongoing flag at all, so a caller can\n * simply skip it. `rowsKey` is where the group's rows live on a stored record\n * (payloadKey, falling back to the group name) — the popup needs that to read a\n * candidate it did not render.\n */\nexport function groupInProgressConfig(group, region) {\n const effective = resolveRegionRules(group, region);\n const inProgressField = effective?.orderBy?.inProgressField;\n if (!inProgressField) return null;\n return {\n name: effective.name,\n label: effective.label ?? effective.name,\n rowsKey: effective.payloadKey || effective.name,\n inProgressField,\n severity: inProgressSeverity(effective),\n message: inProgressError(effective),\n };\n}\n\n/**\n * inProgressVerdict — what a MODULE's rules say about a RECORD.\n *\n * This is the single question both sides of the flow ask, and the reason it\n * lives here rather than in either of them: Quick Submit has to refuse exactly\n * what the submission form would refuse. Two implementations of \"does this\n * candidate have an unfinished qualification\" would drift, and the failure mode\n * is the worst kind — the popup lets someone through to a form that then will\n * not submit, with no way back.\n *\n * groups the TARGET module's form groups (submissions, when gating a\n * submit) — never the source record's own module\n * region the tenant's configured region\n * record the record being judged (a candidate), whose rows are read by\n * each group's rowsKey\n *\n * Returns { severity, message, group } — severity 'allow' when nothing\n * objects, so a caller can treat any other value as \"say something\".\n */\nexport function inProgressVerdict(groups = [], region, record) {\n if (!record) return { severity: 'allow', message: '', group: null };\n\n let warning = null;\n for (const group of groups) {\n const cfg = groupInProgressConfig(group, region);\n if (!cfg || cfg.severity === 'allow') continue;\n\n const rows = readRows(record, cfg.rowsKey);\n if (!rows.some((row) => isInProgressRow(row, cfg))) continue;\n\n // A block is final; keep looking only while all we have is a warning, so\n // one group that merely cautions never masks another that refuses.\n if (cfg.severity === 'block') {\n return { severity: 'block', message: cfg.message, group: cfg };\n }\n warning = warning ?? { severity: 'warn', message: cfg.message, group: cfg };\n }\n return warning ?? { severity: 'allow', message: '', group: null };\n}\n\n/**\n * readRows pulls a group's rows off a stored record.\n *\n * Tolerates the three shapes one arrives in: the plain array a record carries,\n * the `{ rows }` wrapper getFormGroups embeds for edit-prefill, and a missing\n * key. Anything else yields no rows — which means \"nothing to object to\", the\n * safe answer for a shape we do not understand.\n */\nfunction readRows(record, key) {\n const raw = record?.[key];\n if (Array.isArray(raw)) return raw;\n if (Array.isArray(raw?.rows)) return raw.rows;\n return [];\n}\n\n// ── 6. the level rule at SUBMIT time ─────────────────────────────────────\n\n/**\n * levelRuleSeverity — how hard a missing lower qualification bites.\n *\n * 'warn' (default) ask, and let the user go ahead\n * 'block' refuse the submit\n *\n * Defaults to WARN, and deliberately so. This rule has never actually fired —\n * the config and the functions existed with nothing calling them — so switching\n * it on as a hard block would start rejecting submissions that have always been\n * accepted, for a reason nobody has seen before. A question the user can answer\n * introduces the same rule without that.\n *\n * A record is also not always wrong: someone genuinely may hold a Master's from\n * a system that never recorded the Bachelor's, and a recruiter looking at the\n * CV knows that better than a config does.\n */\nexport function levelRuleSeverity(cfg) {\n return String(cfg?.severity ?? '').toLowerCase() === 'block' ? 'block' : 'warn';\n}\n\n/**\n * levelRuleVerdict — check EVERY group that has a level rule, for one form's\n * values.\n *\n * groups the module's form groups (region-resolved by the caller)\n * values the submitted form values; each group's rows are read from its\n * own name, falling back to its payloadKey\n *\n * Returns { severity, message, group } with severity 'allow' when nothing\n * objects. A BLOCK anywhere wins over a warning, so a group that merely\n * cautions can never mask one that refuses.\n */\nexport function levelRuleVerdict(groups = [], values = {}) {\n let warning = null;\n for (const group of groups) {\n const cfg = group?.levelRule;\n if (!cfg) continue;\n const rows = rowsForGroup(values, group);\n if (!rows.length) continue;\n const message = levelRuleError(rows, cfg);\n if (!message) continue;\n if (levelRuleSeverity(cfg) === 'block') {\n return { severity: 'block', message, group };\n }\n warning = warning ?? { severity: 'warn', message, group };\n }\n return warning ?? { severity: 'allow', message: '', group: null };\n}\n\n/** A group's submitted rows, under its name or its payloadKey. */\nfunction rowsForGroup(values, group) {\n for (const key of [group?.name, group?.payloadKey]) {\n if (!key) continue;\n const raw = values?.[key];\n if (Array.isArray(raw)) return raw;\n }\n return [];\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// clearGroupOnChange — \"when THIS field changes, the rest of its group no\n// longer describes anything\".\n//\n// THE REQUIREMENT\n// \"After the new employer is added by clicking Add More in the submission and\n// candidate form, after successful add it selects the employer name. If the\n// employer name changes, reset all the other fields in the employer group.\"\n//\n// The employer group's other fields — recruiter, email, contact code, contact,\n// VMS %, tax — all describe the PREVIOUSLY selected employer. Leaving them\n// behind after the employer changes silently attaches one company's recruiter\n// and phone number to another company, which is worse than a blank form\n// because it looks filled in and correct.\n//\n// `linkedClearField` already existed but clears exactly ONE named sibling and\n// only from a checkbox. This generalises it: any field may declare\n// `clearGroupOnChange: true` to clear every OTHER field of its own group, and\n// `linkedClearField` may now name several fields.\n//\n// Row-scoped inside a repeatable group: clearing employer row 2 must not touch\n// row 1.\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * siblingPath — the form path of `key` as a sibling of the field at `name`.\n * Inside a Form.List row, `name` is [listName, rowIndex, fieldKey], so the\n * sibling shares the first two segments and only the last changes.\n */\nexport function siblingPath(name, key) {\n return Array.isArray(name) && name.length > 1\n ? [...name.slice(0, -1), key]\n : [key];\n}\n\n/**\n * fieldsToClear — which keys this change should blank.\n *\n * @param {object} field the field that changed\n * @param {Set|Array} groupKeys every field key in the field's group\n * @returns {string[]} keys to clear, never including the field itself\n */\nexport function fieldsToClear(field, groupKeys) {\n const own = field?.field;\n const keys = [];\n\n if (field?.clearGroupOnChange) {\n const all = groupKeys instanceof Set ? [...groupKeys] : (groupKeys ?? []);\n all.forEach((key) => { if (key && key !== own) keys.push(key); });\n }\n\n // linkedClearField now accepts one key or several. A field named here is\n // cleared even when it is NOT part of the group, which is what makes it\n // usable for a cross-group dependency.\n const linked = field?.linkedClearField;\n if (Array.isArray(linked)) {\n linked.forEach((key) => { if (key && key !== own) keys.push(key); });\n } else if (typeof linked === 'string' && linked.trim() && linked.trim() !== own) {\n keys.push(linked.trim());\n }\n\n return [...new Set(keys)];\n}\n\n/**\n * applyGroupClear — perform the clear.\n *\n * Values are set to `null` rather than deleted: antd keeps a Form.Item\n * registered either way, and null is what every other clear path in these forms\n * writes, so a subsequent payload build treats it identically.\n *\n * Returns the paths cleared, so a caller can re-validate or test.\n */\nexport function applyGroupClear(form, field, name, groupKeys) {\n const keys = fieldsToClear(field, groupKeys);\n const paths = keys.map((key) => siblingPath(name, key));\n paths.forEach((path) => form.setFieldValue(path, null));\n return paths;\n}\n","import { AUTH_URL } from '../../services/apiConfig';\nimport { fetchJsonWithAuth } from '../../services/authApi';\n\n// =============================================================================\n// Stored values that fall outside a filtered dropdown\n// -----------------------------------------------------------------------------\n// A lookup field's `lookupFilters` answer \"who may I PICK?\" — the jobs\n// Assign-to picker lists only recruiters who report to the signed-in user. They\n// were also, accidentally, answering \"whose name may I SEE?\": a value already on\n// the record but outside that filtered set had no matching option, so antd fell\n// back to printing the raw stored value. The edit form showed \"149\" and \"1\"\n// where it should have shown two people's names.\n//\n// That is not a cosmetic problem. The user cannot tell who the job is assigned\n// to, cannot verify it, and cannot even tell whether removing the chip is safe.\n// And it is invisible to whoever configured the filter, because it only shows up\n// for records assigned before the filter existed, or by somebody higher up the\n// reporting chain.\n//\n// So: options stay filtered, and any value ALREADY STORED is resolved\n// separately and added to the list. Nothing here names a module or a field.\n// =============================================================================\n\n/** The values a select currently holds, flattened to plain scalars. */\nexport function selectedValues(value) {\n const list = Array.isArray(value) ? value : [value];\n return list\n .map((item) => (item && typeof item === 'object' ? (item.value ?? item.key ?? item.id) : item))\n .filter((item) => item !== undefined && item !== null && item !== '');\n}\n\n/**\n * missingOptionValues — stored values with no option to render them.\n *\n * Compared as STRINGS: a legacyUserId is the number 149 on the record and may\n * arrive as \"149\" from the option list. Comparing them raw would report every\n * value as missing and re-fetch on every render.\n */\nexport function missingOptionValues(value, options = []) {\n const known = new Set((options ?? []).map((opt) => String(opt?.value)));\n const missing = [];\n for (const v of selectedValues(value)) {\n const key = String(v);\n if (!known.has(key) && !missing.includes(key)) missing.push(key);\n }\n return missing;\n}\n\n/**\n * fetchLookupLabels — labels for specific stored values, unfiltered.\n *\n * Uses the SAME endpoint the options come from, in its `values` mode, so the\n * label a resolved chip shows is built by the same displayField/displayField2\n * the dropdown itself uses — a separately-built label would drift from the list\n * the moment an admin changed either.\n */\nexport async function fetchLookupLabels(field, values) {\n if (!field?.lookupCollection || !values?.length) return [];\n const params = new URLSearchParams({\n collection: field.lookupCollection,\n displayField: field.displayField ?? '',\n valueField: field.valueField ?? '_id',\n values: values.join(','),\n });\n if (field.displayField2) params.set('displayField2', field.displayField2);\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/lookup-dropdown-values?${params}`);\n const rows = json?.data ?? json ?? [];\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * mergeResolvedOptions — the filtered list plus the resolved stragglers.\n *\n * Resolved entries are marked `resolvedOnly` so a caller can tell them apart.\n * They are NOT disabled: the value is on the record, and the user must be able\n * to remove it. What they cannot do is add it back once removed — which is\n * exactly what the filter is there to prevent, and is now the only thing it\n * prevents.\n */\nexport function mergeResolvedOptions(options = [], resolved = []) {\n if (!resolved.length) return options;\n const known = new Set((options ?? []).map((opt) => String(opt?.value)));\n const extra = resolved\n .filter((row) => !known.has(String(row?.value)))\n .map((row) => ({ ...row, resolvedOnly: true }));\n return extra.length ? [...options, ...extra] : options;\n}\n\n/**\n * labelForMissingValue — the last resort when even the lookup finds nothing.\n *\n * A deleted user, or an id that never existed. Showing the bare number implies\n * it is a name; this says plainly that it could not be resolved while keeping\n * the id visible, because the id is the only thing left to investigate with.\n */\nexport function labelForMissingValue(value) {\n return `Unknown (${value})`;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// dateRules — the ONE implementation of every date-comparison rule, shared by\n// AddFormV1 and EditFormV1.\n//\n// WHY THIS MODULE EXISTS\n// The rule engine was duplicated verbatim in both forms. That is how a fix\n// lands on Add and silently misses Edit — exactly what had already happened to\n// `minLength3` (defined only in AddFormV1, a no-op on the edit form). A\n// requirement phrased as \"candidate AND submission, add AND edit must behave\n// the same\" cannot be satisfied by two copies that merely look alike, so the\n// logic lives here and both forms import it.\n//\n// WHAT A RULE LOOKS LIKE (all options optional, absent === previous behaviour)\n//\n// { type: 'dateAfterField', // this field must be AFTER…\n// value: 'startDate', // …this sibling\n// strict: true, // equal dates are INVALID\n// minGap: { value: 6, unit: 'month' }, // and at least 6 months apart\n// minGapFrom: 'duration', // …or read the gap from a sibling\n// maxGap: { value: 40, unit: 'year' },\n// message: 'End Date must be after Start Date' }\n//\n// `dateBeforeField` is the mirror image. Writing BOTH (start declares\n// dateBeforeField(end), end declares dateAfterField(start)) is what makes the\n// constraint show up in BOTH pickers: pick 7 July as Start and the End picker\n// greys out everything up to and including 7 July, and vice-versa. The two\n// rules are generated from one table in the seeder so they cannot drift.\n//\n// Every rule drives BOTH the submit-time validator and the picker's\n// `disabledDate` from the same object — a greyed-out calendar that disagrees\n// with the error message is worse than either alone.\n// ─────────────────────────────────────────────────────────────────────────\nimport dayjs from 'dayjs';\nimport { appNow } from '../../services/timezone';\n\nexport const isEmptyValue = (v) => v === undefined || v === null || v === '';\n\n/** Milliseconds for an instant-comparable value (dayjs, Date, ISO string). */\nexport function getComparableDateTime(value) {\n if (!value) return null;\n if (typeof value.valueOf === 'function') {\n const time = value.valueOf();\n return Number.isNaN(time) ? null : time;\n }\n const time = new Date(value).getTime();\n return Number.isNaN(time) ? null : time;\n}\n\n/** Milliseconds at the START of the value's day — the unit date rules compare in. */\nexport function getComparableDateDay(value) {\n if (!value) return null;\n const date = dayjs(value);\n return date.isValid() ? date.startOf('day').valueOf() : null;\n}\n\n/** Minutes since midnight. TimePicker values share a day, so startOf('day') would make every time equal. */\nexport function getComparableTimeOfDay(value) {\n if (!value) return null;\n const time = dayjs(value);\n return time.isValid() ? time.hour() * 60 + time.minute() : null;\n}\n\n// Units dayjs accepts for add/subtract, mapped from what an admin might type.\nconst GAP_UNITS = {\n d: 'day', day: 'day', days: 'day',\n w: 'week', week: 'week', weeks: 'week',\n m: 'month', mo: 'month', month: 'month', months: 'month',\n y: 'year', yr: 'year', year: 'year', years: 'year',\n};\n\n/**\n * parseGap — normalise every shape a gap can arrive in into {value, unit}.\n *\n * It has to be forgiving because one of the sources is a DROPDOWN whose options\n * are admin-authored master data: \"6 months\", \"6m\", \"6\", {value:6,unit:'month'}\n * all have to mean the same thing, or the config screen becomes a trap where\n * the wrong-but-reasonable spelling silently disables the rule.\n *\n * A bare number means months — the unit the duration dropdown is expressed in.\n * Returns null for anything that carries no usable magnitude.\n */\nexport function parseGap(raw) {\n if (raw === null || raw === undefined || raw === '') return null;\n\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n const value = Number(raw.value ?? raw.count ?? raw.amount);\n if (!Number.isFinite(value) || value <= 0) return null;\n return { value, unit: GAP_UNITS[String(raw.unit ?? 'month').toLowerCase()] ?? 'month' };\n }\n\n const text = String(raw).trim().toLowerCase();\n if (!text) return null;\n const match = text.match(/^(\\d+(?:\\.\\d+)?)\\s*([a-z]*)$/);\n if (!match) return null;\n const value = Number(match[1]);\n if (!Number.isFinite(value) || value <= 0) return null;\n return { value, unit: GAP_UNITS[match[2]] ?? 'month' };\n}\n\n/**\n * resolveGap — the gap actually in force for this field right now.\n *\n * `minGapFrom` names a sibling whose CURRENT value supplies the gap, which is\n * how the \"select 6 months and the pickers tighten to 6 months\" dropdown works\n * without any rule rewriting. It wins over a static `minGap` when it holds a\n * usable value, and falls back to the static one when the dropdown is empty.\n */\nexport function resolveGap(rule, readSibling) {\n const fromField = rule?.minGapFrom ?? rule?.gapFrom;\n if (fromField && typeof readSibling === 'function') {\n const dynamic = parseGap(readSibling(fromField));\n if (dynamic) return dynamic;\n }\n return parseGap(rule?.minGap);\n}\n\n/**\n * compareBoundary — the earliest (dateAfterField) or latest (dateBeforeField)\n * day this field may hold, given the other end of the pair.\n *\n * Returns a day-start timestamp, or null when there is nothing to constrain.\n */\nexport function compareBoundary(type, compareDay, rule, readSibling) {\n if (compareDay === null) return null;\n const gap = resolveGap(rule, readSibling);\n let boundary = dayjs(compareDay);\n if (gap) {\n boundary = type === 'dateAfterField'\n ? boundary.add(gap.value, gap.unit)\n : boundary.subtract(gap.value, gap.unit);\n }\n return boundary.startOf('day').valueOf();\n}\n\n/**\n * violatesPair — is `inputDay` on the wrong side of the boundary?\n *\n * `strict` is what makes \"the end date can't be the same day as the start date\"\n * work: without it the rule reads \"on or after\", which is the behaviour that\n * let 7 July → 7 July through.\n */\nexport function violatesPair(type, inputDay, compareDay, rule, readSibling) {\n if (inputDay === null || compareDay === null) return false;\n const boundary = compareBoundary(type, compareDay, rule, readSibling);\n if (boundary === null) return false;\n const strict = Boolean(rule?.strict) && !resolveGap(rule, readSibling);\n if (type === 'dateAfterField') {\n return strict ? inputDay <= boundary : inputDay < boundary;\n }\n return strict ? inputDay >= boundary : inputDay > boundary;\n}\n\n/**\n * pairMessage — the error text, falling back to something that names the real\n * constraint rather than a generic \"invalid date\".\n */\nexport function pairMessage(type, label, rule, readSibling) {\n if (rule?.message) return rule.message;\n const other = rule?.value ?? rule?.compareField ?? rule?.field ?? 'the paired date';\n const gap = resolveGap(rule, readSibling);\n if (gap) {\n const unit = gap.value === 1 ? gap.unit : `${gap.unit}s`;\n return type === 'dateAfterField'\n ? `${label} must be at least ${gap.value} ${unit} after ${other}`\n : `${label} must be at least ${gap.value} ${unit} before ${other}`;\n }\n if (rule?.strict) {\n return type === 'dateAfterField'\n ? `${label} must be after ${other}`\n : `${label} must be before ${other}`;\n }\n return type === 'dateAfterField'\n ? `${label} must be on or after ${other}`\n : `${label} must be on or before ${other}`;\n}\n\n/**\n * buildPairValidator — the antd rule object for dateAfterField/dateBeforeField.\n *\n * `readSibling(fieldName)` resolves a sibling's CURRENT value with the caller's\n * own row-vs-top-level scoping, so this module never needs to know it is inside\n * a repeatable group.\n */\nexport function buildPairValidator({ type, label, rule, readSibling }) {\n return {\n validator: async (_, input) => {\n const compareField = rule?.value ?? rule?.compareField ?? rule?.field;\n // An empty value on EITHER side is the `required` rule's business —\n // otherwise an optional date pair becomes mandatory the moment one half\n // is filled in.\n if (!compareField || isEmptyValue(input)) return Promise.resolve();\n const compareValue = readSibling(compareField);\n if (isEmptyValue(compareValue)) return Promise.resolve();\n\n const inputDay = getComparableDateDay(input);\n const compareDay = getComparableDateDay(compareValue);\n if (inputDay === null || compareDay === null) return Promise.resolve();\n\n return violatesPair(type, inputDay, compareDay, rule, readSibling)\n ? Promise.reject(new Error(pairMessage(type, label, rule, readSibling)))\n : Promise.resolve();\n },\n };\n}\n\n/**\n * buildDisabledDate — the DatePicker `disabledDate` predicate for a field,\n * assembled from every date rule it declares.\n *\n * `now` is injected so the \"today\" boundary can come from the application's\n * configured timezone rather than the browser's (see services/timezone.js) —\n * a user in another zone would otherwise have noFutureDate reject their own\n * today, or accept a tomorrow.\n */\n/**\n * violatesRowHierarchy — does this date clash with an ADJACENT ROW?\n *\n * A newest-first list (education, work experience) says something the per-row\n * rules cannot: row 2 happened BEFORE row 1. So once row 1 has a start date,\n * every date at or after it is impossible for row 2 — you cannot have finished\n * a later qualification before starting an earlier one.\n *\n * Constraining the CALENDAR rather than only erroring on submit is the point:\n * the user sees which dates are available while choosing, instead of being told\n * afterwards that the one they picked was wrong.\n *\n * @param {number} day candidate day (start-of-day ms)\n * @param {object} cfg the field's `rowHierarchy` config\n * @param {object} rowsAccess { rows, rowIndex } — every row of the group + this row's index\n */\nexport function violatesRowHierarchy(day, cfg, { rows, rowIndex } = {}) {\n if (day === null || !cfg || !Array.isArray(rows) || rowIndex == null) return false;\n // 'newestFirst' is the only shape today; naming it keeps an 'oldestFirst'\n // list addable as config rather than as a second code path.\n if ((cfg.mode ?? 'newestFirst') !== 'newestFirst') return false;\n\n const startKey = cfg.startField ?? 'startDate';\n const endKey = cfg.endField ?? 'endDate';\n\n // The row ABOVE is more recent, so this row must end before that row began.\n const above = rows[rowIndex - 1];\n if (above) {\n const ceiling = getComparableDateDay(above[startKey]);\n if (ceiling !== null && day >= ceiling) return true;\n }\n\n // The row BELOW is older, so this row must not start before that row ended.\n const below = rows[rowIndex + 1];\n if (below) {\n const floor = getComparableDateDay(below[endKey]) ?? getComparableDateDay(below[startKey]);\n if (floor !== null && day <= floor) return true;\n }\n\n return false;\n}\n\n/**\n * rowHierarchyMessage — why a date was refused, in terms of the OTHER row.\n * \"Overlaps the entry above\" is actionable; \"invalid date\" is not.\n */\nexport function rowHierarchyMessage(cfg, position = 'above') {\n const messages = cfg?.messages ?? {};\n if (position === 'below') {\n return messages.below\n || 'This starts before the entry below it finished. Entries are listed newest first, so they cannot overlap.';\n }\n return messages.above\n || 'This overlaps the entry above it. Entries are listed newest first, so this one must finish before that one started.';\n}\n\nexport function buildDisabledDate({ field, readSibling, rows, rowIndex, now = appNow }) {\n const rules = field?.validations ?? field?.validation ?? field?.rules ?? [];\n const predicates = [];\n\n for (const raw of rules) {\n const rule = typeof raw === 'string' ? { type: raw } : raw;\n const type = rule?.type;\n\n if (type === 'noPastDate') {\n predicates.push((current) => {\n if (!current) return false;\n return getComparableDateDay(current) < now().startOf('day').valueOf();\n });\n }\n\n if (type === 'noFutureDate') {\n predicates.push((current) => {\n if (!current) return false;\n return getComparableDateDay(current) > now().startOf('day').valueOf();\n });\n }\n\n if (type === 'minAge') {\n const years = Number(rule?.value ?? 18);\n predicates.push((current) => {\n if (!current) return false;\n return getComparableDateDay(current) > now().subtract(years, 'year').startOf('day').valueOf();\n });\n }\n\n if (type === 'dateAfterField' || type === 'dateBeforeField') {\n const compareField = rule?.value ?? rule?.compareField ?? rule?.field;\n if (!compareField) continue;\n predicates.push((current) => {\n if (!current) return false;\n const compareValue = readSibling(compareField);\n if (isEmptyValue(compareValue)) return false;\n return violatesPair(\n type,\n getComparableDateDay(current),\n getComparableDateDay(compareValue),\n rule,\n readSibling,\n );\n });\n }\n }\n\n // Cross-row constraint. Declared on the FIELD (`rowHierarchy`) but evaluated\n // against the whole group, which is why buildDisabledDate needs `rows` and\n // `rowIndex` — a per-row `readSibling` cannot see the neighbouring rows.\n if (field?.rowHierarchy && Array.isArray(rows) && rowIndex != null) {\n predicates.push((current) => {\n if (!current) return false;\n return violatesRowHierarchy(getComparableDateDay(current), field.rowHierarchy, { rows, rowIndex });\n });\n }\n\n return predicates.length\n ? (current) => predicates.some((predicate) => predicate(current))\n : undefined;\n}\n\n/**\n * defaultPickerValue — which month the calendar opens on, so a Date of Birth\n * field does not open on today and make the user page back 30 years.\n */\nexport function defaultPickerValue(field, now = appNow) {\n const rules = field?.validations ?? field?.validation ?? field?.rules ?? [];\n for (const raw of rules) {\n const rule = typeof raw === 'string' ? { type: raw } : raw;\n if (rule?.type === 'minAge') return now().subtract(Number(rule?.value ?? 18), 'year');\n if (rule?.type === 'noFutureDate' || rule?.type === 'noPastDate') return now();\n }\n return undefined;\n}\n\n/**\n * buildRowHierarchyValidator — the submit-time counterpart of the greyed-out\n * calendar.\n *\n * The picker stops a date being CHOSEN; this stops one that arrived another way\n * — typed, pasted, prefilled from a résumé, or entered before the neighbouring\n * row was filled in. A calendar constraint with no validator behind it is a\n * suggestion.\n */\nexport function buildRowHierarchyValidator({ field, rows, rowIndex }) {\n const cfg = field?.rowHierarchy;\n return {\n validator: async (_, input) => {\n if (!cfg || isEmptyValue(input) || !Array.isArray(rows) || rowIndex == null) {\n return Promise.resolve();\n }\n const day = getComparableDateDay(input);\n if (day === null) return Promise.resolve();\n if (!violatesRowHierarchy(day, cfg, { rows, rowIndex })) return Promise.resolve();\n\n // Name WHICH neighbour it clashes with, so the fix is obvious.\n const startKey = cfg.startField ?? 'startDate';\n const above = rows[rowIndex - 1];\n const ceiling = above ? getComparableDateDay(above[startKey]) : null;\n const position = (ceiling !== null && day >= ceiling) ? 'above' : 'below';\n return Promise.reject(new Error(rowHierarchyMessage(cfg, position)));\n },\n };\n}\n","// Shared submit-toast resolver used by AddFormV1/EditFormV1.\n// Admin-configured per FormGroup (group.submitMessages — see FormGroupsSection),\n// generic across every module/project: no module name is ever referenced here.\n//\n// Defaults (no configuration anywhere, including brand-new modules):\n// add → \"<Module> has added successfully\"\n// edit → \"<Module> has Updated successfully\"\n// An admin can override any template (with ${module} substitution) or set\n// suppress to skip the success toast entirely.\n\n// Picks the configured messages from the group with the lowest `order` that\n// declares any — the same resolution rule afterSubmitNav uses.\nexport function resolveSubmitMessages(groups) {\n const withConfig = (groups ?? [])\n .filter((g) => g?.submitMessages && typeof g.submitMessages === 'object')\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n return withConfig[0]?.submitMessages ?? null;\n}\n\n// \"jobs\" → \"Jobs\", \"locationTaxMaster\" → \"LocationTaxMaster\" (first letter only —\n// the module key is the admin-facing name across the platform).\nfunction moduleLabel(moduleName) {\n const raw = String(moduleName ?? '').trim();\n return raw ? raw.charAt(0).toUpperCase() + raw.slice(1) : 'Record';\n}\n\nfunction fillTemplate(template, moduleName) {\n return String(template).replaceAll('${module}', moduleLabel(moduleName))\n .replaceAll('${moduleName}', moduleLabel(moduleName));\n}\n\n// Returns the success toast text for a finished submit, or null when the admin\n// suppressed success toasts for this module. kind: 'add' | 'edit'.\nexport function submitSuccessMessage(groups, moduleName, kind) {\n const config = resolveSubmitMessages(groups);\n if (config?.suppress) return null;\n const template = kind === 'edit' ? config?.editSuccess : config?.addSuccess;\n if (template && String(template).trim()) return fillTemplate(template, moduleName);\n return kind === 'edit'\n ? `${moduleLabel(moduleName)} has Updated successfully`\n : `${moduleLabel(moduleName)} has added successfully`;\n}\n\n// Returns the error toast text: admin override first, then the real server\n// error (most actionable), then a generic fallback. Never null — a failed\n// submit must always surface.\nexport function submitErrorMessage(groups, moduleName, kind, serverText) {\n const config = resolveSubmitMessages(groups);\n const template = kind === 'edit' ? config?.editError : config?.addError;\n if (template && String(template).trim()) return fillTemplate(template, moduleName);\n if (serverText && String(serverText).trim()) return String(serverText);\n return kind === 'edit'\n ? `Failed to update ${moduleLabel(moduleName)}`\n : `Failed to create ${moduleLabel(moduleName)}`;\n}\n","// Shared \"scroll to the first invalid field\" handler for AddFormV1/EditFormV1.\n// Wire it from the antd Form's onFinishFailed: when Create/Save is clicked with\n// validation errors, the screen scrolls to the first errored field, focuses it\n// (cursor placed in the input) and pulses a red halo so the user sees exactly\n// which field failed. Generic — works for every module's add & edit form.\n//\n// Robustness notes:\n// - antd applies the .ant-form-item-has-error classes AFTER onFinishFailed\n// fires, so location runs on a short delay.\n// - The DOM query + scrollIntoView is the PRIMARY mechanism and always runs.\n// form.scrollToField is only a best-effort extra: antd locates fields by\n// DOM id, which custom field controls don't always forward — relying on it\n// alone silently scrolls nothing.\n// - Callers should expand any collapsed sections BEFORE calling this (a field\n// inside a display:none section can be neither scrolled to nor focused) —\n// both form engines do so in their onFinishFailed wrapper.\nexport function scrollToFirstFormError({ errorFields } = {}, form) {\n if (!errorFields?.length) return;\n const firstName = errorFields[0]?.name;\n\n setTimeout(() => {\n if (form?.scrollToField && firstName !== undefined) {\n try {\n form.scrollToField(firstName, { behavior: 'smooth', block: 'center' });\n } catch { /* best-effort only — the DOM scroll below always runs */ }\n }\n\n const errorFormItem = document.querySelector('.ant-form-item-has-error');\n if (!errorFormItem) return;\n const input = errorFormItem.querySelector('input, textarea, select, .ProseMirror');\n const target = input ?? errorFormItem;\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n\n setTimeout(() => {\n input?.focus?.({ preventScroll: true });\n target.style.boxShadow = '0 0 0 3px rgba(255, 77, 79, 0.3)';\n setTimeout(() => { target.style.boxShadow = ''; }, 1200);\n }, 350);\n }, 60);\n}\n\nexport default scrollToFirstFormError;\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","import { Breadcrumb } from 'antd';\nimport { DownOutlined } from '@ant-design/icons';\nimport { Link } from 'react-router-dom';\nimport AppTypography from './typography/Typography';\n\nexport default function AppBreadcrumb({ items = [] }) {\n const breadcrumbItems = items.map((item, index) => {\n const isLast = index === items.length - 1;\n return {\n title: isLast ? (\n <AppTypography variant=\"body\" weight=\"medium\" color=\"link\">\n {item.label}\n </AppTypography>\n ) : (\n <Link to={item.href}>\n <AppTypography variant=\"body\" color=\"secondary\">\n {item.label}\n </AppTypography>\n {item.dropdown && <DownOutlined />}\n </Link>\n ),\n };\n });\n\n return <Breadcrumb items={breadcrumbItems} />;\n}"],"mappings":"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"}
|