diff --git a/api/src/controllers/projects.contentMapper.controller.ts b/api/src/controllers/projects.contentMapper.controller.ts index 956e3638d..fd1000aec 100644 --- a/api/src/controllers/projects.contentMapper.controller.ts +++ b/api/src/controllers/projects.contentMapper.controller.ts @@ -48,6 +48,22 @@ const getExistingContentTypes = async ( const resp = await contentMapperService.getExistingContentTypes(req); res.status(201).json(resp); }; + +/** + * Retrieves the existing global fields. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the operation is complete. + */ +const getExistingGlobalFields = async ( + req: Request, + res: Response +): Promise => { + const resp = await contentMapperService.getExistingGlobalFields(req); + res.status(201).json(resp); +}; + /** * Updates the content type fields. * @@ -131,5 +147,6 @@ export const contentMapperController = { // removeMapping, getSingleContentTypes, removeContentMapper, - updateContentMapper + updateContentMapper, + getExistingGlobalFields }; diff --git a/api/src/routes/contentMapper.routes.ts b/api/src/routes/contentMapper.routes.ts index a50282d66..6b8fed79f 100644 --- a/api/src/routes/contentMapper.routes.ts +++ b/api/src/routes/contentMapper.routes.ts @@ -40,6 +40,15 @@ router.get( asyncRouter(contentMapperController.getExistingContentTypes) ); +/** + * Get Existing GlobalFields List + * @route GET /:projectId + */ +router.get( + "/globalFields/:projectId", + asyncRouter(contentMapperController.getExistingGlobalFields) +); + /** * Update FieldMapping or contentType * @route PUT /contentTypes/:orgId/:projectId/:contentTypeId diff --git a/api/src/services/contentMapper.service.ts b/api/src/services/contentMapper.service.ts index e138ccc93..2146509c8 100644 --- a/api/src/services/contentMapper.service.ts +++ b/api/src/services/contentMapper.service.ts @@ -268,7 +268,6 @@ const getExistingContentTypes = async (req: Request) => { data: err.response.data, status: err.response.status, }; - const contentTypes = res.data.content_types.map((singleCT: any) => { return { title: singleCT.title, @@ -280,6 +279,57 @@ const getExistingContentTypes = async (req: Request) => { //Add logic to get Project from DB return { contentTypes }; }; + +/** + * Retrieves existing global fields for a given project. + * @param req - The request object containing the project ID and token payload. + * @returns An object containing the retrieved content types. + */ +const getExistingGlobalFields = async (req: Request) => { + const projectId = req?.params?.projectId; + + const { token_payload } = req.body; + + const authtoken = await getAuthtoken( + token_payload?.region, + token_payload?.user_id + ); + await ProjectModelLowdb.read(); + const project = ProjectModelLowdb.chain + .get("projects") + .find({ id: projectId }) + .value(); + const stackId = project?.destination_stack_id; + const [err, res] = await safePromise( + https({ + method: "GET", + url: `${config.CS_API[ + token_payload?.region as keyof typeof config.CS_API + ]!}/global_fields`, + headers: { + api_key: stackId, + authtoken: authtoken, + }, + }) + ); + + if (err) + return { + data: err.response.data, + status: err.response.status, + }; + const globalFields = res.data.global_fields.map((global: any) => { + return { + title: global.title, + uid: global.uid, + schema: global.schema, + }; + }); + + //Add logic to get Project from DB + return { globalFields }; +}; + /** * Updates the content type based on the provided request. * @param req - The request object containing the necessary parameters and data. @@ -923,8 +973,6 @@ const removeContentMapper = async (req: Request) => { * @throws ExceptionFunction if an error occurs during the update. */ const updateContentMapper = async (req: Request) => { - console.info("updateContentMapper", req.params, req.body); - const { orgId, projectId } = req.params; const { token_payload, content_mapper } = req.body; const srcFunc = "updateContentMapper"; @@ -944,7 +992,6 @@ const updateContentMapper = async (req: Request) => { try { ProjectModelLowdb.update((data: any) => { - // console.info("data ===============", data, content_mapper) data.projects[projectIndex].mapperKeys = content_mapper; data.projects[projectIndex].updated_at = new Date().toISOString(); }); @@ -989,5 +1036,6 @@ export const contentMapperService = { removeContentMapper, removeMapping, getSingleContentTypes, - updateContentMapper + updateContentMapper, + getExistingGlobalFields }; diff --git a/ui/src/components/ContentMapper/index.tsx b/ui/src/components/ContentMapper/index.tsx index bd0a3b03c..099312958 100644 --- a/ui/src/components/ContentMapper/index.tsx +++ b/ui/src/components/ContentMapper/index.tsx @@ -21,6 +21,7 @@ import { getContentTypes, getFieldMapping, getExistingContentTypes, + getExistingGlobalFields, updateContentType, resetToInitialMapping, fetchExistingContentType, @@ -226,6 +227,8 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re }= {} } = migrationData; + // const contentTypesList = awau + const [tableData, setTableData] = useState([]); const [loading, setLoading] = useState(false); const [isLoading, setIsLoading] = useState(newMigrationData?.isprojectMapped); @@ -237,7 +240,13 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re const [contentTypes, setContentTypes] = useState([]); const [otherCmsTitle, setOtherCmsTitle] = useState(contentTypes[0]?.otherCmsTitle); const [contentTypeUid, setContentTypeUid] = useState(''); - const [contentTypesList, setContentTypesList] = useState([]); + + const [existingContentTypes, setExistingContentTypes] = useState([]); + const [existingGlobalFields, setExistingGlobalFields] = useState([]) + const [isContentType, setIsContentType] = useState(contentTypes?.[0]?.type === "content_type"); + const [contentModels, setContentModels] = useState([]); + + const [selectedContentType, setSelectedContentType] = useState(); const [existingField, setExistingField] = useState({}); const [selectedOptions, setSelectedOptions] = useState([]); @@ -246,8 +255,8 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re newMigrationData?.content_mapping?.content_type_mapping || {} ); const [otherContentType, setOtherContentType] = useState({ - label: contentTypeMapped?.[otherCmsTitle] || 'Select content type from existing stack', - value: contentTypeMapped?.[otherCmsTitle] || 'Select content type from existing stack', + label: contentTypeMapped?.[otherCmsTitle] || `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, + value: contentTypeMapped?.[otherCmsTitle] || `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, }); const [otherCmsUid, setOtherCmsUid] = useState(contentTypes[0]?.otherCmsUid); const [isContentTypeMapped, setIsContentTypeMapped] = useState(false); @@ -284,6 +293,7 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re const deletedExstingField : ExistingFieldType= existingField; const isNewStack = newMigrationData?.stackDetails?.isNewStack; const [isFieldDeleted, setIsFieldDeleted] = useState(false); + const [isContentDeleted, setIsContentDeleted] = useState(false); /** ALL HOOKS Here */ const { projectId = '' } = useParams(); @@ -309,8 +319,9 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re console.error(err); }); - fetchExistingContentTypes(); fetchContentTypes(searchText || ''); + fetchExistingContentTypes(); + fetchExistingGlobalFields(); }, []); // Make title and url field non editable @@ -323,13 +334,46 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re },[tableData]); useEffect(() => { - if (contentTypeMapped && otherCmsTitle) { - setOtherContentType({ - label: contentTypeMapped?.[otherCmsTitle] ?? 'Select content type from existing stack', - value: contentTypeMapped?.[otherCmsTitle] ?? 'Select content type from existing stack' - }); + const mappedContentType = contentModels && contentModels?.find((item)=>item?.title === contentTypeMapped?.[otherCmsTitle]); + + if (contentTypeMapped && otherCmsTitle ) { + + if (mappedContentType?.uid) { + setOtherContentType({ + id: mappedContentType?.uid, + label: contentTypeMapped?.[otherCmsTitle], + value: contentTypeMapped?.[otherCmsTitle], + }); + setIsContentDeleted(false); + } else { + + setOtherContentType({ + label: `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, + value: `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack` + }); + + } + } - }, [contentTypeMapped, otherCmsTitle]); + + }, [contentTypeMapped, otherCmsTitle, contentModels]); + + + + useEffect(()=>{ + if(isContentDeleted){ + setContentTypeMapped((prevState: ContentTypeMap) => { + const { [otherCmsTitle]: removed, ...newState } = prevState; + + return newState; + }); + + setIsFieldDeleted(false); + } + + + },[isContentDeleted, contentModels, otherCmsTitle]) + useEffect(() => { if (contentTypeMapped[otherCmsTitle] === otherContentType?.label) { @@ -392,6 +436,15 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re setRowIds(selectedId); }, [tableData]); + // To fetch existing content types or global fields as per the type + useEffect(() => { + if(isContentType) { + setContentModels(existingContentTypes); + } else { + setContentModels(existingGlobalFields); + } + }, [existingContentTypes, existingGlobalFields, isContentType]) + // To close the filter panel on outside click useEffect(() => { document.addEventListener('click', handleClickOutside, true); @@ -453,6 +506,7 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re setContentTypeUid(data?.contentTypes?.[0]?.id); fetchFields(data?.contentTypes?.[0]?.id, searchText || ''); setOtherCmsUid(data?.contentTypes?.[0]?.otherCmsUid); + setIsContentType(data?.contentTypes?.[0]?.type === "content_type"); }; // Method to search content types @@ -503,7 +557,7 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re // Fetch table data const fetchData = async ({ searchText }: TableTypes) => { setSearchText(searchText) - fetchFields(contentTypeUid, searchText); + contentTypeUid && fetchFields(contentTypeUid, searchText); }; // Method for Load more table data @@ -567,25 +621,45 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re const otherTitle = contentTypes?.[i]?.otherCmsTitle; setOtherCmsTitle(otherTitle); - setOtherContentType({ - label: contentTypeMapped?.[otherTitle] || 'Select content type from existing stack', - value: contentTypeMapped?.[otherTitle] || 'Select content type from existing stack' - }); + // setOtherContentType({ + // label: contentTypeMapped?.[otherTitle] || 'Select content type from existing stack', + // value: contentTypeMapped?.[otherTitle] || 'Select content type from existing stack' + // }); setContentTypeUid(contentTypes?.[i]?.id ?? ''); fetchFields(contentTypes?.[i]?.id ?? '', searchText || ''); setOtherCmsUid(contentTypes?.[i]?.otherCmsUid); setSelectedContentType(contentTypes?.[i]); + setIsContentType(contentTypes?.[i]?.type === "content_type"); } // Function to get exisiting content types list const fetchExistingContentTypes = async () => { const { data, status } = await getExistingContentTypes(projectId); if (status === 201) { - setContentTypesList(data?.contentTypes); + setExistingContentTypes(data?.contentTypes); + const mappedContentType = data?.contentTypes && data?.contentTypes?.find((item:ContentTypeList)=>item?.title === contentTypeMapped?.[otherCmsTitle]); + + if (mappedContentType?.uid) { + setOtherContentType({ + id: mappedContentType?.uid, + label: contentTypeMapped?.[otherCmsTitle], + value: contentTypeMapped?.[otherCmsTitle], + }); + setIsContentDeleted(false); + } } }; + // Function to get exisiting global fields list + const fetchExistingGlobalFields = async () => { + const { data, status } = await getExistingGlobalFields(projectId); + + if (status === 201) { + setExistingGlobalFields(data?.globalFields); + } + } + const updateFieldSettings = (rowId: string, updatedSettings: Advanced, checkBoxChanged: boolean) => { setIsDropDownChanged(checkBoxChanged); @@ -1162,9 +1236,9 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re const OptionsForRow: OptionsType[] = []; - // If OtherContentType label and contentTypesList are present, set the contentTypeSchema - if (otherContentType?.label && contentTypesList) { - const ContentType: ContentTypeList | undefined = contentTypesList?.find( + // If OtherContentType label and contentModels are present, set the contentTypeSchema + if (otherContentType?.label && contentModels) { + const ContentType: ContentTypeList | undefined = contentModels?.find( ({ title }) => title === otherContentType?.label ); setContentTypeSchema(ContentType?.schema); @@ -1245,6 +1319,7 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re ...updatedExstingField, [data?.uid]: { label: newLabel, value: newvalue } }; + existingField[data?.uid] = { label: newLabel, value: newvalue } } const newValue: string = OptionsForRow[0]?.value?.display_name; @@ -1417,13 +1492,13 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re setIsContentTypeMapped(true); setIsContentTypeSaved(true); - // const newMigrationDataObj: INewMigration = { - // ...newMigrationData, - // content_mapping: { ...newMigrationData?.content_mapping, isDropDownChanged: false } - // }; + const newMigrationDataObj: INewMigration = { + ...newMigrationData, + content_mapping: { ...newMigrationData?.content_mapping, isDropDownChanged: false } + }; - // dispatch(updateNewMigrationData((newMigrationDataObj))); + dispatch(updateNewMigrationData((newMigrationDataObj))); const savedCT = filteredContentTypes?.map(ct => ct?.id === data?.data?.updatedContentType?.id ? { ...ct, status: data?.data?.updatedContentType?.status } : ct @@ -1487,6 +1562,15 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re fieldMapping: updatedRows } }; + let newstate = {} ; + setContentTypeMapped((prevState: ContentTypeMap) => { + const newState = { ...prevState }; + + delete newState[otherCmsTitle]; + newstate = newState; + + return newState; + }); if (orgId && selectedContentType) { const { status } = await resetToInitialMapping( orgId, @@ -1494,10 +1578,13 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re selectedContentType?.id ?? '', dataCs ); + setExistingField({}); setContentTypeSchema([]); - setContentTypeMapped({}); + if (status == 200) { + await updateContentMapper(orgId, projectID, {...newstate} ); + Notification({ notificationContent: { text: 'Content type reset successfully' }, notificationProps: { @@ -1512,6 +1599,50 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re // Function to fetch single content type const handleFetchContentType = async () => { + const { data , status} = await fetchExistingContentType(projectId,'') ; + if(data?.contentTypes?.length <= 0){ + Notification({ + notificationContent: { text: "No content found in the stack" }, + notificationProps: { + position: 'bottom-center', + hideProgressBar: false + }, + type: 'error' + }); + } + const contentTypesArr: ContentTypeList[] = contentModels; + const index = contentModels.findIndex(ct => ct?.uid === data?.uid); + + if(index != -1) { + contentTypesArr[index] = data; + } + + setContentModels(data?.contentTypes); + + + const content_type = data?.contentTypes?.find((item:any)=>item?.title === otherContentType?.label); + const contentTypeKey = Object.keys(contentTypeMapped).find(key => contentTypeMapped[key] === otherContentType?.label); + + + if(! content_type && contentTypeKey){ + const updatedState = { ...contentTypeMapped }; + delete updatedState[contentTypeKey]; + + setContentTypeMapped((prevState: ContentTypeMap) => { + const newState = { ...prevState }; + + delete newState[contentTypeKey] + + return newState; + }); + await updateContentMapper(selectedOrganisation?.value, projectId, {... updatedState} ); + setOtherContentType({ + label: `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, + value: `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack` + + }); + } + if (otherContentType?.label === "Select Content Type") { Notification({ notificationContent: { text: "Please Select a Content Type to fetch." }, @@ -1524,19 +1655,19 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re } else { const { data , status} = await fetchExistingContentType(projectId, otherContentType?.id ?? ''); - const index = contentTypesList.findIndex(ct => ct?.uid === data?.uid); + const index = contentModels.findIndex(ct => ct?.uid === data?.uid); - const contentTypesArr: ContentTypeList[] = contentTypesList; + const contentTypesArr: ContentTypeList[] = contentModels; if(index != -1) { contentTypesArr[index] = data; } - setContentTypesList(contentTypesArr); + //setContentTypesList(contentTypesArr); setContentTypeSchema(data?.schema); if (status == 201) { Notification({ - notificationContent: { text: 'Content type fetched successfully' }, + notificationContent: { text: 'Content type data fetched successfully' }, notificationProps: { position: 'bottom-center', hideProgressBar: false @@ -1560,7 +1691,7 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re } ]; - const isOtherContentType = contentTypesList?.some((ct) => ct?.title === otherContentType?.label); + const isOtherContentType = contentModels && contentModels?.some((ct) => ct?.title === otherContentType?.label); if (!isNewStack) { columns?.push({ @@ -1581,7 +1712,7 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re }); } - const options = contentTypesList?.map((item) => ({ + const options = contentModels?.map((item) => ({ label: item?.title, value: item?.title, id: item?.uid, @@ -1806,7 +1937,7 @@ const ContentMapper = forwardRef(({projectData}: ContentMapperComponentProps, re options={adjustedOption} width="440px" maxWidth="440px" - placeholder={otherContentType && 'Select content type from existing stack'} + placeholder={otherContentType && `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`} version="v2" /> diff --git a/ui/src/components/LegacyCms/Actions/LoadFileFormat.tsx b/ui/src/components/LegacyCms/Actions/LoadFileFormat.tsx index ee8354109..6cfab9018 100644 --- a/ui/src/components/LegacyCms/Actions/LoadFileFormat.tsx +++ b/ui/src/components/LegacyCms/Actions/LoadFileFormat.tsx @@ -60,9 +60,6 @@ const LoadFileFormat = (props: LoadFileFormatProps) => { isFileFormatCheckboxChecked: isCheckedBoxChecked } })); - await fileformatConfirmation(selectedOrganisation?.value, projectId, { - fileformat_confirmation: true - }); //call for Step Change props.handleStepChange(props?.currentStep); @@ -138,16 +135,21 @@ const LoadFileFormat = (props: LoadFileFormatProps) => { return (
- } - /> + + {isError &&

{error}

}
diff --git a/ui/src/components/LegacyCms/Actions/LoadPrefix.tsx b/ui/src/components/LegacyCms/Actions/LoadPrefix.tsx index b01125a3c..deb0d31f3 100644 --- a/ui/src/components/LegacyCms/Actions/LoadPrefix.tsx +++ b/ui/src/components/LegacyCms/Actions/LoadPrefix.tsx @@ -118,6 +118,7 @@ const LoadPreFix = (props: LoadSelectCmsProps) => { placeholder={'Enter Affix'} version="v2" error={isError} + aria-label='affix' /> {isError &&

{errorMessage}

} diff --git a/ui/src/components/Stepper/HorizontalStepper/HorizontalStepper.tsx b/ui/src/components/Stepper/HorizontalStepper/HorizontalStepper.tsx index 4424acf0d..92a5d024f 100644 --- a/ui/src/components/Stepper/HorizontalStepper/HorizontalStepper.tsx +++ b/ui/src/components/Stepper/HorizontalStepper/HorizontalStepper.tsx @@ -77,13 +77,15 @@ const showNotification = (currentIndex:number) =>{ } const HorizontalStepper = forwardRef( (props: stepperProps, ref: React.ForwardedRef) => { + + const { stepId } = useParams<{ stepId: string }>(); + const stepIndex = parseInt(stepId || '', 10) - 1; + const { steps, className, emptyStateMsg, hideTabView, testId } = props; - const [showStep, setShowStep] = useState(0); + const [showStep, setShowStep] = useState(stepIndex); const [stepsCompleted, setStepsCompleted] = useState([]); const [isModalOpen, setIsModalOpen] = useState(false); - const { stepId } = useParams<{ stepId: string }>(); - const navigate = useNavigate(); const { projectId = '' } = useParams(); @@ -156,7 +158,8 @@ const HorizontalStepper = forwardRef( notificationContent: { text: `Please select a stack to proceed further` }, type: 'warning' }) - } else if (newMigrationData?.destination_stack?.selectedStack?.value !== newMigrationData?.stackDetails?.value) { + } + else if (newMigrationData?.destination_stack?.selectedStack?.value !== newMigrationData?.stackDetails?.value) { return Notification({ notificationContent: { text: `Please save the stack to proceed further` }, type: 'warning' diff --git a/ui/src/context/app/app.interface.ts b/ui/src/context/app/app.interface.ts index da29be635..7f82fc8e5 100644 --- a/ui/src/context/app/app.interface.ts +++ b/ui/src/context/app/app.interface.ts @@ -5,6 +5,7 @@ import { import { ICardType, defaultCardType } from '../../components/Common/Card/card.interface'; import { CTA } from '../../types/common.interface'; import { IFilterType } from '../../components/Common/Modal/FilterModal/filterModal.interface'; +import { ContentTypeList } from '../../components/ContentMapper/contentMapper.interface'; export interface ICTA { title: string; href: string; @@ -170,6 +171,7 @@ export interface IContentMapper { content_type_mapping: ContentTypeMap; isDropDownChanged?: boolean; otherCmsTitle?: string; + contentTypeList:ContentTypeList[] } export interface INewMigration { mapperKeys: ContentTypeMap; @@ -307,7 +309,8 @@ export const DEFAULT_DESTINATION_STACK: IDestinationStack = { export const DEFAULT_CONTENT_MAPPER: IContentMapper = { content_type_mapping: {}, isDropDownChanged: false, - otherCmsTitle: '' + otherCmsTitle: '', + contentTypeList: [], }; export const DEFAULT_TEST_MIGRATION: ITestMigration = { diff --git a/ui/src/services/api/migration.service.ts b/ui/src/services/api/migration.service.ts index 71dadafe9..d46a4b536 100644 --- a/ui/src/services/api/migration.service.ts +++ b/ui/src/services/api/migration.service.ts @@ -164,6 +164,18 @@ export const getExistingContentTypes = async (projectId: string) => { } }; +export const getExistingGlobalFields = async (projectId: string) => { + try { + return await getCall(`${API_VERSION}/mapper/globalFields/${projectId}`, options); + } catch (error) { + if (error instanceof Error) { + throw new Error(`${error.message}`); + } else { + throw new Error('Unknown error'); + } + } +}; + export const updateContentType = async ( orgId: string, projectId: string,