Merge branch 'dev' into staging

This commit is contained in:
pida123 2023-08-03 17:48:07 +02:00
commit 2f731faece
25 changed files with 319 additions and 97 deletions

View File

@ -15,14 +15,14 @@ jobs:
- checkout - checkout
- add_ssh_keys: - add_ssh_keys:
fingerprints: fingerprints:
- "39:25:57:64:62:43:1f:98:b1:5e:75:53:87:d8:e7:71" - "4c:8e:00:16:94:44:d9:ad:e9:e9:2c:8b:02:d4:8d:7a"
- run: cp $HOME/.ssh/id_rsa_3925576462431f98b15e755387d8e771 id_rsa - run: cp $HOME/.ssh/id_rsa_4c8e00169444d9ade9e92c8b02d48d7a id_rsa
- setup_remote_docker: - setup_remote_docker:
version: 20.10.12 version: 20.10.12
docker_layer_caching: true docker_layer_caching: true
- run: docker login rg.fr-par.scw.cloud/lecoffre -u nologin -p $SCW_SECRET_KEY - run: docker login rg.fr-par.scw.cloud/lecoffre -u nologin -p $SCW_SECRET_KEY
- run: docker build --tag rg.fr-par.scw.cloud/lecoffre/front:$TAG . - run: docker build --tag rg.fr-par.scw.cloud/lecoffre/front:${CIRCLE_SHA1:0:7} .
- run: docker push rg.fr-par.scw.cloud/lecoffre/front:$TAG - run: docker push rg.fr-par.scw.cloud/lecoffre/front:${CIRCLE_SHA1:0:7}
deploy-docker-image: deploy-docker-image:
@ -30,6 +30,10 @@ jobs:
- image: cimg/base:stable - image: cimg/base:stable
environment: environment:
TAG: << pipeline.git.tag >> TAG: << pipeline.git.tag >>
parameters:
env:
type: string
default: stg
steps: steps:
- checkout - checkout
- kubernetes/install-kubeconfig: - kubernetes/install-kubeconfig:
@ -39,31 +43,61 @@ jobs:
name: Deploy name: Deploy
command: > command: >
helm upgrade helm upgrade
lecoffre-front devops/ -i -f devops/values.yaml lecoffre-front devops/ -i -f devops/<<parameters.env>>.values.yaml
-n lecoffre -n lecoffre-<<parameters.env>>
--create-namespace --create-namespace
--set lecoffreFront.image.repository='rg.fr-par.scw.cloud/lecoffre/front' --set lecoffreFront.image.repository='rg.fr-par.scw.cloud/lecoffre/front'
--set lecoffreFront.image.tag=$TAG --set lecoffreFront.image.tag=${CIRCLE_SHA1:0:7}
workflows: workflows:
version: 2 version: 2
build-and-register: build-and-register-stg:
jobs: jobs:
- build-push-docker-image: - build-push-docker-image:
filters: filters:
tags:
only: /^v.*/
branches: branches:
ignore: /.*/ only: staging
- deploy-docker-image: - deploy-docker-image:
env: stg
requires: requires:
- build-push-docker-image - build-push-docker-image
context: context:
- staging - sc-shared-prd
filters: filters:
tags:
only: /^v.*/
branches: branches:
ignore: /.*/ only: staging
build-and-register-ppd:
jobs:
- build-push-docker-image:
filters:
branches:
only: preprod
- deploy-docker-image:
env: ppd
requires:
- build-push-docker-image
context:
- sc-shared-prd
filters:
branches:
only: preprod
build-and-register-prd:
jobs:
- build-push-docker-image:
filters:
branches:
only: main
- deploy-docker-image:
env: prd
requires:
- build-push-docker-image
context:
- sc-shared-prd
filters:
branches:
only: main

View File

@ -1,5 +1,5 @@
apiVersion: v2 apiVersion: v2
name: leCoffre-front name: leCoffre-back
description: A Helm chart for Kubernetes description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart. # A chart can be either an 'application' or a 'library' chart.
@ -21,4 +21,5 @@ version: 0.0.1
# incremented each time you make changes to the application. Versions are not expected to # incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using. # follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes. # It is recommended to use it with quotes.
appVersion: 0.5.0 appVersion: 0.5.6

34
devops/ppd.values.yaml Normal file
View File

@ -0,0 +1,34 @@
dockerPullSecret: docker-pull-secret
scwSecretKey: AgCgjF5QEzxT3GYTS5B6cmQ0e+0/qFWzKaUDSi+Vjc7RoameuvaIJvTXMBkS3he1oy1ulbB34v6vpZI2kxnGNqERA/U5BaYDAyfKSBwMAy4br7HVKhhuwkoF5qoG5JzJXseSmqB1U9vncVIGOZWzJc1Y4/eGlWcvLcLyfw2z/WEpyeNiWJfEhTYpJOB7gv0XnRb2U/JM3jRy1QgEUIk1WR6kgBalF+xaczPQ6uKh+PR2pqkbZa3WaKUrddmzNsgEz4d8PZMWt8IBwR2JOQEHUqCd34p/pJNyLdUgcdDhg02DKwn1oRoAxKTbAio/a7WrMbodjCb3TNWIYGal5mFmItZ7Ok/EBmUf4E85eOkTR+j8ynuuiexld3Q5Kw3o8LsHjgzVL9uP+T2rYaKkjtVt+YQRX1U8l9CrsdUEz0/wEBA0jwCWMfnh1qhD5pM/xwwjsEEAcK4rYV+Q7iAgGZZvZBCQ5aEHzrtn5D95tr1GZCV2hmrW6Seu+LKKLVBS1JmsuEsOuhudYsEK9m2RYVcxbjuS5eokKEjNrGobf2oB8rhBByavfw1JTBixR5JrI8lcYlnCa+oEhxXKJY+4Fx5SAB4YaLCMSo5vw6zsFQ3WKQzlEmCFt+EnapS+a+MGrdlwq07OHTDpvgk/1z39hopoCuhhKckGGfErLXsTYQvDOkFu+EPzgY7m7qDw/d9pSiht5tuSOkAqeOgm7tpNkUufZhaXmP+1aT7i+H5gq1JILGAmXzTI5Wc=
lecoffreFront:
serviceAccountName: lecoffre-front-sa
command: "'sh', '-c', 'export $(xargs </etc/env/.env) && npm run cron'"
envSecrets: front-env-ppd
imagePullSecrets:
- name: docker-pull-secret
image:
pullPolicy: Always
repository: "rg.fr-par.scw.cloud/lecoffre/front"
resources:
requests:
cpu: 200m
memory: 1Gi
limits:
memory: 2Gi
ingress:
host: app.ppd.lecoffre.smart-chain.fr
tls:
hosts:
- app.ppd.lecoffre.smart-chain.fr
secretName: api-tls
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
env:
- key: .env
scwID: "id:a131edea-84e0-49d6-b4a8-20ab417220c9"

34
devops/prd.values.yaml Normal file
View File

@ -0,0 +1,34 @@
dockerPullSecret: docker-pull-secret
scwSecretKey: AgBG2y7uQuap+2akNPGFxpCR+l0INO6Wxez5qljtY6t71GFGhJLYN9ZfefflKcFzD2Nv7DQMXXhpnCCaFti+9JMCMDuN324dDgtMMLTot+Pkxk/bAm+L8t3HfRharFdLz/vvzg77bvypi28TEoNYR/AM0e8VMYxBEgEp2TmP5uXcxZOgPzXMrfQoSdNRyzGTJ5tXZwe3PP7XvXyTNsZzHBtoQQM+nul9nL+VFA7CBRaaOpCmKOXjAlt7TyNXo4X5eYBNlxr+NuQw4dh4E/1zqdU/dDCE1+vx88BDbdydBA1qJaTOUSGTFquSK4kb9qAVAexBAIUqRwpfEW6Li945AXtnxLN42gEGPRsA9tSXL2c20k6thuRCqxwEOZljq2E03qtLAkxdP6WFBcb77o4PIEMZ8AmzPASnI+eW5z2mCoP3L+HZQrTLliDjmF4AMtOfZxRi0CCTrsSabOrimJC6v3y3ve0VcSsjA3rd5vvJ3Va4mZK4JAtYwEUx4PCHCGkUxc0w6jRwKB5tL/auZVT4SV/0z/WgW4Kq4AdvxsU6yGOqflt6e3ePIIuvCgjw+1yOYRpUiSGj36oOqNPMA4smxIB7p7Gi3csqt2TrQoW3TaLv/s7gbCcxHWSor+WT71WGg8AVmLm+FzUINmNop+c2RNo3O/Gj7h1uybX/pj+tRLNOuBQCqa+GQkY2bT2NcT9ifnAZB6K+2zAWXl+tdbMlDGV89P2yMYuRMdHGhuOoyuIUPWeA5i0=
lecoffreFront:
serviceAccountName: lecoffre-front-sa
command: "'sh', '-c', 'export $(xargs </etc/env/.env) && npm run cron'"
envSecrets: front-env-prd
imagePullSecrets:
- name: docker-pull-secret
image:
pullPolicy: Always
repository: "rg.fr-par.scw.cloud/lecoffre/front"
resources:
requests:
cpu: 200m
memory: 1Gi
limits:
memory: 2Gi
ingress:
host: app.lecoffre.smart-chain.fr
tls:
hosts:
- app.lecoffre.smart-chain.fr
secretName: api-tls
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
env:
- key: .env
scwID: "id:a131edea-84e0-49d6-b4a8-20ab417220c9"

36
devops/stg.values.yaml Normal file
View File

@ -0,0 +1,36 @@
dockerPullSecret: docker-pull-secret
scwSecretKey: AgChoEnPitXp4Ny/rVMEcevaWKNVpyj2cJYAcq+yFqKwVwnLB+ffDvwqz9XBHu+6d4Nyyjkf37zUAMoaM21lEDWA7x3zfG2/D/j+rvX1qxzZgLD0mjBk7fGElVm332I6JA83oInes8AMMYEDPLElzHnpKRb9KtkIP4NzgOcCeW0ijft3N7Vroez6LEHsBPCA1I9XjKSkGEDvrO0MhWX3iJOlfz+SPMfJAV7rPawOs0ZmohTHrPW8qIvGDn8HCzKyU8zRBoMt+Ogpf5pH4U3JryEFuqD61KAQgablAM8edPIvsgNno9HAEuC2QtRLYA9aUhuKdaKuS58c9P2E80PHWXIlbpFCg6EugQTgNfnYp+3qDUNz8edeCfapYLvF4s9eCMGyMsGnpDR8EDNOyuGy7Y3l7okX8Xqu464gMp9E+hX7bHkcD6a4xfyIgJcWxsku0tm1TH1dpn4M1UXRuyZZif8P08nuE6MTUL67sAR9J1lpn4lVEL4kflk0pP2tZ5ncgPQFafJrRz05krMb0eU5tb2H4gs7ao/LL6idWo8MM9K1yr8lIuT5x2WW5CX+RjA+i50ex114V6vX3PNP5oVyt+DynTUB9QmXzVm3oLfDc3Cae1uqh7X0CFd+xiztJBtg0VtJaD/xUJcuWfY4cV2lERo9fRrykltzlJqiXHO4nowt8OtN0BcViVV8NJhPhYFzyb4ympxpOlTjm3GETuT2TYhUqdgS9nzleEAbOmOHZdIO2COunPE=
lecoffreFront:
serviceAccountName: lecoffre-front-sa
envSecrets: front-env-stg
command: "'sh', '-c', 'export $(xargs </etc/env/.env) && npm run start'"
imagePullSecrets:
- name: docker-pull-secret
image:
pullPolicy: Always
repository: "rg.fr-par.scw.cloud/lecoffre/front"
resources:
requests:
cpu: 200m
memory: 1Gi
limits:
memory: 2Gi
ingress:
host: app.stg.lecoffre.smart-chain.fr
tls:
hosts:
- app.stg.lecoffre.smart-chain.fr
secretName: api-tls
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
# key is name of the environment variable, scwID is the secret ID in SCW with "id:" in front
env:
- key: .env
scwID: "id:a131edea-84e0-49d6-b4a8-20ab417220c9"

View File

@ -0,0 +1 @@
### USE SECRET FROM BACK

View File

@ -3,7 +3,6 @@ apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
metadata: metadata:
name: lecoffre-front name: lecoffre-front
namespace: {{ .Values.namespace }}
{{if .Values.lecoffreFront.ingress.annotations}} {{if .Values.lecoffreFront.ingress.annotations}}
annotations: annotations:
{{toYaml .Values.lecoffreFront.ingress.annotations | indent 4 }} {{toYaml .Values.lecoffreFront.ingress.annotations | indent 4 }}
@ -28,13 +27,12 @@ apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: lecoffre-front-svc name: lecoffre-front-svc
namespace: {{ .Values.namespace }}
labels: labels:
spec: spec:
ports: ports:
- port: 80 - port: 80
name: http name: http
targetPort: 3000 targetPort: 3001
selector: selector:
app: lecoffre-front app: lecoffre-front
--- ---
@ -42,7 +40,6 @@ apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: lecoffre-front name: lecoffre-front
namespace: {{ .Values.namespace }}
labels: labels:
app: lecoffre-front app: lecoffre-front
spec: spec:
@ -53,7 +50,6 @@ spec:
template: template:
metadata: metadata:
annotations: annotations:
{{toYaml .Values.lecoffreFront.vault.annotations | indent 8 }}
labels: labels:
app: lecoffre-front app: lecoffre-front
spec: spec:
@ -62,10 +58,17 @@ spec:
- name: docker-pull-secret - name: docker-pull-secret
containers: containers:
- name: lecoffre-front - name: lecoffre-front
image: "{{ .Values.lecoffreFront.image.repository }}:v{{ .Chart.AppVersion }}" image: "{{ .Values.lecoffreFront.image.repository }}:{{ .Values.lecoffreFront.image.tag }}"
{{if .Values.lecoffreFront.resources}} {{if .Values.lecoffreFront.resources}}
resources: resources:
{{toYaml .Values.lecoffreFront.resources | indent 10}} {{toYaml .Values.lecoffreFront.resources | indent 10}}
{{end}} {{end}}
imagePullPolicy: {{ .Values.lecoffreFront.image.pullPolicy }} imagePullPolicy: {{ .Values.lecoffreFront.image.pullPolicy }}
command: [{{ .Values.lecoffreFront.command }}] command: [{{ .Values.lecoffreFront.command }}]
volumeMounts:
- name: secret-volume
mountPath: /etc/env
volumes:
- name: secret-volume
secret:
secretName: {{ .Values.lecoffreFront.envSecrets }}

View File

@ -0,0 +1 @@
## USE SEALED SECRET FROM BACK

View File

@ -0,0 +1 @@
## USE SECRET STORE FROM BACK

View File

@ -0,0 +1,16 @@
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: {{ .Values.lecoffreFront.envSecrets }}
spec:
refreshInterval: 20s
secretStoreRef:
kind: SecretStore
name: secret-store
data:
{{ range $v := .Values.lecoffreFront.env }}
- secretKey: {{ $v.key }}
remoteRef:
key: {{ $v.scwID}}
version: latest_enabled
{{ end }}

View File

@ -1,29 +1,17 @@
dockerPullSecret: secret/data/lecoffre-front-stg/config/dockerpullsecret dockerPullSecret: docker-pull-secret
namespace: lecoffre scwSecretKey: ss
lecoffreFront: lecoffreFront:
serviceAccountName: lecoffre-front-sa serviceAccountName: lecoffre-front-sa
command: "'sh', '-c', '. /vault/secrets/envs && npm run start'" command: "npm run api:start"
vault: envSecrets: env-env
role : custom_lecoffre-front_injector_rol
server: https://vault-stg.smart-chain.fr
annotations:
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-secret-envs: secret/data/lecoffre-front-stg/config/envs
vault.hashicorp.com/role: custom_lecoffre-front_injector_rol
vault.hashicorp.com/agent-inject-template-envs: |
{{ with secret "secret/data/lecoffre-front-stg/config/envs" }}
{{ range $k, $v := .Data.data }}
export {{ $k }}="{{ $v }}"
{{ end }}
{{ end }}
imagePullSecrets: imagePullSecrets:
- name: docker-pull-secret - name: docker-pull-secret
image: image:
pullPolicy: Always pullPolicy: Always
repository: "rg.fr-par.scw.cloud/lecoffre/front" repository: "rg.fr-par.scw.cloud/lecoffre/front"
tag:
resources: resources:
requests: requests:
cpu: 200m cpu: 200m
@ -31,14 +19,18 @@ lecoffreFront:
limits: limits:
memory: 2Gi memory: 2Gi
ingress: ingress:
host: app.stg.lecoffre.smart-chain.fr host: api.stg.lecoffre.smart-chain.fr
tls: tls:
hosts: hosts:
- app.stg.lecoffre.smart-chain.fr - api.stg.lecoffre.smart-chain.fr
secretName: app-tls secretName: api-tls
annotations: annotations:
kubernetes.io/ingress.class: nginx kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/from-to-www-redirect: "true" nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
env:
- key: a
scwID: b

View File

@ -15,7 +15,9 @@
} }
.folderlist-container { .folderlist-container {
height: 100%; max-height: calc(100vh - 290px);
height: calc(100vh - 290px);
overflow: auto;
border-right: 1px solid var(--grey-medium); border-right: 1px solid var(--grey-medium);
} }
} }

View File

@ -16,6 +16,7 @@
.folderlist-container { .folderlist-container {
max-height: calc(100vh - 290px); max-height: calc(100vh - 290px);
height: calc(100vh - 290px);
overflow: auto; overflow: auto;
border-right: 1px solid var(--grey-medium); border-right: 1px solid var(--grey-medium);
} }

View File

@ -16,6 +16,7 @@
.folderlist-container { .folderlist-container {
max-height: calc(100vh - 290px); max-height: calc(100vh - 290px);
height: calc(100vh - 290px);
overflow: auto; overflow: auto;
border-right: 1px solid var(--grey-medium); border-right: 1px solid var(--grey-medium);
} }

View File

@ -15,7 +15,9 @@
} }
.folderlist-container { .folderlist-container {
height: 100%; max-height: 100vh;
height: 100vh;
overflow: auto;
border-right: 1px solid var(--grey-medium); border-right: 1px solid var(--grey-medium);
} }
} }

View File

@ -15,7 +15,9 @@
} }
.folderlist-container { .folderlist-container {
height: 100%; max-height: 100vh;
height: 100vh;
overflow: auto;
border-right: 1px solid var(--grey-medium); border-right: 1px solid var(--grey-medium);
} }
} }

View File

@ -15,7 +15,9 @@
} }
.folderlist-container { .folderlist-container {
height: 100%; max-height: 100vh;
height: 100vh;
overflow: auto;
border-right: 1px solid var(--grey-medium); border-right: 1px solid var(--grey-medium);
} }
} }

View File

@ -1,3 +1,4 @@
import Customers from "@Front/Api/LeCoffreApi/SuperAdmin/Customers/Customers";
import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Documents/Documents"; import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Documents/Documents";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import DepositDocument from "@Front/Components/DesignSystem/DepositDocument"; import DepositDocument from "@Front/Components/DesignSystem/DepositDocument";
@ -6,10 +7,10 @@ import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import Base from "@Front/Components/Layouts/Base"; import Base from "@Front/Components/Layouts/Base";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate"; import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import Customer, { Document, DocumentType } from "le-coffre-resources/dist/Customer";
import React from "react"; import React from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Customers from "@Front/Api/LeCoffreApi/SuperAdmin/Customers/Customers";
import Customer, { Document } from "le-coffre-resources/dist/Customer";
type IProps = { type IProps = {
targetedCustormer: string; // MOCK targetedCustormer: string; // MOCK
@ -68,7 +69,13 @@ export default class ClientDashboard extends Base<IProps, IState> {
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
document correspondant. document correspondant.
</Typography> </Typography>
{/* <DepositDocument document={document} /> */} <DepositDocument
document={Document.hydrate<Document>({
document_type: DocumentType.hydrate<DocumentType>({
name: "Document annexe",
}),
})}
/>
</div> </div>
</Confirm> </Confirm>
</div> </div>

View File

@ -33,7 +33,7 @@ export default function CollaboratorInformations(props: IProps) {
useEffect(() => { useEffect(() => {
if (!userSelected) return; if (!userSelected) return;
setIsAdminChecked(userSelected.role?.name === "admin" && !userSelected.office_role); setIsAdminChecked(userSelected.role?.name === "admin");
}, [userSelected]); }, [userSelected]);
const handleRoleChange = useCallback((option: IOption) => { const handleRoleChange = useCallback((option: IOption) => {
@ -81,7 +81,21 @@ export default function CollaboratorInformations(props: IProps) {
}), }),
); );
} else { } else {
// retirer rôle admin const defaultRole = await Roles.getInstance().getOne({
where: {
name: "default",
},
});
if (!defaultRole) return;
await Users.getInstance().put(
userSelected?.uid as string,
User.hydrate<User>({
uid: userSelected?.uid as string,
office_role: undefined,
role: defaultRole,
}),
);
} }
setAdminModalOpened(false); setAdminModalOpened(false);
} catch (e) { } catch (e) {

View File

@ -203,9 +203,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
const allCustomersToLink = this.state.selectedCustomers.concat(this.state.existingCustomers); const allCustomersToLink = this.state.selectedCustomers.concat(this.state.existingCustomers);
let customersToLink: Partial<Customer>[] = allCustomersToLink.map((customer) => { let customersToLink: Partial<Customer>[] = allCustomersToLink.map((customer) => {
return { return Customer.hydrate<Customer>({ uid: customer.value as string });
customer: { uid: customer.value },
};
}) as Partial<Customer>[]; }) as Partial<Customer>[];
if (this.state.selectedOption === "new_customer") { if (this.state.selectedOption === "new_customer") {

View File

@ -61,6 +61,7 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
const backUrl = Module.getInstance() const backUrl = Module.getInstance()
.get() .get()
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid); .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid);
return ( return (
<DefaultNotaryDashboard title={"Demander des documents"} onSelectedFolder={() => {}}> <DefaultNotaryDashboard title={"Demander des documents"} onSelectedFolder={() => {}}>
<div className={classes["root"]}> <div className={classes["root"]}>
@ -133,11 +134,7 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
q: { q: {
deed: { deed: {
include: { include: {
deed_has_document_types: { document_types: true,
include: {
document_type: true,
},
},
}, },
}, },
office: true, office: true,

View File

@ -1,16 +1,5 @@
.root { .root {
position: relative; position: relative;
.background-container {
width: 100%;
height: 100%;
position: absolute;
z-index: -1;
> img {
width: 100%;
object-fit: cover;
}
}
.select-folder-container { .select-folder-container {
max-width: 530px; max-width: 530px;
padding: 80px 72px; padding: 80px 72px;

View File

@ -1,14 +1,13 @@
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import classes from "./classes.module.scss";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import { OfficeFolder } from "le-coffre-resources/dist/Customer";
import { useCallback, useEffect, useState } from "react";
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import { OfficeFolder } from "le-coffre-resources/dist/Customer";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Module from "@Front/Config/Module"; import { useCallback, useEffect, useState } from "react";
import BackgroundImage from "@Assets/images/background.png";
import Image from "next/image"; import LandingImage from "../Login/landing-connect.jpeg";
import classes from "./classes.module.scss";
export default function SelectFolder() { export default function SelectFolder() {
const [folders, setFolders] = useState<OfficeFolder[]>([]); const [folders, setFolders] = useState<OfficeFolder[]>([]);
@ -25,19 +24,14 @@ export default function SelectFolder() {
const handleSelectBlock = useCallback( const handleSelectBlock = useCallback(
(block: IBlock) => { (block: IBlock) => {
router.push( router.push("/client-dashboard");
Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", block.id),
);
}, },
[router], [router],
); );
return ( return (
<DefaultTemplate title="Sélectionner un dossier" hasHeaderLinks={false}> <DefaultDoubleSidePage title="Sélectionner un dossier" image={LandingImage}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["background-container"]}>
<Image src={BackgroundImage} alt="background" />
</div>
<div className={classes["select-folder-container"]}> <div className={classes["select-folder-container"]}>
<div className={classes["title"]}> <div className={classes["title"]}>
<Typography typo={ITypo.H1}>Vos dossiers</Typography> <Typography typo={ITypo.H1}>Vos dossiers</Typography>
@ -56,6 +50,6 @@ export default function SelectFolder() {
</div> </div>
</div> </div>
</div> </div>
</DefaultTemplate> </DefaultDoubleSidePage>
); );
} }

View File

@ -12,7 +12,7 @@ import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Ty
import DefaultUserDashboard from "@Front/Components/LayoutTemplates/DefaultUserDashboard"; import DefaultUserDashboard from "@Front/Components/LayoutTemplates/DefaultUserDashboard";
import JwtService from "@Front/Services/JwtService/JwtService"; import JwtService from "@Front/Services/JwtService/JwtService";
import Toasts from "@Front/Stores/Toasts"; import Toasts from "@Front/Stores/Toasts";
import User, { Appointment, Vote } from "le-coffre-resources/dist/SuperAdmin"; import User, { Appointment, OfficeRole, Vote } from "le-coffre-resources/dist/SuperAdmin";
import { EAppointmentStatus, EVote } from "le-coffre-resources/dist/SuperAdmin/Appointment"; import { EAppointmentStatus, EVote } from "le-coffre-resources/dist/SuperAdmin/Appointment";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -28,6 +28,7 @@ export default function UserInformations(props: IProps) {
const [userSelected, setUserSelected] = useState<User | null>(null); const [userSelected, setUserSelected] = useState<User | null>(null);
const [availableRoles, setAvailableRoles] = useState<IOption[]>([]); const [availableRoles, setAvailableRoles] = useState<IOption[]>([]);
const [roleModalOpened, setRoleModalOpened] = useState<boolean>(false);
const [isSuperAdminModalOpened, setIsSuperAdminModalOpened] = useState<boolean>(false); const [isSuperAdminModalOpened, setIsSuperAdminModalOpened] = useState<boolean>(false);
const [superAdminModalType, setSuperAdminModalType] = useState<"add" | "remove">("add"); const [superAdminModalType, setSuperAdminModalType] = useState<"add" | "remove">("add");
const [adminModalType, setAdminModalType] = useState<"add" | "remove">("add"); const [adminModalType, setAdminModalType] = useState<"add" | "remove">("add");
@ -36,8 +37,15 @@ export default function UserInformations(props: IProps) {
const [isAdminChecked, setIsAdminChecked] = useState<boolean>(false); const [isAdminChecked, setIsAdminChecked] = useState<boolean>(false);
const [isAdminModalOpened, setIsAdminModalOpened] = useState<boolean>(false); const [isAdminModalOpened, setIsAdminModalOpened] = useState<boolean>(false);
const [selectedOption, setSelectedOption] = useState<IOption | null>(null);
const [currentAppointment, setCurrentAppointment] = useState<Appointment | null>(null); const [currentAppointment, setCurrentAppointment] = useState<Appointment | null>(null);
const handleRoleChange = useCallback((option: IOption) => {
setSelectedOption(option);
setRoleModalOpened(true);
}, []);
/** When page change, get the user of the page */ /** When page change, get the user of the page */
const getUser = useCallback(async () => { const getUser = useCallback(async () => {
@ -83,7 +91,7 @@ export default function UserInformations(props: IProps) {
const closeAdminModal = useCallback(() => { const closeAdminModal = useCallback(() => {
setIsAdminModalOpened(false); setIsAdminModalOpened(false);
setIsAdminChecked(userSelected?.role?.name === "admin" && !userSelected.office_role); setIsAdminChecked(userSelected?.role?.name === "admin");
}, [userSelected]); }, [userSelected]);
const handleAdminChanged = (checked: boolean) => { const handleAdminChanged = (checked: boolean) => {
@ -111,7 +119,21 @@ export default function UserInformations(props: IProps) {
}), }),
); );
} else { } else {
// retirer rôle admin const defaultRole = await Roles.getInstance().getOne({
where: {
name: "default",
},
});
if (!defaultRole) return;
await Users.getInstance().put(
userSelected?.uid as string,
User.hydrate<User>({
uid: userSelected?.uid as string,
office_role: undefined,
role: defaultRole,
}),
);
} }
setIsAdminModalOpened(false); setIsAdminModalOpened(false);
}, [userSelected, adminModalType]); }, [userSelected, adminModalType]);
@ -123,7 +145,7 @@ export default function UserInformations(props: IProps) {
const closeSuperAdminModal = useCallback(() => { const closeSuperAdminModal = useCallback(() => {
setIsSuperAdminModalOpened(false); setIsSuperAdminModalOpened(false);
setIsSuperAdminChecked(userSelected?.role?.name === "super-admin" && !userSelected.office_role); setIsSuperAdminChecked(userSelected?.role?.name === "super-admin");
}, [userSelected]); }, [userSelected]);
const handleSuperAdminChanged = (checked: boolean) => { const handleSuperAdminChanged = (checked: boolean) => {
@ -156,11 +178,33 @@ export default function UserInformations(props: IProps) {
setIsSuperAdminModalOpened(false); setIsSuperAdminModalOpened(false);
}, [userSelected, currentAppointment, superAdminModalType, getUser]); }, [userSelected, currentAppointment, superAdminModalType, getUser]);
const closeRoleModal = useCallback(() => {
setRoleModalOpened(false);
setSelectedOption({
value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid,
label: userSelected?.office_role ? userSelected?.office_role?.name : userSelected?.role?.name!,
});
}, [userSelected?.office_role, userSelected?.role?.name, userSelected?.role?.uid]);
const changeRole = useCallback(async () => {
await Users.getInstance().put(
userSelected?.uid as string,
User.hydrate<User>({
uid: userSelected?.uid as string,
office_role: OfficeRole.hydrate<OfficeRole>({
uid: selectedOption?.value as string,
}),
}),
);
setRoleModalOpened(false);
getUser();
}, [getUser, selectedOption?.value, userSelected?.uid]);
/** Reset switch state when userSelect change */ /** Reset switch state when userSelect change */
useEffect(() => { useEffect(() => {
if (!userSelected) return; if (!userSelected) return;
setIsSuperAdminChecked(userSelected.role?.name === "super-admin"); setIsSuperAdminChecked(userSelected.role?.name === "super-admin");
setIsAdminChecked(userSelected.role?.name === "admin" && !userSelected.office_role); setIsAdminChecked(userSelected.role?.name === "admin");
}, [userSelected]); }, [userSelected]);
const userHasVoted = useCallback(() => { const userHasVoted = useCallback(() => {
@ -223,7 +267,8 @@ export default function UserInformations(props: IProps) {
<SelectField <SelectField
placeholder="Rôle" placeholder="Rôle"
name="role" name="role"
options={availableRoles} options={availableRoles.filter((role) => role.label !== "admin")}
onChange={handleRoleChange}
selectedOption={{ selectedOption={{
value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid, value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid,
label: userSelected?.office_role ? userSelected?.office_role?.name : userSelected?.role?.name!, label: userSelected?.office_role ? userSelected?.office_role?.name : userSelected?.role?.name!,
@ -271,6 +316,21 @@ export default function UserInformations(props: IProps) {
</div> </div>
</div> </div>
</div> </div>
<Confirm
isOpen={roleModalOpened}
onClose={closeRoleModal}
onAccept={changeRole}
closeBtn
header={"Changement de rôle"}
confirmText={"Valider"}
cancelText={"Annuler"}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Attribuer le rôle de <span className={classes["role-name"]}>{selectedOption?.label}</span> à{" "}
{userSelected?.contact?.first_name} {userSelected?.contact?.last_name} ?
</Typography>
</div>
</Confirm>
<Confirm <Confirm
isOpen={isSuperAdminModalOpened} isOpen={isSuperAdminModalOpened}
onClose={closeSuperAdminModal} onClose={closeSuperAdminModal}

View File

@ -1,5 +1,5 @@
import ClientDashboard from "@Front/Components/Layouts/ClientDashboard"; import ClientDashboard from "@Front/Components/Layouts/ClientDashboard";
export default function Route() { export default function Route() {
return <ClientDashboard targetedCustormer="kevin.hautefaye@gmail.com" />; return <ClientDashboard targetedCustormer="lucie.chevalier@outlook.com" />;
} }