Skip to content
Snippets Groups Projects
Commit a3945eee authored by Rémi PAILHAREY's avatar Rémi PAILHAREY :fork_knife_plate:
Browse files

chore(release): Ecolyo canary release

parent 1d7c524e
No related branches found
No related tags found
1 merge request!77Back-office SGE before canary release
Showing
with 493 additions and 62 deletions
...@@ -2,8 +2,9 @@ NODE_TLS_REJECT_UNAUTHORIZED = '0' ...@@ -2,8 +2,9 @@ NODE_TLS_REJECT_UNAUTHORIZED = '0'
HTTPS=true HTTPS=true
SSL_CRT_FILE=cert.pem SSL_CRT_FILE=cert.pem
SSL_KEY_FILE=key.pem SSL_KEY_FILE=key.pem
# Common
HOSTNAME= # Common settings
HOSTNAME=localhost
ADMIN_ROLE= ADMIN_ROLE=
DEBUG_MODE= DEBUG_MODE=
MOCK_OAUTH2= MOCK_OAUTH2=
...@@ -22,4 +23,7 @@ LOGOUT_URL= ...@@ -22,4 +23,7 @@ LOGOUT_URL=
# Access to the database # Access to the database
DATABASE_USER= DATABASE_USER=
DATABASE_PASSWORD= DATABASE_PASSWORD=
DATABASE_NAME= DATABASE_NAME=
\ No newline at end of file
SGE_API_TOKEN=
MEILI_MASTER_KEY=
\ No newline at end of file
module.exports = { module.exports = {
env: { parser: '@typescript-eslint/parser', // Specifies the ESLint parser
commonjs: true, extends: [
es6: true, 'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react
node: true, 'plugin:@typescript-eslint/eslint-recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin
}, 'prettier', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
'plugin:react-hooks/recommended',
],
parserOptions: { parserOptions: {
ecmaVersion: 2018, ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
sourceType: 'module', // Allows for the use of imports
ecmaFeatures: {
jsx: true, // Allows for the parsing of JSX
},
}, },
plugins: ['prettier', 'react-hooks'],
extends: 'react-app',
rules: { rules: {
'react-hooks/rules-of-hooks': 'error', // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
'react-hooks/exhaustive-deps': 'warn', '@typescript-eslint/explicit-function-return-type': 'off',
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'warn',
},
settings: {
react: {
version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
},
}, },
} }
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
/coverage /coverage
# production # production
/build /build
/image-lib /mnt/image-lib
# misc # misc
.DS_Store .DS_Store
/.env /.env
...@@ -18,7 +18,9 @@ ...@@ -18,7 +18,9 @@
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
meili_data
db_data
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
\ No newline at end of file
image: docker:git default:
image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:14.19.3-alpine
services: services:
- docker:dind - name: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/docker:20.10.9-dind
alias: docker
variables: variables:
DOCKER_DRIVER: overlay2 DEPENDENCY_PROXY: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/
DOCKER_TLS_CERTDIR: ''
stages: stages:
- build
- quality - quality
- build
build-test: build-test:
stage: build stage: build
image: node:14.15.4-alpine image: node:14.20-alpine
before_script: before_script:
- apk add git - apk add git
- apk add bash - apk add bash
...@@ -40,13 +40,15 @@ build: ...@@ -40,13 +40,15 @@ build:
sonarqube: sonarqube:
stage: quality stage: quality
only: only:
- dev
- merge_requests - merge_requests
when: manual image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/sonarsource/sonar-scanner-cli:4
image: registry.forge.grandlyon.com/apoyen2/sonnar-scanner-gl:master variables:
before_script: SONAR_USER_HOME: '${CI_PROJECT_DIR}/.sonar' # Defines the location of the analysis task cache
- export NODE_PATH=$NODE_PATH:`npm root -g` GIT_DEPTH: '0' # T
- npm install -g typescript cache:
key: '${CI_JOB_NAME}'
paths:
- .sonar/cache
script: script:
- > - >
sonar-scanner sonar-scanner
......
...@@ -28,7 +28,7 @@ More reading on checklists can be found in the "Checklist Manifesto": http://atu ...@@ -28,7 +28,7 @@ More reading on checklists can be found in the "Checklist Manifesto": http://atu
---> --->
### Quality ### Quality [![Bugs](https://sonarqube.forge.grandlyon.com/api/project_badges/measure?project=web-et-numerique-llle-project-backoffice-client&metric=bugs&token=56be0e829f585726c42f15aeeb6bb71e090e4eee)](https://sonarqube.forge.grandlyon.com/dashboard?id=web-et-numerique-llle-project-backoffice-client) - [![Code Smells](https://sonarqube.forge.grandlyon.com/api/project_badges/measure?project=web-et-numerique-llle-project-backoffice-client&metric=code_smells&token=56be0e829f585726c42f15aeeb6bb71e090e4eee)](https://sonarqube.forge.grandlyon.com/dashboard?id=web-et-numerique-llle-project-backoffice-client)
* [ ] Confirmed * [ ] Confirmed
......
{ {
"endOfLine": "lf", "endOfLine": "auto",
"semi": false, "semi": false,
"singleQuote": true, "singleQuote": true,
"tabWidth": 2, "tabWidth": 2,
......
...@@ -24,5 +24,9 @@ ...@@ -24,5 +24,9 @@
"source.fixAll.eslint": true "source.fixAll.eslint": true
}, },
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"peacock.color": "#2aa63d" "peacock.color": "#2aa63d",
"sonarlint.connectedMode.project": {
"connectionId": "sonarqube-forge-grandlyon",
"projectKey": "web-et-numerique-llle-project-backoffice-client"
}
} }
...@@ -16,11 +16,12 @@ You can then clone the app repository and install dependencies: ...@@ -16,11 +16,12 @@ You can then clone the app repository and install dependencies:
```sh ```sh
$ git clone https://forge.grandlyon.com/web-et-numerique/llle_project/backoffice-client.git $ git clone https://forge.grandlyon.com/web-et-numerique/llle_project/backoffice-client.git
$ cd backoffice-client $ cd backoffice-client
yarn
``` ```
## Local usage ## Local usage
Before launching the application, ensure you've properly filled the .env file according to the template. If needed please refer to a team member. Before launching the application, ensure you've properly filled the **.env** file according to the .env.template. If needed please refer to a team member.
In order to launch the projet in local with the backend working launch the following command In order to launch the projet in local with the backend working launch the following command
......
...@@ -17,6 +17,8 @@ services: ...@@ -17,6 +17,8 @@ services:
database-agent: database-agent:
image: mysql:5 image: mysql:5
volumes:
- ./db_data:/var/lib/mysql
ports: ports:
- 3306:3306 - 3306:3306
environment: environment:
...@@ -27,20 +29,41 @@ services: ...@@ -27,20 +29,41 @@ services:
interval: 5s interval: 5s
timeout: 10s timeout: 10s
retries: 60 retries: 60
meilisearch:
image: getmeili/meilisearch:v0.28.1
healthcheck:
test: ['CMD', 'curl', '-f', 'http://0.0.0.0:7700']
interval: 10s
timeout: 10s
retries: 3
volumes: volumes:
- ./dbinit:/dbinit - ./meili_data:/meili_data
ports:
- 7700:7700
environment:
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
depends_on:
- database-agent
ports:
- 8008:80
environment:
PMA_HOST: database-agent
backend: backend:
image: registry.forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server:dev # --When using yarn local-up build backoffice service image with: docker build . -t backoffice-server
image: backoffice-server
depends_on: depends_on:
- 'database-agent' database-agent:
condition: service_healthy
meilisearch:
condition: service_healthy
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /etc/localtime:/etc/localtime:ro - ./mnt:/app/mnt
- ./configs:/app/configs
- ./letsencrypt_cache:/app/letsencrypt_cache
- ./data:/app/data
- ./${IMAGE_FOLDER}:/app/${IMAGE_FOLDER}
ports: ports:
- ${HTTPS_PORT}:${HTTPS_PORT} - ${HTTPS_PORT}:${HTTPS_PORT}
- 8090:8090 - 8090:8090
...@@ -61,3 +84,6 @@ services: ...@@ -61,3 +84,6 @@ services:
- DATABASE_HOST=database-agent - DATABASE_HOST=database-agent
- MOCK_OAUTH2=${MOCK_OAUTH2} - MOCK_OAUTH2=${MOCK_OAUTH2}
- IMAGE_FOLDER=${IMAGE_FOLDER} - IMAGE_FOLDER=${IMAGE_FOLDER}
- SGE_API_TOKEN=${SGE_API_TOKEN}
- MEILI_HOST=http://meilisearch:7700
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
{
"Key": "F+gSLqJL7zZynkJYp/fd2jI6t3TolYGRt9bjO7mMgQc="
}
...@@ -14,12 +14,6 @@ ...@@ -14,12 +14,6 @@
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": { "browserslist": {
"production": [ "production": [
">0.2%", ">0.2%",
...@@ -38,17 +32,18 @@ ...@@ -38,17 +32,18 @@
"@types/react": "^17.0.0", "@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^17.0.0",
"@types/react-router-dom": "^5.1.8", "@types/react-router-dom": "^5.1.8",
"@typescript-eslint/eslint-plugin": "^4.28.4", "@typescript-eslint/eslint-plugin": "^5.3.1",
"@typescript-eslint/parser": "^4.28.4", "@typescript-eslint/parser": "^5.3.1",
"eslint": "^7.31.0", "eslint": "^8.21.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.5.0",
"eslint-config-react-app": "^6.0.0", "eslint-config-react-app": "^6.0.0",
"eslint-plugin-import": "^2.23.4", "eslint-plugin-import": "^2.23.4",
"eslint-plugin-jest": "^24.4.0", "eslint-plugin-jest": "^24.4.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.24.0", "eslint-plugin-react": "7.30.1",
"eslint-plugin-react-hooks": "^4.2.1-alpha-e4e8226c6-20210812", "eslint-plugin-react-hooks": "^4.2.0",
"prettier": "^2.3.2" "prettier": "^2.7.1",
"prettier-eslint": "^15.0.1"
}, },
"dependencies": { "dependencies": {
"@material-ui/core": "^4.12.3", "@material-ui/core": "^4.12.3",
...@@ -58,17 +53,21 @@ ...@@ -58,17 +53,21 @@
"@testing-library/user-event": "^12.1.10", "@testing-library/user-event": "^12.1.10",
"@types/draft-js": "^0.11.4", "@types/draft-js": "^0.11.4",
"@types/html-to-draftjs": "^1.4.0", "@types/html-to-draftjs": "^1.4.0",
"@types/luxon": "^3.0.0",
"@types/react-draft-wysiwyg": "^1.13.3", "@types/react-draft-wysiwyg": "^1.13.3",
"ag-grid-community": "^27.1.0",
"ag-grid-react": "^27.1.0",
"axios": "^0.21.1", "axios": "^0.21.1",
"dayjs": "^1.10.7", "dayjs": "^1.10.7",
"draft-js": "^0.11.7", "draft-js": "^0.11.7",
"draft-js-export-html": "^1.4.1", "draft-js-export-html": "^1.4.1",
"html-to-draftjs": "^1.5.0", "html-to-draftjs": "^1.5.0",
"luxon": "^3.0.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-draft-wysiwyg": "^1.14.7", "react-draft-wysiwyg": "^1.14.7",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "4.0.3", "react-scripts": "^5.0.1",
"react-toastify": "^7.0.4", "react-toastify": "^7.0.4",
"sass": "^1.35.2", "sass": "^1.35.2",
"typescript": "^4.1.2", "typescript": "^4.1.2",
......
<?xml version="1.0"?> <?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path xmlns="http://www.w3.org/2000/svg" d="m368 511.957031h-309.332031c-32.363281 0-58.667969-26.304687-58.667969-58.667969v-309.332031c0-32.363281 26.304688-58.667969 58.667969-58.667969h181.332031c8.832031 0 16 7.167969 16 16 0 8.832032-7.167969 16-16 16h-181.332031c-14.699219 0-26.667969 11.96875-26.667969 26.667969v309.332031c0 14.699219 11.96875 26.667969 26.667969 26.667969h309.332031c14.699219 0 26.667969-11.96875 26.667969-26.667969v-181.332031c0-8.832031 7.167969-16 16-16s16 7.148438 16 16v181.332031c0 32.363282-26.304688 58.667969-58.667969 58.667969zm0 0" fill="#fafafa" data-original="#000000" style="" class=""/><path xmlns="http://www.w3.org/2000/svg" d="m187.136719 340.820312c-4.203125 0-8.300781-1.664062-11.308594-4.691406-3.796875-3.777344-5.417969-9.21875-4.371094-14.445312l15.082031-75.433594c.617188-3.113281 2.152344-5.953125 4.371094-8.171875l220.953125-220.925781c22.867188-22.871094 60.074219-22.871094 82.964844 0 11.070313 11.070312 17.171875 25.792968 17.171875 41.472656s-6.101562 30.398438-17.195312 41.472656l-220.925782 220.949219c-2.21875 2.238281-5.078125 3.753906-8.171875 4.371094l-75.414062 15.082031c-1.046875.214844-2.113281.320312-3.15625.320312zm75.433593-31.082031h.214844zm-45.609374-52.457031-9.410157 47.144531 47.125-9.429687 217.515625-217.511719c5.035156-5.058594 7.808594-11.734375 7.808594-18.859375s-2.773438-13.804688-7.808594-18.859375c-10.367187-10.390625-27.285156-10.390625-37.714844 0zm0 0" fill="#fafafa" data-original="#000000" style="" class=""/><path xmlns="http://www.w3.org/2000/svg" d="m453.332031 134.976562c-4.09375 0-8.191406-1.558593-11.304687-4.695312l-60.332032-60.351562c-6.25-6.25-6.25-16.382813 0-22.632813s16.382813-6.25 22.636719 0l60.328125 60.351563c6.25 6.25 6.25 16.382812 0 22.632812-3.136718 3.117188-7.230468 4.695312-11.328125 4.695312zm0 0" fill="#fafafa" data-original="#000000" style="" class=""/></g></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" xmlnsSvgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path xmlns="http://www.w3.org/2000/svg" d="m368 511.957031h-309.332031c-32.363281 0-58.667969-26.304687-58.667969-58.667969v-309.332031c0-32.363281 26.304688-58.667969 58.667969-58.667969h181.332031c8.832031 0 16 7.167969 16 16 0 8.832032-7.167969 16-16 16h-181.332031c-14.699219 0-26.667969 11.96875-26.667969 26.667969v309.332031c0 14.699219 11.96875 26.667969 26.667969 26.667969h309.332031c14.699219 0 26.667969-11.96875 26.667969-26.667969v-181.332031c0-8.832031 7.167969-16 16-16s16 7.148438 16 16v181.332031c0 32.363282-26.304688 58.667969-58.667969 58.667969zm0 0" fill="#fafafa" data-original="#000000" style="" class=""/><path xmlns="http://www.w3.org/2000/svg" d="m187.136719 340.820312c-4.203125 0-8.300781-1.664062-11.308594-4.691406-3.796875-3.777344-5.417969-9.21875-4.371094-14.445312l15.082031-75.433594c.617188-3.113281 2.152344-5.953125 4.371094-8.171875l220.953125-220.925781c22.867188-22.871094 60.074219-22.871094 82.964844 0 11.070313 11.070312 17.171875 25.792968 17.171875 41.472656s-6.101562 30.398438-17.195312 41.472656l-220.925782 220.949219c-2.21875 2.238281-5.078125 3.753906-8.171875 4.371094l-75.414062 15.082031c-1.046875.214844-2.113281.320312-3.15625.320312zm75.433593-31.082031h.214844zm-45.609374-52.457031-9.410157 47.144531 47.125-9.429687 217.515625-217.511719c5.035156-5.058594 7.808594-11.734375 7.808594-18.859375s-2.773438-13.804688-7.808594-18.859375c-10.367187-10.390625-27.285156-10.390625-37.714844 0zm0 0" fill="#fafafa" data-original="#000000" style="" class=""/><path xmlns="http://www.w3.org/2000/svg" d="m453.332031 134.976562c-4.09375 0-8.191406-1.558593-11.304687-4.695312l-60.332032-60.351562c-6.25-6.25-6.25-16.382813 0-22.632813s16.382813-6.25 22.636719 0l60.328125 60.351563c6.25 6.25 6.25 16.382812 0 22.632812-3.136718 3.117188-7.230468 4.695312-11.328125 4.695312zm0 0" fill="#fafafa" data-original="#000000" style="" class=""/></g></svg>
<svg width="49" height="48" viewBox="0 0 49 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.5 36L24.5 5M24.5 36L11.5 24.5789M24.5 36L37.5 24.5789" stroke="#E3B82A" stroke-width="6"/>
<path d="M10.5 45L38.5 45" stroke="#E3B82A" stroke-width="3"/>
</svg>
<?xml version="1.0"?> <?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 444.531 444.531" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g> <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" xmlnsSvgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 444.531 444.531" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g>
<g xmlns="http://www.w3.org/2000/svg"> <g xmlns="http://www.w3.org/2000/svg">
<path d="M213.13,222.409L351.88,83.653c7.05-7.043,10.567-15.657,10.567-25.841c0-10.183-3.518-18.793-10.567-25.835 l-21.409-21.416C323.432,3.521,314.817,0,304.637,0s-18.791,3.521-25.841,10.561L92.649,196.425 c-7.044,7.043-10.566,15.656-10.566,25.841s3.521,18.791,10.566,25.837l186.146,185.864c7.05,7.043,15.66,10.564,25.841,10.564 s18.795-3.521,25.834-10.564l21.409-21.412c7.05-7.039,10.567-15.604,10.567-25.697c0-10.085-3.518-18.746-10.567-25.978 L213.13,222.409z" fill="#e3b82a" data-original="#000000" style="" class=""/> <path d="M213.13,222.409L351.88,83.653c7.05-7.043,10.567-15.657,10.567-25.841c0-10.183-3.518-18.793-10.567-25.835 l-21.409-21.416C323.432,3.521,314.817,0,304.637,0s-18.791,3.521-25.841,10.561L92.649,196.425 c-7.044,7.043-10.566,15.656-10.566,25.841s3.521,18.791,10.566,25.837l186.146,185.864c7.05,7.043,15.66,10.564,25.841,10.564 s18.795-3.521,25.834-10.564l21.409-21.412c7.05-7.039,10.567-15.604,10.567-25.697c0-10.085-3.518-18.746-10.567-25.978 L213.13,222.409z" fill="#e3b82a" data-original="#000000" style="" class=""/>
</g> </g>
......
<?xml version="1.0"?> <?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path xmlns="http://www.w3.org/2000/svg" d="m512 256c0-141.488281-114.496094-256-256-256-141.488281 0-256 114.496094-256 256 0 140.234375 113.539062 256 256 256 141.875 0 256-115.121094 256-256zm-256-226c124.617188 0 226 101.382812 226 226 0 45.585938-13.558594 89.402344-38.703125 126.515625-100.96875-108.609375-273.441406-108.804687-374.59375 0-25.144531-37.113281-38.703125-80.929687-38.703125-126.515625 0-124.617188 101.382812-226 226-226zm-168.585938 376.5c89.773438-100.695312 247.421876-100.671875 337.167969 0-90.074219 100.773438-247.054687 100.804688-337.167969 0zm0 0" fill="#fafafa" data-original="#000000" style=""/><path xmlns="http://www.w3.org/2000/svg" d="m256 271c49.625 0 90-40.375 90-90v-30c0-49.625-40.375-90-90-90s-90 40.375-90 90v30c0 49.625 40.375 90 90 90zm-60-120c0-33.085938 26.914062-60 60-60s60 26.914062 60 60v30c0 33.085938-26.914062 60-60 60s-60-26.914062-60-60zm0 0" fill="#fafafa" data-original="#000000" style=""/></g></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" xmlnsSvgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path xmlns="http://www.w3.org/2000/svg" d="m512 256c0-141.488281-114.496094-256-256-256-141.488281 0-256 114.496094-256 256 0 140.234375 113.539062 256 256 256 141.875 0 256-115.121094 256-256zm-256-226c124.617188 0 226 101.382812 226 226 0 45.585938-13.558594 89.402344-38.703125 126.515625-100.96875-108.609375-273.441406-108.804687-374.59375 0-25.144531-37.113281-38.703125-80.929687-38.703125-126.515625 0-124.617188 101.382812-226 226-226zm-168.585938 376.5c89.773438-100.695312 247.421876-100.671875 337.167969 0-90.074219 100.773438-247.054687 100.804688-337.167969 0zm0 0" fill="#fafafa" data-original="#000000" style=""/><path xmlns="http://www.w3.org/2000/svg" d="m256 271c49.625 0 90-40.375 90-90v-30c0-49.625-40.375-90-90-90s-90 40.375-90 90v30c0 49.625 40.375 90 90 90zm-60-120c0-33.085938 26.914062-60 60-60s60 26.914062 60 60v30c0 33.085938-26.914062 60-60 60s-60-26.914062-60-60zm0 0" fill="#fafafa" data-original="#000000" style=""/></g></svg>
<?xml version="1.0"?> <?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 384 384" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g> <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" xmlnsSvgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 384 384" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g>
<g xmlns="http://www.w3.org/2000/svg"> <g xmlns="http://www.w3.org/2000/svg">
<g> <g>
<g> <g>
......
<?xml version="1.0"?> <?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 444.819 444.819" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g> <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" xmlnsSvgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 444.819 444.819" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g>
<g xmlns="http://www.w3.org/2000/svg"> <g xmlns="http://www.w3.org/2000/svg">
<path d="M352.025,196.712L165.884,10.848C159.029,3.615,150.469,0,140.187,0c-10.282,0-18.842,3.619-25.697,10.848L92.792,32.264 c-7.044,7.043-10.566,15.604-10.566,25.692c0,9.897,3.521,18.56,10.566,25.981l138.753,138.473L92.786,361.168 c-7.042,7.043-10.564,15.604-10.564,25.693c0,9.896,3.521,18.562,10.564,25.98l21.7,21.413 c7.043,7.043,15.612,10.564,25.697,10.564c10.089,0,18.656-3.521,25.697-10.564l186.145-185.864 c7.046-7.423,10.571-16.084,10.571-25.981C362.597,212.321,359.071,203.755,352.025,196.712z" fill="#e3b82a" data-original="#000000" style="" class=""/> <path d="M352.025,196.712L165.884,10.848C159.029,3.615,150.469,0,140.187,0c-10.282,0-18.842,3.619-25.697,10.848L92.792,32.264 c-7.044,7.043-10.566,15.604-10.566,25.692c0,9.897,3.521,18.56,10.566,25.981l138.753,138.473L92.786,361.168 c-7.042,7.043-10.564,15.604-10.564,25.693c0,9.896,3.521,18.562,10.564,25.98l21.7,21.413 c7.043,7.043,15.612,10.564,25.697,10.564c10.089,0,18.656-3.521,25.697-10.564l186.145-185.864 c7.046-7.423,10.571-16.084,10.571-25.981C362.597,212.321,359.071,203.755,352.025,196.712z" fill="#e3b82a" data-original="#000000" style="" class=""/>
</g> </g>
......
<?xml version="1.0"?> <?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 477.867 477.867" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g> <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" xmlnsvgjs="http://svgjs.com/svgjs" version="1.1" width="512" height="512" x="0" y="0" viewBox="0 0 477.867 477.867" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g>
<g xmlns="http://www.w3.org/2000/svg"> <g xmlns="http://www.w3.org/2000/svg">
<g> <g>
<path d="M460.8,221.867H185.31c-9.255-36.364-46.237-58.34-82.602-49.085c-24.116,6.138-42.947,24.969-49.085,49.085H17.067 C7.641,221.867,0,229.508,0,238.934S7.641,256,17.067,256h36.557c9.255,36.364,46.237,58.34,82.602,49.085 c24.116-6.138,42.947-24.969,49.085-49.085H460.8c9.426,0,17.067-7.641,17.067-17.067S470.226,221.867,460.8,221.867z M119.467,273.067c-18.851,0-34.133-15.282-34.133-34.133c0-18.851,15.282-34.133,34.133-34.133s34.133,15.282,34.133,34.133 C153.6,257.785,138.318,273.067,119.467,273.067z" fill="#fafafa" data-original="#000000" style="" class=""/> <path d="M460.8,221.867H185.31c-9.255-36.364-46.237-58.34-82.602-49.085c-24.116,6.138-42.947,24.969-49.085,49.085H17.067 C7.641,221.867,0,229.508,0,238.934S7.641,256,17.067,256h36.557c9.255,36.364,46.237,58.34,82.602,49.085 c24.116-6.138,42.947-24.969,49.085-49.085H460.8c9.426,0,17.067-7.641,17.067-17.067S470.226,221.867,460.8,221.867z M119.467,273.067c-18.851,0-34.133-15.282-34.133-34.133c0-18.851,15.282-34.133,34.133-34.133s34.133,15.282,34.133,34.133 C153.6,257.785,138.318,273.067,119.467,273.067z" fill="#fafafa" data-original="#000000" style="" class=""/>
......
import { AxiosRequestConfig } from 'axios'
export const getAxiosXSRFHeader = (token: string): AxiosRequestConfig => ({
headers: {
'XSRF-TOKEN': token,
},
})
import React, {
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react'
import { AgGridReact } from 'ag-grid-react'
import 'ag-grid-community/dist/styles/ag-grid.css'
import 'ag-grid-community/dist/styles/ag-theme-alpine-dark.css'
import TablePagination from '@material-ui/core/TablePagination'
import {
ColDef,
ColGroupDef,
CsvExportParams,
GridApi,
GridReadyEvent,
RowNode,
RowSelectedEvent,
ValueFormatterParams,
} from 'ag-grid-community'
import styles from './consents.module.scss'
import './agGridOverrides.scss'
import './muiPaginationOverrides.scss'
import DowloadModal from './DowloadModal'
import { ConsentService } from '../../services/consent.service'
import { UserContextProps, UserContext } from '../../hooks/userContext'
import { IConsent } from '../../models/consent.model'
import { getAxiosXSRFHeader } from '../../axios.config'
import { DateTime } from 'luxon'
const Consents: React.FC = () => {
const [gridApi, setGridApi] = useState<GridApi | null>(null)
const [search, setSearch] = useState<string>('')
const [selectedNodes, setSelectedNodes] = useState<RowNode[]>([])
const [isShowingSelection, setIsShowingSelection] = useState<boolean>(false)
const [openDowloadModal, setOpenDowloadModal] = useState<boolean>(false)
const [consents, setConsents] = useState<IConsent[]>([])
const [page, setPage] = useState<number>(0)
const [rowsPerPage, setRowsPerPage] = useState<number>(50)
const [totalRows, setTotalRows] = useState<number>(50)
const { user }: Partial<UserContextProps> = useContext(UserContext)
const consentService = useMemo(() => {
return new ConsentService()
}, [])
const toggleOpenModal = useCallback(() => {
setOpenDowloadModal((prev) => !prev)
}, [])
const defaultColDef = useMemo(
() => ({
sortable: true,
resizable: true,
}),
[]
)
const dateFormatter = (data: ValueFormatterParams): string => {
return (data.value as DateTime).toLocaleString()
}
const [columnDefs] = useState<(ColDef | ColGroupDef)[] | null>([
{
field: 'ID',
hide: true,
},
{
field: 'pointID',
headerName: 'N° PDL',
initialWidth: 180,
filter: true,
checkboxSelection: true,
},
{
field: 'lastname',
headerName: 'Nom',
initialWidth: 180,
filter: true,
cellStyle: { textTransform: 'uppercase' },
},
{
field: 'firstname',
headerName: 'Prénom',
initialWidth: 180,
filter: true,
cellStyle: { textTransform: 'capitalize' },
},
{
field: 'address',
headerName: 'Adresse',
initialWidth: 300,
filter: true,
flex: 1,
},
{
field: 'postalCode',
headerName: 'CP',
initialWidth: 80,
filter: true,
},
{
field: 'city',
headerName: 'Ville',
},
{
field: 'safetyOnBoarding',
headerName: 'Secours',
initialWidth: 100,
},
{
field: 'startDate',
valueFormatter: dateFormatter,
headerName: 'Début du consentement',
initialWidth: 150,
filter: true,
sort: 'desc',
},
{
field: 'endDate',
valueFormatter: dateFormatter,
headerName: 'Fin du consentement',
initialWidth: 150,
filter: true,
},
])
const handleChangePage = useCallback(
(
_event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null,
newPage: number
) => {
setPage(newPage)
},
[]
)
const handleChangeRowsPerPage = useCallback((event: any) => {
setRowsPerPage(event.target.value)
setPage(0)
}, [])
const checkSelectedNodes = useCallback(() => {
if (gridApi) {
const newNodes = gridApi.getRenderedNodes()
const idsToCheck: string[] = selectedNodes
.filter((node: RowNode) => node.isSelected)
.map((node: RowNode) => node.data.ID)
newNodes.forEach((node: RowNode) => {
if (idsToCheck.includes(node.data.ID))
node.setSelected(true, false, true)
})
}
}, [gridApi, selectedNodes])
const handleSearchChange = useCallback(
async (newSearch: string) => {
setSearch(newSearch)
if (user) {
let consentsData: IConsent[] | null = []
if (newSearch) {
consentsData = await consentService.searchConsent(
newSearch,
getAxiosXSRFHeader(user.xsrftoken)
)
} else {
const consentPagination = await consentService.getConsents(
rowsPerPage,
page,
getAxiosXSRFHeader(user.xsrftoken)
)
consentsData = consentPagination && consentPagination.rows
}
if (consentsData) {
setConsents(consentsData)
checkSelectedNodes()
}
}
},
[user, consentService, rowsPerPage, page, checkSelectedNodes]
)
const resetSelection = useCallback(() => {
if (gridApi) {
setIsShowingSelection(false)
gridApi.setRowData(consents)
gridApi.deselectAll()
setSelectedNodes([])
}
}, [gridApi, consents])
const onRowSelected = useCallback(
(event: RowSelectedEvent) => {
if (event.node.isSelected()) {
const index = selectedNodes.findIndex(
(node) => node.data.ID === event.node.data.ID
)
if (index === -1) {
setSelectedNodes((prev) => [...prev, event.node])
}
} else {
setSelectedNodes((prev) =>
prev.filter((node) => {
return node.data.ID != event.node.data.ID
})
)
}
},
[selectedNodes]
)
const continueSelection = useCallback(() => {
if (gridApi) {
setIsShowingSelection(false)
gridApi?.setRowData(consents)
const newNodes = gridApi.getRenderedNodes()
// We have to select nodes that have already been selected since we cannot pass a Node array to init AgGrid
const idsToCheck: string[] = selectedNodes
.filter((node: RowNode) => node.isSelected)
.map((node: RowNode) => node.data.ID)
newNodes.forEach((node: RowNode) => {
if (idsToCheck.includes(node.data.ID)) node.setSelected(true)
})
}
}, [gridApi, consents, selectedNodes])
const showCurrentSelection = useCallback(() => {
setIsShowingSelection(true)
const dataFromNode = selectedNodes.map((item: RowNode) => item.data)
selectedNodes && gridApi?.setRowData(dataFromNode)
gridApi?.selectAll()
}, [gridApi, selectedNodes])
const exportData = useCallback(() => {
//You can change default column separator
const params: CsvExportParams = {
columnSeparator: ',',
}
gridApi?.exportDataAsCsv(params)
setOpenDowloadModal(false)
resetSelection()
}, [gridApi, resetSelection])
const onGridReady = ({ api }: GridReadyEvent) => {
//Grid init method
setGridApi(api)
api.sizeColumnsToFit()
}
useEffect(() => {
function handleResize() {
gridApi?.sizeColumnsToFit()
}
handleResize()
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [gridApi])
useEffect(() => {
async function getConsentsData() {
if (user) {
const consentsPaginationData = await consentService.getConsents(
rowsPerPage,
page,
getAxiosXSRFHeader(user.xsrftoken)
)
if (consentsPaginationData) {
setConsents(consentsPaginationData.rows)
checkSelectedNodes()
setTotalRows(consentsPaginationData.totalRows)
}
}
}
getConsentsData()
// /!\ Do not add checkSelected in dependencies or effect will trigger on each selection
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [user, consentService, rowsPerPage, page])
return (
<>
<div className="header">
<p className="title pagetitle">Gestion des consentements Enedis</p>
</div>
<div className={styles.content}>
<div className={styles.searchField}>
<div className={styles.inputGroup}>
<label htmlFor="search">Recherche</label>
<input
value={search}
name="search"
type="text"
placeholder="N°PDL, Nom, Prénom..."
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleSearchChange(e.target.value)
}
disabled={isShowingSelection}
autoComplete="off"
></input>
</div>
</div>
<div
className="ag-theme-alpine-dark"
style={{ width: '100%', height: '75vh' }}
>
<AgGridReact
onGridReady={onGridReady}
defaultColDef={defaultColDef}
rowHeight={35}
rowData={consents}
columnDefs={columnDefs}
animateRows={true}
rowSelection="multiple"
allowDragFromColumnsToolPanel={false}
onRowSelected={onRowSelected}
sortingOrder={['asc', 'desc']}
rowMultiSelectWithClick={true}
pagination={false}
suppressCellFocus={true}
></AgGridReact>
{search === '' && !isShowingSelection && (
<TablePagination
labelRowsPerPage="Consentements par page"
component="div"
count={totalRows}
page={page}
onPageChange={handleChangePage}
rowsPerPage={rowsPerPage}
onRowsPerPageChange={handleChangeRowsPerPage}
rowsPerPageOptions={[10, 25, 50, 100]}
/>
)}
</div>
{openDowloadModal && (
<DowloadModal
toggleOpenModal={toggleOpenModal}
exportData={exportData}
/>
)}
</div>
<div className={styles.footerButtons}>
<button
className="btnDelete"
onClick={isShowingSelection ? continueSelection : resetSelection}
disabled={
!isShowingSelection && selectedNodes && selectedNodes.length === 0
}
>
{isShowingSelection
? 'Continuer ma sélection'
: 'Tout déselectionner'}
</button>
<button
className={styles.btnSelection + ' btnValid'}
onClick={!isShowingSelection ? showCurrentSelection : toggleOpenModal}
disabled={selectedNodes && selectedNodes.length <= 0}
>
{!isShowingSelection ? 'Voir mes sélections' : 'Télécharger'}
<span>{selectedNodes?.length}</span>
</button>
</div>
</>
)
}
export default Consents
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment