From 180317c5bf3585b90a3a3165a343e9a6e2efa2ec Mon Sep 17 00:00:00 2001 From: Shradha Nahar Date: Thu, 20 Aug 2026 12:39:16 +0530 Subject: [PATCH 1/3] save button added for asset selection --- ui/src/components/ContentMapper/assetMapper.tsx | 1 + .../ContentMapper/useMeasuredTableHeight.ts | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/src/components/ContentMapper/assetMapper.tsx b/ui/src/components/ContentMapper/assetMapper.tsx index 04af48499..d12d19222 100644 --- a/ui/src/components/ContentMapper/assetMapper.tsx +++ b/ui/src/components/ContentMapper/assetMapper.tsx @@ -105,6 +105,7 @@ const AssetMapper = ({ const tableHeight = useMeasuredTableHeight(tableWrapperRef, [tableData?.length], { panelSelector: '.TablePanel', footerSelector: '.mapper-footer', + toolbarSelector: '.asset-mapper-toolbar', }); // Single server-paginated fetch (same pattern as entryMapper's fetchEntries). The diff --git a/ui/src/components/ContentMapper/useMeasuredTableHeight.ts b/ui/src/components/ContentMapper/useMeasuredTableHeight.ts index 1b0e559c5..ebdfff1e0 100644 --- a/ui/src/components/ContentMapper/useMeasuredTableHeight.ts +++ b/ui/src/components/ContentMapper/useMeasuredTableHeight.ts @@ -23,6 +23,13 @@ export interface MeasuredTableHeightOptions { panelSelector: string; /** Selector for the Save footer, resolved within `wrapperRef`. */ footerSelector: string; + /** + * Selector for an extra chrome row above the table (e.g. the asset mapper's status-filter + * toolbar) that takes its own flex-flow height, resolved within `wrapperRef`. Omit when the + * mapper has no such row (e.g. the entry mapper, whose locale select is absolutely positioned + * and doesn't need reserving). + */ + toolbarSelector?: string; } // Fixed chrome fallbacks, used only until the real elements are mounted/measured. @@ -42,7 +49,7 @@ const TOGGLE_SELECTOR = '.mapper-view-toggle'; export function useMeasuredTableHeight( wrapperRef: RefObject, deps: unknown[], - { panelSelector, footerSelector }: MeasuredTableHeightOptions, + { panelSelector, footerSelector, toolbarSelector }: MeasuredTableHeightOptions, ): number { // Pre-measure guess: same model as measure() (box fallback − reserve), clamped to the floor // so the one frame react-window renders before the effect runs never gets a negative height. @@ -61,6 +68,9 @@ export function useMeasuredTableHeight( const toggle = box?.querySelector(TOGGLE_SELECTOR) as HTMLElement | null; const panel = wrapper.querySelector(panelSelector) as HTMLElement | null; const footer = wrapper.querySelector(footerSelector) as HTMLElement | null; + const toolbar = toolbarSelector + ? (wrapper.querySelector(toolbarSelector) as HTMLElement | null) + : null; if (import.meta.env.DEV) { // A rename/markup change in venus would drop us to the magic constants and quietly @@ -68,6 +78,7 @@ export function useMeasuredTableHeight( if (!box) console.warn(`useMeasuredTableHeight: "${BOX_SELECTOR}" not found — falling back.`); if (!panel) console.warn(`useMeasuredTableHeight: "${panelSelector}" not found — using ${PANEL_FALLBACK}px fallback.`); if (!footer) console.warn(`useMeasuredTableHeight: "${footerSelector}" not found — using ${FOOTER_FALLBACK}px fallback.`); + if (toolbarSelector && !toolbar) console.warn(`useMeasuredTableHeight: "${toolbarSelector}" not found — not reserving space for it.`); } // `||` not `??`: a momentarily 0-height box (measured before layout settles) should @@ -77,6 +88,7 @@ export function useMeasuredTableHeight( (toggle?.offsetHeight ?? 0) + (panel?.offsetHeight ?? PANEL_FALLBACK) + (footer?.offsetHeight ?? FOOTER_FALLBACK) + + (toolbar?.offsetHeight ?? 0) + PAGINATION_AND_BUFFER; // Clamp rather than skip: at extreme zoom `avail` can dip low, but keeping the previous // (possibly large) value would re-expose the overflow this hook exists to prevent. From 516710fb97907a5a2432bb83b332053c2bb450d7 Mon Sep 17 00:00:00 2001 From: Shradha Nahar Date: Thu, 20 Aug 2026 16:22:48 +0530 Subject: [PATCH 2/3] fix: prevent uploaded file input from shrinking on invalid path validation --- ui/src/components/LegacyCms/legacyCms.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/components/LegacyCms/legacyCms.scss b/ui/src/components/LegacyCms/legacyCms.scss index e96b98782..2b7c41fc9 100644 --- a/ui/src/components/LegacyCms/legacyCms.scss +++ b/ui/src/components/LegacyCms/legacyCms.scss @@ -83,7 +83,9 @@ background-color: $color-base-white-5; flex-direction: column; justify-content: center; - align-items: flex-start; + // Stretch (not flex-start) so the path row keeps the container's full width — flex-start + // let it shrink-to-fit for short/invalid paths, visibly narrowing the input (CMG-1113). + align-items: stretch; margin-left: 20px !important; border: 1px solid $color-brand-fail-base; border-radius: var(--TermCount, 5px); From 7d36eb607ec873d82603cccf0d0006be3f614e8a Mon Sep 17 00:00:00 2001 From: Shradha Nahar Date: Fri, 21 Aug 2026 17:04:09 +0530 Subject: [PATCH 3/3] fix: skip duplicate AEM entries sharing the same source id --- api/src/services/aem.service.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/api/src/services/aem.service.ts b/api/src/services/aem.service.ts index 740cae9b0..9486e7c25 100644 --- a/api/src/services/aem.service.ts +++ b/api/src/services/aem.service.ts @@ -1387,9 +1387,18 @@ const createEntry = async ({ ? uidCorrector(`${parseData.title}_${parseData.templateType}`) : uidCorrector(parseData.templateType); } - const uid = modelId && !usedEntryUids.has(modelId) - ? modelId - : uuidv4?.()?.replace?.(/-/g, ''); + // A stable modelId already seen earlier in this same run means this file is a + // duplicate export of a page already processed (AEM can emit both a page's generic + // model and its template's structure/model definition as separate files sharing the + // same id — see CMG-1112). Skip it instead of minting a fresh random uid: a random + // uid here would create a second, permanent duplicate entry that mints yet another + // untracked random uid (another duplicate) on every subsequent delta iteration, + // since it can never match anything recorded in entry_mapper. This mirrors + // extractEntries's collision policy in upload-api's migration-aem. + if (modelId && usedEntryUids.has(modelId)) { + continue; + } + const uid = modelId || uuidv4?.()?.replace?.(/-/g, ''); usedEntryUids.add(uid); const title = getTitle(parseData); const isEFragment = isExperienceFragment(parseData);