{"version":3,"file":"Breadcrumb-CuTogk4G.cjs","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/documentApi.js","../src/services/detailedViewApi.js","../src/utils/roleFormPermissionGate.js","../src/components/form/inputSecurity.js","../src/components/form/validationOverride.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/ValidationOverrideCheckbox.jsx","../src/components/form/prefillWhenRules.js","../src/components/form/optionConstraints.js","../src/components/form/optionRowFilters.js","../src/components/form/fieldTooltip.js","../src/components/form/dependentLookupReset.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    filterGroupFields: Array.isArray(field.filterGroupFields) ? field.filterGroupFields : [],\n    filterGroupSeparator: field.filterGroupSeparator ?? ' ',\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', includeMeta = false) {\n  const params = new URLSearchParams({ module: apiModule, userId: '0', [idParam]: String(idValue), configType });\n  if (includeMeta && (!configType || configType === 'listView')) params.set('includeMeta', '1');\n  const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}?${params}`);\n  const payload = json.data ?? json;\n  const fields = firstArray(payload, payload?.visibleFields, json?.visibleFields);\n  return includeMeta\n    ? {\n      fields,\n      dataVisibilityMode: payload?.dataVisibilityMode ?? json?.dataVisibilityMode ?? 'all',\n      dataOwnerField: payload?.dataOwnerField ?? json?.dataOwnerField ?? 'createdBy',\n    }\n    : fields;\n}\n\nexport async function getRoleModuleFields(role, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n  const roleId = getRoleId(role);\n  const customizableModules = normalizeCustomizableModules(modules);\n  const entries = await Promise.all(\n    customizableModules.map(async ({ key, apiModule }) => {\n      const fields = await fetchFieldConfigModule(apiModule, 'roleId', roleId, configType);\n      return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, roleId }))];\n    })\n  );\n  return Object.fromEntries(entries);\n}\n\n\nexport async function getRoleModuleFieldsWithMeta(role, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n  const roleId = getRoleId(role);\n  const customizableModules = normalizeCustomizableModules(modules);\n  const entries = await Promise.all(\n    customizableModules.map(async ({ key, apiModule }) => {\n      const result = await fetchFieldConfigModule(apiModule, 'roleId', roleId, configType, true);\n      const fields = result.fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, roleId }));\n      return [key, {\n        fields,\n        dataVisibilityMode: result.dataVisibilityMode ?? 'all',\n        dataOwnerField: result.dataOwnerField ?? 'createdBy',\n      }];\n    })\n  );\n  return Object.fromEntries(entries);\n}\n\nexport async function getTeamModuleFields(team, configType = 'listView') {\n  const teamId = getTeamId(team);\n  const entries = await Promise.all(\n    CUSTOMIZABLE_MODULES.map(async ({ key, apiModule }) => {\n      const fields = await fetchFieldConfigModule(apiModule, 'teamId', teamId, configType);\n      return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, teamId }))];\n    })\n  );\n  return Object.fromEntries(entries);\n}\n\nfunction normalizeListViewLookup(field) {\n  if (!field?.lookup) return null;\n\n  const lookup = { ...field.lookup };\n  const localField = String(lookup.localField ?? '').trim() || String(field.fieldKey ?? '').trim();\n  const valueField = String(lookup.valueField ?? lookup.foreignField ?? '').trim() || '_id';\n  const displayField = String(lookup.displayField ?? lookup.projectField ?? '').trim();\n  const displayField2 = String(lookup.displayField2 ?? '').trim();\n\n  let projectFields = Array.isArray(lookup.projectFields)\n    ? lookup.projectFields.map((value) => String(value ?? '').trim()).filter(Boolean)\n    : [];\n\n  // The list API accepts displayField, projectField and projectFields. Persist\n  // projectFields as well so old/new backend versions resolve the same config.\n  if (projectFields.length === 0) {\n    projectFields = [displayField, displayField2].filter(Boolean);\n  }\n\n  return {\n    ...lookup,\n    localField,\n    valueField,\n    displayField,\n    ...(displayField2 ? { displayField2 } : {}),\n    projectFields,\n  };\n}\n\nfunction visibleFieldPayload(fields, configType) {\n  const isListView = !configType || configType === 'listView';\n  return fields.map((field, index) => {\n    const base = {\n      label: field.fieldName, field: field.fieldKey,\n      isVisible: field.isVisible, type: field.type ?? 'text', order: field.order ?? index,\n    };\n    if (isListView) {\n      Object.assign(base, {\n        isLink: field.isLink ?? false, linkTemplate: field.linkTemplate ?? '',\n        linkType: field.linkType ?? 'internal', linkTarget: field.linkTarget ?? '_self',\n        action: field.action ?? 'navigate',\n        secondaryField: field.secondaryField ?? '', secondaryLabel: field.secondaryLabel ?? '',\n        secondaryFields: Array.isArray(field.secondaryFields) ? field.secondaryFields : [],\n        secondarySeparator: field.secondarySeparator ?? '',\n        lookup: normalizeListViewLookup(field), actionButtons: field.actionButtons ?? [],\n        derived: field.derived ?? null, computed: field.computed ?? null,\n        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        base.filterGroupFields = Array.isArray(field.filterGroupFields) ? field.filterGroupFields : [];\n        base.filterGroupSeparator = field.filterGroupSeparator ?? ' ';\n        if (field.lookup) base.lookup = field.lookup;\n      } else {\n        if (['select', 'radio', 'checkbox'].includes(field.type)) {\n          base.dataSource = field.dataSource ?? 'module';\n          if (field.dataSource === 'group') base.groupName = field.groupName ?? '';\n        }\n      }\n    }\n    return base;\n  });\n}\n\nexport async function updateRoleModuleFieldConfig(role, module, fields, configType = 'listView', modules = CUSTOMIZABLE_MODULES, dataVisibilityMode = '', dataOwnerField = 'createdBy') {\n  const roleId    = getRoleId(role);\n  const apiModule = normalizeCustomizableModules(modules).find((m) => m.key === module)?.apiModule ?? module;\n  return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/role`, {\n    method: 'PUT',\n    body: JSON.stringify({\n      module: apiModule, roleId, configType,\n      visibleFields: visibleFieldPayload(fields, configType),\n      ...((!configType || configType === 'listView') && dataVisibilityMode\n        ? { dataVisibilityMode, dataOwnerField: String(dataOwnerField || 'createdBy').trim() || 'createdBy' }\n        : {}),\n    }),\n  });\n}\n\n// ── Role Configure → Form (derived from Form Groups) ─────────────────────────\n// Unlike getRoleModuleFields/updateRoleModuleFieldConfig above (an\n// independently-seeded flat field list), these back the Form tab's grouped\n// view: the backend derives groups/fields straight from the module's Form\n// Groups config and overlays this role's saved grants (GET\n// /admin/role-form-permissions), so Form Groups stays the single source of\n// truth and new/renamed/removed fields sync automatically. Saving still goes\n// through the existing PUT /admin/field-config/role (configType \"form\") —\n// the backend clamps it against Form Groups' global Show flags either way.\nconst ROLE_FORM_PERMISSIONS_PATH = '/admin/role-form-permissions';\n\n// Must match Be_Auth_DevOps models/roleFormPermissions.go's\n// roleGroupPermissionKey — the synthetic field-config key a group-level\n// ON/OFF toggle is persisted under, alongside real per-field grants, in the\n// same flat visibleFields list (no separate schema/endpoint needed).\nfunction roleGroupPermissionKey(groupName) {\n  return `__group__:${groupName}`;\n}\n\n// scope: { clientId, region } — same client/region scope as\n// getAdminFormGroups/AddFormV1's effectiveClientId+effectiveRegion, so a\n// client- or region-scoped Form Groups override (e.g. a per-client Jobs\n// variant) is reflected here too, not just the tenant-wide default. Generic\n// for any module — GetFormGroupConfigScoped falls back to the default when\n// no scoped config exists for the given module, so this is always safe to pass.\nexport async function getRoleFormPermissions(role, module, scope = {}) {\n  const roleId = getRoleId(role);\n  const params = new URLSearchParams({ module, roleId: String(roleId ?? '') });\n  if (scope.clientId) params.set('clientId', scope.clientId);\n  if (scope.region) params.set('region', scope.region);\n  const json = await fetchJsonWithAuth(AUTH_URL, `${ROLE_FORM_PERMISSIONS_PATH}?${params}`);\n  const payload = json?.data ?? json;\n  return Array.isArray(payload) ? payload : [];\n}\n\n// groups: [{ name, label, enabled, locked, fields: [{ field, label, enabled, locked, editable }] }]\n// as edited in the UI (see RoleFormPermissionsEditor). Group toggles are sent\n// as synthetic visibleFields rows so the existing role-save + clamp pipeline\n// needs no changes.\n//\n// A role's field-level grants are NOT stored per client/region — they're one\n// shared set overlaid onto whichever scoped Form Groups structure a caller\n// requests (getRoleFormPermissions). So any group/field that's `locked` in\n// the CURRENTLY VIEWED scope (globally disabled there, but possibly enabled\n// under the tenant-wide default or a different client's scope) is left out\n// of the payload entirely — sending its scope-computed `enabled: false`\n// would silently overwrite the role's real, scope-independent stored\n// preference the next time anyone loads a different scope. Locked items\n// aren't editable in this view anyway (their Switch is disabled), so there's\n// nothing this save is meant to change for them.\nexport async function saveRoleFormPermissions(role, module, groups) {\n  const roleId = getRoleId(role);\n  const visibleFields = [];\n  let order = 0;\n  for (const group of groups) {\n    if (!group.locked) {\n      visibleFields.push({\n        label: group.label,\n        field: roleGroupPermissionKey(group.name),\n        // A group carries two independent role toggles: isVisible = shown,\n        // isEditable = not-disabled (read-only when false). Default both true.\n        isVisible: group.enabled,\n        isEditable: group.editable !== false,\n        order: order++,\n      });\n    }\n    for (const field of group.fields ?? []) {\n      if (field.locked) continue;\n      visibleFields.push({\n        label: field.label,\n        field: field.field,\n        isVisible: field.enabled,\n        isEditable: field.editable !== false,\n        order: order++,\n      });\n    }\n  }\n  return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/role`, {\n    method: 'PUT',\n    body: JSON.stringify({ module, roleId, configType: 'form', visibleFields }),\n  });\n}\n\nexport async function updateTeamModuleFieldConfig(team, module, fields, configType = 'listView') {\n  const teamId    = getTeamId(team);\n  const apiModule = CUSTOMIZABLE_MODULES.find((m) => m.key === module)?.apiModule ?? module;\n  return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/team`, {\n    method: 'PUT',\n    body: JSON.stringify({ module: apiModule, teamId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n  });\n}\n\n// ── Users ─────────────────────────────────────────────────────────────────────\n\nexport async function getAllUsers({ offset = 0, limit = 10, sortBy = 'new', searchQuery = '' } = {}) {\n  const params = new URLSearchParams({ offset: String(offset), limit: String(limit), sortBy });\n  if (searchQuery) params.set('searchquery', searchQuery);\n  const json = await fetchJsonWithAuth(AUTH_URL, `/get-all-users?${params}`);\n  const payload = json.data ?? json;\n  const users = firstArray(\n    payload, json.users, json.rows, json.items, json.records,\n    payload?.users, payload?.data, payload?.rows, payload?.items,\n    payload?.records, payload?.docs, payload?.result, payload?.results,\n  );\n  return { users, total: normalizeTotal({ ...json, ...payload }, users) };\n}\n\nexport function getUserId(user) {\n  const value = firstValue(\n    user?.raw ?? user,\n    ['USER_ID', 'user_id', 'userId', 'id', '_id', 'uuid'],\n    user?.key\n  );\n  const num = Number(value);\n  return Number.isFinite(num) ? num : value;\n}\n\nexport function getUserRoleId(user) {\n  const value = firstValue(user?.raw ?? user, ['ROLE_ID', 'role_id', 'roleId', 'ROLEID'], 0);\n  const num = Number(value);\n  return Number.isFinite(num) && num > 0 ? num : 0;\n}\n\nasync function getFieldConfigModule(apiModule, userId, roleId, configType) {\n  const params = new URLSearchParams({ module: apiModule, userId: String(userId ?? ''), roleId: String(roleId ?? ''), configType });\n  const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}?${params}`);\n  const payload = json.data ?? json;\n  return firstArray(payload, payload?.visibleFields, json?.visibleFields);\n}\n\nasync function getDropdownModuleFields(apiModule) {\n  const json = await fetchJsonWithAuth(SUBMISSIONS_URL, `/filter-dropdown-fields?module=${encodeURIComponent(apiModule)}`);\n  const payload = json.data ?? json;\n  return firstArray(payload, payload?.fields, payload?.items, payload?.rows, json?.fields);\n}\n\nexport async function getUserModuleFields(user, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n  const userId = getUserId(user);\n  const roleId = getUserRoleId(user);\n  const customizableModules = normalizeCustomizableModules(modules);\n  const entries = await Promise.all(\n    customizableModules.map(async ({ key, apiModule }) => {\n      let fields;\n      try {\n        fields = await getFieldConfigModule(apiModule, userId, roleId, configType);\n      } catch {\n        try { fields = await getDropdownModuleFields(apiModule); } catch { fields = []; }\n      }\n      if ((fields?.length ?? 0) === 0) {\n        try { fields = await getDropdownModuleFields(apiModule); } catch { fields = []; }\n      }\n      return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, userId, roleId }))];\n    })\n  );\n  return Object.fromEntries(entries);\n}\n\nexport async function hasUserFieldConfigData(user, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n  const userId = getUserId(user);\n  const roleId = getUserRoleId(user);\n  const customizableModules = normalizeCustomizableModules(modules);\n  const results = await Promise.all(\n    customizableModules.map(async ({ apiModule }) => {\n      try {\n        const fields = await getFieldConfigModule(apiModule, userId, roleId, configType);\n        return (fields?.length ?? 0) > 0;\n      } catch {\n        return false;\n      }\n    })\n  );\n  return results.some(Boolean);\n}\n\nexport async function seedFieldConfig(collections = []) {\n  return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/seed`, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ collections }),\n  });\n}\n\nexport async function getFieldConfigSeedStatus() {\n  const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/seed-status`);\n  const data = json?.data ?? json ?? {};\n  return {\n    modules: Array.isArray(data.modules) ? data.modules : [],\n    seededModules: data.seededModules ?? {},\n  };\n}\n\nexport async function updateUserModuleFieldConfig(user, module, fields, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n  const userId    = getUserId(user);\n  const roleId    = getUserRoleId(user);\n  const apiModule = normalizeCustomizableModules(modules).find((m) => m.key === module)?.apiModule ?? module;\n  return fetchJsonWithAuth(AUTH_URL, FIELD_CONFIG_PATH, {\n    method: 'PUT',\n    body: JSON.stringify({ module: apiModule, userId, roleId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n  });\n}\n\nexport async function updateUserModuleFields(user, moduleFields, configType = 'listView') {\n  return Promise.all(\n    Object.entries(moduleFields).map(([module, fields]) => updateUserModuleFieldConfig(user, module, fields, configType))\n  );\n}\n\nexport async function uploadFormGroupIcon(file) {\n  const token = localStorage.getItem('authToken');\n  const formData = new FormData();\n  formData.append('icon', file);\n  const res = await fetch(`${AUTH_URL}/admin/form-groups/icon`, {\n    method: 'POST',\n    headers: { Authorization: `Bearer ${token}` },\n    body: formData,\n  });\n  if (!res.ok) { const e = new Error(`Upload failed: ${res.status}`); e.status = res.status; throw e; }\n  const json = await res.json();\n  return json.data ?? json;\n}\n\nexport async function uploadListViewActionIcon(file) {\n  const token = localStorage.getItem('authToken');\n  const formData = new FormData();\n  formData.append('icon', file);\n  const res = await fetch(`${AUTH_URL}/admin/field-config/action-icon`, {\n    method: 'POST',\n    headers: { Authorization: `Bearer ${token}` },\n    body: formData,\n  });\n  if (!res.ok) { const e = new Error(`Upload failed: ${res.status}`); e.status = res.status; throw e; }\n  const json = await res.json();\n  return json.data ?? json;\n}\n\n// Module options for the Actions admin screens: the configured menu/module\n// catalog PLUS every collection in this app's database, so row actions can be\n// configured for ANY module (onboarding, movements, a brand-new collection…)\n// without first registering it as a menu module. Resolves per app DB via\n// X-App-Id, so each project sees its own collections.\nexport async function getActionConfigModules() {\n  const [modules, collections, existingConfigs] = await Promise.all([\n    getAvailableModules().catch(() => []),\n    getAvailableCollections().catch(() => []),\n    getRowActionConfigs().catch(() => []),\n  ]);\n  const baseModules = Array.isArray(modules) ? modules : [];\n  // Detail-view action variants: a module's DETAIL page can carry its own row\n  // actions (e.g. an \"Open JD\" primary button on the submission detail view)\n  // without those actions also appearing on the LIST view. The detail page\n  // fetches actions under whatever key it passes as DetailHeaderCard's\n  // customConfigName — \"submissiondetails\", \"submissionJob\" — which follows no\n  // derivable rule. This used to GENERATE `${key}detail` names, which produced\n  // keys nothing ever requests (\"submissionsdetail\" vs the real\n  // \"submissiondetails\"), so configuring one silently changed nothing.\n  // Offer the keys that actually exist in rowActionConfig instead.\n  const variantKeys = existingConfigs\n    .map((config) => String(config?.module ?? '').trim())\n    .filter(Boolean);\n  return [\n    ...baseModules,\n    ...(Array.isArray(collections) ? collections : []),\n    ...variantKeys,\n  ];\n}\n\n// Generic module CRUD — used by the Masters admin screen (locations/taxes),\n// but not specific to either: works for any module via the dynamic gateway.\nexport async function listModuleRecords(module, { page = 1, limit = 100 } = {}) {\n  const params = new URLSearchParams({ module, page: String(page), limit: String(limit) });\n  const json = await fetchJsonWithAuth(AUTH_URL, `/module/list?${params}`);\n  const data = json?.data ?? json ?? {};\n  return { items: Array.isArray(data.data) ? data.data : [], total: data.pagination?.total ?? 0 };\n}\n\nexport async function createModuleRecordGeneric(module, payload) {\n  return fetchJsonWithAuth(AUTH_URL, `/module/create?module=${encodeURIComponent(module)}`, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify(payload),\n  });\n}\n\nexport async function updateModuleRecordGeneric(module, id, payload) {\n  return fetchJsonWithAuth(AUTH_URL, `/module/update/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`, {\n    method: 'PUT',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify(payload),\n  });\n}\n\nexport async function deleteModuleRecordGeneric(module, id) {\n  return fetchJsonWithAuth(AUTH_URL, `/module/delete/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`, {\n    method: 'DELETE',\n  });\n}\n\n// Searchable \"City, State, Country\" options from the locationMasters collection\n// — the same source AddFormV1's `location` field type uses. Used by the Location\n// Tax Master screen to pick a real location instead of free-typing one.\nexport async function getLocationMasterOptions(search = '', limit = 50) {\n  const params = new URLSearchParams({ search, limit: String(limit), offset: '0' });\n  const json = await fetchJsonWithAuth(AUTH_URL, `/admin/location-dropdown-values?${params}`);\n  const raw = json?.data ?? json ?? [];\n  return (Array.isArray(raw) ? raw : [])\n    .map((item) => {\n      const label = typeof item === 'string' ? item : (item.label ?? item.value ?? '');\n      return { label, value: label };\n    })\n    .filter((option) => option.label);\n}\n\n// Generic record options for any lookup-configured field — used by the Form\n// Groups editor's \"Conditional Default\" record picker (e.g. choose the default\n// Employer applied when Contract Type is W2). Returns { label, value } pairs\n// where value is the record's valueField (usually _id).\nexport async function getLookupRecordOptions(collection, displayField, valueField = '_id') {\n  const params = new URLSearchParams({\n    collection: String(collection),\n    displayField: String(displayField),\n    valueField: String(valueField),\n  });\n  const json = await fetchJsonWithAuth(AUTH_URL, `/admin/lookup-dropdown-values?${params}`);\n  const raw = json?.data ?? json ?? [];\n  return (Array.isArray(raw) ? raw : [])\n    .map((item) => ({\n      label: item.label ?? item.displayValue ?? item[displayField] ?? String(item.value ?? ''),\n      value: String(item.value ?? item._id ?? item.id ?? ''),\n    }))\n    .filter((option) => option.value && option.label);\n}\n\n// Default Fields — per-module 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\nfunction normalizeRolePermissionValue(value) {\n  if (value && typeof value === 'object' && !Array.isArray(value)) {\n    return normalizeRolePermissionValue(\n      value.value\n      ?? value.access_value\n      ?? value.accessValue\n      ?? value.permissionValue\n      ?? value.enabled,\n    );\n  }\n\n  if (value === true || value === 1 || value === '1') return '1';\n  const normalized = String(value ?? '').trim().toLowerCase();\n  return normalized === 'true' || normalized === 'yes' || normalized === 'active' ? '1' : '0';\n}\n\nfunction normalizeRoleMenusForSave(menus = {}) {\n  if (!menus || typeof menus !== 'object' || Array.isArray(menus)) return menus;\n\n  return Object.fromEntries(\n    Object.entries(menus).map(([menuName, menu]) => {\n      const permissions = Object.fromEntries(\n        Object.entries(menu?.permissions ?? {}).map(([permissionKey, entry]) => [\n          permissionKey,\n          {\n            ...(entry && typeof entry === 'object' && !Array.isArray(entry) ? entry : {}),\n            id: entry?.id ?? entry?.permission_id ?? entry?.permissionId ?? 0,\n            value: normalizeRolePermissionValue(entry),\n          },\n        ]),\n      );\n\n      return [\n        menuName,\n        {\n          ...menu,\n          menuId: menu?.menuId ?? menu?.menu_id ?? 0,\n          permissions,\n        },\n      ];\n    }),\n  );\n}\n\n\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  const result = await fetchJsonWithAuth(AUTH_URL, '/edit-role-details', {\n    method: 'PUT',\n    headers: { 'Content-Type': 'application/json' },\n    // Normalize every permission before saving. This prevents a value loaded as\n    // `value: \"1\"`/boolean true from being silently converted to 0 by another\n    // Admin screen that expects only accessValue/access_value.\n    body: JSON.stringify({ roleId, menus: normalizeRoleMenusForSave(menus) }),\n  });\n\n  // If the edited role is also the role in this browser, PermissionProvider\n  // immediately re-fetches /me/permissions. For other roles this is harmless\n  // and keeps all permission-aware components on one update contract.\n  if (typeof window !== 'undefined') {\n    window.dispatchEvent(new CustomEvent('permissions:changed', { detail: { roleId } }));\n  }\n  return result;\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\n// ── PO/MSA validity settings ─────────────────────────────────────────────────\n\nexport async function getPOMsaConfig() {\n  const json = await fetchJsonWithAuth(AUTH_URL, '/admin/po-msa-config');\n  return json?.data ?? json ?? {};\n}\n\nexport async function savePOMsaConfig(config) {\n  const json = await fetchJsonWithAuth(AUTH_URL, '/admin/po-msa-config', {\n    method: 'PUT',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify(config),\n  });\n  return json?.data ?? json ?? {};\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  // An EMPTY input is not a moment, and must never become one.\n  //\n  // dayjs(undefined) returns the CURRENT time and reports isValid() === true —\n  // so without this guard an untouched picker was serialised as \"now\". On the\n  // submission form that wrote the moment of saving into interviewAvailability\n  // .startTime AND .endTime, giving every submission an interview slot the\n  // recruiter never entered, with both ends identical. dayjs(null) and\n  // dayjs('') are correctly invalid; only `undefined` has this behaviour, which\n  // is exactly the shape a registered-but-empty antd picker hands back.\n  if (value === undefined || value === null || value === '') return null;\n  const d = dayjs(value);\n  if (!d.isValid()) return null;\n  const zone = getAppTimeZone(defaults);\n  // Re-interpret the WALL-CLOCK reading in the target zone, rather than\n  // converting the instant (which would keep the wrong moment and merely\n  // relabel it).\n  return dayjs.tz(d.format('YYYY-MM-DDTHH:mm:ss'), zone);\n}\n\n/**\n * zoneOffsetLabel — \"UTC+05:30\" for a zone, at the current moment.\n * Computed rather than tabulated, so it stays correct across DST.\n */\nexport function zoneOffsetLabel(zone = getAppTimeZone()) {\n  try {\n    const minutes = dayjs().tz(zone).utcOffset();\n    const sign = minutes < 0 ? '-' : '+';\n    const abs = Math.abs(minutes);\n    const hh = String(Math.floor(abs / 60)).padStart(2, '0');\n    const mm = String(abs % 60).padStart(2, '0');\n    return `UTC${sign}${hh}:${mm}`;\n  } catch {\n    return '';\n  }\n}\n\n/**\n * tzLabel — what a viewer sees next to a time so they know WHICH zone they are\n * reading: \"IST (UTC+05:30)\" when an alias names it, else\n * \"Asia/Kolkata (UTC+05:30)\".\n */\nexport function tzLabel(defaults = getDefaults()) {\n  const zone = getAppTimeZone(defaults);\n  const alias = aliasFor(zone, defaults);\n  return `${alias || zone} (${zoneOffsetLabel(zone)})`;\n}\n\n// Built-in aliases covering the abbreviations the requirement names, plus the\n// common business zones. Admin-editable via detailViewDefaults.timeZoneAliases;\n// anything configured there wins, and unknown zones simply have no alias.\nexport const BUILT_IN_TZ_ALIASES = Object.freeze({\n  UTC: 'UTC',\n  'Asia/Kolkata': 'IST',\n  'Asia/Calcutta': 'IST',\n  'Asia/Dubai': 'GST',\n  'America/New_York': 'EST/EDT',\n  'America/Chicago': 'CST/CDT',\n  'America/Denver': 'MST/MDT',\n  'America/Los_Angeles': 'PST/PDT',\n  'Europe/London': 'GMT/BST',\n  'Europe/Berlin': 'CET/CEST',\n  'Asia/Singapore': 'SGT',\n  'Asia/Tokyo': 'JST',\n  'Australia/Sydney': 'AEST/AEDT',\n});\n\n/** aliasFor — the short name for a zone, config first. */\nexport function aliasFor(zone, defaults = getDefaults()) {\n  const configured = defaults?.timeZoneAliases ?? {};\n  return configured[zone] ?? BUILT_IN_TZ_ALIASES[zone] ?? '';\n}\n\n/**\n * listTimeZones — every zone the runtime knows, labelled with its alias and\n * current offset, sorted by offset then name so the picker reads like a map\n * rather than an alphabetical wall.\n *\n * Returns [{ value, label, alias, offsetLabel, offsetMinutes }].\n */\nexport function listTimeZones(defaults = getDefaults()) {\n  let enumerated = [];\n  try {\n    enumerated = Intl.supportedValuesOf('timeZone') ?? [];\n  } catch {\n    // Older runtimes cannot enumerate at all; the union below still yields the\n    // named zones, so the picker is never empty.\n    enumerated = [];\n  }\n\n  // UNION, not just the enumerated list. ICU builds disagree about which name\n  // is canonical: this runtime enumerates \"Asia/Calcutta\" and omits both\n  // \"Asia/Kolkata\" and \"UTC\", yet accepts all three. Listing only what is\n  // enumerated would therefore hide IST and UTC — two of the three zones the\n  // requirement names by hand — on some machines and not others.\n  // Everything is validated, so an alias for a zone this runtime does not know\n  // is dropped rather than offered and then failing at format time.\n  const named = ['UTC', ...Object.keys(BUILT_IN_TZ_ALIASES), ...Object.keys(defaults?.timeZoneAliases ?? {})];\n  const zones = [...new Set([...named, ...enumerated])].filter(isValidZone);\n\n  return zones\n    .map((zone) => {\n      let offsetMinutes = 0;\n      try {\n        offsetMinutes = dayjs().tz(zone).utcOffset();\n      } catch {\n        return null;\n      }\n      const alias = aliasFor(zone, defaults);\n      const offsetLabel = zoneOffsetLabel(zone);\n      return {\n        value: zone,\n        alias,\n        offsetLabel,\n        offsetMinutes,\n        label: `${alias ? `${alias} — ` : ''}${zone} (${offsetLabel})`,\n      };\n    })\n    .filter(Boolean)\n    .sort((a, b) => a.offsetMinutes - b.offsetMinutes || a.value.localeCompare(b.value));\n}\n","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 {\n    Table,\n    TableRow,\n    TableHeader,\n    TableCell,\n} from '@tiptap/extension-table';\n\nimport '../styles/TipTapEditor.css';\n\n// ─── Clipboard table parsing ────────────────────────────────────────────────\n\nconst stripMarkdownCellFormatting = (value = '') => value\n    .trim()\n    .replace(/^\\*\\*(.*?)\\*\\*$/s, '$1')\n    .replace(/^__(.*?)__$/s, '$1');\n\nconst isMarkdownSeparatorRow = (row = []) => (\n    row.length > 0\n    && row.every((cell) => /^:?-{3,}:?$/.test(String(cell).trim()))\n);\n\nconst parseTabularClipboardText = (rawText = '') => {\n    const text = String(rawText || '').replace(/\\r\\n?/g, '\\n').trim();\n    if (!text) return null;\n\n    const lines = text.split('\\n').filter((line) => line.trim().length > 0);\n    if (lines.length < 2) return null;\n\n    // Google Sheets / Excel copy: cells are separated by TABs.\n    if (lines.some((line) => line.includes('\\t'))) {\n        const rows = lines.map((line) => line.split('\\t').map(stripMarkdownCellFormatting));\n        const columnCount = Math.max(...rows.map((row) => row.length));\n        if (columnCount < 2) return null;\n        return {\n            rows: rows.map((row) => [...row, ...Array(columnCount - row.length).fill('')]),\n            hasHeaderRow: true,\n        };\n    }\n\n    // Markdown table copy: | Heading | Value |\n    const pipeRows = lines\n        .filter((line) => line.includes('|'))\n        .map((line) => {\n            let normalized = line.trim();\n            if (normalized.startsWith('|')) normalized = normalized.slice(1);\n            if (normalized.endsWith('|')) normalized = normalized.slice(0, -1);\n            return normalized.split('|').map(stripMarkdownCellFormatting);\n        });\n\n    if (pipeRows.length < 2) return null;\n\n    const separatorIndex = pipeRows.findIndex(isMarkdownSeparatorRow);\n    const rows = pipeRows.filter((_, index) => index !== separatorIndex);\n    const columnCount = Math.max(...rows.map((row) => row.length));\n    if (rows.length < 2 || columnCount < 2) return null;\n\n    return {\n        rows: rows.map((row) => [...row, ...Array(columnCount - row.length).fill('')]),\n        hasHeaderRow: separatorIndex === 1,\n    };\n};\n\nconst createTableNodeFromRows = (schema, rows, hasHeaderRow) => {\n    const { table, tableRow, tableHeader, tableCell, paragraph } = schema.nodes;\n    if (!table || !tableRow || !tableCell || !paragraph) return null;\n\n    const tableRows = rows.map((row, rowIndex) => {\n        const CellType = hasHeaderRow && rowIndex === 0 && tableHeader ? tableHeader : tableCell;\n        const cells = row.map((cellValue) => {\n            const text = String(cellValue ?? '');\n            const paragraphContent = text ? schema.text(text) : undefined;\n            return CellType.create(null, paragraph.create(null, paragraphContent));\n        });\n        return tableRow.create(null, cells);\n    });\n\n    return table.create(null, tableRows);\n};\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 tte-table-tools\">\n                <ToolbarButton\n                    title=\"Insert 3 × 3 table\"\n                    disabled={disabled}\n                    active={editor.isActive('table')}\n                    onClick={() => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()}\n                >\n                    ▦\n                </ToolbarButton>\n\n                {editor.isActive('table') && (\n                    <>\n                        <ToolbarButton\n                            title=\"Add column\"\n                            disabled={disabled || !editor.can().addColumnAfter()}\n                            onClick={() => editor.chain().focus().addColumnAfter().run()}\n                        >\n                            C+\n                        </ToolbarButton>\n                        <ToolbarButton\n                            title=\"Delete column\"\n                            disabled={disabled || !editor.can().deleteColumn()}\n                            onClick={() => editor.chain().focus().deleteColumn().run()}\n                        >\n                            C−\n                        </ToolbarButton>\n                        <ToolbarButton\n                            title=\"Add row\"\n                            disabled={disabled || !editor.can().addRowAfter()}\n                            onClick={() => editor.chain().focus().addRowAfter().run()}\n                        >\n                            R+\n                        </ToolbarButton>\n                        <ToolbarButton\n                            title=\"Delete row\"\n                            disabled={disabled || !editor.can().deleteRow()}\n                            onClick={() => editor.chain().focus().deleteRow().run()}\n                        >\n                            R−\n                        </ToolbarButton>\n                        <ToolbarButton\n                            title=\"Toggle header row\"\n                            disabled={disabled || !editor.can().toggleHeaderRow()}\n                            onClick={() => editor.chain().focus().toggleHeaderRow().run()}\n                        >\n                            H\n                        </ToolbarButton>\n                        <ToolbarButton\n                            title=\"Delete table\"\n                            disabled={disabled || !editor.can().deleteTable()}\n                            onClick={() => editor.chain().focus().deleteTable().run()}\n                        >\n                            🗑\n                        </ToolbarButton>\n                    </>\n                )}\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            Table.configure({\n                resizable: true,\n                HTMLAttributes: {\n                    class: 'email-template-table',\n                },\n            }),\n            TableRow,\n            TableHeader,\n            TableCell,\n        ],\n        content: value,           // set initial content correctly on mount\n        editable: !disabled,\n        editorProps: {\n            attributes: {\n                class: 'tte-content',\n            },\n            handlePaste: (view, event) => {\n                const clipboardText = event.clipboardData?.getData('text/plain') || '';\n                const parsedTable = parseTabularClipboardText(clipboardText);\n                if (!parsedTable) return false;\n\n                const tableNode = createTableNodeFromRows(\n                    view.state.schema,\n                    parsedTable.rows,\n                    parsedTable.hasHeaderRow,\n                );\n                if (!tableNode) return false;\n\n                event.preventDefault();\n                const transaction = view.state.tr.replaceSelectionWith(tableNode, false).scrollIntoView();\n                view.dispatch(transaction);\n                return true;\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 = '', filters = [] } = options;\n  // `scope` is the ONE source being viewed; `selectedSource` may be a comma list\n  // of every ticked source. Splitting the fallback keeps a list from being\n  // normalized into a bogus scope like \"internal,dice\".\n  const inferredScope = normalizeCandidateSearchSource(\n    scope || String(searchParams.selectedSource ?? '').split(','),\n  );\n\n  const query = new URLSearchParams();\n  query.append('module', getBaseModuleName(moduleName));\n  query.append('limit', String(limit));\n  query.append('offset', String(offset));\n\n  if (inferredScope) {\n    query.set('scope', inferredScope);\n    console.log('[searchModuleData] Adding scope to query:', inferredScope);\n  }\n  if (sort) query.set('sort', sort);\n  if (sortDir) query.set('sortDir', sortDir);\n  // Same generic filter mechanism getModuleDataList already sends — reused\n  // here (rather than a second convention) for search-form fields whose value\n  // needs an operator (gte/lte for a date range) instead of a flat param.\n  // No existing caller passes this today, so nothing changes for them.\n  if (Array.isArray(filters) && filters.some((item) => item?.field)) {\n    query.set('filters', JSON.stringify(filters));\n  }\n\n  console.log('[searchModuleData] Final query string:', query.toString());\n\n  Object.entries(searchParams).forEach(([key, value]) => {\n    if (value === undefined || value === null || value === '') return;\n\n    let normalizedValue = value;\n    if (typeof value === 'object') {\n      normalizedValue = value.value ?? value.label ?? value.name ?? String(value);\n    } else {\n      normalizedValue = String(value);\n    }\n\n    query.append(key, normalizedValue);\n  });\n\n  const response = await fetchJsonWithAuth(AUTH_URL, `/module-data-list?${query.toString()}`);\n  // const response = await fetchJsonWithAuth(\"http://localhost:9009/v1\", `/module-data-list?${query.toString()}`);\n  // response = { status, data: { actionRules, actions, columnActions, data: [...records] } }\n  const payload = response?.data ?? response;\n  const rows = Array.isArray(payload)\n    ? payload\n    : payload?.rows ?? payload?.records ?? payload?.items ?? payload?.list ?? payload?.data ?? [];\n\n  const total = extractResponseTotal(response, payload, rows, inferredScope);\n  const counts = extractResponseCounts(response, payload);\n  if (inferredScope && counts[inferredScope] === undefined && Number.isFinite(total)) {\n    counts[inferredScope] = total;\n  }\n\n  // Check if response contains Dice candidates and transform if needed\n  const hasDiceCandidates = rows.some((record) => {\n    const sourceFields = [\n      record?.sourceType,\n      record?.profileSource,\n      record?.selectedSource,\n      record?.customFields?.sourceType,\n      record?.customFields?.profileSource,\n    ].flat();\n    return sourceFields.some(\n      (value) => String(value ?? '').trim().toLowerCase() === 'dice',\n    ) || Boolean(record?.customFields?.diceProfileData);\n  });\n\n  let normalizedRows = rows;\n  if (hasDiceCandidates) {\n    try {\n      const { transformDiceResponseToInternalFormat } = await import('./diceCandidateMapper');\n      const transformed = transformDiceResponseToInternalFormat(response, response);\n      normalizedRows = transformed?.data?.data ?? rows;\n    } catch (error) {\n      console.error('[searchModuleData] Dice transformation failed:', error);\n      normalizedRows = rows.map(normalizeCandidateSearchRow);\n    }\n  } else {\n    normalizedRows = Array.isArray(rows) ? rows.map(normalizeCandidateSearchRow) : [];\n  }\n\n  return {\n    rows: normalizedRows,\n    total: Number(total) || 0,\n    counts,\n    tabs: response?.tabs ?? response?.tabList ?? payload?.tabs ?? payload?.tabList ?? [],\n    fields: response?.fields ?? response?.fieldConfig ?? payload?.fields ?? payload?.fieldConfig ?? [],\n    tabField: response?.tabField ?? payload?.tabField ?? '',\n    actionRules: payload?.actionRules ?? [],\n    actions: payload?.actions ?? [],\n    columnActions: payload?.columnActions ?? [],\n  };\n}\n\nfunction getNestedValue(record, path) {\n  return String(path)\n    .split('.')\n    .reduce((value, key) => value?.[key], record);\n}\n\nfunction setNestedValue(record, path, value) {\n  const keys = String(path).split('.').filter(Boolean);\n  if (!keys.length) return record;\n\n  const nextRecord = { ...record };\n  let target = nextRecord;\n  let source = record;\n\n  keys.slice(0, -1).forEach((key) => {\n    const currentValue = source?.[key];\n    const nextValue = currentValue && typeof currentValue === 'object' && !Array.isArray(currentValue)\n      ? { ...currentValue }\n      : {};\n\n    target[key] = nextValue;\n    target = nextValue;\n    source = currentValue;\n  });\n\n  target[keys[keys.length - 1]] = value;\n  return nextRecord;\n}\n\nfunction normalizeCandidateSearchRow(row) {\n  if (!row || typeof row !== 'object') return row;\n\n  const skillPaths = [\n    'skills',\n    'technicalSkills',\n    'primarySkills',\n    'keySkills',\n    'customFields.diceProfileData.skills',\n  ];\n\n  let nextRow = row;\n  let firstNormalizedSkills = null;\n\n  skillPaths.forEach((path) => {\n    const rawSkills = getNestedValue(nextRow, path);\n    if (rawSkills === undefined || rawSkills === null) return;\n\n    const mappedSkills = normalizeDiceSkills(rawSkills);\n    if (mappedSkills.length === 0 && Array.isArray(rawSkills) && rawSkills.length > 0) return;\n\n    firstNormalizedSkills = firstNormalizedSkills ?? mappedSkills;\n    nextRow = setNestedValue(nextRow, path, mappedSkills);\n  });\n\n  if (firstNormalizedSkills && getNestedValue(nextRow, 'skills') === undefined) {\n    nextRow = setNestedValue(nextRow, 'skills', firstNormalizedSkills);\n  }\n\n  const sourceFields = [\n    nextRow?.sourceType,\n    nextRow?.profileSource,\n    nextRow?.selectedSource,\n    nextRow?.customFields?.sourceType,\n    nextRow?.customFields?.profileSource,\n  ].flat();\n  const isDiceCandidate = sourceFields.some(\n    (value) => String(value ?? '').trim().toLowerCase() === 'dice',\n  ) || Boolean(nextRow?.customFields?.diceProfileData);\n\n  if (isDiceCandidate) {\n    const diceProfileId = resolveDiceProfileId(nextRow);\n    const candidateId = nextRow?.candidateId\n      ?? nextRow?.customFields?.diceProfileData?.candidateId\n      ?? '';\n\n    nextRow = {\n      ...nextRow,\n      id: nextRow?.id ?? diceProfileId,\n      diceId: diceProfileId,\n      candidateId,\n    };\n  }\n\n  return nextRow;\n}\n","import { ensureToken, fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL } from './apiConfig';\n\n// ── Common Module Documents API ───────────────────────────────────────────────\n// Module-agnostic document registry (Auth gateway \"documents\" collection, files\n// in S3). Every module's Add Form registers its uploads here AFTER the module\n// record is created — the module's Create API generates the reference id\n// (jobId / candidateId / submissionId), and that id is what links each document\n// row back to its record.\n\n// Strips the transport decorations collectFileParts may leave on a part key —\n// the moduleWrites routing prefix (\"__mw__<module>__\") and an addRow row-index\n// suffix (\"[0]\") — so the registered fieldName is the clean form-field key\n// (e.g. \"resume\", \"jobDescription\", \"offerLetter\").\nfunction cleanFieldName(formKey) {\n  return String(formKey ?? '')\n    .replace(/^__mw__.*?__/, '')\n    .replace(/\\[\\d+\\]$/, '')\n    .trim() || 'file';\n}\n\n/**\n * uploadModuleDocuments — registers uploaded files against a created module\n * record. Common across ALL modules: pass the module name and the record id\n * its Create API returned.\n *\n * @param moduleName the module key (e.g. \"jobs\", \"candidates\", \"submissions\")\n * @param refId      the created record's _id (jobId / candidateId / …)\n * @param fileParts  [{ formKey, file }] — the shape collectFileParts returns;\n *                   formKey is the form field the file was uploaded against\n * @param metadata   optional { [fieldName]: {...} } extra metadata per field\n */\nexport async function uploadModuleDocuments(moduleName, refId, fileParts = [], metadata = {}) {\n  if (!moduleName) throw new Error('moduleName is required to upload documents');\n  if (!refId) throw new Error('refId (created record id) is required to upload documents');\n  if (!fileParts.length) return { data: [], total: 0 };\n\n  const token = await ensureToken();\n\n  // NOTE: do NOT set Content-Type — the browser must add the multipart\n  // boundary itself (same convention as createModuleRecord).\n  const formData = new FormData();\n  fileParts.forEach(({ formKey, file }) => {\n    const fileName = file?.name ?? undefined;\n    formData.append(cleanFieldName(formKey), file, fileName);\n  });\n  if (metadata && Object.keys(metadata).length > 0) {\n    formData.append('metadata', JSON.stringify(metadata));\n  }\n\n  const params = new URLSearchParams({ module: moduleName, refId: String(refId) });\n  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  // See createModuleRecord: a successful write makes every cached form-groups\n  // payload for this record stale, and the caller usually reads one back\n  // immediately (navigating to the detail view).\n  const { invalidateFormGroupsCache } = await import('./formGroupsCache');\n  invalidateFormGroupsCache();\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  // Any other HTML-looking tag (\"<b>\", \"<div class=x>\", \"</span>\", …) — plain\n  // fields never legitimately contain markup; only a text-editor field (rich\n  // text, sanitized separately via DOMPurify below) is allowed real tags, so\n  // this is appended AFTER the slice(0,3) rich-text safety check reads from.\n  { re: /<\\/?[a-zA-Z][a-zA-Z0-9]*(?:\\s[^<>]*)?>/, message: 'HTML tags 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","// validationOverride — an admin-configured \"I know, let it through\" checkbox\n// that suspends named validations on ONE field.\n//\n// WHY THIS EXISTS\n// A cross-field cap is right almost always and wrong occasionally. A submission\n// rate may not exceed the job's client rate — except when the account manager\n// has actually agreed a higher one, and the recruiter in front of the record\n// knows that and the config does not. Before this, the only ways out were to\n// weaken the rule for everyone or to edit the job. Both lose the signal.\n//\n// So the rule STAYS, and the exception becomes explicit, deliberate and\n// recorded: a checkbox beside the field's label, a confirmation that says\n// plainly what is being given up, and a stored flag on the record saying an\n// override was taken.\n//\n// Entirely config-driven (FormGroupField.overrideCheckbox). Nothing here names\n// a module, a field or a rate — any field may gate any of its own validation\n// types this way.\n//\n//   overrideCheckbox: {\n//     label:           \"Rate agreed above the job's client rate\",\n//     field:           \"candidateRateOverride\",   // where the flag is stored\n//     skipValidations: [\"maxFieldWithFallback\"],  // which rule TYPES it lifts\n//     confirmTitle:    \"…\", confirmBody: \"…\",\n//     confirmOkText:   \"…\", confirmCancelText: \"…\",\n//     store:           true,                      // persist the flag (default)\n//   }\n\nconst truthy = (v) => v === true || v === 1 || v === '1' || v === 'true';\n\n/** The config block, or null when this field has no override checkbox. */\nexport function overrideConfig(field) {\n  const cfg = field?.overrideCheckbox;\n  if (!cfg || typeof cfg !== 'object') return null;\n  // A checkbox that lifts nothing is not a checkbox — it would render a control\n  // that visibly does nothing, which is worse than not rendering it.\n  return overrideSkips(field).size > 0 ? cfg : null;\n}\n\n/**\n * The form/payload key holding the flag.\n *\n * Defaults to a deterministic key derived from the field's own key, so an admin\n * who only ticks \"add an override checkbox\" gets a working, collision-free flag\n * without having to invent a name. Dots become underscores: the flag is a flat\n * sibling, not a leaf inside the field's own container.\n */\nexport function overrideFieldKey(field) {\n  const explicit = String(field?.overrideCheckbox?.field ?? '').trim();\n  if (explicit) return explicit;\n  return `${String(field?.field ?? '').replace(/\\./g, '_')}__override`;\n}\n\n/** Validation TYPES this checkbox suspends, as a Set. */\nexport function overrideSkips(field) {\n  const raw = field?.overrideCheckbox?.skipValidations;\n  const list = Array.isArray(raw)\n    ? raw\n    : String(raw ?? '').split(',');\n  return new Set(list.map((t) => String(t ?? '').trim()).filter(Boolean));\n}\n\n/** Should the flag be written to the record? Defaults to YES — it is an audit fact. */\nexport function overrideIsStored(field) {\n  return field?.overrideCheckbox?.store !== false;\n}\n\n/**\n * The absolute name path of the flag, scoped exactly like a cross-field rule's\n * referenced key: the same addRow row when the field sits in one, top level\n * otherwise. So a per-row override stays that row's own.\n */\nexport function overrideFlagPath(field, name, prefixFor) {\n  const key = overrideFieldKey(field);\n  // A row's own override, when the field sits in a repeatable group — ticking\n  // it on one row must not lift the limit on every other row.\n  if (typeof prefixFor === 'function') {\n    const prefix = prefixFor(key);\n    if (Array.isArray(prefix) && prefix.length) return [...prefix, key];\n  }\n  // prefixFor answers for the GROUP'S OWN field keys, and the flag is not one\n  // of them, so it declines (null) even inside a row. Recover the row from the\n  // field's absolute path instead: inside a Form.List it is\n  // [listName, rowIndex, …].\n  if (Array.isArray(name) && name.length >= 3 && typeof name[1] === 'number') {\n    return [name[0], name[1], key];\n  }\n  // Deliberately NOT resolveScopedFieldPath's sibling fallback: for a dotted\n  // field (\"candidateBudget.candidateBudgetStart\") that would bury the flag\n  // INSIDE the field's own container — which, for an appendHistory container,\n  // means inside each pushed history entry rather than on the record. The flag\n  // is a flat sibling at the field's scope root, which is also exactly where\n  // the forms register its hidden Form.Item.\n  return [key];\n}\n\n/** Is the override currently taken, per live form state? */\nexport function overrideActive(form, field, name, prefixFor) {\n  if (!overrideConfig(field)) return false;\n  try {\n    return truthy(form?.getFieldValue?.(overrideFlagPath(field, name, prefixFor)));\n  } catch {\n    return false;\n  }\n}\n\n/**\n * gateRuleByOverride — wrap ONE built rule so it stands down while the override\n * is taken.\n *\n * Returned as antd's FUNCTION form of a rule, which antd re-evaluates on every\n * validation pass against the CURRENT form. That is what makes the suspension\n * live: ticking the box clears the error without re-rendering the rule list,\n * and unticking it brings the rule straight back. An object rule captured at\n * render time could not do either.\n *\n * Works for declarative rules ({pattern}, {max}, …) and validator rules alike —\n * neither is inspected, only returned or withheld.\n */\nexport function gateRuleByOverride(builtRule, flagPath) {\n  return (formInstance) => {\n    let active = false;\n    try {\n      active = truthy(formInstance?.getFieldValue?.(flagPath));\n    } catch {\n      active = false;\n    }\n    // `{}` is antd's no-op rule: it validates nothing and never rejects.\n    return active ? {} : builtRule;\n  };\n}\n\n/**\n * applyOverrideGating — the single call site each form's getRules makes.\n *\n * Takes the RAW admin validations (which still carry their `type`) and returns\n * a mapper that gates only the types this field's checkbox names, leaving every\n * other rule exactly as it was.\n */\nexport function overrideRuleMapper(field, name, prefixFor, buildRule) {\n  const cfg = overrideConfig(field);\n  if (!cfg) return buildRule;\n  const skips = overrideSkips(field);\n  const flagPath = overrideFlagPath(field, name, prefixFor);\n  return (validation) => {\n    const built = buildRule(validation);\n    const type = typeof validation === 'string' ? validation : validation?.type;\n    return skips.has(type) ? gateRuleByOverride(built, flagPath) : built;\n  };\n}\n\n/** Dialog copy, with defaults that read as a deliberate, reversible decision. */\nexport function overrideConfirmCopy(field) {\n  const cfg = overrideConfig(field) ?? {};\n  const label = field?.label ?? field?.field ?? 'this field';\n  return {\n    tone: cfg.tone ?? 'overwrite',\n    title: cfg.confirmTitle || 'Remove the validation on this field?',\n    body: cfg.confirmBody\n      || `The check that keeps ${label} within its allowed limit will be turned off for this record, `\n      + 'and a value above that limit will be accepted. The override is recorded against the record. '\n      + 'Do you want to continue?',\n    okText: cfg.confirmOkText || 'Yes, remove the limit',\n    cancelText: cfg.confirmCancelText || 'Keep the limit',\n  };\n}\n\nexport default {\n  overrideConfig,\n  overrideFieldKey,\n  overrideSkips,\n  overrideIsStored,\n  overrideFlagPath,\n  overrideActive,\n  gateRuleByOverride,\n  overrideRuleMapper,\n  overrideConfirmCopy,\n};\n","// optionMatching — snap an incoming value onto a field's CONFIGURED option.\n//\n// Any value that arrives from outside the form (an AI parse of a JD or resume,\n// an edit prefill, a cross-module prefill, an import) is free text. A select /\n// radio / checkbox control only selects when the value is character-for-\n// character one of its configured option values, so \"onsite\", \"ONSITE\",\n// \"On Site\" and \"on_site\" all silently failed to select the \"On-Site\" radio —\n// the parse looked like it had worked while the control sat empty.\n//\n// Matching is deliberately CONSERVATIVE and lossless:\n//   • only fields that actually declare options are touched;\n//   • an unmatched value is returned UNCHANGED, never blanked — a value we\n//     cannot map is still shown to the user (and still saved) rather than\n//     silently dropped;\n//   • matching never invents a selection: it compares against the option's own\n//     value and label only, plus whatever aliases the admin configured.\n//\n// Everything here is config-driven; no module, field or option name appears.\n\n// canonical — the comparison key. Case-folded, accent-folded and stripped of\n// every non-alphanumeric character, so \"On-Site\" / \"on site\" / \"ON_SITE\" /\n// \"onsite\" all collapse onto \"onsite\". Digits are kept so \"1\" ≠ \"10\".\nexport function canonical(value) {\n  return String(value ?? '')\n    .normalize('NFKD')\n    .replace(/[\\u0300-\\u036f]/g, '')\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '');\n}\n\n// optionEntries — the (canonicalKey → optionValue) pairs a field offers.\n// Both the option's value and its label are accepted as input, because a parse\n// far more often echoes the human label (\"Full Time\") than the stored value.\n// field.optionAliases lets an admin add synonyms the data uses but the option\n// list does not spell out, e.g. { \"WFH\": \"Remote\", \"work from home\": \"Remote\" }.\nexport function optionEntries(field) {\n  const pairs = new Map();\n  const add = (key, target) => {\n    const k = canonical(key);\n    // First writer wins: an earlier option keeps the key when a later option's\n    // label happens to collapse onto the same string.\n    if (k && !pairs.has(k)) pairs.set(k, target);\n  };\n\n  (Array.isArray(field?.options) ? field.options : []).forEach((option) => {\n    if (option === null || option === undefined) return;\n    const value = typeof option === 'object' ? option.value : option;\n    if (value === undefined || value === null || value === '') return;\n    add(value, value);\n    if (typeof option === 'object' && option.label !== undefined) add(option.label, value);\n  });\n\n  Object.entries(field?.optionAliases ?? {}).forEach(([alias, target]) => {\n    // An alias may only point AT a real option — otherwise a typo in config\n    // would inject a value the control cannot select, which is the exact class\n    // of bug this module exists to remove.\n    const resolved = pairs.get(canonical(target));\n    if (resolved !== undefined) add(alias, resolved);\n  });\n\n  return pairs;\n}\n\nconst hasOptions = (field) => Array.isArray(field?.options) && field.options.length > 0;\n\n// snapOne — map a single scalar onto an option value, or return it unchanged.\nfunction snapOne(pairs, value) {\n  if (value === undefined || value === null || value === '') return value;\n  // An object (a resolved reference like { id, value }) is not free text — the\n  // reference resolver already owns it, so leave it entirely alone.\n  if (typeof value === 'object') return value;\n  const match = pairs.get(canonical(value));\n  return match === undefined ? value : match;\n}\n\n// snapToOption — the entry point. Arrays map element-wise so a multi-select or\n// a checkbox group snaps every entry. Returns the input untouched for fields\n// with no options, which is most of them.\nexport function snapToOption(field, value) {\n  if (!hasOptions(field)) return value;\n  const pairs = optionEntries(field);\n  if (pairs.size === 0) return value;\n  if (Array.isArray(value)) return value.map((item) => snapOne(pairs, item));\n  return snapOne(pairs, value);\n}\n\n// unmatchedOptionValues — the entries that could NOT be mapped onto an option.\n// Callers use it to tell the user what a parse failed to place, instead of\n// leaving a control looking mysteriously empty.\nexport function unmatchedOptionValues(field, value) {\n  if (!hasOptions(field)) return [];\n  const pairs = optionEntries(field);\n  const list = Array.isArray(value) ? value : [value];\n  return list.filter((item) => item !== undefined && item !== null && item !== ''\n    && typeof item !== 'object'\n    && !pairs.has(canonical(item)));\n}\n","/**\n * payloadTransformer — the single, configuration-driven payload engine shared by\n * AddFormV1 and EditFormV1.\n *\n * Goal: the Admin form-group config is the ONLY source of truth for how a form\n * value becomes an API payload value. There is ZERO field-name / module-specific\n * branching here. Every behaviour is driven by these per-field config keys:\n *\n *   payloadKey      output key (dot-notation allowed). Default: field.field\n *   dataType        auto | string | number | boolean | array | object | date | custom\n *   elementType     for dataType \"array\": coerce each element (e.g. \"number\")\n *   payloadMode     auto | value | label | object | custom | template | skip\n *   valueKey        which key holds the id/value on an option object (default: value/id/_id…)\n *   displayKey      which key holds the label on an option object (default: label/name…)\n *   payloadTemplate object template for custom/template modes, with {{value}} {{label}} {{raw}} tokens\n *   defaultValue    value substituted when the form value is empty\n *   transformRule   { map: {...}, default, name } — value maps / named transforms\n *   omitEmpty       drop the key entirely when the final value is empty\n *\n * Nothing here knows about \"priority\", \"recruiters\", \"noticePeriod\", etc. Those\n * are expressed purely through the config above.\n *\n * Backward compatibility: when a field carries NONE of the new keys, the engine\n * falls back to the historical generic behaviour — dot-notation nesting, dayjs →\n * ISO string, scalar pass-through — so existing forms keep working unchanged.\n */\n\nimport dayjs from 'dayjs';\nimport { fromAppInput, shouldConvertToZone } from '../../services/timezone';\nimport { securePayload } from './inputSecurity';\nimport { overrideConfig, overrideFieldKey, overrideIsStored } from './validationOverride';\nimport { snapToOption } from './optionMatching';\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  // An admin-configured validation override (field.overrideCheckbox) writes its\n  // own flag alongside the field, in whatever scope the field itself lives in\n  // (flat payload or one addRow row). Recorded because \"someone deliberately\n  // lifted this limit\" is an audit fact about the record, not a UI detail —\n  // without it a value that breaks a documented rule looks like the rule failed.\n  // `store: false` opts out for a purely transient override.\n  const overrideCfg = overrideConfig(field);\n  if (overrideCfg && overrideIsStored(field)) {\n    const flagKey = overrideFieldKey(field);\n    if (hasFormValue(scope, flagKey)) {\n      setDeep(target, flagKey, Boolean(readFormValue(scope, flagKey)));\n    }\n  }\n\n  // isUploadField, NOT `type === 'file'`. Admin config in the wild types an\n  // upload as \"document\"/\"image\"/\"upload\"/\"attachment\" just as often, and those\n  // aliases used to MISS this branch entirely: the antd fileList\n  // ([{uid,name,stored,…}]) then fell through to the ordinary scalar path and\n  // was written to the payload as the field's value, overwriting the stored file\n  // container with UI junk — or, when the user had not touched the field and it\n  // held an empty list, writing [] over it. That is the \"an uploaded document\n  // disappears when you edit the record\" bug: the update wiped the container the\n  // form never intended to change. Every upload type now takes the branch below,\n  // whose contract is \"carry the stored reference back, or write NOTHING\" —\n  // never null, never an empty array, in any scope (flat field, addRow row, or\n  // indexed document row: buildRowObject routes through this same function).\n  if (isUploadField(field)) {\n    // A NEW upload rides the multipart request (collectFileParts) and the\n    // downstream handler writes its stored shape — nothing to do here.\n    // An ALREADY-STORED file has no File object to upload, so without carrying\n    // its reference the record ends up with no document at all: that is why a\n    // Quick Submit prefilled from a previous submission lost every document it\n    // showed in the form. Write back the untouched original container, merged\n    // so sibling scalars written from the same container (passport.number,\n    // passport.expiry) survive regardless of field order.\n    const carried = carriedFileValue(readFormValue(scope, field.field));\n    if (carried !== undefined) {\n      // At the top level the fileKey names the stored container (\"passport\",\n      // \"resume\"); inside an addRow row it is the multipart part name for the\n      // whole group, so only the row's own key applies there.\n      const inRow = root !== undefined && root !== scope;\n      const key = field.payloadKey || (inRow ? field.field : (field.fileKey || field.field));\n      mergeDeepAt(target, key, carried);\n    }\n    return;\n  }\n\n  // showIf gating at PAYLOAD time: a field whose visibility condition fails is\n  // not stored. antd preserves unmounted Form.Item values, so without this a\n  // value typed before the condition flipped (e.g. VMS Commission after VMS\n  // Required was unchecked) would silently ride along. Writing null (rather\n  // than skipping) also clears the stale stored value on edit; inside an\n  // addRow row the whole row array is rewritten anyway, so the key simply\n  // drops out of the row object. Visibility is TRANSITIVE: a field whose gate\n  // field is itself hidden drops too (fieldVisibleForPayload).\n  if (showIfConditionFields(field.showIf).length && !fieldVisibleForPayload(field, scope, root, fieldIndex)) {\n    // Top-level (scope === root): write null so an edit clears the stale\n    // stored value. Row scope: the row array is rewritten wholesale, so simply\n    // omitting the key removes it from the stored row.\n    if ((root === undefined || root === scope) && hasFormValue(scope, field.field)) {\n      setDeep(target, field.payloadKey || field.field, null);\n    }\n    return;\n  }\n\n  // Repeatable field-GROUP (subFields): each Form.List row is an object of the\n  // sub-fields, so this field submits as an array of row objects —\n  // [{question, answer}, …] — under its own payloadKey. Recurses so a nested\n  // repeat group (e.g. inside an addRow row) is shaped element-by-element.\n  if (Array.isArray(field.subFields) && field.subFields.length > 0) {\n    if (!hasFormValue(scope, field.field)) return;\n    const rows = readFormValue(scope, field.field);\n    if (!Array.isArray(rows)) return;\n    const rowObjects = rows\n      .map((row) => buildRowObject(field.subFields, row, root, fieldIndex))\n      .filter((obj) => obj && Object.keys(obj).length > 0);\n    setDeep(target, field.payloadKey || field.field, rowObjects);\n    return;\n  }\n\n  if (!hasFormValue(scope, field.field)) return; // not in this submit — leave base untouched\n\n  // Repeatable SCALAR field (addRow, no subFields): each Form.List item is one\n  // value, so normalise every element through the field's single-value shape\n  // (dates → ISO, select → id) instead of relying on a blanket array coercion.\n  // This makes a repeat field work INSIDE an addRow row (subjects[] per row) as\n  // well as at the top level.\n  if (truthy(field.addRow) && (!Array.isArray(field.subFields) || field.subFields.length === 0)) {\n    const raw = readFormValue(scope, field.field);\n    const arr = Array.isArray(raw) ? raw : isEmpty(raw) ? [] : [raw];\n    const single = singleFieldOf(field);\n    const out = arr\n      .map((el) => shapeFieldValue(single, el))\n      .filter((r) => r.kind !== 'skip')\n      .map((r) => r.out);\n    setDeep(target, field.payloadKey || field.field, out);\n    return;\n  }\n\n  const raw = readFormValue(scope, field.field);\n  const result = shapeFieldValue(field, raw);\n  if (result.kind === 'skip') return;\n  if (result.kind === 'spread' && isPlainObject(result.out)) {\n    mergeDeep(target, result.out);\n    return;\n  }\n  setDeep(target, field.payloadKey || field.field, result.out);\n}\n\n// buildRowObject — shape one addRow row into a plain object keyed by each\n// field's payloadKey/field (relative to the row). File fields are skipped (they\n// upload separately and their metadata is merged server-side).\nfunction buildRowObject(groupFields, row, root, fieldIndex) {\n  const obj = {};\n  groupFields.forEach((field) => writeField(field, row, obj, root ?? row, fieldIndex));\n  return obj;\n}\n\nfunction deleteDeep(obj, path) {\n  const parts = String(path).split('.');\n  let cur = obj;\n  for (let i = 0; i < parts.length - 1; i += 1) {\n    if (!isPlainObject(cur[parts[i]])) return;\n    cur = cur[parts[i]];\n  }\n  delete cur[parts[parts.length - 1]];\n}\n\n/**\n * buildInitialValue — reverse direction, for EditForm prefill.\n * Given a field config and the stored value (which may be a scalar, an\n * { id, value } reference, an { id, name } object, or an array of those),\n * returns the value the input control expects.\n *\n *   - date/time fields → dayjs\n *   - select/radio/checkbox → the id/value (single) or array of ids (multi)\n *   - everything else → the scalar\n *\n * This replaces convertInitialValue's hardcoded field-name handling.\n */\n// Controls that render exactly one primitive. Deliberately excludes select /\n// lookup / reference / file types, whose values are legitimately objects.\nconst SCALAR_CONTROL_TYPES = new Set([\n  'text', 'textarea', 'email', 'phone', 'tel', 'number', 'date', 'time', 'datetime', 'password',\n]);\n\nexport function buildInitialValue(field, stored) {\n  const configuredEmpty = (value) => {\n    const emptyValues = Array.isArray(field.emptyValues) ? field.emptyValues : [];\n    return emptyValues.some((v) => String(v) === String(value));\n  };\n  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: 'Alphanumeric & Spaces',                 value: 'alphanumericSpace' },\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  alphanumericSpace:          /^[a-zA-Z0-9\\s]$/,\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 'alphanumericSpace': {\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 and spaces.`;\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(/&nbsp;/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 — resolve audit `updatedBy` ids strictly from MongoDB users.\n//\n// Rate-history entries store the actor as an id. User identity for this view\n// must come from the MongoDB `users` module, not the legacy MySQL user-detail\n// endpoint. The Mongo row can expose the same person through legacyUserId,\n// userId, _id, or id, so all of those identities are indexed.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport { fetchJsonWithAuth } from '../../services/authApi';\nimport { AUTH_URL } from '../../services/apiConfig';\n\nexport function userDisplayName(user) {\n  if (!user || typeof user !== 'object') return '';\n  const firstName = user.firstName ?? user.first_name ?? user.FIRST_NAME;\n  const lastName = user.lastName ?? user.last_name ?? user.LAST_NAME;\n  return [firstName, lastName].filter(Boolean).join(' ').trim()\n    || user.name\n    || user.userName\n    || user.user_name\n    || user.username\n    || user.email\n    || '';\n}\n\nfunction idText(value) {\n  if (value === undefined || value === null || value === '') return '';\n  if (typeof value === 'object') {\n    return String(value.$oid ?? value.id ?? value._id ?? '').trim();\n  }\n  return String(value).trim();\n}\n\nfunction userIds(user = {}) {\n  return [\n    user.legacyUserId,\n    user.userId,\n    user.user_id,\n    user._id,\n    user.id,\n  ].map(idText).filter(Boolean);\n}\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    userIds(user).forEach((key) => {\n      if (map[key] === undefined) map[key] = name;\n    });\n  });\n  return map;\n}\n\n// One raw Mongo users-directory request is shared by every unresolved actor.\n// IMPORTANT: do not replace this with /get-detailed-view; that endpoint is the\n// legacy user-detail path and can resolve an id against MySQL instead of the\n// MongoDB users collection used by Zinnext.\nlet mongoUsersPromise;\nfunction getMongoUsers() {\n  if (!mongoUsersPromise) {\n    mongoUsersPromise = fetchJsonWithAuth(AUTH_URL, '/module/list?module=users')\n      .then((res) => {\n        const value = res?.data?.data ?? res?.data ?? [];\n        return Array.isArray(value) ? value : [];\n      })\n      .catch(() => []);\n  }\n  return mongoUsersPromise;\n}\n\n// Session cache: actor id → Promise<string>.\nconst userNameCache = new Map();\n\n/**\n * Resolve an audit actor from MongoDB users only.\n * Prefer legacyUserId/userId matches because rate-history `updatedBy` is\n * normally an auth/legacy numeric id. ObjectId/id matching is retained for\n * records stamped by newer flows.\n */\nexport function fetchUserName(userId) {\n  const key = idText(userId);\n  if (!key) return Promise.resolve('');\n  if (userNameCache.has(key)) return userNameCache.get(key);\n\n  const request = getMongoUsers().then((rows) => {\n    const exact = rows.find((user) => (\n      idText(user?.legacyUserId) === key\n      || idText(user?.userId) === key\n      || idText(user?.user_id) === key\n    ));\n    if (exact) return userDisplayName(exact);\n\n    const objectMatch = rows.find((user) => (\n      idText(user?._id) === key || idText(user?.id) === key\n    ));\n    return userDisplayName(objectMatch);\n  }).catch(() => '');\n\n  userNameCache.set(key, request);\n  return request;\n}\n\nexport function primeUserName(userId, name) {\n  userNameCache.set(String(userId), Promise.resolve(name));\n}\n\nexport function clearUserNameCache() {\n  userNameCache.clear();\n  mongoUsersPromise = undefined;\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/**\n * readCompareValue — read ONE compare field's current value, tolerantly.\n *\n * The ceiling for these rules is routinely parked in a HIDDEN carrier field\n * (show:0, payloadMode:\"skip\") that some other mechanism fills in — on a\n * submission, the job's client rate arrives that way via prefillFromModule.\n * A carrier is never rendered, so its value only ever reaches the store\n * through setFieldsValue, and it can land in one of two shapes:\n *\n *   • the NESTED name path  {jobClientRate: {clientRateTo: 68}}   — correct\n *   • the LITERAL dotted key {\"jobClientRate.clientRateTo\": 68}   — what a\n *     plain `values[field.field] = …` assignment produces, since rc-field-form\n *     treats a dotted string as ONE field name rather than a path\n *\n * Only the first is visible to a name-path lookup. Reading just that one means\n * a carrier written the other way is invisible, `selectCeiling` finds nothing,\n * and the rule FAILS OPEN — the cap silently disappears and an over-limit value\n * is accepted with no error at all. Which is strictly worse than having no cap:\n * the rule looks present in config and enforces nothing.\n *\n * So both shapes are read, nested first. Costs one extra store lookup and makes\n * the cap independent of how its ceiling happened to be written.\n */\nexport function readCompareValue(form, currentName, compareField) {\n    const nested = form?.getFieldValue?.(resolveCompareFieldName(currentName, compareField));\n    if (nested !== undefined && nested !== null && nested !== '') return nested;\n    return form?.getFieldValue?.(compareField);\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","// ValidationOverrideCheckbox — the control half of validationOverride.js.\n//\n// Renders beside a field's LABEL (not under the input) because it changes what\n// the field means, not what it holds: \"this value is allowed to break its\n// limit\" belongs with the name of the thing, the way a qualifier does.\n//\n// Turning the limit OFF asks first — that direction removes a safeguard, so it\n// is the one that deserves a sentence explaining what is being given up.\n// Turning it back ON is silent: restoring a check needs no permission.\n//\n// Both directions re-validate the field immediately, so the error appears or\n// clears the moment the box is used, rather than at the next submit.\nimport { Checkbox, Form, Tooltip } from 'antd';\nimport { openFormDecision } from './formDecisionDialog';\nimport { overrideConfig, overrideConfirmCopy } from './validationOverride';\n\nexport default function ValidationOverrideCheckbox({ form, field, flagPath, fieldPath, disabled }) {\n  const cfg = overrideConfig(field);\n  const checked = Form.useWatch(flagPath, form);\n  if (!cfg) return null;\n  const label = cfg.label || 'Allow beyond the limit';\n\n  const revalidate = () => {\n    // Deferred: antd must see the new flag value before the rules re-run, and\n    // the rules read it through getFieldValue at validation time.\n    setTimeout(() => {\n      form?.validateFields?.([fieldPath]).catch(() => { });\n    });\n  };\n\n  const onChange = async (event) => {\n    const next = event.target.checked;\n    if (!next) {\n      form.setFieldValue(flagPath, false);\n      revalidate();\n      return;\n    }\n    const confirmed = await openFormDecision(overrideConfirmCopy(field));\n    // A declined confirmation must leave the box exactly as it was. antd has\n    // already painted it checked by this point, so the value is written back\n    // explicitly rather than simply not-set.\n    form.setFieldValue(flagPath, Boolean(confirmed));\n    revalidate();\n  };\n\n  // Label on HOVER, not inline. A rate field's label line is already carrying a\n  // name, a required mark and (on a combined block) three inputs beside it; a\n  // full sentence wedged in there crowds the one thing the user came to read.\n  // The box stays visible because the OPTION has to be discoverable — only its\n  // explanation is deferred to hover/focus, where someone who wants it will\n  // look. The same text is the accessible name, so it is never hover-only for\n  // a screen reader or a keyboard user.\n  const title = cfg.help ? `${label} — ${cfg.help}` : label;\n\n  return (\n    <Tooltip title={title}>\n      <span className=\"v1-override-wrap\">\n        <Checkbox\n          className=\"v1-override-checkbox\"\n          checked={Boolean(checked)}\n          disabled={disabled}\n          onChange={onChange}\n          aria-label={title}\n        />\n      </span>\n    </Tooltip>\n  );\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","/*\n * Dependent-lookup staleness guard.\n *\n * A lookup field scoped by `lookupFilterValueFrom` (Jobs → Contact Person is\n * scoped to the selected Client) must not keep a value that was chosen under a\n * DIFFERENT parent.\n *\n * Changing the parent already refetches the OPTIONS, but the field's own VALUE\n * used to survive that: pick client A's contact \"Naveen\", switch to client B,\n * and A's contact id was still sitting on the form. It went unnoticed because a\n * nested reference id is only unique WITHIN its parent —\n * `clientContact.clientContactId` is a per-client sequence (1, 2, 3 …) — so the\n * orphaned id still resolved against the new client, to a DIFFERENT person, and\n * saved looking perfectly valid. A wrong name that looks right is worse than a\n * blank one.\n *\n * Kept as a pure function so the rule can be tested without rendering\n * AddFormV1/EditFormV1 (whose render pulls in pdfjs → DOMMatrix, which jsdom\n * does not have).\n */\n\n// \"This dependent has not observed its parent yet\", so the FIRST value seen\n// never counts as a change. undefined/null cannot serve as the sentinel: an\n// unselected parent is legitimately undefined on mount.\nexport const PARENT_FILTER_UNSEEN = Symbol('parentFilterUnseen');\n\nconst empty = (v) => v === undefined || v === null || v === '';\n\n// Filter values arrive as ObjectId hex strings, numbers, or boxed objects\n// depending on the source, so compare by string form — otherwise a re-render\n// that merely reboxes the same id would read as a parent change and wipe a\n// perfectly valid choice.\nexport const sameFilterValue = (a, b) => String(a ?? '') === String(b ?? '');\n\n/**\n * Should a dependent lookup drop its current value?\n *\n * @param {object}  args\n * @param {*}       args.previous      last observed parent value, or PARENT_FILTER_UNSEEN\n * @param {*}       args.next          the parent value now\n * @param {*}       args.currentValue  what the dependent field currently holds\n * @param {boolean} args.scoped        field has lookupFilterField + lookupFilterValueFrom\n */\nexport function shouldClearDependentValue({ previous, next, currentValue, scoped = true }) {\n  // Not a scoped lookup — it has no parent, so nothing can invalidate it.\n  if (!scoped) return false;\n  // First observation: record, never clear. This is what keeps EDIT PREFILL\n  // (which sets parent and child in the same pass) from wiping itself.\n  if (previous === PARENT_FILTER_UNSEEN) return false;\n  if (sameFilterValue(previous, next)) return false;\n  // Parent only just became known — nothing was selectable under it before, so\n  // whatever is in the field came from prefill, not from the old parent.\n  if (empty(previous)) return false;\n  // Nothing to clear.\n  if (empty(currentValue)) return false;\n  return true;\n}\n","// Shared role/permission gate used by row actions (ListView, DetailHeaderCard)\n// and, via AddFormV1/EditFormV1, by field-level visiblePermission/editablePermission.\n// A permission value looks like \"<module>.<key>\" (e.g. \"submission.viewRate\").\n//\n// 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.\n// `root` narrows the DOM query below to a subtree — pass it when the form\n// isn't the only one on the page (e.g. one portaled into a Modal via\n// rootClassName), since a plain document-wide query returns the FIRST\n// matching `.ant-form-item-has-error` in document order, which may belong to\n// an unrelated form elsewhere on the page rather than this one.\nexport function scrollToFirstFormError({ errorFields } = {}, form, { root = document } = {}) {\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 = (root ?? 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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"}