From 2e683cc872ec0304547fd53ab2fa862e444c351b Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Fri, 17 Jul 2026 18:49:36 +0400 Subject: [PATCH] fix(static-pages): stop createPage/duplicatePage from generating colliding slugs/routes createPage() derived its slug from array length (custom-page-${length+1}): create, delete, create again reliably reproduces a duplicate slug against a surviving page. duplicatePage() had the same issue with a fixed '-copy' suffix - duplicating the same page twice collides with the first duplicate. Both trip the duplicate-slug/route validator on a page the user never directly touched. Added uniqueValue() (append -2, -3, ... until free) and used it for both. Verified live via window.ng.getComponent(): reproduced the exact collision scenario pre-fix, confirmed no duplicates post-fix (custom-page-6-2, about-us-copy/about-us-copy-2). Co-Authored-By: Claude Sonnet 5 --- .../static-pages-editor.component.ts | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/app/features/content-management/components/static-pages-editor.component.ts b/src/app/features/content-management/components/static-pages-editor.component.ts index 7b36343..b4a723c 100644 --- a/src/app/features/content-management/components/static-pages-editor.component.ts +++ b/src/app/features/content-management/components/static-pages-editor.component.ts @@ -156,8 +156,28 @@ export class StaticPagesEditorComponent { this.mediaPickerOpen = false; } + /** + * Appends `-2`, `-3`, ... to `candidate` until it's not in `existing`. + * createPage()/duplicatePage() both used to derive slugs/routes from array + * length or a fixed "-copy" suffix, which reproducibly collides with an + * existing page (create, delete, create again; or duplicate the same page + * twice) - the duplicate then trips the duplicate-slug/route validator + * on a page the user never touched. + */ + private uniqueValue(candidate: string, existing: Set): string { + if (!existing.has(candidate)) { + return candidate; + } + let suffix = 2; + while (existing.has(`${candidate}-${suffix}`)) { + suffix++; + } + return `${candidate}-${suffix}`; + } + createPage(): void { - const slug = `custom-page-${this.allPages().length + 1}`; + const existingSlugs = new Set(this.allPages().map(page => page.slug)); + const slug = this.uniqueValue(`custom-page-${this.allPages().length + 1}`, existingSlugs); const page: ContentPage = { id: `page-${Date.now()}`, slug, @@ -189,11 +209,13 @@ export class StaticPagesEditorComponent { if (!source) { return; } + const existingSlugs = new Set(this.allPages().map(page => page.slug)); + const existingRoutes = new Set(this.allPages().map(page => page.route)); const clone: ContentPage = { ...source, id: `page-${Date.now()}`, - slug: `${source.slug}-copy`, - route: `${source.route}-copy`, + slug: this.uniqueValue(`${source.slug}-copy`, existingSlugs), + route: this.uniqueValue(`${source.route}-copy`, existingRoutes), order: this.allPages().length + 1, status: 'draft', translations: Object.fromEntries(Object.entries(source.translations).map(([locale, t]) => [locale, { ...t }])),