@realtek/core-theme 0.0.261 → 0.0.263
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/Breadcrumb-CPjRRUg1.cjs +3 -0
- package/dist-lib/{Breadcrumb-BV5DtmmO.cjs.map → Breadcrumb-CPjRRUg1.cjs.map} +1 -1
- package/dist-lib/{Breadcrumb-Db_WFy70.js → Breadcrumb-DoifsTga.js} +248 -231
- package/dist-lib/{Breadcrumb-Db_WFy70.js.map → Breadcrumb-DoifsTga.js.map} +1 -1
- package/dist-lib/index.cjs +7 -7
- package/dist-lib/index.cjs.map +1 -1
- package/dist-lib/index.js +11154 -11055
- package/dist-lib/index.js.map +1 -1
- package/package.json +1 -1
- package/dist-lib/Breadcrumb-BV5DtmmO.cjs +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Breadcrumb-BV5DtmmO.cjs","names":[],"sources":["../src/theme/colors/colors.js","../src/components/typography/Typography.jsx","../src/components/TipTapEditor.jsx","../src/components/DocumentViewer.jsx","../src/utils/modulePath.js","../src/services/moduleDataApi.js","../src/services/addFormApi.js","../src/services/adminApi.js","../src/services/documentApi.js","../src/services/detailedViewApi.js","../src/services/detailDefaults.js","../src/utils/roleFormPermissionGate.js","../src/components/form/inputSecurity.js","../src/components/form/optionMatching.js","../src/components/form/payloadTransformer.js","../src/components/form/linkedAddRowGroups.js","../src/services/aiActionApi.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/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/optionConstraints.js","../src/components/form/fieldTooltip.js","../src/utils/roleAllowsAction.js","../src/utils/afterSubmitNav.js","../src/utils/submitMessages.js","../src/utils/scrollToFirstFormError.js","../src/components/form/conditionalFieldLabel.js","../src/components/Breadcrumb.jsx"],"sourcesContent":["export const colors = {\n brand: '#0053a5',\n brandDark: '#1d4ed8',\n brandDarker: '#1e40af',\n brandHover: '#004f85',\n brandSoft: '#dfedf7',\n brandSofter: '#e7f2fa',\n brandSubtle: '#edf8fe',\n\n textPrimary: '#111827',\n textSecondary: '#4b5563',\n textMuted: '#6b7280',\n textSubtle: '#010306',\n textHeading: '#142235',\n textDark: '#232a31',\n textPlaceholder: '#a7b0bb',\n textInverse: '#ffffff',\n textLink: '#0053a5',\n\n surfacePage: '#f5f6fa',\n surfaceSoft: '#f8fafc',\n surfaceSofter: '#f3f8fb',\n surfaceCard: '#ffffff',\n surfaceHover: '#eff6ff',\n surfaceHoverLight: '#f8fcff',\n surfaceHoverSoft: '#f5fbff',\n surfaceSelected: '#dbeafe',\n surfaceControl: '#f4f8fb',\n surfaceRowAlt: '#fbfdff',\n\n border: '#e6f0ff',\n borderLight: '#eef3f8',\n borderSofter: '#f0f0f0',\n borderMuted: '#d8e4ef',\n borderInput: '#d5dde5',\n borderFocus: '#7dbce6',\n borderHover: '#99c7e8',\n controlBorder: '#77abd0',\n controlBorderMuted: '#e7edf3',\n controlAccent: '#4f9ac7',\n\n iconMuted: '#d1d5db',\n iconSubtle: '#9aa6b2',\n iconNeutral: '#8c8c8c',\n iconSoft: '#bbbbbb',\n scrollbarThumb: '#c4ccd8',\n scrollbarThumbLight: '#d1d5db',\n danger: '#dc2626',\n dangerSoft: '#ef4444',\n dangerStrong: '#e11d24',\n success: '#15803d',\n successSoft: '#16a34a',\n warning: '#f97316',\n info: '#3b82f6',\n transparent: 'transparent',\n\n statusNeutralBg: '#f1f3ee',\n statusNeutralText: '#717b36',\n statusProcessingBg: '#f6f4f7',\n statusProcessingText: '#273048',\n statusProcessingBorder: '#d4d8dd',\n statusWarningBg: '#fff7ea',\n statusWarningText: '#bf7328',\n\n shadowMenu: 'rgba(15, 35, 55, 0.08)',\n shadowBadge: 'rgba(26, 95, 145, 0.08)',\n shadowTag: 'rgba(39, 51, 70, 0.05)',\n shadowAvatar: 'rgba(57, 77, 103, 0.12)',\n shadowDropdown: '0 6px 16px 0 rgba(0, 0, 0, .08), 0 3px 6px -4px rgba(0, 0, 0, .12), 0 9px 28px 8px rgba(0, 0, 0, .05)',\n avatarBlueBg: '#eaf3ff',\n avatarBlueText: '#142235',\n avatarPurpleBg: '#f2eaff',\n avatarMoreBg: '#eef6ff',\n avatarMoreText: '#0053a5',\n\n avatarNeutralBg: '#d9d9d9',\n avatarNeutralText: '#555555',\n avatarIndigo: '#6366f1',\n avatarPurple: '#8b5cf6',\n avatarBlue: '#3b82f6',\n avatarGreen: '#10b981',\n linkedIn: '#0077b5',\n};\n\nexport const typographyColors = {\n primary: colors.textPrimary,\n secondary: colors.textSecondary,\n muted: colors.textMuted,\n subtle: colors.textSubtle,\n inverse: colors.textInverse,\n link: colors.textLink,\n danger: colors.danger,\n success: colors.success,\n};\n\nexport const onboardingStageToneColors = {\n approved: {\n background: '#f2f2e8',\n text: '#79772d',\n },\n danger: {\n background: '#fde7e9',\n text: colors.danger,\n },\n issued: {\n background: '#e9f2fb',\n text: colors.textLink,\n },\n neutral: {\n background: colors.surfaceSoft,\n text: colors.textSecondary,\n },\n success: {\n background: '#e7f5ee',\n text: colors.success,\n },\n warning: {\n background: '#fbf0e7',\n text: colors.warning,\n },\n};\n\nexport const colorVars = {\n brand: 'var(--color-brand)',\n brandDark: 'var(--color-brand-dark)',\n brandDarker: 'var(--color-brand-darker)',\n brandHover: 'var(--color-brand-hover)',\n brandSoft: 'var(--color-brand-soft)',\n brandSofter: 'var(--color-brand-softer)',\n brandSubtle: 'var(--color-brand-subtle)',\n\n textPrimary: 'var(--color-text-primary)',\n textSecondary: 'var(--color-text-secondary)',\n textMuted: 'var(--color-text-muted)',\n textSubtle: 'var(--color-text-subtle)',\n textHeading: 'var(--color-text-heading)',\n textDark: 'var(--color-text-dark)',\n textPlaceholder: 'var(--color-text-placeholder)',\n textInverse: 'var(--color-text-inverse)',\n textLink: 'var(--color-text-link)',\n\n surfacePage: 'var(--color-surface-page)',\n surfaceSoft: 'var(--color-surface-soft)',\n surfaceSofter: 'var(--color-surface-softer)',\n surfaceCard: 'var(--color-surface-card)',\n surfaceHover: 'var(--color-surface-hover)',\n surfaceHoverLight: 'var(--color-surface-hover-light)',\n surfaceHoverSoft: 'var(--color-surface-hover-soft)',\n surfaceSelected: 'var(--color-surface-selected)',\n surfaceControl: 'var(--color-surface-control)',\n surfaceRowAlt: 'var(--color-surface-row-alt)',\n\n border: 'var(--color-border)',\n borderLight: 'var(--color-border-light)',\n borderSofter: 'var(--color-border-softer)',\n borderMuted: 'var(--color-border-muted)',\n borderInput: 'var(--color-border-input)',\n borderFocus: 'var(--color-border-focus)',\n borderHover: 'var(--color-border-hover)',\n controlBorder: 'var(--color-control-border)',\n controlBorderMuted: 'var(--color-control-border-muted)',\n controlAccent: 'var(--color-control-accent)',\n\n iconMuted: 'var(--color-icon-muted)',\n iconSubtle: 'var(--color-icon-subtle)',\n iconNeutral: 'var(--color-icon-neutral)',\n iconSoft: 'var(--color-icon-soft)',\n scrollbarThumb: 'var(--color-scrollbar-thumb)',\n scrollbarThumbLight: 'var(--color-scrollbar-thumb-light)',\n danger: 'var(--color-danger)',\n dangerSoft: 'var(--color-danger-soft)',\n dangerStrong: 'var(--color-danger-strong)',\n success: 'var(--color-success)',\n successSoft: 'var(--color-success-soft)',\n warning: 'var(--color-warning)',\n info: 'var(--color-info)',\n transparent: 'var(--color-transparent)',\n linkedIn: 'var(--color-linkedin)',\n\n statusNeutralBg: 'var(--color-status-neutral-bg)',\n statusNeutralText: 'var(--color-status-neutral-text)',\n statusProcessingBg: 'var(--color-status-processing-bg)',\n statusProcessingText: 'var(--color-status-processing-text)',\n statusProcessingBorder: 'var(--color-status-processing-border)',\n statusWarningBg: 'var(--color-status-warning-bg)',\n statusWarningText: 'var(--color-status-warning-text)',\n\n shadowMenu: 'var(--color-shadow-menu)',\n shadowBadge: 'var(--color-shadow-badge)',\n shadowTag: 'var(--color-shadow-tag)',\n shadowAvatar: 'var(--color-shadow-avatar)',\n shadowDropdown: 'var(--color-shadow-dropdown)',\n avatarBlueBg: 'var(--color-avatar-blue-bg)',\n avatarBlueText: 'var(--color-avatar-blue-text)',\n avatarPurpleBg: 'var(--color-avatar-purple-bg)',\n avatarMoreBg: 'var(--color-avatar-more-bg)',\n avatarMoreText: 'var(--color-avatar-more-text)',\n};\n","import { Typography } from 'antd';\nimport { colorVars } from '../../theme/colors/colors';\n\nconst { Text, Title, Paragraph, Link } = Typography;\n\nconst defaultElementByVariant = {\n display: 'h1',\n h1: 'h1',\n h2: 'h2',\n h3: 'h3',\n h4: 'h4',\n h5: 'h5',\n 'section-title': 'h3',\n 'card-title': 'h4',\n subtitle: 'span',\n body: 'span',\n 'body-strong': 'span',\n label: 'span',\n caption: 'span',\n meta: 'span',\n metric: 'span',\n helper: 'span',\n link: 'a',\n};\n\nconst namedSizes = {\n xs: 'var(--font-size-xs)',\n sm: 'var(--font-size-sm)',\n md: 'var(--font-size-md)',\n lg: 'var(--font-size-lg)',\n xl: 'var(--font-size-xl)',\n '2xl': 'var(--font-size-2xl)',\n '3xl': 'var(--font-size-3xl)',\n '4xl': 'var(--font-size-4xl)',\n};\n\nconst namedWeights = {\n regular: 'var(--font-weight-regular)',\n medium: 'var(--font-weight-medium)',\n semibold: 'var(--font-weight-semibold)',\n bold: 'var(--font-weight-bold)',\n extrabold: 'var(--font-weight-extrabold)',\n};\n\nconst namedLineHeights = {\n tight: 'var(--line-height-tight)',\n snug: 'var(--line-height-snug)',\n normal: 'var(--line-height-normal)',\n relaxed: 'var(--line-height-relaxed)',\n};\n\nconst namedColors = {\n primary: colorVars.textPrimary,\n secondary: colorVars.textSecondary,\n muted: colorVars.textMuted,\n subtle: colorVars.textSubtle,\n inverse: colorVars.textInverse,\n link: colorVars.textLink,\n danger: colorVars.danger,\n success: colorVars.success,\n};\n\nfunction cx(...classes) {\n return classes.filter(Boolean).join(' ');\n}\n\nfunction tokenValue(value, tokens) {\n if (value === undefined || value === null) return undefined;\n return tokens[value] || value;\n}\n\nfunction getAntTypographyComponent(tag, variant) {\n if (variant === 'link' || tag === 'a') return Link;\n if (tag === 'p') return Paragraph;\n if (['h1', 'h2', 'h3', 'h4', 'h5'].includes(tag)) return Title;\n return Text;\n}\n\nfunction getTitleLevel(tag, variant) {\n const resolvedTag = tag || defaultElementByVariant[variant];\n if (!resolvedTag?.startsWith('h')) return undefined;\n return Number(resolvedTag.slice(1));\n}\n\nexport default function AppTypography({\n as,\n tag,\n variant = 'body',\n color,\n size,\n weight,\n lineHeight,\n align,\n truncate = false,\n display,\n className,\n style,\n children,\n ...props\n}) {\n const resolvedTag = tag || as || defaultElementByVariant[variant] || 'span';\n const Component = getAntTypographyComponent(resolvedTag, variant);\n const titleLevel = getTitleLevel(resolvedTag, variant);\n const dynamicStyle = {\n color: tokenValue(color, namedColors),\n fontSize: tokenValue(size, namedSizes),\n fontWeight: tokenValue(weight, namedWeights),\n lineHeight: tokenValue(lineHeight, namedLineHeights),\n display,\n ...style,\n };\n\n return (\n <Component\n {...(titleLevel ? { level: titleLevel } : {})}\n className={cx(\n 'app-typography',\n `app-typography--${variant}`,\n color && namedColors[color] && `app-typography--${color}`,\n align && `app-typography--${align}`,\n truncate && 'app-typography--truncate',\n className,\n )}\n style={dynamicStyle}\n {...props}\n >\n {children}\n </Component>\n );\n}\n","/**\n * TipTapEditor — Rich text editor component\n * Drop-in replacement for ReactQuill in EditFormV1\n *\n * Props:\n * value string — HTML string (controlled)\n * onChange function — called with HTML string on every change\n * disabled boolean — makes editor read-only\n * placeholder string — placeholder text\n */\n\nimport { useEffect, useRef } from 'react';\nimport { useEditor, EditorContent } from '@tiptap/react';\nimport StarterKit from '@tiptap/starter-kit';\nimport Link from '@tiptap/extension-link';\nimport Underline from '@tiptap/extension-underline';\nimport '../styles/TipTapEditor.css';\n\n// ─── Toolbar button ───────────────────────────────────────────────────────────\n\nfunction ToolbarButton({ onClick, active, disabled, title, children }) {\n return (\n <button\n type=\"button\"\n title={title}\n disabled={disabled}\n className={`tte-btn${active ? ' tte-btn--active' : ''}`}\n onMouseDown={(e) => {\n e.preventDefault(); // prevent editor losing focus\n onClick?.();\n }}\n >\n {children}\n </button>\n );\n}\n\n// ─── Toolbar ─────────────────────────────────────────────────────────────────\n\nfunction Toolbar({ editor, disabled }) {\n if (!editor) return null;\n\n const setLink = () => {\n const url = window.prompt('Enter URL');\n if (!url) {\n editor.chain().focus().unsetLink().run();\n return;\n }\n editor.chain().focus().setLink({ href: url }).run();\n };\n\n return (\n <div className={`tte-toolbar${disabled ? ' tte-toolbar--disabled' : ''}`}>\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Bold\"\n disabled={disabled}\n active={editor.isActive('bold')}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <strong>B</strong>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Italic\"\n disabled={disabled}\n active={editor.isActive('italic')}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <em>I</em>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Underline\"\n disabled={disabled}\n active={editor.isActive('underline')}\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n >\n <span style={{ textDecoration: 'underline' }}>U</span>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Strikethrough\"\n disabled={disabled}\n active={editor.isActive('strike')}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <s>S</s>\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Bullet List\"\n disabled={disabled}\n active={editor.isActive('bulletList')}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n ≡\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Ordered List\"\n disabled={disabled}\n active={editor.isActive('orderedList')}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n 1.\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Link\"\n disabled={disabled}\n active={editor.isActive('link')}\n onClick={setLink}\n >\n 🔗\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Clear formatting\"\n disabled={disabled}\n onClick={() => editor.chain().focus().clearNodes().unsetAllMarks().run()}\n >\n ✕\n </ToolbarButton>\n </div>\n </div>\n );\n}\n\n// ─── Main editor ──────────────────────────────────────────────────────────────\n\nexport default function TipTapEditor({\n value = '',\n onChange,\n disabled = false,\n placeholder = '',\n}) {\n // Tracks the last value this editor itself produced (via onUpdate) or was\n // last told to show (via the sync effect below) — NOT the editor's live\n // getHTML(), which can drift from `value` after a round-trip through\n // TipTap's HTML serializer (e.g. plain AI-generated text with no <p> tags\n // never equals its own wrapped-in-<p> serialization, which previously made\n // the old getHTML()-based comparison useless as a \"did this come from us\"\n // check and caused an external update to fight with a stale echo).\n const lastKnownValueRef = useRef(value ?? '');\n\n const editor = useEditor({\n extensions: [\n StarterKit,\n Underline,\n Link.configure({\n openOnClick: false,\n HTMLAttributes: { rel: 'noopener noreferrer' },\n }),\n ],\n content: value, // set initial content correctly on mount\n editable: !disabled,\n editorProps: {\n attributes: {\n class: 'tte-content',\n },\n },\n onUpdate: ({ editor }) => {\n const html = editor.getHTML();\n const next = html === '<p></p>' ? '' : html;\n lastKnownValueRef.current = next;\n onChange?.(next);\n },\n });\n\n // Sync value from outside — handles setFieldsValue from Ant Design form\n // (e.g. an AI Action replacing the content). Skips only when the incoming\n // value is exactly what this editor itself last emitted, so a genuine\n // external update always applies even if it differs from getHTML() purely\n // due to HTML serialization (missing <p> wrapper, entity encoding, etc).\n useEffect(() => {\n if (!editor || editor.isDestroyed) return;\n const nextValue = value || '';\n if (nextValue === (lastKnownValueRef.current || '')) return;\n lastKnownValueRef.current = nextValue;\n editor.commands.setContent(nextValue, false);\n }, [value, editor]);\n\n // Sync disabled state\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(!disabled);\n }, [disabled, editor]);\n\n return (\n <div className={`tte-wrapper${disabled ? ' tte-wrapper--disabled' : ''}`}>\n <Toolbar editor={editor} disabled={disabled} />\n <EditorContent editor={editor} />\n {!value && !editor?.isFocused && placeholder && (\n <div className=\"tte-placeholder\">{placeholder}</div>\n )}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { Modal, Spin, Tooltip, message } from 'antd';\nimport {\n CloseOutlined,\n DownloadOutlined,\n FileUnknownOutlined,\n LeftOutlined,\n RightOutlined,\n ZoomInOutlined,\n ZoomOutOutlined,\n} from '@ant-design/icons';\nimport { Document, Page, pdfjs } from 'react-pdf';\nimport { renderAsync } from 'docx-preview';\nimport DOMPurify from 'dompurify';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\nimport '../styles/DocumentViewer.css';\n\n// pdf.js needs a web worker; Vite resolves this URL at build time so it works\n// in dev and production without copying files into /public.\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n 'pdfjs-dist/build/pdf.worker.min.mjs',\n import.meta.url,\n).toString();\n\nconst IMAGE_EXT = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'avif', 'ico'];\nconst TEXT_EXT = ['txt', 'csv', 'log', 'json', 'md', 'xml'];\nconst VIDEO_EXT = ['mp4', 'webm', 'ogg', 'ogv', 'mov', 'm4v'];\nconst ZOOM_STEP = 0.2;\nconst ZOOM_MIN = 0.4;\nconst ZOOM_MAX = 3;\n\nfunction fileNameFromUrl(url) {\n if (!url) return 'Document';\n const clean = String(url).split('?')[0].split('#')[0];\n return decodeURIComponent(clean.split('/').pop() || clean) || 'Document';\n}\n\nfunction extOf(nameOrUrl) {\n const clean = String(nameOrUrl || '').split('?')[0].split('#')[0];\n const dot = clean.lastIndexOf('.');\n return dot === -1 ? '' : clean.slice(dot + 1).toLowerCase();\n}\n\nconst KNOWN_BUCKETS = ['pdf', 'docx', 'image', 'text', 'video', 'html'];\n\n// bucketFromExt maps a bare extension (no dot) to a renderer bucket, or null.\nfunction bucketFromExt(ext) {\n if (!ext) return null;\n if (ext === 'pdf') return 'pdf';\n if (ext === 'doc' || ext === 'docx') return 'docx';\n if (IMAGE_EXT.includes(ext)) return 'image';\n if (TEXT_EXT.includes(ext)) return 'text';\n if (VIDEO_EXT.includes(ext)) return 'video';\n return null;\n}\n\n// kindOf maps a document to a renderer bucket: pdf | docx | image | text |\n// video | html | unknown.\n//\n// `doc.type` is normalized rather than trusted verbatim, because it arrives\n// in different shapes depending on the caller: an already-correct bucket\n// name (\"image\", inline html), a bare file extension as the backend upload\n// handler stores it (\"jpg\", \"png\", \"pdf\" — see timesheetController.go's\n// `ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(...), \".\"))`), or a\n// MIME type (\"image/jpeg\"). Returning a bare extension straight through only\n// happened to work for \"pdf\"/\"docx\" because those extensions equal their own\n// bucket name — every image extension (\"jpg\", \"png\", …) doesn't equal\n// \"image\", so it silently fell through to the \"no preview available\"\n// fallback. This maps all three shapes through the same extension table.\nfunction kindOf(doc) {\n const rawType = String(doc.type || '').toLowerCase().trim();\n if (KNOWN_BUCKETS.includes(rawType)) return rawType;\n\n const mimeExt = rawType.includes('/') ? rawType.split('/').pop() : rawType;\n const fromType = bucketFromExt(mimeExt);\n if (fromType) return fromType;\n\n const fromName = bucketFromExt(extOf(doc.name || doc.url));\n if (fromName) return fromName;\n\n return 'unknown';\n}\n\n// normalizeDocs accepts either bare URL strings, file objects, or inline\n// document objects ({ name, type: 'text'|'html', content }).\nfunction normalizeDocs(documents) {\n return (documents || [])\n .map((d, i) => {\n if (typeof d === 'string') {\n return { url: d, name: fileNameFromUrl(d), key: String(i) };\n }\n const url = d.url || d.location || d.path || '';\n return {\n url,\n name: d.name || fileNameFromUrl(url),\n type: d.type,\n content: typeof d.content === 'string' ? d.content : '',\n key: String(d.id ?? d._id ?? i),\n };\n })\n .filter((d) => d.url || d.content);\n}\n\n/* ------------------------------- renderers ------------------------------- */\n\nfunction PdfRenderer({ url, scale, onNativeFallback }) {\n const wrapRef = useRef(null);\n const [numPages, setNumPages] = useState(0);\n const [width, setWidth] = useState(0);\n const [error, setError] = useState(false);\n\n useEffect(() => {\n const el = wrapRef.current;\n if (!el) return undefined;\n const update = () => setWidth(el.clientWidth);\n update();\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n // pdf.js streams the file via its own fetch, which fails cross-origin on\n // storage URLs the bucket's CORS policy doesn't allowlist this origin for\n // (common — see the S3 bucket's CORS config, not something fixable here) —\n // distinct from a plain navigation (an <iframe> load), which is NOT subject\n // to CORS at all and works regardless. Fall back to that rather than\n // dead-ending the preview; the native viewer brings its own zoom/controls,\n // so the toolbar hides its (now inert) zoom buttons via onNativeFallback.\n //\n // This intentionally points the iframe at the raw presigned `url`, not a\n // fetched blob — fetching it would hit the same CORS wall react-pdf just\n // did. Whether this renders inline vs. downloads depends entirely on the\n // `Content-Disposition` the presigned URL responds with; that's set\n // server-side (GetPresignedURLInline in Be_Auth_DevOps) rather than worked\n // around here, so every consumer of the URL — this iframe, a plain link,\n // anything — gets the same correct inline behavior.\n if (error) {\n return <iframe title=\"PDF preview\" src={url} className=\"dv-pdf-native\" />;\n }\n\n return (\n <div ref={wrapRef} className=\"dv-pdf-wrap\">\n <Document\n file={url}\n loading={<Spin />}\n error={<FallbackStage label=\"This PDF could not be displayed.\" />}\n onLoadSuccess={({ numPages: n }) => setNumPages(n)}\n onLoadError={() => {\n setError(true);\n onNativeFallback?.();\n }}\n >\n {Array.from({ length: numPages }, (_, i) => (\n <Page\n key={`page-${i + 1}`}\n pageNumber={i + 1}\n width={width ? width * scale : undefined}\n className=\"dv-pdf-page\"\n renderTextLayer\n renderAnnotationLayer\n />\n ))}\n </Document>\n </div>\n );\n}\n\nPdfRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n scale: PropTypes.number.isRequired,\n onNativeFallback: PropTypes.func,\n};\n\nfunction DocxRenderer({ url }) {\n const ref = useRef(null);\n const [status, setStatus] = useState('loading'); // loading | ready | error\n\n useEffect(() => {\n let cancelled = false;\n\n fetch(url)\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return r.blob();\n })\n .then((blob) => {\n if (cancelled || !ref.current) return undefined;\n ref.current.innerHTML = '';\n return renderAsync(blob, ref.current, undefined, {\n className: 'dv-docx',\n inWrapper: true,\n ignoreWidth: false,\n ignoreHeight: false,\n });\n })\n .then(() => {\n if (!cancelled) setStatus('ready');\n })\n .catch(() => {\n if (!cancelled) setStatus('error');\n });\n\n return () => {\n cancelled = true;\n };\n }, [url]);\n\n if (status === 'error') {\n return <FallbackStage label=\"This document could not be displayed.\" />;\n }\n\n return (\n <div className=\"dv-docx-scroll\">\n {status === 'loading' && <div className=\"dv-center\"><Spin /></div>}\n <div ref={ref} style={{ visibility: status === 'ready' ? 'visible' : 'hidden' }} />\n </div>\n );\n}\n\nDocxRenderer.propTypes = { url: PropTypes.string.isRequired };\n\nfunction TextRenderer({ url, content = '' }) {\n const [text, setText] = useState(null);\n const [status, setStatus] = useState('loading');\n\n useEffect(() => {\n if (content) return undefined;\n\n let cancelled = false;\n fetch(url)\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return r.text();\n })\n .then((t) => {\n if (!cancelled) {\n setText(t);\n setStatus('ready');\n }\n })\n .catch(() => !cancelled && setStatus('error'));\n return () => {\n cancelled = true;\n };\n }, [content, url]);\n\n if (content) return <pre className=\"dv-text\">{content}</pre>;\n if (status === 'loading') return <div className=\"dv-center\"><Spin /></div>;\n if (status === 'error') return <FallbackStage label=\"This file could not be displayed.\" />;\n return <pre className=\"dv-text\">{text}</pre>;\n}\n\nTextRenderer.propTypes = {\n url: PropTypes.string,\n content: PropTypes.string,\n};\n\nfunction HtmlRenderer({ content }) {\n const safeHtml = useMemo(() => DOMPurify.sanitize(content), [content]);\n return <div className=\"dv-html\" dangerouslySetInnerHTML={{ __html: safeHtml }} />;\n}\n\nHtmlRenderer.propTypes = { content: PropTypes.string.isRequired };\n\nfunction ImageRenderer({ url, name, scale }) {\n const [error, setError] = useState(false);\n if (error) return <FallbackStage label=\"This image could not be displayed.\" />;\n return (\n <div className=\"dv-image-wrap\">\n <img\n className=\"dv-image\"\n src={url}\n alt={name}\n style={{ transform: `scale(${scale})` }}\n onError={() => setError(true)}\n />\n </div>\n );\n}\n\nImageRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n scale: PropTypes.number.isRequired,\n};\n\nfunction VideoRenderer({ url, name }) {\n const [error, setError] = useState(false);\n if (error) return <FallbackStage label=\"This video could not be played.\" />;\n return (\n <div className=\"dv-video-wrap\">\n <video\n className=\"dv-video\"\n src={url}\n title={name}\n controls\n preload=\"metadata\"\n controlsList=\"nodownload\"\n onError={() => setError(true)}\n />\n </div>\n );\n}\n\nVideoRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n};\n\nfunction FallbackStage({ label }) {\n return (\n <div className=\"dv-center dv-fallback\">\n <FileUnknownOutlined className=\"dv-fallback-icon\" />\n <p>{label}</p>\n <span className=\"dv-fallback-hint\">Use the download button to open it.</span>\n </div>\n );\n}\n\nFallbackStage.propTypes = { label: PropTypes.string.isRequired };\n\n/* ----------------------------- main component ---------------------------- */\n\n// ViewerBody holds the per-session state (active index + zoom). It lives inside\n// the Modal body, which is destroyed on close (destroyOnHidden), so it remounts\n// fresh on every open — no manual \"reset on open\" effects required.\nfunction ViewerBody({ docs, initialIndex, onClose }) {\n const total = docs.length;\n const safeInitial = Math.min(Math.max(initialIndex, 0), Math.max(0, total - 1));\n const [index, setIndex] = useState(safeInitial);\n const [scale, setScale] = useState(1);\n // Set when a PDF falls back to the browser's native viewer (see\n // PdfRenderer) — that viewer has its own zoom UI, so ours would sit there\n // doing nothing if left visible.\n const [pdfNativeFallback, setPdfNativeFallback] = useState(false);\n\n const current = docs[index];\n const kind = current ? kindOf(current) : 'unknown';\n const zoomable = (kind === 'pdf' && !pdfNativeFallback) || kind === 'image';\n\n // setActive changes the document and resets zoom/fallback in one event\n // handler, so we never have to reset them from an effect.\n const setActive = useCallback((next) => {\n setIndex(next);\n setScale(1);\n setPdfNativeFallback(false);\n }, []);\n\n const goPrev = useCallback(() => setActive(Math.max(0, index - 1)), [index, setActive]);\n const goNext = useCallback(\n () => setActive(Math.min(total - 1, index + 1)),\n [index, total, setActive],\n );\n\n // Keyboard navigation.\n useEffect(() => {\n const onKey = (e) => {\n // A focused <video> uses arrow keys to seek — don't switch documents.\n if (e.target?.tagName === 'VIDEO') return;\n if (e.key === 'ArrowLeft') goPrev();\n else if (e.key === 'ArrowRight') goNext();\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [goPrev, goNext]);\n\n const download = useCallback(async (doc) => {\n if (!doc) return;\n try {\n let blob;\n if (doc.content) {\n const mimeType = doc.type === 'html' ? 'text/html;charset=utf-8' : 'text/plain;charset=utf-8';\n blob = new Blob([doc.content], { type: mimeType });\n } else {\n const res = await fetch(doc.url);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n blob = await res.blob();\n }\n const objUrl = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = objUrl;\n a.download = doc.name || 'document';\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(objUrl);\n } catch {\n // Cross-origin without CORS: fall back to opening in a new tab.\n message.info('Opening file in a new tab…');\n if (doc.url) window.open(doc.url, '_blank', 'noopener,noreferrer');\n }\n }, []);\n\n function renderStage() {\n if (!current) {\n return <FallbackStage label=\"No document to preview.\" />;\n }\n switch (kind) {\n case 'pdf':\n return (\n <PdfRenderer\n key={current.key}\n url={current.url}\n scale={scale}\n onNativeFallback={() => setPdfNativeFallback(true)}\n />\n );\n case 'docx':\n return <DocxRenderer key={current.key} url={current.url} />;\n case 'image':\n return (\n <ImageRenderer key={current.key} url={current.url} name={current.name} scale={scale} />\n );\n case 'text':\n return <TextRenderer key={current.key} url={current.url} content={current.content} />;\n case 'html':\n return <HtmlRenderer key={current.key} content={current.content} />;\n case 'video':\n return <VideoRenderer key={current.key} url={current.url} name={current.name} />;\n default:\n return <FallbackStage label=\"Preview is not available for this file type.\" />;\n }\n }\n\n return (\n <div className=\"dv-root\">\n {/* toolbar */}\n <div className=\"dv-toolbar\">\n <div className=\"dv-title\" title={current?.name}>\n <span className=\"dv-title-text\">{current?.name || 'Document'}</span>\n {total > 1 && <span className=\"dv-counter\">{index + 1} / {total}</span>}\n </div>\n <div className=\"dv-actions\">\n {zoomable && (\n <>\n <Tooltip title=\"Zoom out\">\n <button\n type=\"button\"\n className=\"dv-icon-btn\"\n onClick={() => setScale((s) => Math.max(ZOOM_MIN, +(s - ZOOM_STEP).toFixed(2)))}\n disabled={scale <= ZOOM_MIN}\n >\n <ZoomOutOutlined />\n </button>\n </Tooltip>\n <span className=\"dv-zoom-label\">{Math.round(scale * 100)}%</span>\n <Tooltip title=\"Zoom in\">\n <button\n type=\"button\"\n className=\"dv-icon-btn\"\n onClick={() => setScale((s) => Math.min(ZOOM_MAX, +(s + ZOOM_STEP).toFixed(2)))}\n disabled={scale >= ZOOM_MAX}\n >\n <ZoomInOutlined />\n </button>\n </Tooltip>\n <span className=\"dv-divider\" />\n </>\n )}\n <Tooltip title=\"Download\">\n <button type=\"button\" className=\"dv-icon-btn dv-download\" onClick={() => download(current)}>\n <DownloadOutlined />\n </button>\n </Tooltip>\n <Tooltip title=\"Close\">\n <button type=\"button\" className=\"dv-icon-btn\" onClick={onClose}>\n <CloseOutlined />\n </button>\n </Tooltip>\n </div>\n </div>\n\n {/* stage + slider arrows */}\n <div className=\"dv-stage\">\n {total > 1 && (\n <button\n type=\"button\"\n className=\"dv-nav dv-nav--prev\"\n onClick={goPrev}\n disabled={index === 0}\n aria-label=\"Previous document\"\n >\n <LeftOutlined />\n </button>\n )}\n\n <div className=\"dv-canvas\">{renderStage()}</div>\n\n {total > 1 && (\n <button\n type=\"button\"\n className=\"dv-nav dv-nav--next\"\n onClick={goNext}\n disabled={index === total - 1}\n aria-label=\"Next document\"\n >\n <RightOutlined />\n </button>\n )}\n </div>\n\n {/* dots */}\n {total > 1 && (\n <div className=\"dv-dots\">\n {docs.map((d, i) => (\n <button\n key={d.key}\n type=\"button\"\n className={`dv-dot${i === index ? ' dv-dot--active' : ''}`}\n onClick={() => setActive(i)}\n aria-label={`Go to document ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nViewerBody.propTypes = {\n docs: PropTypes.arrayOf(PropTypes.object).isRequired,\n initialIndex: PropTypes.number.isRequired,\n onClose: PropTypes.func.isRequired,\n};\n\n// DocumentViewer is the public component: a Modal shell that mounts ViewerBody\n// only while open. `documents` may be URL strings or document objects.\n//\n// Pass `inline` to render ViewerBody directly with no Modal — used by the timesheet\n// manager review, which shows the uploaded screenshot side-by-side with the calendar\n// rather than in a popup. Everything ViewerBody already does (pdf/image/docx render,\n// zoom, download, multi-document navigation) comes along for free.\nexport default function DocumentViewer({ documents, open, onClose, initialIndex = 0, inline = false }) {\n const docs = useMemo(() => normalizeDocs(documents), [documents]);\n\n if (inline) {\n return (\n <div className=\"dv-root--inline\">\n <ViewerBody docs={docs} initialIndex={initialIndex} onClose={onClose ?? (() => {})} />\n </div>\n );\n }\n\n return (\n <Modal\n open={open}\n onCancel={onClose}\n footer={null}\n title={null}\n closable={false}\n centered\n width=\"min(1100px, 94vw)\"\n className=\"dv-modal\"\n styles={{ content: { padding: 0, overflow: 'hidden', borderRadius: 14 }, body: { padding: 0 } }}\n destroyOnHidden\n >\n <ViewerBody docs={docs} initialIndex={initialIndex} onClose={onClose} />\n </Modal>\n );\n}\n\nDocumentViewer.propTypes = {\n // URL strings, file objects, or inline objects ({ content, type: 'text'|'html' }).\n documents: PropTypes.arrayOf(\n PropTypes.oneOfType([PropTypes.string, PropTypes.object]),\n ).isRequired,\n // Required for the Modal shell; unused (and optional) when `inline` is set.\n open: PropTypes.bool,\n onClose: PropTypes.func,\n initialIndex: PropTypes.number,\n // Render the viewer body directly, with no Modal wrapper.\n inline: PropTypes.bool,\n};\n","// Dot-notation module names: \"job.client.company\" means \"fetch the `job`\n// module, then display the data found at `client.company` inside each record\".\n// Only the segment before the first dot is a real backend module — every API\n// call (module-data-list, field-config, permissions) must use it, while the\n// remaining segments are resolved client-side against each fetched record.\n\nexport function parseModulePath(moduleName) {\n const raw = String(moduleName ?? '').trim();\n if (!raw) return { baseModule: '', nestedPath: '' };\n\n const [baseModule, ...rest] = raw.split('.');\n return {\n baseModule: baseModule.trim(),\n nestedPath: rest.map((part) => part.trim()).filter(Boolean).join('.'),\n };\n}\n\nexport function getBaseModuleName(moduleName) {\n return parseModulePath(moduleName).baseModule;\n}\n\nexport function getValueAtPath(source, path) {\n return String(path ?? '')\n .split('.')\n .filter(Boolean)\n .reduce((value, key) => (value == null ? undefined : value[key]), source);\n}\n","import { fetchJsonWithAuth, apiGetWithAuth } from './authApi';\nimport { AUTH_URL, SUBMISSIONS_URL } from './apiConfig';\nimport { normalizeDiceSkills, resolveDiceProfileId } from './diceCandidateMapper';\nimport { getBaseModuleName } from '../utils/modulePath';\n\nconst TEST_FLOW_MODULE = 'test-flows';\nconst LMS_FLOW_MODULE = 'lms-flows';\n\nconst TEST_FLOW_FIELDS = [\n { value: 'module', label: 'Module', isVisible: true, type: 'text' },\n { value: 'flow', label: 'Flow', isVisible: true, type: 'text' },\n { value: 'step_order', label: 'Step Order', isVisible: true, type: 'number' },\n { value: 'keyword', label: 'Action', isVisible: true, type: 'text' },\n { value: 'description', label: 'Description', isVisible: true, type: 'text' },\n { value: 'target', label: 'Key', isVisible: true, type: 'text' },\n { value: 'tags', label: 'Tags', isVisible: true, type: 'text' },\n { value: 'value', label: 'Value', isVisible: true, type: 'text' },\n { value: 'expected', label: 'Expected', isVisible: true, type: 'text' },\n];\n\nconst LMS_FLOW_FIELDS = [\n { value: 'module', label: 'Module', isVisible: true, type: 'text' },\n { value: 'flow', label: 'Flow', isVisible: true, type: 'text' },\n { value: 'step_order', label: 'Step Order', isVisible: true, type: 'number' },\n { value: 'keyword', label: 'Action', isVisible: true, type: 'text' },\n { value: 'description', label: 'Description', isVisible: true, type: 'text' },\n { value: 'target', label: 'Key', isVisible: true, type: 'text' },\n { value: 'tags', label: 'Tags', isVisible: true, type: 'text' },\n { value: 'value', label: 'Value', isVisible: true, type: 'text' },\n { value: 'expected', label: 'Expected', isVisible: true, type: 'text' },\n];\n\nfunction normalizeCandidateSearchSource(value) {\n const values = (Array.isArray(value) ? value : [value])\n .flat()\n .map((item) => {\n if (item && typeof item === 'object') {\n return item.value ?? item.label ?? item.name ?? '';\n }\n return item;\n })\n .map((item) => String(item ?? '').trim().toLowerCase())\n .filter(Boolean);\n\n if (values.includes('dice')) return 'dice';\n if (values.includes('internal')) return 'internal';\n return values[0] || '';\n}\n\nfunction firstFiniteNumber(...values) {\n for (const value of values) {\n const number = Number(value);\n if (Number.isFinite(number)) return number;\n }\n\n return undefined;\n}\n\nfunction extractResponseTotal(response, payload, rows, scope = '') {\n const responseCount = response?.count;\n const payloadCount = payload?.count;\n const normalizedScope = String(scope ?? '').trim().toLowerCase();\n\n if (responseCount && typeof responseCount === 'object') {\n const total = firstFiniteNumber(\n normalizedScope ? responseCount[normalizedScope] : undefined,\n responseCount.total,\n responseCount.searchCount,\n responseCount.count,\n );\n if (total !== undefined) return total;\n }\n\n if (payloadCount && typeof payloadCount === 'object') {\n const total = firstFiniteNumber(\n normalizedScope ? payloadCount[normalizedScope] : undefined,\n payloadCount.total,\n payloadCount.searchCount,\n payloadCount.count,\n );\n if (total !== undefined) return total;\n }\n\n return firstFiniteNumber(\n response?.total,\n typeof responseCount !== 'object' ? responseCount : undefined,\n response?.totalCount,\n payload?.total,\n typeof payloadCount !== 'object' ? payloadCount : undefined,\n payload?.totalCount,\n rows.length,\n ) ?? 0;\n}\n\nfunction extractResponseCounts(response, payload) {\n const counts = {};\n [response?.count, payload?.count].forEach((count) => {\n if (!count || typeof count !== 'object') return;\n\n Object.entries(count).forEach(([key, value]) => {\n const number = Number(value);\n if (Number.isFinite(number)) counts[String(key).trim().toLowerCase()] = number;\n });\n });\n\n return counts;\n}\n\nexport async function getDropdownFields(module) {\n if (module === TEST_FLOW_MODULE) return TEST_FLOW_FIELDS;\n if (module === LMS_FLOW_MODULE) return LMS_FLOW_FIELDS;\n // Dot-notation names (\"job.client\") target nested data client-side; the\n // backend only knows the base module.\n const baseModule = getBaseModuleName(module);\n return apiGetWithAuth(SUBMISSIONS_URL, `/filter-dropdown-fields?module=${encodeURIComponent(baseModule)}`);\n}\n\nexport async function getFieldConfig(module) {\n if (module === TEST_FLOW_MODULE) return TEST_FLOW_FIELDS;\n if (module === LMS_FLOW_MODULE) return LMS_FLOW_FIELDS;\n return apiGetWithAuth(AUTH_URL, `/admin/field-config?module=${encodeURIComponent(getBaseModuleName(module))}`);\n}\n\nexport async function getDropdownValues(module, field, search = '', limit = 50, offset = 0, meta = {}) {\n const params = new URLSearchParams({\n module: getBaseModuleName(module), field, value: search,\n limit: String(limit), offset: String(offset),\n });\n if (meta.dataSource) params.set('dataSource', meta.dataSource);\n if (meta.masterName) params.set('masterName', meta.masterName);\n if (meta.groupName) params.set('groupName', meta.groupName);\n // The Auth gateway owns the admin form configuration and understands\n // dataSource/masterName. Keeping this generic lets every configured master,\n // module and lookup field work without module-specific UI code.\n return apiGetWithAuth(AUTH_URL, `/filter-dropdown-values?${params}`);\n}\n\nexport async function getModuleDataList(module, limit = 10, offset = 0, options = {}) {\n const { scope = '', sort = '', sortDir = '', filters = [] } = options;\n\n if (module === TEST_FLOW_MODULE) {\n const { getTestFlows } = await import('./testingApi');\n const data = await getTestFlows({ limit, offset });\n const rows = Array.isArray(data?.items) ? data.items : [];\n const total = Number(data?.total) || 0;\n return { items: rows, total, limit, offset, tabs: [{ key: 'all', title: 'Test Cases', count: total }] };\n }\n\n if (module === LMS_FLOW_MODULE) {\n const { getLmsFlows } = await import('./testingApi');\n const data = await getLmsFlows({ limit, offset });\n const rows = Array.isArray(data?.items) ? data.items : [];\n const total = Number(data?.total) || 0;\n return { items: rows, total, limit, offset, tabs: [{ key: 'all', title: 'LMS Cases', count: total }] };\n }\n\n // const params = new URLSearchParams({ module, limit: String(limit), offset: String(offset) });\n // if (scope) params.set('scope', scope);\n const params = new URLSearchParams({\n module: getBaseModuleName(module), limit: String(limit), offset: String(offset),\n });\n const normalizedScope = String(scope ?? '').trim();\n if (normalizedScope) params.set('scope', normalizedScope);\n if (sort) params.set('sort', sort);\n if (sortDir) params.set('sortDir', sortDir);\n if (Array.isArray(filters) && filters.some((item) => item?.field)) {\n params.set('filters', JSON.stringify(filters));\n }\n\n return fetchJsonWithAuth(AUTH_URL, `/module-data-list?${params.toString()}`);\n}\n\nexport async function searchModuleData(moduleName, searchParams = {}, pagination = {}, options = {}) {\n const { limit = 10, offset = 0 } = pagination;\n const { sort = '', sortDir = '', scope = '' } = options;\n const inferredScope = normalizeCandidateSearchSource(scope || searchParams.selectedSource);\n\n const query = new URLSearchParams();\n query.append('module', getBaseModuleName(moduleName));\n query.append('limit', String(limit));\n query.append('offset', String(offset));\n\n if (inferredScope) {\n query.set('scope', inferredScope);\n console.log('[searchModuleData] Adding scope to query:', inferredScope);\n }\n if (sort) query.set('sort', sort);\n if (sortDir) query.set('sortDir', sortDir);\n\n console.log('[searchModuleData] Final query string:', query.toString());\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null || value === '') return;\n\n let normalizedValue = value;\n if (typeof value === 'object') {\n normalizedValue = value.value ?? value.label ?? value.name ?? String(value);\n } else {\n normalizedValue = String(value);\n }\n\n query.append(key, normalizedValue);\n });\n\n const response = await fetchJsonWithAuth(AUTH_URL, `/module-data-list?${query.toString()}`);\n // const response = await fetchJsonWithAuth(\"http://localhost:9009/v1\", `/module-data-list?${query.toString()}`);\n // response = { status, data: { actionRules, actions, columnActions, data: [...records] } }\n const payload = response?.data ?? response;\n const rows = Array.isArray(payload)\n ? payload\n : payload?.rows ?? payload?.records ?? payload?.items ?? payload?.list ?? payload?.data ?? [];\n\n const total = extractResponseTotal(response, payload, rows, inferredScope);\n const counts = extractResponseCounts(response, payload);\n if (inferredScope && counts[inferredScope] === undefined && Number.isFinite(total)) {\n counts[inferredScope] = total;\n }\n\n // Check if response contains Dice candidates and transform if needed\n const hasDiceCandidates = rows.some((record) => {\n const sourceFields = [\n record?.sourceType,\n record?.profileSource,\n record?.selectedSource,\n record?.customFields?.sourceType,\n record?.customFields?.profileSource,\n ].flat();\n return sourceFields.some(\n (value) => String(value ?? '').trim().toLowerCase() === 'dice',\n ) || Boolean(record?.customFields?.diceProfileData);\n });\n\n let normalizedRows = rows;\n if (hasDiceCandidates) {\n try {\n const { transformDiceResponseToInternalFormat } = await import('./diceCandidateMapper');\n const transformed = transformDiceResponseToInternalFormat(response, response);\n normalizedRows = transformed?.data?.data ?? rows;\n } catch (error) {\n console.error('[searchModuleData] Dice transformation failed:', error);\n normalizedRows = rows.map(normalizeCandidateSearchRow);\n }\n } else {\n normalizedRows = Array.isArray(rows) ? rows.map(normalizeCandidateSearchRow) : [];\n }\n\n return {\n rows: normalizedRows,\n total: Number(total) || 0,\n counts,\n tabs: response?.tabs ?? response?.tabList ?? payload?.tabs ?? payload?.tabList ?? [],\n fields: response?.fields ?? response?.fieldConfig ?? payload?.fields ?? payload?.fieldConfig ?? [],\n tabField: response?.tabField ?? payload?.tabField ?? '',\n actionRules: payload?.actionRules ?? [],\n actions: payload?.actions ?? [],\n columnActions: payload?.columnActions ?? [],\n };\n}\n\nfunction getNestedValue(record, path) {\n return String(path)\n .split('.')\n .reduce((value, key) => value?.[key], record);\n}\n\nfunction setNestedValue(record, path, value) {\n const keys = String(path).split('.').filter(Boolean);\n if (!keys.length) return record;\n\n const nextRecord = { ...record };\n let target = nextRecord;\n let source = record;\n\n keys.slice(0, -1).forEach((key) => {\n const currentValue = source?.[key];\n const nextValue = currentValue && typeof currentValue === 'object' && !Array.isArray(currentValue)\n ? { ...currentValue }\n : {};\n\n target[key] = nextValue;\n target = nextValue;\n source = currentValue;\n });\n\n target[keys[keys.length - 1]] = value;\n return nextRecord;\n}\n\nfunction normalizeCandidateSearchRow(row) {\n if (!row || typeof row !== 'object') return row;\n\n const skillPaths = [\n 'skills',\n 'technicalSkills',\n 'primarySkills',\n 'keySkills',\n 'customFields.diceProfileData.skills',\n ];\n\n let nextRow = row;\n let firstNormalizedSkills = null;\n\n skillPaths.forEach((path) => {\n const rawSkills = getNestedValue(nextRow, path);\n if (rawSkills === undefined || rawSkills === null) return;\n\n const mappedSkills = normalizeDiceSkills(rawSkills);\n if (mappedSkills.length === 0 && Array.isArray(rawSkills) && rawSkills.length > 0) return;\n\n firstNormalizedSkills = firstNormalizedSkills ?? mappedSkills;\n nextRow = setNestedValue(nextRow, path, mappedSkills);\n });\n\n if (firstNormalizedSkills && getNestedValue(nextRow, 'skills') === undefined) {\n nextRow = setNestedValue(nextRow, 'skills', firstNormalizedSkills);\n }\n\n const sourceFields = [\n nextRow?.sourceType,\n nextRow?.profileSource,\n nextRow?.selectedSource,\n nextRow?.customFields?.sourceType,\n nextRow?.customFields?.profileSource,\n ].flat();\n const isDiceCandidate = sourceFields.some(\n (value) => String(value ?? '').trim().toLowerCase() === 'dice',\n ) || Boolean(nextRow?.customFields?.diceProfileData);\n\n if (isDiceCandidate) {\n const diceProfileId = resolveDiceProfileId(nextRow);\n const candidateId = nextRow?.candidateId\n ?? nextRow?.customFields?.diceProfileData?.candidateId\n ?? '';\n\n nextRow = {\n ...nextRow,\n id: nextRow?.id ?? diceProfileId,\n diceId: diceProfileId,\n candidateId,\n };\n }\n\n return nextRow;\n}\n","import { ensureToken, fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL, JOBS_URL } from './apiConfig';\n\n// ── Form Groups ───────────────────────────────────────────────────────────────\n\nfunction extractFormGroups(json) {\n const payload = json?.data ?? json;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(payload?.groups)) return payload.groups;\n if (Array.isArray(json?.groups)) return json.groups;\n return [];\n}\n\nexport async function getFormGroups({ module, id, action, clientId, region, group } = {}) {\n if (!module) throw new Error('module is required to fetch form groups');\n const token = await ensureToken();\n const params = new URLSearchParams({ module });\n if (id) params.set('id', id);\n if (action) params.set('action', action);\n if (clientId) params.set('clientId', clientId);\n if (region) params.set('region', region);\n if (group) params.set('group', group);\n const res = await fetch(`${AUTH_URL}/admin/form-groups?${params.toString()}`, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);\n const json = await res.json();\n return extractFormGroups(json);\n}\n\nexport async function getCandidateFormGroups(options = {}) {\n return getFormGroups({ module: 'candidates', ...options });\n}\n\nexport async function getMentionUsers(search = '') {\n const { apiGetWithAuth } = await import('./authApi');\n const params = search ? `?search=${encodeURIComponent(search)}` : '';\n return apiGetWithAuth(JOBS_URL, `/users/any${params}`);\n}\n\n// ── Create Records ────────────────────────────────────────────────────────────\n// Routes through the Auth gateway which injects tenant context from JWT\n// and proxies to the correct downstream service based on module name.\n\nexport async function createModuleRecord(moduleName, formData) {\n // ensureToken (not getStoredToken) so a missing/expired session throws a\n // clear \"Authentication required\" error instead of silently sending the\n // request with no Authorization header (which the gateway rejects as 401).\n // NOTE: do NOT set Content-Type here — the browser must add the multipart\n // boundary itself. Only the Authorization header is set manually.\n const token = await ensureToken();\n\n const res = await fetch(\n `${AUTH_URL}/module/create?module=${encodeURIComponent(moduleName)}`,\n { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData },\n );\n\n const { logout } = await import('./authApi');\n if (res.status === 401) logout();\n\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('application/json') ? await res.json() : await res.text();\n\n if (!res.ok) {\n const error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\n error.status = res.status;\n error.data = data;\n throw error;\n }\n\n return data;\n}\n","import { 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 ?? null,\n valueStyles: Array.isArray(field.valueStyles) ? field.valueStyles : [],\n commonStyle: field.commonStyle ?? null,\n displayTemplate: field.displayTemplate ?? '',\n dataSource: field.dataSource ?? 'module',\n groupName: field.groupName ?? '',\n };\n}\n\nfunction normalizeCustomizableModule(module) {\n if (typeof module === 'string') {\n return { key: module, apiModule: module };\n }\n\n const key = firstValue(\n module,\n ['key', 'moduleKey', 'value', 'module', 'apiModule', 'name', 'modulename', 'menuName'],\n ''\n );\n if (!key) return null;\n\n return {\n key,\n apiModule: firstValue(\n module,\n ['apiModule', 'api_module', 'module', 'value', 'key', 'name', 'modulename', 'menuName'],\n key\n ),\n };\n}\n\nfunction normalizeCustomizableModules(modules = CUSTOMIZABLE_MODULES) {\n const source = Array.isArray(modules) && modules.length ? modules : CUSTOMIZABLE_MODULES;\n const seen = new Set();\n return source\n .map(normalizeCustomizableModule)\n .filter(Boolean)\n .filter((module) => {\n if (seen.has(module.key)) return false;\n seen.add(module.key);\n return true;\n });\n}\n\n// ── Modules ───────────────────────────────────────────────────────────────────\n\nconst MODULES_PATH = '/admin/modules';\n\nexport async function getModules() {\n const json = await fetchJsonWithAuth(AUTH_URL, MODULES_PATH);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\n// createModule/updateModule let Admin define workflow modules (e.g. \"Source\n// Candidates\") that extend an existing module via baseModule, inheriting its\n// forms/columns/tabs/filters/actions wherever the new module hasn't\n// configured its own — see the backend's ModuleLookupChain.\nexport async function createModule({ key, label, collectionName, baseModule = '', order = 0 }) {\n return fetchJsonWithAuth(AUTH_URL, MODULES_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ key, label, collectionName, baseModule, order }),\n });\n}\n\nexport async function updateModule(key, { label, collectionName, baseModule = '', order = 0, isActive = true }) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULES_PATH}/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ label, collectionName, baseModule, order, isActive }),\n });\n}\n\n// getModuleAutomations/saveModuleAutomations edit a module's Gateway +\n// Strategies — the Admin \"Automations\" screen. This is the config-driven\n// replacement for logic legacy services used to hardcode in Go (reference-id\n// generation, duplicate checks, cross-module field snapshots, reporting-chain\n// hierarchy expansion, board updates, notifications, cache invalidation).\n// Separate endpoint from updateModule() above, so saving automations can\n// never touch label/forms/columns, and vice versa.\nexport async function getModuleAutomations(key) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${MODULES_PATH}/${encodeURIComponent(key)}/automations`);\n const data = json?.data ?? json ?? {};\n return { gateway: data.gateway ?? null, strategies: data.strategies ?? null };\n}\n\n// Platform metadata (workflows / resolvers / state-machines / applications) —\n// the event-driven behavior layer. kind ∈ 'workflows' | 'resolvers' |\n// 'state-machines' | 'applications'. Docs are keyed by their \"key\" field.\nexport async function listPlatformDocs(kind) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/platform/${kind}`);\n const data = json?.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\nexport async function savePlatformDoc(kind, doc) {\n return fetchJsonWithAuth(AUTH_URL, `/admin/platform/${kind}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(doc),\n });\n}\n\nexport async function deletePlatformDoc(kind, key) {\n return fetchJsonWithAuth(AUTH_URL, `/admin/platform/${kind}/${encodeURIComponent(key)}`, {\n method: 'DELETE',\n });\n}\n\nexport async function saveModuleAutomations(key, { gateway, strategies }) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULES_PATH}/${encodeURIComponent(key)}/automations`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ gateway, strategies }),\n });\n}\n\n// getAvailableModules() is the single source of truth for \"which modules can\n// be picked in an Admin configuration screen\" (Form Groups, Detail Groups,\n// Row Actions, Module Action Rules, Templates, Role Configuration). It's\n// driven by the curated Menus list (an admin explicitly adds/removes/disables\n// entries there) rather than the `modules` collection, which is really the\n// CRUD/gateway registry and accumulates every module ever wired for storage\n// (including disabled/legacy ones no admin actively curates).\n//\n// Falls back to getModules() when Menus has nothing configured yet, so a\n// project that hasn't set up Menus keeps working exactly as before.\nexport async function getAvailableModules() {\n let menus = [];\n try {\n menus = await getMenuModules();\n } catch {\n menus = [];\n }\n const active = (Array.isArray(menus) ? menus : []).filter((m) => (\n m?.isDeleted !== true\n && String(m?.status ?? 'active').toLowerCase() !== 'inactive'\n && String(m?.status ?? 'active').toLowerCase() !== 'disabled'\n ));\n if (active.length > 0) return active;\n return getModules();\n}\n\n// getModuleCollectionMap resolves module key → the Mongo collection that module\n// is stored in, straight from the module registry (`modules`, the CRUD/gateway\n// registry that owns `collectionName`). It exists so an Admin screen can DISPLAY\n// the collection a configuration will act on without ever asking the admin to\n// type a collection name — the collection is module configuration, and the\n// server resolves it authoritatively on every request regardless of what this\n// map says. If the registry is unreachable the map is simply empty and callers\n// fall back to \"resolved on the server\".\n//\n// Module names are matched loosely on purpose: config is stored sometimes under\n// the singular key and sometimes the plural one (the backend's own\n// ModuleAliasVariants exists for the same reason), so both spellings are\n// indexed here. Real registry entries always win over a generated alias, so an\n// alias can never shadow a module that genuinely exists.\nexport async function getModuleCollectionMap() {\n let modules;\n try {\n modules = await getModules();\n } catch {\n return {};\n }\n const map = {};\n const put = (key, collection, exact) => {\n const k = String(key ?? '').trim().toLowerCase();\n if (!k || !collection) return;\n if (exact || map[k] === undefined) map[k] = collection;\n };\n for (const m of Array.isArray(modules) ? modules : []) {\n const collection = String(m?.collectionName ?? '').trim();\n if (!collection) continue;\n put(m?.key, collection, true);\n }\n // Second pass so aliases never overwrite a registered key from pass one.\n for (const m of Array.isArray(modules) ? modules : []) {\n const collection = String(m?.collectionName ?? '').trim();\n const key = String(m?.key ?? '').trim().toLowerCase();\n if (!collection || !key) continue;\n if (key.endsWith('s')) put(key.slice(0, -1), collection, false);\n else put(`${key}s`, collection, false);\n }\n return map;\n}\n\n// ── Admin Form Groups ─────────────────────────────────────────────────────────\n\nconst FORM_GROUPS_PATH = '/admin/form-groups';\nconst CONFIG_DEFAULTS_PATH = '/admin/config-defaults';\n\nconst NIL_OBJECT_ID = '000000000000000000000000';\n\n/** True for an absent id or Go's zero-value ObjectID (24 zeros). */\nfunction isNilObjectId(value) {\n const id = String(value ?? '').trim();\n return id === '' || id === NIL_OBJECT_ID;\n}\n\nfunction formGroupScopeQuery(scope = {}) {\n const params = new URLSearchParams();\n if (scope.module) params.set('module', scope.module);\n if (scope.clientId) params.set('clientId', scope.clientId);\n if (scope.region) params.set('region', scope.region);\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n}\n\nexport async function getAdminFormGroups(module = '', scope = {}) {\n const query = formGroupScopeQuery({ ...scope, module: module || scope.module || '' });\n const path = `${FORM_GROUPS_PATH}${query}`;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const payload = json?.data;\n // Backend returns EITHER a flat array of groups, OR a wrapper object\n // { id, module, ..., groups: [...] }. Support both shapes.\n if (Array.isArray(payload)) return payload;\n if (payload && Array.isArray(payload.groups)) return payload.groups;\n if (Array.isArray(json)) return json;\n if (json && Array.isArray(json.groups)) return json.groups;\n return [];\n}\n\nexport async function getAdminClients({ offset = 0, limit = 100, sortBy = 'new', searchvalue = '' } = {}) {\n const params = new URLSearchParams({\n offset: String(offset),\n limit: String(limit),\n sortBy,\n searchvalue,\n });\n // Scope the list to the active tenant/business/businessUnit captured from the\n // login token (authApi stores these on login). The backend uses them when\n // present, otherwise falls back to the token claims — so the dropdown lists\n // the right clients for the selected project, same as the legacy flow.\n const tenantId = localStorage.getItem('tenantId') || '';\n const businessId = localStorage.getItem('businessId') || '';\n const businessUnitId = localStorage.getItem('businessUnitId') || '';\n if (tenantId) params.set('tenantId', tenantId);\n if (businessId) params.set('businessId', businessId);\n if (businessUnitId) params.set('businessUnitId', businessUnitId);\n const json = await fetchJsonWithAuth(AUTH_URL, `/client/view?${params.toString()}`);\n const data = json?.data ?? json;\n return data?.clients ?? data?.Clients ?? data?.items ?? [];\n}\n\nexport async function getModuleFields(module) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/filter-dropdown-fields?module=${encodeURIComponent(module)}`);\n const fields = json?.data ?? json;\n return Array.isArray(fields) ? fields : [];\n}\n\nexport async function createAdminFormGroup(group, scope = {}) {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}${formGroupScopeQuery(scope)}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(group),\n });\n}\n\nexport async function updateAdminFormGroup(id, group, scope = {}) {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/${id}${formGroupScopeQuery(scope)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(group),\n });\n}\n\n// Scope-aware, like create/update: with a client selected the group is removed\n// from THAT client's list only. A client-scoped config is a fork of the module\n// default and keeps the same group ids, so an unscoped delete would clear the\n// group from the default and every other client at once.\nexport async function deleteAdminFormGroup(id, scope = {}) {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/${id}${formGroupScopeQuery(scope)}`, { method: 'DELETE' });\n}\n\nexport async function seedAdminFormGroups() {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/seed`, { method: 'POST' });\n}\n\nexport async function requestConfigOtp(scope, action) {\n return fetchJsonWithAuth(AUTH_URL, `${CONFIG_DEFAULTS_PATH}/request-otp`, {\n method: 'POST',\n body: JSON.stringify({ scope, action }),\n });\n}\n\nexport async function applyConfigDefault(scope, action, otp) {\n return fetchJsonWithAuth(AUTH_URL, `${CONFIG_DEFAULTS_PATH}/apply`, {\n method: 'POST',\n body: JSON.stringify({ scope, action, otp }),\n });\n}\n\n// ── Admin Detail-View Config ──────────────────────────────────────────────────\n// Per-module customization layered on top of the form groups, applied only by\n// the detail view (?view=detail). Shape: { module, fields:[…], merges:[…] }.\n\nconst DETAIL_CONFIG_PATH = '/admin/detail-config';\n\n// Client scope mirrors the form-group admin exactly: pass a clientId to read /\n// write THAT client's detail config, omit it for the module default. The backend\n// falls back to the default when the client has none of its own, so a client\n// with no overrides still renders.\nexport async function getDetailConfig(module, scope = {}) {\n const query = formGroupScopeQuery({ module, clientId: scope.clientId });\n const json = await fetchJsonWithAuth(AUTH_URL, `${DETAIL_CONFIG_PATH}${query}`);\n const data = json?.data ?? json ?? {};\n return {\n module: data.module ?? module,\n // Which scope the response belongs to — '' means the module default. Go\n // serialises an unset ObjectID as 24 zeros, which is NOT a client.\n clientId: isNilObjectId(data.clientId) ? '' : String(data.clientId),\n fields: Array.isArray(data.fields) ? data.fields : [],\n merges: Array.isArray(data.merges) ? data.merges : [],\n };\n}\n\nexport async function saveDetailConfig({ module, fields = [], merges = [], clientId = '' }) {\n return fetchJsonWithAuth(AUTH_URL, DETAIL_CONFIG_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n // clientId travels in the BODY (this is a JSON POST); the backend treats an\n // absent/zero id as \"the module default\".\n body: JSON.stringify({ module, fields, merges, ...(clientId ? { clientId } : {}) }),\n });\n}\n\n// ── Detail-View Display Defaults ──────────────────────────────────────────────\n// Tenant-wide display settings (S3 base, date format, boolean/empty labels,\n// separator, document name order) — the configurable home of what used to be\n// hard-coded in fileUtils.js / FieldValue.jsx.\n\nconst DETAIL_DEFAULTS_PATH = '/admin/detail-defaults';\n\nexport async function getDetailDefaults() {\n const json = await fetchJsonWithAuth(AUTH_URL, DETAIL_DEFAULTS_PATH);\n return json?.data ?? json ?? {};\n}\n\nexport async function saveDetailDefaults(defaults) {\n return fetchJsonWithAuth(AUTH_URL, DETAIL_DEFAULTS_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(defaults),\n });\n}\n\n// ── AI Service registry (backends AI Actions can call) ─────────────────────\n\nconst AI_SERVICE_CONFIG_PATH = '/admin/ai-service-config';\n\nexport async function getAiServiceConfigs() {\n const json = await fetchJsonWithAuth(AUTH_URL, AI_SERVICE_CONFIG_PATH);\n return json?.data ?? json ?? [];\n}\n\nexport async function saveAiServiceConfig(config) {\n return fetchJsonWithAuth(AUTH_URL, AI_SERVICE_CONFIG_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n}\n\nexport async function deleteAiServiceConfig(serviceKey) {\n return fetchJsonWithAuth(AUTH_URL, `${AI_SERVICE_CONFIG_PATH}?serviceKey=${encodeURIComponent(serviceKey)}`, {\n method: 'DELETE',\n });\n}\n\n// ── Background Check integration ───────────────────────────────────────────\n\nconst BGC_INTEGRATION_CONFIG_PATH = '/admin/bgc-integration-config';\n\nexport async function getBgcIntegrationConfig() {\n const json = await fetchJsonWithAuth(AUTH_URL, BGC_INTEGRATION_CONFIG_PATH);\n return json?.data ?? json ?? { updateMethod: 'MANUAL', enabled: true };\n}\n\nexport async function saveBgcIntegrationConfig(config) {\n return fetchJsonWithAuth(AUTH_URL, BGC_INTEGRATION_CONFIG_PATH, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n}\n\n// Safe runtime view for consumers such as the onboarding BGC stage. It never\n// contains credential values, only credentialsConfigured/webhookConfigured.\nexport async function getBgcIntegrationSummary() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/integration-config/bgc/summary');\n return json?.data ?? json ?? { updateMethod: 'MANUAL', enabled: true };\n}\n\nconst INTEGRATION_CONFIG_PATH = '/admin/integration-config';\n\nexport async function getIntegrationConfigs() {\n const json = await fetchJsonWithAuth(AUTH_URL, INTEGRATION_CONFIG_PATH);\n return json?.data ?? json ?? [];\n}\n\nexport async function getIntegrationConfig(integrationKey) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `${INTEGRATION_CONFIG_PATH}/${encodeURIComponent(integrationKey)}`,\n );\n return json?.data ?? json;\n}\n\nexport async function saveIntegrationConfig(config) {\n const key = config?.integrationKey;\n const path = key\n ? `${INTEGRATION_CONFIG_PATH}/${encodeURIComponent(key)}`\n : INTEGRATION_CONFIG_PATH;\n return fetchJsonWithAuth(AUTH_URL, path, {\n method: key ? 'PUT' : 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n}\n\nexport async function deleteIntegrationConfig(integrationKey) {\n return fetchJsonWithAuth(\n AUTH_URL,\n `${INTEGRATION_CONFIG_PATH}/${encodeURIComponent(integrationKey)}`,\n { method: 'DELETE' },\n );\n}\n\nexport async function testIntegrationConnection(config) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${INTEGRATION_CONFIG_PATH}/test`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n return json?.data ?? json;\n}\n\nexport async function getIntegrationSummary(integrationKey) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/integration-config/${encodeURIComponent(integrationKey)}/summary`,\n );\n return json?.data ?? json;\n}\n\nexport async function applyIntegrationStatus(integrationKey, recordId, providerResponse) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/integration-config/${encodeURIComponent(integrationKey)}/apply-status/${encodeURIComponent(recordId)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(providerResponse),\n },\n );\n return json?.data ?? json;\n}\n\n// getModuleDetailPreview fetches a record's detail-view groups WITH resolved\n// values (references → {id,value}, documents → full object) so the admin page\n// can preview exactly what the detail view will render.\nexport async function getModuleDetailPreview(module, id) {\n const path = `${FORM_GROUPS_PATH}?module=${encodeURIComponent(module)}&id=${encodeURIComponent(id)}&view=detail`;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const data = json?.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\n// ── Roles ─────────────────────────────────────────────────────────────────────\n\nfunction normalizeRole(record) {\n return {\n key: record.role_id ?? record.id,\n roleId: record.role_id ?? record.id,\n roleName: record.role_name ?? 'N/A',\n description: record.role_description ?? 'N/A',\n userCount: record.user_count ?? 0,\n roleType: record.role_type ?? 0,\n raw: record,\n };\n}\n\nexport function getRoleId(role) {\n return role?.raw?.role_id ?? role?.roleId ?? role?.key;\n}\n\nexport async function getAllRoles() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/get-roles');\n const payload = json.data ?? json;\n const defaultRoles = firstArray(payload?.zinnext_default).map(normalizeRole);\n const customRoles = firstArray(payload?.custom_roles).map(normalizeRole);\n const all = [...defaultRoles, ...customRoles];\n return { roles: all, total: all.length };\n}\n\n// ── Teams ─────────────────────────────────────────────────────────────────────\n\nfunction normalizeTeam(record) {\n return {\n key: record.teamId,\n teamId: record.teamId,\n teamName: record.teamName ?? 'N/A',\n manager: record.reportingManager ?? 'N/A',\n managerId: record.reportingManagerId ?? 0,\n memberCount: record.noOfTeamMembers ?? 0,\n members: firstArray(record.teamMembers),\n memberIds: firstArray(record.teamMembersIds),\n raw: record,\n };\n}\n\nexport function getTeamId(team) {\n return team?.raw?.teamId ?? team?.teamId ?? team?.key;\n}\n\nexport async function getAllTeams({ offset = 0, limit = 10, sortBy = 'new', search = '' } = {}) {\n const params = new URLSearchParams({ searchquery: search, sortBy, limit: String(limit), offset: String(offset) });\n const json = await fetchJsonWithAuth(AUTH_URL, `/get-teams?${params}`);\n const payload = json.data ?? json;\n const teams = firstArray(payload?.teams, payload, json?.teams);\n const total = payload?.totalCount ?? payload?.total ?? teams.length;\n return { teams: teams.map(normalizeTeam), total };\n}\n\nexport async function updateTeam(team, { teamName, managerId, memberIds = [] }) {\n const teamId = getTeamId(team);\n return fetchJsonWithAuth(AUTH_URL, `/team/edit?teamId=${teamId}`, {\n method: 'PUT',\n body: JSON.stringify({\n team_name: teamName,\n team_manager: managerId,\n other_team_members: memberIds,\n reporting_team_member: [],\n }),\n });\n}\n\n// ── Role / Team Field Config ──────────────────────────────────────────────────\n\nasync function fetchFieldConfigModule(apiModule, idParam, idValue, configType = 'listView') {\n const params = new URLSearchParams({ module: apiModule, userId: '0', [idParam]: String(idValue), configType });\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}?${params}`);\n const payload = json.data ?? json;\n return firstArray(payload, payload?.visibleFields, json?.visibleFields);\n}\n\nexport async function getRoleModuleFields(role, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const roleId = getRoleId(role);\n const customizableModules = normalizeCustomizableModules(modules);\n const entries = await Promise.all(\n customizableModules.map(async ({ key, apiModule }) => {\n const fields = await fetchFieldConfigModule(apiModule, 'roleId', roleId, configType);\n return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, roleId }))];\n })\n );\n return Object.fromEntries(entries);\n}\n\nexport async function getTeamModuleFields(team, configType = 'listView') {\n const teamId = getTeamId(team);\n const entries = await Promise.all(\n CUSTOMIZABLE_MODULES.map(async ({ key, apiModule }) => {\n const fields = await fetchFieldConfigModule(apiModule, 'teamId', teamId, configType);\n return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, teamId }))];\n })\n );\n return Object.fromEntries(entries);\n}\n\nfunction visibleFieldPayload(fields, configType) {\n const isListView = !configType || configType === 'listView';\n return fields.map((field, index) => {\n const base = {\n label: field.fieldName, field: field.fieldKey,\n isVisible: field.isVisible, type: field.type ?? 'text', order: field.order ?? index,\n };\n if (isListView) {\n Object.assign(base, {\n isLink: field.isLink ?? false, linkTemplate: field.linkTemplate ?? '',\n linkType: field.linkType ?? 'internal', linkTarget: field.linkTarget ?? '_self',\n action: field.action ?? 'navigate',\n secondaryField: field.secondaryField ?? '', secondaryLabel: field.secondaryLabel ?? '',\n secondaryFields: Array.isArray(field.secondaryFields) ? field.secondaryFields : [],\n secondarySeparator: field.secondarySeparator ?? '',\n lookup: field.lookup ?? null, actionButtons: field.actionButtons ?? [],\n derived: field.derived ?? null, computed: field.computed ?? null,\n renderType: field.renderType ?? '', renderConfig: field.renderConfig ?? null,\n valueStyles: Array.isArray(field.valueStyles) ? field.valueStyles : [],\n commonStyle: field.commonStyle ?? null, displayTemplate: field.displayTemplate ?? '',\n });\n } else {\n base.isEditable = field.isEditable ?? false;\n base.ismandatory = field.ismandatory ?? field.isMandatory ?? false;\n if (configType === 'filter') {\n base.filterInputType = field.filterInputType ?? 'dropdown';\n base.isDefaultFilter = Boolean(field.isDefaultFilter);\n base.dataSource = field.dataSource ?? 'module';\n if (field.dataSource === 'group') base.groupName = field.groupName ?? '';\n if (field.lookup) base.lookup = field.lookup;\n } else {\n if (['select', 'radio', 'checkbox'].includes(field.type)) {\n base.dataSource = field.dataSource ?? 'module';\n if (field.dataSource === 'group') base.groupName = field.groupName ?? '';\n }\n }\n }\n return base;\n });\n}\n\nexport async function updateRoleModuleFieldConfig(role, module, fields, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const roleId = getRoleId(role);\n const apiModule = normalizeCustomizableModules(modules).find((m) => m.key === module)?.apiModule ?? module;\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/role`, {\n method: 'PUT',\n body: JSON.stringify({ module: apiModule, roleId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n });\n}\n\n// ── Role Configure → Form (derived from Form Groups) ─────────────────────────\n// Unlike getRoleModuleFields/updateRoleModuleFieldConfig above (an\n// independently-seeded flat field list), these back the Form tab's grouped\n// view: the backend derives groups/fields straight from the module's Form\n// Groups config and overlays this role's saved grants (GET\n// /admin/role-form-permissions), so Form Groups stays the single source of\n// truth and new/renamed/removed fields sync automatically. Saving still goes\n// through the existing PUT /admin/field-config/role (configType \"form\") —\n// the backend clamps it against Form Groups' global Show flags either way.\nconst ROLE_FORM_PERMISSIONS_PATH = '/admin/role-form-permissions';\n\n// Must match Be_Auth_DevOps models/roleFormPermissions.go's\n// roleGroupPermissionKey — the synthetic field-config key a group-level\n// ON/OFF toggle is persisted under, alongside real per-field grants, in the\n// same flat visibleFields list (no separate schema/endpoint needed).\nfunction roleGroupPermissionKey(groupName) {\n return `__group__:${groupName}`;\n}\n\n// scope: { clientId, region } — same client/region scope as\n// getAdminFormGroups/AddFormV1's effectiveClientId+effectiveRegion, so a\n// client- or region-scoped Form Groups override (e.g. a per-client Jobs\n// variant) is reflected here too, not just the tenant-wide default. Generic\n// for any module — GetFormGroupConfigScoped falls back to the default when\n// no scoped config exists for the given module, so this is always safe to pass.\nexport async function getRoleFormPermissions(role, module, scope = {}) {\n const roleId = getRoleId(role);\n const params = new URLSearchParams({ module, roleId: String(roleId ?? '') });\n if (scope.clientId) params.set('clientId', scope.clientId);\n if (scope.region) params.set('region', scope.region);\n const json = await fetchJsonWithAuth(AUTH_URL, `${ROLE_FORM_PERMISSIONS_PATH}?${params}`);\n const payload = json?.data ?? json;\n return Array.isArray(payload) ? payload : [];\n}\n\n// groups: [{ name, label, enabled, locked, fields: [{ field, label, enabled, locked, editable }] }]\n// as edited in the UI (see RoleFormPermissionsEditor). Group toggles are sent\n// as synthetic visibleFields rows so the existing role-save + clamp pipeline\n// needs no changes.\n//\n// A role's field-level grants are NOT stored per client/region — they're one\n// shared set overlaid onto whichever scoped Form Groups structure a caller\n// requests (getRoleFormPermissions). So any group/field that's `locked` in\n// the CURRENTLY VIEWED scope (globally disabled there, but possibly enabled\n// under the tenant-wide default or a different client's scope) is left out\n// of the payload entirely — sending its scope-computed `enabled: false`\n// would silently overwrite the role's real, scope-independent stored\n// preference the next time anyone loads a different scope. Locked items\n// aren't editable in this view anyway (their Switch is disabled), so there's\n// nothing this save is meant to change for them.\nexport async function saveRoleFormPermissions(role, module, groups) {\n const roleId = getRoleId(role);\n const visibleFields = [];\n let order = 0;\n for (const group of groups) {\n if (!group.locked) {\n visibleFields.push({\n label: group.label,\n field: roleGroupPermissionKey(group.name),\n // A group carries two independent role toggles: isVisible = shown,\n // isEditable = not-disabled (read-only when false). Default both true.\n isVisible: group.enabled,\n isEditable: group.editable !== false,\n order: order++,\n });\n }\n for (const field of group.fields ?? []) {\n if (field.locked) continue;\n visibleFields.push({\n label: field.label,\n field: field.field,\n isVisible: field.enabled,\n isEditable: field.editable !== false,\n order: order++,\n });\n }\n }\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/role`, {\n method: 'PUT',\n body: JSON.stringify({ module, roleId, configType: 'form', visibleFields }),\n });\n}\n\nexport async function updateTeamModuleFieldConfig(team, module, fields, configType = 'listView') {\n const teamId = getTeamId(team);\n const apiModule = CUSTOMIZABLE_MODULES.find((m) => m.key === module)?.apiModule ?? module;\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/team`, {\n method: 'PUT',\n body: JSON.stringify({ module: apiModule, teamId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n });\n}\n\n// ── Users ─────────────────────────────────────────────────────────────────────\n\nexport async function getAllUsers({ offset = 0, limit = 10, sortBy = 'new', searchQuery = '' } = {}) {\n const params = new URLSearchParams({ offset: String(offset), limit: String(limit), sortBy });\n if (searchQuery) params.set('searchquery', searchQuery);\n const json = await fetchJsonWithAuth(AUTH_URL, `/get-all-users?${params}`);\n const payload = json.data ?? json;\n const users = firstArray(\n payload, json.users, json.rows, json.items, json.records,\n payload?.users, payload?.data, payload?.rows, payload?.items,\n payload?.records, payload?.docs, payload?.result, payload?.results,\n );\n return { users, total: normalizeTotal({ ...json, ...payload }, users) };\n}\n\nexport function getUserId(user) {\n const value = firstValue(\n user?.raw ?? user,\n ['USER_ID', 'user_id', 'userId', 'id', '_id', 'uuid'],\n user?.key\n );\n const num = Number(value);\n return Number.isFinite(num) ? num : value;\n}\n\nexport function getUserRoleId(user) {\n const value = firstValue(user?.raw ?? user, ['ROLE_ID', 'role_id', 'roleId', 'ROLEID'], 0);\n const num = Number(value);\n return Number.isFinite(num) && num > 0 ? num : 0;\n}\n\nasync function getFieldConfigModule(apiModule, userId, roleId, configType) {\n const params = new URLSearchParams({ module: apiModule, userId: String(userId ?? ''), roleId: String(roleId ?? ''), configType });\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}?${params}`);\n const payload = json.data ?? json;\n return firstArray(payload, payload?.visibleFields, json?.visibleFields);\n}\n\nasync function getDropdownModuleFields(apiModule) {\n const json = await fetchJsonWithAuth(SUBMISSIONS_URL, `/filter-dropdown-fields?module=${encodeURIComponent(apiModule)}`);\n const payload = json.data ?? json;\n return firstArray(payload, payload?.fields, payload?.items, payload?.rows, json?.fields);\n}\n\nexport async function getUserModuleFields(user, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const userId = getUserId(user);\n const roleId = getUserRoleId(user);\n const customizableModules = normalizeCustomizableModules(modules);\n const entries = await Promise.all(\n customizableModules.map(async ({ key, apiModule }) => {\n let fields;\n try {\n fields = await getFieldConfigModule(apiModule, userId, roleId, configType);\n } catch {\n try { fields = await getDropdownModuleFields(apiModule); } catch { fields = []; }\n }\n if ((fields?.length ?? 0) === 0) {\n try { fields = await getDropdownModuleFields(apiModule); } catch { fields = []; }\n }\n return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, userId, roleId }))];\n })\n );\n return Object.fromEntries(entries);\n}\n\nexport async function hasUserFieldConfigData(user, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const userId = getUserId(user);\n const roleId = getUserRoleId(user);\n const customizableModules = normalizeCustomizableModules(modules);\n const results = await Promise.all(\n customizableModules.map(async ({ apiModule }) => {\n try {\n const fields = await getFieldConfigModule(apiModule, userId, roleId, configType);\n return (fields?.length ?? 0) > 0;\n } catch {\n return false;\n }\n })\n );\n return results.some(Boolean);\n}\n\nexport async function seedFieldConfig(collections = []) {\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/seed`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ collections }),\n });\n}\n\nexport async function getFieldConfigSeedStatus() {\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/seed-status`);\n const data = json?.data ?? json ?? {};\n return {\n modules: Array.isArray(data.modules) ? data.modules : [],\n seededModules: data.seededModules ?? {},\n };\n}\n\nexport async function updateUserModuleFieldConfig(user, module, fields, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const userId = getUserId(user);\n const roleId = getUserRoleId(user);\n const apiModule = normalizeCustomizableModules(modules).find((m) => m.key === module)?.apiModule ?? module;\n return fetchJsonWithAuth(AUTH_URL, FIELD_CONFIG_PATH, {\n method: 'PUT',\n body: JSON.stringify({ module: apiModule, userId, roleId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n });\n}\n\nexport async function updateUserModuleFields(user, moduleFields, configType = 'listView') {\n return Promise.all(\n Object.entries(moduleFields).map(([module, fields]) => updateUserModuleFieldConfig(user, module, fields, configType))\n );\n}\n\nexport async function uploadFormGroupIcon(file) {\n const token = localStorage.getItem('authToken');\n const formData = new FormData();\n formData.append('icon', file);\n const res = await fetch(`${AUTH_URL}/admin/form-groups/icon`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n if (!res.ok) { const e = new Error(`Upload failed: ${res.status}`); e.status = res.status; throw e; }\n const json = await res.json();\n return json.data ?? json;\n}\n\nexport async function uploadListViewActionIcon(file) {\n const token = localStorage.getItem('authToken');\n const formData = new FormData();\n formData.append('icon', file);\n const res = await fetch(`${AUTH_URL}/admin/field-config/action-icon`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n if (!res.ok) { const e = new Error(`Upload failed: ${res.status}`); e.status = res.status; throw e; }\n const json = await res.json();\n return json.data ?? json;\n}\n\n// Module options for the Actions admin screens: the configured menu/module\n// catalog PLUS every collection in this app's database, so row actions can be\n// configured for ANY module (onboarding, movements, a brand-new collection…)\n// without first registering it as a menu module. Resolves per app DB via\n// X-App-Id, so each project sees its own collections.\nexport async function getActionConfigModules() {\n const [modules, collections, existingConfigs] = await Promise.all([\n getAvailableModules().catch(() => []),\n getAvailableCollections().catch(() => []),\n getRowActionConfigs().catch(() => []),\n ]);\n const baseModules = Array.isArray(modules) ? modules : [];\n // Detail-view action variants: a module's DETAIL page can carry its own row\n // actions (e.g. an \"Open JD\" primary button on the submission detail view)\n // without those actions also appearing on the LIST view. The detail page\n // fetches actions under whatever key it passes as DetailHeaderCard's\n // customConfigName — \"submissiondetails\", \"submissionJob\" — which follows no\n // derivable rule. This used to GENERATE `${key}detail` names, which produced\n // keys nothing ever requests (\"submissionsdetail\" vs the real\n // \"submissiondetails\"), so configuring one silently changed nothing.\n // Offer the keys that actually exist in rowActionConfig instead.\n const variantKeys = existingConfigs\n .map((config) => String(config?.module ?? '').trim())\n .filter(Boolean);\n return [\n ...baseModules,\n ...(Array.isArray(collections) ? collections : []),\n ...variantKeys,\n ];\n}\n\n// Generic module CRUD — used by the Masters admin screen (locations/taxes),\n// but not specific to either: works for any module via the dynamic gateway.\nexport async function listModuleRecords(module, { page = 1, limit = 100 } = {}) {\n const params = new URLSearchParams({ module, page: String(page), limit: String(limit) });\n const json = await fetchJsonWithAuth(AUTH_URL, `/module/list?${params}`);\n const data = json?.data ?? json ?? {};\n return { items: Array.isArray(data.data) ? data.data : [], total: data.pagination?.total ?? 0 };\n}\n\nexport async function createModuleRecordGeneric(module, payload) {\n return fetchJsonWithAuth(AUTH_URL, `/module/create?module=${encodeURIComponent(module)}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n}\n\nexport async function updateModuleRecordGeneric(module, id, payload) {\n return fetchJsonWithAuth(AUTH_URL, `/module/update/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n}\n\nexport async function deleteModuleRecordGeneric(module, id) {\n return fetchJsonWithAuth(AUTH_URL, `/module/delete/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`, {\n method: 'DELETE',\n });\n}\n\n// Searchable \"City, State, Country\" options from the locationMasters collection\n// — the same source AddFormV1's `location` field type uses. Used by the Location\n// Tax Master screen to pick a real location instead of free-typing one.\nexport async function getLocationMasterOptions(search = '', limit = 50) {\n const params = new URLSearchParams({ search, limit: String(limit), offset: '0' });\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/location-dropdown-values?${params}`);\n const raw = json?.data ?? json ?? [];\n return (Array.isArray(raw) ? raw : [])\n .map((item) => {\n const label = typeof item === 'string' ? item : (item.label ?? item.value ?? '');\n return { label, value: label };\n })\n .filter((option) => option.label);\n}\n\n// Generic record options for any lookup-configured field — used by the Form\n// Groups editor's \"Conditional Default\" record picker (e.g. choose the default\n// Employer applied when Contract Type is W2). Returns { label, value } pairs\n// where value is the record's valueField (usually _id).\nexport async function getLookupRecordOptions(collection, displayField, valueField = '_id') {\n const params = new URLSearchParams({\n collection: String(collection),\n displayField: String(displayField),\n valueField: String(valueField),\n });\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/lookup-dropdown-values?${params}`);\n const raw = json?.data ?? json ?? [];\n return (Array.isArray(raw) ? raw : [])\n .map((item) => ({\n label: item.label ?? item.displayValue ?? item[displayField] ?? String(item.value ?? ''),\n value: String(item.value ?? item._id ?? item.id ?? ''),\n }))\n .filter((option) => option.value && option.label);\n}\n\n// Default Fields — per-module field keys forced always-shown + always-required.\n// getModuleDefaultFields is also called at runtime by AddFormV1/EditFormV1.\n// Saving is OTP-gated: request the code (requestConfigOtp('defaultFields','save'))\n// then pass it here.\nexport async function getModuleDefaultFields(module) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/default-fields?module=${encodeURIComponent(module)}`);\n const data = json?.data ?? json ?? {};\n return Array.isArray(data.fields) ? data.fields : [];\n}\n\nexport async function saveModuleDefaultFields(module, fields, otp) {\n return fetchJsonWithAuth(AUTH_URL, '/admin/default-fields', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module, fields, otp }),\n });\n}\n\n// Generic email verification — used by any admin-configured\n// field.verifyAction === \"email\" (AddFormV1/EditFormV1's VerifyFieldButton).\n//\n// The backend runs a layered check (syntax -> MX -> Mailgun -> SMTP mailbox\n// probe -> catch-all detection), so the verdict is richer than a boolean:\n//\n// valid keep/reject the address (unchanged meaning — the form gate)\n// result 'deliverable' | 'undeliverable' | 'risky' | 'unknown'\n// mailboxConfirmed the receiving server confirmed THIS mailbox specifically\n// catchAll the domain accepts every address, so the mailbox is unproven\n//\n// mailboxConfirmed is the one to trust for \"this person will actually get mail\".\n// A catch-all domain (Google Workspace default, many corporates) can never be\n// proven from outside — the UI says so rather than implying certainty.\nexport async function verifyEmail(email) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/email-verify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email }),\n });\n const data = json?.data ?? json ?? {};\n return {\n valid: Boolean(data.valid),\n reason: data.reason ?? '',\n result: data.result ?? '',\n mailboxConfirmed: Boolean(data.mailboxConfirmed),\n catchAll: Boolean(data.catchAll),\n disposable: Boolean(data.disposable),\n roleAddress: Boolean(data.roleAddress),\n risk: data.risk ?? '',\n checks: Array.isArray(data.checks) ? data.checks : [],\n };\n}\n\nexport async function getAvailableCollections() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/lookup/collections');\n const data = json.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\nexport async function getCollectionFields(collection) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/lookup/collection-fields?collection=${encodeURIComponent(collection)}`);\n const data = json.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\nexport async function getArraySubfields(module, field) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/field-array-subfields?module=${encodeURIComponent(module)}&field=${encodeURIComponent(field)}`);\n const data = json.data ?? json;\n return Array.isArray(data?.fields) ? data.fields : [];\n}\n\n// ── Menu Modules & Actions (master data management) ──────────────────────────\n\nexport async function getMenuModules() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-modules');\n return firstArray(json.data, json);\n}\n\nexport async function createMenuModule({ menuName, apiUrl = '', menuType = 'menu', parentMenuId = 0, displayOrder = 0 }) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-modules', {\n method: 'POST',\n body: JSON.stringify({ menuName, apiUrl, menuType, parentMenuId, displayOrder }),\n });\n return json.data ?? json;\n}\n\nexport async function updateMenuModule(id, { menuName }) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-modules/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ menuName }),\n });\n return json.data ?? json;\n}\n\nexport async function deleteMenuModule(id) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-modules/${id}`, { method: 'DELETE' });\n return json.data ?? json;\n}\n\nexport async function getMenuActions() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-actions');\n return firstArray(json.data, json);\n}\n\nexport async function createMenuAction({ permissionName, permissionKey }) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-actions', {\n method: 'POST',\n body: JSON.stringify({ permissionName, permissionKey }),\n });\n return json.data ?? json;\n}\n\nexport async function updateMenuAction(id, { permissionName }) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-actions/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ permissionName }),\n });\n return json.data ?? json;\n}\n\nexport async function deleteMenuAction(id) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-actions/${id}`, { method: 'DELETE' });\n return json.data ?? json;\n}\n\n// ── Permissions ───────────────────────────────────────────────────────────────\n\nexport async function getRolePermissions(roleId) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/get-role-details?roleid=${roleId}`);\n return (json?.data ?? json) ?? {};\n}\n\nexport async function updateRolePermissions(roleId, menus) {\n return fetchJsonWithAuth(AUTH_URL, '/edit-role-details', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ roleId, menus }),\n });\n}\n\n// Resolved permissions for the logged-in user's own role — moduleName ->\n// actionKey -> allowed (or the wildcard shape { \"*\": { \"*\": true } } for\n// full-access roles). Fetched once on login by PermissionContext.\nexport async function getMyPermissions() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/me/permissions');\n return (json?.data ?? json) ?? {};\n}\n\n// ── Custom Forms ──────────────────────────────────────────────────────────────\n\nconst CUSTOM_FORMS_PATH = '/admin/custom-forms';\n\nexport async function getCustomForms(module = '', type = '', action = '') {\n const params = new URLSearchParams();\n if (module) params.append('module', module);\n if (type) params.append('formType', type);\n if (action) params.append('action', action);\n const path = `${CUSTOM_FORMS_PATH}?${params.toString()}`;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\nexport async function createCustomForm(form) {\n return fetchJsonWithAuth(AUTH_URL, CUSTOM_FORMS_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(form),\n });\n}\n\nexport async function updateCustomForm(id, form) {\n return fetchJsonWithAuth(AUTH_URL, `${CUSTOM_FORMS_PATH}/${id}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(form),\n });\n}\n\nexport async function deleteCustomForm(id) {\n return fetchJsonWithAuth(AUTH_URL, `${CUSTOM_FORMS_PATH}/${id}`, { method: 'DELETE' });\n}\n\n// ── Row Action Config ─────────────────────────────────────────────────────────\n\nconst ROW_ACTION_CONFIG_PATH = '/admin/row-action-config';\n\n// Keep the module key written by Admin identical to the key used by ListView.\n// Module labels/keys supplied by the module catalog (or typed into the tags\n// selector) may contain casing and surrounding whitespace, while list routes\n// consistently request normalized keys.\nfunction normalizeRowActionModule(module) {\n return String(module ?? '').trim().toLowerCase();\n}\n\nexport async function getRowActionConfigs(module = '') {\n const normalizedModule = normalizeRowActionModule(module);\n const path = normalizedModule\n ? `${ROW_ACTION_CONFIG_PATH}?module=${encodeURIComponent(normalizedModule)}`\n : ROW_ACTION_CONFIG_PATH;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\nexport async function createRowActionConfig({ module, roleId = 0, rowActions = [] }) {\n const normalizedModule = normalizeRowActionModule(module);\n return fetchJsonWithAuth(AUTH_URL, ROW_ACTION_CONFIG_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module: normalizedModule, roleId, rowActions }),\n });\n}\n\nexport async function updateRowActionConfig(id, rowActions = []) {\n return fetchJsonWithAuth(AUTH_URL, `${ROW_ACTION_CONFIG_PATH}/${id}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ rowActions }),\n });\n}\n\nexport async function deleteRowActionConfig(id) {\n return fetchJsonWithAuth(AUTH_URL, `${ROW_ACTION_CONFIG_PATH}/${id}`, { method: 'DELETE' });\n}\n\nexport async function seedRowActionConfigs() {\n return fetchJsonWithAuth(AUTH_URL, `${ROW_ACTION_CONFIG_PATH}/seed`, { method: 'POST' });\n}\n\n// ── Module Action Rules ───────────────────────────────────────────────────────\n// Per-module configurable rules stored in auth repo, returned by module-data-list.\n// Frontend evaluates these rules per-row to show/hide row action buttons.\n\nconst MODULE_ACTION_RULES_PATH = '/admin/module-action-rules';\n\nexport async function getModuleActionRules(module) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}?module=${encodeURIComponent(module)}`);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\nexport async function createModuleActionRule({ module, name, description = '', conditions = [], blockedActions = [], disabledActions = [], isActive = true }) {\n return fetchJsonWithAuth(AUTH_URL, MODULE_ACTION_RULES_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module, name, description, conditions, blockedActions, disabledActions, isActive }),\n });\n}\n\nexport async function updateModuleActionRule(id, { name, description = '', conditions = [], blockedActions = [], disabledActions = [], isActive = true }) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}/${id}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name, description, conditions, blockedActions, disabledActions, isActive }),\n });\n}\n\nexport async function deleteModuleActionRule(id) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}/${id}`, { method: 'DELETE' });\n}\n","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 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 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 throw new Error(serverErrorMessage(errorText) || `API ${res.status}: ${res.statusText}`);\n }\n\n const json = await res.json();\n return json.data ?? json;\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","// 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 dateFormat: 'DD MMM YYYY',\n booleanTrueLabel: 'Yes',\n booleanFalseLabel: 'No',\n emptyPlaceholder: '-',\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.booleanTrueLabel) out.booleanTrueLabel = d.booleanTrueLabel;\n if (d.booleanFalseLabel) out.booleanFalseLabel = d.booleanFalseLabel;\n if (d.emptyPlaceholder) out.emptyPlaceholder = d.emptyPlaceholder;\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","// Runtime gate consulted by AddFormV1/EditFormV1 so a role's Form\n// Configuration (Role Configure → Form) actually restricts the real add/edit\n// form, not just the admin preview. rolePerms is the array returned by\n// GET /admin/role-form-permissions (see adminApi.js getRoleFormPermissions):\n// [{ name, enabled, locked, fields: [{ field, enabled, locked }] }].\n//\n// Fails OPEN (returns true) whenever rolePerms is null/empty/not-yet-loaded —\n// a module or role with no derived permissions behaves exactly as before\n// (gated only by Form Groups' own show/visiblePermission), so this is purely\n// additive and cannot hide a field that used to render.\nexport function groupAllowedByRole(rolePerms, groupName) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n return group.enabled !== false;\n}\n\nexport function fieldAllowedByRole(rolePerms, groupName, fieldKey) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n const field = group.fields?.find((f) => f.field === fieldKey);\n if (!field) return true;\n return field.enabled !== false;\n}\n\n// Editability gate — the \"disable\" side of Role Configure → Form. A group or\n// field marked editable:false is still SHOWN but rendered read-only. Fails\n// OPEN (returns true = editable) when nothing is configured, so a role/module\n// with no derived permissions stays fully editable, exactly as before.\nexport function groupEditableByRole(rolePerms, groupName) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n return group.editable !== false;\n}\n\nexport function fieldEditableByRole(rolePerms, groupName, fieldKey) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n if (group.editable === false) return false; // group disabled → every field read-only\n const field = group.fields?.find((f) => f.field === fieldKey);\n if (!field) return true;\n return field.editable !== false;\n}\n","import DOMPurify from 'dompurify';\n\nconst RICH_TEXT_TAGS = [\n 'p', 'br', 'strong', 'b', 'em', 'i', 'u', 's', 'ul', 'ol', 'li',\n 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'a', 'code', 'pre',\n];\nconst RICH_TEXT_ATTRS = ['href', 'title', 'target', 'rel'];\nconst INVISIBLE_RE = /[\\u200B-\\u200D\\u2060\\uFEFF]/g;\n// Security normalization intentionally targets ASCII control characters.\n// eslint-disable-next-line no-control-regex\nconst CONTROL_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\nconst NAME_RE = /^[\\p{L}\\p{M}\\p{N} .'-]+$/u;\nconst PHONE_RE = /^\\+?[0-9 ()-]+$/;\nconst NUMBER_RE = /^-?(?:\\d+|\\d*\\.\\d+)$/;\n\nconst ATTACK_PATTERNS = [\n { re: /<\\s*\\/?\\s*(?:script|iframe|object|embed|svg|img|form|input|style|link|meta)\\b/i, message: 'HTML/script content is not allowed' },\n { re: /\\b(?:javascript|vbscript)\\s*:|\\bdata\\s*:\\s*text\\/html/i, message: 'Unsafe URL protocol is not allowed' },\n { re: /\\bon[a-z]+\\s*=/i, message: 'HTML event handlers are not allowed' },\n { re: /\\bunion\\s+(?:all\\s+)?select\\b|\\b(?:drop\\s+table|delete\\s+from|insert\\s+into|xp_cmdshell)\\b/i, message: 'Database command patterns are not allowed' },\n { re: /(?:['\"]\\s*)?\\b(?:or|and)\\s+\\d+\\s*=\\s*\\d+/i, message: 'Injection patterns are not allowed' },\n { re: /[\"']?\\$(?:where|gt|gte|lt|lte|ne|regex|or|and|expr|function)\\b/i, message: 'MongoDB operators are not allowed in input values' },\n { re: /(?:\\.\\.[/\\\\])|(?:%2e|%2f|%5c)/i, message: 'Path traversal patterns are not allowed' },\n { re: /&&|\\|\\||\\$\\(|\\$\\{|`|\\b(?:rm\\s+-rf|cat\\s+\\/etc\\/|whoami\\b|curl\\s+https?:\\/\\/|wget\\s+https?:\\/\\/)/i, message: 'Command execution patterns are not allowed' },\n];\n\nfunction decodeHtmlEntities(value) {\n if (typeof document === 'undefined') return value;\n const textarea = document.createElement('textarea');\n textarea.innerHTML = value;\n return textarea.value;\n}\n\nexport function canonicalizeForSecurityScan(value) {\n let result = String(value ?? '');\n for (let i = 0; i < 2; i += 1) {\n try {\n const decoded = decodeURIComponent(result);\n if (decoded === result) break;\n result = decoded;\n } catch { break; }\n }\n result = decodeHtmlEntities(result)\n .replace(/\\\\x([0-9a-f]{2})/gi, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)))\n .replace(/\\\\u([0-9a-f]{4})/gi, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)));\n return result.normalize('NFC');\n}\n\nexport function sanitizeRichText(value) {\n return DOMPurify.sanitize(String(value ?? ''), {\n ALLOWED_TAGS: RICH_TEXT_TAGS,\n ALLOWED_ATTR: RICH_TEXT_ATTRS,\n ALLOW_DATA_ATTR: false,\n FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', 'svg', 'img', 'form', 'input'],\n FORBID_ATTR: ['style', 'src', 'srcset'],\n });\n}\n\nfunction validationMax(field) {\n const rule = (field.validations ?? []).find((item) => item?.type === 'max');\n const configured = Number(field.validator?.maxLength ?? rule?.value ?? field.maxLength);\n if (Number.isFinite(configured) && configured > 0) return configured;\n if (field.type === 'email' || field.formatter === 'email') return 255;\n if (field.formatter === 'phone' || field.formatter === 'digits') return 32;\n if (field.formatter === 'name') return 100;\n if (field.type === 'text-editor') return 50000;\n if (field.type === 'textarea') return 5000;\n return 5000;\n}\n\nfunction isMultiline(field) {\n return field.type === 'textarea' || field.type === 'text-editor';\n}\n\nexport function normalizeSecureString(value, field = {}) {\n if (typeof value !== 'string') return value;\n if (field.type === 'text-editor') return sanitizeRichText(value.normalize('NFC'));\n\n let normalized = value.normalize('NFC')\n .replace(INVISIBLE_RE, '')\n .replace(/\\u00A0/g, ' ')\n .replace(CONTROL_RE, '');\n if (isMultiline(field)) {\n normalized = normalized.replace(/\\r\\n?/g, '\\n').replace(/[ \\t]{2,}/g, ' ').trim();\n } else {\n normalized = normalized.replace(/\\s+/g, ' ').trim();\n }\n return normalized;\n}\n\nexport function validateSecureString(value, field = {}) {\n if (typeof value !== 'string' || value === '') return null;\n const label = field.label || field.field || 'Field';\n const canonical = canonicalizeForSecurityScan(value);\n\n if (canonical.includes('\\0') || canonical.includes('\\u0000')) return `${label} contains a null byte`;\n if (field.type !== 'text-editor') {\n const attack = ATTACK_PATTERNS.find(({ re }) => re.test(canonical));\n if (attack) return `${label}: ${attack.message}`;\n } else {\n const dangerousRichText = ATTACK_PATTERNS.slice(0, 3).find(({ re }) => re.test(canonical));\n if (dangerousRichText) return `${label}: ${dangerousRichText.message}`;\n }\n\n const normalized = normalizeSecureString(value, field);\n if ([...normalized].length > validationMax(field)) return `${label} is too long`;\n if (field.formatter === 'name' && normalized && !NAME_RE.test(normalized)) {\n return `${label} allows only letters, numbers, spaces, apostrophes, hyphens and periods`;\n }\n if ((field.formatter === 'phone' || field.formatter === 'digits') && normalized && !PHONE_RE.test(normalized)) {\n return `${label} contains invalid phone characters`;\n }\n if (field.type === 'number' && normalized && !NUMBER_RE.test(normalized)) {\n return `${label} must contain only a valid number`;\n }\n if ((field.type === 'url' || field.formatter === 'url') && normalized && !/^https?:\\/\\//i.test(normalized)) {\n return `${label} must use http:// or https://`;\n }\n return null;\n}\n\nexport function securityValidationRule(field) {\n return {\n validator: (_, value) => {\n const values = Array.isArray(value) ? value : [value];\n const error = values.map((item) => validateSecureString(item, field)).find(Boolean);\n return error ? Promise.reject(new Error(error)) : Promise.resolve();\n },\n };\n}\n\nfunction policyMap(groups = []) {\n const policies = new Map();\n groups.forEach((group) => {\n (group.fields ?? []).forEach((field) => {\n if (field.type === 'file') return;\n const destination = field.payloadKey || field.field;\n if (!destination) return;\n const path = group.addRow ? `${group.payloadKey || group.name}[].${destination}` : destination;\n policies.set(path, field);\n });\n });\n return policies;\n}\n\nexport function securePayload(payload, groups = []) {\n const policies = policyMap(groups);\n const walk = (node, path = '') => {\n if (typeof node === 'string') {\n const field = policies.get(path) ?? {};\n const error = validateSecureString(node, field);\n if (error) throw new Error(error);\n return normalizeSecureString(node, field);\n }\n if (Array.isArray(node)) return node.map((item) => walk(item, `${path}[]`));\n if (node && typeof node === 'object') {\n return Object.fromEntries(Object.entries(node).map(([key, value]) => {\n if (key.startsWith('$') || key.includes('.') || key.includes('\\0')) {\n throw new Error(`Unsafe object key: ${key}`);\n }\n const childPath = path ? `${path}.${key}` : key;\n return [key, walk(value, childPath)];\n }));\n }\n return node;\n };\n return walk(payload);\n}\n","// optionMatching — snap an incoming value onto a field's CONFIGURED option.\n//\n// Any value that arrives from outside the form (an AI parse of a JD or resume,\n// an edit prefill, a cross-module prefill, an import) is free text. A select /\n// radio / checkbox control only selects when the value is character-for-\n// character one of its configured option values, so \"onsite\", \"ONSITE\",\n// \"On Site\" and \"on_site\" all silently failed to select the \"On-Site\" radio —\n// the parse looked like it had worked while the control sat empty.\n//\n// Matching is deliberately CONSERVATIVE and lossless:\n// • only fields that actually declare options are touched;\n// • an unmatched value is returned UNCHANGED, never blanked — a value we\n// cannot map is still shown to the user (and still saved) rather than\n// silently dropped;\n// • matching never invents a selection: it compares against the option's own\n// value and label only, plus whatever aliases the admin configured.\n//\n// Everything here is config-driven; no module, field or option name appears.\n\n// canonical — the comparison key. Case-folded, accent-folded and stripped of\n// every non-alphanumeric character, so \"On-Site\" / \"on site\" / \"ON_SITE\" /\n// \"onsite\" all collapse onto \"onsite\". Digits are kept so \"1\" ≠ \"10\".\nexport function canonical(value) {\n return String(value ?? '')\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '');\n}\n\n// optionEntries — the (canonicalKey → optionValue) pairs a field offers.\n// Both the option's value and its label are accepted as input, because a parse\n// far more often echoes the human label (\"Full Time\") than the stored value.\n// field.optionAliases lets an admin add synonyms the data uses but the option\n// list does not spell out, e.g. { \"WFH\": \"Remote\", \"work from home\": \"Remote\" }.\nexport function optionEntries(field) {\n const pairs = new Map();\n const add = (key, target) => {\n const k = canonical(key);\n // First writer wins: an earlier option keeps the key when a later option's\n // label happens to collapse onto the same string.\n if (k && !pairs.has(k)) pairs.set(k, target);\n };\n\n (Array.isArray(field?.options) ? field.options : []).forEach((option) => {\n if (option === null || option === undefined) return;\n const value = typeof option === 'object' ? option.value : option;\n if (value === undefined || value === null || value === '') return;\n add(value, value);\n if (typeof option === 'object' && option.label !== undefined) add(option.label, value);\n });\n\n Object.entries(field?.optionAliases ?? {}).forEach(([alias, target]) => {\n // An alias may only point AT a real option — otherwise a typo in config\n // would inject a value the control cannot select, which is the exact class\n // of bug this module exists to remove.\n const resolved = pairs.get(canonical(target));\n if (resolved !== undefined) add(alias, resolved);\n });\n\n return pairs;\n}\n\nconst hasOptions = (field) => Array.isArray(field?.options) && field.options.length > 0;\n\n// snapOne — map a single scalar onto an option value, or return it unchanged.\nfunction snapOne(pairs, value) {\n if (value === undefined || value === null || value === '') return value;\n // An object (a resolved reference like { id, value }) is not free text — the\n // reference resolver already owns it, so leave it entirely alone.\n if (typeof value === 'object') return value;\n const match = pairs.get(canonical(value));\n return match === undefined ? value : match;\n}\n\n// snapToOption — the entry point. Arrays map element-wise so a multi-select or\n// a checkbox group snaps every entry. Returns the input untouched for fields\n// with no options, which is most of them.\nexport function snapToOption(field, value) {\n if (!hasOptions(field)) return value;\n const pairs = optionEntries(field);\n if (pairs.size === 0) return value;\n if (Array.isArray(value)) return value.map((item) => snapOne(pairs, item));\n return snapOne(pairs, value);\n}\n\n// unmatchedOptionValues — the entries that could NOT be mapped onto an option.\n// Callers use it to tell the user what a parse failed to place, instead of\n// leaving a control looking mysteriously empty.\nexport function unmatchedOptionValues(field, value) {\n if (!hasOptions(field)) return [];\n const pairs = optionEntries(field);\n const list = Array.isArray(value) ? value : [value];\n return list.filter((item) => item !== undefined && item !== null && item !== ''\n && typeof item !== 'object'\n && !pairs.has(canonical(item)));\n}\n","/**\n * payloadTransformer — the single, configuration-driven payload engine shared by\n * AddFormV1 and EditFormV1.\n *\n * Goal: the Admin form-group config is the ONLY source of truth for how a form\n * value becomes an API payload value. There is ZERO field-name / module-specific\n * branching here. Every behaviour is driven by these per-field config keys:\n *\n * payloadKey output key (dot-notation allowed). Default: field.field\n * dataType auto | string | number | boolean | array | object | date | custom\n * elementType for dataType \"array\": coerce each element (e.g. \"number\")\n * payloadMode auto | value | label | object | custom | template | skip\n * valueKey which key holds the id/value on an option object (default: value/id/_id…)\n * displayKey which key holds the label on an option object (default: label/name…)\n * payloadTemplate object template for custom/template modes, with {{value}} {{label}} {{raw}} tokens\n * defaultValue value substituted when the form value is empty\n * transformRule { map: {...}, default, name } — value maps / named transforms\n * omitEmpty drop the key entirely when the final value is empty\n *\n * Nothing here knows about \"priority\", \"recruiters\", \"noticePeriod\", etc. Those\n * are expressed purely through the config above.\n *\n * Backward compatibility: when a field carries NONE of the new keys, the engine\n * falls back to the historical generic behaviour — dot-notation nesting, dayjs →\n * ISO string, scalar pass-through — so existing forms keep working unchanged.\n */\n\nimport dayjs from 'dayjs';\nimport { securePayload } from './inputSecurity';\nimport { snapToOption } from './optionMatching';\n\n// ── small shared predicates ───────────────────────────────────────────────────\n\nconst isEmpty = (v) =>\n v === undefined ||\n v === null ||\n v === '' ||\n (Array.isArray(v) && v.length === 0);\n\n// truthy tolerates the shapes a boolean config flag can arrive in (true/1/\"1\"),\n// matching how the forms read the same flags elsewhere.\nconst truthy = (v) => v === true || v === 1 || v === '1';\n\nconst isPlainObject = (v) =>\n v !== null && typeof v === 'object' && !Array.isArray(v) && !isDayjs(v);\n\nfunction isDayjs(v) {\n return (\n v &&\n typeof v === 'object' &&\n typeof v.format === 'function' &&\n typeof v.isValid === 'function'\n );\n}\n\nfunction isDateLike(v) {\n return isDayjs(v) || v instanceof Date;\n}\n\nfunction toISO(v) {\n if (v instanceof Date) return v.toISOString();\n if (isDayjs(v)) return v.isValid() ? v.toISOString() : null;\n return v;\n}\n\n// ── 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 toISO(value);\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 toISO(value);\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 toISO(value);\n\n case 'custom':\n case 'auto':\n case undefined:\n case '':\n default:\n return value;\n }\n}\n\n// ── transformRule (value maps + named transforms) ─────────────────────────────\n\nconst NAMED_TRANSFORMS = {\n firstChecked: (v) => (Array.isArray(v) ? v[0] : v),\n csvToArray: (v) =>\n typeof v === 'string' ? v.split(',').map((s) => s.trim()).filter(Boolean) : v,\n arrayToCsv: (v) => (Array.isArray(v) ? v.join(',') : v),\n trim: (v) => (typeof v === 'string' ? v.trim() : v),\n upper: (v) => (typeof v === 'string' ? v.toUpperCase() : v),\n lower: (v) => (typeof v === 'string' ? v.toLowerCase() : v),\n};\n\nfunction applyTransformRule(value, transformRule) {\n if (!transformRule) return value;\n let rule = transformRule;\n if (typeof rule === 'string') {\n // Either a named transform or a JSON blob.\n if (NAMED_TRANSFORMS[rule]) return NAMED_TRANSFORMS[rule](value);\n try {\n rule = JSON.parse(rule);\n } catch {\n return value;\n }\n }\n\n let out = value;\n if (rule.name && NAMED_TRANSFORMS[rule.name]) out = NAMED_TRANSFORMS[rule.name](out);\n\n if (rule.map && typeof rule.map === 'object') {\n const key = Array.isArray(out) ? String(out[0]) : String(out);\n if (Object.prototype.hasOwnProperty.call(rule.map, key)) out = rule.map[key];\n else if (rule.default !== undefined) out = rule.default;\n }\n return out;\n}\n\n// ── template resolution (custom / template payload modes) ─────────────────────\n\nconst TOKEN_RE = /\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g;\n\nfunction resolveToken(token, ctx) {\n const [head, ...rest] = token.split('.');\n let base;\n if (head === 'value') base = ctx.value;\n else if (head === 'label') base = ctx.label;\n else if (head === 'raw') base = ctx.raw;\n else return undefined;\n return rest.length ? getDeep(base, rest.join('.')) : base;\n}\n\nfunction resolveTemplateNode(node, ctx) {\n if (typeof node === 'string') {\n // Whole-string single token → return the typed value (keep numbers numeric).\n const whole = node.match(/^\\{\\{\\s*([\\w.]+)\\s*\\}\\}$/);\n if (whole) return resolveToken(whole[1], ctx);\n return node.replace(TOKEN_RE, (_, tok) => {\n const v = resolveToken(tok, ctx);\n return v == null ? '' : String(v);\n });\n }\n if (Array.isArray(node)) return node.map((n) => resolveTemplateNode(n, ctx));\n if (isPlainObject(node)) {\n const out = {};\n Object.entries(node).forEach(([k, v]) => {\n out[k] = resolveTemplateNode(v, ctx);\n });\n return out;\n }\n return node;\n}\n\nfunction parseTemplate(template) {\n if (!template) return null;\n if (typeof template === 'string') {\n try {\n return JSON.parse(template);\n } catch {\n return null;\n }\n }\n return template;\n}\n\n// ── payloadMode shaping ───────────────────────────────────────────────────────\n\nfunction shapeSingle(field, raw) {\n const mode = field.payloadMode || 'auto';\n const dataType = effectiveDataType(field);\n const { value, label } = splitValueLabel(raw, field);\n\n switch (mode) {\n case 'value':\n return { kind: 'scalar', out: coerceDataType(value, dataType, field) };\n\n case 'label':\n return { kind: 'scalar', out: coerceDataType(label, field.dataType || 'string', field) };\n\n case 'object': {\n const vKey = field.valueKey || 'id';\n const dKey = field.displayKey || 'name';\n return {\n kind: 'scalar',\n out: {\n [vKey]: coerceDataType(value, field.elementType || 'auto', {}),\n [dKey]: label,\n },\n };\n }\n\n case 'custom':\n case 'template': {\n const tpl = parseTemplate(field.payloadTemplate);\n if (!tpl) return { kind: 'scalar', out: coerceDataType(value, dataType, field) };\n const ctx = { value: coerceDataType(value, field.elementType || 'auto', {}), label, raw };\n const resolved = resolveTemplateNode(tpl, ctx);\n // A template that yields a plain object spreads into the parent unless the\n // admin pinned an explicit payloadKey.\n return { kind: field.payloadKey ? 'scalar' : 'spread', out: resolved };\n }\n\n case 'auto':\n default:\n return { kind: 'scalar', out: coerceDataType(value, dataType, field) };\n }\n}\n\n/**\n * shapeFieldValue — turns one field's raw form value into its payload contribution.\n * Returns { kind: 'scalar'|'spread'|'skip', out }.\n * scalar → write `out` at the field's payloadKey\n * spread → merge `out` (a plain object) into the parent payload\n * skip → contribute nothing\n */\nexport function shapeFieldValue(field, rawValue) {\n if (field.payloadMode === 'skip') return { kind: 'skip' };\n\n let raw = rawValue;\n if (isEmpty(raw) && field.defaultValue !== undefined && field.defaultValue !== '') {\n raw = field.defaultValue;\n }\n\n raw = applyTransformRule(raw, field.transformRule);\n\n if (isEmpty(raw) && field.omitEmpty) return { kind: 'skip' };\n\n // Multi-value (multi-select / array source) with a per-element shape.\n const isMultiSource =\n Array.isArray(raw) &&\n (effectiveDataType(field) === 'array' || field.payloadMode === 'object');\n\n if (isMultiSource && field.payloadMode && field.payloadMode !== 'value' && field.payloadMode !== 'auto') {\n const out = raw.map((item) => shapeSingle(field, item).out);\n return { kind: 'scalar', out };\n }\n\n return shapeSingle(field, raw);\n}\n\nexport function usesLabeledSelectValue(field = {}) {\n return Boolean(field.richValue) || ['label', 'object', 'custom', 'template'].includes(field.payloadMode);\n}\n\nexport function toSelectControlValue(field, value, options = []) {\n if (!usesLabeledSelectValue(field) || value == null) return value;\n const toLabeled = (item) => {\n if (isPlainObject(item) && item.value !== undefined) return item;\n const match = options.find((option) =>\n String(option?.value ?? '') === String(item) || String(option?.label ?? '') === String(item));\n return {\n value: match?.value ?? item,\n label: match?.label ?? String(item),\n };\n };\n return Array.isArray(value) ? value.map(toLabeled) : toLabeled(value);\n}\n\n// ── showIf payload gating ─────────────────────────────────────────────────────\n//\n// A field hidden by its showIf condition must not STORE either: antd preserves\n// values of unmounted Form.Items, so \"check VMS Required, type a commission,\n// uncheck it\" would still submit the commission. showIfSatisfied evaluates the\n// same operators the forms render with, against the field's own scope (the row\n// object for an addRow row) falling back to the whole form values (a condition\n// field living in another group, e.g. contractType). writeField consults it and\n// writes null instead of the stale value, so an edit also clears what a\n// now-hidden field previously stored. Config-driven; no field names.\n// One leaf condition ({field,operator,value}) evaluated against a scope.\nfunction showIfLeafSatisfied(cond, scope, root) {\n if (!cond?.field) return true;\n const val = hasFormValue(scope, cond.field)\n ? readFormValue(scope, cond.field)\n : readFormValue(root ?? scope, cond.field);\n const list = () => String(cond.value ?? '').split(',').map((v) => v.trim());\n switch (cond.operator) {\n case 'eq': return String(val ?? '') === String(cond.value ?? '');\n case 'neq': return String(val ?? '') !== String(cond.value ?? '');\n case 'truthy': return val !== undefined && val !== null && val !== '' && val !== false;\n case 'falsy': return val === undefined || val === null || val === '' || val === false;\n case 'notEmpty': return Array.isArray(val) ? val.length > 0 : Boolean(val);\n case 'in': return list().includes(String(val ?? ''));\n case 'notIn': return !list().includes(String(val ?? ''));\n default: return true;\n }\n}\n\n// All field keys a Show Condition reads (single leaf + every conditions[] entry)\n// — used to walk transitive visibility across each gate field.\nexport function showIfConditionFields(showIf) {\n const keys = [];\n if (showIf?.field) keys.push(showIf.field);\n if (Array.isArray(showIf?.conditions)) {\n showIf.conditions.forEach((c) => { if (c?.field) keys.push(c.field); });\n }\n return keys;\n}\n\n// A Show Condition may be a single leaf OR a `conditions` array combined by\n// `logic` (\"and\" default | \"or\"), plus an optional leaf folded in with the same\n// logic — mirrors the form-side evaluateShowIf so payload gating matches render.\nexport function showIfSatisfied(showIf, scope, root) {\n if (!showIf) return true;\n const conditions = Array.isArray(showIf.conditions) ? showIf.conditions.filter((c) => c?.field) : [];\n if (conditions.length) {\n const results = conditions.map((c) => showIfLeafSatisfied(c, scope, root));\n const orLogic = String(showIf.logic ?? 'and').toLowerCase() === 'or';\n let combined = orLogic ? results.some(Boolean) : results.every(Boolean);\n if (showIf.field) {\n const leaf = showIfLeafSatisfied(showIf, scope, root);\n combined = orLogic ? (combined || leaf) : (combined && leaf);\n }\n return combined;\n }\n return showIfLeafSatisfied(showIf, scope, root);\n}\n\n// fieldVisibleForPayload — transitive showIf: a field is storable only when its\n// own condition passes AND the field its condition READS is itself storable\n// (e.g. vmsCommission is gated on isVMSRequired, which is gated on\n// contractType — on W2 all of them drop together even if stale values linger).\n// `index` maps field key → field config across every group; `seen` guards\n// against condition cycles.\nfunction fieldVisibleForPayload(field, scope, root, index, seen = new Set()) {\n const gateFields = showIfConditionFields(field?.showIf);\n if (!gateFields.length) return true;\n if (seen.has(field.field)) return true; // cycle — fail open\n seen.add(field.field);\n if (!showIfSatisfied(field.showIf, scope, root)) return false;\n // Transitive: EACH gate field this condition reads must itself be storable.\n return gateFields.every((key) => {\n const gate = index?.get?.(key);\n return gate ? fieldVisibleForPayload(gate, scope, root, index, seen) : true;\n });\n}\n\n// payloadFieldIndex — field key → field config across all groups, for the\n// transitive showIf walk above.\nfunction payloadFieldIndex(groups = []) {\n const index = new Map();\n groups.forEach((g) => (g.fields ?? []).forEach((f) => {\n if (f?.field && !index.has(f.field)) index.set(f.field, f);\n }));\n return index;\n}\n\n// rowDefaultsFor — the seed object for a brand-new addRow row: every non-file\n// field's configured defaultValue (e.g. VMS Commission 5.5, Rate Currency USD)\n// keyed by its form key. Used for a group's initial empty rows AND every row\n// the user adds, so admin defaults show inside repeatable groups too (top-level\n// fields already get theirs via Form.Item initialValue).\nexport function rowDefaultsFor(group = {}, { editing = false } = {}) {\n const seed = {};\n (group.fields ?? []).forEach((f) => {\n // Same alias-tolerant upload test used everywhere else — a \"document\"-typed\n // upload must not be seeded with a defaultValue any more than a \"file\" one.\n if (!f.field || isUploadField(f)) return;\n if (editing && !f.defaultOnEdit) return;\n if (f.defaultValue === undefined || f.defaultValue === '') return;\n seed[f.field] = f.defaultValue;\n });\n return seed;\n}\n\n// ── field flattening from groups ──────────────────────────────────────────────\n\n/** All leaf fields across groups, with their group context (for addRow arrays). */\nexport function flattenFields(groups = []) {\n const out = [];\n groups.forEach((g) => {\n (g.fields ?? []).forEach((f) => {\n out.push({ field: f, group: g });\n });\n });\n return out;\n}\n\n// UPLOAD_FIELD_TYPES — every field type that holds an uploaded file.\n//\n// \"file\" is the canonical type the form renderer uses, but admin config in the\n// wild (and the seeded module defaults) also carries \"document\", \"image\",\n// \"upload\" and \"attachment\" for the same intent. Those aliases used to be\n// invisible to the collector, so their files silently never reached the shared\n// `documents` collection — a module whose uploads simply never appeared in the\n// registry, with no error anywhere. Matching on intent instead of on one exact\n// spelling makes the central documents registry work for EVERY module by\n// default, whichever synonym the config happens to use.\n//\n// This is safe for a field that is not really an upload: the collector only\n// ever pushes actual File/Blob values (see push in collectFileParts), so a\n// non-upload field simply contributes nothing.\nexport const UPLOAD_FIELD_TYPES = ['file', 'document', 'image', 'upload', 'attachment'];\n\n// isUploadField is the SINGLE predicate every file-collection path uses, so the\n// set of upload types can never drift between them again.\nexport function isUploadField(field) {\n return UPLOAD_FIELD_TYPES.includes(String(field?.type ?? '').trim().toLowerCase());\n}\n\nexport function fileFields(groups = []) {\n return flattenFields(groups)\n .map(({ field }) => field)\n .filter(isUploadField);\n}\n\n/**\n * collectFileParts — gather the NEW files the user picked, ready to append to a\n * multipart request, so create AND edit upload through the same gateway endpoint.\n *\n * Returns [{ formKey, file }]. The part name is the field's `fileKey` if set\n * (e.g. jobs reads \"file\"), else the first segment of its path\n * (\"passport.passportUploadName\" → \"passport\"). addRow groups fan out across\n * every row. Only fresh uploads (antd `originFileObj`, or a raw File/Blob) are\n * included — existing/stored files (url only) are left alone. Config-driven; no\n * field-name or module-specific logic.\n *\n * opts.indexed — when true, files inside an addRow group are emitted under an\n * INDEXED part name (\"documents[0]\", \"documents[1]\", …) where the index is the\n * row's position. EditForm uses this because the candidates update handler reads\n * document files by indexed key and aligns each file to its metadata row by\n * index.\n *\n * opts.scope — 'all' (default) | 'flat' | 'addRow'. Lets a caller collect only\n * the non-repeatable (flat) file fields, or only the repeatable (addRow) ones.\n * AddForm uploads flat files in the create request, then attaches addRow files\n * (e.g. candidate documents) in a follow-up update — the create handler for\n * repeatable document files is unreliable, while the indexed update path is the\n * proven one. Config-driven (keyed on the group's addRow flag), no module names.\n */\n// normalizeModuleKey trims + lower-cases a module key so a group's Target\n// Collection can be compared against the form's own module case-insensitively.\n//\n// It deliberately does NOT singularize (strip a trailing \"s\"). The gateway is\n// the source of truth for collection identity: it folds a moduleWrites entry\n// back into the main record only when the target resolves to the SAME physical\n// collection (foldSameCollectionModuleWrites → isSameCollectionTarget in\n// moduleCrudController.go), and its NormalizeFormGroupModule does not blindly\n// singularize either. A naive \"trainers\" → \"trainer\" fold here diverged from\n// that and silently merged a genuinely-distinct collection (e.g. \"trainers\")\n// into the primary record (e.g. module \"trainer\") — the exact multi-collection\n// bug. Exact-match keeps the FE from over-folding; any real same-collection\n// alias/plural case is folded server-side where the collection names are known.\nfunction normalizeModuleKey(module) {\n return String(module ?? '').trim().toLowerCase();\n}\n\n// isSameModuleTarget — the group's Target Collection is the form's own module\n// (exact, case-insensitive), i.e. the group writes to the MAIN record rather\n// than a linked document. Plural/alias targets that still resolve to the main\n// collection are folded by the gateway, not guessed here.\nfunction isSameModuleTarget(group, primaryKey) {\n return Boolean(primaryKey) && normalizeModuleKey(group.moduleName) === primaryKey;\n}\n\nexport function collectFileParts(values, groups = [], opts = {}) {\n const parts = [];\n const scope = opts.scope ?? 'all';\n const primaryKey = normalizeModuleKey(opts.module);\n const push = (formKey, node) => {\n if (node == null) return;\n const list = Array.isArray(node) ? node : [node];\n list.forEach((file) => {\n const raw = file?.originFileObj || file;\n if (typeof File !== 'undefined' && raw instanceof File) parts.push({ formKey, file: raw });\n else if (typeof Blob !== 'undefined' && raw instanceof Blob) parts.push({ formKey, file: raw });\n });\n };\n (groups ?? []).forEach((group) => {\n const files = (group.fields ?? []).filter(isUploadField);\n if (!files.length) return;\n // A moduleWrites group's files must be routed to ITS collection, not the\n // primary record — prefixed so the gateway's applyModuleWrites can tell\n // them apart (see splitModuleWriteFiles / filesForModuleWrite server-side).\n // A group targeting the form's own module writes to the main record, so\n // its files stay unprefixed (mirrors buildPayload's targetFor).\n const routed = group.moduleName && !isSameModuleTarget(group, primaryKey);\n const keyFor = (baseKey) => (routed ? `__mw__${group.moduleName}__${baseKey}` : baseKey);\n if (group.addRow) {\n if (scope === 'flat') return;\n const rows = values[group.name];\n if (!Array.isArray(rows)) return;\n files.forEach((field) => {\n const baseKey = field.fileKey ?? String(field.field).split('.')[0];\n rows.forEach((row, rowIdx) => {\n const formKey = keyFor(opts.indexed ? `${baseKey}[${rowIdx}]` : baseKey);\n push(formKey, readFormValue(row, field.field));\n });\n });\n return;\n }\n if (scope === 'addRow') return;\n files.forEach((field) => {\n const formKey = keyFor(field.fileKey ?? String(field.field).split('.')[0]);\n push(formKey, readFormValue(values, field.field));\n });\n });\n return parts;\n}\n\n// ── the two public builders ───────────────────────────────────────────────────\n\n/**\n * buildPayload — config-driven payload object from antd form values.\n *\n * @param values the antd form values object (may contain dot-notation nesting,\n * Form.List arrays for addRow groups, and dayjs date objects)\n * @param groups normalised form groups (with the per-field payload config)\n * @param opts { base } optional base object to merge onto (edit keeps the\n * untouched parts of the original record)\n * @returns a plain payload object ready to JSON.stringify\n *\n * File-typed fields are skipped — the caller handles uploads separately.\n */\nexport function buildPayload(values, groups, opts = {}) {\n const payload = isPlainObject(opts.base) ? structuredClone(opts.base) : {};\n\n // Strip file fields out of any base so stale file arrays never ride along.\n fileFields(groups).forEach((f) => {\n if (f.payloadKey || f.field) {\n // best-effort removal at both the configured key and source key\n deleteDeep(payload, f.payloadKey || f.field);\n deleteDeep(payload, f.field);\n }\n });\n\n // moduleWrites — a group with `moduleName` set (Feature 1: Per-Group Target\n // Collection) is routed to a DIFFERENT collection than the default record,\n // so its fields build their OWN sub-payload instead of merging into\n // `payload`. Groups sharing the same moduleName merge into ONE entry — the\n // backend's applyModuleWrites (moduleCrudController.go) upserts them the\n // same way, into one secondary document per (primary record, moduleName).\n //\n // A target that IS the form's own module (opts.module) means \"the main\n // record\": routing it through moduleWrites would create a parentId-linked\n // TWIN document in the same collection, so it merges into `payload` instead.\n // The backend applies the same guard (foldSameCollectionModuleWrites).\n const primaryKey = normalizeModuleKey(opts.module);\n const fieldIndex = payloadFieldIndex(groups);\n const moduleWrites = new Map();\n const targetFor = (group) => {\n if (!group.moduleName || isSameModuleTarget(group, primaryKey)) return payload;\n if (!moduleWrites.has(group.moduleName)) moduleWrites.set(group.moduleName, {});\n return moduleWrites.get(group.moduleName);\n };\n\n (groups ?? []).forEach((group) => {\n const target = targetFor(group);\n const groupFields = group.fields ?? [];\n const groupToggleOn = group.toggleEnabled && group.toggleField && hasFormValue(values, group.toggleField)\n ? Boolean(readFormValue(values, group.toggleField))\n : false;\n if (group.toggleEnabled && group.toggleField && hasFormValue(values, group.toggleField)) {\n setDeep(target, group.toggleField, groupToggleOn);\n if (groupToggleOn && group.addRow) {\n setDeep(target, group.payloadKey || group.name, []);\n return;\n }\n }\n\n if (group.addRow) {\n // Repeatable group → ROW-oriented array of objects:\n // [{ fieldA: v, fieldB: v }, { ... }]\n // matching a Go []struct (e.g. workExperience, educationDetails, documents).\n // The output key is the group's payloadKey (the struct's array key, e.g.\n // \"workExperience\") falling back to the group name.\n const rows = values[group.name];\n // Not present in this submit (e.g. EditForm doesn't prefill Form.List) →\n // leave whatever the base record already has untouched.\n if (!Array.isArray(rows)) return;\n const outKey = group.payloadKey || group.name;\n const rowObjects = rows\n .map((row) => buildRowObject(groupFields, row, values, fieldIndex))\n .filter((obj) => obj && Object.keys(obj).length > 0);\n setDeep(target, outKey, rowObjects);\n return;\n }\n\n groupFields.forEach((field) => writeField(field, values, target, values, fieldIndex));\n });\n\n if (moduleWrites.size > 0) {\n payload.moduleWrites = Array.from(moduleWrites, ([moduleName, data]) => ({ moduleName, data }));\n }\n\n return securePayload(payload, groups);\n}\n\n// writeField — the single, shared rule for turning ONE field's value (read from\n// `scope`, which is the whole form values for a normal group or a single row\n// object for an addRow row) into its contribution on `target`. Used by both\n// buildPayload (top-level groups) and buildRowObject (addRow rows) so a field\n// behaves identically no matter how deeply it is nested — a repeatable\n// field-GROUP (subFields) or a repeatable scalar field (addRow) serialises to a\n// nested array the same way at any level. Config-driven; no field-name logic.\nfunction writeField(field, scope, target, root, fieldIndex) {\n // isUploadField, NOT `type === 'file'`. Admin config in the wild types an\n // upload as \"document\"/\"image\"/\"upload\"/\"attachment\" just as often, and those\n // aliases used to MISS this branch entirely: the antd fileList\n // ([{uid,name,stored,…}]) then fell through to the ordinary scalar path and\n // was written to the payload as the field's value, overwriting the stored file\n // container with UI junk — or, when the user had not touched the field and it\n // held an empty list, writing [] over it. That is the \"an uploaded document\n // disappears when you edit the record\" bug: the update wiped the container the\n // form never intended to change. Every upload type now takes the branch below,\n // whose contract is \"carry the stored reference back, or write NOTHING\" —\n // never null, never an empty array, in any scope (flat field, addRow row, or\n // indexed document row: buildRowObject routes through this same function).\n if (isUploadField(field)) {\n // A NEW upload rides the multipart request (collectFileParts) and the\n // downstream handler writes its stored shape — nothing to do here.\n // An ALREADY-STORED file has no File object to upload, so without carrying\n // its reference the record ends up with no document at all: that is why a\n // Quick Submit prefilled from a previous submission lost every document it\n // showed in the form. Write back the untouched original container, merged\n // so sibling scalars written from the same container (passport.number,\n // passport.expiry) survive regardless of field order.\n const carried = carriedFileValue(readFormValue(scope, field.field));\n if (carried !== undefined) {\n // At the top level the fileKey names the stored container (\"passport\",\n // \"resume\"); inside an addRow row it is the multipart part name for the\n // whole group, so only the row's own key applies there.\n const inRow = root !== undefined && root !== scope;\n const key = field.payloadKey || (inRow ? field.field : (field.fileKey || field.field));\n mergeDeepAt(target, key, carried);\n }\n return;\n }\n\n // showIf gating at PAYLOAD time: a field whose visibility condition fails is\n // not stored. antd preserves unmounted Form.Item values, so without this a\n // value typed before the condition flipped (e.g. VMS Commission after VMS\n // Required was unchecked) would silently ride along. Writing null (rather\n // than skipping) also clears the stale stored value on edit; inside an\n // addRow row the whole row array is rewritten anyway, so the key simply\n // drops out of the row object. Visibility is TRANSITIVE: a field whose gate\n // field is itself hidden drops too (fieldVisibleForPayload).\n if (showIfConditionFields(field.showIf).length && !fieldVisibleForPayload(field, scope, root, fieldIndex)) {\n // Top-level (scope === root): write null so an edit clears the stale\n // stored value. Row scope: the row array is rewritten wholesale, so simply\n // omitting the key removes it from the stored row.\n if ((root === undefined || root === scope) && hasFormValue(scope, field.field)) {\n setDeep(target, field.payloadKey || field.field, null);\n }\n return;\n }\n\n // Repeatable field-GROUP (subFields): each Form.List row is an object of the\n // sub-fields, so this field submits as an array of row objects —\n // [{question, answer}, …] — under its own payloadKey. Recurses so a nested\n // repeat group (e.g. inside an addRow row) is shaped element-by-element.\n if (Array.isArray(field.subFields) && field.subFields.length > 0) {\n if (!hasFormValue(scope, field.field)) return;\n const rows = readFormValue(scope, field.field);\n if (!Array.isArray(rows)) return;\n const rowObjects = rows\n .map((row) => buildRowObject(field.subFields, row, root, fieldIndex))\n .filter((obj) => obj && Object.keys(obj).length > 0);\n setDeep(target, field.payloadKey || field.field, rowObjects);\n return;\n }\n\n if (!hasFormValue(scope, field.field)) return; // not in this submit — leave base untouched\n\n // Repeatable SCALAR field (addRow, no subFields): each Form.List item is one\n // value, so normalise every element through the field's single-value shape\n // (dates → ISO, select → id) instead of relying on a blanket array coercion.\n // This makes a repeat field work INSIDE an addRow row (subjects[] per row) as\n // well as at the top level.\n if (truthy(field.addRow) && (!Array.isArray(field.subFields) || field.subFields.length === 0)) {\n const raw = readFormValue(scope, field.field);\n const arr = Array.isArray(raw) ? raw : isEmpty(raw) ? [] : [raw];\n const single = singleFieldOf(field);\n const out = arr\n .map((el) => shapeFieldValue(single, el))\n .filter((r) => r.kind !== 'skip')\n .map((r) => r.out);\n setDeep(target, field.payloadKey || field.field, out);\n return;\n }\n\n const raw = readFormValue(scope, field.field);\n const result = shapeFieldValue(field, raw);\n if (result.kind === 'skip') return;\n if (result.kind === 'spread' && isPlainObject(result.out)) {\n mergeDeep(target, result.out);\n return;\n }\n setDeep(target, field.payloadKey || field.field, result.out);\n}\n\n// buildRowObject — shape one addRow row into a plain object keyed by each\n// field's payloadKey/field (relative to the row). File fields are skipped (they\n// upload separately and their metadata is merged server-side).\nfunction buildRowObject(groupFields, row, root, fieldIndex) {\n const obj = {};\n groupFields.forEach((field) => writeField(field, row, obj, root ?? row, fieldIndex));\n return obj;\n}\n\nfunction deleteDeep(obj, path) {\n const parts = String(path).split('.');\n let cur = obj;\n for (let i = 0; i < parts.length - 1; i += 1) {\n if (!isPlainObject(cur[parts[i]])) return;\n cur = cur[parts[i]];\n }\n delete cur[parts[parts.length - 1]];\n}\n\n/**\n * buildInitialValue — reverse direction, for EditForm prefill.\n * Given a field config and the stored value (which may be a scalar, an\n * { id, value } reference, an { id, name } object, or an array of those),\n * returns the value the input control expects.\n *\n * - date/time fields → dayjs\n * - select/radio/checkbox → the id/value (single) or array of ids (multi)\n * - everything else → the scalar\n *\n * This replaces convertInitialValue's hardcoded field-name handling.\n */\n// Controls that render exactly one primitive. Deliberately excludes select /\n// lookup / reference / file types, whose values are legitimately objects.\nconst SCALAR_CONTROL_TYPES = new Set([\n 'text', 'textarea', 'email', 'phone', 'tel', 'number', 'date', 'time', 'datetime', 'password',\n]);\n\nexport function buildInitialValue(field, stored) {\n const configuredEmpty = (value) => {\n const emptyValues = Array.isArray(field.emptyValues) ? field.emptyValues : [];\n return emptyValues.some((v) => String(v) === String(value));\n };\n const fallbackEditDefault = () =>\n field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== ''\n ? field.defaultValue\n : undefined;\n\n if (stored === undefined || stored === null) {\n return fallbackEditDefault() ?? stored;\n }\n if (configuredEmpty(stored)) {\n return fallbackEditDefault();\n }\n if (isEmpty(stored) && field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== '') {\n return field.defaultValue;\n }\n\n // A text/number/date control can only show a scalar. When the stored value is\n // a whole sub-document — a field whose key names its own container, e.g.\n // \"address\" storing {addressLine, city, state} while its payloadKey is\n // \"address.addressLine\" — reduce it to the payloadKey's leaf. Without this the\n // input renders \"[object Object]\" and saving writes that string over the real\n // address. Falls through to undefined (blank control) when no leaf matches:\n // never destructive, always recoverable.\n if (SCALAR_CONTROL_TYPES.has(field.type) && isPlainObject(stored)) {\n const leaf = String(field.payloadKey || field.field || '').split('.').pop();\n const reduced = leaf ? stored[leaf] : undefined;\n if (reduced === undefined || isPlainObject(reduced)) return undefined;\n stored = reduced; // eslint-disable-line no-param-reassign\n }\n\n if (field.type === 'date' || field.type === 'time') {\n if (!stored || stored === '0001-01-01T00:00:00Z') return null;\n const p = dayjs(stored);\n return p.isValid() ? p : null;\n }\n\n // Snap onto the field's configured option before the control sees it. A\n // select/radio only selects on an exact value match, so an incoming \"onsite\"\n // or \"ONSITE\" would leave an \"On-Site\" radio blank even though the value\n // arrived. Fields without options, and values that match nothing, pass\n // through untouched — this can correct a value but never discard one.\n stored = snapToOption(field, stored);\n\n const pickId = (item) => {\n if (isPlainObject(item)) {\n return (\n (field.valueKey && item[field.valueKey]) ??\n item.id ??\n item._id ??\n item.value ??\n item.userId ??\n item.recruiterId\n );\n }\n return item;\n };\n\n // Radio groups are ALWAYS single-select in the UI, even when the payload\n // dataType is \"array\" (e.g. jobRemoteStatus → []string). Prefill the scalar\n // so the selected radio shows; the payload engine re-wraps it to an array on\n // save via coerceDataType. Without this, a radio gets an array value and\n // renders with nothing selected.\n const isMulti = field.type === 'radio' ? false : effectiveDataType(field) === 'array';\n\n // A stored value that only differs from a static option by case (e.g. a\n // legacy \"active\" vs the configured option value \"Active\") would otherwise\n // match nothing and render as an unselected/empty control.\n const matchOptionCase = (v) => {\n if (v === undefined || v === null || v === '' || !Array.isArray(field.options)) return v;\n const match = field.options.find((option) => String(option?.value ?? '').toLowerCase() === String(v).toLowerCase());\n return match ? match.value : v;\n };\n\n if (field.type === 'select' || field.type === 'radio' || field.type === 'checkbox') {\n if (isMulti) {\n const arr = Array.isArray(stored) ? stored : [stored];\n return arr.map(pickId).map(matchOptionCase).filter((v) => v !== undefined && v !== null && v !== '' && !configuredEmpty(v));\n }\n const selected = matchOptionCase(Array.isArray(stored) ? pickId(stored[0]) : pickId(stored));\n return configuredEmpty(selected) ? fallbackEditDefault() : selected;\n }\n\n // checkbox group with a transformRule map (e.g. \"high\" → checked) is handled\n // by the caller via the same map; here we just pass the scalar through.\n return stored;\n}\n\n// singleFieldOf strips a repeatable (addRow) field down to its per-item shape:\n// each Form.List item holds ONE value, so multi/array coercion must NOT apply\n// when reading or rendering a single item. Preserves everything else (type,\n// options, datasource, validations) so the item control still behaves like the\n// field otherwise would.\nexport function singleFieldOf(field = {}) {\n return { ...field, addRow: false, multiSelect: false, mode: undefined, dataType: undefined };\n}\n\n// buildRepeatFieldInitial — the Form.List initialValue for a field-level repeat\n// (field.addRow). Unlike a repeatable GROUP (rows are objects), each item here\n// is a single scalar, so a stored array maps element-by-element through\n// buildInitialValue as if the field were single (see singleFieldOf). The result\n// is padded to minRows; when there is no stored value at all, initialRows\n// (falling back to minRows) empty inputs are shown so the user sees a starting\n// control instead of only a \"+\" button. Config keys mirror the group ones:\n// field.minRows / field.initialRows.\nexport function buildRepeatFieldInitial(field, stored) {\n const minRows = Math.max(0, Number(field.minRows ?? 0) || 0);\n const initialRows = field.initialRows !== undefined && field.initialRows !== ''\n ? Math.max(minRows, Math.max(0, Number(field.initialRows) || 0))\n : minRows;\n\n // Repeatable field-GROUP (subFields): each stored element is a row OBJECT, so\n // map every sub-field through buildInitialValue into a per-row object (mirrors\n // getAddRowInitialValue's group-addRow row mapping). Empty rows fall back to {}\n // so the sub-field controls still render.\n if (Array.isArray(field.subFields) && field.subFields.length > 0) {\n let rows = [];\n if (Array.isArray(stored)) {\n rows = stored.map((row) => {\n const out = {};\n field.subFields.forEach((sub) => {\n if (!sub.field || isUploadField(sub)) return;\n const v = getDeep(row ?? {}, sub.field) ?? (row ?? {})[sub.field];\n if (v !== undefined && v !== null) out[sub.field] = buildInitialValue(sub, v);\n });\n return out;\n });\n }\n const target = rows.length > 0 ? minRows : Math.max(minRows, initialRows);\n while (rows.length < target) rows.push({});\n return rows;\n }\n\n const singleField = singleFieldOf(field);\n let items = [];\n if (Array.isArray(stored)) {\n items = stored\n .map((v) => buildInitialValue(singleField, v))\n .filter((v) => v !== undefined && v !== null && v !== '');\n } else if (stored !== undefined && stored !== null && stored !== '') {\n const v = buildInitialValue(singleField, stored);\n if (v !== undefined && v !== null && v !== '') items = [v];\n }\n\n const target = items.length > 0 ? minRows : Math.max(minRows, initialRows);\n while (items.length < target) items.push(undefined);\n return items;\n}\n\nexport default {\n buildPayload,\n buildInitialValue,\n showIfSatisfied,\n rowDefaultsFor,\n buildRepeatFieldInitial,\n singleFieldOf,\n shapeFieldValue,\n usesLabeledSelectValue,\n toSelectControlValue,\n coerceDataType,\n getDeep,\n setDeep,\n flattenFields,\n fileFields,\n};\n","// linkedAddRowGroups.js\n//\n// Pure helpers for group.linkGroup — an opt-in mechanism that keeps two or\n// more addRow groups' rows synchronized (same add/remove, same row count)\n// while each group keeps rendering as its own Card and submitting its own\n// independent payload array (buildPayload / buildAddRowInitial are untouched\n// and stay fully per-group — see payloadTransformer.js / applyGroupValues.js).\n//\n// A group only participates when BOTH group.addRow is true AND group.linkGroup\n// is a non-empty string shared by at least one other group. Every group that\n// predates this field (i.e. every group in production today) has no\n// linkGroup, so these helpers are a strict no-op for it.\n\nconst truthy = (value) => value === true || value === 1 || value === '1';\n\n// computeLinkedSets(groups) -> Map<linkGroupKey, { leaderName, memberNames: string[] }>\n//\n// `groups` is expected already order-sorted (normalizeGroups sorts by `order`\n// before render), so the first member encountered per key is the leader —\n// Array.prototype.sort is stable, so this matches the existing order\n// convention with no extra tie-break logic needed. A \"set\" of size 1 (nothing\n// else shares that linkGroup value) is dropped — a lone linkGroup value isn't\n// meaningfully linked to anything, and the group renders as if unlinked.\nexport function computeLinkedSets(groups = []) {\n const byKey = new Map();\n groups.forEach((group) => {\n const key = group?.linkGroup;\n if (!truthy(group?.addRow) || !key) return;\n if (!byKey.has(key)) byKey.set(key, []);\n byKey.get(key).push(group.name);\n });\n\n const sets = new Map();\n byKey.forEach((memberNames, key) => {\n if (memberNames.length < 2) return;\n sets.set(key, { leaderName: memberNames[0], memberNames });\n });\n return sets;\n}\n\n// findLinkedSet(linkedSets, groupName) -> { leaderName, memberNames } | null\n// Cheap membership lookup against an already-computed sets Map (compute once\n// per render via computeLinkedSets, look up per group here — avoids\n// recomputing the whole map on every group).\nexport function findLinkedSet(linkedSets, groupName) {\n for (const set of linkedSets.values()) {\n if (set.memberNames.includes(groupName)) return set;\n }\n return null;\n}\n\n// padLinkedInitialValues(linkedSets, initialValuesByGroupName) -> a new\n// { [groupName]: rows[] } object where every member of a linked set is padded\n// with {} placeholder rows up to the set's max length. Guards against\n// pre-existing data where two linked groups' stored `rows` happen to differ\n// in length (undefined territory otherwise — buildAddRowInitial/\n// getAddRowInitialValue compute each group's rows fully independently). A\n// no-op passthrough for any group not in a real (>=2 member) linked set.\nexport function padLinkedInitialValues(linkedSets, initialValuesByGroupName = {}) {\n const next = { ...initialValuesByGroupName };\n linkedSets.forEach(({ memberNames }) => {\n const maxLen = Math.max(...memberNames.map((name) => (next[name] ?? []).length));\n memberNames.forEach((name) => {\n const rows = next[name] ?? [];\n if (rows.length < maxLen) {\n next[name] = [...rows, ...Array.from({ length: maxLen - rows.length }, () => ({}))];\n }\n });\n });\n return next;\n}\n","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","// 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 { 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// 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 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 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, Modal, message } from 'antd';\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\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 }) {\n const [loadingKey, setLoadingKey] = useState(null);\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 if (existingRows.some(rowHasData)) {\n Modal.confirm({\n title: 'Replace existing rows?',\n content: `This will replace ${existingRows.length} existing row(s) in \"${group.label || group.name}\".`,\n okText: 'Replace',\n cancelText: 'Cancel',\n onOk: applyRows,\n });\n } else {\n applyRows();\n }\n });\n\n return appliedAny;\n }, [form]);\n\n const runAction = useCallback(async (groupName, field, action, extraFile) => {\n setLoadingKey(action.key);\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 appliedAny = applyResponse(result?.groups);\n const label = action.label || 'AI action';\n if (appliedAny) {\n message.success(`${label} completed successfully.`);\n } else {\n message.info(`${label} completed, but no matching details were found.`);\n }\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 }\n }, [form, module, applyResponse]);\n\n return { runAction, loadingKey, busy: loadingKey !== null, isActionEnabled };\n}\n","import { Button, Space } from 'antd';\n\n// AiActionButtonGroup — renders one position bucket (\"top-left\", \"bottom-right\",\n// etc.) of a field's AI action buttons, admin-configured entirely via each\n// action's `position`. Horizontal alignment follows the position's own\n// left/center/right suffix; the caller places top vs. bottom buckets around\n// the field itself.\nexport default function AiActionButtonGroup({ position, actions, aiActions, groupName, field }) {\n if (!actions || actions.length === 0) return null;\n\n const justifyContent = position.endsWith('center')\n ? 'center'\n : position.endsWith('right')\n ? 'flex-end'\n : 'flex-start';\n\n return (\n <Space size={8} className=\"v1-ai-action-buttons\" style={{ width: '100%', justifyContent }}>\n {actions.map((action) => (\n <Button\n key={action.key}\n size=\"small\"\n loading={aiActions.loadingKey === action.key}\n disabled={aiActions.busy || !aiActions.isActionEnabled(action)}\n onClick={() => aiActions.runAction(groupName, field, action)}\n >\n {action.label}\n </Button>\n ))}\n </Space>\n );\n}\n","// Generic email rule used by Add/Edit forms and the real-time input validator.\n// The rule is selected through the DB validation type `email`; it contains no\n// module or field-name assumptions.\nexport function isValidConfiguredEmail(value) {\n const email = String(value ?? '').trim();\n const match = /^([^\\s@]+)@([^\\s@]+)\\.([A-Za-z]{2,})$/.exec(email);\n if (!match) return false;\n\n const [, localPart, domainHost] = match;\n return /[A-Za-z]/.test(localPart) && /[A-Za-z]/.test(domainHost);\n}\n\nexport function configuredEmailRule(label = 'Email', message) {\n return {\n validator: (_, value) => {\n if (value === undefined || value === null || value === '') return Promise.resolve();\n return isValidConfiguredEmail(value)\n ? Promise.resolve()\n : Promise.reject(new Error(message ?? `Enter a valid ${label}; numeric-only email addresses are not allowed`));\n },\n };\n}\n","// inputValidator.js — config-driven real-time input restriction engine.\n// Driven entirely by field.validator in formGroupConfig — no hardcoding.\n// Used by FieldControl in AddFormV1 and EditFormV1.\n\nimport { isValidConfiguredEmail } from './emailValidator';\n\nexport const INPUT_VALIDATOR_TYPES = [\n { label: 'Only Numbers', value: 'onlyNumber' },\n { label: 'Only Letters', value: 'onlyLetter' },\n { label: 'Only Letters & Spaces', value: 'onlyLettersAndSpace' },\n { label: 'Only Alphanumeric', value: 'onlyAlphanumeric' },\n { label: 'Letters, Numbers & Hyphen', value: 'onlyLettersNumberAndHyphen' },\n { label: 'Letters, Numbers, Hyphen & Dot', value: 'onlyLettersNumberHyphenAndDot' },\n { label: 'Contact Number (digits, (), -)', value: 'contactNumber' },\n { label: 'Email (reject numeric-only address)', value: 'email' },\n { label: 'Job Title (letters, nums, symbols)', value: 'jobTitle' },\n { label: 'MSP / Ref ID (alphanumeric only)', value: 'mspRefId' },\n { label: 'Location (letters, nums, , . -)', value: 'locationValidation' },\n { label: 'Decimal / Range (number + dot)', value: 'decimalRange' },\n { label: 'Budget (numbers, must be > 0)', value: 'budgetValidation' },\n { label: 'Experience (numbers only)', value: 'experience' },\n { label: 'Numeric — 2 digits max', value: 'numericTwoDigits' },\n { label: 'Job Description (character count)', value: 'jobDescription' },\n { label: 'Website URL', value: 'websiteUrl' },\n];\n\n// onKeyPress allowlist patterns — used to block invalid chars before they appear.\n// null means no per-key blocking for that type.\nconst KEY_PATTERNS = {\n onlyNumber: /^[0-9]$/,\n experience: /^[0-9]$/,\n numericTwoDigits: /^[0-9]$/,\n budgetValidation: /^[0-9]$/,\n onlyLetter: /^[a-zA-Z\\s]$/,\n onlyLettersAndSpace: /^[a-zA-Z\\s]$/,\n onlyAlphanumeric: /^[a-zA-Z0-9]$/,\n mspRefId: /^[a-zA-Z0-9]$/,\n onlyLettersNumberAndHyphen: /^[a-zA-Z0-9\\s-]$/,\n locationValidation: /^[a-zA-Z0-9\\s\\-.,]$/,\n jobTitle: /^[a-zA-Z0-9\\s\\-.,/&+()*'\"#@]$/,\n decimalRange: /^[0-9.]$/,\n contactNumber: /^[0-9()\\-+\\s]$/,\n};\n\nexport function getKeyPattern(type) {\n return KEY_PATTERNS[type] ?? null;\n}\n\n// applyInputValidator — clean a raw input value according to the validator config.\n// Returns { cleaned: string, error: string|null }.\n// eventType 'blur' triggers trim; 'change' only strips leading whitespace.\nexport function applyInputValidator(rawValue, config = {}, eventType = 'change') {\n if (!config?.type || rawValue === undefined || rawValue === null) {\n return { cleaned: rawValue ?? '', error: null };\n }\n\n const value = String(rawValue);\n const { type, maxLength, maxChars } = config;\n const title = config.title || 'Field';\n let cleaned;\n let error = null;\n\n const trimStart = (s) => (eventType === 'blur' ? s.trim() : s.replace(/^\\s+/, ''));\n\n switch (type) {\n case 'onlyNumber':\n case 'experience':\n case 'numericTwoDigits': {\n const limit = Number(maxLength ?? (type === 'numericTwoDigits' ? 2 : 10));\n cleaned = value.replace(/[^0-9]/g, '');\n if (value !== cleaned) error = `${title} allows only numbers.`;\n if (cleaned.length > limit) {\n error = `${title} cannot exceed ${limit} characters.`;\n cleaned = cleaned.slice(0, limit);\n }\n break;\n }\n\n case 'onlyLetter': {\n const limit = Number(maxLength ?? 50);\n cleaned = value.replace(/[^a-zA-Z]/g, '');\n if (value !== cleaned) error = `${title} allows only letters.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersAndSpace': {\n const limit = Number(maxLength ?? 55);\n cleaned = trimStart(value.replace(/[^a-zA-Z\\s]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters and spaces.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyAlphanumeric':\n case 'mspRefId': {\n const limit = Number(maxLength ?? 50);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters and numbers.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersNumberAndHyphen': {\n const limit = Number(maxLength ?? 55);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s-]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers and hyphens.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersNumberHyphenAndDot': {\n const limit = Number(maxLength ?? 100);\n cleaned = value.replace(/[^a-zA-Z0-9\\-./#+[\\]{}()\\s]/g, '').slice(0, limit);\n if (value !== cleaned) error = `${title} allows only letters, numbers, hyphens and dots.`;\n break;\n }\n\n case 'contactNumber': {\n const limit = Number(maxLength ?? 15);\n cleaned = value.replace(/[^0-9()\\-+\\s]/g, '').slice(0, limit);\n const re = /^(\\+?[0-9]{1,3}[- ]?)?(\\(?\\d{1,4}\\)?[- ]?)?[\\d\\-\\s]{3,15}$/;\n if (value !== cleaned) error = `${title} allows only digits, parentheses () and hyphens.`;\n else if (cleaned && !re.test(cleaned)) error = `${title} is not a valid phone number format.`;\n break;\n }\n\n case 'locationValidation': {\n const limit = Number(maxLength ?? 100);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s\\-.,]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers, spaces, commas, dots and hyphens.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'jobTitle': {\n const limit = Number(maxLength ?? 80);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s\\-/&.,()+#@'\"*]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers, spaces and common symbols.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'decimalRange': {\n let c = value.replace(/[^0-9.]/g, '');\n const dotCount = (c.match(/\\./g) || []).length;\n if (dotCount > 1) {\n const di = c.indexOf('.');\n c = c.slice(0, di + 1) + c.slice(di + 1).replace(/\\./g, '');\n error = `${title} can have only one decimal point.`;\n }\n const digits = c.replace(/\\./g, '');\n const limit = Number(maxLength ?? 10);\n if (digits.length > limit) {\n c = c.slice(0, limit + (c.includes('.') ? 1 : 0));\n error = `${title} cannot exceed ${limit} digits.`;\n }\n cleaned = c;\n if (!error && value !== cleaned) error = `${title} allows only numbers and one dot.`;\n break;\n }\n\n case 'budgetValidation': {\n const limit = Number(maxLength ?? 10);\n cleaned = value.replace(/[^0-9]/g, '').slice(0, limit);\n const num = Number(cleaned);\n if (value !== cleaned) error = `${title} allows only numbers.`;\n else if (cleaned && num <= 0) error = `${title} must be greater than 0.`;\n break;\n }\n\n case 'jobDescription': {\n const limit = Number(maxChars ?? maxLength ?? 1500);\n const plain = value\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n if (plain.length > limit) error = `${title} cannot exceed ${limit} characters.`;\n cleaned = value; // never truncate HTML content\n break;\n }\n\n case 'email': {\n cleaned = eventType === 'blur' ? value.trim() : value;\n if (cleaned && !isValidConfiguredEmail(cleaned)) {\n error = `${title} must be a valid email address; numeric-only email addresses are not allowed.`;\n }\n break;\n }\n\n case 'websiteUrl': {\n cleaned = eventType === 'blur' ? value.trim() : value;\n const urlRe = /^(https?:\\/\\/)?([\\da-z.-]+)\\.([a-z.]{2,6})([/\\w .-]*)*\\/?$/i;\n if (cleaned && !urlRe.test(cleaned)) error = `${title} must be a valid URL.`;\n break;\n }\n\n default:\n cleaned = value;\n }\n\n return { cleaned, error };\n}\n","// Config-driven field uniqueness — the shared, pure runtime shared by\n// AddFormV1 and EditFormV1.\n//\n// An admin marks a field unique in Form Groups → Field → Validations; the\n// stored entry is an ordinary validations[] row:\n//\n// { type: 'unique', message: 'Email already present',\n// value: { collectionField, normalizer, skipBlank, validateOn } }\n//\n// Everything module-specific comes from that config. NOTHING in this file (or\n// in the two form engines) may name a module, a collection or a field — the\n// server resolves the collection, the stored field and the tenant scope from\n// the module + the caller's claims.\n//\n// This module owns ALL of the uniqueness behaviour so neither form duplicates\n// it: rule extraction, blank handling, normalization, the per-session cache,\n// the stale-response guard, the pending-request counter that blocks submit, the\n// antd rule factory (incl. \"only after the synchronous rules pass\") and the\n// mapping of a submit-time 409 back onto form fields.\n\nexport const UNIQUE_VALIDATION_TYPE = 'unique';\nexport const ALREADY_PRESENT_CODE = 'already_present';\nexport const DUPLICATE_VALUE_CODE = 'DUPLICATE_VALUE';\n\nconst DEFAULT_MESSAGE = 'This value is already present';\n\n// ── Rule extraction ──────────────────────────────────────────────────────────\n\nfunction validationType(validation) {\n if (typeof validation === 'string') return validation;\n return validation?.type ?? validation?.rule ?? validation?.name;\n}\n\n// The admin editor stores the unique options as an OBJECT under `value` (the\n// declared valueType is 'object', so the save path leaves it untouched). Older\n// or hand-written config may put the same keys flat on the validation itself,\n// so both shapes are accepted.\nexport function parseUniqueValidation(validation) {\n if (validationType(validation) !== UNIQUE_VALIDATION_TYPE) return null;\n if (typeof validation === 'string') {\n return { message: DEFAULT_MESSAGE, normalizer: 'trim', skipBlank: true, validateOn: 'blur' };\n }\n\n const raw = (validation.value && typeof validation.value === 'object') ? validation.value : validation;\n const validateOn = String(raw.validateOn ?? 'blur').toLowerCase();\n\n return {\n message: (typeof validation.message === 'string' && validation.message.trim())\n ? validation.message.trim()\n : DEFAULT_MESSAGE,\n // Kept only so the admin's configured target is inspectable client-side;\n // it is NEVER sent to the server (the server resolves the stored field).\n collectionField: raw.collectionField ?? '',\n normalizer: String(raw.normalizer ?? 'trim'),\n // skipBlank defaults to true; only an explicit `false` turns it off.\n skipBlank: raw.skipBlank !== false,\n validateOn: ['blur', 'change', 'submit'].includes(validateOn) ? validateOn : 'blur',\n };\n}\n\nexport function getUniqueRule(field) {\n const validations = field?.validations ?? field?.validation ?? field?.rules ?? [];\n if (!Array.isArray(validations)) return null;\n for (const validation of validations) {\n const rule = parseUniqueValidation(validation);\n if (rule) return rule;\n }\n return null;\n}\n\n// Every unique-configured field in the form, flattened across groups. Used to\n// map a submit-time 409 back onto a field when the transport lost the\n// structured body (see extractDuplicateFieldErrors).\nexport function collectUniqueRules(groups = []) {\n const collected = [];\n (Array.isArray(groups) ? groups : []).forEach((group) => {\n (group?.fields ?? []).forEach((field) => {\n const rule = getUniqueRule(field);\n if (!rule || !field?.field) return;\n collected.push({ field: field.field, label: field.label ?? field.field, rule });\n });\n });\n return collected;\n}\n\n// ── Blank + normalization ────────────────────────────────────────────────────\n\n// Blank = missing / null / empty / whitespace-only / empty list.\n// Numeric ZERO and boolean FALSE are REAL values, not blanks — a \"0\" employee\n// code or a `false` flag must still be uniqueness-checked.\nexport function isBlankUniqueValue(value) {\n if (value === undefined || value === null) return true;\n if (typeof value === 'number') return Number.isNaN(value);\n if (typeof value === 'boolean') return false;\n if (Array.isArray(value)) return value.length === 0;\n return String(value).trim() === '';\n}\n\nexport function normalizeUniqueValue(value, normalizer = 'trim') {\n if (value === undefined || value === null) return '';\n const text = String(value);\n switch (String(normalizer)) {\n case 'exact': return text;\n case 'lower':\n case 'trimLower':\n case 'email': return text.trim().toLowerCase();\n case 'digitsOnly': return text.replace(/\\D+/g, '');\n case 'trim':\n default: return text.trim();\n }\n}\n\nexport function shouldCheckUniqueValue(rule, value) {\n if (!rule) return false;\n if (rule.skipBlank !== false && isBlankUniqueValue(value)) return false;\n return true;\n}\n\n// antd/rc-field-form filters rules by trigger. `[]` matches no trigger at all,\n// so a submit-only rule runs exclusively inside form.validateFields().\nexport function uniqueValidateTriggers(rule) {\n if (!rule) return [];\n if (rule.validateOn === 'submit') return [];\n if (rule.validateOn === 'change') return ['onChange', 'onBlur'];\n return ['onBlur'];\n}\n\n// ── The checker (cache + staleness + pending) ────────────────────────────────\n\nexport const UNIQUE_STATUS = {\n SKIPPED: 'skipped',\n AVAILABLE: 'available',\n DUPLICATE: 'duplicate',\n STALE: 'stale',\n ERROR: 'error',\n};\n\nfunction cacheKey({ module, field, recordId, normalized }) {\n return `${module}\u0000${field}\u0000${recordId ?? ''}\u0000${normalized}`;\n}\n\n/**\n * One checker per form session. Both form engines create exactly one and pass\n * it into getRules; it is the only place a uniqueness request is ever made.\n *\n * @param checkFieldUnique the API function (injected so it can be mocked)\n * @param onPendingChange (pendingCount) => void — drives the submit button\n */\nexport function createUniqueChecker({ checkFieldUnique, onPendingChange } = {}) {\n // Normalized-value cache: the same normalized value is never re-checked in\n // one form session (blur → submit → blur again is one request, not three).\n // Only DEFINITIVE outcomes are cached — a failed request must be retried.\n const cache = new Map();\n // Monotonic sequence. Every request takes the next number and records itself\n // as its field's latest; when a response comes back with a number that is no\n // longer the latest for that field, the user has typed on and the answer\n // describes an OLD value — it is dropped instead of overwriting the new one.\n const latestSeq = new Map();\n let seq = 0;\n let pending = 0;\n\n function setPending(next) {\n pending = next;\n if (typeof onPendingChange === 'function') onPendingChange(pending);\n }\n\n async function check({ module, field, rule, value, recordId, clientId, region } = {}) {\n if (!rule || !module || !field || typeof checkFieldUnique !== 'function') {\n return { status: UNIQUE_STATUS.SKIPPED };\n }\n if (!shouldCheckUniqueValue(rule, value)) {\n return { status: UNIQUE_STATUS.SKIPPED };\n }\n\n const normalized = normalizeUniqueValue(value, rule.normalizer);\n const key = cacheKey({ module, field, recordId, normalized });\n if (cache.has(key)) return cache.get(key);\n\n seq += 1;\n const mySeq = seq;\n latestSeq.set(field, mySeq);\n setPending(pending + 1);\n\n try {\n const result = await checkFieldUnique({\n module,\n field,\n value,\n // recordId is passed straight through: EditFormV1 supplies it (so the\n // record does not collide with itself), AddFormV1 never does.\n recordId,\n clientId,\n region,\n });\n\n if (latestSeq.get(field) !== mySeq) return { status: UNIQUE_STATUS.STALE };\n\n const outcome = result?.available === false\n ? {\n status: UNIQUE_STATUS.DUPLICATE,\n message: firstDuplicateMessage(result) || rule.message || DEFAULT_MESSAGE,\n }\n : { status: UNIQUE_STATUS.AVAILABLE };\n\n cache.set(key, outcome);\n return outcome;\n } catch (err) {\n if (latestSeq.get(field) !== mySeq) return { status: UNIQUE_STATUS.STALE };\n // FAIL SAFE. A network/server failure must never block a user from\n // typing or submitting — submit-time enforcement on the server is the\n // authoritative check, and it still runs. Not cached, so the next blur\n // retries.\n return { status: UNIQUE_STATUS.ERROR, error: err };\n } finally {\n setPending(Math.max(0, pending - 1));\n }\n }\n\n return {\n check,\n isPending: () => pending > 0,\n pendingCount: () => pending,\n // Exposed for tests / a form that reloads its config mid-session.\n reset: () => { cache.clear(); latestSeq.clear(); },\n };\n}\n\nfunction firstDuplicateMessage(result) {\n const fromErrors = (result?.errors ?? []).find((item) => item?.message)?.message;\n return fromErrors || result?.message || '';\n}\n\n// ── antd rule factory ────────────────────────────────────────────────────────\n\n// Marker so getRules can find the unique rules again after the array is built\n// (see attachUniqueSyncGuards).\nconst UNIQUE_RULE_FLAG = '__uniqueRule';\n\n/**\n * Build the async antd rule for one `unique` validations entry.\n * `context` is supplied by the form engine: { checker, module, recordId,\n * clientId, region }. With no context (test harnesses, other callers of\n * getRules) the rule degrades to a no-op instead of throwing.\n */\nexport function buildUniqueValidationRule({ field, validation, context }) {\n const rule = parseUniqueValidation(validation);\n if (!rule) return { validator: () => Promise.resolve() };\n\n const fieldKey = field?.field ?? '';\n if (!context?.checker || !context?.module || !fieldKey) {\n return { validator: () => Promise.resolve() };\n }\n\n return {\n [UNIQUE_RULE_FLAG]: rule,\n validateTrigger: uniqueValidateTriggers(rule),\n validator: async (_, value) => {\n const outcome = await context.checker.check({\n module: context.module,\n field: fieldKey,\n rule,\n value,\n recordId: context.recordId,\n clientId: context.clientId,\n region: context.region,\n });\n if (outcome.status === UNIQUE_STATUS.DUPLICATE) {\n return Promise.reject(new Error(outcome.message || rule.message));\n }\n // skipped / available / stale / error all pass: a stale answer describes\n // a value the user has already replaced, and an error is handled by the\n // authoritative server-side check at submit.\n return Promise.resolve();\n },\n };\n}\n\n/**\n * The uniqueness call must never fire for a value that is ALREADY known\n * invalid (blank required field, malformed email, failed pattern) — that would\n * waste a round trip and stack a confusing second error under the field.\n *\n * antd runs a field's rules in parallel, so ordering alone cannot express\n * \"after the synchronous rules\". Instead each unique rule is re-wrapped with a\n * guard that first evaluates its sibling rules against the same value and\n * resolves immediately if any of them fails.\n */\nexport function attachUniqueSyncGuards(rules = []) {\n const list = Array.isArray(rules) ? rules : [];\n if (!list.some((rule) => rule && rule[UNIQUE_RULE_FLAG])) return list;\n\n const siblings = list.filter((rule) => rule && !rule[UNIQUE_RULE_FLAG]);\n return list.map((rule) => {\n if (!rule || !rule[UNIQUE_RULE_FLAG]) return rule;\n const inner = rule.validator;\n return {\n ...rule,\n validator: async (ruleArg, value) => {\n if (await hasSyncRuleError(siblings, value, ruleArg)) return Promise.resolve();\n return inner(ruleArg, value);\n },\n };\n });\n}\n\n// Minimal evaluator for the rule shapes this form engine actually produces:\n// { required }, { pattern }, { len }, { type:'url' } and custom { validator }.\n// Any rule it cannot interpret is treated as passing — the guard exists to\n// suppress a redundant request, never to invent a failure.\nexport async function hasSyncRuleError(rules = [], value, ruleArg = {}) {\n for (const rule of rules) {\n if (!rule || typeof rule === 'string') continue;\n if (rule.required && isBlankUniqueValue(value)) return true;\n if (!isBlankUniqueValue(value)) {\n if (rule.pattern instanceof RegExp && !new RegExp(rule.pattern.source, rule.pattern.flags).test(String(value))) return true;\n if (rule.len != null && String(value).length !== Number(rule.len)) return true;\n }\n if (typeof rule.validator === 'function') {\n try {\n await rule.validator(ruleArg, value);\n } catch {\n return true;\n }\n }\n }\n return false;\n}\n\n// ── Submit-time 409 → field errors ───────────────────────────────────────────\n\nfunction parseMaybeJson(text) {\n const trimmed = String(text ?? '').trim();\n if (!trimmed.startsWith('{')) return null;\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n}\n\nfunction duplicateBody(source) {\n if (!source) return null;\n if (typeof source === 'string') return parseMaybeJson(source);\n // An Error thrown by the create/update services: the structured body rides\n // on `.data` (createModuleRecord) or `.response` (fetchJsonWithAuth).\n const candidates = [source.data, source.response, source, parseMaybeJson(source.message)];\n for (const candidate of candidates) {\n if (!candidate || typeof candidate !== 'object') continue;\n if (Array.isArray(candidate.errors) || candidate.code === DUPLICATE_VALUE_CODE) return candidate;\n }\n return null;\n}\n\n/**\n * 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 field: item.field,\n message: item.message || body.message || DEFAULT_MESSAGE,\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 return matched.map((entry) => ({ field: 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","// 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","import { Button } from 'antd';\nimport { Link as RouterLink } from 'react-router-dom';\nimport '../styles/AppButton.css';\n\nexport default function AppButton({\n children,\n className = '',\n icon,\n to,\n variant = 'default',\n ...rest\n}) {\n const button = (\n <Button\n className={`app-button app-button--${variant} ${className}`.trim()}\n icon={icon}\n {...rest}\n >\n {children}\n </Button>\n );\n\n if (to) {\n return (\n <RouterLink className=\"app-button-link\" to={to}>\n {button}\n </RouterLink>\n );\n }\n\n return button;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// userNames — turning a stored `updatedBy` id into a person's name.\n//\n// Rate history (and any other audit trail) stores WHO changed a value as an id.\n// The detail view resolved it through one directory call — /module/list?\n// module=users — and indexed the result on `legacyUserId` ALONE. Ids stamped by\n// the auth service are auth user ids, so they missed that index and the history\n// row read \"User #123\": a number shown to a recruiter as if it meant something.\n//\n// Two fixes, both generic:\n// • index every id form a users row can carry (legacyUserId / userId / _id /\n// id) — the same person under all their identities;\n// • resolve a still-unknown id through the auth service's own user endpoint,\n// cached per id for the session so a long history costs one call per\n// distinct actor, not one per row.\n//\n// And when it is STILL unresolvable, render nothing. \"User #123\" is not\n// information; an empty attribution is honest.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport { fetchJsonWithAuth } from '../../services/authApi';\nimport { AUTH_URL } from '../../services/apiConfig';\n\n/**\n * userDisplayName — the same name-shape preference the finance panel uses\n * (financeApprovalData.userDisplayName), so one person reads identically on\n * every surface regardless of which endpoint produced the row.\n */\nexport function userDisplayName(user) {\n if (!user || typeof user !== 'object') return '';\n const firstName = user.first_name ?? user.firstName ?? user.FIRST_NAME;\n const lastName = user.last_name ?? user.lastName ?? user.LAST_NAME;\n return [firstName, lastName].filter(Boolean).join(' ').trim()\n || user.name\n || user.user_name\n || user.userName\n || user.username\n || user.email\n || '';\n}\n\n/**\n * indexUserRows — id → name for EVERY id a directory row carries, so an actor\n * stamped with an auth id and one stamped with a legacy id both resolve.\n */\nexport function indexUserRows(rows) {\n const map = {};\n (Array.isArray(rows) ? rows : []).forEach((user) => {\n const name = userDisplayName(user);\n if (!name) return;\n [user.legacyUserId, user.userId, user.user_id, user._id, user.id].forEach((id) => {\n if (id === undefined || id === null || id === '') return;\n const key = String(typeof id === 'object' ? (id.$oid ?? id.id ?? '') : id);\n if (key && map[key] === undefined) map[key] = name;\n });\n });\n return map;\n}\n\n// Session cache: id → Promise<string>. Shared across every detail view so\n// re-opening a record never re-asks for the same person.\nconst userNameCache = new Map();\n\n/**\n * fetchUserName — resolve one id through the auth service, '' when unknown.\n * Never throws: an unresolvable actor must degrade to no attribution, never to\n * a broken detail page.\n */\nexport function fetchUserName(userId) {\n const key = String(userId ?? '').trim();\n if (!key) return Promise.resolve('');\n if (userNameCache.has(key)) return userNameCache.get(key);\n const request = fetchJsonWithAuth(\n AUTH_URL,\n `/get-detailed-view?userId=${encodeURIComponent(key)}`,\n )\n .then((json) => userDisplayName(json?.data ?? json ?? null))\n .catch(() => '');\n userNameCache.set(key, request);\n return request;\n}\n\n// Test seam — lets a suite prime/clear the cache without a network call.\nexport function primeUserName(userId, name) {\n userNameCache.set(String(userId), Promise.resolve(name));\n}\nexport function clearUserNameCache() {\n userNameCache.clear();\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// quickCreateNotice — config + resolution logic for the \"you just created a\n// brand-new record\" popup shown after an in-dropdown quick-create.\n//\n// WHY this lives in its own module rather than inside QuickCreateEditField:\n// anything that imports AddFormV1/EditFormV1 (which QuickCreateEditField does,\n// lazily, and which the admin screen does statically) cannot be rendered under\n// vitest — react-pdf's DocumentViewer needs DOMMatrix and jsdom has none. That\n// is a pre-existing trap in this repo. Keeping the *decisions* (is the notice\n// on? what is the record called? who created it?) in a dependency-free module\n// means the part that can actually be wrong is unit-testable, and the React\n// file is left with nothing but rendering.\n//\n// Everything here is generic: no module name, no field name, no collection is\n// special-cased. A field opts in through the admin config key\n// `quickCreateNotice`.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport { fetchJsonWithAuth, getStoredUser } from '../../services/authApi';\nimport { AUTH_URL } from '../../services/apiConfig';\nimport { fetchUserName, userDisplayName } from '../detail/userNames';\n\n/**\n * The product-owner-approved default. It is the DEFAULT rather than something\n * the admin must type, so that a field which merely switches the notice on\n * still shows the correct warning. Admin config overrides it verbatim.\n */\nexport const DEFAULT_QUICK_CREATE_NOTICE_MESSAGE =\n 'A new client has been created successfully. Please contact your Organization Admin to '\n + 'configure the required forms, workflows, permissions, and client-specific details before '\n + 'proceeding with further operations.';\n\nexport const DEFAULT_QUICK_CREATE_NOTICE_TITLE = 'New record created';\nexport const DEFAULT_QUICK_CREATE_RECORD_LABEL = 'Record name';\nexport const DEFAULT_QUICK_CREATE_CREATOR_LABEL = 'Created by';\n\nconst str = (v) => (v === undefined || v === null ? '' : String(v).trim());\n\n/**\n * normalizeQuickCreateNotice — field config → the resolved notice settings, or\n * null when this field has not opted in.\n *\n * `enabled` defaults to TRUE when the key is present at all: an admin who took\n * the trouble to author a `quickCreateNotice` object meant to switch it on, and\n * a config object that silently does nothing is the worst of the two failure\n * modes. Only an explicit `enabled: false` turns it off.\n */\nexport function normalizeQuickCreateNotice(field) {\n const raw = field?.quickCreateNotice;\n if (!raw || typeof raw !== 'object') return null;\n if (raw.enabled === false) return null;\n\n return {\n enabled: true,\n title: str(raw.title) || DEFAULT_QUICK_CREATE_NOTICE_TITLE,\n message: str(raw.message) || DEFAULT_QUICK_CREATE_NOTICE_MESSAGE,\n // Both traceability lines are ON unless explicitly disabled — same reasoning\n // as `enabled` above.\n showCreator: raw.showCreator !== false,\n showRecordName: raw.showRecordName !== false,\n recordNameField: str(raw.recordNameField),\n recordLabel: str(raw.recordLabel) || DEFAULT_QUICK_CREATE_RECORD_LABEL,\n creatorLabel: str(raw.creatorLabel) || DEFAULT_QUICK_CREATE_CREATOR_LABEL,\n };\n}\n\n// Keys that commonly hold a record's human name, tried in order when the admin\n// configured no recordNameField and the field carries no lookup displayField.\nconst NAME_LIKE_KEYS = [\n 'clientName', 'companyName', 'name', 'title', 'fullName',\n 'displayName', 'label', 'employerName', 'vendorName',\n];\n\nconst readPath = (record, key) => {\n if (!record || !key) return undefined;\n return String(key).split('.').reduce((acc, part) => (\n acc && typeof acc === 'object' ? acc[part] : undefined\n ), record);\n};\n\nconst scalarName = (v) => {\n if (typeof v === 'string' || typeof v === 'number') return str(v);\n // Labeled-select / reference shapes ({label,value}) show up on records too.\n if (v && typeof v === 'object') return str(v.label ?? v.name ?? v.title ?? '');\n return '';\n};\n\n/**\n * pickRecordName — the record's display name, by the configured key first, then\n * the field's lookup displayField, then well-known name-like keys, then ANY\n * string key whose name ends in \"name\". Purely best-effort: an empty result is\n * a legitimate outcome and must not stop the notice from appearing.\n */\nexport function pickRecordName(record, { recordNameField = '', displayField = '' } = {}) {\n if (!record || typeof record !== 'object') return '';\n for (const key of [recordNameField, displayField, ...NAME_LIKE_KEYS]) {\n if (!key) continue;\n const value = scalarName(readPath(record, key));\n if (value) return value;\n }\n const loose = Object.keys(record).find((k) => (\n /name$/i.test(k) && typeof record[k] === 'string' && record[k].trim()\n ));\n return loose ? str(record[loose]) : '';\n}\n\n/**\n * pickCreatorId — the id of whoever the stored record credits with its\n * creation, across the several shapes the gateway's audit stamp can take\n * (plain id, ObjectId wrapper, nested user object).\n */\nexport function pickCreatorId(record) {\n if (!record || typeof record !== 'object') return '';\n const candidates = [\n record.createdBy, record.created_by, record.createdById,\n record.createdUserId, record.recordMeta?.createdBy, record.ownerId,\n ];\n for (const candidate of candidates) {\n if (candidate === undefined || candidate === null || candidate === '') continue;\n if (typeof candidate === 'object') {\n const nested = candidate.$oid ?? candidate.userId ?? candidate._id ?? candidate.id;\n const value = str(nested);\n if (value) return value;\n continue;\n }\n const value = str(candidate);\n if (value) return value;\n }\n return '';\n}\n\n/** currentUserName — the signed-in user's display name, '' when unknowable. */\nexport function currentUserName(getUser = getStoredUser) {\n try {\n return userDisplayName(getUser()) || '';\n } catch {\n return '';\n }\n}\n\nconst defaultFetchRecord = (module, recordId) => fetchJsonWithAuth(\n AUTH_URL,\n `/module/list?module=${encodeURIComponent(module)}&id=${encodeURIComponent(recordId)}`,\n).then((res) => {\n const rows = res?.data?.data ?? res?.data ?? [];\n return Array.isArray(rows) ? rows[0] : rows;\n});\n\n/**\n * resolveQuickCreateNoticeDetails — the two traceability lookups.\n *\n * FAIL OPEN, deliberately and on every branch: the warning message is the point\n * of this popup, the name and the creator are garnish. A directory that is down,\n * a record the list endpoint cannot return, a module name that does not resolve\n * — none of it may suppress the notice or throw into the form. Every await is\n * individually caught and degrades to an empty string.\n *\n * The creator falls back to the signed-in user because in this flow the creator\n * IS the current user: they pressed \"Add More\" seconds ago. That fallback is\n * therefore accurate, not a guess.\n *\n * Dependencies are injected (with real defaults) so this is testable without a\n * network or a browser.\n */\nexport async function resolveQuickCreateNoticeDetails({\n module,\n recordId,\n notice,\n displayField = '',\n deps = {},\n} = {}) {\n const {\n fetchRecord = defaultFetchRecord,\n fetchUserNameFn = fetchUserName,\n getUser = getStoredUser,\n } = deps;\n\n let record = null;\n if (module && recordId) {\n try {\n record = await fetchRecord(module, recordId);\n } catch {\n record = null; // fail open\n }\n }\n\n const recordName = notice?.showRecordName === false\n ? ''\n : pickRecordName(record, { recordNameField: notice?.recordNameField, displayField });\n\n let creatorName = '';\n if (notice?.showCreator !== false) {\n const creatorId = pickCreatorId(record);\n if (creatorId) {\n try {\n creatorName = str(await fetchUserNameFn(creatorId));\n } catch {\n creatorName = ''; // fail open\n }\n }\n if (!creatorName) creatorName = currentUserName(getUser);\n }\n\n return { recordName, creatorName };\n}\n","// QuickCreateEditField — the \"Add More\" / \"Edit\" affordance inside a select\n// dropdown. Fully admin-config driven (FormGroupField.quickCreate / quickEdit,\n// see the Go struct). When a select carries quickCreate, its dropdown grows an\n// \"Add More\" button that opens the target module's Add form in a modal; on a\n// successful create the host field refetches its options and selects the new\n// record. quickEdit adds an \"Edit\" button that opens the target module's Edit\n// form for a resolved record id (this field's own value, or a sibling field via\n// quickEditIdFrom — e.g. \"edit the SELECTED CLIENT to add a contact\" from the\n// job's Contact Person field). No module or field name is hardcoded here.\n//\n// A field may also carry `quickCreateNotice` (see ./quickCreateNotice.js): a\n// popup shown the instant a record is created HERE, warning that a brand-new\n// record is not yet configured. Because only a real quick-create reaches that\n// code path, the notice cannot fire for an existing record that was merely\n// selected.\n//\n// AddFormV1/EditFormV1 are pulled in with React.lazy so this module can be\n// imported by those same files without a static circular dependency.\nimport React, { Suspense, lazy, useMemo, useState } from 'react';\nimport { 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 const openCreate = () => {\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 {field.quickCreateLabel || 'Add More'}\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 ? 'Select a record first' : undefined}\n >\n {field.quickEditLabel || 'Edit'}\n </Button>\n )}\n </Space>\n </div>\n );\n\n const modalNode = modal ? (\n <Modal\n open\n title={modal.mode === 'create' ? (field.quickCreateLabel || 'Add More') : (field.quickEditLabel || 'Edit')}\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","// 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\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\nexport default { narrowOptionsByConstraint };\n","// fieldTooltip — the single resolver for a field's admin-set help tooltip,\n// shared by AddFormV1 and EditFormV1.\n//\n// Two config shapes exist and both are honoured:\n// • `field.tooltip` (NEW key — one string, always shown)\n// • `field.infoEnabled` + `field.infoText` (the original toggle + text pair)\n//\n// Before this, tooltip text only ever reached the screen through FieldLabel, so\n// a field rendered WITHOUT a label (an inline/combined box, or a group that\n// prints one shared header row) could never carry one. Both forms now fall back\n// to wrapping the control itself for those, so ANY control type can get an\n// admin-set tooltip — which is the whole point: the text is config, the\n// capability is code.\n\nconst blank = (v) => v === undefined || v === null || String(v).trim() === '';\nconst truthy = (v) => v === true || v === 1 || v === '1' || v === 'true';\n\n/** @returns {string} the tooltip text for a field, or '' when it has none. */\nexport function fieldTooltipText(field) {\n if (!blank(field?.tooltip)) return String(field.tooltip);\n if (truthy(field?.infoEnabled) && !blank(field?.infoText)) return String(field.infoText);\n return '';\n}\n\nexport default { fieldTooltipText };\n","// Shared role/permission gate used by row actions (ListView, DetailHeaderCard)\n// and, via AddFormV1/EditFormV1, by field-level visiblePermission/editablePermission.\n// A permission value looks like \"<module>.<key>\" (e.g. \"submission.viewRate\").\n//\n// checkPermission() is the CURRENT gate — it resolves against the `can`\n// function from src/hooks/usePermissions.js (backed by GET /me/permissions,\n// see src/contexts/PermissionContext.jsx), which every consumer must call\n// usePermissions() to obtain and pass in. roleAllowsAction() below is the\n// OLD, localStorage.menuPermission-based gate — kept only for isActionValueAllowed's\n// row-level (record.actionPermission.<key>) use, which is a separate,\n// per-record mechanism unrelated to role permissions.\n\nexport function isActionValueAllowed(value) {\n if (value === undefined || value === null) return true;\n if (typeof value === 'object') {\n return isActionValueAllowed(value.permission ?? value.allowed ?? value.value);\n }\n return value !== false && value !== 0 && value !== '0';\n}\n\n// permission: \"<module>.<key>\" string from admin config (RowAction.permission,\n// field.visiblePermission/editablePermission). can: the `can` function returned\n// by usePermissions(). Falls back to the field/action's own moduleName when the\n// permission string has no module prefix (a bare key, e.g. \"edit\").\nexport function checkPermission(can, permission, moduleName) {\n if (!permission) return true;\n if (typeof can !== 'function') return true;\n const [configuredModule, configuredKey] = String(permission).split('.');\n const module = configuredKey ? configuredModule : (moduleName || configuredModule);\n const key = configuredKey ?? configuredModule;\n return can(module, key);\n}\n\n// Deprecated — localStorage.menuPermission-based gate, superseded by\n// checkPermission()/usePermissions(). No remaining call sites; kept only so a\n// stray import doesn't break until it's confirmed unused everywhere.\nexport function roleAllowsAction(permission, moduleName) {\n if (!permission) return true;\n try {\n const permissions = JSON.parse(localStorage.getItem('menuPermission') || '{}');\n const [configuredModule, configuredKey] = String(permission).split('.');\n const moduleKey = configuredModule || String(moduleName || '').replace(/s$/i, '');\n const modulePermissions = permissions[moduleKey]\n ?? permissions[String(moduleName || '')]\n ?? permissions[String(moduleName || '').replace(/s$/i, '')]\n ?? {};\n return isActionValueAllowed(modulePermissions[configuredKey]);\n } catch {\n return true;\n }\n}\n","// Shared \"After Submit\" navigation resolver used by AddFormV1/EditFormV1.\n// Admin-configured per FormGroup (group.afterSubmit — see FormGroupsSection.jsx),\n// generic across every module/project: no module name is ever referenced here.\n\n// Picks the configured behavior from the group with the lowest `order` that\n// declares one. Returns null when no group configures it, so callers fall\n// back to their existing (pre-feature) navigation — unchanged behavior.\nexport function resolveAfterSubmit(groups) {\n const withConfig = (groups ?? [])\n .filter((g) => g?.afterSubmit?.mode)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n return withConfig[0]?.afterSubmit ?? null;\n}\n\n// Resolves a configured route template (e.g. \"/trainers/:id\") against the\n// created/updated record id and the submitted field values, and rejects\n// anything that isn't a safe in-app path (blocks open-redirect / javascript:\n// / data: payloads an admin could otherwise paste into the target field).\nexport function safeNavTarget(template, recordId, values) {\n const raw = String(template ?? '').trim();\n if (!raw || !raw.startsWith('/') || raw.startsWith('//')) return null;\n if (/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null; // any \"scheme:\" prefix, e.g. javascript:, data:\n return raw.replace(/:([A-Za-z_][\\w]*)/g, (_, key) => {\n const value = key === 'id' ? recordId : values?.[key];\n return encodeURIComponent(value ?? '');\n });\n}\n","// Shared submit-toast resolver used by AddFormV1/EditFormV1.\n// Admin-configured per FormGroup (group.submitMessages — see FormGroupsSection),\n// generic across every module/project: no module name is ever referenced here.\n//\n// Defaults (no configuration anywhere, including brand-new modules):\n// add → \"<Module> has added successfully\"\n// edit → \"<Module> has Updated successfully\"\n// An admin can override any template (with ${module} substitution) or set\n// suppress to skip the success toast entirely.\n\n// Picks the configured messages from the group with the lowest `order` that\n// declares any — the same resolution rule afterSubmitNav uses.\nexport function resolveSubmitMessages(groups) {\n const withConfig = (groups ?? [])\n .filter((g) => g?.submitMessages && typeof g.submitMessages === 'object')\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n return withConfig[0]?.submitMessages ?? null;\n}\n\n// \"jobs\" → \"Jobs\", \"locationTaxMaster\" → \"LocationTaxMaster\" (first letter only —\n// the module key is the admin-facing name across the platform).\nfunction moduleLabel(moduleName) {\n const raw = String(moduleName ?? '').trim();\n return raw ? raw.charAt(0).toUpperCase() + raw.slice(1) : 'Record';\n}\n\nfunction fillTemplate(template, moduleName) {\n return String(template).replaceAll('${module}', moduleLabel(moduleName))\n .replaceAll('${moduleName}', moduleLabel(moduleName));\n}\n\n// Returns the success toast text for a finished submit, or null when the admin\n// suppressed success toasts for this module. kind: 'add' | 'edit'.\nexport function submitSuccessMessage(groups, moduleName, kind) {\n const config = resolveSubmitMessages(groups);\n if (config?.suppress) return null;\n const template = kind === 'edit' ? config?.editSuccess : config?.addSuccess;\n if (template && String(template).trim()) return fillTemplate(template, moduleName);\n return kind === 'edit'\n ? `${moduleLabel(moduleName)} has Updated successfully`\n : `${moduleLabel(moduleName)} has added successfully`;\n}\n\n// Returns the error toast text: admin override first, then the real server\n// error (most actionable), then a generic fallback. Never null — a failed\n// submit must always surface.\nexport function submitErrorMessage(groups, moduleName, kind, serverText) {\n const config = resolveSubmitMessages(groups);\n const template = kind === 'edit' ? config?.editError : config?.addError;\n if (template && String(template).trim()) return fillTemplate(template, moduleName);\n if (serverText && String(serverText).trim()) return String(serverText);\n return kind === 'edit'\n ? `Failed to update ${moduleLabel(moduleName)}`\n : `Failed to create ${moduleLabel(moduleName)}`;\n}\n","// Shared \"scroll to the first invalid field\" handler for AddFormV1/EditFormV1.\n// Wire it from the antd Form's onFinishFailed: when Create/Save is clicked with\n// validation errors, the screen scrolls to the first errored field, focuses it\n// (cursor placed in the input) and pulses a red halo so the user sees exactly\n// which field failed. Generic — works for every module's add & edit form.\n//\n// Robustness notes:\n// - antd applies the .ant-form-item-has-error classes AFTER onFinishFailed\n// fires, so location runs on a short delay.\n// - The DOM query + scrollIntoView is the PRIMARY mechanism and always runs.\n// form.scrollToField is only a best-effort extra: antd locates fields by\n// DOM id, which custom field controls don't always forward — relying on it\n// alone silently scrolls nothing.\n// - Callers should expand any collapsed sections BEFORE calling this (a field\n// inside a display:none section can be neither scrolled to nor focused) —\n// both form engines do so in their onFinishFailed wrapper.\nexport function scrollToFirstFormError({ errorFields } = {}, form) {\n if (!errorFields?.length) return;\n const firstName = errorFields[0]?.name;\n\n setTimeout(() => {\n if (form?.scrollToField && firstName !== undefined) {\n try {\n form.scrollToField(firstName, { behavior: 'smooth', block: 'center' });\n } catch { /* best-effort only — the DOM scroll below always runs */ }\n }\n\n const errorFormItem = document.querySelector('.ant-form-item-has-error');\n if (!errorFormItem) return;\n const input = errorFormItem.querySelector('input, textarea, select, .ProseMirror');\n const target = input ?? errorFormItem;\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n\n setTimeout(() => {\n input?.focus?.({ preventScroll: true });\n target.style.boxShadow = '0 0 0 3px rgba(255, 77, 79, 0.3)';\n setTimeout(() => { target.style.boxShadow = ''; }, 1200);\n }, 350);\n }, 60);\n}\n\nexport default scrollToFirstFormError;\n","function comparisonValues(value) {\n if (Array.isArray(value)) return value.map((item) => String(item ?? '').trim());\n return String(value ?? '').split(',').map((item) => item.trim());\n}\n\n// Matches the same generic operators supported by showIf. Keeping this helper\n// independent of React makes conditional labels usable in Add/Edit forms and\n// straightforward to test without module- or field-specific code.\nexport function conditionMatches(condition, value) {\n if (!condition?.field) return false;\n switch (condition.operator ?? 'eq') {\n case 'eq': return String(value ?? '') === String(condition.value ?? '');\n case 'neq': return String(value ?? '') !== String(condition.value ?? '');\n case 'truthy': return value !== undefined && value !== null && value !== '' && value !== false;\n case 'falsy': return value === undefined || value === null || value === '' || value === false;\n case 'notEmpty': return Array.isArray(value) ? value.length > 0 : Boolean(value);\n case 'in': return comparisonValues(condition.value).includes(String(value ?? ''));\n case 'notIn': return !comparisonValues(condition.value).includes(String(value ?? ''));\n default: return false;\n }\n}\n\nexport function configuredFieldLabel(field, watchedValue, combined = false) {\n const fallback = combined\n ? (field?.combineLabel ?? field?.label)\n : field?.label;\n const rule = field?.labelWhen;\n return conditionMatches(rule, watchedValue) && rule?.label\n ? rule.label\n : fallback;\n}\n\n// Flatten a showIf into its leaf conditions: the top-level {field,operator,value}\n// plus any \"add more\" entries in conditions[]. Mirrors how the forms read it.\nexport function showIfConditions(showIf) {\n if (!showIf) return [];\n const leaf = showIf.field ? [showIf] : [];\n return [...leaf, ...(showIf.conditions ?? []).filter((c) => c?.field)];\n}\n\n// Evaluate a showIf against an arbitrary value source. readValue(fieldKey) lets\n// the caller decide where values come from — live form state in the forms, or a\n// saved record in the detail view — so one condition definition drives both.\nexport function evaluateShowIfWith(showIf, readValue) {\n const conditions = showIfConditions(showIf);\n if (!conditions.length) return true;\n const results = conditions.map((c) => conditionMatches(c, readValue(c.field)));\n return showIf.logic === 'or' ? results.some(Boolean) : results.every(Boolean);\n}\n\n// colWhen — conditionally override a field's grid column span, so one field can\n// shrink to make room for a sibling that a showIf has just revealed (e.g. Work\n// Authorization narrows when its expiry date appears). Config is a rule, or a\n// list of rules, each {field, operator, value, col}; the first match wins.\n// Display only — the field key, payload mapping and validation are untouched.\nexport function colWhenRules(field) {\n const raw = field?.colWhen;\n if (!raw) return [];\n return (Array.isArray(raw) ? raw : [raw])\n .filter((rule) => rule?.field && Number(rule?.col) > 0);\n}\n\n// readValue(rule) resolves the watched value for one rule — the caller owns path\n// resolution so a rule can reference a sibling in the same addRow row or a\n// top-level field, exactly like showIf.\nexport function resolveColSpan(field, fallbackSpan, readValue) {\n for (const rule of colWhenRules(field)) {\n if (conditionMatches(rule, readValue(rule))) return Number(rule.col);\n }\n return fallbackSpan;\n}\n\nexport function conditionFieldPath(condition, siblingPrefix) {\n if (!condition?.field) return ['__noop_conditional_label__'];\n return siblingPrefix != null\n ? [...(Array.isArray(siblingPrefix) ? siblingPrefix : [siblingPrefix]), condition.field]\n : [condition.field];\n}\n","import { Breadcrumb } from 'antd';\nimport { DownOutlined } from '@ant-design/icons';\nimport { Link } from 'react-router-dom';\nimport AppTypography from './typography/Typography';\n\nexport default function AppBreadcrumb({ items = [] }) {\n const breadcrumbItems = items.map((item, index) => {\n const isLast = index === items.length - 1;\n return {\n title: isLast ? (\n <AppTypography variant=\"body\" weight=\"medium\" color=\"link\">\n {item.label}\n </AppTypography>\n ) : (\n <Link to={item.href}>\n <AppTypography variant=\"body\" color=\"secondary\">\n {item.label}\n </AppTypography>\n {item.dropdown && <DownOutlined />}\n </Link>\n ),\n };\n });\n\n return <Breadcrumb items={breadcrumbItems} />;\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Breadcrumb-CPjRRUg1.cjs","names":[],"sources":["../src/theme/colors/colors.js","../src/components/typography/Typography.jsx","../src/components/TipTapEditor.jsx","../src/components/DocumentViewer.jsx","../src/utils/modulePath.js","../src/services/moduleDataApi.js","../src/services/addFormApi.js","../src/services/adminApi.js","../src/services/documentApi.js","../src/services/detailedViewApi.js","../src/services/detailDefaults.js","../src/utils/roleFormPermissionGate.js","../src/components/form/inputSecurity.js","../src/components/form/optionMatching.js","../src/components/form/payloadTransformer.js","../src/components/form/linkedAddRowGroups.js","../src/services/aiActionApi.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/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/optionConstraints.js","../src/components/form/fieldTooltip.js","../src/utils/roleAllowsAction.js","../src/utils/afterSubmitNav.js","../src/utils/submitMessages.js","../src/utils/scrollToFirstFormError.js","../src/components/form/conditionalFieldLabel.js","../src/components/Breadcrumb.jsx"],"sourcesContent":["export const colors = {\n brand: '#0053a5',\n brandDark: '#1d4ed8',\n brandDarker: '#1e40af',\n brandHover: '#004f85',\n brandSoft: '#dfedf7',\n brandSofter: '#e7f2fa',\n brandSubtle: '#edf8fe',\n\n textPrimary: '#111827',\n textSecondary: '#4b5563',\n textMuted: '#6b7280',\n textSubtle: '#010306',\n textHeading: '#142235',\n textDark: '#232a31',\n textPlaceholder: '#a7b0bb',\n textInverse: '#ffffff',\n textLink: '#0053a5',\n\n surfacePage: '#f5f6fa',\n surfaceSoft: '#f8fafc',\n surfaceSofter: '#f3f8fb',\n surfaceCard: '#ffffff',\n surfaceHover: '#eff6ff',\n surfaceHoverLight: '#f8fcff',\n surfaceHoverSoft: '#f5fbff',\n surfaceSelected: '#dbeafe',\n surfaceControl: '#f4f8fb',\n surfaceRowAlt: '#fbfdff',\n\n border: '#e6f0ff',\n borderLight: '#eef3f8',\n borderSofter: '#f0f0f0',\n borderMuted: '#d8e4ef',\n borderInput: '#d5dde5',\n borderFocus: '#7dbce6',\n borderHover: '#99c7e8',\n controlBorder: '#77abd0',\n controlBorderMuted: '#e7edf3',\n controlAccent: '#4f9ac7',\n\n iconMuted: '#d1d5db',\n iconSubtle: '#9aa6b2',\n iconNeutral: '#8c8c8c',\n iconSoft: '#bbbbbb',\n scrollbarThumb: '#c4ccd8',\n scrollbarThumbLight: '#d1d5db',\n danger: '#dc2626',\n dangerSoft: '#ef4444',\n dangerStrong: '#e11d24',\n success: '#15803d',\n successSoft: '#16a34a',\n warning: '#f97316',\n info: '#3b82f6',\n transparent: 'transparent',\n\n statusNeutralBg: '#f1f3ee',\n statusNeutralText: '#717b36',\n statusProcessingBg: '#f6f4f7',\n statusProcessingText: '#273048',\n statusProcessingBorder: '#d4d8dd',\n statusWarningBg: '#fff7ea',\n statusWarningText: '#bf7328',\n\n shadowMenu: 'rgba(15, 35, 55, 0.08)',\n shadowBadge: 'rgba(26, 95, 145, 0.08)',\n shadowTag: 'rgba(39, 51, 70, 0.05)',\n shadowAvatar: 'rgba(57, 77, 103, 0.12)',\n shadowDropdown: '0 6px 16px 0 rgba(0, 0, 0, .08), 0 3px 6px -4px rgba(0, 0, 0, .12), 0 9px 28px 8px rgba(0, 0, 0, .05)',\n avatarBlueBg: '#eaf3ff',\n avatarBlueText: '#142235',\n avatarPurpleBg: '#f2eaff',\n avatarMoreBg: '#eef6ff',\n avatarMoreText: '#0053a5',\n\n avatarNeutralBg: '#d9d9d9',\n avatarNeutralText: '#555555',\n avatarIndigo: '#6366f1',\n avatarPurple: '#8b5cf6',\n avatarBlue: '#3b82f6',\n avatarGreen: '#10b981',\n linkedIn: '#0077b5',\n};\n\nexport const typographyColors = {\n primary: colors.textPrimary,\n secondary: colors.textSecondary,\n muted: colors.textMuted,\n subtle: colors.textSubtle,\n inverse: colors.textInverse,\n link: colors.textLink,\n danger: colors.danger,\n success: colors.success,\n};\n\nexport const onboardingStageToneColors = {\n approved: {\n background: '#f2f2e8',\n text: '#79772d',\n },\n danger: {\n background: '#fde7e9',\n text: colors.danger,\n },\n issued: {\n background: '#e9f2fb',\n text: colors.textLink,\n },\n neutral: {\n background: colors.surfaceSoft,\n text: colors.textSecondary,\n },\n success: {\n background: '#e7f5ee',\n text: colors.success,\n },\n warning: {\n background: '#fbf0e7',\n text: colors.warning,\n },\n};\n\nexport const colorVars = {\n brand: 'var(--color-brand)',\n brandDark: 'var(--color-brand-dark)',\n brandDarker: 'var(--color-brand-darker)',\n brandHover: 'var(--color-brand-hover)',\n brandSoft: 'var(--color-brand-soft)',\n brandSofter: 'var(--color-brand-softer)',\n brandSubtle: 'var(--color-brand-subtle)',\n\n textPrimary: 'var(--color-text-primary)',\n textSecondary: 'var(--color-text-secondary)',\n textMuted: 'var(--color-text-muted)',\n textSubtle: 'var(--color-text-subtle)',\n textHeading: 'var(--color-text-heading)',\n textDark: 'var(--color-text-dark)',\n textPlaceholder: 'var(--color-text-placeholder)',\n textInverse: 'var(--color-text-inverse)',\n textLink: 'var(--color-text-link)',\n\n surfacePage: 'var(--color-surface-page)',\n surfaceSoft: 'var(--color-surface-soft)',\n surfaceSofter: 'var(--color-surface-softer)',\n surfaceCard: 'var(--color-surface-card)',\n surfaceHover: 'var(--color-surface-hover)',\n surfaceHoverLight: 'var(--color-surface-hover-light)',\n surfaceHoverSoft: 'var(--color-surface-hover-soft)',\n surfaceSelected: 'var(--color-surface-selected)',\n surfaceControl: 'var(--color-surface-control)',\n surfaceRowAlt: 'var(--color-surface-row-alt)',\n\n border: 'var(--color-border)',\n borderLight: 'var(--color-border-light)',\n borderSofter: 'var(--color-border-softer)',\n borderMuted: 'var(--color-border-muted)',\n borderInput: 'var(--color-border-input)',\n borderFocus: 'var(--color-border-focus)',\n borderHover: 'var(--color-border-hover)',\n controlBorder: 'var(--color-control-border)',\n controlBorderMuted: 'var(--color-control-border-muted)',\n controlAccent: 'var(--color-control-accent)',\n\n iconMuted: 'var(--color-icon-muted)',\n iconSubtle: 'var(--color-icon-subtle)',\n iconNeutral: 'var(--color-icon-neutral)',\n iconSoft: 'var(--color-icon-soft)',\n scrollbarThumb: 'var(--color-scrollbar-thumb)',\n scrollbarThumbLight: 'var(--color-scrollbar-thumb-light)',\n danger: 'var(--color-danger)',\n dangerSoft: 'var(--color-danger-soft)',\n dangerStrong: 'var(--color-danger-strong)',\n success: 'var(--color-success)',\n successSoft: 'var(--color-success-soft)',\n warning: 'var(--color-warning)',\n info: 'var(--color-info)',\n transparent: 'var(--color-transparent)',\n linkedIn: 'var(--color-linkedin)',\n\n statusNeutralBg: 'var(--color-status-neutral-bg)',\n statusNeutralText: 'var(--color-status-neutral-text)',\n statusProcessingBg: 'var(--color-status-processing-bg)',\n statusProcessingText: 'var(--color-status-processing-text)',\n statusProcessingBorder: 'var(--color-status-processing-border)',\n statusWarningBg: 'var(--color-status-warning-bg)',\n statusWarningText: 'var(--color-status-warning-text)',\n\n shadowMenu: 'var(--color-shadow-menu)',\n shadowBadge: 'var(--color-shadow-badge)',\n shadowTag: 'var(--color-shadow-tag)',\n shadowAvatar: 'var(--color-shadow-avatar)',\n shadowDropdown: 'var(--color-shadow-dropdown)',\n avatarBlueBg: 'var(--color-avatar-blue-bg)',\n avatarBlueText: 'var(--color-avatar-blue-text)',\n avatarPurpleBg: 'var(--color-avatar-purple-bg)',\n avatarMoreBg: 'var(--color-avatar-more-bg)',\n avatarMoreText: 'var(--color-avatar-more-text)',\n};\n","import { Typography } from 'antd';\nimport { colorVars } from '../../theme/colors/colors';\n\nconst { Text, Title, Paragraph, Link } = Typography;\n\nconst defaultElementByVariant = {\n display: 'h1',\n h1: 'h1',\n h2: 'h2',\n h3: 'h3',\n h4: 'h4',\n h5: 'h5',\n 'section-title': 'h3',\n 'card-title': 'h4',\n subtitle: 'span',\n body: 'span',\n 'body-strong': 'span',\n label: 'span',\n caption: 'span',\n meta: 'span',\n metric: 'span',\n helper: 'span',\n link: 'a',\n};\n\nconst namedSizes = {\n xs: 'var(--font-size-xs)',\n sm: 'var(--font-size-sm)',\n md: 'var(--font-size-md)',\n lg: 'var(--font-size-lg)',\n xl: 'var(--font-size-xl)',\n '2xl': 'var(--font-size-2xl)',\n '3xl': 'var(--font-size-3xl)',\n '4xl': 'var(--font-size-4xl)',\n};\n\nconst namedWeights = {\n regular: 'var(--font-weight-regular)',\n medium: 'var(--font-weight-medium)',\n semibold: 'var(--font-weight-semibold)',\n bold: 'var(--font-weight-bold)',\n extrabold: 'var(--font-weight-extrabold)',\n};\n\nconst namedLineHeights = {\n tight: 'var(--line-height-tight)',\n snug: 'var(--line-height-snug)',\n normal: 'var(--line-height-normal)',\n relaxed: 'var(--line-height-relaxed)',\n};\n\nconst namedColors = {\n primary: colorVars.textPrimary,\n secondary: colorVars.textSecondary,\n muted: colorVars.textMuted,\n subtle: colorVars.textSubtle,\n inverse: colorVars.textInverse,\n link: colorVars.textLink,\n danger: colorVars.danger,\n success: colorVars.success,\n};\n\nfunction cx(...classes) {\n return classes.filter(Boolean).join(' ');\n}\n\nfunction tokenValue(value, tokens) {\n if (value === undefined || value === null) return undefined;\n return tokens[value] || value;\n}\n\nfunction getAntTypographyComponent(tag, variant) {\n if (variant === 'link' || tag === 'a') return Link;\n if (tag === 'p') return Paragraph;\n if (['h1', 'h2', 'h3', 'h4', 'h5'].includes(tag)) return Title;\n return Text;\n}\n\nfunction getTitleLevel(tag, variant) {\n const resolvedTag = tag || defaultElementByVariant[variant];\n if (!resolvedTag?.startsWith('h')) return undefined;\n return Number(resolvedTag.slice(1));\n}\n\nexport default function AppTypography({\n as,\n tag,\n variant = 'body',\n color,\n size,\n weight,\n lineHeight,\n align,\n truncate = false,\n display,\n className,\n style,\n children,\n ...props\n}) {\n const resolvedTag = tag || as || defaultElementByVariant[variant] || 'span';\n const Component = getAntTypographyComponent(resolvedTag, variant);\n const titleLevel = getTitleLevel(resolvedTag, variant);\n const dynamicStyle = {\n color: tokenValue(color, namedColors),\n fontSize: tokenValue(size, namedSizes),\n fontWeight: tokenValue(weight, namedWeights),\n lineHeight: tokenValue(lineHeight, namedLineHeights),\n display,\n ...style,\n };\n\n return (\n <Component\n {...(titleLevel ? { level: titleLevel } : {})}\n className={cx(\n 'app-typography',\n `app-typography--${variant}`,\n color && namedColors[color] && `app-typography--${color}`,\n align && `app-typography--${align}`,\n truncate && 'app-typography--truncate',\n className,\n )}\n style={dynamicStyle}\n {...props}\n >\n {children}\n </Component>\n );\n}\n","/**\n * TipTapEditor — Rich text editor component\n * Drop-in replacement for ReactQuill in EditFormV1\n *\n * Props:\n * value string — HTML string (controlled)\n * onChange function — called with HTML string on every change\n * disabled boolean — makes editor read-only\n * placeholder string — placeholder text\n */\n\nimport { useEffect, useRef } from 'react';\nimport { useEditor, EditorContent } from '@tiptap/react';\nimport StarterKit from '@tiptap/starter-kit';\nimport Link from '@tiptap/extension-link';\nimport Underline from '@tiptap/extension-underline';\nimport '../styles/TipTapEditor.css';\n\n// ─── Toolbar button ───────────────────────────────────────────────────────────\n\nfunction ToolbarButton({ onClick, active, disabled, title, children }) {\n return (\n <button\n type=\"button\"\n title={title}\n disabled={disabled}\n className={`tte-btn${active ? ' tte-btn--active' : ''}`}\n onMouseDown={(e) => {\n e.preventDefault(); // prevent editor losing focus\n onClick?.();\n }}\n >\n {children}\n </button>\n );\n}\n\n// ─── Toolbar ─────────────────────────────────────────────────────────────────\n\nfunction Toolbar({ editor, disabled }) {\n if (!editor) return null;\n\n const setLink = () => {\n const url = window.prompt('Enter URL');\n if (!url) {\n editor.chain().focus().unsetLink().run();\n return;\n }\n editor.chain().focus().setLink({ href: url }).run();\n };\n\n return (\n <div className={`tte-toolbar${disabled ? ' tte-toolbar--disabled' : ''}`}>\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Bold\"\n disabled={disabled}\n active={editor.isActive('bold')}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <strong>B</strong>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Italic\"\n disabled={disabled}\n active={editor.isActive('italic')}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <em>I</em>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Underline\"\n disabled={disabled}\n active={editor.isActive('underline')}\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n >\n <span style={{ textDecoration: 'underline' }}>U</span>\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Strikethrough\"\n disabled={disabled}\n active={editor.isActive('strike')}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <s>S</s>\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Bullet List\"\n disabled={disabled}\n active={editor.isActive('bulletList')}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n ≡\n </ToolbarButton>\n\n <ToolbarButton\n title=\"Ordered List\"\n disabled={disabled}\n active={editor.isActive('orderedList')}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n 1.\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Link\"\n disabled={disabled}\n active={editor.isActive('link')}\n onClick={setLink}\n >\n 🔗\n </ToolbarButton>\n </div>\n\n <div className=\"tte-toolbar-divider\" />\n\n <div className=\"tte-toolbar-group\">\n <ToolbarButton\n title=\"Clear formatting\"\n disabled={disabled}\n onClick={() => editor.chain().focus().clearNodes().unsetAllMarks().run()}\n >\n ✕\n </ToolbarButton>\n </div>\n </div>\n );\n}\n\n// ─── Main editor ──────────────────────────────────────────────────────────────\n\nexport default function TipTapEditor({\n value = '',\n onChange,\n disabled = false,\n placeholder = '',\n}) {\n // Tracks the last value this editor itself produced (via onUpdate) or was\n // last told to show (via the sync effect below) — NOT the editor's live\n // getHTML(), which can drift from `value` after a round-trip through\n // TipTap's HTML serializer (e.g. plain AI-generated text with no <p> tags\n // never equals its own wrapped-in-<p> serialization, which previously made\n // the old getHTML()-based comparison useless as a \"did this come from us\"\n // check and caused an external update to fight with a stale echo).\n const lastKnownValueRef = useRef(value ?? '');\n\n const editor = useEditor({\n extensions: [\n StarterKit,\n Underline,\n Link.configure({\n openOnClick: false,\n HTMLAttributes: { rel: 'noopener noreferrer' },\n }),\n ],\n content: value, // set initial content correctly on mount\n editable: !disabled,\n editorProps: {\n attributes: {\n class: 'tte-content',\n },\n },\n onUpdate: ({ editor }) => {\n const html = editor.getHTML();\n const next = html === '<p></p>' ? '' : html;\n lastKnownValueRef.current = next;\n onChange?.(next);\n },\n });\n\n // Sync value from outside — handles setFieldsValue from Ant Design form\n // (e.g. an AI Action replacing the content). Skips only when the incoming\n // value is exactly what this editor itself last emitted, so a genuine\n // external update always applies even if it differs from getHTML() purely\n // due to HTML serialization (missing <p> wrapper, entity encoding, etc).\n useEffect(() => {\n if (!editor || editor.isDestroyed) return;\n const nextValue = value || '';\n if (nextValue === (lastKnownValueRef.current || '')) return;\n lastKnownValueRef.current = nextValue;\n editor.commands.setContent(nextValue, false);\n }, [value, editor]);\n\n // Sync disabled state\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(!disabled);\n }, [disabled, editor]);\n\n return (\n <div className={`tte-wrapper${disabled ? ' tte-wrapper--disabled' : ''}`}>\n <Toolbar editor={editor} disabled={disabled} />\n <EditorContent editor={editor} />\n {!value && !editor?.isFocused && placeholder && (\n <div className=\"tte-placeholder\">{placeholder}</div>\n )}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { Modal, Spin, Tooltip, message } from 'antd';\nimport {\n CloseOutlined,\n DownloadOutlined,\n FileUnknownOutlined,\n LeftOutlined,\n RightOutlined,\n ZoomInOutlined,\n ZoomOutOutlined,\n} from '@ant-design/icons';\nimport { Document, Page, pdfjs } from 'react-pdf';\nimport { renderAsync } from 'docx-preview';\nimport DOMPurify from 'dompurify';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\nimport '../styles/DocumentViewer.css';\n\n// pdf.js needs a web worker; Vite resolves this URL at build time so it works\n// in dev and production without copying files into /public.\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n 'pdfjs-dist/build/pdf.worker.min.mjs',\n import.meta.url,\n).toString();\n\nconst IMAGE_EXT = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'avif', 'ico'];\nconst TEXT_EXT = ['txt', 'csv', 'log', 'json', 'md', 'xml'];\nconst VIDEO_EXT = ['mp4', 'webm', 'ogg', 'ogv', 'mov', 'm4v'];\nconst ZOOM_STEP = 0.2;\nconst ZOOM_MIN = 0.4;\nconst ZOOM_MAX = 3;\n\nfunction fileNameFromUrl(url) {\n if (!url) return 'Document';\n const clean = String(url).split('?')[0].split('#')[0];\n return decodeURIComponent(clean.split('/').pop() || clean) || 'Document';\n}\n\nfunction extOf(nameOrUrl) {\n const clean = String(nameOrUrl || '').split('?')[0].split('#')[0];\n const dot = clean.lastIndexOf('.');\n return dot === -1 ? '' : clean.slice(dot + 1).toLowerCase();\n}\n\nconst KNOWN_BUCKETS = ['pdf', 'docx', 'image', 'text', 'video', 'html'];\n\n// bucketFromExt maps a bare extension (no dot) to a renderer bucket, or null.\nfunction bucketFromExt(ext) {\n if (!ext) return null;\n if (ext === 'pdf') return 'pdf';\n if (ext === 'doc' || ext === 'docx') return 'docx';\n if (IMAGE_EXT.includes(ext)) return 'image';\n if (TEXT_EXT.includes(ext)) return 'text';\n if (VIDEO_EXT.includes(ext)) return 'video';\n return null;\n}\n\n// kindOf maps a document to a renderer bucket: pdf | docx | image | text |\n// video | html | unknown.\n//\n// `doc.type` is normalized rather than trusted verbatim, because it arrives\n// in different shapes depending on the caller: an already-correct bucket\n// name (\"image\", inline html), a bare file extension as the backend upload\n// handler stores it (\"jpg\", \"png\", \"pdf\" — see timesheetController.go's\n// `ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(...), \".\"))`), or a\n// MIME type (\"image/jpeg\"). Returning a bare extension straight through only\n// happened to work for \"pdf\"/\"docx\" because those extensions equal their own\n// bucket name — every image extension (\"jpg\", \"png\", …) doesn't equal\n// \"image\", so it silently fell through to the \"no preview available\"\n// fallback. This maps all three shapes through the same extension table.\nfunction kindOf(doc) {\n const rawType = String(doc.type || '').toLowerCase().trim();\n if (KNOWN_BUCKETS.includes(rawType)) return rawType;\n\n const mimeExt = rawType.includes('/') ? rawType.split('/').pop() : rawType;\n const fromType = bucketFromExt(mimeExt);\n if (fromType) return fromType;\n\n const fromName = bucketFromExt(extOf(doc.name || doc.url));\n if (fromName) return fromName;\n\n return 'unknown';\n}\n\n// normalizeDocs accepts either bare URL strings, file objects, or inline\n// document objects ({ name, type: 'text'|'html', content }).\nfunction normalizeDocs(documents) {\n return (documents || [])\n .map((d, i) => {\n if (typeof d === 'string') {\n return { url: d, name: fileNameFromUrl(d), key: String(i) };\n }\n const url = d.url || d.location || d.path || '';\n return {\n url,\n name: d.name || fileNameFromUrl(url),\n type: d.type,\n content: typeof d.content === 'string' ? d.content : '',\n key: String(d.id ?? d._id ?? i),\n };\n })\n .filter((d) => d.url || d.content);\n}\n\n/* ------------------------------- renderers ------------------------------- */\n\nfunction PdfRenderer({ url, scale, onNativeFallback }) {\n const wrapRef = useRef(null);\n const [numPages, setNumPages] = useState(0);\n const [width, setWidth] = useState(0);\n const [error, setError] = useState(false);\n\n useEffect(() => {\n const el = wrapRef.current;\n if (!el) return undefined;\n const update = () => setWidth(el.clientWidth);\n update();\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n // pdf.js streams the file via its own fetch, which fails cross-origin on\n // storage URLs the bucket's CORS policy doesn't allowlist this origin for\n // (common — see the S3 bucket's CORS config, not something fixable here) —\n // distinct from a plain navigation (an <iframe> load), which is NOT subject\n // to CORS at all and works regardless. Fall back to that rather than\n // dead-ending the preview; the native viewer brings its own zoom/controls,\n // so the toolbar hides its (now inert) zoom buttons via onNativeFallback.\n //\n // This intentionally points the iframe at the raw presigned `url`, not a\n // fetched blob — fetching it would hit the same CORS wall react-pdf just\n // did. Whether this renders inline vs. downloads depends entirely on the\n // `Content-Disposition` the presigned URL responds with; that's set\n // server-side (GetPresignedURLInline in Be_Auth_DevOps) rather than worked\n // around here, so every consumer of the URL — this iframe, a plain link,\n // anything — gets the same correct inline behavior.\n if (error) {\n return <iframe title=\"PDF preview\" src={url} className=\"dv-pdf-native\" />;\n }\n\n return (\n <div ref={wrapRef} className=\"dv-pdf-wrap\">\n <Document\n file={url}\n loading={<Spin />}\n error={<FallbackStage label=\"This PDF could not be displayed.\" />}\n onLoadSuccess={({ numPages: n }) => setNumPages(n)}\n onLoadError={() => {\n setError(true);\n onNativeFallback?.();\n }}\n >\n {Array.from({ length: numPages }, (_, i) => (\n <Page\n key={`page-${i + 1}`}\n pageNumber={i + 1}\n width={width ? width * scale : undefined}\n className=\"dv-pdf-page\"\n renderTextLayer\n renderAnnotationLayer\n />\n ))}\n </Document>\n </div>\n );\n}\n\nPdfRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n scale: PropTypes.number.isRequired,\n onNativeFallback: PropTypes.func,\n};\n\nfunction DocxRenderer({ url }) {\n const ref = useRef(null);\n const [status, setStatus] = useState('loading'); // loading | ready | error\n\n useEffect(() => {\n let cancelled = false;\n\n fetch(url)\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return r.blob();\n })\n .then((blob) => {\n if (cancelled || !ref.current) return undefined;\n ref.current.innerHTML = '';\n return renderAsync(blob, ref.current, undefined, {\n className: 'dv-docx',\n inWrapper: true,\n ignoreWidth: false,\n ignoreHeight: false,\n });\n })\n .then(() => {\n if (!cancelled) setStatus('ready');\n })\n .catch(() => {\n if (!cancelled) setStatus('error');\n });\n\n return () => {\n cancelled = true;\n };\n }, [url]);\n\n if (status === 'error') {\n return <FallbackStage label=\"This document could not be displayed.\" />;\n }\n\n return (\n <div className=\"dv-docx-scroll\">\n {status === 'loading' && <div className=\"dv-center\"><Spin /></div>}\n <div ref={ref} style={{ visibility: status === 'ready' ? 'visible' : 'hidden' }} />\n </div>\n );\n}\n\nDocxRenderer.propTypes = { url: PropTypes.string.isRequired };\n\nfunction TextRenderer({ url, content = '' }) {\n const [text, setText] = useState(null);\n const [status, setStatus] = useState('loading');\n\n useEffect(() => {\n if (content) return undefined;\n\n let cancelled = false;\n fetch(url)\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return r.text();\n })\n .then((t) => {\n if (!cancelled) {\n setText(t);\n setStatus('ready');\n }\n })\n .catch(() => !cancelled && setStatus('error'));\n return () => {\n cancelled = true;\n };\n }, [content, url]);\n\n if (content) return <pre className=\"dv-text\">{content}</pre>;\n if (status === 'loading') return <div className=\"dv-center\"><Spin /></div>;\n if (status === 'error') return <FallbackStage label=\"This file could not be displayed.\" />;\n return <pre className=\"dv-text\">{text}</pre>;\n}\n\nTextRenderer.propTypes = {\n url: PropTypes.string,\n content: PropTypes.string,\n};\n\nfunction HtmlRenderer({ content }) {\n const safeHtml = useMemo(() => DOMPurify.sanitize(content), [content]);\n return <div className=\"dv-html\" dangerouslySetInnerHTML={{ __html: safeHtml }} />;\n}\n\nHtmlRenderer.propTypes = { content: PropTypes.string.isRequired };\n\nfunction ImageRenderer({ url, name, scale }) {\n const [error, setError] = useState(false);\n if (error) return <FallbackStage label=\"This image could not be displayed.\" />;\n return (\n <div className=\"dv-image-wrap\">\n <img\n className=\"dv-image\"\n src={url}\n alt={name}\n style={{ transform: `scale(${scale})` }}\n onError={() => setError(true)}\n />\n </div>\n );\n}\n\nImageRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n scale: PropTypes.number.isRequired,\n};\n\nfunction VideoRenderer({ url, name }) {\n const [error, setError] = useState(false);\n if (error) return <FallbackStage label=\"This video could not be played.\" />;\n return (\n <div className=\"dv-video-wrap\">\n <video\n className=\"dv-video\"\n src={url}\n title={name}\n controls\n preload=\"metadata\"\n controlsList=\"nodownload\"\n onError={() => setError(true)}\n />\n </div>\n );\n}\n\nVideoRenderer.propTypes = {\n url: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n};\n\nfunction FallbackStage({ label }) {\n return (\n <div className=\"dv-center dv-fallback\">\n <FileUnknownOutlined className=\"dv-fallback-icon\" />\n <p>{label}</p>\n <span className=\"dv-fallback-hint\">Use the download button to open it.</span>\n </div>\n );\n}\n\nFallbackStage.propTypes = { label: PropTypes.string.isRequired };\n\n/* ----------------------------- main component ---------------------------- */\n\n// ViewerBody holds the per-session state (active index + zoom). It lives inside\n// the Modal body, which is destroyed on close (destroyOnHidden), so it remounts\n// fresh on every open — no manual \"reset on open\" effects required.\nfunction ViewerBody({ docs, initialIndex, onClose }) {\n const total = docs.length;\n const safeInitial = Math.min(Math.max(initialIndex, 0), Math.max(0, total - 1));\n const [index, setIndex] = useState(safeInitial);\n const [scale, setScale] = useState(1);\n // Set when a PDF falls back to the browser's native viewer (see\n // PdfRenderer) — that viewer has its own zoom UI, so ours would sit there\n // doing nothing if left visible.\n const [pdfNativeFallback, setPdfNativeFallback] = useState(false);\n\n const current = docs[index];\n const kind = current ? kindOf(current) : 'unknown';\n const zoomable = (kind === 'pdf' && !pdfNativeFallback) || kind === 'image';\n\n // setActive changes the document and resets zoom/fallback in one event\n // handler, so we never have to reset them from an effect.\n const setActive = useCallback((next) => {\n setIndex(next);\n setScale(1);\n setPdfNativeFallback(false);\n }, []);\n\n const goPrev = useCallback(() => setActive(Math.max(0, index - 1)), [index, setActive]);\n const goNext = useCallback(\n () => setActive(Math.min(total - 1, index + 1)),\n [index, total, setActive],\n );\n\n // Keyboard navigation.\n useEffect(() => {\n const onKey = (e) => {\n // A focused <video> uses arrow keys to seek — don't switch documents.\n if (e.target?.tagName === 'VIDEO') return;\n if (e.key === 'ArrowLeft') goPrev();\n else if (e.key === 'ArrowRight') goNext();\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [goPrev, goNext]);\n\n const download = useCallback(async (doc) => {\n if (!doc) return;\n try {\n let blob;\n if (doc.content) {\n const mimeType = doc.type === 'html' ? 'text/html;charset=utf-8' : 'text/plain;charset=utf-8';\n blob = new Blob([doc.content], { type: mimeType });\n } else {\n const res = await fetch(doc.url);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n blob = await res.blob();\n }\n const objUrl = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = objUrl;\n a.download = doc.name || 'document';\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(objUrl);\n } catch {\n // Cross-origin without CORS: fall back to opening in a new tab.\n message.info('Opening file in a new tab…');\n if (doc.url) window.open(doc.url, '_blank', 'noopener,noreferrer');\n }\n }, []);\n\n function renderStage() {\n if (!current) {\n return <FallbackStage label=\"No document to preview.\" />;\n }\n switch (kind) {\n case 'pdf':\n return (\n <PdfRenderer\n key={current.key}\n url={current.url}\n scale={scale}\n onNativeFallback={() => setPdfNativeFallback(true)}\n />\n );\n case 'docx':\n return <DocxRenderer key={current.key} url={current.url} />;\n case 'image':\n return (\n <ImageRenderer key={current.key} url={current.url} name={current.name} scale={scale} />\n );\n case 'text':\n return <TextRenderer key={current.key} url={current.url} content={current.content} />;\n case 'html':\n return <HtmlRenderer key={current.key} content={current.content} />;\n case 'video':\n return <VideoRenderer key={current.key} url={current.url} name={current.name} />;\n default:\n return <FallbackStage label=\"Preview is not available for this file type.\" />;\n }\n }\n\n return (\n <div className=\"dv-root\">\n {/* toolbar */}\n <div className=\"dv-toolbar\">\n <div className=\"dv-title\" title={current?.name}>\n <span className=\"dv-title-text\">{current?.name || 'Document'}</span>\n {total > 1 && <span className=\"dv-counter\">{index + 1} / {total}</span>}\n </div>\n <div className=\"dv-actions\">\n {zoomable && (\n <>\n <Tooltip title=\"Zoom out\">\n <button\n type=\"button\"\n className=\"dv-icon-btn\"\n onClick={() => setScale((s) => Math.max(ZOOM_MIN, +(s - ZOOM_STEP).toFixed(2)))}\n disabled={scale <= ZOOM_MIN}\n >\n <ZoomOutOutlined />\n </button>\n </Tooltip>\n <span className=\"dv-zoom-label\">{Math.round(scale * 100)}%</span>\n <Tooltip title=\"Zoom in\">\n <button\n type=\"button\"\n className=\"dv-icon-btn\"\n onClick={() => setScale((s) => Math.min(ZOOM_MAX, +(s + ZOOM_STEP).toFixed(2)))}\n disabled={scale >= ZOOM_MAX}\n >\n <ZoomInOutlined />\n </button>\n </Tooltip>\n <span className=\"dv-divider\" />\n </>\n )}\n <Tooltip title=\"Download\">\n <button type=\"button\" className=\"dv-icon-btn dv-download\" onClick={() => download(current)}>\n <DownloadOutlined />\n </button>\n </Tooltip>\n <Tooltip title=\"Close\">\n <button type=\"button\" className=\"dv-icon-btn\" onClick={onClose}>\n <CloseOutlined />\n </button>\n </Tooltip>\n </div>\n </div>\n\n {/* stage + slider arrows */}\n <div className=\"dv-stage\">\n {total > 1 && (\n <button\n type=\"button\"\n className=\"dv-nav dv-nav--prev\"\n onClick={goPrev}\n disabled={index === 0}\n aria-label=\"Previous document\"\n >\n <LeftOutlined />\n </button>\n )}\n\n <div className=\"dv-canvas\">{renderStage()}</div>\n\n {total > 1 && (\n <button\n type=\"button\"\n className=\"dv-nav dv-nav--next\"\n onClick={goNext}\n disabled={index === total - 1}\n aria-label=\"Next document\"\n >\n <RightOutlined />\n </button>\n )}\n </div>\n\n {/* dots */}\n {total > 1 && (\n <div className=\"dv-dots\">\n {docs.map((d, i) => (\n <button\n key={d.key}\n type=\"button\"\n className={`dv-dot${i === index ? ' dv-dot--active' : ''}`}\n onClick={() => setActive(i)}\n aria-label={`Go to document ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nViewerBody.propTypes = {\n docs: PropTypes.arrayOf(PropTypes.object).isRequired,\n initialIndex: PropTypes.number.isRequired,\n onClose: PropTypes.func.isRequired,\n};\n\n// DocumentViewer is the public component: a Modal shell that mounts ViewerBody\n// only while open. `documents` may be URL strings or document objects.\n//\n// Pass `inline` to render ViewerBody directly with no Modal — used by the timesheet\n// manager review, which shows the uploaded screenshot side-by-side with the calendar\n// rather than in a popup. Everything ViewerBody already does (pdf/image/docx render,\n// zoom, download, multi-document navigation) comes along for free.\nexport default function DocumentViewer({ documents, open, onClose, initialIndex = 0, inline = false }) {\n const docs = useMemo(() => normalizeDocs(documents), [documents]);\n\n if (inline) {\n return (\n <div className=\"dv-root--inline\">\n <ViewerBody docs={docs} initialIndex={initialIndex} onClose={onClose ?? (() => {})} />\n </div>\n );\n }\n\n return (\n <Modal\n open={open}\n onCancel={onClose}\n footer={null}\n title={null}\n closable={false}\n centered\n width=\"min(1100px, 94vw)\"\n className=\"dv-modal\"\n styles={{ content: { padding: 0, overflow: 'hidden', borderRadius: 14 }, body: { padding: 0 } }}\n destroyOnHidden\n >\n <ViewerBody docs={docs} initialIndex={initialIndex} onClose={onClose} />\n </Modal>\n );\n}\n\nDocumentViewer.propTypes = {\n // URL strings, file objects, or inline objects ({ content, type: 'text'|'html' }).\n documents: PropTypes.arrayOf(\n PropTypes.oneOfType([PropTypes.string, PropTypes.object]),\n ).isRequired,\n // Required for the Modal shell; unused (and optional) when `inline` is set.\n open: PropTypes.bool,\n onClose: PropTypes.func,\n initialIndex: PropTypes.number,\n // Render the viewer body directly, with no Modal wrapper.\n inline: PropTypes.bool,\n};\n","// Dot-notation module names: \"job.client.company\" means \"fetch the `job`\n// module, then display the data found at `client.company` inside each record\".\n// Only the segment before the first dot is a real backend module — every API\n// call (module-data-list, field-config, permissions) must use it, while the\n// remaining segments are resolved client-side against each fetched record.\n\nexport function parseModulePath(moduleName) {\n const raw = String(moduleName ?? '').trim();\n if (!raw) return { baseModule: '', nestedPath: '' };\n\n const [baseModule, ...rest] = raw.split('.');\n return {\n baseModule: baseModule.trim(),\n nestedPath: rest.map((part) => part.trim()).filter(Boolean).join('.'),\n };\n}\n\nexport function getBaseModuleName(moduleName) {\n return parseModulePath(moduleName).baseModule;\n}\n\nexport function getValueAtPath(source, path) {\n return String(path ?? '')\n .split('.')\n .filter(Boolean)\n .reduce((value, key) => (value == null ? undefined : value[key]), source);\n}\n","import { fetchJsonWithAuth, apiGetWithAuth } from './authApi';\nimport { AUTH_URL, SUBMISSIONS_URL } from './apiConfig';\nimport { normalizeDiceSkills, resolveDiceProfileId } from './diceCandidateMapper';\nimport { getBaseModuleName } from '../utils/modulePath';\n\nconst TEST_FLOW_MODULE = 'test-flows';\nconst LMS_FLOW_MODULE = 'lms-flows';\n\nconst TEST_FLOW_FIELDS = [\n { value: 'module', label: 'Module', isVisible: true, type: 'text' },\n { value: 'flow', label: 'Flow', isVisible: true, type: 'text' },\n { value: 'step_order', label: 'Step Order', isVisible: true, type: 'number' },\n { value: 'keyword', label: 'Action', isVisible: true, type: 'text' },\n { value: 'description', label: 'Description', isVisible: true, type: 'text' },\n { value: 'target', label: 'Key', isVisible: true, type: 'text' },\n { value: 'tags', label: 'Tags', isVisible: true, type: 'text' },\n { value: 'value', label: 'Value', isVisible: true, type: 'text' },\n { value: 'expected', label: 'Expected', isVisible: true, type: 'text' },\n];\n\nconst LMS_FLOW_FIELDS = [\n { value: 'module', label: 'Module', isVisible: true, type: 'text' },\n { value: 'flow', label: 'Flow', isVisible: true, type: 'text' },\n { value: 'step_order', label: 'Step Order', isVisible: true, type: 'number' },\n { value: 'keyword', label: 'Action', isVisible: true, type: 'text' },\n { value: 'description', label: 'Description', isVisible: true, type: 'text' },\n { value: 'target', label: 'Key', isVisible: true, type: 'text' },\n { value: 'tags', label: 'Tags', isVisible: true, type: 'text' },\n { value: 'value', label: 'Value', isVisible: true, type: 'text' },\n { value: 'expected', label: 'Expected', isVisible: true, type: 'text' },\n];\n\nfunction normalizeCandidateSearchSource(value) {\n const values = (Array.isArray(value) ? value : [value])\n .flat()\n .map((item) => {\n if (item && typeof item === 'object') {\n return item.value ?? item.label ?? item.name ?? '';\n }\n return item;\n })\n .map((item) => String(item ?? '').trim().toLowerCase())\n .filter(Boolean);\n\n if (values.includes('dice')) return 'dice';\n if (values.includes('internal')) return 'internal';\n return values[0] || '';\n}\n\nfunction firstFiniteNumber(...values) {\n for (const value of values) {\n const number = Number(value);\n if (Number.isFinite(number)) return number;\n }\n\n return undefined;\n}\n\nfunction extractResponseTotal(response, payload, rows, scope = '') {\n const responseCount = response?.count;\n const payloadCount = payload?.count;\n const normalizedScope = String(scope ?? '').trim().toLowerCase();\n\n if (responseCount && typeof responseCount === 'object') {\n const total = firstFiniteNumber(\n normalizedScope ? responseCount[normalizedScope] : undefined,\n responseCount.total,\n responseCount.searchCount,\n responseCount.count,\n );\n if (total !== undefined) return total;\n }\n\n if (payloadCount && typeof payloadCount === 'object') {\n const total = firstFiniteNumber(\n normalizedScope ? payloadCount[normalizedScope] : undefined,\n payloadCount.total,\n payloadCount.searchCount,\n payloadCount.count,\n );\n if (total !== undefined) return total;\n }\n\n return firstFiniteNumber(\n response?.total,\n typeof responseCount !== 'object' ? responseCount : undefined,\n response?.totalCount,\n payload?.total,\n typeof payloadCount !== 'object' ? payloadCount : undefined,\n payload?.totalCount,\n rows.length,\n ) ?? 0;\n}\n\nfunction extractResponseCounts(response, payload) {\n const counts = {};\n [response?.count, payload?.count].forEach((count) => {\n if (!count || typeof count !== 'object') return;\n\n Object.entries(count).forEach(([key, value]) => {\n const number = Number(value);\n if (Number.isFinite(number)) counts[String(key).trim().toLowerCase()] = number;\n });\n });\n\n return counts;\n}\n\nexport async function getDropdownFields(module) {\n if (module === TEST_FLOW_MODULE) return TEST_FLOW_FIELDS;\n if (module === LMS_FLOW_MODULE) return LMS_FLOW_FIELDS;\n // Dot-notation names (\"job.client\") target nested data client-side; the\n // backend only knows the base module.\n const baseModule = getBaseModuleName(module);\n return apiGetWithAuth(SUBMISSIONS_URL, `/filter-dropdown-fields?module=${encodeURIComponent(baseModule)}`);\n}\n\nexport async function getFieldConfig(module) {\n if (module === TEST_FLOW_MODULE) return TEST_FLOW_FIELDS;\n if (module === LMS_FLOW_MODULE) return LMS_FLOW_FIELDS;\n return apiGetWithAuth(AUTH_URL, `/admin/field-config?module=${encodeURIComponent(getBaseModuleName(module))}`);\n}\n\nexport async function getDropdownValues(module, field, search = '', limit = 50, offset = 0, meta = {}) {\n const params = new URLSearchParams({\n module: getBaseModuleName(module), field, value: search,\n limit: String(limit), offset: String(offset),\n });\n if (meta.dataSource) params.set('dataSource', meta.dataSource);\n if (meta.masterName) params.set('masterName', meta.masterName);\n if (meta.groupName) params.set('groupName', meta.groupName);\n // The Auth gateway owns the admin form configuration and understands\n // dataSource/masterName. Keeping this generic lets every configured master,\n // module and lookup field work without module-specific UI code.\n return apiGetWithAuth(AUTH_URL, `/filter-dropdown-values?${params}`);\n}\n\nexport async function getModuleDataList(module, limit = 10, offset = 0, options = {}) {\n const { scope = '', sort = '', sortDir = '', filters = [] } = options;\n\n if (module === TEST_FLOW_MODULE) {\n const { getTestFlows } = await import('./testingApi');\n const data = await getTestFlows({ limit, offset });\n const rows = Array.isArray(data?.items) ? data.items : [];\n const total = Number(data?.total) || 0;\n return { items: rows, total, limit, offset, tabs: [{ key: 'all', title: 'Test Cases', count: total }] };\n }\n\n if (module === LMS_FLOW_MODULE) {\n const { getLmsFlows } = await import('./testingApi');\n const data = await getLmsFlows({ limit, offset });\n const rows = Array.isArray(data?.items) ? data.items : [];\n const total = Number(data?.total) || 0;\n return { items: rows, total, limit, offset, tabs: [{ key: 'all', title: 'LMS Cases', count: total }] };\n }\n\n // const params = new URLSearchParams({ module, limit: String(limit), offset: String(offset) });\n // if (scope) params.set('scope', scope);\n const params = new URLSearchParams({\n module: getBaseModuleName(module), limit: String(limit), offset: String(offset),\n });\n const normalizedScope = String(scope ?? '').trim();\n if (normalizedScope) params.set('scope', normalizedScope);\n if (sort) params.set('sort', sort);\n if (sortDir) params.set('sortDir', sortDir);\n if (Array.isArray(filters) && filters.some((item) => item?.field)) {\n params.set('filters', JSON.stringify(filters));\n }\n\n return fetchJsonWithAuth(AUTH_URL, `/module-data-list?${params.toString()}`);\n}\n\nexport async function searchModuleData(moduleName, searchParams = {}, pagination = {}, options = {}) {\n const { limit = 10, offset = 0 } = pagination;\n const { sort = '', sortDir = '', scope = '' } = options;\n const inferredScope = normalizeCandidateSearchSource(scope || searchParams.selectedSource);\n\n const query = new URLSearchParams();\n query.append('module', getBaseModuleName(moduleName));\n query.append('limit', String(limit));\n query.append('offset', String(offset));\n\n if (inferredScope) {\n query.set('scope', inferredScope);\n console.log('[searchModuleData] Adding scope to query:', inferredScope);\n }\n if (sort) query.set('sort', sort);\n if (sortDir) query.set('sortDir', sortDir);\n\n console.log('[searchModuleData] Final query string:', query.toString());\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null || value === '') return;\n\n let normalizedValue = value;\n if (typeof value === 'object') {\n normalizedValue = value.value ?? value.label ?? value.name ?? String(value);\n } else {\n normalizedValue = String(value);\n }\n\n query.append(key, normalizedValue);\n });\n\n const response = await fetchJsonWithAuth(AUTH_URL, `/module-data-list?${query.toString()}`);\n // const response = await fetchJsonWithAuth(\"http://localhost:9009/v1\", `/module-data-list?${query.toString()}`);\n // response = { status, data: { actionRules, actions, columnActions, data: [...records] } }\n const payload = response?.data ?? response;\n const rows = Array.isArray(payload)\n ? payload\n : payload?.rows ?? payload?.records ?? payload?.items ?? payload?.list ?? payload?.data ?? [];\n\n const total = extractResponseTotal(response, payload, rows, inferredScope);\n const counts = extractResponseCounts(response, payload);\n if (inferredScope && counts[inferredScope] === undefined && Number.isFinite(total)) {\n counts[inferredScope] = total;\n }\n\n // Check if response contains Dice candidates and transform if needed\n const hasDiceCandidates = rows.some((record) => {\n const sourceFields = [\n record?.sourceType,\n record?.profileSource,\n record?.selectedSource,\n record?.customFields?.sourceType,\n record?.customFields?.profileSource,\n ].flat();\n return sourceFields.some(\n (value) => String(value ?? '').trim().toLowerCase() === 'dice',\n ) || Boolean(record?.customFields?.diceProfileData);\n });\n\n let normalizedRows = rows;\n if (hasDiceCandidates) {\n try {\n const { transformDiceResponseToInternalFormat } = await import('./diceCandidateMapper');\n const transformed = transformDiceResponseToInternalFormat(response, response);\n normalizedRows = transformed?.data?.data ?? rows;\n } catch (error) {\n console.error('[searchModuleData] Dice transformation failed:', error);\n normalizedRows = rows.map(normalizeCandidateSearchRow);\n }\n } else {\n normalizedRows = Array.isArray(rows) ? rows.map(normalizeCandidateSearchRow) : [];\n }\n\n return {\n rows: normalizedRows,\n total: Number(total) || 0,\n counts,\n tabs: response?.tabs ?? response?.tabList ?? payload?.tabs ?? payload?.tabList ?? [],\n fields: response?.fields ?? response?.fieldConfig ?? payload?.fields ?? payload?.fieldConfig ?? [],\n tabField: response?.tabField ?? payload?.tabField ?? '',\n actionRules: payload?.actionRules ?? [],\n actions: payload?.actions ?? [],\n columnActions: payload?.columnActions ?? [],\n };\n}\n\nfunction getNestedValue(record, path) {\n return String(path)\n .split('.')\n .reduce((value, key) => value?.[key], record);\n}\n\nfunction setNestedValue(record, path, value) {\n const keys = String(path).split('.').filter(Boolean);\n if (!keys.length) return record;\n\n const nextRecord = { ...record };\n let target = nextRecord;\n let source = record;\n\n keys.slice(0, -1).forEach((key) => {\n const currentValue = source?.[key];\n const nextValue = currentValue && typeof currentValue === 'object' && !Array.isArray(currentValue)\n ? { ...currentValue }\n : {};\n\n target[key] = nextValue;\n target = nextValue;\n source = currentValue;\n });\n\n target[keys[keys.length - 1]] = value;\n return nextRecord;\n}\n\nfunction normalizeCandidateSearchRow(row) {\n if (!row || typeof row !== 'object') return row;\n\n const skillPaths = [\n 'skills',\n 'technicalSkills',\n 'primarySkills',\n 'keySkills',\n 'customFields.diceProfileData.skills',\n ];\n\n let nextRow = row;\n let firstNormalizedSkills = null;\n\n skillPaths.forEach((path) => {\n const rawSkills = getNestedValue(nextRow, path);\n if (rawSkills === undefined || rawSkills === null) return;\n\n const mappedSkills = normalizeDiceSkills(rawSkills);\n if (mappedSkills.length === 0 && Array.isArray(rawSkills) && rawSkills.length > 0) return;\n\n firstNormalizedSkills = firstNormalizedSkills ?? mappedSkills;\n nextRow = setNestedValue(nextRow, path, mappedSkills);\n });\n\n if (firstNormalizedSkills && getNestedValue(nextRow, 'skills') === undefined) {\n nextRow = setNestedValue(nextRow, 'skills', firstNormalizedSkills);\n }\n\n const sourceFields = [\n nextRow?.sourceType,\n nextRow?.profileSource,\n nextRow?.selectedSource,\n nextRow?.customFields?.sourceType,\n nextRow?.customFields?.profileSource,\n ].flat();\n const isDiceCandidate = sourceFields.some(\n (value) => String(value ?? '').trim().toLowerCase() === 'dice',\n ) || Boolean(nextRow?.customFields?.diceProfileData);\n\n if (isDiceCandidate) {\n const diceProfileId = resolveDiceProfileId(nextRow);\n const candidateId = nextRow?.candidateId\n ?? nextRow?.customFields?.diceProfileData?.candidateId\n ?? '';\n\n nextRow = {\n ...nextRow,\n id: nextRow?.id ?? diceProfileId,\n diceId: diceProfileId,\n candidateId,\n };\n }\n\n return nextRow;\n}\n","import { ensureToken, fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL, JOBS_URL } from './apiConfig';\n\n// ── Form Groups ───────────────────────────────────────────────────────────────\n\nfunction extractFormGroups(json) {\n const payload = json?.data ?? json;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(payload?.groups)) return payload.groups;\n if (Array.isArray(json?.groups)) return json.groups;\n return [];\n}\n\nexport async function getFormGroups({ module, id, action, clientId, region, group } = {}) {\n if (!module) throw new Error('module is required to fetch form groups');\n const token = await ensureToken();\n const params = new URLSearchParams({ module });\n if (id) params.set('id', id);\n if (action) params.set('action', action);\n if (clientId) params.set('clientId', clientId);\n if (region) params.set('region', region);\n if (group) params.set('group', group);\n const res = await fetch(`${AUTH_URL}/admin/form-groups?${params.toString()}`, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);\n const json = await res.json();\n return extractFormGroups(json);\n}\n\nexport async function getCandidateFormGroups(options = {}) {\n return getFormGroups({ module: 'candidates', ...options });\n}\n\nexport async function getMentionUsers(search = '') {\n const { apiGetWithAuth } = await import('./authApi');\n const params = search ? `?search=${encodeURIComponent(search)}` : '';\n return apiGetWithAuth(JOBS_URL, `/users/any${params}`);\n}\n\n// ── Create Records ────────────────────────────────────────────────────────────\n// Routes through the Auth gateway which injects tenant context from JWT\n// and proxies to the correct downstream service based on module name.\n\nexport async function createModuleRecord(moduleName, formData) {\n // ensureToken (not getStoredToken) so a missing/expired session throws a\n // clear \"Authentication required\" error instead of silently sending the\n // request with no Authorization header (which the gateway rejects as 401).\n // NOTE: do NOT set Content-Type here — the browser must add the multipart\n // boundary itself. Only the Authorization header is set manually.\n const token = await ensureToken();\n\n const res = await fetch(\n `${AUTH_URL}/module/create?module=${encodeURIComponent(moduleName)}`,\n { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData },\n );\n\n const { logout } = await import('./authApi');\n if (res.status === 401) logout();\n\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('application/json') ? await res.json() : await res.text();\n\n if (!res.ok) {\n const error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\n error.status = res.status;\n error.data = data;\n throw error;\n }\n\n return data;\n}\n","import { 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 ?? null,\n valueStyles: Array.isArray(field.valueStyles) ? field.valueStyles : [],\n commonStyle: field.commonStyle ?? null,\n displayTemplate: field.displayTemplate ?? '',\n dataSource: field.dataSource ?? 'module',\n groupName: field.groupName ?? '',\n };\n}\n\nfunction normalizeCustomizableModule(module) {\n if (typeof module === 'string') {\n return { key: module, apiModule: module };\n }\n\n const key = firstValue(\n module,\n ['key', 'moduleKey', 'value', 'module', 'apiModule', 'name', 'modulename', 'menuName'],\n ''\n );\n if (!key) return null;\n\n return {\n key,\n apiModule: firstValue(\n module,\n ['apiModule', 'api_module', 'module', 'value', 'key', 'name', 'modulename', 'menuName'],\n key\n ),\n };\n}\n\nfunction normalizeCustomizableModules(modules = CUSTOMIZABLE_MODULES) {\n const source = Array.isArray(modules) && modules.length ? modules : CUSTOMIZABLE_MODULES;\n const seen = new Set();\n return source\n .map(normalizeCustomizableModule)\n .filter(Boolean)\n .filter((module) => {\n if (seen.has(module.key)) return false;\n seen.add(module.key);\n return true;\n });\n}\n\n// ── Modules ───────────────────────────────────────────────────────────────────\n\nconst MODULES_PATH = '/admin/modules';\n\nexport async function getModules() {\n const json = await fetchJsonWithAuth(AUTH_URL, MODULES_PATH);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\n// createModule/updateModule let Admin define workflow modules (e.g. \"Source\n// Candidates\") that extend an existing module via baseModule, inheriting its\n// forms/columns/tabs/filters/actions wherever the new module hasn't\n// configured its own — see the backend's ModuleLookupChain.\nexport async function createModule({ key, label, collectionName, baseModule = '', order = 0 }) {\n return fetchJsonWithAuth(AUTH_URL, MODULES_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ key, label, collectionName, baseModule, order }),\n });\n}\n\nexport async function updateModule(key, { label, collectionName, baseModule = '', order = 0, isActive = true }) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULES_PATH}/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ label, collectionName, baseModule, order, isActive }),\n });\n}\n\n// getModuleAutomations/saveModuleAutomations edit a module's Gateway +\n// Strategies — the Admin \"Automations\" screen. This is the config-driven\n// replacement for logic legacy services used to hardcode in Go (reference-id\n// generation, duplicate checks, cross-module field snapshots, reporting-chain\n// hierarchy expansion, board updates, notifications, cache invalidation).\n// Separate endpoint from updateModule() above, so saving automations can\n// never touch label/forms/columns, and vice versa.\nexport async function getModuleAutomations(key) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${MODULES_PATH}/${encodeURIComponent(key)}/automations`);\n const data = json?.data ?? json ?? {};\n return { gateway: data.gateway ?? null, strategies: data.strategies ?? null };\n}\n\n// Platform metadata (workflows / resolvers / state-machines / applications) —\n// the event-driven behavior layer. kind ∈ 'workflows' | 'resolvers' |\n// 'state-machines' | 'applications'. Docs are keyed by their \"key\" field.\nexport async function listPlatformDocs(kind) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/platform/${kind}`);\n const data = json?.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\nexport async function savePlatformDoc(kind, doc) {\n return fetchJsonWithAuth(AUTH_URL, `/admin/platform/${kind}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(doc),\n });\n}\n\nexport async function deletePlatformDoc(kind, key) {\n return fetchJsonWithAuth(AUTH_URL, `/admin/platform/${kind}/${encodeURIComponent(key)}`, {\n method: 'DELETE',\n });\n}\n\nexport async function saveModuleAutomations(key, { gateway, strategies }) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULES_PATH}/${encodeURIComponent(key)}/automations`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ gateway, strategies }),\n });\n}\n\n// getAvailableModules() is the single source of truth for \"which modules can\n// be picked in an Admin configuration screen\" (Form Groups, Detail Groups,\n// Row Actions, Module Action Rules, Templates, Role Configuration). It's\n// driven by the curated Menus list (an admin explicitly adds/removes/disables\n// entries there) rather than the `modules` collection, which is really the\n// CRUD/gateway registry and accumulates every module ever wired for storage\n// (including disabled/legacy ones no admin actively curates).\n//\n// Falls back to getModules() when Menus has nothing configured yet, so a\n// project that hasn't set up Menus keeps working exactly as before.\nexport async function getAvailableModules() {\n let menus = [];\n try {\n menus = await getMenuModules();\n } catch {\n menus = [];\n }\n const active = (Array.isArray(menus) ? menus : []).filter((m) => (\n m?.isDeleted !== true\n && String(m?.status ?? 'active').toLowerCase() !== 'inactive'\n && String(m?.status ?? 'active').toLowerCase() !== 'disabled'\n ));\n if (active.length > 0) return active;\n return getModules();\n}\n\n// getModuleCollectionMap resolves module key → the Mongo collection that module\n// is stored in, straight from the module registry (`modules`, the CRUD/gateway\n// registry that owns `collectionName`). It exists so an Admin screen can DISPLAY\n// the collection a configuration will act on without ever asking the admin to\n// type a collection name — the collection is module configuration, and the\n// server resolves it authoritatively on every request regardless of what this\n// map says. If the registry is unreachable the map is simply empty and callers\n// fall back to \"resolved on the server\".\n//\n// Module names are matched loosely on purpose: config is stored sometimes under\n// the singular key and sometimes the plural one (the backend's own\n// ModuleAliasVariants exists for the same reason), so both spellings are\n// indexed here. Real registry entries always win over a generated alias, so an\n// alias can never shadow a module that genuinely exists.\nexport async function getModuleCollectionMap() {\n let modules;\n try {\n modules = await getModules();\n } catch {\n return {};\n }\n const map = {};\n const put = (key, collection, exact) => {\n const k = String(key ?? '').trim().toLowerCase();\n if (!k || !collection) return;\n if (exact || map[k] === undefined) map[k] = collection;\n };\n for (const m of Array.isArray(modules) ? modules : []) {\n const collection = String(m?.collectionName ?? '').trim();\n if (!collection) continue;\n put(m?.key, collection, true);\n }\n // Second pass so aliases never overwrite a registered key from pass one.\n for (const m of Array.isArray(modules) ? modules : []) {\n const collection = String(m?.collectionName ?? '').trim();\n const key = String(m?.key ?? '').trim().toLowerCase();\n if (!collection || !key) continue;\n if (key.endsWith('s')) put(key.slice(0, -1), collection, false);\n else put(`${key}s`, collection, false);\n }\n return map;\n}\n\n// ── Admin Form Groups ─────────────────────────────────────────────────────────\n\nconst FORM_GROUPS_PATH = '/admin/form-groups';\nconst CONFIG_DEFAULTS_PATH = '/admin/config-defaults';\n\nconst NIL_OBJECT_ID = '000000000000000000000000';\n\n/** True for an absent id or Go's zero-value ObjectID (24 zeros). */\nfunction isNilObjectId(value) {\n const id = String(value ?? '').trim();\n return id === '' || id === NIL_OBJECT_ID;\n}\n\nfunction formGroupScopeQuery(scope = {}) {\n const params = new URLSearchParams();\n if (scope.module) params.set('module', scope.module);\n if (scope.clientId) params.set('clientId', scope.clientId);\n if (scope.region) params.set('region', scope.region);\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n}\n\nexport async function getAdminFormGroups(module = '', scope = {}) {\n const query = formGroupScopeQuery({ ...scope, module: module || scope.module || '' });\n const path = `${FORM_GROUPS_PATH}${query}`;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const payload = json?.data;\n // Backend returns EITHER a flat array of groups, OR a wrapper object\n // { id, module, ..., groups: [...] }. Support both shapes.\n if (Array.isArray(payload)) return payload;\n if (payload && Array.isArray(payload.groups)) return payload.groups;\n if (Array.isArray(json)) return json;\n if (json && Array.isArray(json.groups)) return json.groups;\n return [];\n}\n\nexport async function getAdminClients({ offset = 0, limit = 100, sortBy = 'new', searchvalue = '' } = {}) {\n const params = new URLSearchParams({\n offset: String(offset),\n limit: String(limit),\n sortBy,\n searchvalue,\n });\n // Scope the list to the active tenant/business/businessUnit captured from the\n // login token (authApi stores these on login). The backend uses them when\n // present, otherwise falls back to the token claims — so the dropdown lists\n // the right clients for the selected project, same as the legacy flow.\n const tenantId = localStorage.getItem('tenantId') || '';\n const businessId = localStorage.getItem('businessId') || '';\n const businessUnitId = localStorage.getItem('businessUnitId') || '';\n if (tenantId) params.set('tenantId', tenantId);\n if (businessId) params.set('businessId', businessId);\n if (businessUnitId) params.set('businessUnitId', businessUnitId);\n const json = await fetchJsonWithAuth(AUTH_URL, `/client/view?${params.toString()}`);\n const data = json?.data ?? json;\n return data?.clients ?? data?.Clients ?? data?.items ?? [];\n}\n\nexport async function getModuleFields(module) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/filter-dropdown-fields?module=${encodeURIComponent(module)}`);\n const fields = json?.data ?? json;\n return Array.isArray(fields) ? fields : [];\n}\n\nexport async function createAdminFormGroup(group, scope = {}) {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}${formGroupScopeQuery(scope)}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(group),\n });\n}\n\nexport async function updateAdminFormGroup(id, group, scope = {}) {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/${id}${formGroupScopeQuery(scope)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(group),\n });\n}\n\n// Scope-aware, like create/update: with a client selected the group is removed\n// from THAT client's list only. A client-scoped config is a fork of the module\n// default and keeps the same group ids, so an unscoped delete would clear the\n// group from the default and every other client at once.\nexport async function deleteAdminFormGroup(id, scope = {}) {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/${id}${formGroupScopeQuery(scope)}`, { method: 'DELETE' });\n}\n\nexport async function seedAdminFormGroups() {\n return fetchJsonWithAuth(AUTH_URL, `${FORM_GROUPS_PATH}/seed`, { method: 'POST' });\n}\n\nexport async function requestConfigOtp(scope, action) {\n return fetchJsonWithAuth(AUTH_URL, `${CONFIG_DEFAULTS_PATH}/request-otp`, {\n method: 'POST',\n body: JSON.stringify({ scope, action }),\n });\n}\n\nexport async function applyConfigDefault(scope, action, otp) {\n return fetchJsonWithAuth(AUTH_URL, `${CONFIG_DEFAULTS_PATH}/apply`, {\n method: 'POST',\n body: JSON.stringify({ scope, action, otp }),\n });\n}\n\n// ── Admin Detail-View Config ──────────────────────────────────────────────────\n// Per-module customization layered on top of the form groups, applied only by\n// the detail view (?view=detail). Shape: { module, fields:[…], merges:[…] }.\n\nconst DETAIL_CONFIG_PATH = '/admin/detail-config';\n\n// Client scope mirrors the form-group admin exactly: pass a clientId to read /\n// write THAT client's detail config, omit it for the module default. The backend\n// falls back to the default when the client has none of its own, so a client\n// with no overrides still renders.\nexport async function getDetailConfig(module, scope = {}) {\n const query = formGroupScopeQuery({ module, clientId: scope.clientId });\n const json = await fetchJsonWithAuth(AUTH_URL, `${DETAIL_CONFIG_PATH}${query}`);\n const data = json?.data ?? json ?? {};\n return {\n module: data.module ?? module,\n // Which scope the response belongs to — '' means the module default. Go\n // serialises an unset ObjectID as 24 zeros, which is NOT a client.\n clientId: isNilObjectId(data.clientId) ? '' : String(data.clientId),\n fields: Array.isArray(data.fields) ? data.fields : [],\n merges: Array.isArray(data.merges) ? data.merges : [],\n };\n}\n\nexport async function saveDetailConfig({ module, fields = [], merges = [], clientId = '' }) {\n return fetchJsonWithAuth(AUTH_URL, DETAIL_CONFIG_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n // clientId travels in the BODY (this is a JSON POST); the backend treats an\n // absent/zero id as \"the module default\".\n body: JSON.stringify({ module, fields, merges, ...(clientId ? { clientId } : {}) }),\n });\n}\n\n// ── Detail-View Display Defaults ──────────────────────────────────────────────\n// Tenant-wide display settings (S3 base, date format, boolean/empty labels,\n// separator, document name order) — the configurable home of what used to be\n// hard-coded in fileUtils.js / FieldValue.jsx.\n\nconst DETAIL_DEFAULTS_PATH = '/admin/detail-defaults';\n\nexport async function getDetailDefaults() {\n const json = await fetchJsonWithAuth(AUTH_URL, DETAIL_DEFAULTS_PATH);\n return json?.data ?? json ?? {};\n}\n\nexport async function saveDetailDefaults(defaults) {\n return fetchJsonWithAuth(AUTH_URL, DETAIL_DEFAULTS_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(defaults),\n });\n}\n\n// ── AI Service registry (backends AI Actions can call) ─────────────────────\n\nconst AI_SERVICE_CONFIG_PATH = '/admin/ai-service-config';\n\nexport async function getAiServiceConfigs() {\n const json = await fetchJsonWithAuth(AUTH_URL, AI_SERVICE_CONFIG_PATH);\n return json?.data ?? json ?? [];\n}\n\nexport async function saveAiServiceConfig(config) {\n return fetchJsonWithAuth(AUTH_URL, AI_SERVICE_CONFIG_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n}\n\nexport async function deleteAiServiceConfig(serviceKey) {\n return fetchJsonWithAuth(AUTH_URL, `${AI_SERVICE_CONFIG_PATH}?serviceKey=${encodeURIComponent(serviceKey)}`, {\n method: 'DELETE',\n });\n}\n\n// ── Background Check integration ───────────────────────────────────────────\n\nconst BGC_INTEGRATION_CONFIG_PATH = '/admin/bgc-integration-config';\n\nexport async function getBgcIntegrationConfig() {\n const json = await fetchJsonWithAuth(AUTH_URL, BGC_INTEGRATION_CONFIG_PATH);\n return json?.data ?? json ?? { updateMethod: 'MANUAL', enabled: true };\n}\n\nexport async function saveBgcIntegrationConfig(config) {\n return fetchJsonWithAuth(AUTH_URL, BGC_INTEGRATION_CONFIG_PATH, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n}\n\n// Safe runtime view for consumers such as the onboarding BGC stage. It never\n// contains credential values, only credentialsConfigured/webhookConfigured.\nexport async function getBgcIntegrationSummary() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/integration-config/bgc/summary');\n return json?.data ?? json ?? { updateMethod: 'MANUAL', enabled: true };\n}\n\nconst INTEGRATION_CONFIG_PATH = '/admin/integration-config';\n\nexport async function getIntegrationConfigs() {\n const json = await fetchJsonWithAuth(AUTH_URL, INTEGRATION_CONFIG_PATH);\n return json?.data ?? json ?? [];\n}\n\nexport async function getIntegrationConfig(integrationKey) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `${INTEGRATION_CONFIG_PATH}/${encodeURIComponent(integrationKey)}`,\n );\n return json?.data ?? json;\n}\n\nexport async function saveIntegrationConfig(config) {\n const key = config?.integrationKey;\n const path = key\n ? `${INTEGRATION_CONFIG_PATH}/${encodeURIComponent(key)}`\n : INTEGRATION_CONFIG_PATH;\n return fetchJsonWithAuth(AUTH_URL, path, {\n method: key ? 'PUT' : 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n}\n\nexport async function deleteIntegrationConfig(integrationKey) {\n return fetchJsonWithAuth(\n AUTH_URL,\n `${INTEGRATION_CONFIG_PATH}/${encodeURIComponent(integrationKey)}`,\n { method: 'DELETE' },\n );\n}\n\nexport async function testIntegrationConnection(config) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${INTEGRATION_CONFIG_PATH}/test`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config),\n });\n return json?.data ?? json;\n}\n\nexport async function getIntegrationSummary(integrationKey) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/integration-config/${encodeURIComponent(integrationKey)}/summary`,\n );\n return json?.data ?? json;\n}\n\nexport async function applyIntegrationStatus(integrationKey, recordId, providerResponse) {\n const json = await fetchJsonWithAuth(\n AUTH_URL,\n `/integration-config/${encodeURIComponent(integrationKey)}/apply-status/${encodeURIComponent(recordId)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(providerResponse),\n },\n );\n return json?.data ?? json;\n}\n\n// getModuleDetailPreview fetches a record's detail-view groups WITH resolved\n// values (references → {id,value}, documents → full object) so the admin page\n// can preview exactly what the detail view will render.\nexport async function getModuleDetailPreview(module, id) {\n const path = `${FORM_GROUPS_PATH}?module=${encodeURIComponent(module)}&id=${encodeURIComponent(id)}&view=detail`;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const data = json?.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\n// ── Roles ─────────────────────────────────────────────────────────────────────\n\nfunction normalizeRole(record) {\n return {\n key: record.role_id ?? record.id,\n roleId: record.role_id ?? record.id,\n roleName: record.role_name ?? 'N/A',\n description: record.role_description ?? 'N/A',\n userCount: record.user_count ?? 0,\n roleType: record.role_type ?? 0,\n raw: record,\n };\n}\n\nexport function getRoleId(role) {\n return role?.raw?.role_id ?? role?.roleId ?? role?.key;\n}\n\nexport async function getAllRoles() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/get-roles');\n const payload = json.data ?? json;\n const defaultRoles = firstArray(payload?.zinnext_default).map(normalizeRole);\n const customRoles = firstArray(payload?.custom_roles).map(normalizeRole);\n const all = [...defaultRoles, ...customRoles];\n return { roles: all, total: all.length };\n}\n\n// ── Teams ─────────────────────────────────────────────────────────────────────\n\nfunction normalizeTeam(record) {\n return {\n key: record.teamId,\n teamId: record.teamId,\n teamName: record.teamName ?? 'N/A',\n manager: record.reportingManager ?? 'N/A',\n managerId: record.reportingManagerId ?? 0,\n memberCount: record.noOfTeamMembers ?? 0,\n members: firstArray(record.teamMembers),\n memberIds: firstArray(record.teamMembersIds),\n raw: record,\n };\n}\n\nexport function getTeamId(team) {\n return team?.raw?.teamId ?? team?.teamId ?? team?.key;\n}\n\nexport async function getAllTeams({ offset = 0, limit = 10, sortBy = 'new', search = '' } = {}) {\n const params = new URLSearchParams({ searchquery: search, sortBy, limit: String(limit), offset: String(offset) });\n const json = await fetchJsonWithAuth(AUTH_URL, `/get-teams?${params}`);\n const payload = json.data ?? json;\n const teams = firstArray(payload?.teams, payload, json?.teams);\n const total = payload?.totalCount ?? payload?.total ?? teams.length;\n return { teams: teams.map(normalizeTeam), total };\n}\n\nexport async function updateTeam(team, { teamName, managerId, memberIds = [] }) {\n const teamId = getTeamId(team);\n return fetchJsonWithAuth(AUTH_URL, `/team/edit?teamId=${teamId}`, {\n method: 'PUT',\n body: JSON.stringify({\n team_name: teamName,\n team_manager: managerId,\n other_team_members: memberIds,\n reporting_team_member: [],\n }),\n });\n}\n\n// ── Role / Team Field Config ──────────────────────────────────────────────────\n\nasync function fetchFieldConfigModule(apiModule, idParam, idValue, configType = 'listView') {\n const params = new URLSearchParams({ module: apiModule, userId: '0', [idParam]: String(idValue), configType });\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}?${params}`);\n const payload = json.data ?? json;\n return firstArray(payload, payload?.visibleFields, json?.visibleFields);\n}\n\nexport async function getRoleModuleFields(role, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const roleId = getRoleId(role);\n const customizableModules = normalizeCustomizableModules(modules);\n const entries = await Promise.all(\n customizableModules.map(async ({ key, apiModule }) => {\n const fields = await fetchFieldConfigModule(apiModule, 'roleId', roleId, configType);\n return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, roleId }))];\n })\n );\n return Object.fromEntries(entries);\n}\n\nexport async function getTeamModuleFields(team, configType = 'listView') {\n const teamId = getTeamId(team);\n const entries = await Promise.all(\n CUSTOMIZABLE_MODULES.map(async ({ key, apiModule }) => {\n const fields = await fetchFieldConfigModule(apiModule, 'teamId', teamId, configType);\n return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, teamId }))];\n })\n );\n return Object.fromEntries(entries);\n}\n\nfunction visibleFieldPayload(fields, configType) {\n const isListView = !configType || configType === 'listView';\n return fields.map((field, index) => {\n const base = {\n label: field.fieldName, field: field.fieldKey,\n isVisible: field.isVisible, type: field.type ?? 'text', order: field.order ?? index,\n };\n if (isListView) {\n Object.assign(base, {\n isLink: field.isLink ?? false, linkTemplate: field.linkTemplate ?? '',\n linkType: field.linkType ?? 'internal', linkTarget: field.linkTarget ?? '_self',\n action: field.action ?? 'navigate',\n secondaryField: field.secondaryField ?? '', secondaryLabel: field.secondaryLabel ?? '',\n secondaryFields: Array.isArray(field.secondaryFields) ? field.secondaryFields : [],\n secondarySeparator: field.secondarySeparator ?? '',\n lookup: field.lookup ?? null, actionButtons: field.actionButtons ?? [],\n derived: field.derived ?? null, computed: field.computed ?? null,\n renderType: field.renderType ?? '', renderConfig: field.renderConfig ?? null,\n valueStyles: Array.isArray(field.valueStyles) ? field.valueStyles : [],\n commonStyle: field.commonStyle ?? null, displayTemplate: field.displayTemplate ?? '',\n });\n } else {\n base.isEditable = field.isEditable ?? false;\n base.ismandatory = field.ismandatory ?? field.isMandatory ?? false;\n if (configType === 'filter') {\n base.filterInputType = field.filterInputType ?? 'dropdown';\n base.isDefaultFilter = Boolean(field.isDefaultFilter);\n base.dataSource = field.dataSource ?? 'module';\n if (field.dataSource === 'group') base.groupName = field.groupName ?? '';\n if (field.lookup) base.lookup = field.lookup;\n } else {\n if (['select', 'radio', 'checkbox'].includes(field.type)) {\n base.dataSource = field.dataSource ?? 'module';\n if (field.dataSource === 'group') base.groupName = field.groupName ?? '';\n }\n }\n }\n return base;\n });\n}\n\nexport async function updateRoleModuleFieldConfig(role, module, fields, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const roleId = getRoleId(role);\n const apiModule = normalizeCustomizableModules(modules).find((m) => m.key === module)?.apiModule ?? module;\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/role`, {\n method: 'PUT',\n body: JSON.stringify({ module: apiModule, roleId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n });\n}\n\n// ── Role Configure → Form (derived from Form Groups) ─────────────────────────\n// Unlike getRoleModuleFields/updateRoleModuleFieldConfig above (an\n// independently-seeded flat field list), these back the Form tab's grouped\n// view: the backend derives groups/fields straight from the module's Form\n// Groups config and overlays this role's saved grants (GET\n// /admin/role-form-permissions), so Form Groups stays the single source of\n// truth and new/renamed/removed fields sync automatically. Saving still goes\n// through the existing PUT /admin/field-config/role (configType \"form\") —\n// the backend clamps it against Form Groups' global Show flags either way.\nconst ROLE_FORM_PERMISSIONS_PATH = '/admin/role-form-permissions';\n\n// Must match Be_Auth_DevOps models/roleFormPermissions.go's\n// roleGroupPermissionKey — the synthetic field-config key a group-level\n// ON/OFF toggle is persisted under, alongside real per-field grants, in the\n// same flat visibleFields list (no separate schema/endpoint needed).\nfunction roleGroupPermissionKey(groupName) {\n return `__group__:${groupName}`;\n}\n\n// scope: { clientId, region } — same client/region scope as\n// getAdminFormGroups/AddFormV1's effectiveClientId+effectiveRegion, so a\n// client- or region-scoped Form Groups override (e.g. a per-client Jobs\n// variant) is reflected here too, not just the tenant-wide default. Generic\n// for any module — GetFormGroupConfigScoped falls back to the default when\n// no scoped config exists for the given module, so this is always safe to pass.\nexport async function getRoleFormPermissions(role, module, scope = {}) {\n const roleId = getRoleId(role);\n const params = new URLSearchParams({ module, roleId: String(roleId ?? '') });\n if (scope.clientId) params.set('clientId', scope.clientId);\n if (scope.region) params.set('region', scope.region);\n const json = await fetchJsonWithAuth(AUTH_URL, `${ROLE_FORM_PERMISSIONS_PATH}?${params}`);\n const payload = json?.data ?? json;\n return Array.isArray(payload) ? payload : [];\n}\n\n// groups: [{ name, label, enabled, locked, fields: [{ field, label, enabled, locked, editable }] }]\n// as edited in the UI (see RoleFormPermissionsEditor). Group toggles are sent\n// as synthetic visibleFields rows so the existing role-save + clamp pipeline\n// needs no changes.\n//\n// A role's field-level grants are NOT stored per client/region — they're one\n// shared set overlaid onto whichever scoped Form Groups structure a caller\n// requests (getRoleFormPermissions). So any group/field that's `locked` in\n// the CURRENTLY VIEWED scope (globally disabled there, but possibly enabled\n// under the tenant-wide default or a different client's scope) is left out\n// of the payload entirely — sending its scope-computed `enabled: false`\n// would silently overwrite the role's real, scope-independent stored\n// preference the next time anyone loads a different scope. Locked items\n// aren't editable in this view anyway (their Switch is disabled), so there's\n// nothing this save is meant to change for them.\nexport async function saveRoleFormPermissions(role, module, groups) {\n const roleId = getRoleId(role);\n const visibleFields = [];\n let order = 0;\n for (const group of groups) {\n if (!group.locked) {\n visibleFields.push({\n label: group.label,\n field: roleGroupPermissionKey(group.name),\n // A group carries two independent role toggles: isVisible = shown,\n // isEditable = not-disabled (read-only when false). Default both true.\n isVisible: group.enabled,\n isEditable: group.editable !== false,\n order: order++,\n });\n }\n for (const field of group.fields ?? []) {\n if (field.locked) continue;\n visibleFields.push({\n label: field.label,\n field: field.field,\n isVisible: field.enabled,\n isEditable: field.editable !== false,\n order: order++,\n });\n }\n }\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/role`, {\n method: 'PUT',\n body: JSON.stringify({ module, roleId, configType: 'form', visibleFields }),\n });\n}\n\nexport async function updateTeamModuleFieldConfig(team, module, fields, configType = 'listView') {\n const teamId = getTeamId(team);\n const apiModule = CUSTOMIZABLE_MODULES.find((m) => m.key === module)?.apiModule ?? module;\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/team`, {\n method: 'PUT',\n body: JSON.stringify({ module: apiModule, teamId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n });\n}\n\n// ── Users ─────────────────────────────────────────────────────────────────────\n\nexport async function getAllUsers({ offset = 0, limit = 10, sortBy = 'new', searchQuery = '' } = {}) {\n const params = new URLSearchParams({ offset: String(offset), limit: String(limit), sortBy });\n if (searchQuery) params.set('searchquery', searchQuery);\n const json = await fetchJsonWithAuth(AUTH_URL, `/get-all-users?${params}`);\n const payload = json.data ?? json;\n const users = firstArray(\n payload, json.users, json.rows, json.items, json.records,\n payload?.users, payload?.data, payload?.rows, payload?.items,\n payload?.records, payload?.docs, payload?.result, payload?.results,\n );\n return { users, total: normalizeTotal({ ...json, ...payload }, users) };\n}\n\nexport function getUserId(user) {\n const value = firstValue(\n user?.raw ?? user,\n ['USER_ID', 'user_id', 'userId', 'id', '_id', 'uuid'],\n user?.key\n );\n const num = Number(value);\n return Number.isFinite(num) ? num : value;\n}\n\nexport function getUserRoleId(user) {\n const value = firstValue(user?.raw ?? user, ['ROLE_ID', 'role_id', 'roleId', 'ROLEID'], 0);\n const num = Number(value);\n return Number.isFinite(num) && num > 0 ? num : 0;\n}\n\nasync function getFieldConfigModule(apiModule, userId, roleId, configType) {\n const params = new URLSearchParams({ module: apiModule, userId: String(userId ?? ''), roleId: String(roleId ?? ''), configType });\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}?${params}`);\n const payload = json.data ?? json;\n return firstArray(payload, payload?.visibleFields, json?.visibleFields);\n}\n\nasync function getDropdownModuleFields(apiModule) {\n const json = await fetchJsonWithAuth(SUBMISSIONS_URL, `/filter-dropdown-fields?module=${encodeURIComponent(apiModule)}`);\n const payload = json.data ?? json;\n return firstArray(payload, payload?.fields, payload?.items, payload?.rows, json?.fields);\n}\n\nexport async function getUserModuleFields(user, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const userId = getUserId(user);\n const roleId = getUserRoleId(user);\n const customizableModules = normalizeCustomizableModules(modules);\n const entries = await Promise.all(\n customizableModules.map(async ({ key, apiModule }) => {\n let fields;\n try {\n fields = await getFieldConfigModule(apiModule, userId, roleId, configType);\n } catch {\n try { fields = await getDropdownModuleFields(apiModule); } catch { fields = []; }\n }\n if ((fields?.length ?? 0) === 0) {\n try { fields = await getDropdownModuleFields(apiModule); } catch { fields = []; }\n }\n return [key, fields.map((f, i) => ({ ...normalizeModuleField(f, i), module: key, apiModule, userId, roleId }))];\n })\n );\n return Object.fromEntries(entries);\n}\n\nexport async function hasUserFieldConfigData(user, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const userId = getUserId(user);\n const roleId = getUserRoleId(user);\n const customizableModules = normalizeCustomizableModules(modules);\n const results = await Promise.all(\n customizableModules.map(async ({ apiModule }) => {\n try {\n const fields = await getFieldConfigModule(apiModule, userId, roleId, configType);\n return (fields?.length ?? 0) > 0;\n } catch {\n return false;\n }\n })\n );\n return results.some(Boolean);\n}\n\nexport async function seedFieldConfig(collections = []) {\n return fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/seed`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ collections }),\n });\n}\n\nexport async function getFieldConfigSeedStatus() {\n const json = await fetchJsonWithAuth(AUTH_URL, `${FIELD_CONFIG_PATH}/seed-status`);\n const data = json?.data ?? json ?? {};\n return {\n modules: Array.isArray(data.modules) ? data.modules : [],\n seededModules: data.seededModules ?? {},\n };\n}\n\nexport async function updateUserModuleFieldConfig(user, module, fields, configType = 'listView', modules = CUSTOMIZABLE_MODULES) {\n const userId = getUserId(user);\n const roleId = getUserRoleId(user);\n const apiModule = normalizeCustomizableModules(modules).find((m) => m.key === module)?.apiModule ?? module;\n return fetchJsonWithAuth(AUTH_URL, FIELD_CONFIG_PATH, {\n method: 'PUT',\n body: JSON.stringify({ module: apiModule, userId, roleId, configType, visibleFields: visibleFieldPayload(fields, configType) }),\n });\n}\n\nexport async function updateUserModuleFields(user, moduleFields, configType = 'listView') {\n return Promise.all(\n Object.entries(moduleFields).map(([module, fields]) => updateUserModuleFieldConfig(user, module, fields, configType))\n );\n}\n\nexport async function uploadFormGroupIcon(file) {\n const token = localStorage.getItem('authToken');\n const formData = new FormData();\n formData.append('icon', file);\n const res = await fetch(`${AUTH_URL}/admin/form-groups/icon`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n if (!res.ok) { const e = new Error(`Upload failed: ${res.status}`); e.status = res.status; throw e; }\n const json = await res.json();\n return json.data ?? json;\n}\n\nexport async function uploadListViewActionIcon(file) {\n const token = localStorage.getItem('authToken');\n const formData = new FormData();\n formData.append('icon', file);\n const res = await fetch(`${AUTH_URL}/admin/field-config/action-icon`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: formData,\n });\n if (!res.ok) { const e = new Error(`Upload failed: ${res.status}`); e.status = res.status; throw e; }\n const json = await res.json();\n return json.data ?? json;\n}\n\n// Module options for the Actions admin screens: the configured menu/module\n// catalog PLUS every collection in this app's database, so row actions can be\n// configured for ANY module (onboarding, movements, a brand-new collection…)\n// without first registering it as a menu module. Resolves per app DB via\n// X-App-Id, so each project sees its own collections.\nexport async function getActionConfigModules() {\n const [modules, collections, existingConfigs] = await Promise.all([\n getAvailableModules().catch(() => []),\n getAvailableCollections().catch(() => []),\n getRowActionConfigs().catch(() => []),\n ]);\n const baseModules = Array.isArray(modules) ? modules : [];\n // Detail-view action variants: a module's DETAIL page can carry its own row\n // actions (e.g. an \"Open JD\" primary button on the submission detail view)\n // without those actions also appearing on the LIST view. The detail page\n // fetches actions under whatever key it passes as DetailHeaderCard's\n // customConfigName — \"submissiondetails\", \"submissionJob\" — which follows no\n // derivable rule. This used to GENERATE `${key}detail` names, which produced\n // keys nothing ever requests (\"submissionsdetail\" vs the real\n // \"submissiondetails\"), so configuring one silently changed nothing.\n // Offer the keys that actually exist in rowActionConfig instead.\n const variantKeys = existingConfigs\n .map((config) => String(config?.module ?? '').trim())\n .filter(Boolean);\n return [\n ...baseModules,\n ...(Array.isArray(collections) ? collections : []),\n ...variantKeys,\n ];\n}\n\n// Generic module CRUD — used by the Masters admin screen (locations/taxes),\n// but not specific to either: works for any module via the dynamic gateway.\nexport async function listModuleRecords(module, { page = 1, limit = 100 } = {}) {\n const params = new URLSearchParams({ module, page: String(page), limit: String(limit) });\n const json = await fetchJsonWithAuth(AUTH_URL, `/module/list?${params}`);\n const data = json?.data ?? json ?? {};\n return { items: Array.isArray(data.data) ? data.data : [], total: data.pagination?.total ?? 0 };\n}\n\nexport async function createModuleRecordGeneric(module, payload) {\n return fetchJsonWithAuth(AUTH_URL, `/module/create?module=${encodeURIComponent(module)}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n}\n\nexport async function updateModuleRecordGeneric(module, id, payload) {\n return fetchJsonWithAuth(AUTH_URL, `/module/update/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n}\n\nexport async function deleteModuleRecordGeneric(module, id) {\n return fetchJsonWithAuth(AUTH_URL, `/module/delete/${encodeURIComponent(id)}?module=${encodeURIComponent(module)}`, {\n method: 'DELETE',\n });\n}\n\n// Searchable \"City, State, Country\" options from the locationMasters collection\n// — the same source AddFormV1's `location` field type uses. Used by the Location\n// Tax Master screen to pick a real location instead of free-typing one.\nexport async function getLocationMasterOptions(search = '', limit = 50) {\n const params = new URLSearchParams({ search, limit: String(limit), offset: '0' });\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/location-dropdown-values?${params}`);\n const raw = json?.data ?? json ?? [];\n return (Array.isArray(raw) ? raw : [])\n .map((item) => {\n const label = typeof item === 'string' ? item : (item.label ?? item.value ?? '');\n return { label, value: label };\n })\n .filter((option) => option.label);\n}\n\n// Generic record options for any lookup-configured field — used by the Form\n// Groups editor's \"Conditional Default\" record picker (e.g. choose the default\n// Employer applied when Contract Type is W2). Returns { label, value } pairs\n// where value is the record's valueField (usually _id).\nexport async function getLookupRecordOptions(collection, displayField, valueField = '_id') {\n const params = new URLSearchParams({\n collection: String(collection),\n displayField: String(displayField),\n valueField: String(valueField),\n });\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/lookup-dropdown-values?${params}`);\n const raw = json?.data ?? json ?? [];\n return (Array.isArray(raw) ? raw : [])\n .map((item) => ({\n label: item.label ?? item.displayValue ?? item[displayField] ?? String(item.value ?? ''),\n value: String(item.value ?? item._id ?? item.id ?? ''),\n }))\n .filter((option) => option.value && option.label);\n}\n\n// Default Fields — per-module field keys forced always-shown + always-required.\n// getModuleDefaultFields is also called at runtime by AddFormV1/EditFormV1.\n// Saving is OTP-gated: request the code (requestConfigOtp('defaultFields','save'))\n// then pass it here.\nexport async function getModuleDefaultFields(module) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/default-fields?module=${encodeURIComponent(module)}`);\n const data = json?.data ?? json ?? {};\n return Array.isArray(data.fields) ? data.fields : [];\n}\n\nexport async function saveModuleDefaultFields(module, fields, otp) {\n return fetchJsonWithAuth(AUTH_URL, '/admin/default-fields', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module, fields, otp }),\n });\n}\n\n// Generic email verification — used by any admin-configured\n// field.verifyAction === \"email\" (AddFormV1/EditFormV1's VerifyFieldButton).\n//\n// The backend runs a layered check (syntax -> MX -> Mailgun -> SMTP mailbox\n// probe -> catch-all detection), so the verdict is richer than a boolean:\n//\n// valid keep/reject the address (unchanged meaning — the form gate)\n// result 'deliverable' | 'undeliverable' | 'risky' | 'unknown'\n// mailboxConfirmed the receiving server confirmed THIS mailbox specifically\n// catchAll the domain accepts every address, so the mailbox is unproven\n//\n// mailboxConfirmed is the one to trust for \"this person will actually get mail\".\n// A catch-all domain (Google Workspace default, many corporates) can never be\n// proven from outside — the UI says so rather than implying certainty.\nexport async function verifyEmail(email) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/email-verify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email }),\n });\n const data = json?.data ?? json ?? {};\n return {\n valid: Boolean(data.valid),\n reason: data.reason ?? '',\n result: data.result ?? '',\n mailboxConfirmed: Boolean(data.mailboxConfirmed),\n catchAll: Boolean(data.catchAll),\n disposable: Boolean(data.disposable),\n roleAddress: Boolean(data.roleAddress),\n risk: data.risk ?? '',\n checks: Array.isArray(data.checks) ? data.checks : [],\n };\n}\n\nexport async function getAvailableCollections() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/lookup/collections');\n const data = json.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\nexport async function getCollectionFields(collection) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/lookup/collection-fields?collection=${encodeURIComponent(collection)}`);\n const data = json.data ?? json;\n return Array.isArray(data) ? data : [];\n}\n\nexport async function getArraySubfields(module, field) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/field-array-subfields?module=${encodeURIComponent(module)}&field=${encodeURIComponent(field)}`);\n const data = json.data ?? json;\n return Array.isArray(data?.fields) ? data.fields : [];\n}\n\n// ── Menu Modules & Actions (master data management) ──────────────────────────\n\nexport async function getMenuModules() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-modules');\n return firstArray(json.data, json);\n}\n\nexport async function createMenuModule({ menuName, apiUrl = '', menuType = 'menu', parentMenuId = 0, displayOrder = 0 }) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-modules', {\n method: 'POST',\n body: JSON.stringify({ menuName, apiUrl, menuType, parentMenuId, displayOrder }),\n });\n return json.data ?? json;\n}\n\nexport async function updateMenuModule(id, { menuName }) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-modules/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ menuName }),\n });\n return json.data ?? json;\n}\n\nexport async function deleteMenuModule(id) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-modules/${id}`, { method: 'DELETE' });\n return json.data ?? json;\n}\n\nexport async function getMenuActions() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-actions');\n return firstArray(json.data, json);\n}\n\nexport async function createMenuAction({ permissionName, permissionKey }) {\n const json = await fetchJsonWithAuth(AUTH_URL, '/admin/menu-actions', {\n method: 'POST',\n body: JSON.stringify({ permissionName, permissionKey }),\n });\n return json.data ?? json;\n}\n\nexport async function updateMenuAction(id, { permissionName }) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-actions/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ permissionName }),\n });\n return json.data ?? json;\n}\n\nexport async function deleteMenuAction(id) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/admin/menu-actions/${id}`, { method: 'DELETE' });\n return json.data ?? json;\n}\n\n// ── Permissions ───────────────────────────────────────────────────────────────\n\nexport async function getRolePermissions(roleId) {\n const json = await fetchJsonWithAuth(AUTH_URL, `/get-role-details?roleid=${roleId}`);\n return (json?.data ?? json) ?? {};\n}\n\nexport async function updateRolePermissions(roleId, menus) {\n return fetchJsonWithAuth(AUTH_URL, '/edit-role-details', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ roleId, menus }),\n });\n}\n\n// Resolved permissions for the logged-in user's own role — moduleName ->\n// actionKey -> allowed (or the wildcard shape { \"*\": { \"*\": true } } for\n// full-access roles). Fetched once on login by PermissionContext.\nexport async function getMyPermissions() {\n const json = await fetchJsonWithAuth(AUTH_URL, '/me/permissions');\n return (json?.data ?? json) ?? {};\n}\n\n// ── Custom Forms ──────────────────────────────────────────────────────────────\n\nconst CUSTOM_FORMS_PATH = '/admin/custom-forms';\n\nexport async function getCustomForms(module = '', type = '', action = '') {\n const params = new URLSearchParams();\n if (module) params.append('module', module);\n if (type) params.append('formType', type);\n if (action) params.append('action', action);\n const path = `${CUSTOM_FORMS_PATH}?${params.toString()}`;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\nexport async function createCustomForm(form) {\n return fetchJsonWithAuth(AUTH_URL, CUSTOM_FORMS_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(form),\n });\n}\n\nexport async function updateCustomForm(id, form) {\n return fetchJsonWithAuth(AUTH_URL, `${CUSTOM_FORMS_PATH}/${id}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(form),\n });\n}\n\nexport async function deleteCustomForm(id) {\n return fetchJsonWithAuth(AUTH_URL, `${CUSTOM_FORMS_PATH}/${id}`, { method: 'DELETE' });\n}\n\n// ── Row Action Config ─────────────────────────────────────────────────────────\n\nconst ROW_ACTION_CONFIG_PATH = '/admin/row-action-config';\n\n// Keep the module key written by Admin identical to the key used by ListView.\n// Module labels/keys supplied by the module catalog (or typed into the tags\n// selector) may contain casing and surrounding whitespace, while list routes\n// consistently request normalized keys.\nfunction normalizeRowActionModule(module) {\n return String(module ?? '').trim().toLowerCase();\n}\n\nexport async function getRowActionConfigs(module = '') {\n const normalizedModule = normalizeRowActionModule(module);\n const path = normalizedModule\n ? `${ROW_ACTION_CONFIG_PATH}?module=${encodeURIComponent(normalizedModule)}`\n : ROW_ACTION_CONFIG_PATH;\n const json = await fetchJsonWithAuth(AUTH_URL, path);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\nexport async function createRowActionConfig({ module, roleId = 0, rowActions = [] }) {\n const normalizedModule = normalizeRowActionModule(module);\n return fetchJsonWithAuth(AUTH_URL, ROW_ACTION_CONFIG_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module: normalizedModule, roleId, rowActions }),\n });\n}\n\nexport async function updateRowActionConfig(id, rowActions = []) {\n return fetchJsonWithAuth(AUTH_URL, `${ROW_ACTION_CONFIG_PATH}/${id}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ rowActions }),\n });\n}\n\nexport async function deleteRowActionConfig(id) {\n return fetchJsonWithAuth(AUTH_URL, `${ROW_ACTION_CONFIG_PATH}/${id}`, { method: 'DELETE' });\n}\n\nexport async function seedRowActionConfigs() {\n return fetchJsonWithAuth(AUTH_URL, `${ROW_ACTION_CONFIG_PATH}/seed`, { method: 'POST' });\n}\n\n// ── Module Action Rules ───────────────────────────────────────────────────────\n// Per-module configurable rules stored in auth repo, returned by module-data-list.\n// Frontend evaluates these rules per-row to show/hide row action buttons.\n\nconst MODULE_ACTION_RULES_PATH = '/admin/module-action-rules';\n\nexport async function getModuleActionRules(module) {\n const json = await fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}?module=${encodeURIComponent(module)}`);\n const payload = json?.data;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(json)) return json;\n return [];\n}\n\nexport async function createModuleActionRule({ module, name, description = '', conditions = [], blockedActions = [], disabledActions = [], isActive = true }) {\n return fetchJsonWithAuth(AUTH_URL, MODULE_ACTION_RULES_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ module, name, description, conditions, blockedActions, disabledActions, isActive }),\n });\n}\n\nexport async function updateModuleActionRule(id, { name, description = '', conditions = [], blockedActions = [], disabledActions = [], isActive = true }) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}/${id}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name, description, conditions, blockedActions, disabledActions, isActive }),\n });\n}\n\nexport async function deleteModuleActionRule(id) {\n return fetchJsonWithAuth(AUTH_URL, `${MODULE_ACTION_RULES_PATH}/${id}`, { method: 'DELETE' });\n}\n","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 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 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 throw new Error(serverErrorMessage(errorText) || `API ${res.status}: ${res.statusText}`);\n }\n\n const json = await res.json();\n return json.data ?? json;\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","// 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 dateFormat: 'DD MMM YYYY',\n booleanTrueLabel: 'Yes',\n booleanFalseLabel: 'No',\n emptyPlaceholder: '-',\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.booleanTrueLabel) out.booleanTrueLabel = d.booleanTrueLabel;\n if (d.booleanFalseLabel) out.booleanFalseLabel = d.booleanFalseLabel;\n if (d.emptyPlaceholder) out.emptyPlaceholder = d.emptyPlaceholder;\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","// Runtime gate consulted by AddFormV1/EditFormV1 so a role's Form\n// Configuration (Role Configure → Form) actually restricts the real add/edit\n// form, not just the admin preview. rolePerms is the array returned by\n// GET /admin/role-form-permissions (see adminApi.js getRoleFormPermissions):\n// [{ name, enabled, locked, fields: [{ field, enabled, locked }] }].\n//\n// Fails OPEN (returns true) whenever rolePerms is null/empty/not-yet-loaded —\n// a module or role with no derived permissions behaves exactly as before\n// (gated only by Form Groups' own show/visiblePermission), so this is purely\n// additive and cannot hide a field that used to render.\nexport function groupAllowedByRole(rolePerms, groupName) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n return group.enabled !== false;\n}\n\nexport function fieldAllowedByRole(rolePerms, groupName, fieldKey) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n const field = group.fields?.find((f) => f.field === fieldKey);\n if (!field) return true;\n return field.enabled !== false;\n}\n\n// Editability gate — the \"disable\" side of Role Configure → Form. A group or\n// field marked editable:false is still SHOWN but rendered read-only. Fails\n// OPEN (returns true = editable) when nothing is configured, so a role/module\n// with no derived permissions stays fully editable, exactly as before.\nexport function groupEditableByRole(rolePerms, groupName) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n return group.editable !== false;\n}\n\nexport function fieldEditableByRole(rolePerms, groupName, fieldKey) {\n if (!rolePerms) return true;\n const group = rolePerms.find((g) => g.name === groupName);\n if (!group) return true;\n if (group.editable === false) return false; // group disabled → every field read-only\n const field = group.fields?.find((f) => f.field === fieldKey);\n if (!field) return true;\n return field.editable !== false;\n}\n","import DOMPurify from 'dompurify';\n\nconst RICH_TEXT_TAGS = [\n 'p', 'br', 'strong', 'b', 'em', 'i', 'u', 's', 'ul', 'ol', 'li',\n 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'a', 'code', 'pre',\n];\nconst RICH_TEXT_ATTRS = ['href', 'title', 'target', 'rel'];\nconst INVISIBLE_RE = /[\\u200B-\\u200D\\u2060\\uFEFF]/g;\n// Security normalization intentionally targets ASCII control characters.\n// eslint-disable-next-line no-control-regex\nconst CONTROL_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\nconst NAME_RE = /^[\\p{L}\\p{M}\\p{N} .'-]+$/u;\nconst PHONE_RE = /^\\+?[0-9 ()-]+$/;\nconst NUMBER_RE = /^-?(?:\\d+|\\d*\\.\\d+)$/;\n\nconst ATTACK_PATTERNS = [\n { re: /<\\s*\\/?\\s*(?:script|iframe|object|embed|svg|img|form|input|style|link|meta)\\b/i, message: 'HTML/script content is not allowed' },\n { re: /\\b(?:javascript|vbscript)\\s*:|\\bdata\\s*:\\s*text\\/html/i, message: 'Unsafe URL protocol is not allowed' },\n { re: /\\bon[a-z]+\\s*=/i, message: 'HTML event handlers are not allowed' },\n { re: /\\bunion\\s+(?:all\\s+)?select\\b|\\b(?:drop\\s+table|delete\\s+from|insert\\s+into|xp_cmdshell)\\b/i, message: 'Database command patterns are not allowed' },\n { re: /(?:['\"]\\s*)?\\b(?:or|and)\\s+\\d+\\s*=\\s*\\d+/i, message: 'Injection patterns are not allowed' },\n { re: /[\"']?\\$(?:where|gt|gte|lt|lte|ne|regex|or|and|expr|function)\\b/i, message: 'MongoDB operators are not allowed in input values' },\n { re: /(?:\\.\\.[/\\\\])|(?:%2e|%2f|%5c)/i, message: 'Path traversal patterns are not allowed' },\n { re: /&&|\\|\\||\\$\\(|\\$\\{|`|\\b(?:rm\\s+-rf|cat\\s+\\/etc\\/|whoami\\b|curl\\s+https?:\\/\\/|wget\\s+https?:\\/\\/)/i, message: 'Command execution patterns are not allowed' },\n];\n\nfunction decodeHtmlEntities(value) {\n if (typeof document === 'undefined') return value;\n const textarea = document.createElement('textarea');\n textarea.innerHTML = value;\n return textarea.value;\n}\n\nexport function canonicalizeForSecurityScan(value) {\n let result = String(value ?? '');\n for (let i = 0; i < 2; i += 1) {\n try {\n const decoded = decodeURIComponent(result);\n if (decoded === result) break;\n result = decoded;\n } catch { break; }\n }\n result = decodeHtmlEntities(result)\n .replace(/\\\\x([0-9a-f]{2})/gi, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)))\n .replace(/\\\\u([0-9a-f]{4})/gi, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)));\n return result.normalize('NFC');\n}\n\nexport function sanitizeRichText(value) {\n return DOMPurify.sanitize(String(value ?? ''), {\n ALLOWED_TAGS: RICH_TEXT_TAGS,\n ALLOWED_ATTR: RICH_TEXT_ATTRS,\n ALLOW_DATA_ATTR: false,\n FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', 'svg', 'img', 'form', 'input'],\n FORBID_ATTR: ['style', 'src', 'srcset'],\n });\n}\n\nfunction validationMax(field) {\n const rule = (field.validations ?? []).find((item) => item?.type === 'max');\n const configured = Number(field.validator?.maxLength ?? rule?.value ?? field.maxLength);\n if (Number.isFinite(configured) && configured > 0) return configured;\n if (field.type === 'email' || field.formatter === 'email') return 255;\n if (field.formatter === 'phone' || field.formatter === 'digits') return 32;\n if (field.formatter === 'name') return 100;\n if (field.type === 'text-editor') return 50000;\n if (field.type === 'textarea') return 5000;\n return 5000;\n}\n\nfunction isMultiline(field) {\n return field.type === 'textarea' || field.type === 'text-editor';\n}\n\nexport function normalizeSecureString(value, field = {}) {\n if (typeof value !== 'string') return value;\n if (field.type === 'text-editor') return sanitizeRichText(value.normalize('NFC'));\n\n let normalized = value.normalize('NFC')\n .replace(INVISIBLE_RE, '')\n .replace(/\\u00A0/g, ' ')\n .replace(CONTROL_RE, '');\n if (isMultiline(field)) {\n normalized = normalized.replace(/\\r\\n?/g, '\\n').replace(/[ \\t]{2,}/g, ' ').trim();\n } else {\n normalized = normalized.replace(/\\s+/g, ' ').trim();\n }\n return normalized;\n}\n\nexport function validateSecureString(value, field = {}) {\n if (typeof value !== 'string' || value === '') return null;\n const label = field.label || field.field || 'Field';\n const canonical = canonicalizeForSecurityScan(value);\n\n if (canonical.includes('\\0') || canonical.includes('\\u0000')) return `${label} contains a null byte`;\n if (field.type !== 'text-editor') {\n const attack = ATTACK_PATTERNS.find(({ re }) => re.test(canonical));\n if (attack) return `${label}: ${attack.message}`;\n } else {\n const dangerousRichText = ATTACK_PATTERNS.slice(0, 3).find(({ re }) => re.test(canonical));\n if (dangerousRichText) return `${label}: ${dangerousRichText.message}`;\n }\n\n const normalized = normalizeSecureString(value, field);\n if ([...normalized].length > validationMax(field)) return `${label} is too long`;\n if (field.formatter === 'name' && normalized && !NAME_RE.test(normalized)) {\n return `${label} allows only letters, numbers, spaces, apostrophes, hyphens and periods`;\n }\n if ((field.formatter === 'phone' || field.formatter === 'digits') && normalized && !PHONE_RE.test(normalized)) {\n return `${label} contains invalid phone characters`;\n }\n if (field.type === 'number' && normalized && !NUMBER_RE.test(normalized)) {\n return `${label} must contain only a valid number`;\n }\n if ((field.type === 'url' || field.formatter === 'url') && normalized && !/^https?:\\/\\//i.test(normalized)) {\n return `${label} must use http:// or https://`;\n }\n return null;\n}\n\nexport function securityValidationRule(field) {\n return {\n validator: (_, value) => {\n const values = Array.isArray(value) ? value : [value];\n const error = values.map((item) => validateSecureString(item, field)).find(Boolean);\n return error ? Promise.reject(new Error(error)) : Promise.resolve();\n },\n };\n}\n\nfunction policyMap(groups = []) {\n const policies = new Map();\n groups.forEach((group) => {\n (group.fields ?? []).forEach((field) => {\n if (field.type === 'file') return;\n const destination = field.payloadKey || field.field;\n if (!destination) return;\n const path = group.addRow ? `${group.payloadKey || group.name}[].${destination}` : destination;\n policies.set(path, field);\n });\n });\n return policies;\n}\n\nexport function securePayload(payload, groups = []) {\n const policies = policyMap(groups);\n const walk = (node, path = '') => {\n if (typeof node === 'string') {\n const field = policies.get(path) ?? {};\n const error = validateSecureString(node, field);\n if (error) throw new Error(error);\n return normalizeSecureString(node, field);\n }\n if (Array.isArray(node)) return node.map((item) => walk(item, `${path}[]`));\n if (node && typeof node === 'object') {\n return Object.fromEntries(Object.entries(node).map(([key, value]) => {\n if (key.startsWith('$') || key.includes('.') || key.includes('\\0')) {\n throw new Error(`Unsafe object key: ${key}`);\n }\n const childPath = path ? `${path}.${key}` : key;\n return [key, walk(value, childPath)];\n }));\n }\n return node;\n };\n return walk(payload);\n}\n","// optionMatching — snap an incoming value onto a field's CONFIGURED option.\n//\n// Any value that arrives from outside the form (an AI parse of a JD or resume,\n// an edit prefill, a cross-module prefill, an import) is free text. A select /\n// radio / checkbox control only selects when the value is character-for-\n// character one of its configured option values, so \"onsite\", \"ONSITE\",\n// \"On Site\" and \"on_site\" all silently failed to select the \"On-Site\" radio —\n// the parse looked like it had worked while the control sat empty.\n//\n// Matching is deliberately CONSERVATIVE and lossless:\n// • only fields that actually declare options are touched;\n// • an unmatched value is returned UNCHANGED, never blanked — a value we\n// cannot map is still shown to the user (and still saved) rather than\n// silently dropped;\n// • matching never invents a selection: it compares against the option's own\n// value and label only, plus whatever aliases the admin configured.\n//\n// Everything here is config-driven; no module, field or option name appears.\n\n// canonical — the comparison key. Case-folded, accent-folded and stripped of\n// every non-alphanumeric character, so \"On-Site\" / \"on site\" / \"ON_SITE\" /\n// \"onsite\" all collapse onto \"onsite\". Digits are kept so \"1\" ≠ \"10\".\nexport function canonical(value) {\n return String(value ?? '')\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '');\n}\n\n// optionEntries — the (canonicalKey → optionValue) pairs a field offers.\n// Both the option's value and its label are accepted as input, because a parse\n// far more often echoes the human label (\"Full Time\") than the stored value.\n// field.optionAliases lets an admin add synonyms the data uses but the option\n// list does not spell out, e.g. { \"WFH\": \"Remote\", \"work from home\": \"Remote\" }.\nexport function optionEntries(field) {\n const pairs = new Map();\n const add = (key, target) => {\n const k = canonical(key);\n // First writer wins: an earlier option keeps the key when a later option's\n // label happens to collapse onto the same string.\n if (k && !pairs.has(k)) pairs.set(k, target);\n };\n\n (Array.isArray(field?.options) ? field.options : []).forEach((option) => {\n if (option === null || option === undefined) return;\n const value = typeof option === 'object' ? option.value : option;\n if (value === undefined || value === null || value === '') return;\n add(value, value);\n if (typeof option === 'object' && option.label !== undefined) add(option.label, value);\n });\n\n Object.entries(field?.optionAliases ?? {}).forEach(([alias, target]) => {\n // An alias may only point AT a real option — otherwise a typo in config\n // would inject a value the control cannot select, which is the exact class\n // of bug this module exists to remove.\n const resolved = pairs.get(canonical(target));\n if (resolved !== undefined) add(alias, resolved);\n });\n\n return pairs;\n}\n\nconst hasOptions = (field) => Array.isArray(field?.options) && field.options.length > 0;\n\n// snapOne — map a single scalar onto an option value, or return it unchanged.\nfunction snapOne(pairs, value) {\n if (value === undefined || value === null || value === '') return value;\n // An object (a resolved reference like { id, value }) is not free text — the\n // reference resolver already owns it, so leave it entirely alone.\n if (typeof value === 'object') return value;\n const match = pairs.get(canonical(value));\n return match === undefined ? value : match;\n}\n\n// snapToOption — the entry point. Arrays map element-wise so a multi-select or\n// a checkbox group snaps every entry. Returns the input untouched for fields\n// with no options, which is most of them.\nexport function snapToOption(field, value) {\n if (!hasOptions(field)) return value;\n const pairs = optionEntries(field);\n if (pairs.size === 0) return value;\n if (Array.isArray(value)) return value.map((item) => snapOne(pairs, item));\n return snapOne(pairs, value);\n}\n\n// unmatchedOptionValues — the entries that could NOT be mapped onto an option.\n// Callers use it to tell the user what a parse failed to place, instead of\n// leaving a control looking mysteriously empty.\nexport function unmatchedOptionValues(field, value) {\n if (!hasOptions(field)) return [];\n const pairs = optionEntries(field);\n const list = Array.isArray(value) ? value : [value];\n return list.filter((item) => item !== undefined && item !== null && item !== ''\n && typeof item !== 'object'\n && !pairs.has(canonical(item)));\n}\n","/**\n * payloadTransformer — the single, configuration-driven payload engine shared by\n * AddFormV1 and EditFormV1.\n *\n * Goal: the Admin form-group config is the ONLY source of truth for how a form\n * value becomes an API payload value. There is ZERO field-name / module-specific\n * branching here. Every behaviour is driven by these per-field config keys:\n *\n * payloadKey output key (dot-notation allowed). Default: field.field\n * dataType auto | string | number | boolean | array | object | date | custom\n * elementType for dataType \"array\": coerce each element (e.g. \"number\")\n * payloadMode auto | value | label | object | custom | template | skip\n * valueKey which key holds the id/value on an option object (default: value/id/_id…)\n * displayKey which key holds the label on an option object (default: label/name…)\n * payloadTemplate object template for custom/template modes, with {{value}} {{label}} {{raw}} tokens\n * defaultValue value substituted when the form value is empty\n * transformRule { map: {...}, default, name } — value maps / named transforms\n * omitEmpty drop the key entirely when the final value is empty\n *\n * Nothing here knows about \"priority\", \"recruiters\", \"noticePeriod\", etc. Those\n * are expressed purely through the config above.\n *\n * Backward compatibility: when a field carries NONE of the new keys, the engine\n * falls back to the historical generic behaviour — dot-notation nesting, dayjs →\n * ISO string, scalar pass-through — so existing forms keep working unchanged.\n */\n\nimport dayjs from 'dayjs';\nimport { securePayload } from './inputSecurity';\nimport { snapToOption } from './optionMatching';\n\n// ── small shared predicates ───────────────────────────────────────────────────\n\nconst isEmpty = (v) =>\n v === undefined ||\n v === null ||\n v === '' ||\n (Array.isArray(v) && v.length === 0);\n\n// truthy tolerates the shapes a boolean config flag can arrive in (true/1/\"1\"),\n// matching how the forms read the same flags elsewhere.\nconst truthy = (v) => v === true || v === 1 || v === '1';\n\nconst isPlainObject = (v) =>\n v !== null && typeof v === 'object' && !Array.isArray(v) && !isDayjs(v);\n\nfunction isDayjs(v) {\n return (\n v &&\n typeof v === 'object' &&\n typeof v.format === 'function' &&\n typeof v.isValid === 'function'\n );\n}\n\nfunction isDateLike(v) {\n return isDayjs(v) || v instanceof Date;\n}\n\nfunction toISO(v) {\n if (v instanceof Date) return v.toISOString();\n if (isDayjs(v)) return v.isValid() ? v.toISOString() : null;\n return v;\n}\n\n// ── 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 toISO(value);\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 toISO(value);\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 toISO(value);\n\n case 'custom':\n case 'auto':\n case undefined:\n case '':\n default:\n return value;\n }\n}\n\n// ── transformRule (value maps + named transforms) ─────────────────────────────\n\nconst NAMED_TRANSFORMS = {\n firstChecked: (v) => (Array.isArray(v) ? v[0] : v),\n csvToArray: (v) =>\n typeof v === 'string' ? v.split(',').map((s) => s.trim()).filter(Boolean) : v,\n arrayToCsv: (v) => (Array.isArray(v) ? v.join(',') : v),\n trim: (v) => (typeof v === 'string' ? v.trim() : v),\n upper: (v) => (typeof v === 'string' ? v.toUpperCase() : v),\n lower: (v) => (typeof v === 'string' ? v.toLowerCase() : v),\n};\n\nfunction applyTransformRule(value, transformRule) {\n if (!transformRule) return value;\n let rule = transformRule;\n if (typeof rule === 'string') {\n // Either a named transform or a JSON blob.\n if (NAMED_TRANSFORMS[rule]) return NAMED_TRANSFORMS[rule](value);\n try {\n rule = JSON.parse(rule);\n } catch {\n return value;\n }\n }\n\n let out = value;\n if (rule.name && NAMED_TRANSFORMS[rule.name]) out = NAMED_TRANSFORMS[rule.name](out);\n\n if (rule.map && typeof rule.map === 'object') {\n const key = Array.isArray(out) ? String(out[0]) : String(out);\n if (Object.prototype.hasOwnProperty.call(rule.map, key)) out = rule.map[key];\n else if (rule.default !== undefined) out = rule.default;\n }\n return out;\n}\n\n// ── template resolution (custom / template payload modes) ─────────────────────\n\nconst TOKEN_RE = /\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g;\n\nfunction resolveToken(token, ctx) {\n const [head, ...rest] = token.split('.');\n let base;\n if (head === 'value') base = ctx.value;\n else if (head === 'label') base = ctx.label;\n else if (head === 'raw') base = ctx.raw;\n else return undefined;\n return rest.length ? getDeep(base, rest.join('.')) : base;\n}\n\nfunction resolveTemplateNode(node, ctx) {\n if (typeof node === 'string') {\n // Whole-string single token → return the typed value (keep numbers numeric).\n const whole = node.match(/^\\{\\{\\s*([\\w.]+)\\s*\\}\\}$/);\n if (whole) return resolveToken(whole[1], ctx);\n return node.replace(TOKEN_RE, (_, tok) => {\n const v = resolveToken(tok, ctx);\n return v == null ? '' : String(v);\n });\n }\n if (Array.isArray(node)) return node.map((n) => resolveTemplateNode(n, ctx));\n if (isPlainObject(node)) {\n const out = {};\n Object.entries(node).forEach(([k, v]) => {\n out[k] = resolveTemplateNode(v, ctx);\n });\n return out;\n }\n return node;\n}\n\nfunction parseTemplate(template) {\n if (!template) return null;\n if (typeof template === 'string') {\n try {\n return JSON.parse(template);\n } catch {\n return null;\n }\n }\n return template;\n}\n\n// ── payloadMode shaping ───────────────────────────────────────────────────────\n\nfunction shapeSingle(field, raw) {\n const mode = field.payloadMode || 'auto';\n const dataType = effectiveDataType(field);\n const { value, label } = splitValueLabel(raw, field);\n\n switch (mode) {\n case 'value':\n return { kind: 'scalar', out: coerceDataType(value, dataType, field) };\n\n case 'label':\n return { kind: 'scalar', out: coerceDataType(label, field.dataType || 'string', field) };\n\n case 'object': {\n const vKey = field.valueKey || 'id';\n const dKey = field.displayKey || 'name';\n return {\n kind: 'scalar',\n out: {\n [vKey]: coerceDataType(value, field.elementType || 'auto', {}),\n [dKey]: label,\n },\n };\n }\n\n case 'custom':\n case 'template': {\n const tpl = parseTemplate(field.payloadTemplate);\n if (!tpl) return { kind: 'scalar', out: coerceDataType(value, dataType, field) };\n const ctx = { value: coerceDataType(value, field.elementType || 'auto', {}), label, raw };\n const resolved = resolveTemplateNode(tpl, ctx);\n // A template that yields a plain object spreads into the parent unless the\n // admin pinned an explicit payloadKey.\n return { kind: field.payloadKey ? 'scalar' : 'spread', out: resolved };\n }\n\n case 'auto':\n default:\n return { kind: 'scalar', out: coerceDataType(value, dataType, field) };\n }\n}\n\n/**\n * shapeFieldValue — turns one field's raw form value into its payload contribution.\n * Returns { kind: 'scalar'|'spread'|'skip', out }.\n * scalar → write `out` at the field's payloadKey\n * spread → merge `out` (a plain object) into the parent payload\n * skip → contribute nothing\n */\nexport function shapeFieldValue(field, rawValue) {\n if (field.payloadMode === 'skip') return { kind: 'skip' };\n\n let raw = rawValue;\n if (isEmpty(raw) && field.defaultValue !== undefined && field.defaultValue !== '') {\n raw = field.defaultValue;\n }\n\n raw = applyTransformRule(raw, field.transformRule);\n\n if (isEmpty(raw) && field.omitEmpty) return { kind: 'skip' };\n\n // Multi-value (multi-select / array source) with a per-element shape.\n const isMultiSource =\n Array.isArray(raw) &&\n (effectiveDataType(field) === 'array' || field.payloadMode === 'object');\n\n if (isMultiSource && field.payloadMode && field.payloadMode !== 'value' && field.payloadMode !== 'auto') {\n const out = raw.map((item) => shapeSingle(field, item).out);\n return { kind: 'scalar', out };\n }\n\n return shapeSingle(field, raw);\n}\n\nexport function usesLabeledSelectValue(field = {}) {\n return Boolean(field.richValue) || ['label', 'object', 'custom', 'template'].includes(field.payloadMode);\n}\n\nexport function toSelectControlValue(field, value, options = []) {\n if (!usesLabeledSelectValue(field) || value == null) return value;\n const toLabeled = (item) => {\n if (isPlainObject(item) && item.value !== undefined) return item;\n const match = options.find((option) =>\n String(option?.value ?? '') === String(item) || String(option?.label ?? '') === String(item));\n return {\n value: match?.value ?? item,\n label: match?.label ?? String(item),\n };\n };\n return Array.isArray(value) ? value.map(toLabeled) : toLabeled(value);\n}\n\n// ── showIf payload gating ─────────────────────────────────────────────────────\n//\n// A field hidden by its showIf condition must not STORE either: antd preserves\n// values of unmounted Form.Items, so \"check VMS Required, type a commission,\n// uncheck it\" would still submit the commission. showIfSatisfied evaluates the\n// same operators the forms render with, against the field's own scope (the row\n// object for an addRow row) falling back to the whole form values (a condition\n// field living in another group, e.g. contractType). writeField consults it and\n// writes null instead of the stale value, so an edit also clears what a\n// now-hidden field previously stored. Config-driven; no field names.\n// One leaf condition ({field,operator,value}) evaluated against a scope.\nfunction showIfLeafSatisfied(cond, scope, root) {\n if (!cond?.field) return true;\n const val = hasFormValue(scope, cond.field)\n ? readFormValue(scope, cond.field)\n : readFormValue(root ?? scope, cond.field);\n const list = () => String(cond.value ?? '').split(',').map((v) => v.trim());\n switch (cond.operator) {\n case 'eq': return String(val ?? '') === String(cond.value ?? '');\n case 'neq': return String(val ?? '') !== String(cond.value ?? '');\n case 'truthy': return val !== undefined && val !== null && val !== '' && val !== false;\n case 'falsy': return val === undefined || val === null || val === '' || val === false;\n case 'notEmpty': return Array.isArray(val) ? val.length > 0 : Boolean(val);\n case 'in': return list().includes(String(val ?? ''));\n case 'notIn': return !list().includes(String(val ?? ''));\n default: return true;\n }\n}\n\n// All field keys a Show Condition reads (single leaf + every conditions[] entry)\n// — used to walk transitive visibility across each gate field.\nexport function showIfConditionFields(showIf) {\n const keys = [];\n if (showIf?.field) keys.push(showIf.field);\n if (Array.isArray(showIf?.conditions)) {\n showIf.conditions.forEach((c) => { if (c?.field) keys.push(c.field); });\n }\n return keys;\n}\n\n// A Show Condition may be a single leaf OR a `conditions` array combined by\n// `logic` (\"and\" default | \"or\"), plus an optional leaf folded in with the same\n// logic — mirrors the form-side evaluateShowIf so payload gating matches render.\nexport function showIfSatisfied(showIf, scope, root) {\n if (!showIf) return true;\n const conditions = Array.isArray(showIf.conditions) ? showIf.conditions.filter((c) => c?.field) : [];\n if (conditions.length) {\n const results = conditions.map((c) => showIfLeafSatisfied(c, scope, root));\n const orLogic = String(showIf.logic ?? 'and').toLowerCase() === 'or';\n let combined = orLogic ? results.some(Boolean) : results.every(Boolean);\n if (showIf.field) {\n const leaf = showIfLeafSatisfied(showIf, scope, root);\n combined = orLogic ? (combined || leaf) : (combined && leaf);\n }\n return combined;\n }\n return showIfLeafSatisfied(showIf, scope, root);\n}\n\n// fieldVisibleForPayload — transitive showIf: a field is storable only when its\n// own condition passes AND the field its condition READS is itself storable\n// (e.g. vmsCommission is gated on isVMSRequired, which is gated on\n// contractType — on W2 all of them drop together even if stale values linger).\n// `index` maps field key → field config across every group; `seen` guards\n// against condition cycles.\nfunction fieldVisibleForPayload(field, scope, root, index, seen = new Set()) {\n const gateFields = showIfConditionFields(field?.showIf);\n if (!gateFields.length) return true;\n if (seen.has(field.field)) return true; // cycle — fail open\n seen.add(field.field);\n if (!showIfSatisfied(field.showIf, scope, root)) return false;\n // Transitive: EACH gate field this condition reads must itself be storable.\n return gateFields.every((key) => {\n const gate = index?.get?.(key);\n return gate ? fieldVisibleForPayload(gate, scope, root, index, seen) : true;\n });\n}\n\n// payloadFieldIndex — field key → field config across all groups, for the\n// transitive showIf walk above.\nfunction payloadFieldIndex(groups = []) {\n const index = new Map();\n groups.forEach((g) => (g.fields ?? []).forEach((f) => {\n if (f?.field && !index.has(f.field)) index.set(f.field, f);\n }));\n return index;\n}\n\n// rowDefaultsFor — the seed object for a brand-new addRow row: every non-file\n// field's configured defaultValue (e.g. VMS Commission 5.5, Rate Currency USD)\n// keyed by its form key. Used for a group's initial empty rows AND every row\n// the user adds, so admin defaults show inside repeatable groups too (top-level\n// fields already get theirs via Form.Item initialValue).\nexport function rowDefaultsFor(group = {}, { editing = false } = {}) {\n const seed = {};\n (group.fields ?? []).forEach((f) => {\n // Same alias-tolerant upload test used everywhere else — a \"document\"-typed\n // upload must not be seeded with a defaultValue any more than a \"file\" one.\n if (!f.field || isUploadField(f)) return;\n if (editing && !f.defaultOnEdit) return;\n if (f.defaultValue === undefined || f.defaultValue === '') return;\n seed[f.field] = f.defaultValue;\n });\n return seed;\n}\n\n// ── field flattening from groups ──────────────────────────────────────────────\n\n/** All leaf fields across groups, with their group context (for addRow arrays). */\nexport function flattenFields(groups = []) {\n const out = [];\n groups.forEach((g) => {\n (g.fields ?? []).forEach((f) => {\n out.push({ field: f, group: g });\n });\n });\n return out;\n}\n\n// UPLOAD_FIELD_TYPES — every field type that holds an uploaded file.\n//\n// \"file\" is the canonical type the form renderer uses, but admin config in the\n// wild (and the seeded module defaults) also carries \"document\", \"image\",\n// \"upload\" and \"attachment\" for the same intent. Those aliases used to be\n// invisible to the collector, so their files silently never reached the shared\n// `documents` collection — a module whose uploads simply never appeared in the\n// registry, with no error anywhere. Matching on intent instead of on one exact\n// spelling makes the central documents registry work for EVERY module by\n// default, whichever synonym the config happens to use.\n//\n// This is safe for a field that is not really an upload: the collector only\n// ever pushes actual File/Blob values (see push in collectFileParts), so a\n// non-upload field simply contributes nothing.\nexport const UPLOAD_FIELD_TYPES = ['file', 'document', 'image', 'upload', 'attachment'];\n\n// isUploadField is the SINGLE predicate every file-collection path uses, so the\n// set of upload types can never drift between them again.\nexport function isUploadField(field) {\n return UPLOAD_FIELD_TYPES.includes(String(field?.type ?? '').trim().toLowerCase());\n}\n\nexport function fileFields(groups = []) {\n return flattenFields(groups)\n .map(({ field }) => field)\n .filter(isUploadField);\n}\n\n/**\n * collectFileParts — gather the NEW files the user picked, ready to append to a\n * multipart request, so create AND edit upload through the same gateway endpoint.\n *\n * Returns [{ formKey, file }]. The part name is the field's `fileKey` if set\n * (e.g. jobs reads \"file\"), else the first segment of its path\n * (\"passport.passportUploadName\" → \"passport\"). addRow groups fan out across\n * every row. Only fresh uploads (antd `originFileObj`, or a raw File/Blob) are\n * included — existing/stored files (url only) are left alone. Config-driven; no\n * field-name or module-specific logic.\n *\n * opts.indexed — when true, files inside an addRow group are emitted under an\n * INDEXED part name (\"documents[0]\", \"documents[1]\", …) where the index is the\n * row's position. EditForm uses this because the candidates update handler reads\n * document files by indexed key and aligns each file to its metadata row by\n * index.\n *\n * opts.scope — 'all' (default) | 'flat' | 'addRow'. Lets a caller collect only\n * the non-repeatable (flat) file fields, or only the repeatable (addRow) ones.\n * AddForm uploads flat files in the create request, then attaches addRow files\n * (e.g. candidate documents) in a follow-up update — the create handler for\n * repeatable document files is unreliable, while the indexed update path is the\n * proven one. Config-driven (keyed on the group's addRow flag), no module names.\n */\n// normalizeModuleKey trims + lower-cases a module key so a group's Target\n// Collection can be compared against the form's own module case-insensitively.\n//\n// It deliberately does NOT singularize (strip a trailing \"s\"). The gateway is\n// the source of truth for collection identity: it folds a moduleWrites entry\n// back into the main record only when the target resolves to the SAME physical\n// collection (foldSameCollectionModuleWrites → isSameCollectionTarget in\n// moduleCrudController.go), and its NormalizeFormGroupModule does not blindly\n// singularize either. A naive \"trainers\" → \"trainer\" fold here diverged from\n// that and silently merged a genuinely-distinct collection (e.g. \"trainers\")\n// into the primary record (e.g. module \"trainer\") — the exact multi-collection\n// bug. Exact-match keeps the FE from over-folding; any real same-collection\n// alias/plural case is folded server-side where the collection names are known.\nfunction normalizeModuleKey(module) {\n return String(module ?? '').trim().toLowerCase();\n}\n\n// isSameModuleTarget — the group's Target Collection is the form's own module\n// (exact, case-insensitive), i.e. the group writes to the MAIN record rather\n// than a linked document. Plural/alias targets that still resolve to the main\n// collection are folded by the gateway, not guessed here.\nfunction isSameModuleTarget(group, primaryKey) {\n return Boolean(primaryKey) && normalizeModuleKey(group.moduleName) === primaryKey;\n}\n\nexport function collectFileParts(values, groups = [], opts = {}) {\n const parts = [];\n const scope = opts.scope ?? 'all';\n const primaryKey = normalizeModuleKey(opts.module);\n const push = (formKey, node) => {\n if (node == null) return;\n const list = Array.isArray(node) ? node : [node];\n list.forEach((file) => {\n const raw = file?.originFileObj || file;\n if (typeof File !== 'undefined' && raw instanceof File) parts.push({ formKey, file: raw });\n else if (typeof Blob !== 'undefined' && raw instanceof Blob) parts.push({ formKey, file: raw });\n });\n };\n (groups ?? []).forEach((group) => {\n const files = (group.fields ?? []).filter(isUploadField);\n if (!files.length) return;\n // A moduleWrites group's files must be routed to ITS collection, not the\n // primary record — prefixed so the gateway's applyModuleWrites can tell\n // them apart (see splitModuleWriteFiles / filesForModuleWrite server-side).\n // A group targeting the form's own module writes to the main record, so\n // its files stay unprefixed (mirrors buildPayload's targetFor).\n const routed = group.moduleName && !isSameModuleTarget(group, primaryKey);\n const keyFor = (baseKey) => (routed ? `__mw__${group.moduleName}__${baseKey}` : baseKey);\n if (group.addRow) {\n if (scope === 'flat') return;\n const rows = values[group.name];\n if (!Array.isArray(rows)) return;\n files.forEach((field) => {\n const baseKey = field.fileKey ?? String(field.field).split('.')[0];\n rows.forEach((row, rowIdx) => {\n const formKey = keyFor(opts.indexed ? `${baseKey}[${rowIdx}]` : baseKey);\n push(formKey, readFormValue(row, field.field));\n });\n });\n return;\n }\n if (scope === 'addRow') return;\n files.forEach((field) => {\n const formKey = keyFor(field.fileKey ?? String(field.field).split('.')[0]);\n push(formKey, readFormValue(values, field.field));\n });\n });\n return parts;\n}\n\n// ── the two public builders ───────────────────────────────────────────────────\n\n/**\n * buildPayload — config-driven payload object from antd form values.\n *\n * @param values the antd form values object (may contain dot-notation nesting,\n * Form.List arrays for addRow groups, and dayjs date objects)\n * @param groups normalised form groups (with the per-field payload config)\n * @param opts { base } optional base object to merge onto (edit keeps the\n * untouched parts of the original record)\n * @returns a plain payload object ready to JSON.stringify\n *\n * File-typed fields are skipped — the caller handles uploads separately.\n */\nexport function buildPayload(values, groups, opts = {}) {\n const payload = isPlainObject(opts.base) ? structuredClone(opts.base) : {};\n\n // Strip file fields out of any base so stale file arrays never ride along.\n fileFields(groups).forEach((f) => {\n if (f.payloadKey || f.field) {\n // best-effort removal at both the configured key and source key\n deleteDeep(payload, f.payloadKey || f.field);\n deleteDeep(payload, f.field);\n }\n });\n\n // moduleWrites — a group with `moduleName` set (Feature 1: Per-Group Target\n // Collection) is routed to a DIFFERENT collection than the default record,\n // so its fields build their OWN sub-payload instead of merging into\n // `payload`. Groups sharing the same moduleName merge into ONE entry — the\n // backend's applyModuleWrites (moduleCrudController.go) upserts them the\n // same way, into one secondary document per (primary record, moduleName).\n //\n // A target that IS the form's own module (opts.module) means \"the main\n // record\": routing it through moduleWrites would create a parentId-linked\n // TWIN document in the same collection, so it merges into `payload` instead.\n // The backend applies the same guard (foldSameCollectionModuleWrites).\n const primaryKey = normalizeModuleKey(opts.module);\n const fieldIndex = payloadFieldIndex(groups);\n const moduleWrites = new Map();\n const targetFor = (group) => {\n if (!group.moduleName || isSameModuleTarget(group, primaryKey)) return payload;\n if (!moduleWrites.has(group.moduleName)) moduleWrites.set(group.moduleName, {});\n return moduleWrites.get(group.moduleName);\n };\n\n (groups ?? []).forEach((group) => {\n const target = targetFor(group);\n const groupFields = group.fields ?? [];\n const groupToggleOn = group.toggleEnabled && group.toggleField && hasFormValue(values, group.toggleField)\n ? Boolean(readFormValue(values, group.toggleField))\n : false;\n if (group.toggleEnabled && group.toggleField && hasFormValue(values, group.toggleField)) {\n setDeep(target, group.toggleField, groupToggleOn);\n if (groupToggleOn && group.addRow) {\n setDeep(target, group.payloadKey || group.name, []);\n return;\n }\n }\n\n if (group.addRow) {\n // Repeatable group → ROW-oriented array of objects:\n // [{ fieldA: v, fieldB: v }, { ... }]\n // matching a Go []struct (e.g. workExperience, educationDetails, documents).\n // The output key is the group's payloadKey (the struct's array key, e.g.\n // \"workExperience\") falling back to the group name.\n const rows = values[group.name];\n // Not present in this submit (e.g. EditForm doesn't prefill Form.List) →\n // leave whatever the base record already has untouched.\n if (!Array.isArray(rows)) return;\n const outKey = group.payloadKey || group.name;\n const rowObjects = rows\n .map((row) => buildRowObject(groupFields, row, values, fieldIndex))\n .filter((obj) => obj && Object.keys(obj).length > 0);\n setDeep(target, outKey, rowObjects);\n return;\n }\n\n groupFields.forEach((field) => writeField(field, values, target, values, fieldIndex));\n });\n\n if (moduleWrites.size > 0) {\n payload.moduleWrites = Array.from(moduleWrites, ([moduleName, data]) => ({ moduleName, data }));\n }\n\n return securePayload(payload, groups);\n}\n\n// writeField — the single, shared rule for turning ONE field's value (read from\n// `scope`, which is the whole form values for a normal group or a single row\n// object for an addRow row) into its contribution on `target`. Used by both\n// buildPayload (top-level groups) and buildRowObject (addRow rows) so a field\n// behaves identically no matter how deeply it is nested — a repeatable\n// field-GROUP (subFields) or a repeatable scalar field (addRow) serialises to a\n// nested array the same way at any level. Config-driven; no field-name logic.\nfunction writeField(field, scope, target, root, fieldIndex) {\n // isUploadField, NOT `type === 'file'`. Admin config in the wild types an\n // upload as \"document\"/\"image\"/\"upload\"/\"attachment\" just as often, and those\n // aliases used to MISS this branch entirely: the antd fileList\n // ([{uid,name,stored,…}]) then fell through to the ordinary scalar path and\n // was written to the payload as the field's value, overwriting the stored file\n // container with UI junk — or, when the user had not touched the field and it\n // held an empty list, writing [] over it. That is the \"an uploaded document\n // disappears when you edit the record\" bug: the update wiped the container the\n // form never intended to change. Every upload type now takes the branch below,\n // whose contract is \"carry the stored reference back, or write NOTHING\" —\n // never null, never an empty array, in any scope (flat field, addRow row, or\n // indexed document row: buildRowObject routes through this same function).\n if (isUploadField(field)) {\n // A NEW upload rides the multipart request (collectFileParts) and the\n // downstream handler writes its stored shape — nothing to do here.\n // An ALREADY-STORED file has no File object to upload, so without carrying\n // its reference the record ends up with no document at all: that is why a\n // Quick Submit prefilled from a previous submission lost every document it\n // showed in the form. Write back the untouched original container, merged\n // so sibling scalars written from the same container (passport.number,\n // passport.expiry) survive regardless of field order.\n const carried = carriedFileValue(readFormValue(scope, field.field));\n if (carried !== undefined) {\n // At the top level the fileKey names the stored container (\"passport\",\n // \"resume\"); inside an addRow row it is the multipart part name for the\n // whole group, so only the row's own key applies there.\n const inRow = root !== undefined && root !== scope;\n const key = field.payloadKey || (inRow ? field.field : (field.fileKey || field.field));\n mergeDeepAt(target, key, carried);\n }\n return;\n }\n\n // showIf gating at PAYLOAD time: a field whose visibility condition fails is\n // not stored. antd preserves unmounted Form.Item values, so without this a\n // value typed before the condition flipped (e.g. VMS Commission after VMS\n // Required was unchecked) would silently ride along. Writing null (rather\n // than skipping) also clears the stale stored value on edit; inside an\n // addRow row the whole row array is rewritten anyway, so the key simply\n // drops out of the row object. Visibility is TRANSITIVE: a field whose gate\n // field is itself hidden drops too (fieldVisibleForPayload).\n if (showIfConditionFields(field.showIf).length && !fieldVisibleForPayload(field, scope, root, fieldIndex)) {\n // Top-level (scope === root): write null so an edit clears the stale\n // stored value. Row scope: the row array is rewritten wholesale, so simply\n // omitting the key removes it from the stored row.\n if ((root === undefined || root === scope) && hasFormValue(scope, field.field)) {\n setDeep(target, field.payloadKey || field.field, null);\n }\n return;\n }\n\n // Repeatable field-GROUP (subFields): each Form.List row is an object of the\n // sub-fields, so this field submits as an array of row objects —\n // [{question, answer}, …] — under its own payloadKey. Recurses so a nested\n // repeat group (e.g. inside an addRow row) is shaped element-by-element.\n if (Array.isArray(field.subFields) && field.subFields.length > 0) {\n if (!hasFormValue(scope, field.field)) return;\n const rows = readFormValue(scope, field.field);\n if (!Array.isArray(rows)) return;\n const rowObjects = rows\n .map((row) => buildRowObject(field.subFields, row, root, fieldIndex))\n .filter((obj) => obj && Object.keys(obj).length > 0);\n setDeep(target, field.payloadKey || field.field, rowObjects);\n return;\n }\n\n if (!hasFormValue(scope, field.field)) return; // not in this submit — leave base untouched\n\n // Repeatable SCALAR field (addRow, no subFields): each Form.List item is one\n // value, so normalise every element through the field's single-value shape\n // (dates → ISO, select → id) instead of relying on a blanket array coercion.\n // This makes a repeat field work INSIDE an addRow row (subjects[] per row) as\n // well as at the top level.\n if (truthy(field.addRow) && (!Array.isArray(field.subFields) || field.subFields.length === 0)) {\n const raw = readFormValue(scope, field.field);\n const arr = Array.isArray(raw) ? raw : isEmpty(raw) ? [] : [raw];\n const single = singleFieldOf(field);\n const out = arr\n .map((el) => shapeFieldValue(single, el))\n .filter((r) => r.kind !== 'skip')\n .map((r) => r.out);\n setDeep(target, field.payloadKey || field.field, out);\n return;\n }\n\n const raw = readFormValue(scope, field.field);\n const result = shapeFieldValue(field, raw);\n if (result.kind === 'skip') return;\n if (result.kind === 'spread' && isPlainObject(result.out)) {\n mergeDeep(target, result.out);\n return;\n }\n setDeep(target, field.payloadKey || field.field, result.out);\n}\n\n// buildRowObject — shape one addRow row into a plain object keyed by each\n// field's payloadKey/field (relative to the row). File fields are skipped (they\n// upload separately and their metadata is merged server-side).\nfunction buildRowObject(groupFields, row, root, fieldIndex) {\n const obj = {};\n groupFields.forEach((field) => writeField(field, row, obj, root ?? row, fieldIndex));\n return obj;\n}\n\nfunction deleteDeep(obj, path) {\n const parts = String(path).split('.');\n let cur = obj;\n for (let i = 0; i < parts.length - 1; i += 1) {\n if (!isPlainObject(cur[parts[i]])) return;\n cur = cur[parts[i]];\n }\n delete cur[parts[parts.length - 1]];\n}\n\n/**\n * buildInitialValue — reverse direction, for EditForm prefill.\n * Given a field config and the stored value (which may be a scalar, an\n * { id, value } reference, an { id, name } object, or an array of those),\n * returns the value the input control expects.\n *\n * - date/time fields → dayjs\n * - select/radio/checkbox → the id/value (single) or array of ids (multi)\n * - everything else → the scalar\n *\n * This replaces convertInitialValue's hardcoded field-name handling.\n */\n// Controls that render exactly one primitive. Deliberately excludes select /\n// lookup / reference / file types, whose values are legitimately objects.\nconst SCALAR_CONTROL_TYPES = new Set([\n 'text', 'textarea', 'email', 'phone', 'tel', 'number', 'date', 'time', 'datetime', 'password',\n]);\n\nexport function buildInitialValue(field, stored) {\n const configuredEmpty = (value) => {\n const emptyValues = Array.isArray(field.emptyValues) ? field.emptyValues : [];\n return emptyValues.some((v) => String(v) === String(value));\n };\n const fallbackEditDefault = () =>\n field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== ''\n ? field.defaultValue\n : undefined;\n\n if (stored === undefined || stored === null) {\n return fallbackEditDefault() ?? stored;\n }\n if (configuredEmpty(stored)) {\n return fallbackEditDefault();\n }\n if (isEmpty(stored) && field.defaultOnEdit && field.defaultValue !== undefined && field.defaultValue !== '') {\n return field.defaultValue;\n }\n\n // A text/number/date control can only show a scalar. When the stored value is\n // a whole sub-document — a field whose key names its own container, e.g.\n // \"address\" storing {addressLine, city, state} while its payloadKey is\n // \"address.addressLine\" — reduce it to the payloadKey's leaf. Without this the\n // input renders \"[object Object]\" and saving writes that string over the real\n // address. Falls through to undefined (blank control) when no leaf matches:\n // never destructive, always recoverable.\n if (SCALAR_CONTROL_TYPES.has(field.type) && isPlainObject(stored)) {\n const leaf = String(field.payloadKey || field.field || '').split('.').pop();\n const reduced = leaf ? stored[leaf] : undefined;\n if (reduced === undefined || isPlainObject(reduced)) return undefined;\n stored = reduced; // eslint-disable-line no-param-reassign\n }\n\n if (field.type === 'date' || field.type === 'time') {\n if (!stored || stored === '0001-01-01T00:00:00Z') return null;\n const p = dayjs(stored);\n return p.isValid() ? p : null;\n }\n\n // Snap onto the field's configured option before the control sees it. A\n // select/radio only selects on an exact value match, so an incoming \"onsite\"\n // or \"ONSITE\" would leave an \"On-Site\" radio blank even though the value\n // arrived. Fields without options, and values that match nothing, pass\n // through untouched — this can correct a value but never discard one.\n stored = snapToOption(field, stored);\n\n const pickId = (item) => {\n if (isPlainObject(item)) {\n return (\n (field.valueKey && item[field.valueKey]) ??\n item.id ??\n item._id ??\n item.value ??\n item.userId ??\n item.recruiterId\n );\n }\n return item;\n };\n\n // Radio groups are ALWAYS single-select in the UI, even when the payload\n // dataType is \"array\" (e.g. jobRemoteStatus → []string). Prefill the scalar\n // so the selected radio shows; the payload engine re-wraps it to an array on\n // save via coerceDataType. Without this, a radio gets an array value and\n // renders with nothing selected.\n const isMulti = field.type === 'radio' ? false : effectiveDataType(field) === 'array';\n\n // A stored value that only differs from a static option by case (e.g. a\n // legacy \"active\" vs the configured option value \"Active\") would otherwise\n // match nothing and render as an unselected/empty control.\n const matchOptionCase = (v) => {\n if (v === undefined || v === null || v === '' || !Array.isArray(field.options)) return v;\n const match = field.options.find((option) => String(option?.value ?? '').toLowerCase() === String(v).toLowerCase());\n return match ? match.value : v;\n };\n\n if (field.type === 'select' || field.type === 'radio' || field.type === 'checkbox') {\n if (isMulti) {\n const arr = Array.isArray(stored) ? stored : [stored];\n return arr.map(pickId).map(matchOptionCase).filter((v) => v !== undefined && v !== null && v !== '' && !configuredEmpty(v));\n }\n const selected = matchOptionCase(Array.isArray(stored) ? pickId(stored[0]) : pickId(stored));\n return configuredEmpty(selected) ? fallbackEditDefault() : selected;\n }\n\n // checkbox group with a transformRule map (e.g. \"high\" → checked) is handled\n // by the caller via the same map; here we just pass the scalar through.\n return stored;\n}\n\n// singleFieldOf strips a repeatable (addRow) field down to its per-item shape:\n// each Form.List item holds ONE value, so multi/array coercion must NOT apply\n// when reading or rendering a single item. Preserves everything else (type,\n// options, datasource, validations) so the item control still behaves like the\n// field otherwise would.\nexport function singleFieldOf(field = {}) {\n return { ...field, addRow: false, multiSelect: false, mode: undefined, dataType: undefined };\n}\n\n// buildRepeatFieldInitial — the Form.List initialValue for a field-level repeat\n// (field.addRow). Unlike a repeatable GROUP (rows are objects), each item here\n// is a single scalar, so a stored array maps element-by-element through\n// buildInitialValue as if the field were single (see singleFieldOf). The result\n// is padded to minRows; when there is no stored value at all, initialRows\n// (falling back to minRows) empty inputs are shown so the user sees a starting\n// control instead of only a \"+\" button. Config keys mirror the group ones:\n// field.minRows / field.initialRows.\nexport function buildRepeatFieldInitial(field, stored) {\n const minRows = Math.max(0, Number(field.minRows ?? 0) || 0);\n const initialRows = field.initialRows !== undefined && field.initialRows !== ''\n ? Math.max(minRows, Math.max(0, Number(field.initialRows) || 0))\n : minRows;\n\n // Repeatable field-GROUP (subFields): each stored element is a row OBJECT, so\n // map every sub-field through buildInitialValue into a per-row object (mirrors\n // getAddRowInitialValue's group-addRow row mapping). Empty rows fall back to {}\n // so the sub-field controls still render.\n if (Array.isArray(field.subFields) && field.subFields.length > 0) {\n let rows = [];\n if (Array.isArray(stored)) {\n rows = stored.map((row) => {\n const out = {};\n field.subFields.forEach((sub) => {\n if (!sub.field || isUploadField(sub)) return;\n const v = getDeep(row ?? {}, sub.field) ?? (row ?? {})[sub.field];\n if (v !== undefined && v !== null) out[sub.field] = buildInitialValue(sub, v);\n });\n return out;\n });\n }\n const target = rows.length > 0 ? minRows : Math.max(minRows, initialRows);\n while (rows.length < target) rows.push({});\n return rows;\n }\n\n const singleField = singleFieldOf(field);\n let items = [];\n if (Array.isArray(stored)) {\n items = stored\n .map((v) => buildInitialValue(singleField, v))\n .filter((v) => v !== undefined && v !== null && v !== '');\n } else if (stored !== undefined && stored !== null && stored !== '') {\n const v = buildInitialValue(singleField, stored);\n if (v !== undefined && v !== null && v !== '') items = [v];\n }\n\n const target = items.length > 0 ? minRows : Math.max(minRows, initialRows);\n while (items.length < target) items.push(undefined);\n return items;\n}\n\nexport default {\n buildPayload,\n buildInitialValue,\n showIfSatisfied,\n rowDefaultsFor,\n buildRepeatFieldInitial,\n singleFieldOf,\n shapeFieldValue,\n usesLabeledSelectValue,\n toSelectControlValue,\n coerceDataType,\n getDeep,\n setDeep,\n flattenFields,\n fileFields,\n};\n","// linkedAddRowGroups.js\n//\n// Pure helpers for group.linkGroup — an opt-in mechanism that keeps two or\n// more addRow groups' rows synchronized (same add/remove, same row count)\n// while each group keeps rendering as its own Card and submitting its own\n// independent payload array (buildPayload / buildAddRowInitial are untouched\n// and stay fully per-group — see payloadTransformer.js / applyGroupValues.js).\n//\n// A group only participates when BOTH group.addRow is true AND group.linkGroup\n// is a non-empty string shared by at least one other group. Every group that\n// predates this field (i.e. every group in production today) has no\n// linkGroup, so these helpers are a strict no-op for it.\n\nconst truthy = (value) => value === true || value === 1 || value === '1';\n\n// computeLinkedSets(groups) -> Map<linkGroupKey, { leaderName, memberNames: string[] }>\n//\n// `groups` is expected already order-sorted (normalizeGroups sorts by `order`\n// before render), so the first member encountered per key is the leader —\n// Array.prototype.sort is stable, so this matches the existing order\n// convention with no extra tie-break logic needed. A \"set\" of size 1 (nothing\n// else shares that linkGroup value) is dropped — a lone linkGroup value isn't\n// meaningfully linked to anything, and the group renders as if unlinked.\nexport function computeLinkedSets(groups = []) {\n const byKey = new Map();\n groups.forEach((group) => {\n const key = group?.linkGroup;\n if (!truthy(group?.addRow) || !key) return;\n if (!byKey.has(key)) byKey.set(key, []);\n byKey.get(key).push(group.name);\n });\n\n const sets = new Map();\n byKey.forEach((memberNames, key) => {\n if (memberNames.length < 2) return;\n sets.set(key, { leaderName: memberNames[0], memberNames });\n });\n return sets;\n}\n\n// findLinkedSet(linkedSets, groupName) -> { leaderName, memberNames } | null\n// Cheap membership lookup against an already-computed sets Map (compute once\n// per render via computeLinkedSets, look up per group here — avoids\n// recomputing the whole map on every group).\nexport function findLinkedSet(linkedSets, groupName) {\n for (const set of linkedSets.values()) {\n if (set.memberNames.includes(groupName)) return set;\n }\n return null;\n}\n\n// padLinkedInitialValues(linkedSets, initialValuesByGroupName) -> a new\n// { [groupName]: rows[] } object where every member of a linked set is padded\n// with {} placeholder rows up to the set's max length. Guards against\n// pre-existing data where two linked groups' stored `rows` happen to differ\n// in length (undefined territory otherwise — buildAddRowInitial/\n// getAddRowInitialValue compute each group's rows fully independently). A\n// no-op passthrough for any group not in a real (>=2 member) linked set.\nexport function padLinkedInitialValues(linkedSets, initialValuesByGroupName = {}) {\n const next = { ...initialValuesByGroupName };\n linkedSets.forEach(({ memberNames }) => {\n const maxLen = Math.max(...memberNames.map((name) => (next[name] ?? []).length));\n memberNames.forEach((name) => {\n const rows = next[name] ?? [];\n if (rows.length < maxLen) {\n next[name] = [...rows, ...Array.from({ length: maxLen - rows.length }, () => ({}))];\n }\n });\n });\n return next;\n}\n","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","// 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 { 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 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, Modal, message } from 'antd';\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\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 }) {\n const [loadingKey, setLoadingKey] = useState(null);\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 if (existingRows.some(rowHasData)) {\n Modal.confirm({\n title: 'Replace existing rows?',\n content: `This will replace ${existingRows.length} existing row(s) in \"${group.label || group.name}\".`,\n okText: 'Replace',\n cancelText: 'Cancel',\n onOk: applyRows,\n });\n } else {\n applyRows();\n }\n });\n\n return appliedAny;\n }, [form]);\n\n const runAction = useCallback(async (groupName, field, action, extraFile) => {\n setLoadingKey(action.key);\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 appliedAny = applyResponse(result?.groups);\n const label = action.label || 'AI action';\n if (appliedAny) {\n message.success(`${label} completed successfully.`);\n } else {\n message.info(`${label} completed, but no matching details were found.`);\n }\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 }\n }, [form, module, applyResponse]);\n\n return { runAction, loadingKey, busy: loadingKey !== null, isActionEnabled };\n}\n","import { Button, Space } from 'antd';\n\n// AiActionButtonGroup — renders one position bucket (\"top-left\", \"bottom-right\",\n// etc.) of a field's AI action buttons, admin-configured entirely via each\n// action's `position`. Horizontal alignment follows the position's own\n// left/center/right suffix; the caller places top vs. bottom buckets around\n// the field itself.\nexport default function AiActionButtonGroup({ position, actions, aiActions, groupName, field }) {\n if (!actions || actions.length === 0) return null;\n\n const justifyContent = position.endsWith('center')\n ? 'center'\n : position.endsWith('right')\n ? 'flex-end'\n : 'flex-start';\n\n return (\n <Space size={8} className=\"v1-ai-action-buttons\" style={{ width: '100%', justifyContent }}>\n {actions.map((action) => (\n <Button\n key={action.key}\n size=\"small\"\n loading={aiActions.loadingKey === action.key}\n disabled={aiActions.busy || !aiActions.isActionEnabled(action)}\n onClick={() => aiActions.runAction(groupName, field, action)}\n >\n {action.label}\n </Button>\n ))}\n </Space>\n );\n}\n","// Generic email rule used by Add/Edit forms and the real-time input validator.\n// The rule is selected through the DB validation type `email`; it contains no\n// module or field-name assumptions.\nexport function isValidConfiguredEmail(value) {\n const email = String(value ?? '').trim();\n const match = /^([^\\s@]+)@([^\\s@]+)\\.([A-Za-z]{2,})$/.exec(email);\n if (!match) return false;\n\n const [, localPart, domainHost] = match;\n return /[A-Za-z]/.test(localPart) && /[A-Za-z]/.test(domainHost);\n}\n\nexport function configuredEmailRule(label = 'Email', message) {\n return {\n validator: (_, value) => {\n if (value === undefined || value === null || value === '') return Promise.resolve();\n return isValidConfiguredEmail(value)\n ? Promise.resolve()\n : Promise.reject(new Error(message ?? `Enter a valid ${label}; numeric-only email addresses are not allowed`));\n },\n };\n}\n","// inputValidator.js — config-driven real-time input restriction engine.\n// Driven entirely by field.validator in formGroupConfig — no hardcoding.\n// Used by FieldControl in AddFormV1 and EditFormV1.\n\nimport { isValidConfiguredEmail } from './emailValidator';\n\nexport const INPUT_VALIDATOR_TYPES = [\n { label: 'Only Numbers', value: 'onlyNumber' },\n { label: 'Only Letters', value: 'onlyLetter' },\n { label: 'Only Letters & Spaces', value: 'onlyLettersAndSpace' },\n { label: 'Only Alphanumeric', value: 'onlyAlphanumeric' },\n { label: 'Letters, Numbers & Hyphen', value: 'onlyLettersNumberAndHyphen' },\n { label: 'Letters, Numbers, Hyphen & Dot', value: 'onlyLettersNumberHyphenAndDot' },\n { label: 'Contact Number (digits, (), -)', value: 'contactNumber' },\n { label: 'Email (reject numeric-only address)', value: 'email' },\n { label: 'Job Title (letters, nums, symbols)', value: 'jobTitle' },\n { label: 'MSP / Ref ID (alphanumeric only)', value: 'mspRefId' },\n { label: 'Location (letters, nums, , . -)', value: 'locationValidation' },\n { label: 'Decimal / Range (number + dot)', value: 'decimalRange' },\n { label: 'Budget (numbers, must be > 0)', value: 'budgetValidation' },\n { label: 'Experience (numbers only)', value: 'experience' },\n { label: 'Numeric — 2 digits max', value: 'numericTwoDigits' },\n { label: 'Job Description (character count)', value: 'jobDescription' },\n { label: 'Website URL', value: 'websiteUrl' },\n];\n\n// onKeyPress allowlist patterns — used to block invalid chars before they appear.\n// null means no per-key blocking for that type.\nconst KEY_PATTERNS = {\n onlyNumber: /^[0-9]$/,\n experience: /^[0-9]$/,\n numericTwoDigits: /^[0-9]$/,\n budgetValidation: /^[0-9]$/,\n onlyLetter: /^[a-zA-Z\\s]$/,\n onlyLettersAndSpace: /^[a-zA-Z\\s]$/,\n onlyAlphanumeric: /^[a-zA-Z0-9]$/,\n mspRefId: /^[a-zA-Z0-9]$/,\n onlyLettersNumberAndHyphen: /^[a-zA-Z0-9\\s-]$/,\n locationValidation: /^[a-zA-Z0-9\\s\\-.,]$/,\n jobTitle: /^[a-zA-Z0-9\\s\\-.,/&+()*'\"#@]$/,\n decimalRange: /^[0-9.]$/,\n contactNumber: /^[0-9()\\-+\\s]$/,\n};\n\nexport function getKeyPattern(type) {\n return KEY_PATTERNS[type] ?? null;\n}\n\n// applyInputValidator — clean a raw input value according to the validator config.\n// Returns { cleaned: string, error: string|null }.\n// eventType 'blur' triggers trim; 'change' only strips leading whitespace.\nexport function applyInputValidator(rawValue, config = {}, eventType = 'change') {\n if (!config?.type || rawValue === undefined || rawValue === null) {\n return { cleaned: rawValue ?? '', error: null };\n }\n\n const value = String(rawValue);\n const { type, maxLength, maxChars } = config;\n const title = config.title || 'Field';\n let cleaned;\n let error = null;\n\n const trimStart = (s) => (eventType === 'blur' ? s.trim() : s.replace(/^\\s+/, ''));\n\n switch (type) {\n case 'onlyNumber':\n case 'experience':\n case 'numericTwoDigits': {\n const limit = Number(maxLength ?? (type === 'numericTwoDigits' ? 2 : 10));\n cleaned = value.replace(/[^0-9]/g, '');\n if (value !== cleaned) error = `${title} allows only numbers.`;\n if (cleaned.length > limit) {\n error = `${title} cannot exceed ${limit} characters.`;\n cleaned = cleaned.slice(0, limit);\n }\n break;\n }\n\n case 'onlyLetter': {\n const limit = Number(maxLength ?? 50);\n cleaned = value.replace(/[^a-zA-Z]/g, '');\n if (value !== cleaned) error = `${title} allows only letters.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersAndSpace': {\n const limit = Number(maxLength ?? 55);\n cleaned = trimStart(value.replace(/[^a-zA-Z\\s]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters and spaces.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyAlphanumeric':\n case 'mspRefId': {\n const limit = Number(maxLength ?? 50);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters and numbers.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersNumberAndHyphen': {\n const limit = Number(maxLength ?? 55);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s-]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers and hyphens.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'onlyLettersNumberHyphenAndDot': {\n const limit = Number(maxLength ?? 100);\n cleaned = value.replace(/[^a-zA-Z0-9\\-./#+[\\]{}()\\s]/g, '').slice(0, limit);\n if (value !== cleaned) error = `${title} allows only letters, numbers, hyphens and dots.`;\n break;\n }\n\n case 'contactNumber': {\n const limit = Number(maxLength ?? 15);\n cleaned = value.replace(/[^0-9()\\-+\\s]/g, '').slice(0, limit);\n const re = /^(\\+?[0-9]{1,3}[- ]?)?(\\(?\\d{1,4}\\)?[- ]?)?[\\d\\-\\s]{3,15}$/;\n if (value !== cleaned) error = `${title} allows only digits, parentheses () and hyphens.`;\n else if (cleaned && !re.test(cleaned)) error = `${title} is not a valid phone number format.`;\n break;\n }\n\n case 'locationValidation': {\n const limit = Number(maxLength ?? 100);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s\\-.,]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers, spaces, commas, dots and hyphens.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'jobTitle': {\n const limit = Number(maxLength ?? 80);\n cleaned = trimStart(value.replace(/[^a-zA-Z0-9\\s\\-/&.,()+#@'\"*]/g, ''));\n if (value.replace(/^\\s+/, '') !== cleaned) error = `${title} allows only letters, numbers, spaces and common symbols.`;\n if (cleaned.length > limit) { error = `${title} cannot exceed ${limit} characters.`; cleaned = cleaned.slice(0, limit); }\n break;\n }\n\n case 'decimalRange': {\n let c = value.replace(/[^0-9.]/g, '');\n const dotCount = (c.match(/\\./g) || []).length;\n if (dotCount > 1) {\n const di = c.indexOf('.');\n c = c.slice(0, di + 1) + c.slice(di + 1).replace(/\\./g, '');\n error = `${title} can have only one decimal point.`;\n }\n const digits = c.replace(/\\./g, '');\n const limit = Number(maxLength ?? 10);\n if (digits.length > limit) {\n c = c.slice(0, limit + (c.includes('.') ? 1 : 0));\n error = `${title} cannot exceed ${limit} digits.`;\n }\n cleaned = c;\n if (!error && value !== cleaned) error = `${title} allows only numbers and one dot.`;\n break;\n }\n\n case 'budgetValidation': {\n const limit = Number(maxLength ?? 10);\n cleaned = value.replace(/[^0-9]/g, '').slice(0, limit);\n const num = Number(cleaned);\n if (value !== cleaned) error = `${title} allows only numbers.`;\n else if (cleaned && num <= 0) error = `${title} must be greater than 0.`;\n break;\n }\n\n case 'jobDescription': {\n const limit = Number(maxChars ?? maxLength ?? 1500);\n const plain = value\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n if (plain.length > limit) error = `${title} cannot exceed ${limit} characters.`;\n cleaned = value; // never truncate HTML content\n break;\n }\n\n case 'email': {\n cleaned = eventType === 'blur' ? value.trim() : value;\n if (cleaned && !isValidConfiguredEmail(cleaned)) {\n error = `${title} must be a valid email address; numeric-only email addresses are not allowed.`;\n }\n break;\n }\n\n case 'websiteUrl': {\n cleaned = eventType === 'blur' ? value.trim() : value;\n const urlRe = /^(https?:\\/\\/)?([\\da-z.-]+)\\.([a-z.]{2,6})([/\\w .-]*)*\\/?$/i;\n if (cleaned && !urlRe.test(cleaned)) error = `${title} must be a valid URL.`;\n break;\n }\n\n default:\n cleaned = value;\n }\n\n return { cleaned, error };\n}\n","// Config-driven field uniqueness — the shared, pure runtime shared by\n// AddFormV1 and EditFormV1.\n//\n// An admin marks a field unique in Form Groups → Field → Validations; the\n// stored entry is an ordinary validations[] row:\n//\n// { type: 'unique', message: 'Email already present',\n// value: { collectionField, normalizer, skipBlank, validateOn } }\n//\n// Everything module-specific comes from that config. NOTHING in this file (or\n// in the two form engines) may name a module, a collection or a field — the\n// server resolves the collection, the stored field and the tenant scope from\n// the module + the caller's claims.\n//\n// This module owns ALL of the uniqueness behaviour so neither form duplicates\n// it: rule extraction, blank handling, normalization, the per-session cache,\n// the stale-response guard, the pending-request counter that blocks submit, the\n// antd rule factory (incl. \"only after the synchronous rules pass\") and the\n// mapping of a submit-time 409 back onto form fields.\n\nexport const UNIQUE_VALIDATION_TYPE = 'unique';\nexport const ALREADY_PRESENT_CODE = 'already_present';\nexport const DUPLICATE_VALUE_CODE = 'DUPLICATE_VALUE';\n\nconst DEFAULT_MESSAGE = 'This value is already present';\n\n// ── Rule extraction ──────────────────────────────────────────────────────────\n\nfunction validationType(validation) {\n if (typeof validation === 'string') return validation;\n return validation?.type ?? validation?.rule ?? validation?.name;\n}\n\n// The admin editor stores the unique options as an OBJECT under `value` (the\n// declared valueType is 'object', so the save path leaves it untouched). Older\n// or hand-written config may put the same keys flat on the validation itself,\n// so both shapes are accepted.\nexport function parseUniqueValidation(validation) {\n if (validationType(validation) !== UNIQUE_VALIDATION_TYPE) return null;\n if (typeof validation === 'string') {\n return { message: DEFAULT_MESSAGE, normalizer: 'trim', skipBlank: true, validateOn: 'blur' };\n }\n\n const raw = (validation.value && typeof validation.value === 'object') ? validation.value : validation;\n const validateOn = String(raw.validateOn ?? 'blur').toLowerCase();\n\n return {\n message: (typeof validation.message === 'string' && validation.message.trim())\n ? validation.message.trim()\n : DEFAULT_MESSAGE,\n // Kept only so the admin's configured target is inspectable client-side;\n // it is NEVER sent to the server (the server resolves the stored field).\n collectionField: raw.collectionField ?? '',\n normalizer: String(raw.normalizer ?? 'trim'),\n // skipBlank defaults to true; only an explicit `false` turns it off.\n skipBlank: raw.skipBlank !== false,\n validateOn: ['blur', 'change', 'submit'].includes(validateOn) ? validateOn : 'blur',\n };\n}\n\nexport function getUniqueRule(field) {\n const validations = field?.validations ?? field?.validation ?? field?.rules ?? [];\n if (!Array.isArray(validations)) return null;\n for (const validation of validations) {\n const rule = parseUniqueValidation(validation);\n if (rule) return rule;\n }\n return null;\n}\n\n// Every unique-configured field in the form, flattened across groups. Used to\n// map a submit-time 409 back onto a field when the transport lost the\n// structured body (see extractDuplicateFieldErrors).\nexport function collectUniqueRules(groups = []) {\n const collected = [];\n (Array.isArray(groups) ? groups : []).forEach((group) => {\n (group?.fields ?? []).forEach((field) => {\n const rule = getUniqueRule(field);\n if (!rule || !field?.field) return;\n collected.push({ field: field.field, label: field.label ?? field.field, rule });\n });\n });\n return collected;\n}\n\n// ── Blank + normalization ────────────────────────────────────────────────────\n\n// Blank = missing / null / empty / whitespace-only / empty list.\n// Numeric ZERO and boolean FALSE are REAL values, not blanks — a \"0\" employee\n// code or a `false` flag must still be uniqueness-checked.\nexport function isBlankUniqueValue(value) {\n if (value === undefined || value === null) return true;\n if (typeof value === 'number') return Number.isNaN(value);\n if (typeof value === 'boolean') return false;\n if (Array.isArray(value)) return value.length === 0;\n return String(value).trim() === '';\n}\n\nexport function normalizeUniqueValue(value, normalizer = 'trim') {\n if (value === undefined || value === null) return '';\n const text = String(value);\n switch (String(normalizer)) {\n case 'exact': return text;\n case 'lower':\n case 'trimLower':\n case 'email': return text.trim().toLowerCase();\n case 'digitsOnly': return text.replace(/\\D+/g, '');\n case 'trim':\n default: return text.trim();\n }\n}\n\nexport function shouldCheckUniqueValue(rule, value) {\n if (!rule) return false;\n if (rule.skipBlank !== false && isBlankUniqueValue(value)) return false;\n return true;\n}\n\n// antd/rc-field-form filters rules by trigger. `[]` matches no trigger at all,\n// so a submit-only rule runs exclusively inside form.validateFields().\nexport function uniqueValidateTriggers(rule) {\n if (!rule) return [];\n if (rule.validateOn === 'submit') return [];\n if (rule.validateOn === 'change') return ['onChange', 'onBlur'];\n return ['onBlur'];\n}\n\n// ── The checker (cache + staleness + pending) ────────────────────────────────\n\nexport const UNIQUE_STATUS = {\n SKIPPED: 'skipped',\n AVAILABLE: 'available',\n DUPLICATE: 'duplicate',\n STALE: 'stale',\n ERROR: 'error',\n};\n\nfunction cacheKey({ module, field, recordId, normalized }) {\n return `${module}\u0000${field}\u0000${recordId ?? ''}\u0000${normalized}`;\n}\n\n/**\n * One checker per form session. Both form engines create exactly one and pass\n * it into getRules; it is the only place a uniqueness request is ever made.\n *\n * @param checkFieldUnique the API function (injected so it can be mocked)\n * @param onPendingChange (pendingCount) => void — drives the submit button\n */\nexport function createUniqueChecker({ checkFieldUnique, onPendingChange } = {}) {\n // Normalized-value cache: the same normalized value is never re-checked in\n // one form session (blur → submit → blur again is one request, not three).\n // Only DEFINITIVE outcomes are cached — a failed request must be retried.\n const cache = new Map();\n // Monotonic sequence. Every request takes the next number and records itself\n // as its field's latest; when a response comes back with a number that is no\n // longer the latest for that field, the user has typed on and the answer\n // describes an OLD value — it is dropped instead of overwriting the new one.\n const latestSeq = new Map();\n let seq = 0;\n let pending = 0;\n\n function setPending(next) {\n pending = next;\n if (typeof onPendingChange === 'function') onPendingChange(pending);\n }\n\n async function check({ module, field, rule, value, recordId, clientId, region } = {}) {\n if (!rule || !module || !field || typeof checkFieldUnique !== 'function') {\n return { status: UNIQUE_STATUS.SKIPPED };\n }\n if (!shouldCheckUniqueValue(rule, value)) {\n return { status: UNIQUE_STATUS.SKIPPED };\n }\n\n const normalized = normalizeUniqueValue(value, rule.normalizer);\n const key = cacheKey({ module, field, recordId, normalized });\n if (cache.has(key)) return cache.get(key);\n\n seq += 1;\n const mySeq = seq;\n latestSeq.set(field, mySeq);\n setPending(pending + 1);\n\n try {\n const result = await checkFieldUnique({\n module,\n field,\n value,\n // recordId is passed straight through: EditFormV1 supplies it (so the\n // record does not collide with itself), AddFormV1 never does.\n recordId,\n clientId,\n region,\n });\n\n if (latestSeq.get(field) !== mySeq) return { status: UNIQUE_STATUS.STALE };\n\n const outcome = result?.available === false\n ? {\n status: UNIQUE_STATUS.DUPLICATE,\n message: firstDuplicateMessage(result) || rule.message || DEFAULT_MESSAGE,\n }\n : { status: UNIQUE_STATUS.AVAILABLE };\n\n cache.set(key, outcome);\n return outcome;\n } catch (err) {\n if (latestSeq.get(field) !== mySeq) return { status: UNIQUE_STATUS.STALE };\n // FAIL SAFE. A network/server failure must never block a user from\n // typing or submitting — submit-time enforcement on the server is the\n // authoritative check, and it still runs. Not cached, so the next blur\n // retries.\n return { status: UNIQUE_STATUS.ERROR, error: err };\n } finally {\n setPending(Math.max(0, pending - 1));\n }\n }\n\n return {\n check,\n isPending: () => pending > 0,\n pendingCount: () => pending,\n // Exposed for tests / a form that reloads its config mid-session.\n reset: () => { cache.clear(); latestSeq.clear(); },\n };\n}\n\nfunction firstDuplicateMessage(result) {\n const fromErrors = (result?.errors ?? []).find((item) => item?.message)?.message;\n return fromErrors || result?.message || '';\n}\n\n// ── antd rule factory ────────────────────────────────────────────────────────\n\n// Marker so getRules can find the unique rules again after the array is built\n// (see attachUniqueSyncGuards).\nconst UNIQUE_RULE_FLAG = '__uniqueRule';\n\n/**\n * Build the async antd rule for one `unique` validations entry.\n * `context` is supplied by the form engine: { checker, module, recordId,\n * clientId, region }. With no context (test harnesses, other callers of\n * getRules) the rule degrades to a no-op instead of throwing.\n */\nexport function buildUniqueValidationRule({ field, validation, context }) {\n const rule = parseUniqueValidation(validation);\n if (!rule) return { validator: () => Promise.resolve() };\n\n const fieldKey = field?.field ?? '';\n if (!context?.checker || !context?.module || !fieldKey) {\n return { validator: () => Promise.resolve() };\n }\n\n return {\n [UNIQUE_RULE_FLAG]: rule,\n validateTrigger: uniqueValidateTriggers(rule),\n validator: async (_, value) => {\n const outcome = await context.checker.check({\n module: context.module,\n field: fieldKey,\n rule,\n value,\n recordId: context.recordId,\n clientId: context.clientId,\n region: context.region,\n });\n if (outcome.status === UNIQUE_STATUS.DUPLICATE) {\n return Promise.reject(new Error(outcome.message || rule.message));\n }\n // skipped / available / stale / error all pass: a stale answer describes\n // a value the user has already replaced, and an error is handled by the\n // authoritative server-side check at submit.\n return Promise.resolve();\n },\n };\n}\n\n/**\n * The uniqueness call must never fire for a value that is ALREADY known\n * invalid (blank required field, malformed email, failed pattern) — that would\n * waste a round trip and stack a confusing second error under the field.\n *\n * antd runs a field's rules in parallel, so ordering alone cannot express\n * \"after the synchronous rules\". Instead each unique rule is re-wrapped with a\n * guard that first evaluates its sibling rules against the same value and\n * resolves immediately if any of them fails.\n */\nexport function attachUniqueSyncGuards(rules = []) {\n const list = Array.isArray(rules) ? rules : [];\n if (!list.some((rule) => rule && rule[UNIQUE_RULE_FLAG])) return list;\n\n const siblings = list.filter((rule) => rule && !rule[UNIQUE_RULE_FLAG]);\n return list.map((rule) => {\n if (!rule || !rule[UNIQUE_RULE_FLAG]) return rule;\n const inner = rule.validator;\n return {\n ...rule,\n validator: async (ruleArg, value) => {\n if (await hasSyncRuleError(siblings, value, ruleArg)) return Promise.resolve();\n return inner(ruleArg, value);\n },\n };\n });\n}\n\n// Minimal evaluator for the rule shapes this form engine actually produces:\n// { required }, { pattern }, { len }, { type:'url' } and custom { validator }.\n// Any rule it cannot interpret is treated as passing — the guard exists to\n// suppress a redundant request, never to invent a failure.\nexport async function hasSyncRuleError(rules = [], value, ruleArg = {}) {\n for (const rule of rules) {\n if (!rule || typeof rule === 'string') continue;\n if (rule.required && isBlankUniqueValue(value)) return true;\n if (!isBlankUniqueValue(value)) {\n if (rule.pattern instanceof RegExp && !new RegExp(rule.pattern.source, rule.pattern.flags).test(String(value))) return true;\n if (rule.len != null && String(value).length !== Number(rule.len)) return true;\n }\n if (typeof rule.validator === 'function') {\n try {\n await rule.validator(ruleArg, value);\n } catch {\n return true;\n }\n }\n }\n return false;\n}\n\n// ── Submit-time 409 → field errors ───────────────────────────────────────────\n\nfunction parseMaybeJson(text) {\n const trimmed = String(text ?? '').trim();\n if (!trimmed.startsWith('{')) return null;\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n}\n\nfunction duplicateBody(source) {\n if (!source) return null;\n if (typeof source === 'string') return parseMaybeJson(source);\n // An Error thrown by the create/update services: the structured body rides\n // on `.data` (createModuleRecord) or `.response` (fetchJsonWithAuth).\n const candidates = [source.data, source.response, source, parseMaybeJson(source.message)];\n for (const candidate of candidates) {\n if (!candidate || typeof candidate !== 'object') continue;\n if (Array.isArray(candidate.errors) || candidate.code === DUPLICATE_VALUE_CODE) return candidate;\n }\n return null;\n}\n\n/**\n * 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 field: item.field,\n message: item.message || body.message || DEFAULT_MESSAGE,\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 return matched.map((entry) => ({ field: 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","// 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","import { Button } from 'antd';\nimport { Link as RouterLink } from 'react-router-dom';\nimport '../styles/AppButton.css';\n\nexport default function AppButton({\n children,\n className = '',\n icon,\n to,\n variant = 'default',\n ...rest\n}) {\n const button = (\n <Button\n className={`app-button app-button--${variant} ${className}`.trim()}\n icon={icon}\n {...rest}\n >\n {children}\n </Button>\n );\n\n if (to) {\n return (\n <RouterLink className=\"app-button-link\" to={to}>\n {button}\n </RouterLink>\n );\n }\n\n return button;\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// userNames — turning a stored `updatedBy` id into a person's name.\n//\n// Rate history (and any other audit trail) stores WHO changed a value as an id.\n// The detail view resolved it through one directory call — /module/list?\n// module=users — and indexed the result on `legacyUserId` ALONE. Ids stamped by\n// the auth service are auth user ids, so they missed that index and the history\n// row read \"User #123\": a number shown to a recruiter as if it meant something.\n//\n// Two fixes, both generic:\n// • index every id form a users row can carry (legacyUserId / userId / _id /\n// id) — the same person under all their identities;\n// • resolve a still-unknown id through the auth service's own user endpoint,\n// cached per id for the session so a long history costs one call per\n// distinct actor, not one per row.\n//\n// And when it is STILL unresolvable, render nothing. \"User #123\" is not\n// information; an empty attribution is honest.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport { fetchJsonWithAuth } from '../../services/authApi';\nimport { AUTH_URL } from '../../services/apiConfig';\n\n/**\n * userDisplayName — the same name-shape preference the finance panel uses\n * (financeApprovalData.userDisplayName), so one person reads identically on\n * every surface regardless of which endpoint produced the row.\n */\nexport function userDisplayName(user) {\n if (!user || typeof user !== 'object') return '';\n const firstName = user.first_name ?? user.firstName ?? user.FIRST_NAME;\n const lastName = user.last_name ?? user.lastName ?? user.LAST_NAME;\n return [firstName, lastName].filter(Boolean).join(' ').trim()\n || user.name\n || user.user_name\n || user.userName\n || user.username\n || user.email\n || '';\n}\n\n/**\n * indexUserRows — id → name for EVERY id a directory row carries, so an actor\n * stamped with an auth id and one stamped with a legacy id both resolve.\n */\nexport function indexUserRows(rows) {\n const map = {};\n (Array.isArray(rows) ? rows : []).forEach((user) => {\n const name = userDisplayName(user);\n if (!name) return;\n [user.legacyUserId, user.userId, user.user_id, user._id, user.id].forEach((id) => {\n if (id === undefined || id === null || id === '') return;\n const key = String(typeof id === 'object' ? (id.$oid ?? id.id ?? '') : id);\n if (key && map[key] === undefined) map[key] = name;\n });\n });\n return map;\n}\n\n// Session cache: id → Promise<string>. Shared across every detail view so\n// re-opening a record never re-asks for the same person.\nconst userNameCache = new Map();\n\n/**\n * fetchUserName — resolve one id through the auth service, '' when unknown.\n * Never throws: an unresolvable actor must degrade to no attribution, never to\n * a broken detail page.\n */\nexport function fetchUserName(userId) {\n const key = String(userId ?? '').trim();\n if (!key) return Promise.resolve('');\n if (userNameCache.has(key)) return userNameCache.get(key);\n const request = fetchJsonWithAuth(\n AUTH_URL,\n `/get-detailed-view?userId=${encodeURIComponent(key)}`,\n )\n .then((json) => userDisplayName(json?.data ?? json ?? null))\n .catch(() => '');\n userNameCache.set(key, request);\n return request;\n}\n\n// Test seam — lets a suite prime/clear the cache without a network call.\nexport function primeUserName(userId, name) {\n userNameCache.set(String(userId), Promise.resolve(name));\n}\nexport function clearUserNameCache() {\n userNameCache.clear();\n}\n","// ─────────────────────────────────────────────────────────────────────────\n// quickCreateNotice — config + resolution logic for the \"you just created a\n// brand-new record\" popup shown after an in-dropdown quick-create.\n//\n// WHY this lives in its own module rather than inside QuickCreateEditField:\n// anything that imports AddFormV1/EditFormV1 (which QuickCreateEditField does,\n// lazily, and which the admin screen does statically) cannot be rendered under\n// vitest — react-pdf's DocumentViewer needs DOMMatrix and jsdom has none. That\n// is a pre-existing trap in this repo. Keeping the *decisions* (is the notice\n// on? what is the record called? who created it?) in a dependency-free module\n// means the part that can actually be wrong is unit-testable, and the React\n// file is left with nothing but rendering.\n//\n// Everything here is generic: no module name, no field name, no collection is\n// special-cased. A field opts in through the admin config key\n// `quickCreateNotice`.\n// ─────────────────────────────────────────────────────────────────────────\n\nimport { fetchJsonWithAuth, getStoredUser } from '../../services/authApi';\nimport { AUTH_URL } from '../../services/apiConfig';\nimport { fetchUserName, userDisplayName } from '../detail/userNames';\n\n/**\n * The product-owner-approved default. It is the DEFAULT rather than something\n * the admin must type, so that a field which merely switches the notice on\n * still shows the correct warning. Admin config overrides it verbatim.\n */\nexport const DEFAULT_QUICK_CREATE_NOTICE_MESSAGE =\n 'A new client has been created successfully. Please contact your Organization Admin to '\n + 'configure the required forms, workflows, permissions, and client-specific details before '\n + 'proceeding with further operations.';\n\nexport const DEFAULT_QUICK_CREATE_NOTICE_TITLE = 'New record created';\nexport const DEFAULT_QUICK_CREATE_RECORD_LABEL = 'Record name';\nexport const DEFAULT_QUICK_CREATE_CREATOR_LABEL = 'Created by';\n\nconst str = (v) => (v === undefined || v === null ? '' : String(v).trim());\n\n/**\n * normalizeQuickCreateNotice — field config → the resolved notice settings, or\n * null when this field has not opted in.\n *\n * `enabled` defaults to TRUE when the key is present at all: an admin who took\n * the trouble to author a `quickCreateNotice` object meant to switch it on, and\n * a config object that silently does nothing is the worst of the two failure\n * modes. Only an explicit `enabled: false` turns it off.\n */\nexport function normalizeQuickCreateNotice(field) {\n const raw = field?.quickCreateNotice;\n if (!raw || typeof raw !== 'object') return null;\n if (raw.enabled === false) return null;\n\n return {\n enabled: true,\n title: str(raw.title) || DEFAULT_QUICK_CREATE_NOTICE_TITLE,\n message: str(raw.message) || DEFAULT_QUICK_CREATE_NOTICE_MESSAGE,\n // Both traceability lines are ON unless explicitly disabled — same reasoning\n // as `enabled` above.\n showCreator: raw.showCreator !== false,\n showRecordName: raw.showRecordName !== false,\n recordNameField: str(raw.recordNameField),\n recordLabel: str(raw.recordLabel) || DEFAULT_QUICK_CREATE_RECORD_LABEL,\n creatorLabel: str(raw.creatorLabel) || DEFAULT_QUICK_CREATE_CREATOR_LABEL,\n };\n}\n\n// Keys that commonly hold a record's human name, tried in order when the admin\n// configured no recordNameField and the field carries no lookup displayField.\nconst NAME_LIKE_KEYS = [\n 'clientName', 'companyName', 'name', 'title', 'fullName',\n 'displayName', 'label', 'employerName', 'vendorName',\n];\n\nconst readPath = (record, key) => {\n if (!record || !key) return undefined;\n return String(key).split('.').reduce((acc, part) => (\n acc && typeof acc === 'object' ? acc[part] : undefined\n ), record);\n};\n\nconst scalarName = (v) => {\n if (typeof v === 'string' || typeof v === 'number') return str(v);\n // Labeled-select / reference shapes ({label,value}) show up on records too.\n if (v && typeof v === 'object') return str(v.label ?? v.name ?? v.title ?? '');\n return '';\n};\n\n/**\n * pickRecordName — the record's display name, by the configured key first, then\n * the field's lookup displayField, then well-known name-like keys, then ANY\n * string key whose name ends in \"name\". Purely best-effort: an empty result is\n * a legitimate outcome and must not stop the notice from appearing.\n */\nexport function pickRecordName(record, { recordNameField = '', displayField = '' } = {}) {\n if (!record || typeof record !== 'object') return '';\n for (const key of [recordNameField, displayField, ...NAME_LIKE_KEYS]) {\n if (!key) continue;\n const value = scalarName(readPath(record, key));\n if (value) return value;\n }\n const loose = Object.keys(record).find((k) => (\n /name$/i.test(k) && typeof record[k] === 'string' && record[k].trim()\n ));\n return loose ? str(record[loose]) : '';\n}\n\n/**\n * pickCreatorId — the id of whoever the stored record credits with its\n * creation, across the several shapes the gateway's audit stamp can take\n * (plain id, ObjectId wrapper, nested user object).\n */\nexport function pickCreatorId(record) {\n if (!record || typeof record !== 'object') return '';\n const candidates = [\n record.createdBy, record.created_by, record.createdById,\n record.createdUserId, record.recordMeta?.createdBy, record.ownerId,\n ];\n for (const candidate of candidates) {\n if (candidate === undefined || candidate === null || candidate === '') continue;\n if (typeof candidate === 'object') {\n const nested = candidate.$oid ?? candidate.userId ?? candidate._id ?? candidate.id;\n const value = str(nested);\n if (value) return value;\n continue;\n }\n const value = str(candidate);\n if (value) return value;\n }\n return '';\n}\n\n/** currentUserName — the signed-in user's display name, '' when unknowable. */\nexport function currentUserName(getUser = getStoredUser) {\n try {\n return userDisplayName(getUser()) || '';\n } catch {\n return '';\n }\n}\n\nconst defaultFetchRecord = (module, recordId) => fetchJsonWithAuth(\n AUTH_URL,\n `/module/list?module=${encodeURIComponent(module)}&id=${encodeURIComponent(recordId)}`,\n).then((res) => {\n const rows = res?.data?.data ?? res?.data ?? [];\n return Array.isArray(rows) ? rows[0] : rows;\n});\n\n/**\n * resolveQuickCreateNoticeDetails — the two traceability lookups.\n *\n * FAIL OPEN, deliberately and on every branch: the warning message is the point\n * of this popup, the name and the creator are garnish. A directory that is down,\n * a record the list endpoint cannot return, a module name that does not resolve\n * — none of it may suppress the notice or throw into the form. Every await is\n * individually caught and degrades to an empty string.\n *\n * The creator falls back to the signed-in user because in this flow the creator\n * IS the current user: they pressed \"Add More\" seconds ago. That fallback is\n * therefore accurate, not a guess.\n *\n * Dependencies are injected (with real defaults) so this is testable without a\n * network or a browser.\n */\nexport async function resolveQuickCreateNoticeDetails({\n module,\n recordId,\n notice,\n displayField = '',\n deps = {},\n} = {}) {\n const {\n fetchRecord = defaultFetchRecord,\n fetchUserNameFn = fetchUserName,\n getUser = getStoredUser,\n } = deps;\n\n let record = null;\n if (module && recordId) {\n try {\n record = await fetchRecord(module, recordId);\n } catch {\n record = null; // fail open\n }\n }\n\n const recordName = notice?.showRecordName === false\n ? ''\n : pickRecordName(record, { recordNameField: notice?.recordNameField, displayField });\n\n let creatorName = '';\n if (notice?.showCreator !== false) {\n const creatorId = pickCreatorId(record);\n if (creatorId) {\n try {\n creatorName = str(await fetchUserNameFn(creatorId));\n } catch {\n creatorName = ''; // fail open\n }\n }\n if (!creatorName) creatorName = currentUserName(getUser);\n }\n\n return { recordName, creatorName };\n}\n","// QuickCreateEditField — the \"Add More\" / \"Edit\" affordance inside a select\n// dropdown. Fully admin-config driven (FormGroupField.quickCreate / quickEdit,\n// see the Go struct). When a select carries quickCreate, its dropdown grows an\n// \"Add More\" button that opens the target module's Add form in a modal; on a\n// successful create the host field refetches its options and selects the new\n// record. quickEdit adds an \"Edit\" button that opens the target module's Edit\n// form for a resolved record id (this field's own value, or a sibling field via\n// quickEditIdFrom — e.g. \"edit the SELECTED CLIENT to add a contact\" from the\n// job's Contact Person field). No module or field name is hardcoded here.\n//\n// A field may also carry `quickCreateNotice` (see ./quickCreateNotice.js): a\n// popup shown the instant a record is created HERE, warning that a brand-new\n// record is not yet configured. Because only a real quick-create reaches that\n// code path, the notice cannot fire for an existing record that was merely\n// selected.\n//\n// AddFormV1/EditFormV1 are pulled in with React.lazy so this module can be\n// imported by those same files without a static circular dependency.\nimport React, { Suspense, lazy, useMemo, useState } from 'react';\nimport { 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 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 {field.quickCreateLabel || 'Add More'}\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 ? 'Select a record first' : undefined}\n >\n {field.quickEditLabel || 'Edit'}\n </Button>\n )}\n </Space>\n </div>\n );\n\n const modalNode = modal ? (\n <Modal\n open\n title={modal.mode === 'create' ? (field.quickCreateLabel || 'Add More') : (field.quickEditLabel || 'Edit')}\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","// 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\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\nexport default { narrowOptionsByConstraint };\n","// fieldTooltip — the single resolver for a field's admin-set help tooltip,\n// shared by AddFormV1 and EditFormV1.\n//\n// Two config shapes exist and both are honoured:\n// • `field.tooltip` (NEW key — one string, always shown)\n// • `field.infoEnabled` + `field.infoText` (the original toggle + text pair)\n//\n// Before this, tooltip text only ever reached the screen through FieldLabel, so\n// a field rendered WITHOUT a label (an inline/combined box, or a group that\n// prints one shared header row) could never carry one. Both forms now fall back\n// to wrapping the control itself for those, so ANY control type can get an\n// admin-set tooltip — which is the whole point: the text is config, the\n// capability is code.\n\nconst blank = (v) => v === undefined || v === null || String(v).trim() === '';\nconst truthy = (v) => v === true || v === 1 || v === '1' || v === 'true';\n\n/** @returns {string} the tooltip text for a field, or '' when it has none. */\nexport function fieldTooltipText(field) {\n if (!blank(field?.tooltip)) return String(field.tooltip);\n if (truthy(field?.infoEnabled) && !blank(field?.infoText)) return String(field.infoText);\n return '';\n}\n\nexport default { fieldTooltipText };\n","// Shared role/permission gate used by row actions (ListView, DetailHeaderCard)\n// and, via AddFormV1/EditFormV1, by field-level visiblePermission/editablePermission.\n// A permission value looks like \"<module>.<key>\" (e.g. \"submission.viewRate\").\n//\n// checkPermission() is the CURRENT gate — it resolves against the `can`\n// function from src/hooks/usePermissions.js (backed by GET /me/permissions,\n// see src/contexts/PermissionContext.jsx), which every consumer must call\n// usePermissions() to obtain and pass in. roleAllowsAction() below is the\n// OLD, localStorage.menuPermission-based gate — kept only for isActionValueAllowed's\n// row-level (record.actionPermission.<key>) use, which is a separate,\n// per-record mechanism unrelated to role permissions.\n\nexport function isActionValueAllowed(value) {\n if (value === undefined || value === null) return true;\n if (typeof value === 'object') {\n return isActionValueAllowed(value.permission ?? value.allowed ?? value.value);\n }\n return value !== false && value !== 0 && value !== '0';\n}\n\n// permission: \"<module>.<key>\" string from admin config (RowAction.permission,\n// field.visiblePermission/editablePermission). can: the `can` function returned\n// by usePermissions(). Falls back to the field/action's own moduleName when the\n// permission string has no module prefix (a bare key, e.g. \"edit\").\nexport function checkPermission(can, permission, moduleName) {\n if (!permission) return true;\n if (typeof can !== 'function') return true;\n const [configuredModule, configuredKey] = String(permission).split('.');\n const module = configuredKey ? configuredModule : (moduleName || configuredModule);\n const key = configuredKey ?? configuredModule;\n return can(module, key);\n}\n\n// Deprecated — localStorage.menuPermission-based gate, superseded by\n// checkPermission()/usePermissions(). No remaining call sites; kept only so a\n// stray import doesn't break until it's confirmed unused everywhere.\nexport function roleAllowsAction(permission, moduleName) {\n if (!permission) return true;\n try {\n const permissions = JSON.parse(localStorage.getItem('menuPermission') || '{}');\n const [configuredModule, configuredKey] = String(permission).split('.');\n const moduleKey = configuredModule || String(moduleName || '').replace(/s$/i, '');\n const modulePermissions = permissions[moduleKey]\n ?? permissions[String(moduleName || '')]\n ?? permissions[String(moduleName || '').replace(/s$/i, '')]\n ?? {};\n return isActionValueAllowed(modulePermissions[configuredKey]);\n } catch {\n return true;\n }\n}\n","// Shared \"After Submit\" navigation resolver used by AddFormV1/EditFormV1.\n// Admin-configured per FormGroup (group.afterSubmit — see FormGroupsSection.jsx),\n// generic across every module/project: no module name is ever referenced here.\n\n// Picks the configured behavior from the group with the lowest `order` that\n// declares one. Returns null when no group configures it, so callers fall\n// back to their existing (pre-feature) navigation — unchanged behavior.\nexport function resolveAfterSubmit(groups) {\n const withConfig = (groups ?? [])\n .filter((g) => g?.afterSubmit?.mode)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n return withConfig[0]?.afterSubmit ?? null;\n}\n\n// Resolves a configured route template (e.g. \"/trainers/:id\") against the\n// created/updated record id and the submitted field values, and rejects\n// anything that isn't a safe in-app path (blocks open-redirect / javascript:\n// / data: payloads an admin could otherwise paste into the target field).\nexport function safeNavTarget(template, recordId, values) {\n const raw = String(template ?? '').trim();\n if (!raw || !raw.startsWith('/') || raw.startsWith('//')) return null;\n if (/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null; // any \"scheme:\" prefix, e.g. javascript:, data:\n return raw.replace(/:([A-Za-z_][\\w]*)/g, (_, key) => {\n const value = key === 'id' ? recordId : values?.[key];\n return encodeURIComponent(value ?? '');\n });\n}\n","// Shared submit-toast resolver used by AddFormV1/EditFormV1.\n// Admin-configured per FormGroup (group.submitMessages — see FormGroupsSection),\n// generic across every module/project: no module name is ever referenced here.\n//\n// Defaults (no configuration anywhere, including brand-new modules):\n// add → \"<Module> has added successfully\"\n// edit → \"<Module> has Updated successfully\"\n// An admin can override any template (with ${module} substitution) or set\n// suppress to skip the success toast entirely.\n\n// Picks the configured messages from the group with the lowest `order` that\n// declares any — the same resolution rule afterSubmitNav uses.\nexport function resolveSubmitMessages(groups) {\n const withConfig = (groups ?? [])\n .filter((g) => g?.submitMessages && typeof g.submitMessages === 'object')\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n return withConfig[0]?.submitMessages ?? null;\n}\n\n// \"jobs\" → \"Jobs\", \"locationTaxMaster\" → \"LocationTaxMaster\" (first letter only —\n// the module key is the admin-facing name across the platform).\nfunction moduleLabel(moduleName) {\n const raw = String(moduleName ?? '').trim();\n return raw ? raw.charAt(0).toUpperCase() + raw.slice(1) : 'Record';\n}\n\nfunction fillTemplate(template, moduleName) {\n return String(template).replaceAll('${module}', moduleLabel(moduleName))\n .replaceAll('${moduleName}', moduleLabel(moduleName));\n}\n\n// Returns the success toast text for a finished submit, or null when the admin\n// suppressed success toasts for this module. kind: 'add' | 'edit'.\nexport function submitSuccessMessage(groups, moduleName, kind) {\n const config = resolveSubmitMessages(groups);\n if (config?.suppress) return null;\n const template = kind === 'edit' ? config?.editSuccess : config?.addSuccess;\n if (template && String(template).trim()) return fillTemplate(template, moduleName);\n return kind === 'edit'\n ? `${moduleLabel(moduleName)} has Updated successfully`\n : `${moduleLabel(moduleName)} has added successfully`;\n}\n\n// Returns the error toast text: admin override first, then the real server\n// error (most actionable), then a generic fallback. Never null — a failed\n// submit must always surface.\nexport function submitErrorMessage(groups, moduleName, kind, serverText) {\n const config = resolveSubmitMessages(groups);\n const template = kind === 'edit' ? config?.editError : config?.addError;\n if (template && String(template).trim()) return fillTemplate(template, moduleName);\n if (serverText && String(serverText).trim()) return String(serverText);\n return kind === 'edit'\n ? `Failed to update ${moduleLabel(moduleName)}`\n : `Failed to create ${moduleLabel(moduleName)}`;\n}\n","// Shared \"scroll to the first invalid field\" handler for AddFormV1/EditFormV1.\n// Wire it from the antd Form's onFinishFailed: when Create/Save is clicked with\n// validation errors, the screen scrolls to the first errored field, focuses it\n// (cursor placed in the input) and pulses a red halo so the user sees exactly\n// which field failed. Generic — works for every module's add & edit form.\n//\n// Robustness notes:\n// - antd applies the .ant-form-item-has-error classes AFTER onFinishFailed\n// fires, so location runs on a short delay.\n// - The DOM query + scrollIntoView is the PRIMARY mechanism and always runs.\n// form.scrollToField is only a best-effort extra: antd locates fields by\n// DOM id, which custom field controls don't always forward — relying on it\n// alone silently scrolls nothing.\n// - Callers should expand any collapsed sections BEFORE calling this (a field\n// inside a display:none section can be neither scrolled to nor focused) —\n// both form engines do so in their onFinishFailed wrapper.\nexport function scrollToFirstFormError({ errorFields } = {}, form) {\n if (!errorFields?.length) return;\n const firstName = errorFields[0]?.name;\n\n setTimeout(() => {\n if (form?.scrollToField && firstName !== undefined) {\n try {\n form.scrollToField(firstName, { behavior: 'smooth', block: 'center' });\n } catch { /* best-effort only — the DOM scroll below always runs */ }\n }\n\n const errorFormItem = document.querySelector('.ant-form-item-has-error');\n if (!errorFormItem) return;\n const input = errorFormItem.querySelector('input, textarea, select, .ProseMirror');\n const target = input ?? errorFormItem;\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n\n setTimeout(() => {\n input?.focus?.({ preventScroll: true });\n target.style.boxShadow = '0 0 0 3px rgba(255, 77, 79, 0.3)';\n setTimeout(() => { target.style.boxShadow = ''; }, 1200);\n }, 350);\n }, 60);\n}\n\nexport default scrollToFirstFormError;\n","function comparisonValues(value) {\n if (Array.isArray(value)) return value.map((item) => String(item ?? '').trim());\n return String(value ?? '').split(',').map((item) => item.trim());\n}\n\n// Matches the same generic operators supported by showIf. Keeping this helper\n// independent of React makes conditional labels usable in Add/Edit forms and\n// straightforward to test without module- or field-specific code.\nexport function conditionMatches(condition, value) {\n if (!condition?.field) return false;\n switch (condition.operator ?? 'eq') {\n case 'eq': return String(value ?? '') === String(condition.value ?? '');\n case 'neq': return String(value ?? '') !== String(condition.value ?? '');\n case 'truthy': return value !== undefined && value !== null && value !== '' && value !== false;\n case 'falsy': return value === undefined || value === null || value === '' || value === false;\n case 'notEmpty': return Array.isArray(value) ? value.length > 0 : Boolean(value);\n case 'in': return comparisonValues(condition.value).includes(String(value ?? ''));\n case 'notIn': return !comparisonValues(condition.value).includes(String(value ?? ''));\n default: return false;\n }\n}\n\nexport function configuredFieldLabel(field, watchedValue, combined = false) {\n const fallback = combined\n ? (field?.combineLabel ?? field?.label)\n : field?.label;\n const rule = field?.labelWhen;\n return conditionMatches(rule, watchedValue) && rule?.label\n ? rule.label\n : fallback;\n}\n\n// Flatten a showIf into its leaf conditions: the top-level {field,operator,value}\n// plus any \"add more\" entries in conditions[]. Mirrors how the forms read it.\nexport function showIfConditions(showIf) {\n if (!showIf) return [];\n const leaf = showIf.field ? [showIf] : [];\n return [...leaf, ...(showIf.conditions ?? []).filter((c) => c?.field)];\n}\n\n// Evaluate a showIf against an arbitrary value source. readValue(fieldKey) lets\n// the caller decide where values come from — live form state in the forms, or a\n// saved record in the detail view — so one condition definition drives both.\nexport function evaluateShowIfWith(showIf, readValue) {\n const conditions = showIfConditions(showIf);\n if (!conditions.length) return true;\n const results = conditions.map((c) => conditionMatches(c, readValue(c.field)));\n return showIf.logic === 'or' ? results.some(Boolean) : results.every(Boolean);\n}\n\n// colWhen — conditionally override a field's grid column span, so one field can\n// shrink to make room for a sibling that a showIf has just revealed (e.g. Work\n// Authorization narrows when its expiry date appears). Config is a rule, or a\n// list of rules, each {field, operator, value, col}; the first match wins.\n// Display only — the field key, payload mapping and validation are untouched.\nexport function colWhenRules(field) {\n const raw = field?.colWhen;\n if (!raw) return [];\n return (Array.isArray(raw) ? raw : [raw])\n .filter((rule) => rule?.field && Number(rule?.col) > 0);\n}\n\n// readValue(rule) resolves the watched value for one rule — the caller owns path\n// resolution so a rule can reference a sibling in the same addRow row or a\n// top-level field, exactly like showIf.\nexport function resolveColSpan(field, fallbackSpan, readValue) {\n for (const rule of colWhenRules(field)) {\n if (conditionMatches(rule, readValue(rule))) return Number(rule.col);\n }\n return fallbackSpan;\n}\n\nexport function conditionFieldPath(condition, siblingPrefix) {\n if (!condition?.field) return ['__noop_conditional_label__'];\n return siblingPrefix != null\n ? [...(Array.isArray(siblingPrefix) ? siblingPrefix : [siblingPrefix]), condition.field]\n : [condition.field];\n}\n","import { Breadcrumb } from 'antd';\nimport { DownOutlined } from '@ant-design/icons';\nimport { Link } from 'react-router-dom';\nimport AppTypography from './typography/Typography';\n\nexport default function AppBreadcrumb({ items = [] }) {\n const breadcrumbItems = items.map((item, index) => {\n const isLast = index === items.length - 1;\n return {\n title: isLast ? (\n <AppTypography variant=\"body\" weight=\"medium\" color=\"link\">\n {item.label}\n </AppTypography>\n ) : (\n <Link to={item.href}>\n <AppTypography variant=\"body\" color=\"secondary\">\n {item.label}\n </AppTypography>\n {item.dropdown && <DownOutlined />}\n </Link>\n ),\n };\n });\n\n return <Breadcrumb items={breadcrumbItems} />;\n}"],"mappings":"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"}
|