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()) {