From 35844e0cf1a639553269f2ab0f8666a56ab5cc47 Mon Sep 17 00:00:00 2001 From: Mihajlo Medjedovic Date: Wed, 29 May 2024 14:35:43 +0200 Subject: [PATCH] feat: added app settings service to handle theme persistance, fix: optimised dark mode contrast --- client/src/app/app.component.ts | 4 +- client/src/app/app.module.ts | 4 +- client/src/app/editor/editor.component.ts | 1 - .../src/app/editor/utils/renderers.utils.ts | 18 ++++++- client/src/app/models/AppSettings.ts | 9 ++++ .../src/app/services/app-settings.service.ts | 54 +++++++++++++++++++ client/src/app/services/app.service.ts | 16 ++++++ client/src/app/services/event.service.ts | 10 +++- client/src/app/stage/stage.component.scss | 6 +++ client/src/app/system/system.component.html | 10 ++++ client/src/app/system/system.component.scss | 2 +- client/src/app/system/system.component.ts | 15 +++++- client/src/app/system/system.module.ts | 8 ++- client/src/styles.scss | 4 ++ sas/package-lock.json | 14 ++--- sas/package.json | 2 +- 16 files changed, 158 insertions(+), 19 deletions(-) create mode 100644 client/src/app/models/AppSettings.ts create mode 100644 client/src/app/services/app-settings.service.ts diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 6ee163e..eb6b132 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -14,9 +14,9 @@ import { DcAdapterSettings } from './models/DcAdapterSettings' import { AppStoreService } from './services/app-store.service' import { LicenceService } from './services/licence.service' import '@cds/core/icon/register.js'; -import { ClarityIcons, moonIcon, sunIcon } from '@cds/core/icon'; +import { ClarityIcons, exclamationTriangleIcon, moonIcon, sunIcon } from '@cds/core/icon'; -ClarityIcons.addIcons(moonIcon, sunIcon); +ClarityIcons.addIcons(moonIcon, sunIcon, exclamationTriangleIcon); @Component({ selector: 'my-app', diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 71fea15..1ed94d2 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -20,10 +20,10 @@ import { UsernavRouteComponent } from './routes/usernav-route/usernav-route.comp import { AppService } from './services/app.service' import { InfoModalComponent } from './shared/abort-modal/info-modal.component' import { RequestsModalComponent } from './shared/requests-modal/requests-modal.component' -import { HomeModule } from './home/home.module' import { DirectivesModule } from './directives/directives.module' import { ViyaApiExplorerComponent } from './viya-api-explorer/viya-api-explorer.component' import { NgxJsonViewerModule } from 'ngx-json-viewer' +import { AppSettingsService } from './services/app-settings.service' @NgModule({ declarations: [ @@ -50,7 +50,7 @@ import { NgxJsonViewerModule } from 'ngx-json-viewer' DirectivesModule, NgxJsonViewerModule ], - providers: [AppService, SasStoreService, LicensingGuard], + providers: [AppService, SasStoreService, LicensingGuard, AppSettingsService], bootstrap: [AppComponent] }) export class AppModule {} diff --git a/client/src/app/editor/editor.component.ts b/client/src/app/editor/editor.component.ts index 0f5a49c..a84afcc 100644 --- a/client/src/app/editor/editor.component.ts +++ b/client/src/app/editor/editor.component.ts @@ -2769,7 +2769,6 @@ export class EditorComponent implements OnInit, AfterViewInit { this.currentEditRecordLoadings.push(column) hot.render() - return this.sasService .request('editors/getdynamiccolvals', data, undefined, { diff --git a/client/src/app/editor/utils/renderers.utils.ts b/client/src/app/editor/utils/renderers.utils.ts index 37c45d9..aa8c80f 100644 --- a/client/src/app/editor/utils/renderers.utils.ts +++ b/client/src/app/editor/utils/renderers.utils.ts @@ -11,9 +11,11 @@ export const errorRenderer = ( value: any, cellProperties: any ) => { + addDarkClass(td) + td.innerHTML = `${ value ? value.toString() : '' - } ` + } ` return td } @@ -31,6 +33,8 @@ export const noSpinnerRenderer = ( value: any, cellProperties: any ) => { + addDarkClass(td) + td.innerHTML = value ? value : '' return td @@ -50,10 +54,20 @@ export const spinnerRenderer = ( value: any, cellProperties: any ) => { - td.classList.add('htDark') + addDarkClass(td) + td.innerHTML = `${ value ? value.toString() : '' } ` return td } + +/** + * Adds a htDark class to a TD element if not existing + */ +const addDarkClass = (td: any) => { + if (!td.classList.contains('htDark')) { + td.classList.add('htDark') + } +} \ No newline at end of file diff --git a/client/src/app/models/AppSettings.ts b/client/src/app/models/AppSettings.ts new file mode 100644 index 0000000..d1d4cf0 --- /dev/null +++ b/client/src/app/models/AppSettings.ts @@ -0,0 +1,9 @@ +export interface AppSettings { + persistSelectedTheme: boolean, + selectedTheme: AppThemes +} + +export enum AppThemes { + light = 'light', + dark = 'dark' +} \ No newline at end of file diff --git a/client/src/app/services/app-settings.service.ts b/client/src/app/services/app-settings.service.ts new file mode 100644 index 0000000..748203e --- /dev/null +++ b/client/src/app/services/app-settings.service.ts @@ -0,0 +1,54 @@ +import { BehaviorSubject } from "rxjs" +import { AppSettings, AppThemes } from "../models/AppSettings" + +export class AppSettingsService { + public defaultSettings: AppSettings = { + persistSelectedTheme: true, + selectedTheme: AppThemes.light + } + public settings = new BehaviorSubject(this.defaultSettings) + + constructor() { + this.restoreAppSettings() + } + + /** + * Restore app settings from the local storage + */ + restoreAppSettings() { + try { + const serializedSettings = localStorage.getItem('app-settings') + + if (serializedSettings) { + const settings = JSON.parse(serializedSettings) + + this.setAppSettings(settings) + } else { + console.info('No app settings stored in the localStorage, we will set to default values.') + } + } catch(err) { + console.warn('Error restoring settings from local storgae.', err) + } + } + + /** + * Save app settings to the local storage + */ + storeAppSettings() { + localStorage.setItem('app-settings', JSON.stringify(this.settings.value)) + } + + /** + * Function used in the app to update global settings object + * + * @param appSettings contains properties that should be updated in settings + */ + setAppSettings(appSettings: Partial) { + this.settings.next({ + ...this.settings.value, + ...appSettings + }) + + this.storeAppSettings() + } +} \ No newline at end of file diff --git a/client/src/app/services/app.service.ts b/client/src/app/services/app.service.ts index 92093ad..9cb8687 100644 --- a/client/src/app/services/app.service.ts +++ b/client/src/app/services/app.service.ts @@ -7,6 +7,8 @@ import { BehaviorSubject } from 'rxjs' import { LoggerService } from './logger.service' import { EnvironmentInfo } from '../system/models/environment-info.model' import { LicenceService } from './licence.service' +import { AppSettingsService } from './app-settings.service' +import { AppThemes } from '../models/AppSettings' @Injectable() export class AppService { @@ -18,6 +20,7 @@ export class AppService { private eventService: EventService, private sasService: SasService, private loggerService: LoggerService, + private appSettingsService: AppSettingsService, private router: Router ) { this.subscribe() @@ -29,6 +32,19 @@ export class AppService { } } }) + + const appSettings = this.appSettingsService.settings.value + + if (!!appSettings.persistSelectedTheme) { + if (appSettings.selectedTheme === AppThemes.light) { + this.eventService.toggleDarkMode(false) + } else if (appSettings.selectedTheme === AppThemes.dark) { + this.eventService.toggleDarkMode(true) + } else { + // Fallback to light mode + this.eventService.toggleDarkMode(false) + } + } } sasServiceInit() { diff --git a/client/src/app/services/event.service.ts b/client/src/app/services/event.service.ts index f265d17..53f0da1 100644 --- a/client/src/app/services/event.service.ts +++ b/client/src/app/services/event.service.ts @@ -2,6 +2,8 @@ import { Injectable, Output, EventEmitter } from '@angular/core' import { InfoModal, AbortDetails } from '../models/InfoModal' import { AlertsService } from '../shared/alerts/alerts.service' import { BehaviorSubject } from 'rxjs' +import { AppSettingsService } from './app-settings.service' +import { AppThemes } from '../models/AppSettings' @Injectable({ providedIn: 'root' @@ -20,7 +22,9 @@ export class EventService { public darkMode: BehaviorSubject = new BehaviorSubject(false) - constructor(private alertsService: AlertsService) {} + constructor( + private appSettingsService: AppSettingsService + ) {} toggleDarkMode(value: boolean) { this.darkMode.next(value) @@ -30,6 +34,10 @@ export class EventService { } else { document.body.setAttribute('cds-theme', 'light') } + + this.appSettingsService.setAppSettings({ + selectedTheme: value ? AppThemes.dark : AppThemes.light + }) } public showDemoLimitModal(featureName: string) { diff --git a/client/src/app/stage/stage.component.scss b/client/src/app/stage/stage.component.scss index f6b66d2..13069ce 100644 --- a/client/src/app/stage/stage.component.scss +++ b/client/src/app/stage/stage.component.scss @@ -12,4 +12,10 @@ cursor:pointer; margin-top:10px; color: #007cbb; +} + +::ng-deep body[cds-theme="dark"] { + .baseTableLink { + color: #4ec0ff; + } } \ No newline at end of file diff --git a/client/src/app/system/system.component.html b/client/src/app/system/system.component.html index f91fbdd..33691af 100644 --- a/client/src/app/system/system.component.html +++ b/client/src/app/system/system.component.html @@ -194,6 +194,16 @@
+ + + +
Refresh Data Lineage diff --git a/client/src/app/system/system.component.scss b/client/src/app/system/system.component.scss index 57c64a5..1534c13 100644 --- a/client/src/app/system/system.component.scss +++ b/client/src/app/system/system.component.scss @@ -12,7 +12,7 @@ } } -.admin-action { +.admin-action, .user-action { display: flex; justify-content: space-between; align-items: center; diff --git a/client/src/app/system/system.component.ts b/client/src/app/system/system.component.ts index e956dc6..5f2f50d 100644 --- a/client/src/app/system/system.component.ts +++ b/client/src/app/system/system.component.ts @@ -7,6 +7,8 @@ import { LicenceService } from '../services/licence.service' import { SasService } from '../services/sas.service' import { AppInfo } from './models/app-info.model' import { EnvironmentInfo } from './models/environment-info.model' +import { AppSettingsService } from '../services/app-settings.service' +import { AppSettings } from '../models/AppSettings' @Component({ selector: 'app-system', @@ -36,25 +38,36 @@ export class SystemComponent implements OnInit { Infinity = Infinity licenceState = this.licenceService.licenceState + settings: AppSettings constructor( private appService: AppService, private sasService: SasService, - private licenceService: LicenceService + private licenceService: LicenceService, + private appSettingsService: AppSettingsService ) { this.serverType = this.sasService.getServerType() this.licenceInfo = this.licenceService.getLicenseKeyData() this.environmentInfo = this.appService.getEnvironmentInfo() + this.settings = this.appSettingsService.settings.value if (this.environmentInfo) { this.environmentInfo.AUTOEXEC = decodeURIComponent( this.environmentInfo.AUTOEXEC ) } + + this.appSettingsService.settings.subscribe((settings: AppSettings) => { + this.settings = settings + }) } ngOnInit(): void {} + settingChange(event: Event) { + this.appSettingsService.setAppSettings(this.settings) + } + downloadConfiguration() { let sasjsConfig = this.sasService.getSasjsConfig() let storage = sasjsConfig.serverUrl diff --git a/client/src/app/system/system.module.ts b/client/src/app/system/system.module.ts index 28ff8c4..f3120e9 100644 --- a/client/src/app/system/system.module.ts +++ b/client/src/app/system/system.module.ts @@ -4,9 +4,15 @@ import { CommonModule } from '@angular/common' import { SystemRoutingModule } from './system-routing.module' import { SystemComponent } from './system.component' import { ClarityModule } from '@clr/angular' +import { FormsModule } from '@angular/forms' @NgModule({ declarations: [SystemComponent], - imports: [CommonModule, SystemRoutingModule, ClarityModule] + imports: [ + CommonModule, + SystemRoutingModule, + ClarityModule, + FormsModule + ] }) export class SystemModule {} diff --git a/client/src/styles.scss b/client/src/styles.scss index 389889b..22a4f1f 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -33,6 +33,10 @@ body[cds-theme="dark"] { // Track border: 3px solid $trackColor; } + + clr-icon.is-highlight { + fill: #4ec0ff; + } } // body::-webkit-scrollbar { diff --git a/sas/package-lock.json b/sas/package-lock.json index bfc6dfd..2e4f9f6 100644 --- a/sas/package-lock.json +++ b/sas/package-lock.json @@ -7,7 +7,7 @@ "name": "dc-sas", "dependencies": { "@sasjs/cli": "^4.11.1", - "@sasjs/core": "^4.52.1" + "@sasjs/core": "^4.52.4" } }, "node_modules/@coolaj86/urequest": { @@ -116,9 +116,9 @@ "integrity": "sha512-Grwydm5GxBsYk238PZw41XPjXVVQ9vWcvfZ06L2P0bQbvK0sGn7l69JA7H5MGr3QcaLpiD4Kg70cAh7PgE+JOw==" }, "node_modules/@sasjs/core": { - "version": "4.52.1", - "resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.1.tgz", - "integrity": "sha512-XPNuKD1T5XLGMKg4Ll3KggOTjhHgnjdbefpwajpfro/8/9bJK7lyNehzUCcmbhJnijJbbChE7drIOF+uSaVxVg==" + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.4.tgz", + "integrity": "sha512-8lf5ixlA312EgA2DorwbpNXXPfLPzUHO67exIV7SjKiU23Tn1au5GD6hT0Ysr2kophOs10Mp1TCXJjhEq7Qk4A==" }, "node_modules/@sasjs/lint": { "version": "2.3.1", @@ -1834,9 +1834,9 @@ } }, "@sasjs/core": { - "version": "4.52.1", - "resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.1.tgz", - "integrity": "sha512-XPNuKD1T5XLGMKg4Ll3KggOTjhHgnjdbefpwajpfro/8/9bJK7lyNehzUCcmbhJnijJbbChE7drIOF+uSaVxVg==" + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.4.tgz", + "integrity": "sha512-8lf5ixlA312EgA2DorwbpNXXPfLPzUHO67exIV7SjKiU23Tn1au5GD6hT0Ysr2kophOs10Mp1TCXJjhEq7Qk4A==" }, "@sasjs/lint": { "version": "2.3.1", diff --git a/sas/package.json b/sas/package.json index 90ae3bc..246cfc8 100644 --- a/sas/package.json +++ b/sas/package.json @@ -29,6 +29,6 @@ "private": true, "dependencies": { "@sasjs/cli": "^4.11.1", - "@sasjs/core": "^4.52.1" + "@sasjs/core": "^4.52.4" } }