diff --git a/JeMPI_Apps/JeMPI_UI/src/hooks/useAppConfig.tsx b/JeMPI_Apps/JeMPI_UI/src/hooks/useAppConfig.tsx index 3493eac0f..054891cb7 100644 --- a/JeMPI_Apps/JeMPI_UI/src/hooks/useAppConfig.tsx +++ b/JeMPI_Apps/JeMPI_UI/src/hooks/useAppConfig.tsx @@ -39,25 +39,23 @@ export const AppConfigProvider = ({ refetchOnWindowFocus: false }) const availableFields: DisplayField[] = useMemo(() => { - return (fields || []) - .filter(({ scope }) => - scope.some(path => { - return matchPath( - { - path: path - }, - location.pathname - ) - }) - ) - .map(field => { - return { + try { + if (!fields || !Array.isArray(fields)) return [] + + return fields + .filter(({ scope }) => + scope?.some(path => matchPath({ path }, location.pathname)) + ) + .map(field => ({ ...field, formatValue: getFieldValueFormatter(field.fieldType), isValid: (value: unknown) => isInputValid(value, field?.validation), getValue: valueGetter - } - }) + })) + } catch (error) { + console.error('Error processing available fields:', error) + return [] + } // eslint-disable-next-line react-hooks/exhaustive-deps }, [fields, location]) diff --git a/JeMPI_Apps/JeMPI_UI/src/pages/settings/Settings.tsx b/JeMPI_Apps/JeMPI_UI/src/pages/settings/Settings.tsx index 431e4c1c1..ee678105f 100644 --- a/JeMPI_Apps/JeMPI_UI/src/pages/settings/Settings.tsx +++ b/JeMPI_Apps/JeMPI_UI/src/pages/settings/Settings.tsx @@ -15,9 +15,11 @@ import { generateId } from 'utils/helpers' import Probabilistic from './probabilistic/Probabilistic' import { useConfig } from 'hooks/useConfig' import { useSnackbar } from 'notistack' +import { useConfiguration } from 'hooks/useUIConfiguration' const Settings = () => { const [value, setValue] = useState(0) + const {configuration} = useConfiguration() const [configurationData, setConfigurationData] = useState(() => { const storedData = localStorage.getItem('configuration') return storedData @@ -106,12 +108,12 @@ const Settings = () => { variant="scrollable" > - + {/* - - - - + */} + + + @@ -120,31 +122,31 @@ const Settings = () => { - + {/* Setup properties that are unique to the golden record - - + */} + {/* Setup properties that are unique to the interaction - - + */} + {/* Setup properties for Golden record lists - - + */} + - + - + diff --git a/JeMPI_Apps/JeMPI_UI/src/pages/settings/common/Common.tsx b/JeMPI_Apps/JeMPI_UI/src/pages/settings/common/Common.tsx index 055ad8a30..5a4eb94eb 100644 --- a/JeMPI_Apps/JeMPI_UI/src/pages/settings/common/Common.tsx +++ b/JeMPI_Apps/JeMPI_UI/src/pages/settings/common/Common.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from 'react' -import Box from '@mui/material/Box' +import { useEffect, useState } from 'react'; +import Box from '@mui/material/Box'; import { DataGrid, GridColDef, @@ -17,49 +17,67 @@ import CancelIcon from '@mui/icons-material/Close' import { EditToolbar } from 'components/shared/EditToolBar' import { processIndex, toSnakeCase, transformFieldName } from 'utils/helpers' import { useConfiguration } from 'hooks/useUIConfiguration' -import { Configuration, LinkMetaData } from 'types/Configuration' +import { Configuration, Field, LinkMetaData } from 'types/Configuration' +import { IconButton, Tooltip, Switch } from '@mui/material' import { RowData } from '../deterministic/SourceView' +import { useSnackbar } from 'notistack' +import FieldDialog from './FieldDialog' +import AddIcon from '@mui/icons-material/Add' const CommonSettings = () => { const [rows, setRows] = useState([]) const { configuration, setConfiguration } = useConfiguration() const [rowModesModel, setRowModesModel] = useState({}) + const [openFieldModal, setOpenFieldModal] = useState(false) + const { enqueueSnackbar } = useSnackbar() useEffect(() => { - if (configuration && configuration.demographicFields) { - const rowData = configuration?.demographicFields.map( + if (configuration?.demographicFields) { + const rowData = configuration.demographicFields.map( (row: any, rowIndex: number) => ({ id: rowIndex + 1, ...row, - rowIndex + rowIndex, + disable: row.isDisabled }) - ) + ); setRows(rowData) } }, [configuration]) const handleEditClick = (id: GridRowId) => () => { setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } }) - } + }; const handleSaveClick = (id: GridRowId) => () => { setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }) - } + }; const handleUpdateConfiguration = (updatedRow: any, rowIndex: number) => { - if (!configuration) return - const updatedConfiguration = getUpdatedConfiguration( - updatedRow, - rowIndex, - configuration - ) - localStorage.setItem('configuration', JSON.stringify(updatedConfiguration)) - setConfiguration(updatedConfiguration) - setRows((prevRows: any) => - prevRows.map((row: any) => - row.id === updatedRow.id ? { ...updatedRow } : row + if (!configuration) { + console.error("Cannot update configuration. Configuration is null.") + return; + } + + const prevConfiguration = { ...configuration } + try { + const updatedConfiguration = getUpdatedConfiguration( + updatedRow, + rowIndex, + configuration ) - ) + localStorage.setItem('configuration', JSON.stringify(updatedConfiguration)) + setConfiguration(updatedConfiguration) + setRows((prevRows: any) => + prevRows.map((row: any) => + row.id === updatedRow.id ? { ...updatedRow } : row + ) + ) + } catch (error) { + console.error("Error updating configuration:", error); + setConfiguration(prevConfiguration) + enqueueSnackbar("Failed to update configuration. Please try again.", { variant: 'error' }) + } } const getUpdatedConfiguration = ( @@ -67,71 +85,162 @@ const CommonSettings = () => { rowIndex: number, currentConfiguration: Configuration ): Configuration => { - const newConfiguration = { ...currentConfiguration } + const newConfiguration = { ...currentConfiguration } const fieldName = toSnakeCase(updatedRow.fieldName) + if (!newConfiguration.demographicFields) { - return currentConfiguration + return currentConfiguration; } - const fieldToUpdate = { ...newConfiguration.demographicFields[rowIndex] } + const fieldToUpdate = { ...newConfiguration.demographicFields[rowIndex] }; if (!fieldToUpdate) { - return currentConfiguration + return currentConfiguration; } - fieldToUpdate.fieldName = fieldName + fieldToUpdate.fieldName = fieldName; if (updatedRow?.indexGoldenRecord) { - fieldToUpdate.indexGoldenRecord = `@index(${updatedRow.indexGoldenRecord.replace( - ' ', - '' - )})` + if (!updatedRow.indexGoldenRecord.startsWith('@index(')) { + fieldToUpdate.indexGoldenRecord = `@index(${updatedRow.indexGoldenRecord.replace( + ' ', + '' + )})` + } else { + fieldToUpdate.indexGoldenRecord = updatedRow.indexGoldenRecord + } } if (updatedRow?.m) { fieldToUpdate.linkMetaData = { ...fieldToUpdate.linkMetaData, m: Number(updatedRow.m) - } as LinkMetaData + } as LinkMetaData; } if (updatedRow?.u) { fieldToUpdate.linkMetaData = { ...fieldToUpdate.linkMetaData, u: Number(updatedRow.u) - } as LinkMetaData + } as LinkMetaData; } - newConfiguration.demographicFields[rowIndex] = fieldToUpdate + newConfiguration.demographicFields[rowIndex] = fieldToUpdate; - return newConfiguration - } + return newConfiguration; + }; const handleCancelClick = (id: GridRowId) => () => { setRowModesModel(prevRowModesModel => { - const newRowModesModel = { ...prevRowModesModel } - delete newRowModesModel[id] - return newRowModesModel - }) - } + const newRowModesModel = { ...prevRowModesModel }; + delete newRowModesModel[id]; + return newRowModesModel; + }); + }; const handleRowEditStop: GridEventListener<'rowEditStop'> = ({ reason }) => - reason === GridRowEditStopReasons.rowFocusOut + reason === GridRowEditStopReasons.rowFocusOut; const handleRowModesModelChange = (newRowModesModel: GridRowModesModel) => { - setRowModesModel(newRowModesModel) - } + setRowModesModel(newRowModesModel); + }; const processRowUpdate = (newRow: GridRowModel) => { - const { id, ...updatedRow } = newRow + const isFieldNameValid = newRow?.fieldName && newRow.fieldName.toLowerCase() !== 'unknown_field'; + + if (!isFieldNameValid) { + enqueueSnackbar('Field name cannot be empty or unknown', { variant: 'error' }); + } + + const { id, ...updatedRow } = newRow; const updatedRows = rows.map((row: { id: any }) => row.id === id ? ({ ...updatedRow, id } as RowData) : row ) setRows(updatedRows) + + if (updatedRow?.disable !== undefined) { + const rowIndex = updatedRow.rowIndex + + const updatedConfiguration: Configuration = { + ...configuration, + auxInteractionFields: configuration?.auxInteractionFields || [], + auxGoldenRecordFields: configuration?.auxGoldenRecordFields || [], + additionalNodes: configuration?.additionalNodes || [], + demographicFields: configuration?.demographicFields || [], + rules: configuration?.rules || { + link: { + deterministic: [], + probabilistic: [] + }, + validate: { + deterministic: [], + probabilistic: [] + }, + matchNotification: { + deterministic: [], + probabilistic: [] + } + } + } + + if ( + updatedConfiguration.demographicFields && + updatedConfiguration.demographicFields[rowIndex] + ) { + updatedConfiguration.demographicFields[rowIndex].isDisabled = + updatedRow.disable + localStorage.setItem( + 'configuration', + JSON.stringify(updatedConfiguration) + ) + + setConfiguration(updatedConfiguration) + } + } + handleUpdateConfiguration(updatedRow, updatedRow.rowIndex) return { ...updatedRow, id } as RowData } + const handleSwitchChange = ( + event: React.ChangeEvent, + params: any + ) => { + const updatedRow = { ...params.row, disable: event.target.checked } + processRowUpdate(updatedRow) + } + + const handleAddNewRow = (fieldName: string) => { + const newRow: Field = { + id: (rows.length + 1).toString(), + fieldName, + fieldType: 'String', + linkMetaData: { + comparison: '', + comparisonLevels: [], + m: 0, + u: 0 + }, + }; + + if (configuration) { + const newConfiguration = { + ...configuration, + demographicFields: [...configuration.demographicFields, newRow] + } + + localStorage.setItem('configuration', JSON.stringify(newConfiguration)) + setConfiguration(newConfiguration) + setRows((prevRows: any) => [...prevRows, newRow]) + setRowModesModel(prevRowModesModel => ({ + ...prevRowModesModel, + [(newRow.id) as string]: { mode: GridRowModes.Edit, fieldToFocus: 'fieldName' } + })) + } else { + console.error("Configuration is null. Cannot add new row.") + } + } + const columns: GridColDef[] = [ { field: 'fieldName', @@ -149,7 +258,7 @@ const CommonSettings = () => { width: 180, align: 'center', headerAlign: 'center', - editable: false + editable: true, }, { field: 'indexGoldenRecord', @@ -160,8 +269,8 @@ const CommonSettings = () => { headerAlign: 'center', editable: true, valueGetter: params => { - const indexGoldenRecord = params.row.indexGoldenRecord - return processIndex(indexGoldenRecord) + const indexGoldenRecord = params.row.indexGoldenRecord; + return processIndex(indexGoldenRecord); } }, { @@ -173,11 +282,11 @@ const CommonSettings = () => { align: 'center', headerAlign: 'center', valueGetter: params => { - const linkMetaData = params.row.linkMetaData + const linkMetaData = params.row.linkMetaData; if (linkMetaData && typeof linkMetaData.m === 'number') { - return linkMetaData.m.toFixed(7) + return linkMetaData.m.toFixed(7); } - return + return; } }, { @@ -189,12 +298,13 @@ const CommonSettings = () => { align: 'center', headerAlign: 'center', valueGetter: params => { - const linkMetaData = params.row.linkMetaData + const linkMetaData = params.row.linkMetaData; if (linkMetaData && typeof linkMetaData.u === 'number') { - return linkMetaData.u.toFixed(7) + return linkMetaData.u.toFixed(7); } } }, + { field: 'actions', type: 'actions', @@ -204,17 +314,19 @@ const CommonSettings = () => { width: 300, cellClassName: 'actions', getActions: ({ id }) => { - const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + const row = rows.find((row: any) => row.id === id); + const isFieldNameValid = row?.fieldName && row.fieldName.toLowerCase() !== 'unknown_field'; + if (isInEditMode) { return [ } id="save-button" label="Save" - sx={{ - color: 'white' - }} + sx={{ color: 'white' }} onClick={handleSaveClick(id)} + disabled={!isFieldNameValid} />, } @@ -223,10 +335,11 @@ const CommonSettings = () => { className="textPrimary" onClick={handleCancelClick(id)} color="inherit" + disabled={!isFieldNameValid} /> - ] + ]; } - + return [ } @@ -236,10 +349,25 @@ const CommonSettings = () => { onClick={handleEditClick(id)} color="inherit" /> - ] + ]; + } + }, + { + field: 'disable', + headerName: '', + width: 90, + align: 'center', + headerAlign: 'center', + renderCell: params => { + return ( + handleSwitchChange(event, params)} + /> + ) } } - ] + ]; return ( { width: '100%' }} > + + + setOpenFieldModal(true)}> + + + + + {configuration && ( { onRowModesModelChange={handleRowModesModelChange} processRowUpdate={processRowUpdate} onRowEditStop={handleRowEditStop} - getRowId={row => row.id} + onProcessRowUpdateError={(error) => { + console.error(error); + }} + getRowId={(row) => row.id} slots={{ toolbar: EditToolbar }} @@ -267,7 +410,7 @@ const CommonSettings = () => { /> )} - ) -} + ); +}; -export default CommonSettings +export default CommonSettings; diff --git a/JeMPI_Apps/JeMPI_UI/src/pages/settings/common/FieldDialog.tsx b/JeMPI_Apps/JeMPI_UI/src/pages/settings/common/FieldDialog.tsx new file mode 100644 index 000000000..20064cf9b --- /dev/null +++ b/JeMPI_Apps/JeMPI_UI/src/pages/settings/common/FieldDialog.tsx @@ -0,0 +1,61 @@ +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; +import { useState } from 'react'; +import { DialogContentText } from '@mui/material'; + +interface FieldDialogProps { + openFieldModal: boolean; + setOpenFieldModal: React.Dispatch>; + onSave: (fieldName: string) => void; +} + +export default function FieldDialog({ openFieldModal, setOpenFieldModal, onSave }: FieldDialogProps) { + const [fieldName, setFieldName] = useState(''); + + const handleClose = () => { + setOpenFieldModal(false); + }; + + const handleSave = () => { + onSave(fieldName); + handleClose(); + }; + + return ( + + Add Field Name + + + To add a new field to common settings, please enter a field name. + + setFieldName(e.target.value)} + /> + + + + + + + ); +} diff --git a/JeMPI_Apps/JeMPI_UI/src/pages/settings/deterministic/DeterministicContent.tsx b/JeMPI_Apps/JeMPI_UI/src/pages/settings/deterministic/DeterministicContent.tsx index c6a7306a3..38ca0a504 100644 --- a/JeMPI_Apps/JeMPI_UI/src/pages/settings/deterministic/DeterministicContent.tsx +++ b/JeMPI_Apps/JeMPI_UI/src/pages/settings/deterministic/DeterministicContent.tsx @@ -31,7 +31,8 @@ export const options = [ { value: 0, label: 'Exact' }, { value: 1, label: 'Low Fuzziness' }, { value: 2, label: 'Medium Fuzziness' }, - { value: 3, label: 'High Fuzziness' } + { value: 3, label: 'High Fuzziness' }, + { value: 4, label: 'Null' } ] export enum Operator { @@ -125,20 +126,29 @@ const DeterministicContent = ({ if (row.rowIndex !== undefined) { setEditedRowIndex(row.rowIndex) } - - const regex = /(eq|match)\(([^),]+)(?:, (\d+))?\)/g + const regex = /(eq|match|null)\(([^),]+)(?:, (\d+))?\)/g const matchedFields: string[] = [] const matchedComparators: number[] = [] let match while ((match = regex.exec(row.ruleText)) !== null) { matchedFields.push(match[2]) - matchedComparators.push(match[1] === 'eq' ? 0 : parseInt(match[3], 10)) + + if (match[1] === 'eq') { + matchedComparators.push(0) + } else if (match[1] === 'match') { + matchedComparators.push(parseInt(match[3], 10)) + } else if (match[1] === 'null') { + matchedComparators.push(4) + } } setComparators(matchedComparators) setFields(matchedFields) - setOperators(new Array(matchedFields.length - 1).fill(Operator.AND)) + + const operatorsLength = Math.max(matchedFields.length - 1, 0) + setOperators(new Array(operatorsLength).fill(Operator.AND)) + setViewType(1) } @@ -163,12 +173,20 @@ const DeterministicContent = ({ const operator = index > 0 ? ` ${operators[index - 1].toLowerCase()} ` : '' const comparator = comparators[index] - const comparatorFunction = - comparator === 0 ? `eq(${field})` : `match(${field},${comparator})` + let comparatorFunction + + if (comparator === 4) { + comparatorFunction = `null(${field})` + } else { + comparatorFunction = + comparator === 0 ? `eq(${field})` : `match(${field},${comparator})` + } + return `${operator}${comparatorFunction}` }) .join('') + console.log('rule', vars, text) const rule: Rule = { vars, text } handleUpdateConfiguration(rule) @@ -228,37 +246,40 @@ const DeterministicContent = ({ const handleDeleteRow = (index: number) => { const updateArray = (arr: any[], idx: number) => { - const newArr = [...arr]; - newArr.splice(idx, 1); - return newArr; - }; - - setComparators(updateArray(comparators, index)); - setFields(updateArray(fields, index)); + const newArr = [...arr] + newArr.splice(idx, 1) + return newArr + } + + setComparators(updateArray(comparators, index)) + setFields(updateArray(fields, index)) if (index > 0) { - setOperators(updateArray(operators, index - 1)); + setOperators(updateArray(operators, index - 1)) } - - const updatedConfiguration = { ...configuration }; + + const updatedConfiguration = { ...configuration } const ruleType = currentTab === 'link' ? 'link' : currentTab === 'validate' ? 'validate' - : 'matchNotification'; - + : 'matchNotification' + if (fields.length === 0) { - const newRules = updateArray(rules, index); - setRules(newRules); - + const newRules = updateArray(rules, index) + setRules(newRules) + if (updatedConfiguration.rules?.[ruleType]) { - updatedConfiguration.rules[ruleType].deterministic.splice(index, 1); + updatedConfiguration.rules[ruleType].deterministic.splice(index, 1) } - - setConfiguration(updatedConfiguration as Configuration); - localStorage.setItem('configuration', JSON.stringify(updatedConfiguration)); + + setConfiguration(updatedConfiguration as Configuration) + localStorage.setItem( + 'configuration', + JSON.stringify(updatedConfiguration) + ) } - }; + } return ( ( diff --git a/JeMPI_Apps/JeMPI_UI/src/services/mockData.ts b/JeMPI_Apps/JeMPI_UI/src/services/mockData.ts index 7db14d49f..63af64b11 100644 --- a/JeMPI_Apps/JeMPI_UI/src/services/mockData.ts +++ b/JeMPI_Apps/JeMPI_UI/src/services/mockData.ts @@ -399,6 +399,7 @@ const configuration = { source: { csvCol: 1 }, + isDisabled: false, indexGoldenRecord: '@index(exact,trigram)', indexInteraction: '@index(exact,trigram)', linkMetaData: { @@ -414,6 +415,7 @@ const configuration = { source: { csvCol: 2 }, + isDisabled: false, indexGoldenRecord: '@index(exact,trigram)', indexInteraction: '@index(exact,trigram)', linkMetaData: { @@ -429,6 +431,7 @@ const configuration = { source: { csvCol: 3 }, + isDisabled: false, indexGoldenRecord: '@index(exact,trigram)', linkMetaData: { comparison: 'JARO_WINKLER_SIMILARITY', @@ -443,6 +446,7 @@ const configuration = { source: { csvCol: 4 }, + isDisabled: false, linkMetaData: { comparison: 'JARO_WINKLER_SIMILARITY', comparisonLevels: [0.92], @@ -456,6 +460,7 @@ const configuration = { source: { csvCol: 5 }, + isDisabled: false, indexGoldenRecord: '@index(trigram)', linkMetaData: { comparison: 'JARO_WINKLER_SIMILARITY', @@ -470,6 +475,7 @@ const configuration = { source: { csvCol: 6 }, + isDisabled: false, indexGoldenRecord: '@index(exact,trigram)', linkMetaData: { comparison: 'JARO_WINKLER_SIMILARITY', @@ -484,6 +490,7 @@ const configuration = { source: { csvCol: 7 }, + isDisabled: false, indexGoldenRecord: '@index(exact,trigram)', indexInteraction: '@index(exact,trigram)', linkMetaData: { diff --git a/JeMPI_Apps/JeMPI_UI/src/test/settings/BlockingContent.test.tsx b/JeMPI_Apps/JeMPI_UI/src/test/settings/BlockingContent.test.tsx index 907424402..e80c8322d 100644 --- a/JeMPI_Apps/JeMPI_UI/src/test/settings/BlockingContent.test.tsx +++ b/JeMPI_Apps/JeMPI_UI/src/test/settings/BlockingContent.test.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import '@testing-library/jest-dom/extend-expect' +import '@testing-library/jest-dom' import { BrowserRouter } from 'react-router-dom' import BlockingContent from 'pages/settings/blocking/BlockingContent' import mockData from 'services/mockData' diff --git a/JeMPI_Apps/JeMPI_UI/src/test/settings/CommonSettings.test.tsx b/JeMPI_Apps/JeMPI_UI/src/test/settings/CommonSettings.test.tsx index 5700c7a4d..da5d5f889 100644 --- a/JeMPI_Apps/JeMPI_UI/src/test/settings/CommonSettings.test.tsx +++ b/JeMPI_Apps/JeMPI_UI/src/test/settings/CommonSettings.test.tsx @@ -5,12 +5,21 @@ import CommonSettings from 'pages/settings/common/Common'; import mockData from 'services/mockData'; import '@testing-library/jest-dom'; import { useConfiguration } from 'hooks/useUIConfiguration'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ConfigProvider } from 'hooks/useConfig'; +import { BrowserRouter } from 'react-router-dom'; jest.mock('hooks/useUIConfiguration', () => ({ useConfiguration: jest.fn(), })); +const queryClient = new QueryClient({ + defaultOptions: { + queries: {} + } +}) + describe('CommonSettings Component', () => { const configData = mockData.configuration.demographicFields.map((row, index) => ({ ...row, @@ -23,12 +32,27 @@ describe('CommonSettings Component', () => { }); }); it('renders without crashing', () => { - render(); + render( + + + + + + ); }); it('handles edit mode', async () => { - render(); + render( + + + + + + + + ) + const editIcon = document.getElementById('edit-button'); const saveButton = document.getElementById('save-button'); const cancelButton = document.getElementById('cancel-button'); diff --git a/JeMPI_Apps/JeMPI_UI/src/test/settings/Probabilistic.test.tsx b/JeMPI_Apps/JeMPI_UI/src/test/settings/Probabilistic.test.tsx index 2902731fd..16d4bc7b0 100644 --- a/JeMPI_Apps/JeMPI_UI/src/test/settings/Probabilistic.test.tsx +++ b/JeMPI_Apps/JeMPI_UI/src/test/settings/Probabilistic.test.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import '@testing-library/jest-dom/extend-expect' +import '@testing-library/jest-dom' import { BrowserRouter } from 'react-router-dom' import mockData from 'services/mockData' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' diff --git a/JeMPI_Apps/JeMPI_UI/src/test/settings/SourceView.test.tsx b/JeMPI_Apps/JeMPI_UI/src/test/settings/SourceView.test.tsx index 1fbdce823..45103c724 100644 --- a/JeMPI_Apps/JeMPI_UI/src/test/settings/SourceView.test.tsx +++ b/JeMPI_Apps/JeMPI_UI/src/test/settings/SourceView.test.tsx @@ -1,5 +1,5 @@ import { render, fireEvent } from '@testing-library/react' -import '@testing-library/jest-dom/extend-expect' +import '@testing-library/jest-dom' import SourceView, { RowData } from 'pages/settings/deterministic/SourceView' describe('SourceView Component', () => { diff --git a/JeMPI_Apps/JeMPI_UI/src/types/Configuration.ts b/JeMPI_Apps/JeMPI_UI/src/types/Configuration.ts index 49754b893..c11596016 100644 --- a/JeMPI_Apps/JeMPI_UI/src/types/Configuration.ts +++ b/JeMPI_Apps/JeMPI_UI/src/types/Configuration.ts @@ -21,6 +21,7 @@ export interface Field { indexGoldenRecord?: string; indexInteraction?: string; linkMetaData?: LinkMetaData; + isDisabled?: boolean; }