diff --git a/api/src/services/contentMapper.service.ts b/api/src/services/contentMapper.service.ts index bef1b1a5f..13692542a 100644 --- a/api/src/services/contentMapper.service.ts +++ b/api/src/services/contentMapper.service.ts @@ -751,7 +751,7 @@ const resetToInitialMapping = async (req: Request) => { const fieldMappingData = contentTypeData.fieldMapping.map((itemId: any) => { const fieldData = FieldMapperModel.chain .get("field_mapper") - .find({ id: itemId, projectId: projectId }) + .find({ id: itemId, projectId: projectId, contentTypeId: contentTypeId}) .value(); return fieldData; }); @@ -771,7 +771,7 @@ const resetToInitialMapping = async (req: Request) => { //await FieldMapperModel.read(); (fieldMappingData || []).forEach((field: any) => { const fieldIndex = FieldMapperModel.data.field_mapper.findIndex( - (f: any) => f?.id === field?.id + (f: any) => f?.id === field?.id && f?.projectId === projectId && f?.contentTypeId === contentTypeId ); if (fieldIndex > -1) { FieldMapperModel.update((data: any) => { diff --git a/api/src/services/sitecore.service.ts b/api/src/services/sitecore.service.ts index 3b3775078..7247dff20 100644 --- a/api/src/services/sitecore.service.ts +++ b/api/src/services/sitecore.service.ts @@ -105,16 +105,31 @@ async function writeFiles( console.error('Error writing files:', error); } } +const uidCorrector = ({ uid } :{uid : string}) => { + if (!uid || typeof uid !== 'string') { + return ''; + } + + let newUid = uid; + + // Note: UIDs starting with numbers and restricted keywords are handled externally in Sitecore + // The prefix is applied in contentTypeMaker function when needed -const uidCorrector = ({ uid }: any) => { - if (startsWithNumber(uid)) { - return `${append}_${_.replace( - uid, - new RegExp('[ -]', 'g'), - '_' - )?.toLowerCase()}`; + // Clean up the UID + newUid = newUid + .replace(/[ -]/g, '_') // Replace spaces and hyphens with underscores + .replace(/[^a-zA-Z0-9_]+/g, '_') // Replace non-alphanumeric characters (except underscore) + .replace(/([A-Z])/g, (match) => `_${match.toLowerCase()}`) // Handle camelCase + .toLowerCase() // Convert to lowercase + .replace(/_+/g, '_') // Replace multiple underscores with single + .replace(/^_|_$/g, ''); // Remove leading/trailing underscores + + // Ensure UID doesn't start with underscore (Contentstack requirement) + if (newUid.startsWith('_')) { + newUid = newUid.substring(1); } - return _.replace(uid, new RegExp('[ -]', 'g'), '_')?.toLowerCase(); + + return newUid; }; const createAssets = async ({ diff --git a/api/src/utils/content-type-creator.utils.ts b/api/src/utils/content-type-creator.utils.ts index 60ed055ce..9bc51d83a 100644 --- a/api/src/utils/content-type-creator.utils.ts +++ b/api/src/utils/content-type-creator.utils.ts @@ -74,12 +74,32 @@ function startsWithNumber(str: string) { return /^\d/.test(str); } -const uidCorrector = ({ uid }: any) => { - if (startsWithNumber(uid)) { - return `a_${_.replace(uid, new RegExp("[ -]", "g"), '_')?.toLowerCase()}` +const uidCorrector = ({ uid } : {uid : string}) => { + if (!uid || typeof uid !== 'string') { + return ''; } - return _.replace(uid, new RegExp("[ -]", "g"), '_')?.toLowerCase() -} + + let newUid = uid; + + // Note: UIDs starting with numbers and restricted keywords are handled externally in Sitecore + // The prefix is applied in contentTypeMaker function when needed + + // Clean up the UID + newUid = newUid + .replace(/[ -]/g, '_') // Replace spaces and hyphens with underscores + .replace(/[^a-zA-Z0-9_]+/g, '_') // Replace non-alphanumeric characters (except underscore) + .replace(/([A-Z])/g, (match) => `_${match.toLowerCase()}`) // Handle camelCase + .toLowerCase() // Convert to lowercase + .replace(/_+/g, '_') // Replace multiple underscores with single + .replace(/^_|_$/g, ''); // Remove leading/trailing underscores + + // Ensure UID doesn't start with underscore (Contentstack requirement) + if (newUid.startsWith('_')) { + newUid = newUid.substring(1); + } + + return newUid; +}; function buildFieldSchema(item: any, marketPlacePath: string, parentUid = ''): any { @@ -577,8 +597,8 @@ const convertToSchemaFormate = ({ field, advanced = false, marketPlacePath, keyM "field_metadata": { description: "", "default_value": { - "title": "", - "url": '', + "title": field?.advanced?.title ?? '', + "url": field?.advanced?.url ?? '', } }, "format": field?.advanced?.validationRegex ?? '', diff --git a/ui/src/cmsData/legacyCms.json b/ui/src/cmsData/legacyCms.json index 38c23d588..c5c4c327e 100644 --- a/ui/src/cmsData/legacyCms.json +++ b/ui/src/cmsData/legacyCms.json @@ -338,7 +338,7 @@ "uid": "cs6c761d71844ac800" }, "title": "Add Source Affix", - "description": "Add a 2–5 character affix for the source name. Use only letters, no numbers or special characters", + "description": "Add a 2–5 letter-only affix for the source name. The affix will function as a prefix if the content type UID matches with restricted UIDs. The affix will function as a suffix for field UIDs. If left blank, \"cs\" will be used by default. Avoid numbers, special characters, and restricted keywords", "step_lock_text": "Editing this step is currently disabled. To make changes in Draft mode, please deactivate the migration", "lock": false, "active": false, diff --git a/ui/src/components/ContentMapper/index.scss b/ui/src/components/ContentMapper/index.scss index f56ea9682..37eb6355b 100644 --- a/ui/src/components/ContentMapper/index.scss +++ b/ui/src/components/ContentMapper/index.scss @@ -151,10 +151,8 @@ } .Table { border-left: 0 none; - // min-height: inherit; + min-height: 26.25rem; .Table__body__row { - // height: auto!important; - // min-height: 80px; .Table-select-body { >.checkbox-wrapper { align-items: flex-start; diff --git a/ui/src/components/ContentMapper/index.tsx b/ui/src/components/ContentMapper/index.tsx index 7fd2d8d4a..71dbd7b3b 100644 --- a/ui/src/components/ContentMapper/index.tsx +++ b/ui/src/components/ContentMapper/index.tsx @@ -760,15 +760,16 @@ const ContentMapper = forwardRef(({ handleStepChange }: contentMapperProps, ref: } setItemStatusMap(itemStatusMap); + setLoading(true); const { data } = await getFieldMapping(contentTypeId || '', 0, 1000, searchText || '', projectId); - for (let index = 0; index <= 1000; index++) { itemStatusMap[index] = 'loaded'; } setItemStatusMap({ ...itemStatusMap }); + setLoading(false); const validTableData = data?.fieldMapping?.filter((field: FieldMapType) => field?.otherCmsType !== undefined); @@ -777,7 +778,6 @@ const ContentMapper = forwardRef(({ handleStepChange }: contentMapperProps, ref: setSelectedEntries(validTableData ?? []); setTotalCounts(validTableData?.length); setInitialRowSelectedData(validTableData?.filter((item: FieldMapType) => !item?.isDeleted)) - setIsLoading(false); generateSourceGroupSchema(validTableData); } catch (error) { console.error('fetchData -> error', error); @@ -811,15 +811,15 @@ const ContentMapper = forwardRef(({ handleStepChange }: contentMapperProps, ref: } setItemStatusMap({ ...updateditemStatusMapCopy }); + setLoading(false); const validTableData = data?.fieldMapping?.filter((field: FieldMapType) => field?.otherCmsType !== undefined); // eslint-disable-next-line no-unsafe-optional-chaining - setTableData([...tableData, ...validTableData ?? tableData]); - setTotalCounts([...tableData, ...validTableData ?? tableData]?.length); - setIsLoading(false); + setTableData(validTableData ?? []); + setSelectedEntries(validTableData ?? []); + setTotalCounts(validTableData?.length); setIsAllCheck(true); - } catch (error) { console.error('loadMoreItems -> error', error); } @@ -2645,20 +2645,6 @@ const ContentMapper = forwardRef(({ handleStepChange }: contentMapperProps, ref: initialRowSelectedData={initialRowSelectedData} initialSelectedRowIds={rowIds} itemSize={80} - v2Features={{ - isNewEmptyState: true - }} - customEmptyState={ - No Fields available} - moduleIcon="NoSearchResult" - description="Try changing the search query to find what you are looking for." - version="v2" - testId="no-results-found-page" - className="custom-empty-state" - /> - } withExportCta={{ component: (
diff --git a/ui/src/components/LegacyCms/Actions/LoadPrefix.tsx b/ui/src/components/LegacyCms/Actions/LoadPrefix.tsx index 0b0a736c2..29369aac5 100644 --- a/ui/src/components/LegacyCms/Actions/LoadPrefix.tsx +++ b/ui/src/components/LegacyCms/Actions/LoadPrefix.tsx @@ -29,7 +29,7 @@ const LoadPreFix = (props: LoadSelectCmsProps) => { const dispatch = useDispatch(); - const [prefix, setPrefix] = useState(newMigrationData?.legacy_cms?.affix || ''); + const [prefix, setPrefix] = useState(newMigrationData?.legacy_cms?.affix || 'cs'); const [isError, setIsError] = useState(false); const [errorMessage, setErrorMessage] = useState(''); @@ -76,7 +76,6 @@ const LoadPreFix = (props: LoadSelectCmsProps) => { isRestictedKeywordCheckboxChecked: isCheckedBoxChecked } }; - dispatch(updateNewMigrationData(newMigrationDataObj)); } else { setPrefix(value); @@ -93,7 +92,6 @@ const LoadPreFix = (props: LoadSelectCmsProps) => { }; dispatch(updateNewMigrationData(newMigrationDataObj)); - setIsError(false); //call for Step Change @@ -101,20 +99,30 @@ const LoadPreFix = (props: LoadSelectCmsProps) => { return; } } else { - setIsError(true); - setErrorMessage('Please enter Affix'); + setIsError(false); + setErrorMessage(''); + setIsRestrictedKey(false); + setPrefix(''); + } + }); + + const handleOnBlur = (value: string) => { + if (isEmptyString(value?.trim())) { + setIsError(false); + setErrorMessage(''); + setIsRestrictedKey(false); + setPrefix('cs'); const newMigrationDataObj: INewMigration = { ...newMigrationData, legacy_cms: { ...newMigrationData?.legacy_cms, - affix: value, + affix: 'cs', isRestictedKeywordCheckboxChecked: isCheckedBoxChecked } }; - dispatch(updateNewMigrationData(newMigrationDataObj)); } - }); + }; /**** ALL USEEffects HERE ****/ @@ -136,6 +144,9 @@ const LoadPreFix = (props: LoadSelectCmsProps) => { aria-label="affix" disabled={newMigrationData?.legacy_cms?.uploadedFile?.isValidated} isReadOnly={newMigrationData?.legacy_cms?.uploadedFile?.isValidated} + onBlur={(e: React.FocusEvent) => { + handleOnBlur(e.target.value); + }} /> {isError &&

{errorMessage}

}
diff --git a/ui/src/components/LegacyCms/Actions/LoadUploadFile.tsx b/ui/src/components/LegacyCms/Actions/LoadUploadFile.tsx index d626a06b1..dfd29427d 100644 --- a/ui/src/components/LegacyCms/Actions/LoadUploadFile.tsx +++ b/ui/src/components/LegacyCms/Actions/LoadUploadFile.tsx @@ -154,7 +154,6 @@ const LoadUploadFile = (props: LoadUploadFileProps) => { setIsDisabled(true); if ( - !isEmptyString(newMigrationData?.legacy_cms?.affix) && !isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) && !isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id) ) { @@ -358,24 +357,23 @@ const LoadUploadFile = (props: LoadUploadFileProps) => { setShowMessage(true); setValidationMessage('File validated successfully.'); setIsDisabled(true); - !isEmptyString(newMigrationData?.legacy_cms?.affix) || + if ( !isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) || - (!isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id) && - props.handleStepChange(props?.currentStep, true)); + !isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id) + ) { + props.handleStepChange(props?.currentStep, true); + } } if (newMigrationData?.legacy_cms?.uploadedFile?.reValidate) { setValidationMessage(''); } - if(!isEmptyString(newMigrationData?.legacy_cms?.affix) && !newMigrationData?.legacy_cms?.uploadedFile?.isValidated && !newMigrationData?.legacy_cms?.uploadedFile?.reValidate){ + if(!newMigrationData?.legacy_cms?.uploadedFile?.isValidated && !newMigrationData?.legacy_cms?.uploadedFile?.reValidate){ setIsDisabled(false); } setReValidate(newMigrationData?.legacy_cms?.uploadedFile?.reValidate || false); - - // else{ - // setIsValidated(false); - // } }, [isValidated, newMigrationData]); + useEffect(() => { if (newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id) { setFileFormat(newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id); @@ -448,7 +446,7 @@ const LoadUploadFile = (props: LoadUploadFileProps) => { isLoading={isLoading} loadingColor="#6c5ce7" version="v2" - disabled={!(reValidate || (!isDisabled && !isEmptyString(newMigrationData?.legacy_cms?.affix)))} + disabled={!(reValidate || (!isDisabled))} > Validate File diff --git a/ui/src/components/LegacyCms/index.tsx b/ui/src/components/LegacyCms/index.tsx index ef300b931..448fab6b1 100644 --- a/ui/src/components/LegacyCms/index.tsx +++ b/ui/src/components/LegacyCms/index.tsx @@ -142,7 +142,7 @@ const LegacyCMSComponent = forwardRef(({ legacyCMSData, isCompleted, handleOnAll } //Make Step 2 complete - if (!isEmptyString(selectedCmsData?.cms_id) && (!isEmptyString(legacyCMSData?.affix) || !isEmptyString(newMigrationData?.legacy_cms?.affix))) { + if (!isEmptyString(selectedCmsData?.cms_id) && (!isEmptyString(legacyCMSData?.affix) )) { setInternalActiveStepIndex(1); } @@ -197,13 +197,12 @@ const LegacyCMSComponent = forwardRef(({ legacyCMSData, isCompleted, handleOnAll //Make Step 2 complete if ( - !isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) && - !isEmptyString(newMigrationData?.legacy_cms?.affix) + !isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) ) { setInternalActiveStepIndex(1); } - if(!isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) && !isEmptyString(newMigrationData?.legacy_cms?.affix) && newMigrationData?.legacy_cms?.uploadedFile?.isValidated){ + if(!isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) && newMigrationData?.legacy_cms?.uploadedFile?.isValidated){ setInternalActiveStepIndex(3); } setisProjectMapped(newMigrationData?.isprojectMapped) @@ -211,8 +210,7 @@ const LegacyCMSComponent = forwardRef(({ legacyCMSData, isCompleted, handleOnAll },[newMigrationData]); useEffect(()=>{ - if(! isEmptyString(newMigrationData?.legacy_cms?.affix) - && !isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.title) && + if( !isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.title) && ! isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.title) && newMigrationData?.legacy_cms?.uploadedFile?.isValidated){ setIsAllStepsCompleted(true); diff --git a/ui/src/components/Stepper/HorizontalStepper/HorizontalStepper.tsx b/ui/src/components/Stepper/HorizontalStepper/HorizontalStepper.tsx index 78db8e418..3a1be8f44 100644 --- a/ui/src/components/Stepper/HorizontalStepper/HorizontalStepper.tsx +++ b/ui/src/components/Stepper/HorizontalStepper/HorizontalStepper.tsx @@ -159,11 +159,6 @@ const HorizontalStepper = forwardRef( shouldCloseOnOverlayClick: false } }); - } else if ( - -1 < newMigrationData?.legacy_cms?.currentStep && - newMigrationData?.legacy_cms?.currentStep < 2 - ) { - showNotification(newMigrationData?.legacy_cms?.currentStep + 1); } else if ( newMigrationData?.destination_stack?.selectedStack === undefined || newMigrationData?.destination_stack?.selectedStack === null || diff --git a/ui/src/components/Stepper/VerticalStepper/AutoVerticalStepper.tsx b/ui/src/components/Stepper/VerticalStepper/AutoVerticalStepper.tsx index 6706bac6a..d9de0f8b1 100644 --- a/ui/src/components/Stepper/VerticalStepper/AutoVerticalStepper.tsx +++ b/ui/src/components/Stepper/VerticalStepper/AutoVerticalStepper.tsx @@ -93,7 +93,12 @@ const AutoVerticalStepper = React.forwardRef< ) : null} */} - {data.description &&
{data.description}
} + {data?.description && ( +
+ )} ); }; diff --git a/ui/src/services/api/upload.service.ts b/ui/src/services/api/upload.service.ts index 851866c5c..c1c9e092b 100644 --- a/ui/src/services/api/upload.service.ts +++ b/ui/src/services/api/upload.service.ts @@ -40,7 +40,7 @@ export const uploadFilePath = () => { return `${UPLOAD_FILE_RELATIVE_URL}upload`; }; -export const fileValidation = async (projectId: string, affix: string) => { +export const fileValidation = async (projectId: string, affix = 'cs') => { try { const options = { headers: { diff --git a/upload-api/migration-sitecore/libs/contenttypes.js b/upload-api/migration-sitecore/libs/contenttypes.js index 7d632d3a7..8802e039b 100644 --- a/upload-api/migration-sitecore/libs/contenttypes.js +++ b/upload-api/migration-sitecore/libs/contenttypes.js @@ -413,7 +413,8 @@ const groupFlat = (data, item) => { contentstackField: item?.meta?.name, contentstackFieldUid: data?.uid, // Use the corrected UID from groupSchema contentstackFieldType: 'group', - backupFieldType: 'group' + backupFieldType: 'group', + backupFieldUid: data?.uid }; flat?.push(group); data?.schema?.forEach((element) => {