From 92c5cc4b070897fcdcd5699ad1532fb729f3dc99 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20AUDEON?= <mael.audeon@teamdlab.com>
Date: Tue, 31 Mar 2020 14:07:47 +0200
Subject: [PATCH] [DLAB-4011] create, update theme colors

---
 .../service/CustomerInternalService.java      | 16 ++--
 .../vitamui-input/vitamui-input.component.ts  | 11 ++-
 .../src/app/modules/startup.service.ts        | 19 +++--
 .../src/app/modules/theme.service.ts          | 49 +++--------
 .../src/app/modules/utils/colors.util.ts      | 62 ++++++++------
 .../src/sass/material/_preview-tab.scss       |  2 +-
 .../customer-colors-input.component.html      | 11 ++-
 .../customer-colors-input.component.scss      | 35 +++++---
 .../customer-colors-input.component.ts        | 85 ++++++++++---------
 .../customer-create.component.html            |  8 +-
 .../customer-create.component.ts              | 26 ++++--
 .../graphic-identity-tab.component.html       | 24 ++++--
 .../graphic-identity-tab.component.scss       | 38 ++++++++-
 .../graphic-identity-tab.component.ts         | 36 ++++++--
 .../graphic-identity-update.component.html    |  4 +-
 .../graphic-identity-update.component.ts      | 24 +++---
 16 files changed, 265 insertions(+), 185 deletions(-)

diff --git a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/customer/service/CustomerInternalService.java b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/customer/service/CustomerInternalService.java
index ea8dead0..e6436ce4 100644
--- a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/customer/service/CustomerInternalService.java
+++ b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/customer/service/CustomerInternalService.java
@@ -327,7 +327,7 @@ public class CustomerInternalService extends VitamUICrudService<CustomerDto, Cus
                 case "themeColors":
                     Object themeColorsValue = entry.getValue();
 
-                    System.out.println("UPDATE THEME COLORS : ");
+                    LOGGER.debug("Update theme colors");
                     System.out.println(themeColorsValue);
 
                     if (themeColorsValue instanceof Map) {
@@ -345,23 +345,23 @@ public class CustomerInternalService extends VitamUICrudService<CustomerDto, Cus
     }
 
     private void processGraphicIdentityPatch(final boolean newCustomGraphicIdentityValue, final Customer customer, final Optional<MultipartFile> logo) {
+
+        customer.getGraphicIdentity().setHasCustomGraphicIdentity(newCustomGraphicIdentityValue);
+
         String base64logo = null;
         if(logo.isPresent()) {
             try {
+
                 base64logo = VitamUIUtils.getBase64(logo.get());
+
+                customer.getGraphicIdentity().setLogoDataBase64(base64logo);
+
             } catch (IOException e) {
                 throw new InvalidFormatException("Cannot store logo", e);
             }
         }
 
-        if(newCustomGraphicIdentityValue ^ logo.isPresent()) {
-            throw new IllegalArgumentException(
-                String.format("Unable to patch customer %s : Custom graphic identity toggle doesn't match logo provision (logo provided without enabling customization, or customization enabled without providing a logo).", customer.getId()));
-        }
-
-        customer.getGraphicIdentity().setHasCustomGraphicIdentity(newCustomGraphicIdentityValue);
 
-        customer.getGraphicIdentity().setLogoDataBase64(base64logo);
     }
 
     @Transactional
diff --git a/ui/ui-frontend-common/src/app/modules/components/vitamui-input/vitamui-input.component.ts b/ui/ui-frontend-common/src/app/modules/components/vitamui-input/vitamui-input.component.ts
index 3ed7d367..03be8298 100644
--- a/ui/ui-frontend-common/src/app/modules/components/vitamui-input/vitamui-input.component.ts
+++ b/ui/ui-frontend-common/src/app/modules/components/vitamui-input/vitamui-input.component.ts
@@ -36,7 +36,7 @@
  */
 /* tslint:disable: no-use-before-declare */
 import { coerceBooleanProperty } from '@angular/cdk/coercion';
-import { Component, ElementRef, forwardRef, HostBinding, HostListener, Input, ViewChild } from '@angular/core';
+import {Component, ElementRef, forwardRef, HostBinding, HostListener, Input, OnInit, ViewChild} from '@angular/core';
 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
 
 export const VITAMUI_INPUT_VALUE_ACCESSOR: any = {
@@ -51,12 +51,13 @@ export const VITAMUI_INPUT_VALUE_ACCESSOR: any = {
   styleUrls: ['./vitamui-input.component.scss'],
   providers: [VITAMUI_INPUT_VALUE_ACCESSOR]
 })
-export class VitamUIInputComponent implements ControlValueAccessor {
+export class VitamUIInputComponent implements ControlValueAccessor, OnInit {
 
   @Input() type = 'text';
   @Input() maxlength: number;
   @Input() placeholder: string;
   @Input() autofocus: boolean;
+  @Input() value: string | number;
   @Input()
   get required(): boolean { return this._required; }
   set required(value: boolean) { this._required = coerceBooleanProperty(value); }
@@ -73,7 +74,6 @@ export class VitamUIInputComponent implements ControlValueAccessor {
   @HostBinding('class.vitamui-focused') focused = false;
   @HostBinding('class.vitamui-float') labelFloat = false;
 
-  value: string | number;
 
   onChange = (_: any) => { };
   onTouched = () => { };
@@ -83,6 +83,10 @@ export class VitamUIInputComponent implements ControlValueAccessor {
     this.input.nativeElement.focus();
   }
 
+  ngOnInit() {
+    this.labelFloat = !!this.value;
+  }
+
   writeValue(value: string | number) {
     this.value = value;
     this.labelFloat = !!this.value;
@@ -109,6 +113,7 @@ export class VitamUIInputComponent implements ControlValueAccessor {
 
   onFocus() {
     this.focused = true;
+    this.onTouched();
   }
 
   onBlur() {
diff --git a/ui/ui-frontend-common/src/app/modules/startup.service.ts b/ui/ui-frontend-common/src/app/modules/startup.service.ts
index 835e7a61..b0fd5bab 100644
--- a/ui/ui-frontend-common/src/app/modules/startup.service.ts
+++ b/ui/ui-frontend-common/src/app/modules/startup.service.ts
@@ -50,8 +50,6 @@ import { AppConfiguration, AuthUser } from './models';
 import {ThemeService} from './theme.service';
 
 const WARNING_DURATION = 2000;
-const DARK_SUFFIX = '-dark';
-const LIGHT_SUFFIX = '-light';
 
 @Injectable({
   providedIn: 'root'
@@ -95,16 +93,21 @@ export class StartupService {
       })
       .then(() => {
 
-        const applicationColorMap = this.configurationData.THEME_COLORS;
-        const customerColorMap = this.authService.user.basicCustomer.graphicIdentity.themeColors;
+        let customerColorMap = null;
 
-        this.themeService.init(applicationColorMap, customerColorMap);
+        if (this.authService.user.basicCustomer.graphicIdentity.hasCustomGraphicIdentity) {
+          customerColorMap = this.authService.user.basicCustomer.graphicIdentity.themeColors;
+        }
+
+        this.themeService.init(this.configurationData.THEME_COLORS);
 
-        for (const themeColorsKey in this.themeService.themeColors) {
-          if (this.themeService.themeColors.hasOwnProperty(themeColorsKey)) {
-            this.themeWrapper.style.setProperty('--' + themeColorsKey, this.themeService.themeColors[themeColorsKey]);
+        const themeColors = this.themeService.getThemeColors(customerColorMap);
+        for (const key in themeColors) {
+          if (themeColors.hasOwnProperty(key)) {
+            this.themeWrapper.style.setProperty('--' + key, themeColors[key]);
           }
         }
+
       })
       .then(() => this.applicationService.list().toPromise());
   }
diff --git a/ui/ui-frontend-common/src/app/modules/theme.service.ts b/ui/ui-frontend-common/src/app/modules/theme.service.ts
index 9f8819b8..c1cf784b 100644
--- a/ui/ui-frontend-common/src/app/modules/theme.service.ts
+++ b/ui/ui-frontend-common/src/app/modules/theme.service.ts
@@ -1,16 +1,13 @@
 import { Injectable } from '@angular/core';
 import {getColorFromMaps, ThemeColors} from './utils';
 
+
 @Injectable({
   providedIn: 'root'
 })
 export class ThemeService {
 
-  applicationColorMap;
-
-  customerColorMap;
-
-  themeColors: ThemeColors = {
+  defaultMap: ThemeColors = {
     'vitamui-primary': '#fe4f02',
     'vitamui-primary-light': '#ff8559',
     'vitamui-primary-light-20': '#ffa789',
@@ -21,43 +18,23 @@ export class ThemeService {
 
   };
 
+  applicationColorMap;
+
   constructor() {
   }
 
-  init(appMap, customerMap) {
+  init(appMap) {
     this.applicationColorMap = appMap;
-    this.customerColorMap = customerMap;
-    this.themeColors = this.getThemeColors(customerMap);
-  }
-
-  getThemeColors(customerColorMap: any) {
-
-    const applicationColorMap = this.applicationColorMap;
-    const defaultPrimary = this.themeColors['vitamui-primary'];
-    const defaultSecondary = this.themeColors['vitamui-secondary'];
-
-    return {
-      'vitamui-primary': getColorFromMaps('vitamui-primary', defaultPrimary, applicationColorMap, customerColorMap),
-      'vitamui-primary-light': getColorFromMaps('vitamui-primary-light', null, applicationColorMap, customerColorMap),
-      'vitamui-primary-light-20': getColorFromMaps('vitamui-primary-light-20', null, applicationColorMap, customerColorMap),
-      'vitamui-secondary': getColorFromMaps('vitamui-secondary', defaultSecondary, applicationColorMap, customerColorMap),
-      'vitamui-secondary-light': getColorFromMaps('vitamui-secondary-light', null, applicationColorMap, customerColorMap),
-      'vitamui-secondary-light-8': getColorFromMaps('vitamui-secondary-light-8', null, applicationColorMap, customerColorMap),
-      'vitamui-secondary-dark-5': getColorFromMaps('vitamui-secondary-dark-5', null, applicationColorMap, customerColorMap)
-    };
-
   }
 
-  refresh(primary: string, secondary: string): void {
-    this.themeColors = {
-      'vitamui-primary': getColorFromMaps('vitamui-primary', primary, null, null),
-      'vitamui-primary-light': getColorFromMaps('vitamui-primary-light', null, null, null),
-      'vitamui-primary-light-20': getColorFromMaps('vitamui-primary-light-20', null, null, null),
-      'vitamui-secondary': getColorFromMaps('vitamui-secondary', secondary, null, null),
-      'vitamui-secondary-light': getColorFromMaps('vitamui-secondary-light', null, null, null),
-      'vitamui-secondary-light-8': getColorFromMaps('vitamui-secondary-light-8', null, null, null),
-      'vitamui-secondary-dark-5': getColorFromMaps('vitamui-secondary-dark-5', null, null, null)
-    };
+  getThemeColors(customerColors = null): {[key: string]: string} {
+    const colors = {};
+    for (const key in this.defaultMap) {
+      if (this.defaultMap.hasOwnProperty(key)) {
+        colors[key] = getColorFromMaps(key, this.defaultMap, this.applicationColorMap, customerColors);
+      }
+    }
+    return colors;
   }
 
 }
diff --git a/ui/ui-frontend-common/src/app/modules/utils/colors.util.ts b/ui/ui-frontend-common/src/app/modules/utils/colors.util.ts
index 92222438..a97edc9e 100644
--- a/ui/ui-frontend-common/src/app/modules/utils/colors.util.ts
+++ b/ui/ui-frontend-common/src/app/modules/utils/colors.util.ts
@@ -14,24 +14,26 @@ class HSL {
 /**
  * Find, compute or return default value for the given name and maps of colors.
  * @param name the color name
- * @param defaultColor the color default value if no overriding in priority and fallback maps
+ * @param defaultMap the default color map if no overriding in priority and fallback maps
  * @param fallbackMap the fallback map. The function will search in it if no color is found in priority map. Should be application config
  * @param priorityMap the priority map. If the color is found in it, the fallbackMap is not used. Should be customer config
  * @return The hex RBG color find or computed from all sources
  */
-export function getColorFromMaps(name: string, defaultColor: string, fallbackMap: any, priorityMap: any): string {
+export function getColorFromMaps(name: string, defaultMap: any, fallbackMap: any, priorityMap: any): string {
 
   const customColor = getColorFromMap(name, priorityMap);
+
   if ( customColor ) {
     return customColor;
   }
 
+
   const applicationColor = getColorFromMap(name, fallbackMap);
   if ( applicationColor ) {
     return applicationColor;
   }
 
-  return defaultColor;
+  return getColorFromMap(name, defaultMap);
 }
 
 function getColorFromMap(colorName: string, colorMap: any) {
@@ -74,9 +76,7 @@ function convertToLightColor(color: string, lightModificator: number = 10) {
     return color;
   }
 
-  if (!lightModificator) {
-    lightModificator = 10;
-  }
+  lightModificator *= 2;
 
   const max = 255;
   const rgbValue: RGB = hexToRgb(color);
@@ -84,7 +84,7 @@ function convertToLightColor(color: string, lightModificator: number = 10) {
 
   // lighten
   hslValue.l = Math.min(hslValue.l + lightModificator, 100);
-  const lightRGBvalue: RGB = hslToRgb(hslValue);
+  const lightRGBvalue: RGB = hslToRgbExperimental(hslValue);
 
   return '#' + toHex(lightRGBvalue.r) + toHex(lightRGBvalue.g) + toHex(lightRGBvalue.b);
 }
@@ -99,12 +99,14 @@ function convertToDarkColor(color: string, lightModificator: number = 10) {
     return color;
   }
 
+  lightModificator *= 2;
+
   const rgbValue: RGB = hexToRgb(color);
   const hslValue: HSL = rgbToHsl(rgbValue);
 
   // darken
   hslValue.l = Math.max(hslValue.l - lightModificator, 0);
-  const darkRGBvalue: RGB = hslToRgb(hslValue);
+  const darkRGBvalue: RGB = hslToRgbExperimental(hslValue);
 
   return '#' + toHex(darkRGBvalue.r) + toHex(darkRGBvalue.g) + toHex(darkRGBvalue.b);
 }
@@ -124,30 +126,34 @@ function hexToRgb(hex): RGB {
     null;
 }
 
-function hslToRgb(inputHSL): RGB {
-  const hsl: HSL = new HSL( inputHSL.h, inputHSL.s / 100, inputHSL.l / 100);
-  const rgb: RGB = new RGB(hsl.l, hsl.l, hsl.l);
-
-  // if no saturation, all colors parts are the same and equls to lightness. Nothing to do
-  if (hsl.s !== 0) {
-    const q = hsl.l < 0.5 ? hsl.l * (1 + hsl.s) : hsl.l + hsl.s - hsl.l * hsl.s;
-    const p = 2 * hsl.l - q;
-
-    rgb.r = Math.round(hueToRGBComponent(p, q, hsl.h + 1 / 3) * 255);
-    rgb.g = Math.round(hueToRGBComponent(p, q, hsl.h) * 255);
-    rgb.b = Math.round(hueToRGBComponent(p, q, hsl.h - 1 / 3) * 255);
+function hslToRgbExperimental(inputHSL): RGB {
+  const hsl: HSL = new HSL( inputHSL.h * 360, inputHSL.s / 100, inputHSL.l / 100);
+  let rgb: RGB;
+
+  const c = (1 - Math.abs(2 * hsl.l - 1)) * hsl.s;
+  const x = c * (1 - Math.abs((hsl.h / 60) % 2 - 1));
+  const m = hsl.l - c / 2;
+
+  if (hsl.h < 60) {
+    rgb = new RGB(c, x, 0);
+  } else if (hsl.h < 120) {
+    rgb = new RGB(x, c, 0);
+  } else if (hsl.h < 180) {
+    rgb = new RGB(0, c, x);
+  } else if (hsl.h < 240) {
+    rgb = new RGB(0, x, c);
+  } else if (hsl.h < 300) {
+    rgb = new RGB(x, 0, c);
+  } else if (hsl.h < 360) {
+    rgb = new RGB(c, 0, x);
   }
 
+  rgb.r = Math.round((rgb.r + m) * 255);
+  rgb.g = Math.round((rgb.g + m) * 255);
+  rgb.b = Math.round((rgb.b + m) * 255);
+
   return rgb;
-}
 
-function hueToRGBComponent(p, q, t): number {
-  if (t < 0) { t += 1; }
-  if (t > 1) { t -= 1; }
-  if (t < 1 / 6) { return p + (q - p) * 6 * t; }
-  if (t < 1 / 2) { return q; }
-  if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; }
-  return p;
 }
 
 function rgbToHsl(inputRGB: RGB): HSL {
diff --git a/ui/ui-frontend-common/src/sass/material/_preview-tab.scss b/ui/ui-frontend-common/src/sass/material/_preview-tab.scss
index 8d1a5ef2..00cffc75 100644
--- a/ui/ui-frontend-common/src/sass/material/_preview-tab.scss
+++ b/ui/ui-frontend-common/src/sass/material/_preview-tab.scss
@@ -50,7 +50,7 @@
     }
 
     .mat-tab-body-content {
-        padding: 30px 105px 30px 40px;
+        padding: 30px 85px 30px 40px;
         overflow: visible;
     }
 }
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.html b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.html
index c7235b49..55a1365c 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.html
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.html
@@ -2,19 +2,22 @@
 <div [formGroup]="colorForm">
   <p><vitamui-common-input class="field-primary-color" formControlName="primary" maxlength="7"
                             placeholder="Couleur principale"
-                            i18n-placeholder="Customer primary theme color input placeholder@@customerUpdatePrimaryColorThemeInputPlaceholder"
-                            (change)="handleValueChange()" required >
+                            i18n-placeholder="Customer primary theme color input placeholder@@customerUpdatePrimaryColorThemeInputPlaceholder">
   </vitamui-common-input>
     <span class="field-color-preview primary"></span>
+    <span class="field-color-preview primary-light"></span>
+    <span class="field-color-preview primary-light-20"></span>
   </p>
   <p>
     <vitamui-common-input class="field-secondary-color" formControlName="secondary" maxlength="7"
                            placeholder="Couleur secondaire"
-                           i18n-placeholder="Customer secondary theme color input placeholder@@customerUpdateSecondaryColorThemeInputPlaceholder"
-                           (change)="handleValueChange()" required >
+                           i18n-placeholder="Customer secondary theme color input placeholder@@customerUpdateSecondaryColorThemeInputPlaceholder">
 
     </vitamui-common-input>
     <span class="field-color-preview secondary"></span>
+    <span class="field-color-preview secondary-light"></span>
+    <span class="field-color-preview secondary-light-8"></span>
+    <span class="field-color-preview secondary-dark-5"></span>
   </p>
 </div>
 <div class="dlab-input-errors">
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.scss b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.scss
index f2204506..825c6908 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.scss
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.scss
@@ -1,32 +1,43 @@
 @import '~ui-frontend-common/sass/variables/colors';
-$field-spacing: 15px;
 
-.field-color-name {
-  width: 300px + $field-spacing / 2;
-  margin-right: $field-spacing;
+p {
+  height: 80px;
 }
 
-.field-color-value {
-  width: 150px + $field-spacing / 2;
-  margin-right: $field-spacing;
+div {
+  margin-bottom: 30px;
 }
 
 .field-color-preview {
   padding: 0;
-  margin: 0;
+  margin: 0 5px 0 0;
   display: inline-block;
-  width: 49px;
-  height: 49px;
+  width: 50px;
+  height: 50px;
   border-radius: 100%;
   position: relative;
-  top: 34px;
+  top: 35px;
   left: -50px;
 
   &.primary {
     background-color: var(--vitamui-primary);
   }
-
+  &.primary-light {
+    background-color: var(--vitamui-primary-light);
+  }
+  &.primary-light-20 {
+    background-color: var(--vitamui-primary-light-20);
+  }
   &.secondary {
     background-color: var(--vitamui-secondary);
   }
+  &.secondary-light {
+    background-color: var(--vitamui-secondary-light);
+  }
+  &.secondary-light-8 {
+    background-color: var(--vitamui-secondary-light-8);
+  }
+  &.secondary-dark-5 {
+    background-color: var(--vitamui-secondary-dark-5);
+  }
 }
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.ts b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.ts
index ab1b1c8a..d5049740 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.ts
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-colors-input/customer-colors-input.component.ts
@@ -1,4 +1,4 @@
-import {Component, forwardRef, Input} from '@angular/core';
+import {Component, forwardRef, Input, OnInit} from '@angular/core';
 import {
   ControlValueAccessor,
   FormBuilder,
@@ -21,17 +21,22 @@ export const COLORS_INPUT_ACCESSOR: any = {
   styleUrls: ['./customer-colors-input.component.scss'],
   providers: [COLORS_INPUT_ACCESSOR]
 })
-export class CustomerColorsInputComponent implements ControlValueAccessor {
+export class CustomerColorsInputComponent implements ControlValueAccessor, OnInit {
 
 
   @Input() placeholder: string;
   @Input() spinnerDiameter = 25;
 
+  // css selector to overload color theme for preview (default = only color circles around inputs)
+  @Input() overloadSelector = '.field-color-preview';
+
   colorForm: FormGroup;
 
-  colors: {[key: string]: string};
+  colors: {[colorId: string]: string} = {
+    'vitamui-primary': '',
+    'vitamui-secondary': ''
+  };
 
-  onChange: (colors: {[key: string]: string}) => void;
   onTouched: () => void;
 
   validator: ValidatorFn = Validators.pattern(/#([0-9A-Fa-f]{6})/);
@@ -41,18 +46,18 @@ export class CustomerColorsInputComponent implements ControlValueAccessor {
       primary: ['', this.validator],
       secondary: ['', this.validator]
     });
+  }
 
+  get value(): {[key: string]: string} {
+    return this.colors;
   }
 
   writeValue(colors: {primary: string, secondary: string}) {
-    this.colorForm.get('primary').setValue(colors.primary);
-    this.colorForm.get('secondary').setValue(colors.secondary);
-
-    this.overloadLocalTheme();
+    this.colorForm.setValue(colors);
   }
 
   registerOnChange(fn: (colors: {[key: string]: string}) => void) {
-    this.onChange = fn;
+    this.colorForm.valueChanges.subscribe(fn);
   }
 
   registerOnTouched(fn: () => void) {
@@ -60,46 +65,48 @@ export class CustomerColorsInputComponent implements ControlValueAccessor {
   }
 
 
-  handleValueChange() {
-    // Force color hex to start with '#'
-    if ( ! this.colorForm.value.primary.startsWith('#') ) {
-      const newPrimary: string = '#' + this.colorForm.value.primary;
-      const oldSecondary: string = this.colorForm.value.secondary;
-      this.colorForm.setValue({primary: newPrimary, secondary: oldSecondary});
-    }
+  handleValueChanges() {
+    this.colorForm.valueChanges.subscribe((colors) => {
 
-    if ( ! this.colorForm.value.secondary.startsWith('#') ) {
-      const newSecondary: string = '#' + this.colorForm.value.secondary;
-      const oldPrimary: string = this.colorForm.value.primary;
-      this.colorForm.setValue({primary: oldPrimary, secondary: newSecondary});
-    }
+      // Force color hex to start with '#'
+      if (!colors.primary.startsWith('#')) {
+        const newPrimary: string = '#' + colors.primary;
+        const oldSecondary: string = colors.secondary;
+        this.colorForm.setValue({primary: newPrimary, secondary: oldSecondary});
+      }
 
-    if (this.colorForm.invalid || this.colorForm.pending) {
-      return;
-    }
+      if (!this.colorForm.value.secondary.startsWith('#')) {
+        const newSecondary: string = '#' + colors.secondary;
+        const oldPrimary: string = colors.primary;
+        this.colorForm.setValue({primary: oldPrimary, secondary: newSecondary});
+      }
 
-    this.colors = {
-      'vitamui-primary': this.colorForm.value.primary,
-      'vitamui-secondary': this.colorForm.value.secondary
-    };
+      if (this.colorForm.invalid || this.colorForm.pending) {
+        return;
+      }
 
-    // propagate changes
-    this.onChange(this.colors);
+      this.colors = {
+        'vitamui-primary': this.colorForm.value.primary,
+        'vitamui-secondary': this.colorForm.value.secondary
+      };
 
-    // If form is valid, overload local theme for preview
-    this.overloadLocalTheme();
+      // If form is valid, overload local theme for preview
+      this.overloadLocalTheme();
+    });
 
   }
 
   overloadLocalTheme() {
-
-    this.themeService.refresh(this.colorForm.value.primary, this.colorForm.value.secondary);
-
-    const selector: HTMLElement = document.querySelector('div.customer-colors-input');
-    for (const key in this.themeService.themeColors) {
-      if (this.themeService.themeColors.hasOwnProperty(key)) {
-        selector.style.setProperty('--' + key, this.themeService.themeColors[key]);
+    const newTheme = this.themeService.getThemeColors(this.colors);
+    const selector: HTMLElement = document.querySelector(this.overloadSelector);
+    for (const key in newTheme) {
+      if (newTheme.hasOwnProperty(key)) {
+        selector.style.setProperty('--' + key, newTheme[key]);
       }
     }
   }
+
+  ngOnInit(): void {
+    this.handleValueChanges();
+  }
 }
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-create.component.html b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-create.component.html
index 4dee390a..d1370dd7 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-create.component.html
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-create.component.html
@@ -2,7 +2,7 @@
   <mat-progress-bar mode="determinate" [value]="stepProgress" class="stepper-progress-bar"></mat-progress-bar>
 </div>
 
-<form [formGroup]="form" (ngSubmit)="onSubmit()">
+<form [formGroup]="form" (ngSubmit)="onSubmit()" id="formCreateCustomer">
   <vitamui-common-stepper (selectionChange)="stepIndex=$event.selectedIndex">
 
     <cdk-step>
@@ -182,10 +182,10 @@
               </p>
             </div>
           </div>
+        </div>
 
-          <div class="theme-colors">
-            <app-customer-colors-input formControlName="themeColors"></app-customer-colors-input>
-          </div>
+        <div class="customer-colors-input">
+          <app-customer-colors-input formControlName="themeColors" overloadSelector="form#formCreateCustomer"></app-customer-colors-input>
         </div>
         <div class="error-message" *ngIf="hasError">
           {{ message }}
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-create.component.ts b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-create.component.ts
index 90876d25..c7e0a5dc 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-create.component.ts
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-create/customer-create.component.ts
@@ -35,7 +35,7 @@
  * knowledge of the CeCILL-C license and that you accept its terms.
  */
 import { merge, Subscription } from 'rxjs';
-import { ConfirmDialogService, Customer, OtpState } from 'ui-frontend-common';
+import {ConfirmDialogService, Customer, OtpState, ThemeService} from 'ui-frontend-common';
 
 import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@@ -82,7 +82,8 @@ export class CustomerCreateComponent implements OnInit, OnDestroy {
     private formBuilder: FormBuilder,
     private customerService: CustomerService,
     private customerCreateValidators: CustomerCreateValidators,
-    private confirmDialogService: ConfirmDialogService
+    private confirmDialogService: ConfirmDialogService,
+    private themeService: ThemeService
   ) {
   }
 
@@ -108,7 +109,7 @@ export class CustomerCreateComponent implements OnInit, OnDestroy {
       emailDomains: [null, Validators.required],
       defaultEmailDomain: [null, Validators.required],
       hasCustomGraphicIdentity: false,
-      themeColors: [null],
+      themeColors: null,
       owners: this.formBuilder.array([
         this.formBuilder.control(null, Validators.required),
       ])
@@ -129,6 +130,12 @@ export class CustomerCreateComponent implements OnInit, OnDestroy {
       }
     });
 
+    const colors = this.themeService.getThemeColors();
+    this.form.get('themeColors').setValue({
+      primary: colors['vitamui-primary'],
+      secondary: colors['vitamui-secondary']
+    });
+
     this.keyPressSubscription = this.confirmDialogService.listenToEscapeKeyPress(this.dialogRef).subscribe(() => this.onCancel());
   }
 
@@ -177,14 +184,15 @@ export class CustomerCreateComponent implements OnInit, OnDestroy {
   }
 
   updateForCustomerModel(formValue: any): Customer {
+    console.log(formValue);
     const { themeColors, ...customer } = formValue;
+    const customerTheme =  {
+      'vitamui-primary': themeColors.primary,
+      'vitamui-secondary': themeColors.secondary
+    };
+
+    customer.themeColors = this.themeService.getThemeColors(customerTheme);
 
-    customer.themeColors = {};
-    for (const key in themeColors) {
-      if (themeColors.hasOwnProperty(key)) {
-        customer.themeColors[key] = themeColors[key];
-      }
-    }
     return customer;
   }
 
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.html b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.html
index 111e542a..afb718fe 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.html
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.html
@@ -17,14 +17,20 @@
   </div>
 </div>
 <div class="row">
-  <span>Couleurs de l'organisation</span>
-
-  <div class="vitamui-chip-list">
-    <div *ngFor="let color of customer.themeColors | keyvalue" class="vitamui-chip color-chip">
-      <div class="vitamui-chip-content">
-        <i class="openvitamui-icon openvitamui-icon-triangle" [style.color]="color.value"></i>
-        &nbsp;{{color.key}}</div>
-    </div>
-  </div>
+  <span i18n="Organisation color theme@@customerGraphicIdentityTabColorTheme">Couleurs de l'organisation</span>
+  <p><vitamui-common-input
+    disabled placeholder="Couleur principale"
+    [value]="themeColors['vitamui-primary']" class="vitamui-float"></vitamui-common-input>
+    <span class="field-color-preview primary"></span>
+    <span class="field-color-preview primary-light"></span>
+    <span class="field-color-preview primary-light-20"></span>
+  </p>
+  <p><vitamui-common-input
+    disabled placeholder="Couleur secondaire"
+    [value]="themeColors['vitamui-secondary']" class="vitamui-float"></vitamui-common-input>
+    <span class="field-color-preview secondary"></span>
+    <span class="field-color-preview secondary-light"></span>
+    <span class="field-color-preview secondary-light-8"></span>
+    <span class="field-color-preview secondary-dark-5"></span></p>
 </div>
 
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.scss b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.scss
index 4a3ee422..6db72c70 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.scss
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.scss
@@ -5,6 +5,40 @@
    margin-left: 5%
 }
 
-.color-chip {
-  border: 2px solid;
+p {
+  height: 80px;
+}
+
+.field-color-preview {
+  padding: 0;
+  margin: 0 5px 0 0;
+  display: inline-block;
+  width: 50px;
+  height: 50px;
+  border-radius: 100%;
+  position: relative;
+  top: 35px;
+  left: -50px;
+
+  &.primary {
+    background-color: var(--vitamui-primary);
+  }
+  &.primary-light {
+    background-color: var(--vitamui-primary-light);
+  }
+  &.primary-light-20 {
+    background-color: var(--vitamui-primary-light-20);
+  }
+  &.secondary {
+    background-color: var(--vitamui-secondary);
+  }
+  &.secondary-light {
+    background-color: var(--vitamui-secondary-light);
+  }
+  &.secondary-light-8 {
+    background-color: var(--vitamui-secondary-light-8);
+  }
+  &.secondary-dark-5 {
+    background-color: var(--vitamui-secondary-dark-5);
+  }
 }
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.ts b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.ts
index 053a674f..21fa1949 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.ts
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-tab.component.ts
@@ -34,11 +34,11 @@
  * The fact that you are presently reading this means that you have had
  * knowledge of the CeCILL-C license and that you accept its terms.
  */
-import { Component, Input } from '@angular/core';
+import {Component, Input, OnInit} from '@angular/core';
 import { MatDialog } from '@angular/material/dialog';
 import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
-import { Customer } from 'ui-frontend-common';
-import { CustomerService } from './../../../core/customer.service';
+import {Customer, ThemeService} from 'ui-frontend-common';
+import { CustomerService } from '../../../core/customer.service';
 import { GraphicIdentityUpdateComponent } from './graphic-identity-update/graphic-identity-update.component';
 
 @Component({
@@ -46,7 +46,7 @@ import { GraphicIdentityUpdateComponent } from './graphic-identity-update/graphi
   templateUrl: './graphic-identity-tab.component.html',
   styleUrls: ['./graphic-identity-tab.component.scss']
 })
-export class GraphicIdentityTabComponent {
+export class GraphicIdentityTabComponent implements OnInit {
 
   @Input()
   set customer(customer: Customer) {
@@ -68,15 +68,35 @@ export class GraphicIdentityTabComponent {
   logo: any;
   trustedUrlInline: SafeUrl;
 
-  constructor(private customerService: CustomerService, private sanitizer: DomSanitizer, private dialog: MatDialog) { }
+  themeColors: {[key: string]: string};
+
+  constructor(private customerService: CustomerService, private sanitizer: DomSanitizer, private dialog: MatDialog,
+              private themeService: ThemeService) {
+  }
+
+  ngOnInit() {
+    this.resetTab(this._customer);
+  }
+
 
   resetTab(customer: Customer) {
     if (customer.hasCustomGraphicIdentity) {
-      this.customerService.getCustomerLogo(customer.id).subscribe((data: any) => {
-      const logo = data;
-      this.trustedUrlInline = this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(logo.body));
+        this.customerService.getCustomerLogo(customer.id).subscribe((data: any) => {
+        const logo = data;
+        this.trustedUrlInline = this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(logo.body));
       });
     }
+
+    this.themeColors = this.themeService.getThemeColors(customer.themeColors);
+    this.overloadLocalTheme();
+  }
+
+  overloadLocalTheme() {
+    const selector: HTMLElement = document.querySelector('div.vitamui-sidepanel');
+    // tslint:disable-next-line:forin
+    for (const key in this.themeColors) {
+      selector.style.setProperty('--' + key, this.themeColors[key]);
+    }
   }
 
   openUpdateCustomerLogo() {
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-update/graphic-identity-update.component.html b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-update/graphic-identity-update.component.html
index 6a075ee0..cfd88da8 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-update/graphic-identity-update.component.html
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-update/graphic-identity-update.component.html
@@ -1,4 +1,4 @@
-<form [formGroup]="graphicIdentityForm">
+<form [formGroup]="graphicIdentityForm" id="graphicIdentityForm">
   <div class="content">
     <h2 i18n="Customer graphical identity title@@customerGraphicalIdentityTitle">Identité graphique du client</h2>
 
@@ -35,7 +35,7 @@
     </div>
 
     <div class="customer-colors-input">
-      <app-customer-colors-input formControlName="themeColors"></app-customer-colors-input>
+      <app-customer-colors-input formControlName="themeColors" overloadSelector="form#graphicIdentityForm" ></app-customer-colors-input>
     </div>
 
     <div class="actions">
diff --git a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-update/graphic-identity-update.component.ts b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-update/graphic-identity-update.component.ts
index 45608e2d..969320ba 100644
--- a/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-update/graphic-identity-update.component.ts
+++ b/ui/ui-frontend/projects/identity/src/app/customer/customer-preview/graphic-identity-tab/graphic-identity-update/graphic-identity-update.component.ts
@@ -83,13 +83,6 @@ export class GraphicIdentityUpdateComponent implements OnInit {
     this.graphicIdentityForm.get('hasCustomGraphicIdentity').setValue(this.customer.hasCustomGraphicIdentity);
     this.hasCustomGraphicIdentity = this.graphicIdentityForm.get('hasCustomGraphicIdentity').value;
 
-    const customerTheme = this.themeService.getThemeColors(this.customer.themeColors);
-    this.graphicIdentityForm.get('themeColors').setValue({
-      primary: customerTheme['vitamui-primary'],
-      secondary: customerTheme['vitamui-secondary']
-    });
-
-
     this.graphicIdentityForm.get('hasCustomGraphicIdentity').valueChanges.subscribe(() => {
       this.hasCustomGraphicIdentity = this.graphicIdentityForm.get('hasCustomGraphicIdentity').value;
       this.message = null;
@@ -103,6 +96,13 @@ export class GraphicIdentityUpdateComponent implements OnInit {
         this.imageUrl = null;
       }
     });
+
+    const customerTheme = this.themeService.getThemeColors(this.customer.themeColors);
+    this.graphicIdentityForm.get('themeColors').setValue({
+      primary: customerTheme['vitamui-primary'],
+      secondary: customerTheme['vitamui-secondary']
+    });
+
   }
 
   onCancel() {
@@ -162,15 +162,15 @@ export class GraphicIdentityUpdateComponent implements OnInit {
 
   updateGraphicIdentity() {
 
-    const themeFormValues = this.graphicIdentityForm.get('themeColors').value;
+    const colorValues = this.graphicIdentityForm.get('themeColors').value;
 
     const formData = {
       id : this.customer.id,
       hasCustomGraphicIdentity: this.graphicIdentityForm.get('hasCustomGraphicIdentity').value,
-      themeColors: {
-        'vitamui-primary': themeFormValues.primary,
-        'vitamui-secondary': themeFormValues.secondary
-      }
+      themeColors: this.themeService.getThemeColors({
+        'vitamui-primary': colorValues.primary,
+        'vitamui-secondary': colorValues.secondary
+      })
     };
     this.customerService.patch(formData, this.imageToUpload)
       .subscribe(
-- 
GitLab