diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml index 6bc1f0d3..f7ee79b9 100644 --- a/.github/workflows/repo-sync.yml +++ b/.github/workflows/repo-sync.yml @@ -97,24 +97,84 @@ jobs: git clean -fdX # Remove untracked files and directories git gc --prune=now # Garbage collect and prune unreachable objects - # Remove unused imports and missing file references + # echo "Installing ESLint dependencies..." + npm install --save-dev eslint @eslint/js globals + + + # if [ ! -f "eslint.config.cjs" ]; then + # echo "Creating default ESLint config..." + # cat < eslint.config.cjs + # const js = require("@eslint/js"); + # const globals = require("globals"); + + # module.exports = [ + # js.configs.recommended, + # { + # languageOptions: { + # ecmaVersion: "latest", + # sourceType: "module", + # globals: globals.node, + # }, + # rules: { + # "no-unused-vars": "warn", + # "no-console": "off" + # } + # } + # ]; + # EOL + # fi + + # # Remove unused imports and missing file references # echo "Running ESLint to remove unused imports..." # npm install eslint -g # eslint . --fix - echo "Running autoflake to remove unused imports..." - pip install autoflake - autoflake --remove-all-unused-imports --recursive --in-place . + # echo "Running autoflake to remove unused imports..." + # pip install autoflake + # autoflake --remove-all-unused-imports --recursive --in-place . - echo "Running ts-prune to remove unused file references..." - npm install -g ts-prune - ts-prune | awk '{print $1}' | xargs rm -f + # echo "Running ts-prune to remove unused file references..." + # npm install -g ts-prune + # ts-prune | awk '{print $1}' | xargs rm -f rsync -av --delete ${{ env.RSYNC_SITECORE_API_SRC_SERVICES_EXCLUDES }} ../api/ ./api/ rsync -av --delete ../cli/ ./cli/ rsync -av --delete ../ui/ ./ui/ rsync -av --delete ${{ env.RSYNC_SITECORE_UPLOAD_API_SRC_EXCLUDES }} ../upload-api/src/ ./upload-api/src/ rsync -av --delete ../upload-api/migration-sitecore/ ./upload-api/migration-sitecore/ + + # Ensure tsconfig.json exists, create a default one if missing + # if [ ! -f "tsconfig.json" ]; then + # echo "Creating default tsconfig.json..." + # cat < tsconfig.json + # { + # "compilerOptions": { + # "target": "ES6", + # "module": "CommonJS", + # "strict": true, + # "esModuleInterop": true, + # "skipLibCheck": true, + # "forceConsistentCasingInFileNames": true + # }, + # "include": ["src/**/*"], + # "exclude": ["node_modules", "dist"] + # } + # EOL + # fi + + # Remove unused imports + # npx ts-remove-unused-imports api/ + # npx ts-remove-unused-imports ui/ + # npx ts-remove-unused-imports upload-api/ + + # Remove missing imports + # npx ts-prune | grep -E '^(api/|ui/|upload-api/)/' | awk '{print $1}' | xargs -I {} sed -i '/import/d' {} + + # # Format code + # npx prettier --write . + + # eslint api/ ui/ upload-api/ --rule 'import/no-unresolved: error' --format compact | awk -F ':' '{print $1}' | sort -u | xargs -I {} sed -i '/import/d' {} + git add . git commit -m "Sync changes from migration-v2 PR #${{ github.event.pull_request.number }}" git push origin sync-from-migration-v2-${{ github.event.pull_request.number }} @@ -163,10 +223,37 @@ jobs: git clean -fdX # Remove untracked files and directories git gc --prune=now # Garbage collect and prune unreachable objects + echo "Installing ESLint dependencies..." + npm install --save-dev eslint @eslint/js globals + + if [ ! -f "eslint.config.cjs" ]; then + echo "Creating default ESLint config..." + cat < eslint.config.cjs + const js = require("@eslint/js"); + const globals = require("globals"); + + module.exports = [ + js.configs.recommended, + { + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: globals.node, + }, + rules: { + "no-unused-vars": "warn", + "no-console": "off" + } + } + ]; + EOL + fi + + # Remove unused imports and missing file references - # echo "Running ESLint to remove unused imports..." - # npm install eslint -g - # eslint . --fix + echo "Running ESLint to remove unused imports..." + npm install eslint -g + eslint . --fix echo "Running autoflake to remove unused imports..." pip install autoflake @@ -229,10 +316,37 @@ jobs: git clean -fdX # Remove untracked files and directories git gc --prune=now # Garbage collect and prune unreachable objects + echo "Installing ESLint dependencies..." + npm install --save-dev eslint @eslint/js globals + + if [ ! -f "eslint.config.cjs" ]; then + echo "Creating default ESLint config..." + cat < eslint.config.cjs + const js = require("@eslint/js"); + const globals = require("globals"); + + module.exports = [ + js.configs.recommended, + { + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: globals.node, + }, + rules: { + "no-unused-vars": "warn", + "no-console": "off" + } + } + ]; + EOL + fi + + # Remove unused imports and missing file references - # echo "Running ESLint to remove unused imports..." - # npm install eslint -g - # eslint . --fix + echo "Running ESLint to remove unused imports..." + npm install eslint -g + eslint . --fix echo "Running autoflake to remove unused imports..." pip install autoflake diff --git a/api/src/services/migration.service.ts b/api/src/services/migration.service.ts index 94eaaa07..6147fdd5 100644 --- a/api/src/services/migration.service.ts +++ b/api/src/services/migration.service.ts @@ -230,8 +230,8 @@ const startTestMigration = async (req: Request): Promise => { case CMS.SITECORE_V9: case CMS.SITECORE_V10: { if (packagePath) { - await siteCoreService?.createEntry({ packagePath, contentTypes, master_locale: project?.stackDetails?.master_locale, destinationStackId: project?.current_test_stack_id, projectId, keyMapper: project?.mapperKeys }); - await siteCoreService?.createLocale(req, project?.current_test_stack_id, projectId); + await siteCoreService?.createEntry({ packagePath, contentTypes, master_locale: project?.stackDetails?.master_locale, destinationStackId: project?.current_test_stack_id, projectId, keyMapper: project?.mapperKeys, project }); + await siteCoreService?.createLocale(req, project?.current_test_stack_id, projectId, project); await siteCoreService?.createVersionFile(project?.current_test_stack_id); } break; @@ -306,8 +306,8 @@ const startMigration = async (req: Request): Promise => { case CMS.SITECORE_V9: case CMS.SITECORE_V10: { if (packagePath) { - await siteCoreService?.createEntry({ packagePath, contentTypes, master_locale: project?.stackDetails?.master_locale, destinationStackId: project?.destination_stack_id, projectId, keyMapper: project?.mapperKeys }); - await siteCoreService?.createLocale(req, project?.destination_stack_id, projectId); + await siteCoreService?.createEntry({ packagePath, contentTypes, master_locale: project?.stackDetails?.master_locale, destinationStackId: project?.destination_stack_id, projectId, keyMapper: project?.mapperKeys, project }); + await siteCoreService?.createLocale(req, project?.destination_stack_id, projectId, project); await siteCoreService?.createVersionFile(project?.destination_stack_id); } break; @@ -417,18 +417,18 @@ const getLogs = async (req: Request): Promise => { */ export const createSourceLocales = async (req: Request) => { - const projectId = req.params.projectId; - const locales = req.body.locale + const projectId = req?.params?.projectId; + const locales = req?.body?.locale; + console.info("🚀 ~ createSourceLocales ~ locales:", locales); try { // Find the project with the specified projectId await ProjectModelLowdb?.read?.(); const index = ProjectModelLowdb?.chain?.get?.("projects")?.findIndex?.({ id: projectId })?.value?.(); - if (index > -1) { + if (typeof index === "number" && index > -1) { ProjectModelLowdb?.update?.((data: any) => { data.projects[index].source_locales = locales; }); - // Write back the updated projects } else { logger.error(`Project with ID: ${projectId} not found`, { status: HTTP_CODES?.NOT_FOUND, diff --git a/api/src/services/org.service.ts b/api/src/services/org.service.ts index eb7eb390..72fa72ea 100644 --- a/api/src/services/org.service.ts +++ b/api/src/services/org.service.ts @@ -8,6 +8,7 @@ import logger from "../utils/logger.js"; import { HTTP_TEXTS, HTTP_CODES } from "../constants/index.js"; import { ExceptionFunction } from "../utils/custom-errors.utils.js"; import { BadRequestError } from "../utils/custom-errors.utils.js"; +import ProjectModelLowdb from "../models/project-lowdb.js"; /** * Retrieves all stacks based on the provided request. @@ -38,7 +39,6 @@ const getAllStacks = async (req: Request): Promise => { }, }) ); - // console.info(err, res); if (err) { logger.error( getLogMessage( @@ -63,11 +63,20 @@ const getAllStacks = async (req: Request): Promise => { ); }); } - // const locale:any[] - // const locale = await getStackLocal(token_payload, stacks); + await ProjectModelLowdb?.read?.(); + const testStacks = ProjectModelLowdb?.chain?.get?.("projects")?.flatMap?.("test_stacks")?.value?.(); + if (testStacks?.length > 0) { + const filterStacks = []; + for (const stack of stacks ?? []) { + const isPresent = testStacks?.find?.((testStack: any) => testStack?.stackUid === stack?.api_key); + if (isPresent === undefined) { + filterStacks?.push(stack); + } + } + stacks = filterStacks; + } return { data: { - // stacks: locale, stacks, }, status: res.status, diff --git a/api/src/services/sitecore.service.ts b/api/src/services/sitecore.service.ts index 2ba20de0..92e0a495 100644 --- a/api/src/services/sitecore.service.ts +++ b/api/src/services/sitecore.service.ts @@ -9,7 +9,6 @@ import { orgService } from './org.service.js'; import { getLogMessage } from '../utils/index.js'; import customLogger from '../utils/custom-logger.utils.js'; - const append = "a"; const baseDirName = MIGRATION_DATA_CONFIG.DATA const { @@ -50,6 +49,18 @@ const AssetsPathSpliter = ({ path, id }: any) => { return newPath; } +const mapLocales = ({ masterLocale, locale, locales }: any) => { + if (locales?.masterLocale?.[masterLocale ?? ''] === locale) { + return Object?.keys(locales?.masterLocale)?.[0] + } + for (const [key, value] of Object?.entries?.(locales) ?? {}) { + if (typeof value !== 'object' && value === locale) { + return key; + } + } + return locale?.toLowerCase?.(); +} + async function writeOneFile(indexPath: string, fileMeta: any) { @@ -195,7 +206,7 @@ const cretaeAssets = async ({ packagePath, baseDir, destinationStackId, projectI return allAssetJSON; } -const createEntry = async ({ packagePath, contentTypes, master_locale, destinationStackId, projectId, keyMapper }: { packagePath: any; contentTypes: any; master_locale?: string, destinationStackId: string, projectId: string, keyMapper: any }) => { +const createEntry = async ({ packagePath, contentTypes, master_locale, destinationStackId, projectId, keyMapper, project }: { packagePath: any; contentTypes: any; master_locale?: string, destinationStackId: string, projectId: string, keyMapper: any, project: any }) => { try { const srcFunc = 'createEntry'; const baseDir = path.join(baseDirName, destinationStackId); @@ -216,7 +227,11 @@ const createEntry = async ({ packagePath, contentTypes, master_locale, destinati const templateIndex = entriesData?.findIndex((ele: any) => ele?.template === template); if (templateIndex >= 0) { const entry = entriesData?.[templateIndex]?.locale?.[language]; - entry[id] = { meta: jsonData?.item?.$, fields: jsonData?.item?.fields }; + if (entry !== undefined) { + entry[id] = { meta: jsonData?.item?.$, fields: jsonData?.item?.fields }; + } else { + entriesData[templateIndex].locale[language] = entries; + } } else { const locale: any = {}; locale[language] = entries; @@ -228,61 +243,59 @@ const createEntry = async ({ packagePath, contentTypes, master_locale, destinati for await (const ctType of contentTypes) { const message = getLogMessage( srcFunc, - `Transforming entries of Content Type ${keyMapper[ctType?.contentstackUid] ?? ctType?.contentstackUid} has begun.`, + `Transforming entries of Content Type ${keyMapper?.[ctType?.contentstackUid] ?? ctType?.contentstackUid} has begun.`, {} ) await customLogger(projectId, destinationStackId, 'info', message); const entryPresent: any = entriesData?.find((item: any) => uidCorrector({ uid: item?.template }) === ctType?.contentstackUid) if (entryPresent) { const locales: any = Object?.keys(entryPresent?.locale); + const allLocales: any = { masterLocale: project?.master_locale ?? LOCALE_MAPPER?.masterLocale, ...project?.locales ?? {} } for await (const locale of locales) { - let newLocale = locale; + const newLocale = mapLocales({ masterLocale: master_locale, locale, locales: allLocales }); const entryLocale: any = {}; - if (typeof LOCALE_MAPPER?.masterLocale === 'object' && LOCALE_MAPPER?.masterLocale !== null && LOCALE_MAPPER?.masterLocale?.[master_locale ?? ''] === locale) { - newLocale = Object?.keys(LOCALE_MAPPER?.masterLocale)?.[0]; - Object.entries(entryPresent?.locale?.[locale] || {}).map(async ([uid, entry]: any) => { - const entryObj: any = {}; - entryObj.uid = uid; - for await (const field of entry?.fields?.field ?? []) { - for await (const fsc of ctType?.fieldMapping ?? []) { - if (fsc?.contentstackFieldType !== 'group' && !field?.$?.key?.includes('__')) { - if (fsc?.contentstackFieldUid === 'title') { - entryObj[fsc?.contentstackFieldUid] = entry?.meta?.name; - } - if (fsc?.contentstackFieldUid === 'url') { - entryObj[fsc?.contentstackFieldUid] = `/${entry?.meta?.key}`; - } - if (getLastKey(fsc?.uid) === field?.$?.key) { - const content: any = await entriesFieldCreator({ field: fsc, content: field?.content, idCorrector, allAssetJSON, contentTypes, entriesData, locale }); - const gpData: any = ctType?.fieldMapping?.find((elemant: any) => elemant?.uid === fsc?.uid?.split('.')?.[0]); - if (gpData?.uid) { - const ctUid = uidCorrector({ uid: gpData?.uid }); - if (ctUid !== gpData?.contentstackFieldUid && fsc?.contentstackFieldUid?.includes(ctUid)) { - const newUid: any = fsc?.contentstackFieldUid?.replace(ctUid, gpData?.contentstackFieldUid); - entryObj[newUid] = content; - } else { - entryObj[fsc?.contentstackFieldUid] = content; - } + Object.entries(entryPresent?.locale?.[locale] || {}).map(async ([uid, entry]: any) => { + const entryObj: any = {}; + entryObj.uid = uid; + for await (const field of entry?.fields?.field ?? []) { + for await (const fsc of ctType?.fieldMapping ?? []) { + if (fsc?.contentstackFieldType !== 'group' && !field?.$?.key?.includes('__')) { + if (fsc?.contentstackFieldUid === 'title') { + entryObj[fsc?.contentstackFieldUid] = entry?.meta?.name; + } + if (fsc?.contentstackFieldUid === 'url') { + entryObj[fsc?.contentstackFieldUid] = `/${entry?.meta?.key}`; + } + if (getLastKey(fsc?.uid) === field?.$?.key) { + const content: any = await entriesFieldCreator({ field: fsc, content: field?.content, idCorrector, allAssetJSON, contentTypes, entriesData, locale }); + const gpData: any = ctType?.fieldMapping?.find((elemant: any) => elemant?.uid === fsc?.uid?.split('.')?.[0]); + if (gpData?.uid) { + const ctUid = uidCorrector({ uid: gpData?.uid }); + if (ctUid !== gpData?.contentstackFieldUid && fsc?.contentstackFieldUid?.includes(ctUid)) { + const newUid: any = fsc?.contentstackFieldUid?.replace(ctUid, gpData?.contentstackFieldUid); + entryObj[newUid] = content; } else { entryObj[fsc?.contentstackFieldUid] = content; } + } else { + entryObj[fsc?.contentstackFieldUid] = content; } } } } - entryObj.publish_details = []; - if (Object.keys?.(entryObj)?.length > 1) { - entryLocale[uid] = unflatten(entryObj) ?? {}; - const message = getLogMessage( - srcFunc, - `Entry title "${entryObj?.title}"(${keyMapper[ctType?.contentstackUid] ?? ctType?.contentstackUid}) in the ${newLocale} locale has been successfully transformed.`, - {} - ) - await customLogger(projectId, destinationStackId, 'info', message) - } - }); - } - const mapperCt: string = (keyMapper[ctType?.contentstackUid] !== "" && keyMapper[ctType?.contentstackUid] !== undefined) ? keyMapper[ctType?.contentstackUid] + } + entryObj.publish_details = []; + if (Object.keys?.(entryObj)?.length > 1) { + entryLocale[uid] = unflatten(entryObj) ?? {}; + const message = getLogMessage( + srcFunc, + `Entry title "${entryObj?.title}"(${keyMapper?.[ctType?.contentstackUid] ?? ctType?.contentstackUid}) in the ${newLocale} locale has been successfully transformed.`, + {} + ) + await customLogger(projectId, destinationStackId, 'info', message) + } + }); + const mapperCt: string = (keyMapper?.[ctType?.contentstackUid] !== "" && keyMapper?.[ctType?.contentstackUid] !== undefined) ? keyMapper?.[ctType?.contentstackUid] : ctType?.contentstackUid; const fileMeta = { "1": `${newLocale}.json` }; const entryPath = path.join( @@ -296,11 +309,11 @@ const createEntry = async ({ packagePath, contentTypes, master_locale, destinati } else { const message = getLogMessage( srcFunc, - `No entries found for the content type ${keyMapper[ctType?.contentstackUid] ?? ctType?.contentstackUid}.`, + `No entries found for the content type ${keyMapper?.[ctType?.contentstackUid] ?? ctType?.contentstackUid}.`, {} ) await customLogger(projectId, destinationStackId, 'error', message) - console.info('Entries missing for', keyMapper[ctType?.contentstackUid] ?? ctType?.contentstackUid) + console.info('Entries missing for', keyMapper?.[ctType?.contentstackUid] ?? ctType?.contentstackUid) } } return true; @@ -309,13 +322,13 @@ const createEntry = async ({ packagePath, contentTypes, master_locale, destinati } } -const createLocale = async (req: any, destinationStackId: string, projectId: string) => { +const createLocale = async (req: any, destinationStackId: string, projectId: string, project: any) => { const srcFunc = 'createLocale'; try { const baseDir = path.join(baseDirName, destinationStackId); const localeSave = path.join(baseDir, LOCALE_DIR_NAME); const allLocalesResp = await orgService.getLocales(req) - const masterLocale = Object?.keys?.(LOCALE_MAPPER?.masterLocale)?.[0]; + const masterLocale = Object?.keys?.(project?.master_locale ?? LOCALE_MAPPER?.masterLocale)?.[0]; const msLocale: any = {}; const uid = uuidv4(); msLocale[uid] = { @@ -331,14 +344,14 @@ const createLocale = async (req: any, destinationStackId: string, projectId: str ) await customLogger(projectId, destinationStackId, 'info', message); const allLocales: any = {}; - for (const [key, value] of Object.entries(LOCALE_MAPPER)) { + for (const [key, value] of Object.entries(project?.locales ?? LOCALE_MAPPER)) { const localeUid = uuidv4(); if (key !== 'masterLocale' && typeof value === 'string') { allLocales[localeUid] = { - "code": value, + "code": key, "fallback_locale": masterLocale, "uid": localeUid, - "name": allLocalesResp?.data?.locales?.[value] ?? '' + "name": allLocalesResp?.data?.locales?.[key] ?? '' } const message = getLogMessage( srcFunc, diff --git a/api/src/utils/content-type-creator.utils.ts b/api/src/utils/content-type-creator.utils.ts index 66424018..e00b14c1 100644 --- a/api/src/utils/content-type-creator.utils.ts +++ b/api/src/utils/content-type-creator.utils.ts @@ -672,6 +672,9 @@ const mergeTwoCts = async (ct: any, mergeCts: any) => { ...ct, title: mergeCts?.title, uid: mergeCts?.uid, + options: { + "singleton": false, + } } for await (const field of ctData?.schema ?? []) { if (field?.data_type === 'group') { diff --git a/ui/src/cmsData/content_mapping.json b/ui/src/cmsData/content_mapping.json index c5b79c8d..cdf74703 100644 --- a/ui/src/cmsData/content_mapping.json +++ b/ui/src/cmsData/content_mapping.json @@ -35,7 +35,7 @@ "open_in_new_tab": false } ], - "content_types_heading": "Content Types", + "content_types_heading": "Content Models", "contentstack_fields": { "title": "Contentstack Field Types", "field_types": [ @@ -143,7 +143,7 @@ "_version": 3 }, "table_search_placeholder": "Search fields", - "search_placeholder": "Search content types", + "search_placeholder": "Search content models", "tags": [], "title": "Content Mapping", "updated_at": "2024-01-25T12:13:32.967Z", diff --git a/ui/src/cmsData/destinationStack.json b/ui/src/cmsData/destinationStack.json index 0ce50cd3..0fe31082 100644 --- a/ui/src/cmsData/destinationStack.json +++ b/ui/src/cmsData/destinationStack.json @@ -1,7 +1,7 @@ { "title": "Select Destination Stack", "cta": "Continue To Content Mapping", - "description":"Select the stack you want to migrate content to from the assigned organization.", + "description": "Select the stack you want to migrate content to from the assigned organization", "new_stack": { "save_stack_cta": "Save", "new_stack_input": "Enter Stack Details Below", diff --git a/ui/src/cmsData/homepage.json b/ui/src/cmsData/homepage.json index 46315cec..e856330c 100644 --- a/ui/src/cmsData/homepage.json +++ b/ui/src/cmsData/homepage.json @@ -18,5 +18,5 @@ "updated_at": "2023-12-12T09:03:00.889Z", "_version": 7, "_in_progress": false, - "description": "

Easily migrate content between stacks or from other CMS platforms to Contentstack.

" + "description": "

Easily migrate content between stacks or from other CMS platforms to Contentstack

" } diff --git a/ui/src/cmsData/legacyCms.json b/ui/src/cmsData/legacyCms.json index a4ca8740..665a2bda 100644 --- a/ui/src/cmsData/legacyCms.json +++ b/ui/src/cmsData/legacyCms.json @@ -377,4 +377,4 @@ "restricted_keyword_checkbox_text": "Please acknowledge that you have referred to the Contentstack restricted keywords", "affix_cta": "Continue", "file_format_cta": "Continue" -} \ No newline at end of file +} diff --git a/ui/src/cmsData/migrationSteps.json b/ui/src/cmsData/migrationSteps.json index c318b700..c8cab282 100644 --- a/ui/src/cmsData/migrationSteps.json +++ b/ui/src/cmsData/migrationSteps.json @@ -17,7 +17,7 @@ "type": "p", "attrs": { "style": {}, "redactor-attributes": {}, "dir": "ltr" }, "uid": "f4f59ae597494fd5bf9b40920a2e7e86", - "children": [{ "text": "Select source CMS for data migration." }] + "children": [{ "text": "Select source CMS for data migration" }] } ], "type": "doc" @@ -118,7 +118,7 @@ "type": "p", "attrs": { "style": {}, "redactor-attributes": {}, "dir": "ltr" }, "uid": "9a6dbc02eff4492d81d5824a82f4c18e", - "children": [{ "text": "Review of content mapping on the target stack." }] + "children": [{ "text": "Review of content mapping on the target stack" }] } ], "_version": 11 diff --git a/ui/src/cmsData/projects.json b/ui/src/cmsData/projects.json index bde386e2..49ec6148 100644 --- a/ui/src/cmsData/projects.json +++ b/ui/src/cmsData/projects.json @@ -53,7 +53,7 @@ "uid": "5ff5db2c09b54cf994ac9c0684d9a9eb", "children": [ { - "text": "Begin your content migration journey by creating a new project. Follow the steps to ensure a smooth and efficient transfer of your content." + "text": "Begin your content migration journey by creating a new project. Follow the steps to ensure a smooth and efficient transfer of your content" } ] } @@ -111,7 +111,7 @@ "uid": "abe73b9230a14732b851770356995ee8", "children": [ { - "text": "Don't worry though, we're here to help. Try adjusting your search terms or check if you misspelled anything." + "text": "Don't worry though, we're here to help. Try adjusting your search terms or check if you misspelled anything" } ] } @@ -141,4 +141,4 @@ }, "title": "New Project" } -} \ No newline at end of file +} diff --git a/ui/src/cmsData/region_login.json b/ui/src/cmsData/region_login.json index c4ed384d..310d7619 100644 --- a/ui/src/cmsData/region_login.json +++ b/ui/src/cmsData/region_login.json @@ -2,7 +2,7 @@ "title": "Account Login", "url": "/region-login", "heading": "Account Login", - "description": "Please select Contentstack North America (AWS, Azure or GCP) or Contentstack Europe (AWS or Azure) to continue.", + "description": "Please select Contentstack North America (AWS, Azure or GCP) or Contentstack Europe (AWS or Azure) to continue", "regions": [ { "region": "NA", @@ -55,30 +55,30 @@ { "region": "GCP_NA", "service_icon": { - "uid": "blt57dafa9c1472b46f", - "created_at": "2024-07-29T10:57:59.058Z", - "updated_at": "2024-07-29T10:57:59.058Z", - "created_by": "blt181afddb6080e3df", - "updated_by": "blt181afddb6080e3df", - "content_type": "image/svg+xml", - "file_size": "1622", - "tags": [], - "filename": "google-cloud_1.svg", - "url": "https://images.contentstack.io/v3/assets/bltd3620ec6418ad3ad/blt57dafa9c1472b46f/66a775b7d14106640a554ec7/google-cloud_1.svg", - "ACL": [], - "is_dir": false, - "parent_uid": null, - "_version": 1, - "title": "google-cloud_1.svg" + "uid": "blt57dafa9c1472b46f", + "created_at": "2024-07-29T10:57:59.058Z", + "updated_at": "2024-07-29T10:57:59.058Z", + "created_by": "blt181afddb6080e3df", + "updated_by": "blt181afddb6080e3df", + "content_type": "image/svg+xml", + "file_size": "1622", + "tags": [], + "filename": "google-cloud_1.svg", + "url": "https://images.contentstack.io/v3/assets/bltd3620ec6418ad3ad/blt57dafa9c1472b46f/66a775b7d14106640a554ec7/google-cloud_1.svg", + "ACL": [], + "is_dir": false, + "parent_uid": null, + "_version": 1, + "title": "google-cloud_1.svg" }, "service_title": "Google Cloud Platform", "region_title": "North America", "cta": { - "title": "Continue", - "url": "/login" + "title": "Continue", + "url": "/login" }, "_metadata": { - "uid": "cs14bce3c329a87986" + "uid": "cs14bce3c329a87986" } }, { diff --git a/ui/src/cmsData/test_migration.json b/ui/src/cmsData/test_migration.json index 3d108e1a..eb4f680d 100644 --- a/ui/src/cmsData/test_migration.json +++ b/ui/src/cmsData/test_migration.json @@ -15,5 +15,5 @@ "url": "", "with_icon": false }, - "subtitle": "Test Migration allows you to migrate selected content to a test stack for review. Verify the content and ensure everything is correct before proceeding to the final migration." + "subtitle": "Test Migration allows you to migrate selected content to a test stack for review. Verify the content and ensure everything is correct before proceeding to the final migration" } diff --git a/ui/src/common/assets/icons.tsx b/ui/src/common/assets/icons.tsx index 18a4870b..52b9b0e8 100644 --- a/ui/src/common/assets/icons.tsx +++ b/ui/src/common/assets/icons.tsx @@ -12,7 +12,8 @@ export const NO_PROJECTS_SEARCH = ( height="300" viewBox="0 0 300 300" fill="none" - xmlns="http://www.w3.org/2000/svg"> + xmlns="http://www.w3.org/2000/svg" + > + fill="none" + > + name="CaretRight" + > - - - + + + ); @@ -811,8 +829,16 @@ export const LOG_OUT = ( */ export const MAGNIFY = ( - - + + ); @@ -826,8 +852,16 @@ export const MAGNIFY = ( */ export const DEMAGNIFY = ( - - + + ); @@ -841,30 +875,181 @@ export const DEMAGNIFY = ( */ export const SCHEMA_PREVIEW = ( - - - - - + + + + + - ); - -export const NoDataFound = ( - - - - - - - - - - +export const NoDataFound = ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); - - diff --git a/ui/src/components/AccountPage/index.tsx b/ui/src/components/AccountPage/index.tsx index bbb5baa3..aa9b5ed8 100644 --- a/ui/src/components/AccountPage/index.tsx +++ b/ui/src/components/AccountPage/index.tsx @@ -6,7 +6,7 @@ import './index.scss'; /** * Renders the AccountPage component. - * + * * @param {AccountObj} props - The props object containing data for the component. * @returns {JSX.Element} The rendered AccountPage component. */ @@ -19,7 +19,7 @@ const AccountPage = (props: AccountObj): JSX.Element => { // eslint-disable-next-line react/no-unknown-property
- Contentstack + Contentstack

{heading}

diff --git a/ui/src/components/AdvancePropertise/advanceProperties.interface.ts b/ui/src/components/AdvancePropertise/advanceProperties.interface.ts index 057ea141..87d1b683 100644 --- a/ui/src/components/AdvancePropertise/advanceProperties.interface.ts +++ b/ui/src/components/AdvancePropertise/advanceProperties.interface.ts @@ -8,17 +8,17 @@ export interface SchemaProps { * The type of the field. */ fieldtype: string; - + /** * The updated settings for the field. */ value: UpdatedSettings; - + /** * The ID of the row. */ rowId: string; - + /** * A function to update the field settings. * @param rowId - The ID of the row. @@ -26,22 +26,22 @@ export interface SchemaProps { * @param checkBoxChanged - Indicates whether the checkbox has changed. */ updateFieldSettings: (rowId: string, value: Advanced, checkBoxChanged: boolean) => void; - + /** * Indicates whether the field is localized. */ isLocalised: boolean; - + /** * A function to close the modal. */ closeModal: () => void; - + /** * The data for the field map. */ data: FieldMapType; - + /** * The ID of the project. */ @@ -121,8 +121,8 @@ export interface UpdatedSettings { embedObjects?: any; - default_value?: string |boolean; - options?: any[] + default_value?: string | boolean; + options?: any[]; } /** @@ -236,8 +236,8 @@ export interface StateType { embedAssests?: boolean; } -export interface optionsType{ - label?:string; - key?:string; - value:string -} \ No newline at end of file +export interface optionsType { + label?: string; + key?: string; + value: string; +} diff --git a/ui/src/components/AdvancePropertise/index.tsx b/ui/src/components/AdvancePropertise/index.tsx index 28ac6a19..6720013f 100644 --- a/ui/src/components/AdvancePropertise/index.tsx +++ b/ui/src/components/AdvancePropertise/index.tsx @@ -22,7 +22,7 @@ import { getContentTypes } from '../../services/api/migration.service'; import { validateArray } from '../../utilities/functions'; // Interfaces -import { optionsType, SchemaProps } from './advanceProperties.interface'; +import { optionsType, SchemaProps } from './advanceProperties.interface'; import { ContentType } from '../ContentMapper/contentMapper.interface'; // Styles @@ -64,45 +64,44 @@ const AdvancePropertise = (props: SchemaProps) => { const embedObjects = props?.value?.embedObjects?.map((item: string) => ({ label: item, - value: item, + value: item })); // State for content types const [contentTypes, setContentTypes] = useState([]); const [ctValue, setCTValue] = useState(embedObjects); - const [embedObjectsLabels, setEmbedObjectsLabels] = useState(props?.value?.embedObjects); + const [embedObjectsLabels, setEmbedObjectsLabels] = useState( + props?.value?.embedObjects + ); const [showOptions, setShowOptions] = useState>({}); const [showIcon, setShowIcon] = useState(); const filterRef = useRef(null); const [options, setOptions] = useState(props?.value?.options || []); const [draggedIndex, setDraggedIndex] = useState(null); - - useEffect(()=>{ + + useEffect(() => { const defaultIndex = toggleStates?.option?.findIndex( (item: optionsType) => toggleStates?.default_value === item?.key ); - + if (defaultIndex !== -1) { setShowIcon(defaultIndex); } - - },[]); + }, []); useEffect(() => { fetchContentTypes(''); - }, []) + }, []); /** * Fetches the content types list. * @param searchText - The search text. */ const fetchContentTypes = async (searchText: string) => { try { - const { data } = await getContentTypes(props?.projectId ?? '', 0,5000, searchText || ''); //org id will always present + const { data } = await getContentTypes(props?.projectId ?? '', 0, 5000, searchText || ''); //org id will always present setContentTypes(data?.contentTypes); } catch (error) { return error; - } - }; /** @@ -111,7 +110,11 @@ const AdvancePropertise = (props: SchemaProps) => { * @param event - The change event. * @param checkBoxChanged - Indicates if the checkbox was changed. */ - const handleOnChange = (field: string, event: React.ChangeEvent, checkBoxChanged: boolean) => { + const handleOnChange = ( + field: string, + event: React.ChangeEvent, + checkBoxChanged: boolean + ) => { setToggleStates((prevStates) => ({ ...prevStates, [field]: (event.target as HTMLInputElement)?.value @@ -119,7 +122,7 @@ const AdvancePropertise = (props: SchemaProps) => { const currentToggleStates = { ...toggleStates, - [field]: (event.target as HTMLInputElement)?.value, + [field]: (event.target as HTMLInputElement)?.value }; props?.updateFieldSettings( @@ -130,7 +133,7 @@ const AdvancePropertise = (props: SchemaProps) => { default_value: currentToggleStates?.default_value, validationRegex: currentToggleStates?.validationRegex ?? '', minChars: currentToggleStates?.minChars, - maxChars:currentToggleStates?.maxChars, + maxChars: currentToggleStates?.maxChars, mandatory: currentToggleStates?.mandatory, multiple: currentToggleStates?.multiple, unique: false, @@ -142,7 +145,7 @@ const AdvancePropertise = (props: SchemaProps) => { minSize: currentToggleStates?.minSize, maxSize: currentToggleStates?.maxSize, title: currentToggleStates?.title, - url:currentToggleStates?.url + url: currentToggleStates?.url }, checkBoxChanged ); @@ -161,9 +164,9 @@ const AdvancePropertise = (props: SchemaProps) => { })); const currentToggleStates = { ...toggleStates, - [field]: value, + [field]: value }; - + props?.updateFieldSettings( props?.rowId, { @@ -175,30 +178,29 @@ const AdvancePropertise = (props: SchemaProps) => { unique: false, nonLocalizable: currentToggleStates?.nonLocalizable, embedObject: currentToggleStates?.embedObject, - embedObjects : embedObjectsLabels, + embedObjects: embedObjectsLabels, default_value: currentToggleStates?.default_value, minChars: currentToggleStates?.minChars, - maxChars:currentToggleStates?.maxChars, + maxChars: currentToggleStates?.maxChars, minRange: currentToggleStates?.minRange, maxRange: currentToggleStates?.maxRange, minSize: currentToggleStates?.minSize, maxSize: currentToggleStates?.maxSize, title: currentToggleStates?.title, - url:currentToggleStates?.url + url: currentToggleStates?.url }, checkBoxChanged ); }; - - const handleRadioChange = (field: string,value:boolean) => { + const handleRadioChange = (field: string, value: boolean) => { setToggleStates((prevStates) => ({ ...prevStates, [field]: value })); const currentToggleStates = { ...toggleStates, - [field]: value, + [field]: value }; props?.updateFieldSettings( @@ -211,26 +213,22 @@ const AdvancePropertise = (props: SchemaProps) => { unique: false, nonLocalizable: currentToggleStates?.nonLocalizable, embedObject: currentToggleStates?.embedObject, - embedObjects : embedObjectsLabels + embedObjects: embedObjectsLabels }, true ); - }; - const handleOnClick = ( index:number) =>{ - + const handleOnClick = (index: number) => { setShowOptions((prev) => ({ - - [index]: !prev[index], + [index]: !prev[index] })); - } - - const handleDefalutValue = (index:number, option:optionsType) => { + }; + + const handleDefalutValue = (index: number, option: optionsType) => { setShowIcon(index); setShowOptions(() => ({ - - [index]: false, + [index]: false })); setToggleStates((prevStates) => ({ ...prevStates, @@ -250,18 +248,16 @@ const AdvancePropertise = (props: SchemaProps) => { unique: false, nonLocalizable: currentToggleStates?.nonLocalizable, embedObject: currentToggleStates?.embedObject, - embedObjects : embedObjectsLabels, - options:options + embedObjects: embedObjectsLabels, + options: options }, true ); - - } - const handleRemoveDefalutValue = (index:number)=>{ + }; + const handleRemoveDefalutValue = (index: number) => { setShowIcon(-1); setShowOptions(() => ({ - - [index]: false, + [index]: false })); setToggleStates((prevStates) => ({ ...prevStates, @@ -281,50 +277,46 @@ const AdvancePropertise = (props: SchemaProps) => { unique: false, nonLocalizable: currentToggleStates?.nonLocalizable, embedObject: currentToggleStates?.embedObject, - embedObjects : embedObjectsLabels, + embedObjects: embedObjectsLabels, options: options }, true ); - } + }; const handleDragStart = (index: number) => { setDraggedIndex(index); document.querySelectorAll('.element-wrapper').forEach((el, i) => { if (i === index) { - el.classList.add('dragging'); + el.classList.add('dragging'); } }); }; - const handleDragOver = (e:React.DragEvent, index:number) => { - e.preventDefault(); + const handleDragOver = (e: React.DragEvent, index: number) => { + e.preventDefault(); document.querySelectorAll('.element-wrapper').forEach((el, i) => { if (i === index) { - el.classList.remove('dragging'); + el.classList.remove('dragging'); } }); }; - const handleDrop = (index:number) => { + const handleDrop = (index: number) => { if (draggedIndex === null) return; - - const updatedOptions = [...options]; - const draggedItem = updatedOptions[draggedIndex]; - const targetItem = updatedOptions[index]; - - updatedOptions[draggedIndex] = targetItem; - updatedOptions[index] = draggedItem; - - setOptions(updatedOptions); - setDraggedIndex(null); - - + + const updatedOptions = [...options]; + const draggedItem = updatedOptions[draggedIndex]; + const targetItem = updatedOptions[index]; + + updatedOptions[draggedIndex] = targetItem; + updatedOptions[index] = draggedItem; + + setOptions(updatedOptions); + setDraggedIndex(null); }; - useEffect(() => { - if (ctValue && Array.isArray(ctValue)) { const labels = ctValue.map((item) => item.label); setEmbedObjectsLabels(labels); @@ -332,147 +324,162 @@ const AdvancePropertise = (props: SchemaProps) => { }, [ctValue]); // Option for content types - const contentTypesList = contentTypes?.filter((ct: ContentType) => ct?.type === "content_type"); - + const contentTypesList = contentTypes?.filter((ct: ContentType) => ct?.type === 'content_type'); + const option = validateArray(contentTypesList) - ? contentTypesList?.map((option: ContentType) => ({ label: option?.contentstackTitle, value: option?.contentstackUid })) + ? contentTypesList?.map((option: ContentType) => ({ + label: option?.contentstackTitle, + value: option?.contentstackUid + })) : [{ label: contentTypesList, value: contentTypesList }]; return ( <> - + -
- - - {(props?.fieldtype === 'Dropdown') && - <> - - Choice - - - (read only) -
- {options?.map((option: optionsType,index)=>( - -
handleDragStart(index)} - onDragOver={(e)=> handleDragOver(e,index)} - onDrop={() => handleDrop(index)} - > -
- -
- + {props?.fieldtype === 'Dropdown' && ( + <> + + Choice + + (read only) +
+ {options?.map((option: optionsType, index) => ( +
handleDragStart(index)} + onDragOver={(e) => handleDragOver(e, index)} + onDrop={() => handleDrop(index)} + > +
+ +
+ } - > - - - - - - {showOptions[index] && ( -
- {showIcon !== index ? - - : - - - } - - -
- )} - - - + suffix={ + index === showIcon && ( + + ) + } + > + + + + {showOptions[index] && ( +
+ {showIcon !== index ? ( + + ) : ( + + )} +
+ )}
- - ))} - + ))} +
+ + )} -
- - - - - } - - {(props?.fieldtype === 'Single Line Textbox' || props?.fieldtype === 'Multi Line Textbox') && ( + {(props?.fieldtype === 'Single Line Textbox' || + props?.fieldtype === 'Multi Line Textbox') && ( <> Default Value - - + + ) => handleOnChange('default_value', e, true))} + onChange={ + handleOnChange && + ((e: React.ChangeEvent) => + handleOnChange('default_value', e, true)) + } /> - + Validation (Regex) - + ) => handleOnChange('validationRegex', e, true))} + onChange={ + handleOnChange && + ((e: React.ChangeEvent) => + handleOnChange('validationRegex', e, true)) + } /> - )} + )} {props?.fieldtype === 'Link' && ( <> -
- - Default Value - - - - -
+
+ + Default Value + + + + +
@@ -483,7 +490,10 @@ const AdvancePropertise = (props: SchemaProps) => { value={toggleStates?.title} placeholder="Enter value" version="v2" - onChange={handleOnChange && ((e: React.ChangeEvent) => handleOnChange('title', e, true))} + onChange={ + handleOnChange && + ((e: React.ChangeEvent) => handleOnChange('title', e, true)) + } /> @@ -496,31 +506,32 @@ const AdvancePropertise = (props: SchemaProps) => { value={toggleStates?.url} placeholder="Enter value" version="v2" - onChange={handleOnChange && ((e: React.ChangeEvent) => handleOnChange('url', e, true))} + onChange={ + handleOnChange && + ((e: React.ChangeEvent) => handleOnChange('url', e, true)) + } /> - + )} {props?.fieldtype === 'Boolean' && ( - - Default Value - -
- handleRadioChange('default_value',true)}> - - handleRadioChange('default_value',false)}> - - -
- + + Default Value + +
+ handleRadioChange('default_value', true)} + > + handleRadioChange('default_value', false)} + > +
)} @@ -529,12 +540,7 @@ const AdvancePropertise = (props: SchemaProps) => { Referenced Content Type - - + )} @@ -543,37 +549,46 @@ const AdvancePropertise = (props: SchemaProps) => { Other Options
- {(props?.fieldtype === 'HTML Rich text Editor' || props?.fieldtype === 'JSON Rich Text Editor') && ( + {(props?.fieldtype === 'HTML Rich text Editor' || + props?.fieldtype === 'JSON Rich Text Editor') && ( <> -
+
0 || toggleStates?.embedObject} - onChange={handleToggleChange && ((e: React.MouseEvent) => handleToggleChange('embedObject', (e.target as HTMLInputElement)?.checked, true))} + onChange={ + handleToggleChange && + ((e: React.MouseEvent) => + handleToggleChange( + 'embedObject', + (e.target as HTMLInputElement)?.checked, + true + )) + } />
- - {(ctValue && ctValue?.length > 0 || toggleStates?.embedObject) && ( + + {((ctValue && ctValue?.length > 0) || toggleStates?.embedObject) && ( handleSelectedCsLocale(key, index, 'csLocale')} + onChange={(key: { label: string; value: string }) => + handleSelectedCsLocale(key, index, 'csLocale') + } options={csOptions} placeholder={placeholder} isSearchable @@ -301,12 +316,16 @@ const Mapper = ({ className="select-container" /> */