import { useState, useEffect, useRef } from 'react'; import type { ZinoClient } from '../../api/client'; import type { FormScreenResponse, FormScreenField } from '../../api/types'; import { Button } from '../buttons/Button'; import { Spinner } from '../reusable/Spinner'; import { FileInput, SmartGridField, GeolocationInput, PhoneInput, SelectField, TextField, EmailField, TextAreaField, DateField, TimeField, WfLookupField, RadioField, } from './fields'; import { ORDER_BOOKING, STORE, DAILY_REPORTS, PIPELINE, HIDDEN_FORM_FIELDS } from '../../api/config'; import { isCategoryColumn, isProductColumn } from './fields/gridUtils'; export interface DynamicFormProps { client: ZinoClient; activityId: string; instanceId?: number | string; onSuccess?: () => void; onCancel?: () => void; ignorePrefill?: boolean; customPrefillData?: Record; initialActivityName?: string; onActivityChange?: (name: string) => void; } export function DynamicForm({ client, activityId: initialActivityId, instanceId: initialInstanceId, onSuccess, onCancel, ignorePrefill, customPrefillData, initialActivityName, onActivityChange }: DynamicFormProps) { const [currentActivityId, setCurrentActivityId] = useState(initialActivityId); const [currentInstanceId, setCurrentInstanceId] = useState(initialInstanceId); const [chainQueue, setChainQueue] = useState>([]); useEffect(() => { setCurrentActivityId(initialActivityId); setCurrentInstanceId(initialInstanceId); setChainQueue([]); }, [initialActivityId, initialInstanceId]); const [schema, setSchema] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [chainedPrefillData, setChainedPrefillData] = useState | undefined>(); useEffect(() => { let mounted = true; setLoading(true); client.formSchema(currentActivityId, currentInstanceId) .then(async (res) => { if (mounted) { setSchema(res); const defaultValues: Record = {}; if (res.field_defaults) { Object.entries(res.field_defaults).forEach(([fieldId, def]) => { if (def.value != null) { defaultValues[fieldId] = def.value; } else if (def.prefill) { if (def.prefill.value === 'current_date') { defaultValues[fieldId] = new Date().toISOString().split('T')[0]; } else if (def.prefill.value === 'current_time') { defaultValues[fieldId] = new Date().toTimeString().split(' ')[0].substring(0, 5); } else if (def.prefill.value === 'current_user_id') { const user = client.currentUser(); defaultValues[fieldId] = user ? Number(user.id) : ''; } else { defaultValues[fieldId] = def.prefill.value; } } }); } const mapPrefillData = (sourceData: Record) => { res.fields.forEach(f => { const getBaseId = (id: string) => id.replace(/_\d+$/, ''); if (sourceData[f.id] !== undefined) { defaultValues[f.id] = sourceData[f.id]; } else if (f.mapped_workflow_field && sourceData[f.mapped_workflow_field] !== undefined) { defaultValues[f.id] = sourceData[f.mapped_workflow_field]; } else if (sourceData[getBaseId(f.id)] !== undefined) { defaultValues[f.id] = sourceData[getBaseId(f.id)]; } else { const matchingDataKey = Object.keys(sourceData).find(k => getBaseId(k) === f.id || getBaseId(k) === getBaseId(f.id) || k === f.mapped_workflow_field); if (matchingDataKey) { defaultValues[f.id] = sourceData[matchingDataKey]; } } if (f.data_type === 'grid' && Array.isArray(defaultValues[f.id])) { defaultValues[f.id] = (defaultValues[f.id] as Record[]).map(row => { const newRow: Record = { ...row }; f.columns?.forEach(col => { const colBaseId = getBaseId(col.id); if (row[col.id] !== undefined) { newRow[col.id] = row[col.id]; } else if (col.mapped_workflow_field && row[col.mapped_workflow_field] !== undefined) { newRow[col.id] = row[col.mapped_workflow_field]; } else if (row[colBaseId] !== undefined) { newRow[col.id] = row[colBaseId]; } else { const matchingRowKey = Object.keys(row).find(k => getBaseId(k) === col.id || getBaseId(k) === colBaseId || k === col.mapped_workflow_field); if (matchingRowKey) { newRow[col.id] = row[matchingRowKey]; } } }); return newRow; }); } }); }; if (!ignorePrefill) { if (res.prefill_data && Object.keys(res.prefill_data).length > 0) { mapPrefillData(res.prefill_data); } else if (res.data) { mapPrefillData(res.data); } } if (customPrefillData) { Object.entries(customPrefillData).forEach(([k, v]) => { defaultValues[k] = v; }); } if (chainedPrefillData) { Object.entries(chainedPrefillData).forEach(([k, v]) => { defaultValues[k] = v; }); } if (currentActivityId === DAILY_REPORTS.activities.PUNCH_OUT.uid) { const user = client.currentUser(); const userEmail = user?.email; if (userEmail) { try { const headers: Record = { 'templateid': '192', 'x-pipeline-version': 'latest', 'orgid': '57', 'groupid': '25' }; const summaryRes = await client.request( 'POST', PIPELINE.endpoints.productiveCallSummary, { email: userEmail }, headers ); let summaryData = summaryRes.data || summaryRes; if (Array.isArray(summaryData)) { summaryData = summaryData[0] || {}; } // Map the API response keys to match the form field keys if (summaryData.productive_call !== undefined) { summaryData.total_productive_calls = summaryData.productive_call; } if (summaryData.non_productive_call !== undefined) { summaryData.total_non_productive_calls = summaryData.non_productive_call; } mapPrefillData(summaryData); } catch (e) { console.warn('Failed to load productive call summary', e); } } } if (currentActivityId === ORDER_BOOKING.activities.LOG_VISIT.uid) { const user = client.currentUser(); const userMobile = user?.mobile || localStorage.getItem('krishna_sales_user_mobile') || user?.email; if (userMobile) { try { const headers: Record = { 'templateid': '192', 'x-pipeline-version': 'latest', 'orgid': '57', 'groupid': '25' }; const summaryRes = await client.request( 'POST', PIPELINE.endpoints.productiveCallSummary, { mobile: userMobile }, headers ); let summaryData = summaryRes.data || summaryRes; if (Array.isArray(summaryData)) { summaryData = summaryData[0] || {}; } if (summaryData.productive_call !== undefined) { summaryData.total_productive_calls = summaryData.productive_call; } if (summaryData.non_productive_call !== undefined) { summaryData.total_non_productive_calls = summaryData.non_productive_call; } mapPrefillData(summaryData); } catch (e) { console.warn('Failed to load productive call summary for Log Visit', e); } } } setValues(defaultValues); setLoading(false); if (res.activity_name) { onActivityChange?.(res.activity_name); } } }) .catch((err: any) => { if (mounted) { setError(err?.message || 'Failed to load schema'); setLoading(false); } }); return () => { mounted = false; }; }, [client, currentActivityId, currentInstanceId]); const [values, setValues] = useState>({}); const [submitting, setSubmitting] = useState(false); const [hasSubmitted, setHasSubmitted] = useState(false); const [submitError, setSubmitError] = useState(null); const [showErrors, setShowErrors] = useState(false); const clickedActionRef = useRef(null); const handleFieldChange = (fieldId: string, newVal: unknown, fullRow?: any) => { if (submitError) { setSubmitError(null); } setValues(prev => { const next = { ...prev, [fieldId]: newVal }; if (fullRow) { next[`${fieldId}_row`] = fullRow; // Auto-fill mapped fields based on the raw row data only for route_name if (fieldId === 'route_name_2' || fieldId.startsWith('route_name')) { schema?.fields.forEach(f => { if (f.id !== fieldId) { const mappedName = (f.mapped_workflow_field || '').toLowerCase(); const nameFallback = (f.name || '').toLowerCase().replace(/\s+/g, '_'); const fieldName = mappedName || nameFallback; let matchKey = Object.keys(fullRow).find(k => k.toLowerCase() === mappedName || k.toLowerCase() === nameFallback); // Special handling for route_name -> distributor details if (!matchKey) { matchKey = Object.keys(fullRow).find(k => fieldName === `distributor_${k.toLowerCase()}` || (k.toLowerCase() === 'phone' && fieldName === 'distributor_phone_number') || (k.toLowerCase() === 'email' && fieldName === 'distributor_email') ); } if (matchKey && fullRow[matchKey] !== undefined && fullRow[matchKey] !== null) { if (matchKey.toLowerCase() === 'owner_name' && fieldName === 'owner_name') { return; } if (matchKey.toLowerCase() === 'email' && fieldName === 'email') { return; } if (matchKey.toLowerCase() === 'phone' && (fieldName === 'phone' || fieldName === 'phone_number')) { return; } let fillVal = fullRow[matchKey]; // If the target field is a select, try to match option value case-insensitively if (f.data_type === 'select' && f.properties?.options) { const matchedOpt = f.properties.options.find((o: any) => String(o.value).toLowerCase() === String(fillVal).toLowerCase()); if (matchedOpt) { fillVal = matchedOpt.value; } } next[f.id] = fillVal; } } }); } } // Auto-calculate order_details totals const getBaseIdForField = (id: string) => id.replace(/_\d+$/, ''); const fieldDef = schema?.fields.find(f => f.id === fieldId); const isOrderDetails = fieldId === 'order_details' || getBaseIdForField(fieldId) === 'order_details' || fieldDef?.mapped_workflow_field === 'order_details'; if (isOrderDetails && Array.isArray(newVal)) { let totalBags = 0; let totalKgs = 0; const getBaseId = (id: string) => id.replace(/_\d+$/, ''); const bagsColId = fieldDef?.columns?.find(c => c.id === 'bags' || getBaseId(c.id) === 'bags' || c.mapped_workflow_field === 'bags' || c.name.toLowerCase() === 'bags' )?.id || 'bags'; const rowKgsColId = fieldDef?.columns?.find(c => c.id === 'row_kgs' || getBaseId(c.id) === 'row_kgs' || c.id === 'rowkgs' || getBaseId(c.id) === 'rowkgs' || c.mapped_workflow_field === 'row_kgs' )?.id || 'row_kgs'; newVal.forEach(row => { const bags = Number(row[bagsColId] ?? row.bags ?? row.quantity) || 0; const sku = Number(row.sku) || 0; const rowKgs = Number(row[rowKgsColId] ?? row.row_kgs) || (bags * sku); totalBags += bags; totalKgs += rowKgs; }); const tbField = schema?.fields.find(f => f.id === 'total_bags' || getBaseId(f.id) === 'total_bags' || f.mapped_workflow_field === 'total_bags'); const tkField = schema?.fields.find(f => f.id === 'total_kgs' || getBaseId(f.id) === 'total_kgs' || f.mapped_workflow_field === 'total_kgs'); if (tbField) next[tbField.id] = totalBags; else next['total_bags'] = totalBags; if (tkField) next[tkField.id] = totalKgs; else next['total_kgs'] = totalKgs; } return next; }); }; if (loading) { return
; } if (error || !schema) { return
Failed to load form: {error}
; } // Filter out disabled fields (usually server-generated IDs) const fields = schema.fields.filter(f => !f.properties?.disabled); const actionField = fields.find(f => f.name.toLowerCase() === 'action' && f.data_type === 'radio'); const normalFields = fields.filter(f => f !== actionField); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setShowErrors(true); setSubmitError(null); const finalValues = { ...values }; if (actionField && clickedActionRef.current) { finalValues[actionField.id] = clickedActionRef.current; } // 1. Pre-submit consolidation for grid fields & total fields normalFields.forEach((f) => { if (f.data_type.startsWith('grid')) { const rawGrid = (finalValues[f.id] as Record[]) || []; if (rawGrid.length > 0) { const merged: Record[] = []; const indexMap = new Map(); const getGridVal = (obj: Record, searchTokens: string[], excludeTokens: string[] = []) => { for (const k of Object.keys(obj)) { const lowerK = k.toLowerCase(); if (searchTokens.some(t => lowerK.includes(t)) && !excludeTokens.some(t => lowerK.includes(t))) { return obj[k]; } } return undefined; }; for (const r of rawGrid) { const cat = String(getGridVal(r, ['category', 'cat']) || '').trim().toLowerCase(); const prod = String(getGridVal(r, ['product', 'name'], ['category', 'cat']) || '').trim().toLowerCase(); const currentBags = Number(getGridVal(r, ['bags', 'quantity'])) || 0; const currentSku = Number(getGridVal(r, ['sku'], ['code'])) || 0; if (cat && prod) { const key = `${cat}::${prod}`; if (indexMap.has(key) && currentBags > 0) { const targetIdx = indexMap.get(key)!; const existingRow = merged[targetIdx]; const existingBags = Number(getGridVal(existingRow, ['bags', 'quantity'])) || 0; const addedBags = currentBags; const newBags = existingBags + addedBags; const skuVal = Number(getGridVal(existingRow, ['sku'], ['code']) || currentSku || 0); merged[targetIdx] = { ...existingRow, bags: newBags, quantity: newBags, row_kgs: skuVal * newBags, }; continue; } else if (!indexMap.has(key)) { indexMap.set(key, merged.length); } } merged.push(r); } // Clean virtual keys const cleanGrid = merged.map((r) => { const clean: Record = {}; Object.keys(r).forEach((k) => { if (k !== '_row_total_kgs' && k !== 'row_kgs' && k !== 'row_total') { clean[k] = r[k]; } }); return clean; }); finalValues[f.id] = cleanGrid; // Recalculate total_bags and total_kgs let totalB = 0; let totalK = 0; cleanGrid.forEach((r) => { const b = Number(getGridVal(r, ['bags', 'quantity'])) || 0; const sku = Number(getGridVal(r, ['sku'], ['code'])) || 0; totalB += b; totalK += b * sku; }); const getBaseId = (id: string) => id.replace(/_\d+$/, ''); normalFields.forEach((tf) => { const baseId = getBaseId(tf.id); const mapped = (tf.mapped_workflow_field || '').toLowerCase(); if (baseId === 'total_bags' || mapped === 'total_bags' || tf.id === 'total_bags') { finalValues[tf.id] = Math.round(totalB); } if (baseId === 'total_kgs' || mapped === 'total_kgs' || tf.id === 'total_kgs') { finalValues[tf.id] = Math.round(totalK); } }); } } }); // 2. Validate grid fields (Order Details) const extractGridRowDetails = (r: Record, columns?: FormScreenField[]) => { let cat = String(r.category || r.product_category || r.cat || '').trim(); let prod = String(r.productName || r.product_name || r.product || r.name || '').trim(); let bagsNum = 0; if (r.bags !== undefined && r.bags !== null && r.bags !== '') bagsNum = Number(r.bags); else if (r.quantity !== undefined && r.quantity !== null && r.quantity !== '') bagsNum = Number(r.quantity); else if (r.actual_potential !== undefined && r.actual_potential !== null && r.actual_potential !== '') bagsNum = Number(r.actual_potential); else if (r.store_potential !== undefined && r.store_potential !== null && r.store_potential !== '') bagsNum = Number(r.store_potential); if (columns && columns.length > 0) { for (const c of columns) { const idL = c.id.toLowerCase(); const nL = (c.name || '').toLowerCase(); const mL = (c.mapped_workflow_field || '').toLowerCase(); if (!cat && (idL.includes('category') || nL.includes('category') || mL.includes('category'))) { cat = String(r[c.id] || '').trim(); } if (!prod && (idL.includes('product') || nL.includes('product') || mL.includes('product') || idL.includes('name') || nL.includes('name'))) { prod = String(r[c.id] || '').trim(); } if (bagsNum <= 0 && (idL.includes('bags') || nL.includes('bags') || mL.includes('bags') || idL.includes('quantity') || nL.includes('quantity') || idL.includes('potential') || nL.includes('potential'))) { const val = Number(r[c.id]); if (!isNaN(val) && val > 0) bagsNum = val; } } } if (bagsNum <= 0) { for (const k of Object.keys(r)) { const kL = k.toLowerCase(); if ((kL.includes('bags') || kL.includes('quantity') || kL.includes('potential')) && !kL.includes('total') && !kL.includes('kgs')) { const val = Number(r[k]); if (!isNaN(val) && val > 0) { bagsNum = val; break; } } } } return { cat, prod, bags: isNaN(bagsNum) ? 0 : bagsNum }; }; for (const f of normalFields) { if (f.data_type.startsWith('grid')) { if (currentActivityId === 'af8ac8df-d868-4f7d-88b2-123955d69c56') { continue; } const gridRows = (finalValues[f.id] as Record[]) || []; const hasProdCol = f.columns?.some(c => c.id.toLowerCase().includes('product_name') || c.id.toLowerCase() === 'product' || c.id.toLowerCase() === 'name' || (c.name || '').toLowerCase().includes('product name') || (c.name || '').toLowerCase() === 'product' || ((c.name || '').toLowerCase().includes('product') && !(c.name || '').toLowerCase().includes('category')) ); // Check if grid has at least one valid row const validRows = gridRows.filter((r) => { const { cat, prod, bags } = extractGridRowDetails(r, f.columns); if (hasProdCol) { return cat && prod && bags > 0; } return cat && bags > 0; }); if (f.mandatory && validRows.length === 0) { setSubmitError(`Please add at least one complete row for ${f.name}.`); return; } // Check if any row is partially filled const hasIncompleteRow = gridRows.some((r) => { const { cat, prod, bags } = extractGridRowDetails(r, f.columns); if (!hasProdCol) { if (cat && bags <= 0) return true; if (bags > 0 && !cat) return true; return false; } if (!cat && !prod && bags <= 0) return false; if (cat && (!prod || bags <= 0)) return true; if (prod && (!cat || bags <= 0)) return true; if (bags > 0 && (!cat || !prod)) return true; return false; }); if (hasIncompleteRow) { if (!hasProdCol) { setSubmitError(`Please complete all required details (Category and Quantity) for ${f.name} before submitting.`); } else { setSubmitError('Please complete all required product details (Category, Product Name, and Bags) before submitting.'); } return; } } } // 3. Validate mandatory normal fields using finalValues const getBaseId = (id: string) => id.replace(/_\d+$/, ''); for (const f of normalFields) { if (f.mandatory) { if (f.data_type.startsWith('grid')) continue; const baseId = getBaseId(f.id); const mapped = (f.mapped_workflow_field || '').toLowerCase(); if (baseId === 'total_bags' || mapped === 'total_bags' || f.id === 'total_bags' || baseId === 'total_kgs' || mapped === 'total_kgs' || f.id === 'total_kgs') { continue; // Total fields are calculated automatically from grid } const v = finalValues[f.id]; if (v === undefined || v === null || String(v).trim() === '') { setSubmitError(`Please fill in required field: ${f.name}`); return; } } } setSubmitting(true); try { const payload: Record = {}; for (const f of fields) { const val = finalValues[f.id]; const isRemark = (f.name || '').toLowerCase().includes('remark') || (f.name || '').toLowerCase().includes('note'); if (isRemark && (val == null || val === '')) { payload[f.id] = ''; continue; } if (val == null) continue; if (f.data_type === 'phone' && typeof val === 'string') { const phoneNum = val.replace(/^\+91\s*/, '').trim(); payload[f.id] = { dial_code: '+91', phone: phoneNum, phone_with_dial_code: `+91${phoneNum}` }; } else if ((f.data_type === 'image' || f.data_type === 'file') && Array.isArray(val)) { const uploadedFiles = []; for (const file of val) { if (file instanceof File) { const fileMeta = await client.uploadFile(file, { activityId: currentActivityId, fieldId: f.id, instanceId: currentInstanceId }); uploadedFiles.push(fileMeta); } else { uploadedFiles.push(file); } } payload[f.id] = uploadedFiles; } else if ((f.data_type === 'image' || f.data_type === 'file') && val instanceof File) { const fileMeta = await client.uploadFile(val, { activityId: currentActivityId, fieldId: f.id, instanceId: currentInstanceId }); payload[f.id] = [fileMeta]; } else if ((f.data_type === 'image' || f.data_type === 'file') && val && typeof val === 'object') { payload[f.id] = [val]; } else if (f.data_type.startsWith('grid') || f.data_type === 'smart_grid') { const gridRows = Array.isArray(val) ? val : []; const getFormattedGridVal = (colKey: string, rawVal: unknown, colDef?: FormScreenField) => { if (rawVal === undefined || rawVal === null || String(rawVal).trim() === '') return undefined; const keyLower = colKey.toLowerCase(); const colNameLower = (colDef?.name || '').toLowerCase(); const dataType = colDef?.data_type; // 1. Explicit number data_type from form screen API if (dataType === 'number') { const num = Number(rawVal); return !isNaN(num) ? num : rawVal; } // 2. Explicit string/select/id_gen data_type from API or SKU/code identifier columns const isStringCol = dataType === 'select' || dataType === 'text' || dataType === 'string' || dataType === 'id_gen' || keyLower.includes('sku') || keyLower.includes('code') || keyLower.includes('br_code') || keyLower === 'br' || colNameLower.includes('sku') || colNameLower.includes('code'); if (isStringCol) { return String(rawVal); } // 3. Bags / Quantity / Row Kgs quantity columns without explicit text colDef const isNumericQuantity = keyLower === 'bags' || keyLower === 'quantity' || keyLower === 'row_kgs' || colNameLower === 'bags' || colNameLower === 'quantity'; if (isNumericQuantity) { const num = Number(rawVal); return !isNaN(num) ? num : rawVal; } return rawVal; }; const cleanRows = gridRows .filter(r => { return Object.keys(r).some(k => { const v = r[k]; return v !== undefined && v !== null && String(v).trim() !== ''; }); }) .map(r => { const formattedRow: Record = {}; // 1. Process all existing keys in row r for (const [k, v] of Object.entries(r)) { if (v === undefined || v === null || String(v).trim() === '') continue; const colDef = f.columns?.find(c => c.id === k || (c.name || '').toLowerCase() === k.toLowerCase() || c.mapped_workflow_field === k ); const formattedVal = getFormattedGridVal(k, v, colDef); if (formattedVal !== undefined) { formattedRow[k] = formattedVal; if (colDef && colDef.id && colDef.id !== k) { formattedRow[colDef.id] = formattedVal; } } } // 2. Also map any schema defined columns from f.columns if (f.columns && f.columns.length > 0) { for (const col of f.columns) { if (formattedRow[col.id] === undefined) { const matchKey = Object.keys(r).find(rk => rk.toLowerCase() === col.id.toLowerCase() || rk.toLowerCase() === (col.name || '').toLowerCase() || (col.mapped_workflow_field && rk.toLowerCase() === col.mapped_workflow_field.toLowerCase()) || (isCategoryColumn(col.id, col.name) && (rk.toLowerCase().includes('category') || rk === 'cat')) || (isProductColumn(col.id, col.name) && (rk.toLowerCase().includes('product') || rk === 'productName')) || (col.name?.toLowerCase().includes('bags') && (rk.toLowerCase().includes('bags') || rk.toLowerCase().includes('quantity'))) ); if (matchKey && r[matchKey] !== undefined && r[matchKey] !== null && String(r[matchKey]).trim() !== '') { const formattedVal = getFormattedGridVal(matchKey, r[matchKey], col); if (formattedVal !== undefined) { formattedRow[col.id] = formattedVal; } } } } } return formattedRow; }); payload[f.id] = cleanRows; } else if (f.data_type === 'number') { if (val !== undefined && val !== null && val !== '') { const num = Number(val); payload[f.id] = !isNaN(num) ? num : val; } else { payload[f.id] = val; } } else { payload[f.id] = val; } } let res; if (currentInstanceId != null) { res = await client.performActivity(currentInstanceId, currentActivityId, payload); } else { res = await client.startInstance(currentActivityId, payload); } setHasSubmitted(true); let pending = [...chainQueue]; let chainSource = (res as any).activity_chain || schema.activity_chain || []; // Centralized activity chaining logic if (currentActivityId === ORDER_BOOKING.activities.PRODUCTIVITY_OF_VISIT.uid) { const actionValue = String(payload[ORDER_BOOKING.activities.PRODUCTIVITY_OF_VISIT.fields.action] || '').toLowerCase().trim(); const normalized = actionValue.replace(/[\s_]+/g, ''); if (normalized === 'order') { chainSource = [{ activity_uid: ORDER_BOOKING.activities.PLACE_ORDER.uid, activity_name: 'Place Order' }]; } else if (normalized === 'noorder') { chainSource = [{ activity_uid: ORDER_BOOKING.activities.POTENTIAL_MINING.uid, activity_name: 'Potential Mining' }]; } } const newActivities = chainSource.filter( (a: any) => a.activity_uid !== currentActivityId ); // Remove any existing occurrences from pending to avoid duplicates pending = pending.filter(p => !newActivities.some((n: any) => n.activity_uid === p.activity_uid)); // Prepend the new activities for depth-first execution (nested chaining) pending = [...newActivities, ...pending]; const nextActivity = pending.shift(); if (nextActivity) { let nextPrefillData = undefined; if (nextActivity.activity_uid === ORDER_BOOKING.activities.POTENTIAL_MINING.uid) { let storeCodeToSend = String((values['select_store_row'] as any)?.store_code || (chainedPrefillData?.['select_store_row'] as any)?.store_code || ''); if (!storeCodeToSend) { let storeId = values['select_store'] || chainedPrefillData?.['select_store'] || (schema?.prefill_data as any)?.select_store || (schema?.data as any)?.select_store; if (typeof storeId === 'object' && storeId !== null) { storeCodeToSend = (storeId as any).store_code || storeCodeToSend; storeId = (storeId as any).value || (storeId as any).instance_id || String(storeId); } if (!storeCodeToSend && storeId) { try { const detailRes = await client.detailView(STORE.detailViews.STORE, storeId); if (detailRes.data && detailRes.data.store_code) { storeCodeToSend = String(detailRes.data.store_code); } } catch(e) { try { const lookupRes = await client.wfLookupRecords({ activityId: ORDER_BOOKING.activities.LOG_VISIT.uid, fieldId: ORDER_BOOKING.activities.LOG_VISIT.fields.selectStore, formData: { ...(chainedPrefillData as any || {}), ...values, ...(schema?.prefill_data as any || {}) }, limit: 500 }); const arr = Array.isArray(lookupRes) ? lookupRes : (lookupRes.data || lookupRes.records || []); const row = arr.find((r: any) => String(r.instance_id || r.id) === String(storeId)); if (row && row.store_code) { storeCodeToSend = String(row.store_code); } } catch (err) { console.error("Failed to fetch store code:", err); } } } } if (!storeCodeToSend) { storeCodeToSend = String(values['select_store'] || chainedPrefillData?.['select_store'] || (schema?.prefill_data as any)?.select_store || ''); } try { const pmRes = await client.request<{ potential: { potential: any[] } }>( 'POST', '/api/papi2/potential-mining', { instance_id: String(res.instance_id ?? currentInstanceId), store_code: storeCodeToSend }, { 'TemplateID': '146' } ); const rawPotential = pmRes.potential?.potential || []; const mappedPotential = rawPotential.map((row: any) => { const cat = row.product_category || row.product_category_ || row.category; return { ...row, product_category_: cat, product_category: cat, productcategory: cat, category: cat, product_category_1: cat }; }); nextPrefillData = { potential: mappedPotential }; } catch (e) { console.error("Failed to fetch potential mining for chain", e); } } setChainedPrefillData(prev => ({ ...prev, ...values, ...(nextPrefillData || {}) })); setChainQueue(pending); setCurrentActivityId(nextActivity.activity_uid); setCurrentInstanceId(res.instance_id ?? currentInstanceId); onActivityChange?.(nextActivity.activity_name); } else { onSuccess?.(); } } catch (err: any) { setSubmitError(err.message || 'Failed to submit form'); } finally { setSubmitting(false); } }; const getBaseIdForField = (id: string) => id.replace(/_\d+$/, ''); const hasGridField = normalFields.some(f => f.data_type.startsWith('grid')); const tbField = normalFields.find(f => f.id === 'total_bags' || getBaseIdForField(f.id) === 'total_bags' || f.mapped_workflow_field === 'total_bags'); const tkField = normalFields.find(f => f.id === 'total_kgs' || getBaseIdForField(f.id) === 'total_kgs' || f.mapped_workflow_field === 'total_kgs'); const displayFields = normalFields.filter(f => { if (hasGridField) { const baseId = getBaseIdForField(f.id); const mapped = (f.mapped_workflow_field || '').toLowerCase(); const isTotalField = baseId === 'total_bags' || baseId === 'total_kgs' || mapped === 'total_bags' || mapped === 'total_kgs' || f.id === 'total_bags' || f.id === 'total_kgs'; if (isTotalField) return false; } if (HIDDEN_FORM_FIELDS.includes(f.id) || HIDDEN_FORM_FIELDS.includes(f.name)) { return false; } return true; }); return (
{displayFields.map(f => { const type = f.data_type; const val = values[f.id]; const isDisabled = schema.field_defaults?.[f.id]?.disabled; const renderField = () => { if (type === 'wf_lookup') { return ( handleFieldChange(f.id, newVal, fullRow)} /> ); } if (type.startsWith('select') || type.startsWith('multiselect')) { return ( handleFieldChange(f.id, newVal, fullRow)} /> ); } if (type === 'radio') { return ( handleFieldChange(f.id, newVal)} /> ); } if (type.startsWith('image') || type.startsWith('file')) { return ( handleFieldChange(f.id, newVal)} /> ); } if (type.startsWith('grid')) { const isPotentialMiningGrid = currentActivityId === 'af8ac8df-d868-4f7d-88b2-123955d69c56'; return ( []) || []} onChange={(newVal) => handleFieldChange(f.id, newVal)} totalBagsValue={tbField ? Number(values[tbField.id]) || 0 : undefined} totalKgsValue={tkField ? Number(values[tkField.id]) || 0 : undefined} showErrors={showErrors} disableAddRow={isPotentialMiningGrid} hideTotal={isPotentialMiningGrid} readOnlyCols={isPotentialMiningGrid ? ['product_category_', 'actual_potential', 'total_ordered', 'difference'] : []} isPotentialMining={isPotentialMiningGrid} /> ); } if (type.startsWith('geolocation')) { return ( handleFieldChange(f.id, newVal)} /> ); } if (type.startsWith('phone')) { const phoneStr = typeof val === 'object' && val !== null ? (val as any).phone || '' : (val as string) ?? ''; return ( handleFieldChange(f.id, newVal)} /> ); } if (type.startsWith('email')) { return ( handleFieldChange(f.id, newVal)} /> ); } if (type.startsWith('textarea') || type.startsWith('text_area') || type.startsWith('longtext')) { return ( handleFieldChange(f.id, newVal)} /> ); } if (type.startsWith('date')) { return ( handleFieldChange(f.id, newVal)} /> ); } if (type.startsWith('time')) { return ( handleFieldChange(f.id, newVal)} /> ); } const isValEmpty = val === undefined || val === null || String(val).trim() === ''; const currentFieldError = showErrors && f.mandatory && isValEmpty ? 'Required' : undefined; let displayVal = (val as string) ?? ''; if (schema.field_defaults?.[f.id]?.prefill?.value === 'current_user_id') { const user = client.currentUser(); if (user && String(val) === String(user.id)) { displayVal = user.name; } } return ( handleFieldChange(f.id, newVal)} error={currentFieldError} /> ); }; const content = renderField(); return isDisabled ? (
{content}
) : (
{content}
); })} {submitError &&
{submitError}
}
{onCancel && ( )} {actionField && actionField.properties?.options ? ( actionField.properties.options.map((opt: any) => ( )) ) : ( )}
); }