From bd48c5d22a94c49537a7ad2f52b9d14c04b7af20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Demazi=C3=A8re?= Date: Wed, 4 Oct 2023 16:46:12 +0200 Subject: [PATCH] Fix/buggy address and open terminated su questionnaire (#83) * fix: provide questionnaireId in queen URL fix : handle buggy address * fix: handle missing departement for export * fix: unnecessary script option * fix: update test --- package.json | 2 +- src/components/ListSU/SUTable.jsx | 499 ++++++++++-------- src/components/ListSU/SurveyUnitLine.jsx | 30 +- src/components/Terminated/Terminated.test.jsx | 269 +++++----- src/components/Terminated/TerminatedTable.jsx | 200 ++++--- 5 files changed, 566 insertions(+), 434 deletions(-) diff --git a/package.json b/package.json index de7c47e..406ba23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sonor", - "version": "0.5.18", + "version": "0.5.19", "private": true, "dependencies": { "@testing-library/jest-dom": "^4.2.4", diff --git a/src/components/ListSU/SUTable.jsx b/src/components/ListSU/SUTable.jsx index 59493fe..fab19b2 100644 --- a/src/components/ListSU/SUTable.jsx +++ b/src/components/ListSU/SUTable.jsx @@ -1,37 +1,41 @@ -import React from 'react'; -import Button from 'react-bootstrap/Button'; -import { Col, Row } from 'react-bootstrap'; -import Table from 'react-bootstrap/Table'; -import Spinner from 'react-bootstrap/Spinner'; -import Card from 'react-bootstrap/Card'; -import Modal from 'react-bootstrap/Modal'; -import Form from 'react-bootstrap/Form'; -import SortIcon from '../SortIcon/SortIcon'; -import SearchField from '../SearchField/SearchField'; -import PaginationNav from '../PaginationNav/PaginationNav'; -import SurveyUnitLine from './SurveyUnitLine'; -import D from '../../i18n'; +import React from "react"; +import Button from "react-bootstrap/Button"; +import { Col, Row } from "react-bootstrap"; +import Table from "react-bootstrap/Table"; +import Spinner from "react-bootstrap/Spinner"; +import Card from "react-bootstrap/Card"; +import Modal from "react-bootstrap/Modal"; +import Form from "react-bootstrap/Form"; +import SortIcon from "../SortIcon/SortIcon"; +import SearchField from "../SearchField/SearchField"; +import PaginationNav from "../PaginationNav/PaginationNav"; +import SurveyUnitLine from "./SurveyUnitLine"; +import D from "../../i18n"; function makeTableForExport(data) { - const header = [[ - D.identifier, - D.interviewer, - D.idep, - D.ssech, - D.department, - D.town, - D.state, - ]]; + const header = [ + [ + D.identifier, + D.interviewer, + D.idep, + D.ssech, + D.department, + D.town, + D.state, + ], + ]; - return header.concat(data.map((line) => ([ - line.id, - line.interviewer, - line.idep, - line.ssech, - line.departement.substring(0, 2), - line.city, - line.state, - ]))); + return header.concat( + data.map((line) => [ + line.id, + line.interviewer, + line.idep, + line.ssech, + line.departement?.substring(0, 2), + line.city, + line.state, + ]) + ); } class SUTable extends React.Component { @@ -41,14 +45,18 @@ class SUTable extends React.Component { pagination: { size: 10, page: 1 }, displayedLines: props.data, checkboxArray: props.data.reduce((acc, curr) => { - if (curr.state !== 'CLO' && curr.state !== 'TBR' && curr.state !== 'FIN') { + if ( + curr.state !== "CLO" && + curr.state !== "TBR" && + curr.state !== "FIN" + ) { acc[curr.id] = false; } return acc; }, {}), checkAll: false, show: false, - stateModified: '', + stateModified: "", }; } @@ -57,7 +65,11 @@ class SUTable extends React.Component { if (prevProps.data !== data) { this.setState({ displayedLines: data }); const newCheckboxArray = data.reduce((acc, curr) => { - if (curr.state !== 'CLO' && curr.state !== 'TBR' && curr.state !== 'FIN') { + if ( + curr.state !== "CLO" && + curr.state !== "TBR" && + curr.state !== "FIN" + ) { acc[curr.id] = false; } return acc; @@ -72,9 +84,10 @@ class SUTable extends React.Component { handleCheckAll(e) { const { checkboxArray } = this.state; - const newCheckboxArray = Object.keys(checkboxArray).reduce( - (acc, curr) => { acc[curr] = e.target.checked; return acc; }, {}, - ); + const newCheckboxArray = Object.keys(checkboxArray).reduce((acc, curr) => { + acc[curr] = e.target.checked; + return acc; + }, {}); this.setState({ checkboxArray: newCheckboxArray, checkAll: e.target.checked, @@ -108,21 +121,25 @@ class SUTable extends React.Component { const { validateChangingState } = this.props; const { checkboxArray, stateModified } = this.state; const lstSUChangingState = Object.entries(checkboxArray) - .filter((su) => (su[1])) - .map((su) => (su[0])); + .filter((su) => su[1]) + .map((su) => su[0]); validateChangingState(lstSUChangingState, stateModified); } handleExport() { const { data, survey, site } = this.props; const fileLabel = `${site}_${survey.label}_UE_confiees`; - const title = `${fileLabel}_${new Date().toLocaleDateString().replace(/\//g, '')}.csv`.replace(/ /g, '_'); + const title = `${fileLabel}_${new Date() + .toLocaleDateString() + .replace(/\//g, "")}.csv`.replace(/ /g, "_"); const table = makeTableForExport(data); - const csvContent = `data:text/csv;charset=utf-8,\ufeff${table.map((e) => e.join(';')).join('\n')}`; + const csvContent = `data:text/csv;charset=utf-8,\ufeff${table + .map((e) => e.join(";")) + .join("\n")}`; const encodedUri = encodeURI(csvContent); - const link = document.createElement('a'); - link.setAttribute('href', encodedUri); - link.setAttribute('download', title); + const link = document.createElement("a"); + link.setAttribute("href", encodedUri); + link.setAttribute("download", title); document.body.appendChild(link); link.click(); link.remove(); @@ -130,9 +147,10 @@ class SUTable extends React.Component { updateLines(matchingLines) { const { pagination, checkboxArray } = this.state; - const newCheckboxArray = Object.keys(checkboxArray).reduce( - (acc, curr) => { acc[curr] = false; return acc; }, {}, - ); + const newCheckboxArray = Object.keys(checkboxArray).reduce((acc, curr) => { + acc[curr] = false; + return acc; + }, {}); this.setState({ checkboxArray: newCheckboxArray, checkAll: false, @@ -142,184 +160,229 @@ class SUTable extends React.Component { } render() { + const { data, sort, handleSort, isLoading } = this.props; + const fieldsToSearch = ["city", "interviewer", "id", "state"]; const { - data, sort, handleSort, isLoading, - } = this.props; - const fieldsToSearch = ['city', 'interviewer', 'id', 'state']; - const { - pagination, displayedLines, checkboxArray, checkAll, show, stateModified, + pagination, + displayedLines, + checkboxArray, + checkAll, + show, + stateModified, } = this.state; - const toggleCheckBox = (i) => { this.toggleCheckBox(i); }; - function handleSortFunct(property) { return () => { handleSort(property); }; } + const toggleCheckBox = (i) => { + this.toggleCheckBox(i); + }; + function handleSortFunct(property) { + return () => { + handleSort(property); + }; + } return ( {D.surveyUnitsAllocatedToTheOU} - {isLoading ? '' : data.length} - {!data.length || isLoading - || ( - - )} + {isLoading ? "" : data.length} + {!data.length || isLoading || ( + + )} - { - isLoading - ? - : ( - <> - { - data.length > 0 - ? ( -
- - - this.updateLines(matchinglines)} - /> - - - this.handlePageChange(newPagination)} - /> - - - - {D.result} - {displayedLines.length} - / - {data.length} -   - {D.units} - - - - - - - - - - - - - - - - - {displayedLines - .slice( - (pagination.page - 1) * pagination.size, - Math.min(pagination.page * pagination.size, displayedLines.length), - ) - .map((line) => ( - toggleCheckBox(line.id)} - /> - ))} - -
- this.handleCheckAll(e)} /> - - {D.identifier} - - - {D.interviewer} - - - {D.ssech} - - - {D.department} - - - {D.town} - - - {D.state} - -
-
- { - this.handlePageChange(newPagination); - }} - numberOfItems={displayedLines.length} - /> -
- - this.handleClose()}> - - {D.modaleModifiedText} - - - - {D.state} - this.setState({ stateModified: e.target.value })} - > - - - - - - - - - - - - - -
- ) - : {D.noListSuToDisplay} - } - - ) - } + {isLoading ? ( + + ) : ( + <> + {data.length > 0 ? ( +
+ + + + this.updateLines(matchinglines) + } + /> + + + + this.handlePageChange(newPagination) + } + /> + + + + {D.result} + {displayedLines.length}/{data.length} +   + {D.units} + + + + + + + + + + + + + + + + + {displayedLines + .slice( + (pagination.page - 1) * pagination.size, + Math.min( + pagination.page * pagination.size, + displayedLines.length + ) + ) + .map((line) => ( + toggleCheckBox(line.id)} + /> + ))} + +
+ this.handleCheckAll(e)} + /> + + {D.identifier} + + + {D.interviewer} + + + {D.ssech} + + + {D.department} + + + {D.town} + + + {D.state} + +
+
+ { + this.handlePageChange(newPagination); + }} + numberOfItems={displayedLines.length} + /> +
+ + this.handleClose()}> + + {D.modaleModifiedText} + + + + {D.state} + + this.setState({ stateModified: e.target.value }) + } + > + + + + + + + + + + + + + +
+ ) : ( + {D.noListSuToDisplay} + )} + + )}
); } diff --git a/src/components/ListSU/SurveyUnitLine.jsx b/src/components/ListSU/SurveyUnitLine.jsx index 77e13c8..11f7bd6 100644 --- a/src/components/ListSU/SurveyUnitLine.jsx +++ b/src/components/ListSU/SurveyUnitLine.jsx @@ -1,25 +1,31 @@ -import React from 'react'; -import D from '../../i18n'; +import React from "react"; +import D from "../../i18n"; function SurveyUnitLine({ lineData, isChecked, updateFunc }) { - const { - id, ssech, departement, city, interviewer, state, closingCause, - } = lineData; + const { id, ssech, departement, city, interviewer, state, closingCause } = + lineData; return ( - { - state !== 'CLO' && state !== 'TBR' && state !== 'FIN' ? ( - updateFunc()} /> - ) : - } + {state !== "CLO" && state !== "TBR" && state !== "FIN" ? ( + updateFunc()} + /> + ) : ( + + )} {id} {interviewer} {ssech} - {departement.substring(0, 2)} + {departement?.substring(0, 2) ?? ""} {city} - {closingCause ? D[closingCause] : ''} + {closingCause ? D[closingCause] : ""} ); } diff --git a/src/components/Terminated/Terminated.test.jsx b/src/components/Terminated/Terminated.test.jsx index 2d4f9d9..a43d30f 100644 --- a/src/components/Terminated/Terminated.test.jsx +++ b/src/components/Terminated/Terminated.test.jsx @@ -1,41 +1,46 @@ // Link.react.test.js -import React from 'react'; -import { - render, screen, fireEvent, cleanup, -} from '@testing-library/react'; -import { Router, Route, Switch } from 'react-router-dom'; -import { createMemoryHistory } from 'history'; -import DataFormatter from '../../utils/DataFormatter'; -import Terminated from './Terminated'; -import mocks from '../../tests/mocks'; +import React from "react"; +import { render, screen, fireEvent, cleanup } from "@testing-library/react"; +import { Router, Route, Switch } from "react-router-dom"; +import { createMemoryHistory } from "history"; +import DataFormatter from "../../utils/DataFormatter"; +import Terminated from "./Terminated"; +import mocks from "../../tests/mocks"; const history = createMemoryHistory(); const toLocaleDateString = Date.prototype.toLocaleString; const getHours = Date.prototype.getUTCHours; const getMinutes = Date.prototype.getUTCMinutes; -Date.prototype.toLocaleDateString = function() { - return toLocaleDateString.call(this, 'en-EN', { timeZone: 'UTC',year: "numeric", month: "numeric", day: "numeric" }); +Date.prototype.toLocaleDateString = function () { + return toLocaleDateString.call(this, "en-EN", { + timeZone: "UTC", + year: "numeric", + month: "numeric", + day: "numeric", + }); }; -Date.prototype.getHours = function() { +Date.prototype.getHours = function () { return getHours.call(this); }; -Date.prototype.getMinutes = function() { +Date.prototype.getMinutes = function () { return getMinutes.call(this); }; const OriginalDate = global.Date; jest - .spyOn(global, 'Date') - .mockImplementation((a) => (a ? new OriginalDate(a) : new OriginalDate('2020-08-20T11:01:58.135Z'))); + .spyOn(global, "Date") + .mockImplementation((a) => + a ? new OriginalDate(a) : new OriginalDate("2020-08-20T11:01:58.135Z") + ); Date.now = jest.fn(() => 1597916474000); beforeEach(() => { - history.push('/terminated/vqs2021x00'); + history.push("/terminated/vqs2021x00"); }); afterEach(cleanup); -jest.mock('../../utils/DataFormatter'); +jest.mock("../../utils/DataFormatter"); const survey = mocks.surveyVqs; const resp = mocks.suTerminated; const { stateHistory } = mocks; @@ -43,16 +48,21 @@ const { stateHistory } = mocks; const TestingRouter = ({ ComponentWithRedirection }) => ( - } /> + } + /> (
-
{JSON.stringify(routeProps.history.location.pathname)}
+
+ {JSON.stringify(routeProps.history.location.pathname)} +
- {!routeProps.history.location - || !routeProps.history.location.survey - || JSON.stringify(routeProps.history.location.survey)} + {!routeProps.history.location || + !routeProps.history.location.survey || + JSON.stringify(routeProps.history.location.survey)}
)} @@ -62,186 +72,191 @@ const TestingRouter = ({ ComponentWithRedirection }) => ( ); DataFormatter.mockImplementation(() => ({ - getListSuTerminated: (id, cb) => (cb(resp)), - getStatesSurvey: (id, cb) => (cb(stateHistory)), + getListSuTerminated: (id, cb) => cb(resp), + getStatesSurvey: (id, cb) => cb(stateHistory), + getQuestionnaireModelIdForReviewLink: (ids, cb) => cb("QUESTIONNAIRE"), })); const mockDataRetreiver = new DataFormatter(); -it('Component is correctly displayed', async () => { +it("Component is correctly displayed", async () => { const component = render( ( - - ) - } - />, + ComponentWithRedirection={(props) => ( + + )} + /> ); // Should match snapshot (rows displayed) expect(component).toMatchSnapshot(); }); -it('Sort by su id', async () => { +it("Sort by su id", async () => { const component = render( ( - - ) - } - />, + ComponentWithRedirection={(props) => ( + + )} + /> ); - screen.getByTestId('TableHeader_id_terminated').click(); + screen.getByTestId("TableHeader_id_terminated").click(); // Should match snapshot (rows sorted by id) expect(component).toMatchSnapshot(); }); -it('Change page', async () => { +it("Change page", async () => { const component = render( ( - - ) - } - />, + ComponentWithRedirection={(props) => ( + + )} + /> ); - screen.getByTestId('pagination-nav').lastChild.firstChild.click(); + screen.getByTestId("pagination-nav").lastChild.firstChild.click(); // Should match snapshot (rows displayed have changed) expect(component).toMatchSnapshot(); }); -it('Change pagination size', async () => { +it("Change pagination size", async () => { const component = render( ( - - ) - } - />, + ComponentWithRedirection={(props) => ( + + )} + /> ); - fireEvent.change(component.getByTestId('pagination-size-selector'), { target: { value: '10' } }); + fireEvent.change(component.getByTestId("pagination-size-selector"), { + target: { value: "10" }, + }); // Should match snapshot (all rows are now displayed) expect(component).toMatchSnapshot(); }); -it('Select another survey', async () => { +it("Select another survey", async () => { const component = render( ( - - ) - } - />, + ComponentWithRedirection={(props) => ( + + )} + /> ); - const redirectUrl = '/terminated/simpsons2020x00'; + const redirectUrl = "/terminated/simpsons2020x00"; - fireEvent.change(component.getByTestId('Survey_selector'), { target: { value: 'simpsons2020x00' } }); + fireEvent.change(component.getByTestId("Survey_selector"), { + target: { value: "simpsons2020x00" }, + }); // Should redirect to '/terminated/simpsons2020x00' - expect(screen.getByTestId('Redirect-url').innerHTML).toEqual(`\"${redirectUrl}\"`); + expect(screen.getByTestId("Redirect-url").innerHTML).toEqual( + `\"${redirectUrl}\"` + ); // Location should contain survey object - expect(screen.getByTestId('Redirect-survey').innerHTML).not.toEqual(''); - expect(screen.getByTestId('Redirect-survey')).toMatchSnapshot(); + expect(screen.getByTestId("Redirect-survey").innerHTML).not.toEqual(""); + expect(screen.getByTestId("Redirect-survey")).toMatchSnapshot(); }); -it('Reloading the page with no survey set (F5)', async () => { - const redirectUrl = '/'; +it("Reloading the page with no survey set (F5)", async () => { + const redirectUrl = "/"; render( ( - - ) - } - />, + ComponentWithRedirection={(props) => ( + + )} + /> ); // Should redirect to '/' - expect(screen.getByTestId('Redirect-url').innerHTML).toEqual(`\"${redirectUrl}\"`); + expect(screen.getByTestId("Redirect-url").innerHTML).toEqual( + `\"${redirectUrl}\"` + ); }); -it('Display and hide history', async () => { +it("Display and hide history", async () => { const component = render( ( - - ) - } - />, + ComponentWithRedirection={(props) => ( + + )} + /> ); - component.baseElement.querySelector('tbody').querySelectorAll('tr')[0].querySelector('.HistoryDisplayIcon').click(); + component.baseElement + .querySelector("tbody") + .querySelectorAll("tr")[0] + .querySelector(".HistoryDisplayIcon") + .click(); // State history table should be displayed - expect(component.baseElement.querySelector('#StateHistoryTableContainer')).toBeTruthy(); + expect( + component.baseElement.querySelector("#StateHistoryTableContainer") + ).toBeTruthy(); // Component should match snapshot expect(component).toMatchSnapshot(); - screen.getByTestId('close-history').click(); + screen.getByTestId("close-history").click(); // State history should not be displayed anymore - expect(component.baseElement.querySelector('#StateHistoryTableContainer')).not.toBeTruthy(); + expect( + component.baseElement.querySelector("#StateHistoryTableContainer") + ).not.toBeTruthy(); }); -it('Click on edit', async () => { +it("Click on edit", async () => { const component = render( ( - - ) - } - />, + ComponentWithRedirection={(props) => ( + + )} + /> ); window.open = jest.fn(); - component.baseElement.querySelector('tbody').querySelectorAll('tr')[0].querySelector('.EditLink').click(); + component.baseElement + .querySelector("tbody") + .querySelectorAll("tr")[0] + .querySelector(".EditLink") + .click(); // window.open should have been called expect(window.open).toHaveBeenCalled(); diff --git a/src/components/Terminated/TerminatedTable.jsx b/src/components/Terminated/TerminatedTable.jsx index f815d6f..93aa278 100644 --- a/src/components/Terminated/TerminatedTable.jsx +++ b/src/components/Terminated/TerminatedTable.jsx @@ -1,19 +1,19 @@ -import React from 'react'; -import Table from 'react-bootstrap/Table'; -import Row from 'react-bootstrap/Row'; -import Col from 'react-bootstrap/Col'; -import Modal from 'react-bootstrap/Modal'; -import Form from 'react-bootstrap/Form'; -import Button from 'react-bootstrap/Button'; -import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; -import Tooltip from 'react-bootstrap/Tooltip'; -import displayStateHistoryTable from './DisplayStateHistoryTable'; -import PaginationNav from '../PaginationNav/PaginationNav'; -import SearchField from '../SearchField/SearchField'; -import SortIcon from '../SortIcon/SortIcon'; -import Utils from '../../utils/Utils'; -import D from '../../i18n'; -import './Terminated.css'; +import React from "react"; +import Table from "react-bootstrap/Table"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import Modal from "react-bootstrap/Modal"; +import Form from "react-bootstrap/Form"; +import Button from "react-bootstrap/Button"; +import OverlayTrigger from "react-bootstrap/OverlayTrigger"; +import Tooltip from "react-bootstrap/Tooltip"; +import displayStateHistoryTable from "./DisplayStateHistoryTable"; +import PaginationNav from "../PaginationNav/PaginationNav"; +import SearchField from "../SearchField/SearchField"; +import SortIcon from "../SortIcon/SortIcon"; +import Utils from "../../utils/Utils"; +import D from "../../i18n"; +import "./Terminated.css"; class TerminatedTable extends React.Component { constructor(props) { @@ -23,21 +23,21 @@ class TerminatedTable extends React.Component { displayData: props.data, toggleStateHistory: false, stateData: [], - stateId: '', + stateId: "", showComment: false, - suToModifySelected: '', - oldComment: '', - newComment: '', - + suToModifySelected: "", + oldComment: "", + newComment: "", }; - this.queenUrl = `${window.localStorage.getItem('QUEEN_URL_FRONT_END')}`; + this.queenUrl = `${window.localStorage.getItem("QUEEN_URL_FRONT_END")}`; } getMaxWidth() { return [ - document.getElementById('stateHistoryDate').getBoundingClientRect().width, - document.getElementById('stateHistoryHour').getBoundingClientRect().width, - document.getElementById('stateHistoryState').getBoundingClientRect().width, + document.getElementById("stateHistoryDate").getBoundingClientRect().width, + document.getElementById("stateHistoryHour").getBoundingClientRect().width, + document.getElementById("stateHistoryState").getBoundingClientRect() + .width, ]; } @@ -64,14 +64,14 @@ class TerminatedTable extends React.Component { handleShowComment(line) { this.setState({ showComment: true, suToModifySelected: line.id }); if (line.comments != null) { - let comToSet = ''; - const comment = line.comments.find((c) => c.type === 'MANAGEMENT'); + let comToSet = ""; + const comment = line.comments.find((c) => c.type === "MANAGEMENT"); if (comment) { comToSet = comment.value; } this.setState({ oldComment: comToSet }); } else { - this.setState({ oldComment: '' }); + this.setState({ oldComment: "" }); } } @@ -94,36 +94,44 @@ class TerminatedTable extends React.Component { } surveyListLine(data, survey, handleShow) { + const { dataRetreiver } = this.props; return ( {survey.label} - {data.id} + + {data.id} + {`${data.interviewer.interviewerLastName} ${data.interviewer.interviewerFirstName}`} - {`${Utils.convertToDateString(data.finalizationDate)}`} + {`${Utils.convertToDateString( + data.finalizationDate + )}`} {data.reading ? D.yes : D.no} - {D.questionnaire} - - )} + overlay={{D.questionnaire}} >