Skip to content

Commit

Permalink
#708 - Add logic to send back frontend configuration and customizations.
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Käser committed Mar 21, 2024
1 parent 58bc117 commit 689e990
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public record ClientConfigDto(
String clientId,
String favicon,
String logo,
String title,
HashMap<String, String> customStyles
) {
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
package ch.puzzle.okr.service.clientconfig;

import ch.puzzle.okr.dto.ClientConfigDto;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Service;

import java.util.HashMap;


@ConfigurationProperties("okr.clientconfig")
public class ClientConfigProperties {
private String favicon;

private String logo;
private String title;
private HashMap<String, String> customStyles = new HashMap<>();

public void setCustomStyles(HashMap<String, String> customStyles) {
this.customStyles = customStyles;
}

public String getFavicon() {
return favicon;
}

public void setFavicon(String favicon) {
this.favicon = favicon;
}

public String getLogo() {
return logo;
}

public void setLogo(String logo) {
this.logo = logo;
}

public HashMap<String, String> getCustomStyles() {
return customStyles;
}

private HashMap<String, String> customStyles;
public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package ch.puzzle.okr.service.clientconfig;

import ch.puzzle.okr.dto.ClientConfigDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.stereotype.Service;

import java.util.HashMap;

@Service
public class ClientConfigService {


@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;

Expand All @@ -17,18 +20,22 @@ public class ClientConfigService {

@Value("${spring.security.oauth2.resourceserver.opaquetoken.client-id}")
private String clientId;
private final ClientConfigProperties clientConfigProperties;

@Value("${okr.clientconfig.favicon}")
private String favicon;

@Value("${okr.clientconfig.logo}")
private String logo;

@Value("${okr.clientconfig.customStyles}")
private HashMap<String, String> customStyles;
public ClientConfigService(ClientConfigProperties clientConfigProperties) {
this.clientConfigProperties = clientConfigProperties;
}

public ClientConfigDto getConfigBasedOnActiveEnv() {
return new ClientConfigDto(activeProfile, issuer, clientId, favicon, logo, customStyles);
return new ClientConfigDto(
activeProfile,
issuer,
clientId,
this.clientConfigProperties.getFavicon(),
this.clientConfigProperties.getLogo(),
this.clientConfigProperties.getTitle(),
this.clientConfigProperties.getCustomStyles()
);
}

}
2 changes: 1 addition & 1 deletion backend/src/main/resources/application-staging.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ logging.level.org.springframework=debug
spring.security.oauth2.resourceserver.opaquetoken.client-id=pitc_okr_staging

okr.user.champion.usernames=peggimann
okr.clientconfig.customStyles.okr-topbar-background-color=#ab31ad
okr.clientconfig.customstyles.okr-topbar-background-color=#ab31ad
2 changes: 1 addition & 1 deletion backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ okr.jwt.user.email=email

okr.clientconfig.favicon=assets/favicon.png
okr.clientconfig.logo=assets/images/okr-logo.svg
okr.clientconfig.customStyles.okr-topbar-background-color=#ff0000
okr.clientconfig.title=Puzzle OKR
5 changes: 4 additions & 1 deletion frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import { ActionPlanComponent } from './action-plan/action-plan.component';
import { CdkDrag, CdkDropList } from '@angular/cdk/drag-drop';
import { TeamManagementComponent } from './shared/dialog/team-management/team-management.component';
import { KeyresultDialogComponent } from './shared/dialog/keyresult-dialog/keyresult-dialog.component';
import { CustomizationService } from './shared/services/customization.service';

function initOauthFactory(configService: ConfigService, oauthService: OAuthService) {
return async () => {
Expand Down Expand Up @@ -202,4 +203,6 @@ export const MY_FORMATS = {
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
export class AppModule {
constructor(customizationService: CustomizationService) {}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div id="topBarHeight">
<div id="okrTopbar">
<span class="d-flex h-100 align-items-center ps-4">
<img alt="okr-logo" height="32" ngSrc="{{this.logoSrc$ | async}}" width="140" />
<img alt="okr-logo" height="32" ngSrc="{{ this.logoSrc$ | async }}" width="140" />
</span>

<div class="d-flex align-items-center me-md-5 me-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
z-index: 102;
height: inherit;
justify-content: space-between;
background-color: var(--okr-topbar-background-color)
background-color: var(--okr-topbar-background-color);

img {
max-height: calc(100% - 16px);
width: auto;
}
}

.topBarEntry {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export class ApplicationTopBarComponent implements OnInit {

@Input()
hasAdminAccess!: ReplaySubject<boolean>;
logoSrc$ = new BehaviorSubject<String>('assets/images/okr-logo.svg');
private dialogRef!: MatDialogRef<TeamManagementComponent> | undefined;
logoSrc$ = new BehaviorSubject<String|undefined>(undefined);

constructor(
private oauthService: OAuthService,
Expand All @@ -36,7 +36,9 @@ export class ApplicationTopBarComponent implements OnInit {
this.configService.config$
.pipe(
map((config) => {
this.logoSrc$.next(config.logo);
if (config.logo) {
this.logoSrc$.next(config.logo);
}
}),
)
.subscribe();
Expand All @@ -45,6 +47,7 @@ export class ApplicationTopBarComponent implements OnInit {
this.username.next(this.oauthService.getIdentityClaims()['name']);
}
}

logOut() {
const currentUrlTree = this.router.createUrlTree([], { queryParams: {} });
this.router.navigateByUrl(currentUrlTree).then(() => {
Expand Down
7 changes: 2 additions & 5 deletions frontend/src/app/config.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, shareReplay } from 'rxjs';
import { ClientConfig } from "./shared/types/model/ClientConfig";

import { ClientConfig } from './shared/types/model/ClientConfig';

@Injectable({
providedIn: 'root',
Expand All @@ -11,8 +10,6 @@ export class ConfigService {
public config$: Observable<ClientConfig>;

constructor(private httpClient: HttpClient) {
this.config$ = this.httpClient
.get<ClientConfig>('/config')
.pipe(shareReplay());
this.config$ = this.httpClient.get<ClientConfig>('/config').pipe(shareReplay());
}
}
53 changes: 33 additions & 20 deletions frontend/src/app/shared/services/customization.service.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { Injectable } from '@angular/core';
import { ConfigService } from "../../config.service";
import { CustomizationConfig } from "../types/model/ClientConfig";
import { Inject, Injectable } from '@angular/core';
import { ConfigService } from '../../config.service';
import { CustomizationConfig, CustomStyles } from '../types/model/ClientConfig';
import { DOCUMENT } from '@angular/common';

@Injectable({
providedIn: 'root',
})
export class CustomizationService {
private currentConfig?: CustomizationConfig;

constructor(private configService: ConfigService) {
configService.config$.subscribe(config => {
constructor(
configService: ConfigService,
@Inject(DOCUMENT) private document: Document,
) {
configService.config$.subscribe((config) => {
this.updateCustomizations(config);
})
});
}


private updateCustomizations(config: CustomizationConfig) {
this.setTitle(config.title);
this.setFavicon(config.favicon);
this.setStyleCustomizations(config.customStyles);

Expand All @@ -27,46 +31,55 @@ export class CustomizationService {
return;
}

document.getElementById("favicon")?.setAttribute("src", favicon);
this.document.getElementById('favicon')?.setAttribute('href', favicon);
}

private setTitle(title: string) {
if (!title || this.currentConfig?.title === title) {
return;
}
debugger;
this.document.querySelector('title')!.innerHTML = title;
}

private setStyleCustomizations(customStylesMap: Map<string, string>) {
private setStyleCustomizations(customStylesMap: CustomStyles) {
if (!customStylesMap || this.areStylesTheSame(customStylesMap)) {
return;
}

this.removeStyles(this.currentConfig?.customStyles)
this.removeStyles(this.currentConfig?.customStyles);
this.setStyles(customStylesMap);
}

private areStylesTheSame(customStylesMap: Map<string, string>) {
private areStylesTheSame(customStylesMap: CustomStyles) {
return JSON.stringify(this.currentConfig?.customStyles) === JSON.stringify(customStylesMap);
}

private removeStyles(customStylesMap: Map<string, string> | undefined) {
private setStyles(customStylesMap: CustomStyles | undefined) {
if (!customStylesMap) {
return;
}
const styles = document?.getElementById("html")!.style;

const styles = this.document.querySelector('html')!.style;
if (!styles) {
return
return;
}

Array.from(customStylesMap.entries()).forEach(([varName, varValue]) => {
Object.entries(customStylesMap).forEach(([varName, varValue]) => {
styles.setProperty(`--${varName}`, varValue);
});
}

private setStyles(customStylesMap: Map<string, string>) {
private removeStyles(customStylesMap: CustomStyles | undefined) {
if (!customStylesMap) {
return;
}
const styles = document?.getElementById("html")!.style;

const styles = this.document.querySelector('html')!.style;
if (!styles) {
return
return;
}

Array.from(customStylesMap.keys()).forEach((varName) => {
Object.keys(customStylesMap).forEach((varName) => {
styles.removeProperty(`--${varName}`);
});
}
Expand Down
11 changes: 7 additions & 4 deletions frontend/src/app/shared/types/model/ClientConfig.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@

export interface AuthConfig {
activeProfile: string;
issuer: string;
clientId: string;
}

export interface CustomStyles {
[key: string]: string;
}

export interface CustomizationConfig {
title: string;
favicon: string;
logo: string;
customStyles: Map<string, string>;
}
export interface ClientConfig extends AuthConfig, CustomizationConfig {
customStyles: CustomStyles;
}
export interface ClientConfig extends AuthConfig, CustomizationConfig {}
2 changes: 1 addition & 1 deletion frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Puzzle OKR</title>
<title>OKR Tool</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link id="favicon" rel="icon" type="image" href="assets/favicon.png" />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/style/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ $pz-dark-blue-palette: (
--mdc-filled-button-label-text-tracking: normal;
--mdc-outlined-button-label-text-tracking: normal;

--okr-topbar-background-color: $pz-dark-blue;
--okr-topbar-background-color: #1e5a96;
}

0 comments on commit 689e990

Please sign in to comment.