diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..642937a
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,6 @@
+**/node_modules
+.gitignore
+.dockerignore
+Dockerfile
+docker-compose.yml
+package-lock.json
diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 0000000..d97b6e1
--- /dev/null
+++ b/.drone.yml
@@ -0,0 +1,77 @@
+---
+global-variables:
+ release: &release ${DRONE_BRANCH##/**}
+ environment: &environment
+ CRE_REGISTRY_IMAGE: registry.fyloz.dev:5443/colorrecipesexplorer/frontend
+ CRE_PORT: 9102
+ CRE_RELEASE: *release
+ alpine-image: &alpine-image alpine:latest
+ docker-registry-repo: &docker-registry-repo registry.fyloz.dev:5443/colorrecipesexplorer/frontend
+
+kind: pipeline
+name: default
+type: docker
+
+steps:
+ - name: set-docker-tags-latest
+ image: *alpine-image
+ environment:
+ <<: *environment
+ commands:
+ - echo -n "latest" > .tags
+ when:
+ branch: develop
+
+ - name: set-docker-tags-release
+ image: *alpine-image
+ environment:
+ <<: *environment
+ commands:
+ - echo -n "latest-release,$CRE_RELEASE" > .tags
+ when:
+ branch: release/**
+
+ - name: containerize-dev
+ image: plugins/docker
+ environment:
+ <<: *environment
+ settings:
+ repo: *docker-registry-repo
+ when:
+ branch:
+ - develop
+ - release/**
+
+ - name: deploy
+ image: alpine:latest
+ environment:
+ <<: *environment
+ CRE_REGISTRY_IMAGE: *docker-registry-repo
+ DEPLOY_SERVER:
+ from_secret: deploy_server
+ DEPLOY_SERVER_USERNAME:
+ from_secret: deploy_server_username
+ DEPLOY_SERVER_SSH_PORT:
+ from_secret: deploy_server_ssh_port
+ DEPLOY_SERVER_SSH_KEY:
+ from_secret: deploy_server_ssh_key
+ DEPLOY_CONTAINER_NAME: cre_frontend-${DRONE_BRANCH}
+ commands:
+ - apk update
+ - apk add --no-cache openssh-client
+ - mkdir -p ~/.ssh
+ - echo "$DEPLOY_SERVER_SSH_KEY" | tr -d '\r' > ~/.ssh/id_rsa
+ - chmod 700 ~/.ssh/id_rsa
+ - eval $(ssh-agent -s)
+ - ssh-add ~/.ssh/id_rsa
+ - ssh-keyscan -p $DEPLOY_SERVER_SSH_PORT -H $DEPLOY_SERVER >> ~/.ssh/known_hosts
+ - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
+ - ssh -p $DEPLOY_SERVER_SSH_PORT $DEPLOY_SERVER_USERNAME@$DEPLOY_SERVER "docker stop $DEPLOY_CONTAINER_NAME || true && docker rm $DEPLOY_CONTAINER_NAME || true"
+ - ssh -p $DEPLOY_SERVER_SSH_PORT $DEPLOY_SERVER_USERNAME@$DEPLOY_SERVER "docker pull $CRE_REGISTRY_IMAGE:$CRE_RELEASE"
+ - ssh -p $DEPLOY_SERVER_SSH_PORT $DEPLOY_SERVER_USERNAME@$DEPLOY_SERVER "docker run -d -p $CRE_PORT:80 --name=$DEPLOY_CONTAINER_NAME $CRE_REGISTRY_IMAGE:$CRE_RELEASE"
+ when:
+ branch: release/**
+trigger:
+ branch:
+ - develop
+ - master
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 74cbfb6..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,72 +0,0 @@
-variables:
- CI_REGISTRY_IMAGE_NG: "$CI_REGISTRY_IMAGE:latest-ng"
- CI_REGISTRY_IMAGE_FRONTEND: "$CI_REGISTRY_IMAGE:latest"
-
-before_script:
- - docker info
- - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
-
-stages:
- - build
- - package
- - deploy
-
-.only-master:
- only:
- - master
-
-build:
- stage: build
- extends: .only-master
- script:
- - docker pull $CI_REGISTRY_IMAGE_NG || true
- - docker build --cache-from $CI_REGISTRY_IMAGE_NG -f ng.Dockerfile -t $CI_REGISTRY_IMAGE_NG .
- - docker push $CI_REGISTRY_IMAGE_NG
-
-package:
- stage: package
- needs: ['build']
- extends: .only-master
- variables:
- PACKAGE_CONTAINER_NAME: "cre_frontend_package"
- ARTIFACT_NAME: "ColorRecipesExplorer-frontend-$CI_PIPELINE_IID"
- script:
- - apk update
- - apk add --no-cache zip
- - mkdir dist
- - docker run --name $PACKAGE_CONTAINER_NAME $CI_REGISTRY_IMAGE_NG ng build --configuration=$ANGULAR_CONFIGURATION --output-hashing=none --stats-json --source-map=false
- - docker cp $PACKAGE_CONTAINER_NAME:/usr/src/cre/dist/color-recipes-explorer-frontend/ dist/
- - zip -r $ARTIFACT_NAME.zip dist/
- - docker build -t $CI_REGISTRY_IMAGE_FRONTEND --build-arg ARTIFACT_NAME=$ARTIFACT_NAME .
- - docker push $CI_REGISTRY_IMAGE_FRONTEND
- after_script:
- - docker stop $PACKAGE_CONTAINER_NAME || true
- - docker rm $PACKAGE_CONTAINER_NAME || true
- artifacts:
- paths:
- - $ARTIFACT_NAME.zip
- expire_in: 1 week
-
-deploy:
- stage: deploy
- image: alpine:latest
- needs: ['package']
- extends: .only-master
- variables:
- DEPLOYED_CONTAINER_NAME: "cre_frontend"
- before_script:
- - apk update
- - apk add --no-cache openssh-client
- - mkdir -p ~/.ssh
- - echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
- - chmod 700 ~/.ssh/id_rsa
- - eval $(ssh-agent -s)
- - ssh-add ~/.ssh/id_rsa
- - ssh-keyscan -p $DEPLOYMENT_SERVER_SSH_PORT -H $DEPLOYMENT_SERVER >> ~/.ssh/known_hosts
- - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- script:
- - ssh -p $DEPLOYMENT_SERVER_SSH_PORT $DEPLOYMENT_SERVER_USERNAME@$DEPLOYMENT_SERVER "docker stop $DEPLOYED_CONTAINER_NAME || true && docker rm $DEPLOYED_CONTAINER_NAME || true"
- - ssh -p $DEPLOYMENT_SERVER_SSH_PORT $DEPLOYMENT_SERVER_USERNAME@$DEPLOYMENT_SERVER "docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && docker pull $CI_REGISTRY_IMAGE_FRONTEND"
- - ssh -p $DEPLOYMENT_SERVER_SSH_PORT $DEPLOYMENT_SERVER_USERNAME@$DEPLOYMENT_SERVER "docker run -d -p $PORT:80 --name=$DEPLOYED_CONTAINER_NAME $CI_REGISTRY_IMAGE_FRONTEND"
-
-
diff --git a/Dockerfile b/Dockerfile
index 464c4fc..1325a16 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,17 +1,29 @@
-FROM nginx:mainline-alpine
-
-WORKDIR /usr/bin/cre/
-
-ARG ARTIFACT_NAME=ColorRecipesExplorer-ng
-COPY $ARTIFACT_NAME.zip .
-COPY nginx.conf /etc/nginx/nginx.conf
+FROM alpine:latest AS build
+WORKDIR /usr/src/
RUN apk update
-RUN apk add --no-cache zip
+RUN apk add --no-cache nodejs npm
-RUN unzip $ARTIFACT_NAME.zip
-RUN rm $ARTIFACT_NAME.zip
+RUN npm install -g typescript@4.0.7 && \
+ npm install -g @angular/cli@11.2.9 || true --fo
-EXPOSE 80
+ENV NG_CLI_ANALYTICS=ci
+
+COPY . .
+
+ARG ANGULAR_CONFIGURATION=production
+
+RUN npm install --force
+RUN ng build --configuration=$ANGULAR_CONFIGURATION --stats-json --source-map=false
+
+FROM nginx:mainline-alpine
+
+WORKDIR /usr/bin/
+
+COPY nginx.conf /etc/nginx/nginx.conf
+COPY --from=build /usr/src/dist/color-recipes-explorer-frontend/ .
+
+ARG CRE_PORT=80
+EXPOSE $CRE_PORT
CMD ["nginx", "-g", "daemon off;"]
diff --git a/docker-compose.yml b/docker-compose.yml
index 135ab08..11391cd 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,18 +8,18 @@ services:
MYSQL_ROOT_PASSWORD: "pass"
MYSQL_DATABASE: "cre"
ports:
- - 3306:3306
+ - "3306:3306"
backend:
- image: fyloz.dev:5443/color-recipes-explorer/backend:master
+ image: registry.fyloz.dev:5443/colorrecipesexplorer/backend:latest
environment:
spring_profiles_active: "mysql,debug"
cre_database_url: "mysql://database:3306/cre"
cre_database_username: "root"
cre_database_password: "pass"
- CRE_ENABLE_DB_UPDATE: 0
+ CRE_ENABLE_DB_UPDATE: 1
server_port: 9090
ports:
- - 9090:9090
+ - "9090:9090"
volumes:
- cre_data:/usr/bin/cre/data
- cre_config:/usr/bin/cre/config
diff --git a/ng.Dockerfile b/ng.Dockerfile
deleted file mode 100644
index 92c9ee8..0000000
--- a/ng.Dockerfile
+++ /dev/null
@@ -1,17 +0,0 @@
-FROM alpine:latest
-WORKDIR /usr/src/cre/
-
-RUN apk update
-RUN apk add --no-cache nodejs
-RUN apk add --no-cache npm
-
-RUN npm install -g typescript@4.0.7
-RUN npm install -g @angular/cli@11.2.9 || true
-
-ENV NG_CLI_ANALYTICS=ci
-
-COPY package.json .
-
-RUN npm install --force
-
-COPY . .
diff --git a/nginx.conf b/nginx.conf
index 1f73fad..dea68be 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -5,7 +5,7 @@ events { worker_connections 1024; }
http {
server {
listen 80;
- root /usr/bin/cre/dist/color-recipes-explorer-frontend;
+ root /usr/bin/;
include /etc/nginx/mime.types;
location / {
diff --git a/src/_variables.scss b/src/_variables.scss
new file mode 100644
index 0000000..4ff7528
--- /dev/null
+++ b/src/_variables.scss
@@ -0,0 +1,20 @@
+@import "assets/sass/modules/fonts";
+@import "custom-theme";
+@import "~material-design-icons/iconfont/material-icons.css";
+
+// Spacing
+$spacer: 1rem;
+$spacers: (
+ 1: $spacer * 0.5,
+ 2: $spacer * 0.75,
+ 3: $spacer,
+ 4: $spacer * 1.5,
+ 5: $spacer * 2
+);
+
+// Colors
+$color-primary: map-get($theme-primary, 500);
+$text-color-primary: white;
+
+$color-accent: map-get($theme-accent, 500);
+$color-warn: map-get($theme-error, 500);
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 24a2458..a0ef43d 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -3,8 +3,7 @@ import {Routes, RouterModule} from '@angular/router'
import {CatalogComponent} from './pages/catalog/catalog.component'
import {AdministrationComponent} from './pages/administration/administration.component'
import {MiscComponent} from './pages/others/misc.component'
-import {CreConfigEditor} from './modules/configuration/config'
-
+import {CreConfigEditor} from './modules/configuration/config-editor'
const routes: Routes = [{
path: 'color',
diff --git a/src/app/modules/colors/components/mix-table/mix-table.component.sass b/src/app/modules/colors/components/mix-table/mix-table.component.sass
index 927de1b..1f8c7e4 100644
--- a/src/app/modules/colors/components/mix-table/mix-table.component.sass
+++ b/src/app/modules/colors/components/mix-table/mix-table.component.sass
@@ -1,4 +1,4 @@
-@import '../../../../../custom-theme'
+@import "~src/variables"
mat-expansion-panel
width: 48rem
diff --git a/src/app/modules/configuration/bool.html b/src/app/modules/configuration/bool.html
deleted file mode 100644
index 48709db..0000000
--- a/src/app/modules/configuration/bool.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- {{lastUpdated}}
-
diff --git a/src/app/modules/configuration/config-bool.html b/src/app/modules/configuration/config-bool.html
new file mode 100644
index 0000000..0f7a298
--- /dev/null
+++ b/src/app/modules/configuration/config-bool.html
@@ -0,0 +1,6 @@
+
+
+
+ {{inputHint}}
+
+
diff --git a/src/app/modules/configuration/config-container.html b/src/app/modules/configuration/config-container.html
new file mode 100644
index 0000000..8c928f0
--- /dev/null
+++ b/src/app/modules/configuration/config-container.html
@@ -0,0 +1,6 @@
+
+
+
diff --git a/src/app/modules/configuration/config-date.html b/src/app/modules/configuration/config-date.html
new file mode 100644
index 0000000..2f443e3
--- /dev/null
+++ b/src/app/modules/configuration/config-date.html
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/src/app/modules/configuration/config-editor.html b/src/app/modules/configuration/config-editor.html
new file mode 100644
index 0000000..9bb90b6
--- /dev/null
+++ b/src/app/modules/configuration/config-editor.html
@@ -0,0 +1,120 @@
+
+
+
+
+
diff --git a/src/app/modules/configuration/config-editor.ts b/src/app/modules/configuration/config-editor.ts
new file mode 100644
index 0000000..bde9502
--- /dev/null
+++ b/src/app/modules/configuration/config-editor.ts
@@ -0,0 +1,98 @@
+import {Component, ViewChild} from '@angular/core'
+import {ErrorHandlingComponent} from '../shared/components/subscribing.component'
+import {ConfirmBoxComponent} from '../shared/components/confirm-box/confirm-box.component'
+import {buildFormControl, Config, ConfigControl} from '../shared/model/config.model'
+import {FormBuilder, FormControl, FormGroup} from '@angular/forms'
+import {ConfigService} from '../shared/service/config.service'
+import {ErrorService} from '../shared/service/error.service'
+import {ActivatedRoute, Router} from '@angular/router'
+
+@Component({
+ selector: 'cre-config-editor',
+ templateUrl: 'config-editor.html'
+})
+export class CreConfigEditor extends ErrorHandlingComponent {
+ @ViewChild('restartingConfirmBox', {static: true}) restartConfirmBox: ConfirmBoxComponent
+
+ keys = {
+ INSTANCE_NAME: Config.INSTANCE_NAME,
+ INSTANCE_LOGO_PATH: Config.INSTANCE_LOGO_PATH,
+ INSTANCE_ICON_PATH: Config.INSTANCE_ICON_PATH,
+ INSTANCE_URL: Config.INSTANCE_URL,
+ DATABASE_URL: Config.DATABASE_URL,
+ DATABASE_USER: Config.DATABASE_USER,
+ DATABASE_PASSWORD: Config.DATABASE_PASSWORD,
+ DATABASE_VERSION: Config.DATABASE_VERSION,
+ RECIPE_APPROBATION_EXPIRATION: Config.RECIPE_APPROBATION_EXPIRATION,
+ TOUCH_UP_KIT_CACHE_PDF: Config.TOUCH_UP_KIT_CACHE_PDF,
+ TOUCH_UP_KIT_EXPIRATION: Config.TOUCH_UP_KIT_EXPIRATION,
+ BACKEND_BUILD_VERSION: Config.BACKEND_BUILD_VERSION,
+ BACKEND_BUILD_TIME: Config.BACKEND_BUILD_TIME,
+ JAVA_VERSION: Config.JAVA_VERSION,
+ OPERATING_SYSTEM: Config.OPERATING_SYSTEM
+ }
+
+ configs = new Map()
+ form: FormGroup | null
+
+ constructor(
+ private configService: ConfigService,
+ formBuilder: FormBuilder,
+ errorService: ErrorService,
+ activatedRoute: ActivatedRoute,
+ router: Router
+ ) {
+ super(errorService, activatedRoute, router)
+
+ this.fetchConfigurations(formBuilder)
+ }
+
+ ngOnInit() {
+ super.ngOnInit()
+ }
+
+ getConfigControl(key: string): ConfigControl {
+ return {
+ config: this.configs.get(key),
+ control: this.form.controls[key] as FormControl
+ }
+ }
+
+ onSubmit() {
+ this.subscribe(
+ this.configService.setFromForm(this.form),
+ () => this.reload()
+ )
+ }
+
+ restart() {
+ this.subscribe(
+ this.configService.restart(),
+ () => this.restartConfirmBox.show()
+ )
+ }
+
+ reload() {
+ window.location.reload()
+ }
+
+ get emergencyMode(): boolean {
+ return this.configs.get(Config.EMERGENCY_MODE).content === 'true';
+ }
+
+ private fetchConfigurations(formBuilder: FormBuilder) {
+ this.subscribe(
+ this.configService.all,
+ configurations => this.buildForm(formBuilder, configurations)
+ )
+ }
+
+ private buildForm(formBuilder: FormBuilder, configurations: Config[]) {
+ const group = {}
+ configurations.forEach(config => {
+ group[config.key] = buildFormControl(config)
+ this.configs.set(config.key, config)
+ })
+ this.form = formBuilder.group(group)
+ }
+}
diff --git a/src/app/modules/configuration/config-image.html b/src/app/modules/configuration/config-image.html
new file mode 100644
index 0000000..084aad1
--- /dev/null
+++ b/src/app/modules/configuration/config-image.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
{{lastUpdated}}
+
+
+
diff --git a/src/app/modules/configuration/config-period.html b/src/app/modules/configuration/config-period.html
new file mode 100644
index 0000000..90d8af9
--- /dev/null
+++ b/src/app/modules/configuration/config-period.html
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/src/app/modules/configuration/config-section.html b/src/app/modules/configuration/config-section.html
index 08c2b94..8d02e34 100644
--- a/src/app/modules/configuration/config-section.html
+++ b/src/app/modules/configuration/config-section.html
@@ -1,8 +1,6 @@
-
-
-
+ {{label}}
diff --git a/src/app/modules/configuration/config-secure.html b/src/app/modules/configuration/config-secure.html
new file mode 100644
index 0000000..aaea0b0
--- /dev/null
+++ b/src/app/modules/configuration/config-secure.html
@@ -0,0 +1,24 @@
+
+
+ {{buttonLabel}}
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/configuration/config-text.html b/src/app/modules/configuration/config-text.html
new file mode 100644
index 0000000..1878b43
--- /dev/null
+++ b/src/app/modules/configuration/config-text.html
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/src/app/modules/configuration/config.html b/src/app/modules/configuration/config.html
deleted file mode 100644
index 8f23ca1..0000000
--- a/src/app/modules/configuration/config.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/src/app/modules/configuration/config.module.ts b/src/app/modules/configuration/config.module.ts
index 8127ec2..d3d6bba 100644
--- a/src/app/modules/configuration/config.module.ts
+++ b/src/app/modules/configuration/config.module.ts
@@ -1,32 +1,35 @@
import {NgModule} from '@angular/core'
import {
- CreConfig,
- CreConfigLabel,
- CreConfigEditor,
- CreConfigSection,
- CreImageConfig,
- CreConfigList,
+ CreBoolConfig,
CreConfigActions,
- CreConfigTooltip, CrePeriodConfig, CreBoolConfig, CreDateConfig
+ CreConfigContainer,
+ CreConfigList,
+ CreConfigSection,
+ CreDateConfig,
+ CreImageConfig,
+ CrePeriodConfig,
+ CreSecureConfig,
+ CreTextConfig
} from './config'
import {SharedModule} from '../shared/shared.module'
import {CreInputsModule} from '../shared/components/inputs/inputs.module'
import {CreActionBarModule} from '../shared/components/action-bar/action-bar.module'
import {CreButtonsModule} from '../shared/components/buttons/buttons.module'
+import {CreConfigEditor} from './config-editor'
@NgModule({
declarations: [
- CreConfigLabel,
- CreConfigTooltip,
- CreConfigEditor,
- CreConfig,
- CreImageConfig,
- CreConfigSection,
CreConfigList,
CreConfigActions,
+ CreConfigSection,
+ CreConfigContainer,
+ CreTextConfig,
+ CreImageConfig,
CreBoolConfig,
CrePeriodConfig,
- CreDateConfig
+ CreDateConfig,
+ CreSecureConfig,
+ CreConfigEditor
],
imports: [
SharedModule,
@@ -35,4 +38,5 @@ import {CreButtonsModule} from '../shared/components/buttons/buttons.module'
CreButtonsModule
]
})
-export class ConfigModule { }
+export class ConfigModule {
+}
diff --git a/src/app/modules/configuration/config.sass b/src/app/modules/configuration/config.sass
index 6812169..0e8f839 100644
--- a/src/app/modules/configuration/config.sass
+++ b/src/app/modules/configuration/config.sass
@@ -1,10 +1,10 @@
mat-hint
font-size: .8em
-cre-config
+cre-config-container
display: block
- cre-input.has-hint
+ .cre-config:not(.cre-editable-config)
margin-bottom: 1em
mat-hint
diff --git a/src/app/modules/configuration/config.ts b/src/app/modules/configuration/config.ts
index a40e7de..ff20401 100644
--- a/src/app/modules/configuration/config.ts
+++ b/src/app/modules/configuration/config.ts
@@ -1,56 +1,12 @@
-import {
- AfterViewInit,
- Component,
- ContentChild,
- Directive,
- ElementRef,
- EventEmitter,
- Input,
- Output,
- ViewChild,
- ViewEncapsulation
-} from '@angular/core'
+import {AfterViewInit, Component, ContentChild, Directive, EventEmitter, Input, Output, ViewChild, ViewEncapsulation} from '@angular/core'
import {ConfigService} from '../shared/service/config.service'
-import {Config} from '../shared/model/config.model'
-import {ErrorHandlingComponent, SubscribingComponent} from '../shared/components/subscribing.component'
+import {Config, ConfigControl} from '../shared/model/config.model'
+import {SubscribingComponent} from '../shared/components/subscribing.component'
import {ErrorService} from '../shared/service/error.service'
import {ActivatedRoute, Router} from '@angular/router'
import {formatDate, formatDateTime, getFileUrl, readFile} from '../shared/utils/utils'
-import {FormControl, Validators} from '@angular/forms'
-import {ConfirmBoxComponent} from '../shared/components/confirm-box/confirm-box.component'
-import {environment} from '../../../environments/environment'
-
-@Directive({
- selector: 'cre-config-label'
-})
-export class CreConfigLabel implements AfterViewInit {
- content: string
-
- constructor(
- private element: ElementRef
- ) {
- }
-
- ngAfterViewInit(): void {
- this.content = this.element.nativeElement.innerHTML
- }
-}
-
-@Directive({
- selector: 'cre-config-tooltip'
-})
-export class CreConfigTooltip implements AfterViewInit {
- content: string
-
- constructor(
- private element: ElementRef
- ) {
- }
-
- ngAfterViewInit(): void {
- this.content = this.element.nativeElement.innerHTML
- }
-}
+import {AbstractControl} from '@angular/forms'
+import {CrePromptDialog} from '../shared/components/dialogs/dialogs'
@Directive({
selector: 'cre-config-list'
@@ -70,6 +26,8 @@ export class CreConfigActions {
templateUrl: 'config-section.html'
})
export class CreConfigSection {
+ @Input() label: string
+
@ContentChild(CreConfigActions) actions: CreConfigActions
get hasActions(): boolean {
@@ -78,17 +36,25 @@ export class CreConfigSection {
}
@Component({
- selector: 'cre-config',
- templateUrl: 'config.html',
- styleUrls: ['config.sass']
+ selector: 'cre-config-container',
+ templateUrl: 'config-container.html',
+ styleUrls: ['config.sass'],
+ encapsulation: ViewEncapsulation.None
})
-export class CreConfig extends SubscribingComponent {
- @Input() config: { key: string, control: FormControl }
+export class CreConfigContainer {
+ @Input() configuration?: Config
+ @Input() tooltip: string
- @ContentChild(CreConfigLabel, {static: true}) label: CreConfigLabel
- @ContentChild(CreConfigTooltip, {static: true}) tooltip: CreConfigTooltip
+ get readOnly(): boolean {
+ return !this.configuration?.editable ?? true
+ }
+}
- configuration: Config | null
+@Directive()
+abstract class _CreConfigBase extends SubscribingComponent {
+ @Input() configControl: ConfigControl
+ @Input() label: string
+ @Input() tooltip?: string
constructor(
private configService: ConfigService,
@@ -101,39 +67,105 @@ export class CreConfig extends SubscribingComponent {
ngOnInit() {
super.ngOnInit()
-
- this.subscribe(
- this.configService.get(this.config.key),
- config => this.setConfig(config)
- )
}
- setConfig(config: Config) {
- this.configuration = config
- this.config.control.setValue(config.content)
- if (!config.editable) {
- this.config.control.disable()
- }
+ get config(): Config {
+ return this.configControl.config
+ }
+
+ get control(): AbstractControl {
+ return this.configControl.control
}
get lastUpdated(): string {
- return 'Dernière mise à jour: ' + formatDateTime(this.configuration.lastUpdated)
+ return 'Dernière mise à jour: ' + formatDateTime(this.config.lastUpdated)
+ }
+
+ get inputHint(): string {
+ return this.config?.editable ? this.lastUpdated : null
+ }
+}
+
+@Directive()
+abstract class _CreTextConfigBase extends _CreConfigBase {
+ private static readonly REQUIRE_RESTART_ICON = 'alert'
+ private static readonly REQUIRE_RESTART_ICON_TITLE = 'Requiert un redémarrage'
+
+ get inputIcon(): string {
+ return this.config?.requireRestart ? _CreTextConfigBase.REQUIRE_RESTART_ICON : null
+ }
+
+ get inputIconTitle(): string {
+ return this.config?.requireRestart ? _CreTextConfigBase.REQUIRE_RESTART_ICON_TITLE : null
}
}
+@Component({
+ selector: 'cre-text-config',
+ templateUrl: 'config-text.html',
+ styleUrls: ['config.sass']
+})
+export class CreTextConfig extends _CreTextConfigBase {
+}
+
@Component({
selector: 'cre-image-config',
- templateUrl: 'image.html',
+ templateUrl: 'config-image.html',
styleUrls: ['config.sass'],
encapsulation: ViewEncapsulation.None
})
-export class CreImageConfig extends CreConfig {
+export class CreImageConfig extends _CreConfigBase {
@Input() previewWidth: string | null
@Output() invalidFormat = new EventEmitter()
updatedImage: any | null
+ updateImage(file: File): any {
+ readFile(file, (content) => this.updatedImage = content)
+ }
+
+
+ get configuredImageUrl(): string {
+ return getFileUrl(this.config.content)
+ }
+}
+
+@Component({
+ selector: 'cre-bool-config',
+ templateUrl: 'config-bool.html'
+})
+export class CreBoolConfig extends _CreConfigBase {
+}
+
+@Component({
+ selector: 'cre-period-config',
+ templateUrl: 'config-period.html'
+})
+export class CrePeriodConfig extends _CreConfigBase {
+}
+
+@Component({
+ selector: 'cre-date-config',
+ templateUrl: 'config-date.html'
+})
+export class CreDateConfig extends _CreTextConfigBase implements AfterViewInit {
+ ngAfterViewInit(): void {
+ this.control.setValue(formatDate(this.config.content))
+ }
+}
+
+@Component({
+ selector: 'cre-secure-config',
+ templateUrl: 'config-secure.html'
+})
+export class CreSecureConfig extends _CreTextConfigBase {
+ @ViewChild(CrePromptDialog) dialog: CrePromptDialog
+
+ @Input() buttonLabel: string
+
+ private initialValue: string | null
+
constructor(
configService: ConfigService,
errorService: ErrorService,
@@ -143,113 +175,12 @@ export class CreImageConfig extends CreConfig {
super(configService, errorService, activatedRoute, router)
}
- updateImage(file: File): any {
- readFile(file, (content) => this.updatedImage = content)
+ onOpen() {
+ this.initialValue = this.control.value
+ this.dialog.show()
}
-
- get configuredImageUrl(): string {
- return getFileUrl(this.configuration.content)
- }
-}
-
-@Component({
- selector: 'cre-bool-config',
- templateUrl: 'bool.html'
-})
-export class CreBoolConfig extends CreConfig {
- setConfig(config: Config) {
- super.setConfig(config)
- this.config.control.setValue(config.content === 'true')
- }
-}
-
-@Component({
- selector: 'cre-period-config',
- templateUrl: 'period.html'
-})
-export class CrePeriodConfig extends CreConfig {
-}
-
-@Component({
- selector: 'cre-date-config',
- templateUrl: 'date.html'
-})
-export class CreDateConfig extends CreConfig {
- setConfig(config: Config) {
- super.setConfig(config);
- this.config.control.setValue(formatDate(config.content))
- }
-}
-
-@Component({
- selector: 'cre-config-editor',
- templateUrl: 'editor.html'
-})
-export class CreConfigEditor extends ErrorHandlingComponent {
- @ViewChild('restartingConfirmBox', {static: true}) restartConfirmBox: ConfirmBoxComponent
-
- keys = {
- INSTANCE_NAME: Config.INSTANCE_NAME,
- INSTANCE_LOGO_PATH: Config.INSTANCE_LOGO_PATH,
- INSTANCE_ICON_PATH: Config.INSTANCE_ICON_PATH,
- INSTANCE_URL: Config.INSTANCE_URL,
- DATABASE_URL: Config.DATABASE_URL,
- DATABASE_USER: Config.DATABASE_USER,
- DATABASE_PASSWORD: Config.DATABASE_PASSWORD,
- DATABASE_VERSION: Config.DATABASE_VERSION,
- RECIPE_APPROBATION_EXPIRATION: Config.RECIPE_APPROBATION_EXPIRATION,
- TOUCH_UP_KIT_CACHE_PDF: Config.TOUCH_UP_KIT_CACHE_PDF,
- TOUCH_UP_KIT_EXPIRATION: Config.TOUCH_UP_KIT_EXPIRATION,
- BACKEND_BUILD_VERSION: Config.BACKEND_BUILD_VERSION,
- BACKEND_BUILD_TIME: Config.BACKEND_BUILD_TIME,
- JAVA_VERSION: Config.JAVA_VERSION,
- OPERATING_SYSTEM: Config.OPERATING_SYSTEM
- }
- controls = new Map()
- emergencyMode: string | null
-
- constructor(
- private configService: ConfigService,
- errorService: ErrorService,
- activatedRoute: ActivatedRoute,
- router: Router
- ) {
- super(errorService, activatedRoute, router)
-
- for (let key in this.keys) {
- this.controls[this.keys[key]] = new FormControl(null, Validators.required)
- }
- }
-
- ngOnInit() {
- this.subscribe(
- this.configService.get(Config.EMERGENCY_MODE),
- config => {
- this.emergencyMode = config.content
- }
- )
- }
-
- getConfig(key: string) {
- return {key, control: this.controls[key]}
- }
-
- save() {
- this.subscribe(
- this.configService.set(this.controls),
- () => this.reload()
- )
- }
-
- restart() {
- this.subscribe(
- this.configService.restart(),
- () => this.restartConfirmBox.show()
- )
- }
-
- reload() {
- window.location.reload()
+ onCancel() {
+ this.control.setValue(this.initialValue)
}
}
diff --git a/src/app/modules/configuration/date.html b/src/app/modules/configuration/date.html
deleted file mode 100644
index 83e8af4..0000000
--- a/src/app/modules/configuration/date.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/src/app/modules/configuration/editor.html b/src/app/modules/configuration/editor.html
deleted file mode 100644
index 66cd181..0000000
--- a/src/app/modules/configuration/editor.html
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
- Retour
-
-
- Enregistrer
-
-
-
-
-
- Apparence
-
-
-
-
-
-
-
-
-
- Logo
-
- Affiché dans la bannière de l'application web. Il peut être nécessaire de forcer le
- rafraîchissement du cache du navigateur pour que ce changement prenne effet (généralement avec les touches
- 'Ctrl+F5').
-
-
-
-
- Icône
-
- Affiché dans l'onglet de la page dans le navigateur. Il peut être nécessaire de forcer le
- rafraîchissement du cache du navigateur pour que ce changement prenne effet (généralement avec les touches
- 'Ctrl+F5').
-
-
-
-
-
-
- Données
-
-
- Période d'expiration de l'approbation de l'échantillon des recettes
-
-
-
- Période d'expiration des kits de retouches complets
-
- Les kits de retouche complétés expirent après la période configurée. Les kits de retouche expirés seront supprimés automatiquement.
-
-
-
-
- Activer le cache des PDFs générés
-
- Cette option permet de stocker les PDFs générés sur le disque, ce qui permet d'accélérer
- l'accès aux PDFs si la lecture des fichiers cachés sur le disque est plus rapide que la génération d'un
- nouveau PDF.
-
-
-
-
-
-
- Système
-
-
- URL de l'instance
-
- Utilisé pour générer l'URL de certaines ressources, comme les images et les fiches signalitiques.
-
-
-
-
- URL de la base de données
-
-
-
- Utilisateur de la base de données
-
-
-
- Mot de passe de la base de données
-
-
-
- Version de la base de données
-
-
-
- Version de Color Recipes Explorer
-
-
-
- Date de compilation de Color Recipes Explorer
-
-
-
- Version de Java
-
-
-
- Système d'exploitation
-
-
-
- Redémarrer le serveur
-
-
-
-
-
-
-
diff --git a/src/app/modules/configuration/image.html b/src/app/modules/configuration/image.html
deleted file mode 100644
index 21459eb..0000000
--- a/src/app/modules/configuration/image.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
{{lastUpdated}}
-
-
diff --git a/src/app/modules/configuration/period.html b/src/app/modules/configuration/period.html
deleted file mode 100644
index 722ec27..0000000
--- a/src/app/modules/configuration/period.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
diff --git a/src/app/modules/shared/components/buttons/buttons.sass b/src/app/modules/shared/components/buttons/buttons.sass
new file mode 100644
index 0000000..3c36340
--- /dev/null
+++ b/src/app/modules/shared/components/buttons/buttons.sass
@@ -0,0 +1,6 @@
+cre-button, cre-primary-button, cre-accent-button, cre-warn-button
+ display: inline-block
+ width: inherit
+
+ button
+ width: 100%
diff --git a/src/app/modules/shared/components/buttons/buttons.ts b/src/app/modules/shared/components/buttons/buttons.ts
index 7b3679e..80ab652 100644
--- a/src/app/modules/shared/components/buttons/buttons.ts
+++ b/src/app/modules/shared/components/buttons/buttons.ts
@@ -1,51 +1,60 @@
-import {Component, Input} from '@angular/core'
+import {Component, Input, ViewEncapsulation} from '@angular/core'
import {ThemePalette} from '@angular/material/core'
@Component({
selector: 'cre-button',
template: `
-
+
- `
+ `,
+ styleUrls: ['buttons.sass'],
+ encapsulation: ViewEncapsulation.None
})
export class CreButtonComponent {
@Input() color: ThemePalette
+ @Input() type = 'button'
@Input() disabled = false
}
@Component({
selector: 'cre-primary-button',
template: `
-
+
- `
+ `,
+ styleUrls: ['buttons.sass']
})
export class CrePrimaryButtonComponent {
+ @Input() type = 'button'
@Input() disabled = false
}
@Component({
selector: 'cre-accent-button',
template: `
-
+
- `
+ `,
+ styleUrls: ['buttons.sass']
})
export class CreAccentButtonComponent {
+ @Input() type = 'button'
@Input() disabled = false
}
@Component({
selector: 'cre-warn-button',
template: `
-
+
- `
+ `,
+ styleUrls: ['buttons.sass']
})
export class CreWarnButtonComponent {
+ @Input() type = 'button'
@Input() disabled = false
}
diff --git a/src/app/modules/shared/components/dialogs/dialogs.module.ts b/src/app/modules/shared/components/dialogs/dialogs.module.ts
new file mode 100644
index 0000000..ac98cdc
--- /dev/null
+++ b/src/app/modules/shared/components/dialogs/dialogs.module.ts
@@ -0,0 +1,21 @@
+import {NgModule} from '@angular/core'
+import {CreDialogBody, CrePromptDialog} from './dialogs'
+import {MatDialogModule} from '@angular/material/dialog'
+import {CreButtonsModule} from '../buttons/buttons.module'
+
+@NgModule({
+ declarations: [
+ CrePromptDialog,
+ CreDialogBody
+ ],
+ exports: [
+ CrePromptDialog,
+ CreDialogBody
+ ],
+ imports: [
+ MatDialogModule,
+ CreButtonsModule
+ ]
+})
+export class CreDialogsModule {
+}
diff --git a/src/app/modules/shared/components/dialogs/dialogs.scss b/src/app/modules/shared/components/dialogs/dialogs.scss
new file mode 100644
index 0000000..3653e6c
--- /dev/null
+++ b/src/app/modules/shared/components/dialogs/dialogs.scss
@@ -0,0 +1,26 @@
+@import "~src/variables";
+
+.cre-dialog-panel {
+ min-width: 20rem;
+
+ mat-dialog-container {
+ padding: 0;
+
+ .mat-dialog-title, .mat-dialog-content, .mat-dialog-actions {
+ margin: 0;
+ padding: $spacer;
+ }
+
+ .mat-dialog-title {
+ background-color: $color-primary;
+ color: $text-color-primary;
+ }
+
+ .mat-dialog-actions {
+ min-height: auto;
+ justify-content: end;
+ gap: map-get($spacers, 1);
+ padding-top: 0;
+ }
+ }
+}
diff --git a/src/app/modules/shared/components/dialogs/dialogs.ts b/src/app/modules/shared/components/dialogs/dialogs.ts
new file mode 100644
index 0000000..a06f602
--- /dev/null
+++ b/src/app/modules/shared/components/dialogs/dialogs.ts
@@ -0,0 +1,74 @@
+import {Component, Directive, EventEmitter, Input, Output, TemplateRef, ViewChild, ViewEncapsulation} from '@angular/core'
+import {MatDialog, MatDialogRef} from '@angular/material/dialog'
+
+@Directive({
+ selector: 'cre-dialog-body'
+})
+export class CreDialogBody {
+}
+
+@Directive()
+abstract class CreDialog {
+ @ViewChild(TemplateRef) dialogTemplate: TemplateRef
+
+ @Output() cancel = new EventEmitter();
+ @Output() continue = new EventEmitter();
+
+ private dialogRef: MatDialogRef> | null
+
+ constructor(
+ protected dialog: MatDialog
+ ) {
+ }
+
+ protected abstract get data(): D
+
+ show() {
+ this.open()
+ }
+
+ onCancel() {
+ this.close()
+ this.cancel.emit();
+ }
+
+ onContinue() {
+ this.close()
+ this.continue.emit();
+ }
+
+ private open() {
+ const config = {
+ panelClass: 'cre-dialog-panel',
+ data: this.data
+ }
+ this.dialogRef = this.dialog.open(this.dialogTemplate, config)
+ }
+
+ private close() {
+ this.dialogRef.close()
+ }
+}
+
+@Component({
+ selector: 'cre-prompt-dialog',
+ templateUrl: 'prompt.html',
+ styleUrls: ['dialogs.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class CrePromptDialog extends CreDialog {
+ @Input() title: string
+
+ protected get data(): CrePromptDialogData {
+ return {
+ title: this.title
+ }
+ }
+}
+
+abstract class CreDialogData {
+ title: string
+}
+
+class CrePromptDialogData extends CreDialogData {
+}
diff --git a/src/app/modules/shared/components/dialogs/prompt.html b/src/app/modules/shared/components/dialogs/prompt.html
new file mode 100644
index 0000000..0d14939
--- /dev/null
+++ b/src/app/modules/shared/components/dialogs/prompt.html
@@ -0,0 +1,10 @@
+
+ {{data.title}}
+
+
+
+
+ Annuler
+ Continuer
+
+
diff --git a/src/app/modules/shared/components/info-banner/info-banner.component.sass b/src/app/modules/shared/components/info-banner/info-banner.component.sass
index 558af2c..31c5568 100644
--- a/src/app/modules/shared/components/info-banner/info-banner.component.sass
+++ b/src/app/modules/shared/components/info-banner/info-banner.component.sass
@@ -1,4 +1,4 @@
-@import "~src/custom-theme"
+@import "~src/variables"
.info-banner-wrapper
background-color: $color-primary
diff --git a/src/app/modules/shared/components/inputs/input.html b/src/app/modules/shared/components/inputs/input.html
index d123b75..2bf813e 100644
--- a/src/app/modules/shared/components/inputs/input.html
+++ b/src/app/modules/shared/components/inputs/input.html
@@ -1,25 +1,21 @@
{{label}}
-
-
+
+
+
+
+
+
+
+
()
+ @ViewChild('input') input: any
@ContentChild(TemplateRef) errors: TemplateRef
+
+ ngAfterViewInit() {
+ const element = this.input.nativeElement
+ element.type = this.type
+ element.step = this.step.toString()
+ element.placeholder = this.placeholder
+ element.required = this.required
+ element.autocomplete = this.autocomplete ? 'on' : 'off'
+ }
}
@Component({
@@ -51,7 +66,7 @@ export class CreInputComponent {
encapsulation: ViewEncapsulation.None
})
export class CreAutocompleteInputComponent {
- @Input() control: FormControl | null
+ @Input() control: AbstractControl | null
@Input() label: string
@Input() icon: string
@Input() required = true
@@ -69,7 +84,7 @@ export class CreAutocompleteInputComponent {
encapsulation: ViewEncapsulation.None
})
export class CreChipInputComponent implements OnInit {
- @Input() control: FormControl
+ @Input() control: AbstractControl
@Input() label: string
@Input() icon: string
@Input() required = true
@@ -118,7 +133,7 @@ export class CreChipInputComponent implements OnInit {
encapsulation: ViewEncapsulation.None
})
export class CreComboBoxComponent {
- @Input() control: FormControl
+ @Input() control: AbstractControl
@Input() label: string
@Input() icon: string
@Input() required = true
@@ -184,12 +199,16 @@ export class CreChipComboBoxComponent extends CreChipInputComponent implements O
selector: 'cre-checkbox-input',
templateUrl: 'checkbox.html'
})
-export class CreCheckboxInputComponent {
+export class CreCheckboxInputComponent implements OnInit {
@Input() label: string
- @Input() control: FormControl
+ @Input() control: AbstractControl
@Input() checked: boolean
@Output() checkedChange = new EventEmitter()
+
+ ngOnInit(): void {
+ this.control?.setValue(this.control.value === 'true')
+ }
}
@Component({
@@ -200,7 +219,7 @@ export class CreFileInputComponent implements OnInit {
@Input() label: string
@Input() icon: string
@Input() accept = ''
- @Input() control: FormControl | null
+ @Input() control: AbstractControl | null
@Output() selection = new EventEmitter()
@Output() invalidFormat = new EventEmitter()
@@ -234,7 +253,7 @@ export class CreFileInputComponent implements OnInit {
encapsulation: ViewEncapsulation.None
})
export class CrePeriodInputComponent implements OnInit {
- @Input() control: FormControl
+ @Input() control: AbstractControl
@Input() label: string
@Input() hint: string | null
@@ -264,9 +283,12 @@ export class CrePeriodInputComponent implements OnInit {
}
private setValuesFromPeriod(period: string) {
+ if (!period) {
+ return
+ }
+
const periodTypeChar = period.slice(-1)
period = period.slice(1, -1)
-
this.selectControl.setValue(periodTypeChar)
this.inputControl.setValue(period)
}
diff --git a/src/app/modules/shared/components/tables/table.sass b/src/app/modules/shared/components/tables/table.sass
index 4458b6e..9cbb67d 100644
--- a/src/app/modules/shared/components/tables/table.sass
+++ b/src/app/modules/shared/components/tables/table.sass
@@ -1,4 +1,4 @@
-@import "~src/custom-theme"
+@import "../../../../../custom-theme"
cre-table
display: block
diff --git a/src/app/modules/shared/components/user-info/user-menu.component.sass b/src/app/modules/shared/components/user-info/user-menu.component.sass
index d3ffcd7..329544c 100644
--- a/src/app/modules/shared/components/user-info/user-menu.component.sass
+++ b/src/app/modules/shared/components/user-info/user-menu.component.sass
@@ -1,4 +1,4 @@
-@import "../../../../../custom-theme"
+@import "~src/variables"
p, labeled-icon
margin: 0
diff --git a/src/app/modules/shared/model/config.model.ts b/src/app/modules/shared/model/config.model.ts
index 642a736..02e4923 100644
--- a/src/app/modules/shared/model/config.model.ts
+++ b/src/app/modules/shared/model/config.model.ts
@@ -1,3 +1,6 @@
+import {AbstractControl, Form, FormControl, Validators} from '@angular/forms'
+import {filterMap} from '../utils/map.utils'
+
export class Config {
static readonly INSTANCE_NAME = 'instance.name'
static readonly INSTANCE_LOGO_PATH = 'instance.logo.path'
@@ -16,12 +19,63 @@ export class Config {
static readonly JAVA_VERSION = 'env.java.version'
static readonly OPERATING_SYSTEM = 'env.os'
- constructor(
- public key: string,
- public content: string,
- public lastUpdated: string,
- public requireRestart: boolean,
- public editable: boolean
- ) {
- }
+ static readonly IMAGE_CONFIG_KEYS = [
+ Config.INSTANCE_LOGO_PATH,
+ Config.INSTANCE_ICON_PATH
+ ]
+
+ static readonly PASSWORD_CONFIG_KEYS = [
+ Config.DATABASE_PASSWORD
+ ]
+
+ public key: string
+ public requireRestart: boolean
+ public editable: boolean
+ public content?: string
+ public lastUpdated?: string
+}
+
+export class ConfigKeyContent {
+ public key: string
+ public content: string
+}
+
+export class ConfigControl {
+ public config: Config
+ public control: AbstractControl
+}
+
+export function buildFormControl(config: Config): AbstractControl {
+ return new FormControl({value: config.content, disabled: !config.editable}, !configKeyIsPassword(config.key) ? Validators.required : null)
+}
+
+export function configKeyIsPassword(key: string): boolean {
+ return Config.PASSWORD_CONFIG_KEYS.indexOf(key) >= 0
+}
+
+export function filterConfigKeyControlMap(map: Map): Map {
+ return filterMap(map, (key, control) => {
+ return control.dirty &&
+ Config.IMAGE_CONFIG_KEYS.indexOf(key) < 0 && // Filter image configs because they are sent to a different endpoint
+ control.value !== undefined &&
+ control.value !== null
+ })
+}
+
+export function filterImageConfigKeyControlMap(map: Map): Map {
+ return filterMap(map, (key, control) => {
+ return Config.IMAGE_CONFIG_KEYS.indexOf(key) >= 0 && control.dirty
+ })
+}
+
+export function mapToConfigKeyContent(key: string, control: AbstractControl): ConfigKeyContent {
+ return {key, content: control.value}
+}
+
+export function mapToConfigKeyContentArray(map: Map): ConfigKeyContent[] {
+ const array: ConfigKeyContent[] = []
+ map.forEach((control, key) => {
+ array.push(mapToConfigKeyContent(key, control))
+ })
+ return array
}
diff --git a/src/app/modules/shared/service/config.service.ts b/src/app/modules/shared/service/config.service.ts
index 880b697..dcfd95d 100644
--- a/src/app/modules/shared/service/config.service.ts
+++ b/src/app/modules/shared/service/config.service.ts
@@ -1,13 +1,9 @@
import {Injectable} from '@angular/core'
-import {Config} from '../model/config.model'
+import {Config, filterConfigKeyControlMap, filterImageConfigKeyControlMap, mapToConfigKeyContentArray} from '../model/config.model'
import {Observable} from 'rxjs'
import {ApiService} from './api.service'
-import {FormControl} from '@angular/forms'
-
-const imageConfigsKeys = [
- Config.INSTANCE_LOGO_PATH,
- Config.INSTANCE_ICON_PATH
-]
+import {AbstractControl, FormGroup} from '@angular/forms'
+import {transformMap} from '../utils/map.utils'
@Injectable({
providedIn: 'root'
@@ -18,32 +14,27 @@ export class ConfigService {
) {
}
+ get all(): Observable {
+ return this.api.get('/config')
+ }
+
get(key: string): Observable {
return this.api.get(`/config/${key}`)
}
- set(configs: Map): Observable {
- const body = []
- for (let key in configs) {
- const control = configs[key]
- if (control.dirty && key.indexOf('path') < 0) {
- body.push({key, content: control.value})
- }
+ setFromForm(form: FormGroup): Observable {
+ const map = new Map()
+ for (let key in form.controls) {
+ map.set(key, form.controls[key])
}
+ return this.set(map);
+ }
- const subscriptions = []
- imageConfigsKeys.forEach(key => {
- if (configs[key].dirty) {
- subscriptions.push(this.setImage(key, configs[key].value))
- }
- })
-
- while (subscriptions.length > 0) {
- const subscription = subscriptions.pop().subscribe({
- next: () => subscription.unsubscribe()
- })
- }
+ set(configs: Map): Observable {
+ const body = mapToConfigKeyContentArray(filterConfigKeyControlMap(configs))
+ const imageConfigs = filterImageConfigKeyControlMap(configs)
+ this.setImages(imageConfigs)
return this.api.put('/config', body)
}
@@ -58,4 +49,19 @@ export class ConfigService {
restart(): Observable {
return this.api.post('/config/restart')
}
+
+ private setImages(configs: Map) {
+ const subscriptions = this.getImageConfigsSubscriptions(configs)
+ while (subscriptions.length > 0) {
+ const subscription = subscriptions.pop().subscribe({
+ next: () => subscription.unsubscribe()
+ })
+ }
+ }
+
+ private getImageConfigsSubscriptions(configs: Map): Observable[] {
+ return transformMap(configs, (key, control) => {
+ return this.setImage(key, control.value)
+ })
+ }
}
diff --git a/src/app/modules/shared/shared.module.ts b/src/app/modules/shared/shared.module.ts
index 1d7d62e..05aaf24 100644
--- a/src/app/modules/shared/shared.module.ts
+++ b/src/app/modules/shared/shared.module.ts
@@ -36,6 +36,7 @@ import {InfoBannerModule} from './components/info-banner/info-banner.module'
import {CreFormsModule} from './components/forms/forms.module'
import {VarDirective} from './directives/var.directive'
import {CreColorPreview} from './components/color-preview/color-preview'
+import {CreDialogsModule} from './components/dialogs/dialogs.module'
@NgModule({
declarations: [VarDirective, HeaderComponent, UserMenuComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent, SliderFieldComponent, LoadingWheelComponent, CreColorPreview],
@@ -71,7 +72,8 @@ import {CreColorPreview} from './components/color-preview/color-preview'
InfoBannerModule,
CreFormsModule,
VarDirective,
- CreColorPreview
+ CreColorPreview,
+ CreDialogsModule
],
imports: [
MatTabsModule,
diff --git a/src/app/modules/shared/utils/map.utils.ts b/src/app/modules/shared/utils/map.utils.ts
new file mode 100644
index 0000000..ba99aec
--- /dev/null
+++ b/src/app/modules/shared/utils/map.utils.ts
@@ -0,0 +1,17 @@
+export function filterMap(map: Map, predicate: (key: K, value: V) => boolean): Map {
+ const filteredMap = new Map()
+ map.forEach((value, key) => {
+ if (predicate(key, value)) {
+ filteredMap.set(key, value)
+ }
+ })
+ return filteredMap
+}
+
+export function transformMap(map: Map, transform: (key: K, value: V) => T): T[] {
+ const transformedArray = []
+ map.forEach((value, key) => {
+ transformedArray.push(transform(key, value))
+ })
+ return transformedArray
+}
diff --git a/src/app/modules/touch-up-kit/components/finish.sass b/src/app/modules/touch-up-kit/components/finish.sass
index 899e060..30c9991 100644
--- a/src/app/modules/touch-up-kit/components/finish.sass
+++ b/src/app/modules/touch-up-kit/components/finish.sass
@@ -1,4 +1,4 @@
-@import '~src/custom-theme'
+@import '../../../../custom-theme'
.touchupkit-finish-container
display: inline-block
diff --git a/src/custom-theme.scss b/src/custom-theme.scss
index 5f9a53b..5f766ef 100644
--- a/src/custom-theme.scss
+++ b/src/custom-theme.scss
@@ -122,10 +122,6 @@ $color-recipes-explorer-frontend-theme: mat-light-theme($theme-primary, $theme-a
// that you are using.
@include angular-material-theme($color-recipes-explorer-frontend-theme);
-$color-primary: map-get($theme-primary, 500);
-$color-accent: map-get($theme-accent, 500);
-$color-warn: map-get($theme-error, 500);
-
html, body {
height: 100%;
diff --git a/src/styles.sass b/src/styles.sass
index e026f75..c096222 100644
--- a/src/styles.sass
+++ b/src/styles.sass
@@ -1,6 +1,4 @@
-@import 'assets/sass/modules/_fonts.sass'
-@import "custom-theme"
-@import "~material-design-icons/iconfont/material-icons.css"
+@import "variables"
mat-card
padding: 0 !important