From 589cbd9632cc362df3c832af8e41f0776903fad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20PAILHAREY?= <rpailharey@grandlyon.com> Date: Wed, 8 Mar 2023 10:04:49 +0000 Subject: [PATCH] chore(release): removed Meilisearch --- .env.template | 1 - .gitignore | 1 - .vscode/settings.json | 3 +- README.md | 83 +------------------- docker-compose.local.yml | 18 ----- docker-compose.yml | 4 +- package.json | 6 +- renovate.json | 3 +- src/components/Consents/Consents.tsx | 74 +++++++---------- src/components/Consents/agGridOverrides.scss | 8 +- src/components/Editing/CustomEditor.tsx | 55 +++++++------ src/components/Editing/CustomLink.tsx | 2 +- src/components/Editing/customEditor.scss | 14 +++- src/components/ImagePicker/ImagePicker.tsx | 4 +- src/services/consent.service.ts | 33 +------- src/utils/editorStateManagment.ts | 10 +-- yarn.lock | 28 +++++-- 17 files changed, 118 insertions(+), 229 deletions(-) diff --git a/.env.template b/.env.template index ea19ce6a..5800894c 100644 --- a/.env.template +++ b/.env.template @@ -26,4 +26,3 @@ DATABASE_PASSWORD= DATABASE_NAME= SGE_API_TOKEN= -MEILI_MASTER_KEY= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 386cb382..045845be 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ .env.development.local .env.test.local .env.production.local -meili_data db_data npm-debug.log* diff --git a/.vscode/settings.json b/.vscode/settings.json index 1adc76b2..a434aa05 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,7 +16,8 @@ "activityBar.activeBackground": "#37cc4e", "sash.hoverBorder": "#37cc4e", "statusBarItem.remoteBackground": "#2aa63d", - "statusBarItem.remoteForeground": "#e7e7e7" + "statusBarItem.remoteForeground": "#e7e7e7", + "commandCenter.border": "#e7e7e799" }, "editor.formatOnSave": true, "eslint.format.enable": true, diff --git a/README.md b/README.md index c587f93a..a2557edd 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,3 @@ -# Client - LLLE +# Ecolyo Agent Client -Projet client du backoffice LLLE - -## Install - -In order to install the project, make sure you have installed the followings : - -- NodeJS -- Yarn -- Docker / Docker Compose -- VSCode Editor (or the one you prefer), with at least ESLint and Prettier extensions - -You can then clone the app repository and install dependencies: - -```sh -$ git clone https://forge.grandlyon.com/web-et-numerique/llle_project/backoffice-client.git -$ cd backoffice-client -yarn -``` - -## Local usage - -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 - -```bash -yarn local-up -``` - -This command will launch the app from the _docker-compose.local.yml_ file, which will launch 3 docker images and start the react app with _yarn start_ - -- The backend Go app, from the image located on its GitLab repository -- The mongo Database, from the DockerHub image -- The Ngnix server with a local configuration located in nginx/site.conf - -To stop it properly use - -```bash -yarn local-down -``` - -This app runs in https, such as the backend, to access it go on https://localhost/ -Also make sure you have HTTPS env variable set to true. -Do not use the app with the port 3000, you won't be able to login. - -> :warning: **If you are launching the project from a Linux OS** : Make sure to uncomment the commented lines in the docker-compose.local file. Otherwise, you you won't be able to access your machine's localhost from the docker container. - -## Backend and Database - -In order to get Backend documentation, please refer directly to the [backend project](https://forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server) - -## Nginx - -The nginx server redirects https requests from frontend to the backend on port 1443. - -It has two configuration files : - -- nginx/site.conf, used for local development -- nginx/site.prod.conf, used for production - -The nginx server needs local ssl certificates in order to run an interact with backend, these certificates are present in the project. - -## CI/CD - -This app has a gitlab CI/CD pipeline you can find in the _.gitlab-ci.yml_ file. -On the dev branch and on merge requests, the pipeline will run a build stage and a quality stage, which is a Sonarqube quality check. -On dev and master branches, the pipeline will run a docker image build that you can find in the GitLab container registry. - -### Editor - -This app uses Draft.js Wysiwyg editor for more information checkout its (documentation page)[https://draftjs.org/docs/getting-started] -For more advanced documentation about editor customization, please checkout the (react draft wysiwig page)[https://jpuri.github.io/react-draft-wysiwyg/#/docs?_k=jjqinp] - -### Environment Variables - -There is a template.env file in this project, however you can ask one the developpers to get a ready-to-use .env file. - -- IMAGE_FOLDER: this variable specify the folder where ecogestures images are located. By default, you can place this folder at the same level as your project for a local usage. (not inside the project). For alpha, rec/prod environment, make sure the path indicated in docker-compose volumes correspond with the location of the image file on the server. - -- MOCK_OAUTH2: Set this variable to true while in local, so the OAuth2 dance will be mocked by backend and you will be able to login to your application. +[check documentation here](https://doc.self-data.alpha.grandlyon.com/ecolyo-agent/technical/getting_started/#local-usage) diff --git a/docker-compose.local.yml b/docker-compose.local.yml index b8fef645..1a357781 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -30,20 +30,6 @@ services: timeout: 10s 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: - - ./meili_data:/meili_data - ports: - - 7700:7700 - environment: - - MEILI_MASTER_KEY=${MEILI_MASTER_KEY} - phpmyadmin: image: phpmyadmin/phpmyadmin:latest depends_on: @@ -59,8 +45,6 @@ services: depends_on: database-agent: condition: service_healthy - meilisearch: - condition: service_healthy restart: unless-stopped volumes: - ./mnt:/app/mnt @@ -85,5 +69,3 @@ services: - MOCK_OAUTH2=${MOCK_OAUTH2} - IMAGE_FOLDER=${IMAGE_FOLDER} - SGE_API_TOKEN=${SGE_API_TOKEN} - - MEILI_HOST=http://meilisearch:7700 - - MEILI_MASTER_KEY=${MEILI_MASTER_KEY} diff --git a/docker-compose.yml b/docker-compose.yml index ca641669..1f99f5a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.7' services: nginx: - image: registry.forge.grandlyon.com/web-et-numerique/llle_project/backoffice-client:dev + image: registry.forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-client:dev restart: unless-stopped ports: - 8088:8080 @@ -33,7 +33,7 @@ services: - ./dbinit:/dbinit backend: - image: registry.forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server:dev + image: registry.forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server:dev networks: backoffice: depends_on: diff --git a/package.json b/package.json index 973841da..80930299 100644 --- a/package.json +++ b/package.json @@ -52,20 +52,22 @@ "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@types/draft-js": "^0.11.4", + "@types/draftjs-to-html": "^0.8.1", "@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.4", "ag-grid-community": "^27.1.0", "ag-grid-react": "^27.1.0", "axios": "^0.21.1", "dayjs": "^1.10.7", "draft-js": "^0.11.7", "draft-js-export-html": "^1.4.1", + "draftjs-to-html": "^0.9.1", "html-to-draftjs": "^1.5.0", "luxon": "^3.0.1", "react": "^17.0.2", "react-dom": "^17.0.2", - "react-draft-wysiwyg": "^1.14.7", + "react-draft-wysiwyg": "^1.15.0", "react-router-dom": "^5.2.0", "react-scripts": "^5.0.1", "react-toastify": "^7.0.4", diff --git a/renovate.json b/renovate.json index 6e459e2d..b112c41f 100644 --- a/renovate.json +++ b/renovate.json @@ -2,5 +2,6 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "local>systemes-dinformation/renovate/renovate-config" - ] + ], + "labels": ["dependencies"] } diff --git a/src/components/Consents/Consents.tsx b/src/components/Consents/Consents.tsx index 912d35d5..044bd815 100644 --- a/src/components/Consents/Consents.tsx +++ b/src/components/Consents/Consents.tsx @@ -153,32 +153,26 @@ const Consents: React.FC = () => { } }, [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() - } + const searchConsents = async () => { + if (user) { + const consentPagination = await consentService.searchConsents( + search, + rowsPerPage, + page, + getAxiosXSRFHeader(user.xsrftoken) + ) + if (consentPagination) { + setConsents(consentPagination.rows) + checkSelectedNodes() + setTotalRows(consentPagination.totalRows) } - }, - [user, consentService, rowsPerPage, page, checkSelectedNodes] - ) + } + } + + const handleSearchChange = (newSearch: string) => { + setSearch(newSearch) + setPage(0) + } const resetSelection = useCallback(() => { if (gridApi) { @@ -259,25 +253,12 @@ const Consents: React.FC = () => { } }, [gridApi]) + /** Trigger search when page loads or when admin changes input or pagination */ 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 + searchConsents() + // /!\ Do not change dependencies or effect will not trigger when pagination changes // eslint-disable-next-line react-hooks/exhaustive-deps - }, [user, consentService, rowsPerPage, page]) + }, [rowsPerPage, page, search]) return ( <> @@ -291,8 +272,8 @@ const Consents: React.FC = () => { <input value={search} name="search" - type="text" - placeholder="N°PDL, Nom, Prénom..." + type="number" + placeholder="N°PDL (14 chiffres)" onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleSearchChange(e.target.value) } @@ -319,8 +300,11 @@ const Consents: React.FC = () => { rowMultiSelectWithClick={true} pagination={false} suppressCellFocus={true} + rowClassRules={{ + expired: (params) => params.data.endDate < DateTime.now(), + }} ></AgGridReact> - {search === '' && !isShowingSelection && ( + {!isShowingSelection && ( <TablePagination labelRowsPerPage="Consentements par page" component="div" diff --git a/src/components/Consents/agGridOverrides.scss b/src/components/Consents/agGridOverrides.scss index 94e9eae6..b4964697 100644 --- a/src/components/Consents/agGridOverrides.scss +++ b/src/components/Consents/agGridOverrides.scss @@ -10,10 +10,14 @@ .ag-header-cell-text { color: $gold; } -.ag-row-odd, -.ag-row-even { +.ag-row { background: transparent !important; transition: all 300ms ease !important; + &.expired { + * { + color: $text-chart; + } + } } .ag-cell-focus { outline: none !important; diff --git a/src/components/Editing/CustomEditor.tsx b/src/components/Editing/CustomEditor.tsx index 3fae4902..960aa784 100644 --- a/src/components/Editing/CustomEditor.tsx +++ b/src/components/Editing/CustomEditor.tsx @@ -1,5 +1,6 @@ -import { stateToHTML } from 'draft-js-export-html' -import React, { useCallback, useMemo, useState } from 'react' +import { convertToRaw } from 'draft-js' +import draftToHtml from 'draftjs-to-html' +import React, { useCallback, useState } from 'react' import { Editor, EditorState } from 'react-draft-wysiwyg' import './customEditor.scss' import CustomLink from './CustomLink' @@ -20,36 +21,38 @@ const CustomEditor: React.FC<CustomEditorProps> = ({ }: CustomEditorProps) => { const [editorState, setEditorState] = useState<EditorState>(baseState) - const entityStyleFn = useMemo( - () => (entity: any) => { - const entityType = entity.get('type').toLowerCase() - if (entityType === 'link') { - const data = entity.getData() - return { - element: 'a', - attributes: { - title: data.title, - href: data.href ? data.href : data.url, - }, - } + const convertStateToHTML = (state: EditorState) => { + const parseElements = ({ type, data }: { type: string; data: any }) => { + // properly align images (see: https://github.com/jpuri/draftjs-to-html/issues/28#issuecomment-607344551) + if (type === 'IMAGE') { + const alignment = data.alignment || 'none' + const textAlign = alignment === 'none' ? 'center' : alignment + const alt = data.alt ? data.alt : '' + return `<p style="text-align:${textAlign};"><img src="${data.src}" alt="${alt}" style="height: ${data.height};width: ${data.width}"/></p>` } - }, - [] - ) + if (type === 'LINK') { + return `<a href=${data.href ? data.href : data.url} title=${ + data.title + }>${data.title}</a>` + } + } + return state.getCurrentContent().hasText() + ? draftToHtml( + convertToRaw(state.getCurrentContent()), + {}, + false, + parseElements + ) + : '' + } const handleStateChange = useCallback( (state: EditorState) => { setEditorState(state) - if (state.getCurrentContent().hasText()) { - handleChange( - stateToHTML(state.getCurrentContent(), { entityStyleFn }), - editorType - ) - } else { - handleChange('', editorType) - } + const htmlState = convertStateToHTML(state) + handleChange(htmlState, editorType) }, - [editorType, handleChange, entityStyleFn] + [editorType, handleChange] ) return ( diff --git a/src/components/Editing/CustomLink.tsx b/src/components/Editing/CustomLink.tsx index 46bb1d97..62ec611a 100644 --- a/src/components/Editing/CustomLink.tsx +++ b/src/components/Editing/CustomLink.tsx @@ -22,7 +22,7 @@ const CustomLink: React.FC<EcolyoLinkProps> = ({ link: 'consumption', }, { - displayTitle: 'Écogestes', + displayTitle: 'Astuces', link: 'ecogestures', }, { diff --git a/src/components/Editing/customEditor.scss b/src/components/Editing/customEditor.scss index 133604b2..74854419 100644 --- a/src/components/Editing/customEditor.scss +++ b/src/components/Editing/customEditor.scss @@ -2,15 +2,25 @@ .toolbar-class { span, - li { + li, + input, + button, + div { color: black; + &:disabled { + opacity: 0.3; + } + &.rdw-image-mandatory-sign { + color: red; + } } } .editor-class { background: white; padding: 0rem 1rem; min-height: 100px; - span { + span, + div { color: black; } } diff --git a/src/components/ImagePicker/ImagePicker.tsx b/src/components/ImagePicker/ImagePicker.tsx index cce9b48a..705b2dcd 100644 --- a/src/components/ImagePicker/ImagePicker.tsx +++ b/src/components/ImagePicker/ImagePicker.tsx @@ -102,8 +102,8 @@ const ImagePicker: React.FC<ImagePickerProps> = ({ <> <div className="image-picker"> {imageNames && - imageNames !== [] && - imageNames[currentPage - 1] !== [] && + imageNames.length !== 0 && + imageNames[currentPage - 1].length !== 0 && imageNames[currentPage - 1].map((imageName) => ( <SingleImage imageURL={imageName} diff --git a/src/services/consent.service.ts b/src/services/consent.service.ts index 94b6c5ab..a474ca4c 100644 --- a/src/services/consent.service.ts +++ b/src/services/consent.service.ts @@ -11,46 +11,19 @@ export class ConsentService { /** * Search for consents * @param search - * @param axiosHeaders - */ - public searchConsent = async ( - search: string, - axiosHeaders: AxiosRequestConfig - ): Promise<IConsent[] | null> => { - try { - const { data } = await axios.get( - `/api/admin/consent?search=${search}`, - axiosHeaders - ) - const consentEntities = data as ConsentEntity[] - return consentEntities.map((entity) => this.parseConsent(entity)) - } catch (e: any) { - if (e.response.status === 403) { - toast.error( - "Unauthorized : You don't have the rights to do this operation" - ) - } else { - toast.error('Failed to search consents') - } - console.error(e) - return null - } - } - - /** - * Gets consents * @param limit * @param page * @param axiosHeaders */ - public getConsents = async ( + public searchConsents = async ( + search: string, limit: number, page: number, axiosHeaders: AxiosRequestConfig ): Promise<IConsentPagination | null> => { try { const { data } = await axios.get( - `/api/admin/consent?limit=${limit}&page=${page}`, + `/api/admin/consent?search=${search}&limit=${limit}&page=${page}`, axiosHeaders ) const consentPagination = data as ConsentPaginationEntity diff --git a/src/utils/editorStateManagment.ts b/src/utils/editorStateManagment.ts index dd3ff893..b37f30ff 100644 --- a/src/utils/editorStateManagment.ts +++ b/src/utils/editorStateManagment.ts @@ -1,10 +1,8 @@ -import { EditorState, convertFromHTML, ContentState } from 'draft-js' +import { EditorState, ContentState } from 'draft-js' +import htmlToDraft from 'html-to-draftjs' export const convertStringToEditorState = (string: string): EditorState => { - const blocksFromHTML = convertFromHTML(string) - const state = ContentState.createFromBlockArray( - blocksFromHTML.contentBlocks, - blocksFromHTML.entityMap - ) + const { contentBlocks, entityMap } = htmlToDraft(string) + const state = ContentState.createFromBlockArray(contentBlocks, entityMap) return EditorState.createWithContent(state) } diff --git a/yarn.lock b/yarn.lock index d10f9e63..ef8cbce0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2097,6 +2097,13 @@ "@types/react" "*" immutable "~3.7.4" +"@types/draftjs-to-html@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@types/draftjs-to-html/-/draftjs-to-html-0.8.1.tgz#0800a4f6ff19ece49b6a337bfa8b39ed3d08a5fa" + integrity sha512-NBkphQs+qZ/sAz/j1pCUaxkPAOx00LTsE88aMSSfcvK+UfCpjHJDqIMCkm6wKotuJvY5w0BtdRazQ0sAaXzPdg== + dependencies: + "@types/draft-js" "*" + "@types/eslint-scope@^3.7.3": version "3.7.4" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" @@ -2280,7 +2287,7 @@ dependencies: "@types/react" "^17" -"@types/react-draft-wysiwyg@^1.13.3": +"@types/react-draft-wysiwyg@^1.13.4": version "1.13.4" resolved "https://registry.yarnpkg.com/@types/react-draft-wysiwyg/-/react-draft-wysiwyg-1.13.4.tgz#df951c76afb47e311061d363f41a10c76de04ac8" integrity sha512-wasD1t78JDmQvdPDRPf/mf5FSHMlncunW0F6KMOKB3awzi3Wi21yHMGsRAUOkfTr3R8F+yceG8fSLz0kYWu/QA== @@ -3520,9 +3527,9 @@ cjs-module-lexer@^1.0.0: integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== classnames@^2.2.6: - version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" - integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + version "2.3.2" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== clean-css@^5.2.2: version "5.3.1" @@ -4304,6 +4311,11 @@ draft-js@^0.11.7: immutable "~3.7.4" object-assign "^4.1.1" +draftjs-to-html@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/draftjs-to-html/-/draftjs-to-html-0.9.1.tgz#1c870fbb588d2390204cb4d0ee7e04ad0c709969" + integrity sha512-fFstE6+IayaVFBEvaFt/wN8vdj8FsTRzij7dy7LI9QIwf5LgfHFi9zSpvCg+feJ2tbYVqHxUkjcibwpsTpgFVQ== + draftjs-utils@^0.10.2: version "0.10.2" resolved "https://registry.yarnpkg.com/draftjs-utils/-/draftjs-utils-0.10.2.tgz#a7f16d2c1c174ac38ba3bbf700c256f176b2699c" @@ -8158,7 +8170,7 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" -react-draft-wysiwyg@^1.14.7: +react-draft-wysiwyg@^1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/react-draft-wysiwyg/-/react-draft-wysiwyg-1.15.0.tgz#d5b4173991033859b9e161c883889ddc00909a57" integrity sha512-p1cYZcWc6/ALFBVksbFoCM3b29fGQDlZLIMrXng0TU/UElxIOF2/AWWo4L5auIYVhmqKTZ0NkNjnXOzGGuxyeA== @@ -9400,9 +9412,9 @@ typescript@^4.1.2, typescript@^4.5.4: integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== ua-parser-js@^0.7.18: - version "0.7.31" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" - integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== + version "0.7.33" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" + integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== uc.micro@^1.0.1: version "1.0.6" -- GitLab