From fbc517ed873e5fa03694a9975ffeb2959ee97637 Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Mon, 25 Nov 2024 14:36:36 +0100 Subject: [PATCH 01/37] make topbar background color of staging violet to visualize its staging environment --- backend/src/main/resources/application-staging.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/application-staging.properties b/backend/src/main/resources/application-staging.properties index 1981bca769..57ade77501 100644 --- a/backend/src/main/resources/application-staging.properties +++ b/backend/src/main/resources/application-staging.properties @@ -44,7 +44,7 @@ okr.tenants.pitc.clientcustomization.triangles=assets/images/triangles-okr-heade okr.tenants.pitc.clientcustomization.background-logo=assets/images/puzzle-p.svg okr.tenants.pitc.clientcustomization.favicon=assets/favicon.png okr.tenants.pitc.clientcustomization.title=Puzzle OKR -okr.tenants.pitc.clientcustomization.customstyles.okr-topbar-background-color=#1e5a96 +okr.tenants.pitc.clientcustomization.customstyles.okr-topbar-background-color=#641e96 okr.tenants.pitc.clientcustomization.customstyles.okr-banner-background-color=#dcedf9 okr.tenants.pitc.clientcustomization.helpSiteUrl=https://wiki.puzzle.ch/Puzzle/OKRs From a9c54e3b35e8f6a4c39ac8a4ba2bd2d7ecdfa9f0 Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Thu, 28 Nov 2024 09:18:16 +0100 Subject: [PATCH 02/37] add new okr demo logo --- frontend/src/assets/images/okr-demo-logo.svg | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 frontend/src/assets/images/okr-demo-logo.svg diff --git a/frontend/src/assets/images/okr-demo-logo.svg b/frontend/src/assets/images/okr-demo-logo.svg new file mode 100644 index 0000000000..da8db3e2a8 --- /dev/null +++ b/frontend/src/assets/images/okr-demo-logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + From be26796f29bfb35937ffd4aea74b6cf92d09da7c Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Thu, 28 Nov 2024 09:20:34 +0100 Subject: [PATCH 03/37] change styling of logo to fit okr demo logo --- .../application-top-bar.component.html | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/frontend/src/app/components/application-top-bar/application-top-bar.component.html b/frontend/src/app/components/application-top-bar/application-top-bar.component.html index 4d764f97ea..549b425893 100644 --- a/frontend/src/app/components/application-top-bar/application-top-bar.component.html +++ b/frontend/src/app/components/application-top-bar/application-top-bar.component.html @@ -1,14 +1,7 @@
- okr-logo + okr-logo
@@ -30,7 +30,6 @@

{{ OVEntity.team.name }}

class="col-xs-12 col-md-6 col-xl-4 pb-3 column-width" id="objective-column" > -
diff --git a/frontend/src/app/components/team/team.component.ts b/frontend/src/app/components/team/team.component.ts index ae17673f4b..57ff8d06aa 100644 --- a/frontend/src/app/components/team/team.component.ts +++ b/frontend/src/app/components/team/team.component.ts @@ -6,6 +6,10 @@ import { Objective } from '../../shared/types/model/Objective'; import { KeyresultDialogComponent } from '../keyresult-dialog/keyresult-dialog.component'; import { ObjectiveMin } from '../../shared/types/model/ObjectiveMin'; import { DialogService } from '../../services/dialog.service'; +import { ConfigService } from '../../services/config.service'; +import { ClientConfig } from '../../shared/types/model/ClientConfig'; +import { HttpClient } from '@angular/common/http'; +import { map, Observable } from 'rxjs'; @Component({ selector: 'app-team', @@ -16,16 +20,24 @@ import { DialogService } from '../../services/dialog.service'; export class TeamComponent implements OnInit { @Input({ required: true }) public overviewEntity!: OverviewEntity; + protected addIconSrc: Observable = this.configService.config$.pipe( + map((config: ClientConfig) => + config.customStyles['okr-add-objective-text-color'] === '#ffffff' + ? '../../../assets/icons/new-icon-demo.svg' + : '../../../assets/icons/new-icon.svg', + ), + ); constructor( private dialogService: DialogService, private refreshDataService: RefreshDataService, + private configService: ConfigService, ) {} - trackByObjectiveId: TrackByFunction = (index, objective) => objective.id; - ngOnInit(): void {} + trackByObjectiveId: TrackByFunction = (index, objective) => objective.id; + createObjective() { const matDialogRef = this.dialogService.open(ObjectiveFormComponent, { data: { diff --git a/frontend/src/assets/icons/new-icon-demo.svg b/frontend/src/assets/icons/new-icon-demo.svg new file mode 100644 index 0000000000..31d9d1c0c4 --- /dev/null +++ b/frontend/src/assets/icons/new-icon-demo.svg @@ -0,0 +1,4 @@ + + + + From 5f78848ac388156a2e028e819a18eb55c299a948 Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Thu, 28 Nov 2024 16:16:06 +0100 Subject: [PATCH 08/37] refactor extraction of customstyles in backend and add a new property for new-icon --- .../TenantClientCustomizationProvider.java | 40 +++++++------------ .../resources/application-demo.properties | 2 + 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/okr/multitenancy/customization/TenantClientCustomizationProvider.java b/backend/src/main/java/ch/puzzle/okr/multitenancy/customization/TenantClientCustomizationProvider.java index ba06060b53..b2cde78358 100644 --- a/backend/src/main/java/ch/puzzle/okr/multitenancy/customization/TenantClientCustomizationProvider.java +++ b/backend/src/main/java/ch/puzzle/okr/multitenancy/customization/TenantClientCustomizationProvider.java @@ -1,28 +1,24 @@ package ch.puzzle.okr.multitenancy.customization; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.AbstractEnvironment; +import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; import org.springframework.stereotype.Component; import java.io.Serializable; import java.text.MessageFormat; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; @Component public class TenantClientCustomizationProvider { private static final String CUSTOM_STYLES_PREFIX = "okr.tenants.{0}.clientcustomization.customstyles"; - private static final String TOPBAR_BACKGROUND_COLOR = ".okr-topbar-background-color"; - private static final String BANNER_BACKGROUND_COLOR = ".okr-banner-background-color"; - private static final String OVERVIEW_BACKGROUND_COLOR = ".okr-overview-background-color"; - private static final String TEAM_HEADER_TEXT_COLOR = ".okr-team-header-color"; - private static final String ADD_OBJECTIVE_TEXT_COLOR = ".okr-add-objective-text-color"; private final Map tenantCustomizations = new HashMap<>(); - private final List customCssStyles = List.of(TOPBAR_BACKGROUND_COLOR, BANNER_BACKGROUND_COLOR, - OVERVIEW_BACKGROUND_COLOR, TEAM_HEADER_TEXT_COLOR, ADD_OBJECTIVE_TEXT_COLOR); + private final Environment env; public TenantClientCustomizationProvider(final @Value("${okr.tenant-ids}") String[] tenantIds, Environment env) { @@ -46,19 +42,15 @@ private TenantClientCustomization readClientCustomizationConfig(String tenantId) } private Map getCustomCssStyles(String tenantId) { - Map styles = new HashMap<>(); - for (String customStyle : customCssStyles) { - String propertyName = formatWithTenant(CUSTOM_STYLES_PREFIX + customStyle, tenantId); - CssConfigItem cssPropertyNameAndValue = getCssPropertyNameAndValue(propertyName, tenantId); - styles.put(cssPropertyNameAndValue.cssName(), cssPropertyNameAndValue.cssValue()); - } - return styles; - } + MutablePropertySources propSrcs = ((AbstractEnvironment) env).getPropertySources(); + Map styles = StreamSupport.stream(propSrcs.spliterator(), false) + .filter(ps -> ps instanceof EnumerablePropertySource) + .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()).flatMap(Arrays:: stream) + .filter(propName -> propName.contains(formatWithTenant(CUSTOM_STYLES_PREFIX, tenantId))) + .collect(Collectors.toMap(propName -> extractCssNameFromPropertyName(propName, tenantId), + propName -> env.getProperty(propName))); - private CssConfigItem getCssPropertyNameAndValue(String propertyName, String tenantId) { - String cssName = extractCssNameFromPropertyName(propertyName, tenantId); - String cssValue = env.getProperty(formatWithTenant(propertyName, tenantId)); - return new CssConfigItem(cssName, cssValue); + return styles; } public Optional getTenantClientCustomizationsById(String tenantId) { @@ -80,6 +72,4 @@ private String formatWithTenant(String stringWithTenantPlaceholder, String tenan return MessageFormat.format(stringWithTenantPlaceholder, tenantId); } - record CssConfigItem(String cssName, String cssValue) implements Serializable { - } } \ No newline at end of file diff --git a/backend/src/main/resources/application-demo.properties b/backend/src/main/resources/application-demo.properties index f5494373e9..e7a778a727 100644 --- a/backend/src/main/resources/application-demo.properties +++ b/backend/src/main/resources/application-demo.properties @@ -18,6 +18,8 @@ okr.tenants.pitc.clientcustomization.customstyles.okr-banner-background-color=#d okr.tenants.pitc.clientcustomization.customstyles.okr-overview-background-color=#003153 okr.tenants.pitc.clientcustomization.customstyles.okr-team-header-color=#ffffff okr.tenants.pitc.clientcustomization.customstyles.okr-add-objective-text-color=#ffffff +okr.tenants.pitc.clientcustomization.customstyles.okr-add-objective-icon=/assets/icons/new-icon-demo.svg + # acme client customization okr.tenants.acme.clientcustomization.logo=assets/images/okr-logo-acme.svg okr.tenants.acme.clientcustomization.triangles=assets/images/triangles-okr-acme-header.svg From 3e242e2754611ff703e0117b85f2b44206ab59ad Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Thu, 28 Nov 2024 16:17:51 +0100 Subject: [PATCH 09/37] change extraction of add-objective-icon to be more generic --- frontend/src/app/components/team/team.component.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frontend/src/app/components/team/team.component.ts b/frontend/src/app/components/team/team.component.ts index 57ff8d06aa..8451903709 100644 --- a/frontend/src/app/components/team/team.component.ts +++ b/frontend/src/app/components/team/team.component.ts @@ -8,7 +8,6 @@ import { ObjectiveMin } from '../../shared/types/model/ObjectiveMin'; import { DialogService } from '../../services/dialog.service'; import { ConfigService } from '../../services/config.service'; import { ClientConfig } from '../../shared/types/model/ClientConfig'; -import { HttpClient } from '@angular/common/http'; import { map, Observable } from 'rxjs'; @Component({ @@ -21,11 +20,7 @@ export class TeamComponent implements OnInit { @Input({ required: true }) public overviewEntity!: OverviewEntity; protected addIconSrc: Observable = this.configService.config$.pipe( - map((config: ClientConfig) => - config.customStyles['okr-add-objective-text-color'] === '#ffffff' - ? '../../../assets/icons/new-icon-demo.svg' - : '../../../assets/icons/new-icon.svg', - ), + map((config: ClientConfig) => config.customStyles['okr-add-objective-icon'] ?? '/assets/icons/new-icon.svg'), ); constructor( From 85a81a078ec64c08999beb9cc0da9002a598391a Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Mon, 2 Dec 2024 09:23:21 +0100 Subject: [PATCH 10/37] change naming and add white-outline when hovering add objective button --- frontend/src/app/components/team/team.component.html | 3 ++- frontend/src/app/components/team/team.component.scss | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/team/team.component.html b/frontend/src/app/components/team/team.component.html index f69f24f91f..f6ed65bed9 100644 --- a/frontend/src/app/components/team/team.component.html +++ b/frontend/src/app/components/team/team.component.html @@ -6,12 +6,13 @@

{{ OVEntity.team.name }}

diff --git a/frontend/src/app/components/team/team.component.spec.ts b/frontend/src/app/components/team/team.component.spec.ts index 5404442e80..eeb3e2bc2c 100644 --- a/frontend/src/app/components/team/team.component.spec.ts +++ b/frontend/src/app/components/team/team.component.spec.ts @@ -92,4 +92,13 @@ describe('TeamComponent', () => { component.ngOnInit(); expect(component.addIconSrc.value).toBe('/assets/icons/new-icon.svg'); }); + + it('should still show add objective icon if next value is null', () => { + component.overviewEntity = { ...overViewEntity2 }; + component.overviewEntity.writeable = true; + component.ngOnInit(); + //@ts-ignore + component.addIconSrc.next(undefined); + expect(component.addIconSrc).toBe('/assets/icons/new-icon.svg'); + }); }); From 82760a96e1e607a361208665c76e7aaf0863d286 Mon Sep 17 00:00:00 2001 From: Jannik Pulfer Date: Fri, 6 Dec 2024 11:47:29 +0100 Subject: [PATCH 22/37] Move check for undefined config value to ts and adjust tests --- .../app/components/team/team.component.html | 5 +-- .../components/team/team.component.spec.ts | 32 +++++++++++-------- .../src/app/components/team/team.component.ts | 5 +-- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/frontend/src/app/components/team/team.component.html b/frontend/src/app/components/team/team.component.html index 7b0679c7cc..deb2b9a557 100644 --- a/frontend/src/app/components/team/team.component.html +++ b/frontend/src/app/components/team/team.component.html @@ -14,9 +14,10 @@

{{ OVEntity.team.name }}

> Add key-result button Objective hinzufügen diff --git a/frontend/src/app/components/team/team.component.spec.ts b/frontend/src/app/components/team/team.component.spec.ts index eeb3e2bc2c..d93ce5b6d4 100644 --- a/frontend/src/app/components/team/team.component.spec.ts +++ b/frontend/src/app/components/team/team.component.spec.ts @@ -18,11 +18,21 @@ import { ConfidenceComponent } from '../confidence/confidence.component'; import { ScoringComponent } from '../../shared/custom/scoring/scoring.component'; import { ChangeDetectionStrategy } from '@angular/core'; import { DialogService } from '../../services/dialog.service'; +import { ConfigService } from '../../services/config.service'; +import { of } from 'rxjs'; const dialogService = { open: jest.fn(), }; +let configService = { + config$: of({ + customStyles: { + 'okr-add-objective-icon': 'new-icon-from-config-service.svg', + }, + }), +}; + const refreshDataServiceMock = { markDataRefresh: jest.fn(), }; @@ -50,6 +60,10 @@ describe('TeamComponent', () => { provide: DialogService, useValue: dialogService, }, + { + provide: ConfigService, + useValue: configService, + }, { provide: RefreshDataService, useValue: refreshDataServiceMock, @@ -86,19 +100,9 @@ describe('TeamComponent', () => { expect(button).toBeFalsy(); }); - it('should display right add objective icon', () => { - component.overviewEntity = { ...overViewEntity2 }; - component.overviewEntity.writeable = true; - component.ngOnInit(); - expect(component.addIconSrc.value).toBe('/assets/icons/new-icon.svg'); - }); - - it('should still show add objective icon if next value is null', () => { - component.overviewEntity = { ...overViewEntity2 }; - component.overviewEntity.writeable = true; - component.ngOnInit(); - //@ts-ignore - component.addIconSrc.next(undefined); - expect(component.addIconSrc).toBe('/assets/icons/new-icon.svg'); + it('should set value of addIconSrc if src from config service is defined', () => { + const addObjectiveIcon = fixture.debugElement.query(By.css('[data-testId="add-objective-icon"]')); + expect(component.addIconSrc.value).toBe('new-icon-from-config-service.svg'); + expect(addObjectiveIcon.attributes['src']).toBe('new-icon-from-config-service.svg'); }); }); diff --git a/frontend/src/app/components/team/team.component.ts b/frontend/src/app/components/team/team.component.ts index 51502e5268..adc04195db 100644 --- a/frontend/src/app/components/team/team.component.ts +++ b/frontend/src/app/components/team/team.component.ts @@ -19,7 +19,7 @@ import { BehaviorSubject } from 'rxjs'; export class TeamComponent implements OnInit { @Input({ required: true }) public overviewEntity!: OverviewEntity; - addIconSrc: BehaviorSubject = new BehaviorSubject('/assets/icons/new-icon.svg'); + addIconSrc: BehaviorSubject = new BehaviorSubject('assets/icons/new-icon.svg'); constructor( private dialogService: DialogService, @@ -27,7 +27,8 @@ export class TeamComponent implements OnInit { private configService: ConfigService, ) { this.configService.config$.subscribe((config: ClientConfig) => { - this.addIconSrc.next(config.customStyles['okr-add-objective-icon']); + const configuredIconSrc = config.customStyles['okr-add-objective-icon']; + if (configuredIconSrc) this.addIconSrc.next(configuredIconSrc); }); } From 6b4a13613313cc140d96d608c0a84c3f47779d18 Mon Sep 17 00:00:00 2001 From: Jannik Pulfer Date: Fri, 6 Dec 2024 13:34:17 +0100 Subject: [PATCH 23/37] Add new describe block to team component tests to test default value of add icon subject --- .../components/team/team.component.spec.ts | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/team/team.component.spec.ts b/frontend/src/app/components/team/team.component.spec.ts index d93ce5b6d4..672e5fec91 100644 --- a/frontend/src/app/components/team/team.component.spec.ts +++ b/frontend/src/app/components/team/team.component.spec.ts @@ -7,7 +7,6 @@ import { RouterTestingModule } from '@angular/router/testing'; import { MatMenuModule } from '@angular/material/menu'; import { KeyresultComponent } from '../keyresult/keyresult.component'; import { MatDialogModule } from '@angular/material/dialog'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; import { By } from '@angular/platform-browser'; import { RefreshDataService } from '../../services/refresh-data.service'; import { TranslateTestingModule } from 'ngx-translate-testing'; @@ -20,12 +19,13 @@ import { ChangeDetectionStrategy } from '@angular/core'; import { DialogService } from '../../services/dialog.service'; import { ConfigService } from '../../services/config.service'; import { of } from 'rxjs'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; const dialogService = { open: jest.fn(), }; -let configService = { +const configServiceDefined = { config$: of({ customStyles: { 'okr-add-objective-icon': 'new-icon-from-config-service.svg', @@ -33,6 +33,14 @@ let configService = { }), }; +const configServiceUndefined = { + config$: of({ + customStyles: { + 'okr-add-objective-icon': undefined, + }, + }), +}; + const refreshDataServiceMock = { markDataRefresh: jest.fn(), }; @@ -47,7 +55,6 @@ describe('TeamComponent', () => { RouterTestingModule, MatMenuModule, MatDialogModule, - HttpClientTestingModule, MatTooltipModule, TranslateTestingModule.withTranslations({ de: de, @@ -62,12 +69,13 @@ describe('TeamComponent', () => { }, { provide: ConfigService, - useValue: configService, + useValue: configServiceDefined, }, { provide: RefreshDataService, useValue: refreshDataServiceMock, }, + provideHttpClient(withInterceptorsFromDi()), ], }) .overrideComponent(TeamComponent, { @@ -101,8 +109,47 @@ describe('TeamComponent', () => { }); it('should set value of addIconSrc if src from config service is defined', () => { - const addObjectiveIcon = fixture.debugElement.query(By.css('[data-testId="add-objective-icon"]')); expect(component.addIconSrc.value).toBe('new-icon-from-config-service.svg'); + const addObjectiveIcon = fixture.debugElement.query(By.css('[data-testId="add-objective-icon"]')); expect(addObjectiveIcon.attributes['src']).toBe('new-icon-from-config-service.svg'); }); }); + +describe('Test', () => { + let component: TeamComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatMenuModule, + TranslateTestingModule.withTranslations({ + de: de, + }), + ], + declarations: [TeamComponent], + providers: [ + { + provide: ConfigService, + useValue: configServiceUndefined, + }, + provideHttpClient(withInterceptorsFromDi()), + ], + }) + .overrideComponent(TeamComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(TeamComponent); + component = fixture.componentInstance; + component.overviewEntity = overViewEntity1; + fixture.detectChanges(); + }); + + it('should keep default value of addIconSrc if src from config service is undefined', () => { + expect(component.addIconSrc.value).toBe('assets/icons/new-icon.svg'); + const addObjectiveIcon = fixture.debugElement.query(By.css('[data-testId="add-objective-icon"]')); + expect(addObjectiveIcon.attributes['src']).toBe('assets/icons/new-icon.svg'); + }); +}); From 912989a390947fda91317bfcb1f2d89446089bc5 Mon Sep 17 00:00:00 2001 From: Jannik Pulfer Date: Fri, 6 Dec 2024 13:51:50 +0100 Subject: [PATCH 24/37] Rename new describe block and remove unused id --- frontend/src/app/components/team/team.component.html | 2 +- frontend/src/app/components/team/team.component.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/team/team.component.html b/frontend/src/app/components/team/team.component.html index deb2b9a557..cff9517cd7 100644 --- a/frontend/src/app/components/team/team.component.html +++ b/frontend/src/app/components/team/team.component.html @@ -1,7 +1,7 @@
-

{{ OVEntity.team.name }}

+

{{ OVEntity.team.name }}