From 7d35875c2035b74a2672bc88cce9551dfa2303f9 Mon Sep 17 00:00:00 2001 From: Alex Sorafumo Date: Fri, 2 Oct 2026 13:12:35 +1000 Subject: [PATCH 1/2] fix(signage-manager): fix brand kit saves and image editor flows - Keep palette keys when saving branding. - Write brand kit changes one at a time. - Prevent double saves and wait for the picked image before saving. - Cancel the job when the editor closes. - Show an error when an option fails to load. - Respect branding-editing for logo changes. - Show errors that name the failed action. - Handle three-digit hex colours and avoid extra font redraws. --- apps/signage-manager/USER_STORIES.md | 2 +- .../src/app/branding/brand-access.ts | 22 ++ .../src/app/branding/branding.component.ts | 96 ++++++--- .../image-gen-layer-controls.component.ts | 16 +- .../image-gen/image-gen-layer.component.ts | 31 ++- .../image-gen/image-gen-modal.component.ts | 178 +++++++++------- .../src/app/image-gen/image-gen.service.ts | 72 +++++-- .../src/app/image-gen/image-gen.util.ts | 28 +-- .../tests/branding/branding.component.spec.ts | 49 ++++- ...image-gen-layer-controls.component.spec.ts | 21 ++ .../image-gen-layer.component.spec.ts | 44 ++++ .../image-gen-modal.component.spec.ts | 194 ++++++++++++++++-- .../tests/image-gen/image-gen.service.spec.ts | 168 ++++++++++++++- .../tests/image-gen/image-gen.util.spec.ts | 20 +- shared/assets/locale/en-AU.json | 4 + 15 files changed, 776 insertions(+), 169 deletions(-) create mode 100644 apps/signage-manager/src/app/branding/brand-access.ts diff --git a/apps/signage-manager/USER_STORIES.md b/apps/signage-manager/USER_STORIES.md index a84e65c4226..62e974e781b 100644 --- a/apps/signage-manager/USER_STORIES.md +++ b/apps/signage-manager/USER_STORIES.md @@ -381,7 +381,7 @@ These stories cover the current app workflows: - Without `template-editing`, template create, edit, duplicate, delete and layout changes are hidden. Template mappings stay available. - Without `ai-generation`, the AI create actions are hidden. - Without `ai-editing`, "Edit with AI" and refinement in the AI editor are hidden. - - Without `branding-editing`, the branding tab is read-only. + - Without `branding-editing`, the branding tab is read-only, and the AI editor cannot change the logo. - The All Groups view uses only `app.features`. --- diff --git a/apps/signage-manager/src/app/branding/brand-access.ts b/apps/signage-manager/src/app/branding/brand-access.ts new file mode 100644 index 00000000000..f1c8b9b78ed --- /dev/null +++ b/apps/signage-manager/src/app/branding/brand-access.ts @@ -0,0 +1,22 @@ +import { SignageContextService } from '../signage-context.service'; + +type BrandAccessContext = Pick< + SignageContextService, + 'is_sys_admin' | 'global_features' +>; + +/** + * Whether brand kit edits are on. The kit is for the whole organisation, so + * only the global `app.features` setting turns them off, not a group. + */ +export function brandEditingOn(context: BrandAccessContext): boolean { + return (context.global_features() || []).includes('branding-editing'); +} + +/** + * Whether this user can change the brand kit, from the branding page or from + * the image editor. Call it inside a `computed` so it follows the signals. + */ +export function canEditBrandKit(context: BrandAccessContext): boolean { + return context.is_sys_admin() && brandEditingOn(context); +} diff --git a/apps/signage-manager/src/app/branding/branding.component.ts b/apps/signage-manager/src/app/branding/branding.component.ts index 506c7081b75..438a4a9433b 100644 --- a/apps/signage-manager/src/app/branding/branding.component.ts +++ b/apps/signage-manager/src/app/branding/branding.component.ts @@ -26,12 +26,22 @@ import { ImageGenBrandKit, ImageGenLogoSlot, } from '../image-gen/image-gen.types'; -import { errorMessage } from '../image-gen/image-gen.util'; +import { actionError } from '../image-gen/image-gen.util'; import { SignageContextService } from '../signage-context.service'; +import { brandEditingOn, canEditBrandKit } from './brand-access'; import { BRAND_FONTS, ensureBrandFont } from './brand-fonts'; const COLOUR_NAMES = ['primary', 'secondary', 'accent']; +/** how many palette colours the page shows and edits */ +const MAX_COLOURS = 3; + +/** a palette colour and the key it is stored under */ +interface BrandColour { + key: string; + value: string; +} + @Component({ selector: 'app-branding', template: ` @@ -100,7 +110,7 @@ const COLOUR_NAMES = ['primary', 'secondary', 'accent']; class="border-base-content/20 h-10 w-14 rounded border bg-transparent disabled:cursor-not-allowed disabled:opacity-60" [class.cursor-pointer]="can_edit()" [disabled]="!can_edit()" - [value]="colour" + [value]="colour.value" (input)="setColourFromInput($index, $event)" [attr.aria-label]=" 'SIGNAGE_MANAGER.BRAND_COLOURS' | translate @@ -113,7 +123,7 @@ const COLOUR_NAMES = ['primary', 'secondary', 'accent']; > {{ colourName($index) }}{{ colour.key }} @if (can_edit()) {