From 87c619accda528b2da733dba25093f47a11e06b6 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Thu, 14 May 2020 09:40:20 +0200 Subject: [PATCH 01/32] Create data validations and implement it --- .../project/components/data.component.tsx | 8 ++++++- .../project/components/data.validations.ts | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/pods/project/components/data.validations.ts diff --git a/src/pods/project/components/data.component.tsx b/src/pods/project/components/data.component.tsx index df68d1c..f09dadb 100644 --- a/src/pods/project/components/data.component.tsx +++ b/src/pods/project/components/data.component.tsx @@ -3,6 +3,7 @@ import { Formik, Form } from 'formik'; import { TextFieldComponent, CheckboxComponent } from 'common/components'; import { CommandFooterComponent } from '../../../common-app/command-footer'; import { Project } from '../project.vm'; +import { formValidation } from './data.validations'; interface Props { project: Project; @@ -18,7 +19,12 @@ export const DataComponent: React.FunctionComponent = ({ className, }) => { return ( - + {() => (
Date: Thu, 14 May 2020 10:10:36 +0200 Subject: [PATCH 02/32] Create report validation to project and implement it --- .../project/components/report.component.tsx | 15 ++++++++++--- .../project/components/report.validations.ts | 21 +++++++++++++++++++ src/pods/project/project.component.tsx | 11 +++++++++- src/pods/project/project.container.tsx | 15 ++++++++++++- src/pods/project/project.vm.ts | 10 +++++++++ 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 src/pods/project/components/report.validations.ts diff --git a/src/pods/project/components/report.component.tsx b/src/pods/project/components/report.component.tsx index 263945b..71415cb 100644 --- a/src/pods/project/components/report.component.tsx +++ b/src/pods/project/components/report.component.tsx @@ -5,25 +5,35 @@ import { monthList } from 'common/constants'; import { CommandFooterComponent } from 'common-app/command-footer'; import { cx } from 'emotion'; import * as classes from './report.styles'; +import { formValidation } from './report.validations'; +import { Report } from '../project.vm'; interface Props { + report: Report; onCancel: () => void; className: string; + onGenerateExcel: (report: Report) => void; } export const ReportComponent: React.FunctionComponent = ({ + report, + onGenerateExcel, onCancel, className, }) => { return ( - + {() => ( = ({ name: '2020', }, ]} - disabled className={classes.year} /> void; onSave: (project: Project) => void; onCancel: () => void; } @@ -23,6 +25,8 @@ interface Props { export const ProjectComponent: React.FunctionComponent = ({ isEditMode, project, + report, + onGenerateExcel, onSave, onCancel, }) => { @@ -52,7 +56,12 @@ export const ProjectComponent: React.FunctionComponent = ({ /> - + ); diff --git a/src/pods/project/project.container.tsx b/src/pods/project/project.container.tsx index 18d9ee7..4a5f417 100644 --- a/src/pods/project/project.container.tsx +++ b/src/pods/project/project.container.tsx @@ -5,13 +5,19 @@ import { useSnackbarContext } from 'common/components'; import { getProjectById } from './api'; import { trackPromise } from 'react-promise-tracker'; import { mapProjectFromApiToVm } from './project.mapper'; -import { Project, createEmptyProject } from './project.vm'; +import { + Project, + createEmptyProject, + Report, + createEmptyReport, +} from './project.vm'; import { isEditModeHelper } from 'common/helpers'; export const ProjectContainer: React.FunctionComponent = () => { const { id } = useParams(); const [project, setProject] = React.useState(createEmptyProject()); const [isEditMode, setIsEditMode] = React.useState(false); + const [report, setReport] = React.useState(createEmptyReport()); const { showMessage } = useSnackbarContext(); const onLoadProject = async () => { @@ -33,6 +39,11 @@ export const ProjectContainer: React.FunctionComponent = () => { history.back(); }; + const handleGenerateExcel = (report: Report) => { + // Pending to create real implementation + console.log('Excel creado'); + }; + React.useEffect(() => { const isEditMode = isEditModeHelper(id); setIsEditMode(isEditMode); @@ -45,8 +56,10 @@ export const ProjectContainer: React.FunctionComponent = () => { ); }; diff --git a/src/pods/project/project.vm.ts b/src/pods/project/project.vm.ts index 1ffb7ef..6cfc458 100644 --- a/src/pods/project/project.vm.ts +++ b/src/pods/project/project.vm.ts @@ -13,6 +13,11 @@ export interface EmployeeSummary { employeeName: string; } +export interface Report { + month: string; + year: string; +} + export const createEmptyProject = (): Project => ({ id: '', name: '', @@ -21,3 +26,8 @@ export const createEmptyProject = (): Project => ({ isActive: false, employees: [], }); + +export const createEmptyReport = (): Report => ({ + month: '', + year: '', +}); From 5f47d80422d4b058e91448550de993bbbdbe60aa Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Mon, 18 May 2020 17:25:32 +0200 Subject: [PATCH 03/32] Refactor name externalId validation --- src/pods/project/components/data.validations.ts | 14 ++------------ src/pods/project/components/report.validations.ts | 14 ++------------ 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/pods/project/components/data.validations.ts b/src/pods/project/components/data.validations.ts index 64acdda..476d2f7 100644 --- a/src/pods/project/components/data.validations.ts +++ b/src/pods/project/components/data.validations.ts @@ -3,18 +3,8 @@ import { createFormikValidation } from '@lemoncode/fonk-formik'; const validationSchema: ValidationSchema = { field: { - name: [ - { - validator: Validators.required, - message: 'Campo obligatorio', - }, - ], - externalId: [ - { - validator: Validators.required, - message: 'Campo obligatorio', - }, - ], + name: [Validators.required], + externalId: [Validators.required], }, }; diff --git a/src/pods/project/components/report.validations.ts b/src/pods/project/components/report.validations.ts index bc8a922..c85a33b 100644 --- a/src/pods/project/components/report.validations.ts +++ b/src/pods/project/components/report.validations.ts @@ -3,18 +3,8 @@ import { createFormikValidation } from '@lemoncode/fonk-formik'; const validationSchema: ValidationSchema = { field: { - month: [ - { - validator: Validators.required, - message: 'Campo obligatorio', - }, - ], - year: [ - { - validator: Validators.required, - message: 'Campo obligatorio', - }, - ], + month: [Validators.required], + year: [Validators.required], }, }; From 52b9b9d258c70b0121cd880368e4605dc6acbce4 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Wed, 17 Jun 2020 14:17:58 +0200 Subject: [PATCH 04/32] Remove externalId validation --- src/pods/project/components/data.validations.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pods/project/components/data.validations.ts b/src/pods/project/components/data.validations.ts index 476d2f7..ecc27bb 100644 --- a/src/pods/project/components/data.validations.ts +++ b/src/pods/project/components/data.validations.ts @@ -4,7 +4,6 @@ import { createFormikValidation } from '@lemoncode/fonk-formik'; const validationSchema: ValidationSchema = { field: { name: [Validators.required], - externalId: [Validators.required], }, }; From c1a335efc0fcb69cc2d450a60bd1f06573b9aa67 Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Munoz Date: Mon, 22 Jun 2020 16:35:14 +0200 Subject: [PATCH 05/32] config graphql client --- config/webpack/dev.js | 3 +++ dev.env | 2 +- prod.env | 2 +- src/core/api/graphql.client.ts | 4 ++++ src/core/api/index.ts | 1 + .../employee-list/api/employee-list.api.ts | 18 +++++++++++++++++- 6 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 src/core/api/graphql.client.ts create mode 100644 src/core/api/index.ts diff --git a/config/webpack/dev.js b/config/webpack/dev.js index a6514f8..0bd9544 100644 --- a/config/webpack/dev.js +++ b/config/webpack/dev.js @@ -28,6 +28,9 @@ module.exports = merge.strategy({ port: 8080, stats: 'minimal', hot: true, + proxy: { + '/graphql': 'http://localhost:8081', + }, }, plugins: [ new Dotenv({ diff --git a/dev.env b/dev.env index 0033ebb..636dd1a 100644 --- a/dev.env +++ b/dev.env @@ -1,2 +1,2 @@ NODE_ENV=development -GRAPHQL_URL=http://localhost:8081/graphql/employee +GRAPHQL_URL=/graphql/admin diff --git a/prod.env b/prod.env index 624e793..479c7bd 100644 --- a/prod.env +++ b/prod.env @@ -1,2 +1,2 @@ NODE_ENV=production -GRAPHQL_URL=/graphql/employee +GRAPHQL_URL=/graphql/admin diff --git a/src/core/api/graphql.client.ts b/src/core/api/graphql.client.ts new file mode 100644 index 0000000..61505b9 --- /dev/null +++ b/src/core/api/graphql.client.ts @@ -0,0 +1,4 @@ +import { GraphQLClient } from 'graphql-request'; + +const url = process.env.GRAPHQL_URL; +export const graphQLClient = new GraphQLClient(url); diff --git a/src/core/api/index.ts b/src/core/api/index.ts new file mode 100644 index 0000000..38f62bb --- /dev/null +++ b/src/core/api/index.ts @@ -0,0 +1 @@ +export * from './graphql.client'; diff --git a/src/pods/employee-list/api/employee-list.api.ts b/src/pods/employee-list/api/employee-list.api.ts index 4cd5a38..6d2128d 100644 --- a/src/pods/employee-list/api/employee-list.api.ts +++ b/src/pods/employee-list/api/employee-list.api.ts @@ -1,10 +1,26 @@ +import { graphQLClient } from 'core/api'; import { Employee } from './employee-list.api-model'; import { mockEmployeeList } from './employee-list.mock-data'; let employeeList = [...mockEmployeeList]; +interface GetEmployeeListResponse { + employees: Employee[]; +} + export const getEmployeeList = async (): Promise => { - return employeeList; + const query = ` + query { + employees { + id + name + } + } + `; + const { employees } = await graphQLClient.request( + query + ); + return employees; }; export const deleteEmployee = async (id: string): Promise => { From cee690ca4cae2718fa95dc9b5756d606049d3a8d Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Munoz Date: Mon, 22 Jun 2020 16:58:24 +0200 Subject: [PATCH 06/32] implement delete method --- .../employee-list/api/employee-list.api.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/pods/employee-list/api/employee-list.api.ts b/src/pods/employee-list/api/employee-list.api.ts index 6d2128d..5219a2e 100644 --- a/src/pods/employee-list/api/employee-list.api.ts +++ b/src/pods/employee-list/api/employee-list.api.ts @@ -1,8 +1,5 @@ import { graphQLClient } from 'core/api'; import { Employee } from './employee-list.api-model'; -import { mockEmployeeList } from './employee-list.mock-data'; - -let employeeList = [...mockEmployeeList]; interface GetEmployeeListResponse { employees: Employee[]; @@ -23,7 +20,18 @@ export const getEmployeeList = async (): Promise => { return employees; }; +interface DeleteEmployeeResponse { + deleteEmployee: boolean; +} + export const deleteEmployee = async (id: string): Promise => { - employeeList = employeeList.filter(e => e.id !== id); - return true; + const query = ` + mutation { + deleteEmployee(id: "${id}") + } + `; + const { deleteEmployee } = await graphQLClient.request< + DeleteEmployeeResponse + >(query); + return deleteEmployee; }; From f281c07f4db1c6d552cdabd4ec3dd9a92ab3ceaf Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Munoz Date: Mon, 22 Jun 2020 17:12:21 +0200 Subject: [PATCH 07/32] implement get employee by id --- .../api/employee-list.mock-data.ts | 1 + src/pods/employee/api/employee.api.ts | 19 +++++++++++++++++-- src/pods/employee/api/employee.mock-data.ts | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/pods/employee-list/api/employee-list.mock-data.ts b/src/pods/employee-list/api/employee-list.mock-data.ts index b48dbfa..d16741d 100644 --- a/src/pods/employee-list/api/employee-list.mock-data.ts +++ b/src/pods/employee-list/api/employee-list.mock-data.ts @@ -1,5 +1,6 @@ import { Employee } from './employee-list.api-model'; +// TODO: Move to back export const mockEmployeeList: Employee[] = [ { id: '1', diff --git a/src/pods/employee/api/employee.api.ts b/src/pods/employee/api/employee.api.ts index 252d864..b51c83c 100644 --- a/src/pods/employee/api/employee.api.ts +++ b/src/pods/employee/api/employee.api.ts @@ -1,6 +1,21 @@ import { Employee } from './employee.api-model'; -import { mockEmployee } from './employee.mock-data'; +import { graphQLClient } from 'core/api'; + +interface GetEmployeeResponse { + employee: Employee; +} export const getEmployeeById = async (id: string): Promise => { - return mockEmployee; + const query = ` + query { + employee(id: "${id}") { + id + name + } + } + `; + + const { employee } = await graphQLClient.request(query); + + return employee; }; diff --git a/src/pods/employee/api/employee.mock-data.ts b/src/pods/employee/api/employee.mock-data.ts index 6eaf7b5..d63aaa6 100644 --- a/src/pods/employee/api/employee.mock-data.ts +++ b/src/pods/employee/api/employee.mock-data.ts @@ -1,5 +1,6 @@ import { Employee, ProjectSummary } from './employee.api-model'; +// TODO: Move to back const mockProjectSummaryList: ProjectSummary[] = [ { id: '1', From c3b7c8060876a658ee0504547859fd7b26d5931c Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Munoz Date: Mon, 22 Jun 2020 17:46:46 +0200 Subject: [PATCH 08/32] implement employee insert --- src/pods/employee/api/employee.api.ts | 23 +++++++++++++++++++++++ src/pods/employee/employee.container.tsx | 14 +++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/pods/employee/api/employee.api.ts b/src/pods/employee/api/employee.api.ts index b51c83c..5fcc070 100644 --- a/src/pods/employee/api/employee.api.ts +++ b/src/pods/employee/api/employee.api.ts @@ -19,3 +19,26 @@ export const getEmployeeById = async (id: string): Promise => { return employee; }; + +interface SaveEmployeeResponse { + saveEmployee: Employee; +} + +export const saveEmployee = async (employee: Employee): Promise => { + const query = ` + mutation($employee: EmployeeInput!) { + saveEmployee(employee: $employee) { + id + } + } + `; + + const { saveEmployee } = await graphQLClient.request( + query, + { + employee, + } + ); + + return saveEmployee.id; +}; diff --git a/src/pods/employee/employee.container.tsx b/src/pods/employee/employee.container.tsx index 876923a..9ae7a7c 100644 --- a/src/pods/employee/employee.container.tsx +++ b/src/pods/employee/employee.container.tsx @@ -8,7 +8,7 @@ import { } from './employee.vm'; import { useSnackbarContext } from 'common/components'; import { trackPromise } from 'react-promise-tracker'; -import { getEmployeeById } from './api'; +import { getEmployeeById, saveEmployee } from './api'; import { mapEmployeeFromApiToVm } from './employee.mappers'; import { useParams } from 'react-router-dom'; import { isEditModeHelper } from 'common/helpers'; @@ -33,8 +33,16 @@ export const EmployeeContainer: React.FunctionComponent = () => { } }; - const handleSave = (employee: Employee) => { - console.log('Guardado'); + const handleSave = async (employee: Employee) => { + try { + // TODO: implement mapper + const apiEmployee: any = { id: employee.id, name: employee.name }; + const id = await saveEmployee(apiEmployee); + setEmployee({ ...employee, id }); + } catch (error) { + error && + showMessage('Ha ocurrido un error al guardar el empleado', 'error'); + } }; const handleCancel = () => { From 27d1b344a8ca9823aca81b89182275a4f9244c18 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Mon, 22 Jun 2020 20:12:42 +0200 Subject: [PATCH 09/32] Fix employee api --- src/pods/employee-list/api/employee-list.api.ts | 3 +++ src/pods/employee/api/employee.api.ts | 3 +++ src/pods/employee/employee.container.tsx | 8 +++++--- src/pods/employee/employee.mappers.ts | 6 ++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/pods/employee-list/api/employee-list.api.ts b/src/pods/employee-list/api/employee-list.api.ts index 5219a2e..40672f3 100644 --- a/src/pods/employee-list/api/employee-list.api.ts +++ b/src/pods/employee-list/api/employee-list.api.ts @@ -11,6 +11,9 @@ export const getEmployeeList = async (): Promise => { employees { id name + isActive + email + lastDateIncurred } } `; diff --git a/src/pods/employee/api/employee.api.ts b/src/pods/employee/api/employee.api.ts index 5fcc070..6c15aa5 100644 --- a/src/pods/employee/api/employee.api.ts +++ b/src/pods/employee/api/employee.api.ts @@ -11,6 +11,9 @@ export const getEmployeeById = async (id: string): Promise => { employee(id: "${id}") { id name + isActive + email + lastDateIncurred } } `; diff --git a/src/pods/employee/employee.container.tsx b/src/pods/employee/employee.container.tsx index 9ae7a7c..95a1439 100644 --- a/src/pods/employee/employee.container.tsx +++ b/src/pods/employee/employee.container.tsx @@ -9,7 +9,10 @@ import { import { useSnackbarContext } from 'common/components'; import { trackPromise } from 'react-promise-tracker'; import { getEmployeeById, saveEmployee } from './api'; -import { mapEmployeeFromApiToVm } from './employee.mappers'; +import { + mapEmployeeFromApiToVm, + mapEmployeeFromVmToApi, +} from './employee.mappers'; import { useParams } from 'react-router-dom'; import { isEditModeHelper } from 'common/helpers'; @@ -35,8 +38,7 @@ export const EmployeeContainer: React.FunctionComponent = () => { const handleSave = async (employee: Employee) => { try { - // TODO: implement mapper - const apiEmployee: any = { id: employee.id, name: employee.name }; + const apiEmployee = mapEmployeeFromVmToApi(employee); const id = await saveEmployee(apiEmployee); setEmployee({ ...employee, id }); } catch (error) { diff --git a/src/pods/employee/employee.mappers.ts b/src/pods/employee/employee.mappers.ts index 3464896..01b26b7 100644 --- a/src/pods/employee/employee.mappers.ts +++ b/src/pods/employee/employee.mappers.ts @@ -23,3 +23,9 @@ export const mapEmployeeFromApiToVm = ( } : viewModel.createEmptyEmployee(); }; + +export const mapEmployeeFromVmToApi = ( + employee: viewModel.Employee +): apiModel.Employee => ({ + ...employee, +}); From 0b2c6ec65d846927e3d714b7b63af0e1465e86ca Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Tue, 23 Jun 2020 16:54:08 +0200 Subject: [PATCH 10/32] Refactor employee components and remove mock data --- .../api/employee-list.mock-data.ts | 61 ------------------- src/pods/employee/api/employee.api.ts | 5 ++ src/pods/employee/api/employee.mock-data.ts | 34 ----------- .../components/project-row.component.tsx | 18 +++++- .../employee/components/project.component.tsx | 25 +++++++- src/pods/employee/employee.component.tsx | 11 ++-- src/pods/employee/employee.container.tsx | 23 ++++++- 7 files changed, 69 insertions(+), 108 deletions(-) delete mode 100644 src/pods/employee-list/api/employee-list.mock-data.ts delete mode 100644 src/pods/employee/api/employee.mock-data.ts diff --git a/src/pods/employee-list/api/employee-list.mock-data.ts b/src/pods/employee-list/api/employee-list.mock-data.ts deleted file mode 100644 index d16741d..0000000 --- a/src/pods/employee-list/api/employee-list.mock-data.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Employee } from './employee-list.api-model'; - -// TODO: Move to back -export const mockEmployeeList: Employee[] = [ - { - id: '1', - isActive: true, - name: 'Daniel Perez', - email: 'daniel.perez@empresa.com', - lastDateIncurred: '02/02/2020', - }, - { - id: '2', - isActive: true, - name: 'Jose Gomez', - email: 'jose.gomez@empresa.com', - lastDateIncurred: '05/02/2020', - }, - { - id: '3', - isActive: false, - name: 'Manuel Ruiz', - email: 'manuel.ruiz@empresa.com', - lastDateIncurred: '06/02/2020', - }, - { - id: '4', - isActive: true, - name: 'Ramón Gomez', - email: 'ramon.gomez@empresa.com', - lastDateIncurred: '02/05/2020', - }, - { - id: '5', - isActive: false, - name: 'María Lopez', - email: 'maria.lopez@empresa.com', - lastDateIncurred: '05/08/2020', - }, - { - id: '6', - isActive: true, - name: 'Manuel Ortiz', - email: 'manuel.ortiz@empresa.com', - lastDateIncurred: '06/06/2020', - }, - { - id: '7', - isActive: false, - name: 'David Martos', - email: 'david.martos@empresa.com', - lastDateIncurred: '14/08/2020', - }, - { - id: '8', - isActive: true, - name: 'Luz Roca', - email: 'luz.roca@empresa.com', - lastDateIncurred: '20/06/2020', - }, -]; diff --git a/src/pods/employee/api/employee.api.ts b/src/pods/employee/api/employee.api.ts index 6c15aa5..e6d02ad 100644 --- a/src/pods/employee/api/employee.api.ts +++ b/src/pods/employee/api/employee.api.ts @@ -14,6 +14,11 @@ export const getEmployeeById = async (id: string): Promise => { isActive email lastDateIncurred + projects { + id + isAssigned + projectName + } } } `; diff --git a/src/pods/employee/api/employee.mock-data.ts b/src/pods/employee/api/employee.mock-data.ts deleted file mode 100644 index d63aaa6..0000000 --- a/src/pods/employee/api/employee.mock-data.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Employee, ProjectSummary } from './employee.api-model'; - -// TODO: Move to back -const mockProjectSummaryList: ProjectSummary[] = [ - { - id: '1', - isAssigned: true, - projectName: 'Mapfre', - }, - { - id: '2', - isAssigned: false, - projectName: 'Bankia', - }, - { - id: '3', - isAssigned: false, - projectName: 'Vacaciones', - }, - { - id: '4', - isAssigned: true, - projectName: 'Baja', - }, -]; - -export const mockEmployee: Employee = { - id: '1', - name: 'Prueba Nombre', - email: 'prueba@email.com', - isActive: true, - temporalPassword: 'admin', - projects: mockProjectSummaryList, -}; diff --git a/src/pods/employee/components/project-row.component.tsx b/src/pods/employee/components/project-row.component.tsx index af5c32f..12df8d4 100644 --- a/src/pods/employee/components/project-row.component.tsx +++ b/src/pods/employee/components/project-row.component.tsx @@ -7,15 +7,27 @@ import { import Checkbox from '@material-ui/core/Checkbox'; import { ProjectSummary } from '../employee.vm'; -type Props = RowRendererProps; +interface RowProps extends RowRendererProps { + onChangeProject: (project: ProjectSummary) => void; +} -export const EmployeeRowComponent: React.FunctionComponent = ({ +export const EmployeeRowComponent: React.FunctionComponent = ({ row, + onChangeProject, }) => { return ( - + + onChangeProject({ + ...row, + isAssigned: checked, + }) + } + checked={row.isAssigned} + /> {row.projectName} diff --git a/src/pods/employee/components/project.component.tsx b/src/pods/employee/components/project.component.tsx index 00df731..e1e2abb 100644 --- a/src/pods/employee/components/project.component.tsx +++ b/src/pods/employee/components/project.component.tsx @@ -7,25 +7,44 @@ import { CommandFooterComponent } from 'common-app/command-footer'; interface Props { projectSummaryList: ProjectSummary[]; className?: string; + onSave: (project: ProjectSummary[]) => void; onCancel: () => void; } export const ProjectComponent: React.FunctionComponent = ({ projectSummaryList, className, + onSave, onCancel, }) => { + const [projectList, setProjectList] = React.useState( + projectSummaryList + ); + + React.useEffect(() => { + setProjectList(projectSummaryList); + }, [projectSummaryList]); + + const handleChangeProject = (id: string) => (project: ProjectSummary) => { + const updateProjectList = projectList.map(p => (p.id === id ? project : p)); + setProjectList(updateProjectList); + }; + + const handleSave = () => onSave(projectList); return ( <> ) => ( - + )} /> - + ); }; diff --git a/src/pods/employee/employee.component.tsx b/src/pods/employee/employee.component.tsx index 62591ea..647ad11 100644 --- a/src/pods/employee/employee.component.tsx +++ b/src/pods/employee/employee.component.tsx @@ -6,14 +6,15 @@ import { } from 'common/components'; import AppBar from '@material-ui/core/AppBar'; import { DataComponent, ProjectComponent, ReportComponent } from './components'; -import { Employee, Report } from './employee.vm'; +import { Employee, Report, ProjectSummary } from './employee.vm'; import * as classes from './employee.styles'; interface Props { employee: Employee; isEditMode: boolean; report: Report; - onSave: (employee: Employee) => void; + onSaveEmployee: (employee: Employee) => void; + onSaveProjectSelection: (project: ProjectSummary[]) => void; onCancel: () => void; onGenerateExcel: (report: Report) => void; } @@ -22,7 +23,8 @@ export const EmployeeComponent: React.FunctionComponent = ({ employee, isEditMode, report, - onSave, + onSaveEmployee, + onSaveProjectSelection, onCancel, onGenerateExcel, }) => { @@ -40,7 +42,7 @@ export const EmployeeComponent: React.FunctionComponent = ({ @@ -49,6 +51,7 @@ export const EmployeeComponent: React.FunctionComponent = ({ diff --git a/src/pods/employee/employee.container.tsx b/src/pods/employee/employee.container.tsx index 95a1439..f482dd6 100644 --- a/src/pods/employee/employee.container.tsx +++ b/src/pods/employee/employee.container.tsx @@ -5,6 +5,7 @@ import { Report, createEmptyEmployee, createEmptyReport, + ProjectSummary, } from './employee.vm'; import { useSnackbarContext } from 'common/components'; import { trackPromise } from 'react-promise-tracker'; @@ -15,6 +16,8 @@ import { } from './employee.mappers'; import { useParams } from 'react-router-dom'; import { isEditModeHelper } from 'common/helpers'; +import { routes } from 'core/router'; +import { useHistory } from 'react-router'; export const EmployeeContainer: React.FunctionComponent = () => { const { id } = useParams(); @@ -24,6 +27,7 @@ export const EmployeeContainer: React.FunctionComponent = () => { const [isEditMode, setIsEditMode] = React.useState(false); const [report, setReport] = React.useState(createEmptyReport()); const { showMessage } = useSnackbarContext(); + const history = useHistory(); const onLoadEmployee = async () => { try { @@ -36,19 +40,31 @@ export const EmployeeContainer: React.FunctionComponent = () => { } }; - const handleSave = async (employee: Employee) => { + const handleSaveEmployee = async (employee: Employee) => { try { const apiEmployee = mapEmployeeFromVmToApi(employee); const id = await saveEmployee(apiEmployee); setEmployee({ ...employee, id }); + showMessage('Empleado guardado con éxito', 'success'); + history.push(routes.editEmployee(id)); } catch (error) { error && showMessage('Ha ocurrido un error al guardar el empleado', 'error'); } }; + const handleSaveProjectSelection = async ( + employeeProjects: ProjectSummary[] + ) => { + try { + console.log(employeeProjects); + } catch (error) { + error && showMessage('Ha ocurrido un error al guardar', 'error'); + } + }; + const handleCancel = () => { - history.back(); + history.goBack(); }; const handleGenerateExcel = (report: Report) => { @@ -69,7 +85,8 @@ export const EmployeeContainer: React.FunctionComponent = () => { employee={employee} isEditMode={isEditMode} report={report} - onSave={handleSave} + onSaveEmployee={handleSaveEmployee} + onSaveProjectSelection={handleSaveProjectSelection} onCancel={handleCancel} onGenerateExcel={handleGenerateExcel} /> From a5ad0303f16b7a35617a1310b19adf366350da15 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Tue, 23 Jun 2020 19:05:55 +0200 Subject: [PATCH 11/32] Implement saveProjectSummaryList and mappers --- src/pods/employee/api/employee.api.ts | 18 +++++++++++++++++- src/pods/employee/employee.container.tsx | 23 +++++++++++++++++------ src/pods/employee/employee.mappers.ts | 13 +++++++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/pods/employee/api/employee.api.ts b/src/pods/employee/api/employee.api.ts index e6d02ad..3fb93d9 100644 --- a/src/pods/employee/api/employee.api.ts +++ b/src/pods/employee/api/employee.api.ts @@ -1,4 +1,4 @@ -import { Employee } from './employee.api-model'; +import { Employee, ProjectSummary } from './employee.api-model'; import { graphQLClient } from 'core/api'; interface GetEmployeeResponse { @@ -50,3 +50,19 @@ export const saveEmployee = async (employee: Employee): Promise => { return saveEmployee.id; }; + +export const saveProjectSummary = async ( + id: string, + projectSummaryList: ProjectSummary[] +): Promise => { + const query = `mutation($projectSummaryList: [ProjectSummaryInput!]!) { + saveProjectSummaryList( + id: "${id}", + projectSummaryList: $projectSummaryList, + ) { + id + } + }`; + + await graphQLClient.request(query, { projectSummaryList }); +}; diff --git a/src/pods/employee/employee.container.tsx b/src/pods/employee/employee.container.tsx index f482dd6..860c088 100644 --- a/src/pods/employee/employee.container.tsx +++ b/src/pods/employee/employee.container.tsx @@ -9,10 +9,11 @@ import { } from './employee.vm'; import { useSnackbarContext } from 'common/components'; import { trackPromise } from 'react-promise-tracker'; -import { getEmployeeById, saveEmployee } from './api'; +import { getEmployeeById, saveEmployee, saveProjectSummary } from './api'; import { mapEmployeeFromApiToVm, mapEmployeeFromVmToApi, + mapProjectSummaryListFromVmToApi, } from './employee.mappers'; import { useParams } from 'react-router-dom'; import { isEditModeHelper } from 'common/helpers'; @@ -54,12 +55,22 @@ export const EmployeeContainer: React.FunctionComponent = () => { }; const handleSaveProjectSelection = async ( - employeeProjects: ProjectSummary[] + employeeSummary: ProjectSummary[] ) => { - try { - console.log(employeeProjects); - } catch (error) { - error && showMessage('Ha ocurrido un error al guardar', 'error'); + if (id) { + try { + const apiProjectSummary = mapProjectSummaryListFromVmToApi( + employeeSummary + ); + await saveProjectSummary(id, apiProjectSummary); + setEmployee({ + ...employee, + projects: employeeSummary, + }); + showMessage('Se actualizó con éxito', 'success'); + } catch (error) { + error && showMessage('Ha ocurrido un error al guardar', 'error'); + } } }; diff --git a/src/pods/employee/employee.mappers.ts b/src/pods/employee/employee.mappers.ts index 01b26b7..af69f3a 100644 --- a/src/pods/employee/employee.mappers.ts +++ b/src/pods/employee/employee.mappers.ts @@ -29,3 +29,16 @@ export const mapEmployeeFromVmToApi = ( ): apiModel.Employee => ({ ...employee, }); + +const mapProjectSummaryFromVmToApi = ( + projectSummary: viewModel.ProjectSummary +): apiModel.ProjectSummary => ({ + id: projectSummary.id, + isAssigned: projectSummary.isAssigned, + projectName: projectSummary.projectName, +}); + +export const mapProjectSummaryListFromVmToApi = ( + projectSummary: viewModel.ProjectSummary[] +): apiModel.ProjectSummary[] => + mapToCollection(projectSummary, mapProjectSummaryFromVmToApi); From 6aab1044aba549a8b30d4c70ee296fd09ed64150 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Wed, 24 Jun 2020 10:50:39 +0200 Subject: [PATCH 12/32] Fix employee container logic --- src/core/router/index.ts | 1 + src/core/router/route-params.vm.ts | 3 +++ src/pods/employee/employee.container.tsx | 31 +++++++++++++++--------- 3 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 src/core/router/route-params.vm.ts diff --git a/src/core/router/index.ts b/src/core/router/index.ts index 0e1ad7f..8b4a4d1 100644 --- a/src/core/router/index.ts +++ b/src/core/router/index.ts @@ -1,2 +1,3 @@ export * from './router.component'; export * from './routes'; +export * from './route-params.vm'; diff --git a/src/core/router/route-params.vm.ts b/src/core/router/route-params.vm.ts new file mode 100644 index 0000000..66c2e6d --- /dev/null +++ b/src/core/router/route-params.vm.ts @@ -0,0 +1,3 @@ +export interface EditParams { + id: string; +} diff --git a/src/pods/employee/employee.container.tsx b/src/pods/employee/employee.container.tsx index 860c088..3fe1f55 100644 --- a/src/pods/employee/employee.container.tsx +++ b/src/pods/employee/employee.container.tsx @@ -17,11 +17,11 @@ import { } from './employee.mappers'; import { useParams } from 'react-router-dom'; import { isEditModeHelper } from 'common/helpers'; -import { routes } from 'core/router'; +import { routes, EditParams } from 'core/router'; import { useHistory } from 'react-router'; export const EmployeeContainer: React.FunctionComponent = () => { - const { id } = useParams(); + const params = useParams(); const [employee, setEmployee] = React.useState( createEmptyEmployee() ); @@ -32,7 +32,7 @@ export const EmployeeContainer: React.FunctionComponent = () => { const onLoadEmployee = async () => { try { - const apiEmployee = await trackPromise(getEmployeeById(id)); + const apiEmployee = await trackPromise(getEmployeeById(params.id)); const viewModelEmployee = mapEmployeeFromApiToVm(apiEmployee); setEmployee(viewModelEmployee); } catch (error) { @@ -41,28 +41,35 @@ export const EmployeeContainer: React.FunctionComponent = () => { } }; + const handleSuccessSaveEmployee = (id: string, newEmployee: Employee) => { + if (id) { + showMessage('Empleado guardado con éxito', 'success'); + setEmployee(newEmployee); + history.push(routes.editEmployee(id)); + } else { + showMessage('Ha ocurrido un error al guardar el empleado', 'error'); + } + }; + const handleSaveEmployee = async (employee: Employee) => { try { const apiEmployee = mapEmployeeFromVmToApi(employee); - const id = await saveEmployee(apiEmployee); - setEmployee({ ...employee, id }); - showMessage('Empleado guardado con éxito', 'success'); - history.push(routes.editEmployee(id)); + const id = await trackPromise(saveEmployee(apiEmployee)); + handleSuccessSaveEmployee(id, employee); } catch (error) { - error && - showMessage('Ha ocurrido un error al guardar el empleado', 'error'); + error && showMessage(error.message, 'error'); } }; const handleSaveProjectSelection = async ( employeeSummary: ProjectSummary[] ) => { - if (id) { + if (params.id) { try { const apiProjectSummary = mapProjectSummaryListFromVmToApi( employeeSummary ); - await saveProjectSummary(id, apiProjectSummary); + await trackPromise(saveProjectSummary(params.id, apiProjectSummary)); setEmployee({ ...employee, projects: employeeSummary, @@ -84,7 +91,7 @@ export const EmployeeContainer: React.FunctionComponent = () => { }; React.useEffect(() => { - const isEditMode = isEditModeHelper(id); + const isEditMode = isEditModeHelper(params.id); setIsEditMode(isEditMode); if (isEditMode) { onLoadEmployee(); From 7eb5db28531aab398c42b7ccee24f4a4a974e182 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Wed, 24 Jun 2020 12:51:56 +0200 Subject: [PATCH 13/32] Fix view model and api model --- src/pods/employee/api/employee.api-model.ts | 10 +++++++--- src/pods/employee/api/employee.api.ts | 4 ++-- .../components/project-row.component.tsx | 8 ++++---- .../employee/components/project.component.tsx | 12 ++++++------ src/pods/employee/employee.component.tsx | 4 ++-- src/pods/employee/employee.container.tsx | 4 ++-- src/pods/employee/employee.mappers.ts | 17 ++++++++--------- src/pods/employee/employee.vm.ts | 11 ++++++++--- 8 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/pods/employee/api/employee.api-model.ts b/src/pods/employee/api/employee.api-model.ts index 87202d4..ea4a1ff 100644 --- a/src/pods/employee/api/employee.api-model.ts +++ b/src/pods/employee/api/employee.api-model.ts @@ -4,11 +4,15 @@ export interface Employee { email: string; isActive: boolean; temporalPassword?: string; - projects?: ProjectSummary[]; + projects?: EmployeeProject[]; } -export interface ProjectSummary { +export interface EmployeeProject { id: string; isAssigned?: boolean; - projectName: string; +} + +export interface Project { + id: string; + name: string; } diff --git a/src/pods/employee/api/employee.api.ts b/src/pods/employee/api/employee.api.ts index 3fb93d9..ea91dec 100644 --- a/src/pods/employee/api/employee.api.ts +++ b/src/pods/employee/api/employee.api.ts @@ -1,4 +1,4 @@ -import { Employee, ProjectSummary } from './employee.api-model'; +import { Employee, EmployeeProject } from './employee.api-model'; import { graphQLClient } from 'core/api'; interface GetEmployeeResponse { @@ -53,7 +53,7 @@ export const saveEmployee = async (employee: Employee): Promise => { export const saveProjectSummary = async ( id: string, - projectSummaryList: ProjectSummary[] + projectSummaryList: EmployeeProject[] ): Promise => { const query = `mutation($projectSummaryList: [ProjectSummaryInput!]!) { saveProjectSummaryList( diff --git a/src/pods/employee/components/project-row.component.tsx b/src/pods/employee/components/project-row.component.tsx index 12df8d4..1dee59e 100644 --- a/src/pods/employee/components/project-row.component.tsx +++ b/src/pods/employee/components/project-row.component.tsx @@ -5,10 +5,10 @@ import { CellComponent, } from 'common/components'; import Checkbox from '@material-ui/core/Checkbox'; -import { ProjectSummary } from '../employee.vm'; +import { EmployeeProject } from '../employee.vm'; -interface RowProps extends RowRendererProps { - onChangeProject: (project: ProjectSummary) => void; +interface RowProps extends RowRendererProps { + onChangeProject: (project: EmployeeProject) => void; } export const EmployeeRowComponent: React.FunctionComponent = ({ @@ -29,7 +29,7 @@ export const EmployeeRowComponent: React.FunctionComponent = ({ checked={row.isAssigned} /> - {row.projectName} + {''} ); }; diff --git a/src/pods/employee/components/project.component.tsx b/src/pods/employee/components/project.component.tsx index e1e2abb..a1bf80b 100644 --- a/src/pods/employee/components/project.component.tsx +++ b/src/pods/employee/components/project.component.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { TableContainer, RowRendererProps } from 'common/components'; -import { ProjectSummary } from '../employee.vm'; +import { EmployeeProject } from '../employee.vm'; import { EmployeeRowComponent } from './project-row.component'; import { CommandFooterComponent } from 'common-app/command-footer'; interface Props { - projectSummaryList: ProjectSummary[]; + projectSummaryList: EmployeeProject[]; className?: string; - onSave: (project: ProjectSummary[]) => void; + onSave: (project: EmployeeProject[]) => void; onCancel: () => void; } @@ -17,7 +17,7 @@ export const ProjectComponent: React.FunctionComponent = ({ onSave, onCancel, }) => { - const [projectList, setProjectList] = React.useState( + const [projectList, setProjectList] = React.useState( projectSummaryList ); @@ -25,7 +25,7 @@ export const ProjectComponent: React.FunctionComponent = ({ setProjectList(projectSummaryList); }, [projectSummaryList]); - const handleChangeProject = (id: string) => (project: ProjectSummary) => { + const handleChangeProject = (id: string) => (project: EmployeeProject) => { const updateProjectList = projectList.map(p => (p.id === id ? project : p)); setProjectList(updateProjectList); }; @@ -37,7 +37,7 @@ export const ProjectComponent: React.FunctionComponent = ({ columns={['Asignado', 'Nombre y Apellido']} rows={projectList} className={className} - rowRenderer={(rowProps: RowRendererProps) => ( + rowRenderer={(rowProps: RowRendererProps) => ( void; - onSaveProjectSelection: (project: ProjectSummary[]) => void; + onSaveProjectSelection: (project: EmployeeProject[]) => void; onCancel: () => void; onGenerateExcel: (report: Report) => void; } diff --git a/src/pods/employee/employee.container.tsx b/src/pods/employee/employee.container.tsx index 3fe1f55..4b6589e 100644 --- a/src/pods/employee/employee.container.tsx +++ b/src/pods/employee/employee.container.tsx @@ -5,7 +5,7 @@ import { Report, createEmptyEmployee, createEmptyReport, - ProjectSummary, + EmployeeProject, } from './employee.vm'; import { useSnackbarContext } from 'common/components'; import { trackPromise } from 'react-promise-tracker'; @@ -62,7 +62,7 @@ export const EmployeeContainer: React.FunctionComponent = () => { }; const handleSaveProjectSelection = async ( - employeeSummary: ProjectSummary[] + employeeSummary: EmployeeProject[] ) => { if (params.id) { try { diff --git a/src/pods/employee/employee.mappers.ts b/src/pods/employee/employee.mappers.ts index af69f3a..68daba8 100644 --- a/src/pods/employee/employee.mappers.ts +++ b/src/pods/employee/employee.mappers.ts @@ -3,14 +3,14 @@ import * as apiModel from './api/employee.api-model'; import * as viewModel from './employee.vm'; const mapProjectSummaryFromApiToVm = ( - projectSummary: apiModel.ProjectSummary -): viewModel.ProjectSummary => ({ + projectSummary: apiModel.EmployeeProject +): viewModel.EmployeeProject => ({ ...projectSummary, }); const mapProjectSummaryListFromApiToVm = ( - projectSummary: apiModel.ProjectSummary[] -): viewModel.ProjectSummary[] => + projectSummary: apiModel.EmployeeProject[] +): viewModel.EmployeeProject[] => mapToCollection(projectSummary, ps => mapProjectSummaryFromApiToVm(ps)); export const mapEmployeeFromApiToVm = ( @@ -31,14 +31,13 @@ export const mapEmployeeFromVmToApi = ( }); const mapProjectSummaryFromVmToApi = ( - projectSummary: viewModel.ProjectSummary -): apiModel.ProjectSummary => ({ + projectSummary: viewModel.EmployeeProject +): apiModel.EmployeeProject => ({ id: projectSummary.id, isAssigned: projectSummary.isAssigned, - projectName: projectSummary.projectName, }); export const mapProjectSummaryListFromVmToApi = ( - projectSummary: viewModel.ProjectSummary[] -): apiModel.ProjectSummary[] => + projectSummary: viewModel.EmployeeProject[] +): apiModel.EmployeeProject[] => mapToCollection(projectSummary, mapProjectSummaryFromVmToApi); diff --git a/src/pods/employee/employee.vm.ts b/src/pods/employee/employee.vm.ts index 5835537..a4de357 100644 --- a/src/pods/employee/employee.vm.ts +++ b/src/pods/employee/employee.vm.ts @@ -4,13 +4,18 @@ export interface Employee { email: string; isActive: boolean; temporalPassword?: string; - projects: ProjectSummary[]; + projects: EmployeeProject[]; } -export interface ProjectSummary { +export interface EmployeeProject { id: string; isAssigned?: boolean; - projectName: string; +} + +export interface Project { + id: string; + isAssigned?: boolean; + name: string; } export interface Report { From 796779d09c56cc621a5cd89a7de3ad66c372bf6f Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Wed, 24 Jun 2020 14:14:52 +0200 Subject: [PATCH 14/32] Work in progress --- src/pods/employee/api/employee.api.ts | 32 ++++++++++++++----- .../employee/components/project.component.tsx | 14 ++++---- src/pods/employee/employee.component.tsx | 2 +- src/pods/employee/employee.container.tsx | 19 ++++++++--- src/pods/employee/employee.mappers.spec.ts | 2 -- src/pods/employee/employee.mappers.ts | 15 ++++----- 6 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/pods/employee/api/employee.api.ts b/src/pods/employee/api/employee.api.ts index ea91dec..354c5f0 100644 --- a/src/pods/employee/api/employee.api.ts +++ b/src/pods/employee/api/employee.api.ts @@ -1,4 +1,4 @@ -import { Employee, EmployeeProject } from './employee.api-model'; +import { Employee, EmployeeProject, Project } from './employee.api-model'; import { graphQLClient } from 'core/api'; interface GetEmployeeResponse { @@ -17,7 +17,6 @@ export const getEmployeeById = async (id: string): Promise => { projects { id isAssigned - projectName } } } @@ -28,6 +27,23 @@ export const getEmployeeById = async (id: string): Promise => { return employee; }; +interface GetProjectListResponse { + projects: Project[]; +} + +export const getProjects = async (): Promise => { + const query = ` + projects { + id + name + } + `; + const { projects } = await graphQLClient.request( + query + ); + return projects; +}; + interface SaveEmployeeResponse { saveEmployee: Employee; } @@ -51,18 +67,18 @@ export const saveEmployee = async (employee: Employee): Promise => { return saveEmployee.id; }; -export const saveProjectSummary = async ( +export const saveEmployeeProjectList = async ( id: string, - projectSummaryList: EmployeeProject[] + employeeProjectList: EmployeeProject[] ): Promise => { - const query = `mutation($projectSummaryList: [ProjectSummaryInput!]!) { - saveProjectSummaryList( + const query = `mutation($employeeProjectList: [EmployeeProjectInput!]!) { + saveEmployeeProjectList( id: "${id}", - projectSummaryList: $projectSummaryList, + employeeProjectList: $employeeProjectList, ) { id } }`; - await graphQLClient.request(query, { projectSummaryList }); + await graphQLClient.request(query, { employeeProjectList }); }; diff --git a/src/pods/employee/components/project.component.tsx b/src/pods/employee/components/project.component.tsx index a1bf80b..d8a5846 100644 --- a/src/pods/employee/components/project.component.tsx +++ b/src/pods/employee/components/project.component.tsx @@ -5,25 +5,25 @@ import { EmployeeRowComponent } from './project-row.component'; import { CommandFooterComponent } from 'common-app/command-footer'; interface Props { - projectSummaryList: EmployeeProject[]; - className?: string; + employeeProjectList: EmployeeProject[]; onSave: (project: EmployeeProject[]) => void; onCancel: () => void; + className?: string; } export const ProjectComponent: React.FunctionComponent = ({ - projectSummaryList, - className, + employeeProjectList, onSave, onCancel, + className, }) => { const [projectList, setProjectList] = React.useState( - projectSummaryList + employeeProjectList ); React.useEffect(() => { - setProjectList(projectSummaryList); - }, [projectSummaryList]); + setProjectList(employeeProjectList); + }, [employeeProjectList]); const handleChangeProject = (id: string) => (project: EmployeeProject) => { const updateProjectList = projectList.map(p => (p.id === id ? project : p)); diff --git a/src/pods/employee/employee.component.tsx b/src/pods/employee/employee.component.tsx index adbd500..6c54de4 100644 --- a/src/pods/employee/employee.component.tsx +++ b/src/pods/employee/employee.component.tsx @@ -49,7 +49,7 @@ export const EmployeeComponent: React.FunctionComponent = ({ { const onLoadEmployee = async () => { try { - const apiEmployee = await trackPromise(getEmployeeById(params.id)); + const [apiProjects, apiEmployee] = await trackPromise( + Promise.all([getProjects(), getEmployeeById(params.id)]) + ); const viewModelEmployee = mapEmployeeFromApiToVm(apiEmployee); setEmployee(viewModelEmployee); } catch (error) { @@ -66,10 +73,12 @@ export const EmployeeContainer: React.FunctionComponent = () => { ) => { if (params.id) { try { - const apiProjectSummary = mapProjectSummaryListFromVmToApi( + const apiProjectSummary = mapEmployeeProjectListFromVmToApi( employeeSummary ); - await trackPromise(saveProjectSummary(params.id, apiProjectSummary)); + await trackPromise( + saveEmployeeProjectList(params.id, apiProjectSummary) + ); setEmployee({ ...employee, projects: employeeSummary, diff --git a/src/pods/employee/employee.mappers.spec.ts b/src/pods/employee/employee.mappers.spec.ts index 8085341..18e0d39 100644 --- a/src/pods/employee/employee.mappers.spec.ts +++ b/src/pods/employee/employee.mappers.spec.ts @@ -90,7 +90,6 @@ describe('./pods/employee/employee.mappers', () => { projects: [ { id: 'test id', - projectName: 'test employee name', isAssigned: true, }, ], @@ -105,7 +104,6 @@ describe('./pods/employee/employee.mappers', () => { projects: [ { id: 'test id', - projectName: 'test employee name', isAssigned: true, }, ], diff --git a/src/pods/employee/employee.mappers.ts b/src/pods/employee/employee.mappers.ts index 68daba8..865f3c1 100644 --- a/src/pods/employee/employee.mappers.ts +++ b/src/pods/employee/employee.mappers.ts @@ -30,14 +30,11 @@ export const mapEmployeeFromVmToApi = ( ...employee, }); -const mapProjectSummaryFromVmToApi = ( - projectSummary: viewModel.EmployeeProject -): apiModel.EmployeeProject => ({ - id: projectSummary.id, - isAssigned: projectSummary.isAssigned, -}); +const mapEmployeeProjectFromVmToApi = ( + employeeProject: viewModel.EmployeeProject +): apiModel.EmployeeProject => ({ ...employeeProject }); -export const mapProjectSummaryListFromVmToApi = ( - projectSummary: viewModel.EmployeeProject[] +export const mapEmployeeProjectListFromVmToApi = ( + employeeProjectList: viewModel.EmployeeProject[] ): apiModel.EmployeeProject[] => - mapToCollection(projectSummary, mapProjectSummaryFromVmToApi); + mapToCollection(employeeProjectList, mapEmployeeProjectFromVmToApi); From 2a6caeea3b38f05c6669b422cbd07c551bc42b7f Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Wed, 24 Jun 2020 14:48:35 +0200 Subject: [PATCH 15/32] Fix query --- src/pods/employee/api/employee.api.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pods/employee/api/employee.api.ts b/src/pods/employee/api/employee.api.ts index 354c5f0..3b521b3 100644 --- a/src/pods/employee/api/employee.api.ts +++ b/src/pods/employee/api/employee.api.ts @@ -33,10 +33,12 @@ interface GetProjectListResponse { export const getProjects = async (): Promise => { const query = ` + query { projects { id name } + } `; const { projects } = await graphQLClient.request( query From 32ed3de41f2302fb01818f8a730f987eb432efd4 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Wed, 24 Jun 2020 16:44:09 +0200 Subject: [PATCH 16/32] Fix view model and mappers --- .../components/project-row.component.tsx | 2 +- src/pods/employee/employee.container.tsx | 5 +++- src/pods/employee/employee.mappers.ts | 27 ++++++++++++++----- src/pods/employee/employee.vm.ts | 1 + 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/pods/employee/components/project-row.component.tsx b/src/pods/employee/components/project-row.component.tsx index 1dee59e..f650ff0 100644 --- a/src/pods/employee/components/project-row.component.tsx +++ b/src/pods/employee/components/project-row.component.tsx @@ -29,7 +29,7 @@ export const EmployeeRowComponent: React.FunctionComponent = ({ checked={row.isAssigned} /> - {''} + {row.name} ); }; diff --git a/src/pods/employee/employee.container.tsx b/src/pods/employee/employee.container.tsx index 0bbe04e..c9e3342 100644 --- a/src/pods/employee/employee.container.tsx +++ b/src/pods/employee/employee.container.tsx @@ -40,7 +40,10 @@ export const EmployeeContainer: React.FunctionComponent = () => { const [apiProjects, apiEmployee] = await trackPromise( Promise.all([getProjects(), getEmployeeById(params.id)]) ); - const viewModelEmployee = mapEmployeeFromApiToVm(apiEmployee); + const viewModelEmployee = mapEmployeeFromApiToVm( + apiEmployee, + apiProjects + ); setEmployee(viewModelEmployee); } catch (error) { error && diff --git a/src/pods/employee/employee.mappers.ts b/src/pods/employee/employee.mappers.ts index 865f3c1..98790f6 100644 --- a/src/pods/employee/employee.mappers.ts +++ b/src/pods/employee/employee.mappers.ts @@ -3,23 +3,29 @@ import * as apiModel from './api/employee.api-model'; import * as viewModel from './employee.vm'; const mapProjectSummaryFromApiToVm = ( - projectSummary: apiModel.EmployeeProject + projectSummary: apiModel.EmployeeProject, + projects: apiModel.Project[] ): viewModel.EmployeeProject => ({ ...projectSummary, + name: projects.find(p => p.id === projectSummary.id).name, }); const mapProjectSummaryListFromApiToVm = ( - projectSummary: apiModel.EmployeeProject[] + projectSummary: apiModel.EmployeeProject[], + projects: apiModel.Project[] ): viewModel.EmployeeProject[] => - mapToCollection(projectSummary, ps => mapProjectSummaryFromApiToVm(ps)); + mapToCollection(projectSummary, ps => + mapProjectSummaryFromApiToVm(ps, projects) + ); export const mapEmployeeFromApiToVm = ( - employee: apiModel.Employee + employee: apiModel.Employee, + projects: apiModel.Project[] ): viewModel.Employee => { return Boolean(employee) ? { ...employee, - projects: mapProjectSummaryListFromApiToVm(employee.projects), + projects: mapProjectSummaryListFromApiToVm(employee.projects, projects), } : viewModel.createEmptyEmployee(); }; @@ -27,12 +33,19 @@ export const mapEmployeeFromApiToVm = ( export const mapEmployeeFromVmToApi = ( employee: viewModel.Employee ): apiModel.Employee => ({ - ...employee, + id: employee.id, + name: employee.name, + email: employee.email, + isActive: employee.isActive, + temporalPassword: employee.temporalPassword, }); const mapEmployeeProjectFromVmToApi = ( employeeProject: viewModel.EmployeeProject -): apiModel.EmployeeProject => ({ ...employeeProject }); +): apiModel.EmployeeProject => ({ + id: employeeProject.id, + isAssigned: employeeProject.isAssigned, +}); export const mapEmployeeProjectListFromVmToApi = ( employeeProjectList: viewModel.EmployeeProject[] diff --git a/src/pods/employee/employee.vm.ts b/src/pods/employee/employee.vm.ts index a4de357..1c6d6b4 100644 --- a/src/pods/employee/employee.vm.ts +++ b/src/pods/employee/employee.vm.ts @@ -10,6 +10,7 @@ export interface Employee { export interface EmployeeProject { id: string; isAssigned?: boolean; + name: string; } export interface Project { From eedf56a173a1c2c0605f65b2397952c10580be58 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Wed, 24 Jun 2020 19:33:43 +0200 Subject: [PATCH 17/32] Add function to employee view model and fix mappers --- src/pods/employee/employee.mappers.ts | 27 ++++++++++++++++----------- src/pods/employee/employee.vm.ts | 6 ++++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/pods/employee/employee.mappers.ts b/src/pods/employee/employee.mappers.ts index 98790f6..abc4c55 100644 --- a/src/pods/employee/employee.mappers.ts +++ b/src/pods/employee/employee.mappers.ts @@ -3,19 +3,24 @@ import * as apiModel from './api/employee.api-model'; import * as viewModel from './employee.vm'; const mapProjectSummaryFromApiToVm = ( - projectSummary: apiModel.EmployeeProject, - projects: apiModel.Project[] -): viewModel.EmployeeProject => ({ - ...projectSummary, - name: projects.find(p => p.id === projectSummary.id).name, -}); + project: apiModel.Project, + employeeProjectList: apiModel.EmployeeProject[] +): viewModel.EmployeeProject => { + const employeeProject = employeeProjectList + ? employeeProjectList.find(ep => ep.id === project.id) + : viewModel.createEmptyEmployeeProject(); + return { + ...project, + isAssigned: employeeProject?.isAssigned, + }; +}; const mapProjectSummaryListFromApiToVm = ( - projectSummary: apiModel.EmployeeProject[], - projects: apiModel.Project[] + project: apiModel.Project[], + employeeProject: apiModel.EmployeeProject[] ): viewModel.EmployeeProject[] => - mapToCollection(projectSummary, ps => - mapProjectSummaryFromApiToVm(ps, projects) + mapToCollection(project, p => + mapProjectSummaryFromApiToVm(p, employeeProject) ); export const mapEmployeeFromApiToVm = ( @@ -25,7 +30,7 @@ export const mapEmployeeFromApiToVm = ( return Boolean(employee) ? { ...employee, - projects: mapProjectSummaryListFromApiToVm(employee.projects, projects), + projects: mapProjectSummaryListFromApiToVm(projects, employee.projects), } : viewModel.createEmptyEmployee(); }; diff --git a/src/pods/employee/employee.vm.ts b/src/pods/employee/employee.vm.ts index 1c6d6b4..3fa7aac 100644 --- a/src/pods/employee/employee.vm.ts +++ b/src/pods/employee/employee.vm.ts @@ -37,3 +37,9 @@ export const createEmptyReport = (): Report => ({ month: '', year: '', }); + +export const createEmptyEmployeeProject = (): EmployeeProject => ({ + id: '', + name: '', + isAssigned: false, +}); From cd89bb5780d9e3c12cee082b8b7b439b0cfe10bd Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Wed, 24 Jun 2020 19:57:57 +0200 Subject: [PATCH 18/32] fix unit test --- src/pods/employee/employee.mappers.spec.ts | 85 +++++++++++++++++++--- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/src/pods/employee/employee.mappers.spec.ts b/src/pods/employee/employee.mappers.spec.ts index 18e0d39..d67c497 100644 --- a/src/pods/employee/employee.mappers.spec.ts +++ b/src/pods/employee/employee.mappers.spec.ts @@ -6,9 +6,10 @@ describe('./pods/employee/employee.mappers', () => { it('should return empty employee when feeding null value', () => { // Arrange const employee = null; + const projects = null; // Act - const result = mapEmployeeFromApiToVm(employee); + const result = mapEmployeeFromApiToVm(employee, projects); // Assert expect(result).toEqual(viewModel.createEmptyEmployee()); @@ -17,9 +18,10 @@ describe('./pods/employee/employee.mappers', () => { it('should return empty employee when feeding undefined value', () => { // Arrange const employee = undefined; + const projects = undefined; // Act - const result = mapEmployeeFromApiToVm(employee); + const result = mapEmployeeFromApiToVm(employee, projects); // Assert expect(result).toEqual(viewModel.createEmptyEmployee()); @@ -36,17 +38,39 @@ describe('./pods/employee/employee.mappers', () => { projects: null, }; + const projects: apiModel.Project[] = [ + { + id: '1', + name: 'project test 1', + }, + { + id: '2', + name: 'project test 2', + }, + ]; + const expectedResult: viewModel.Employee = { id: 'test id', name: 'test name', email: 'test@email.com', isActive: true, temporalPassword: 'test password', - projects: [], + projects: [ + { + id: '1', + name: 'project test 1', + isAssigned: false, + }, + { + id: '2', + name: 'project test 2', + isAssigned: false, + }, + ], }; // Act - const result = mapEmployeeFromApiToVm(employee); + const result = mapEmployeeFromApiToVm(employee, projects); // Assert expect(result).toEqual(expectedResult); @@ -69,11 +93,33 @@ describe('./pods/employee/employee.mappers', () => { email: 'test@email.com', isActive: true, temporalPassword: 'test password', - projects: [], + projects: [ + { + id: '1', + name: 'project test 1', + isAssigned: false, + }, + { + id: '2', + name: 'project test 2', + isAssigned: false, + }, + ], }; + const projects: apiModel.Project[] = [ + { + id: '1', + name: 'project test 1', + }, + { + id: '2', + name: 'project test 2', + }, + ]; + // Act - const result = mapEmployeeFromApiToVm(employee); + const result = mapEmployeeFromApiToVm(employee, projects); // Assert expect(result).toEqual(expectedResult); @@ -89,12 +135,27 @@ describe('./pods/employee/employee.mappers', () => { temporalPassword: 'test password', projects: [ { - id: 'test id', + id: '1', isAssigned: true, }, + { + id: '2', + isAssigned: false, + }, ], }; + const projects: apiModel.Project[] = [ + { + id: '1', + name: 'project test 1', + }, + { + id: '2', + name: 'project test 2', + }, + ]; + const expectedResult: viewModel.Employee = { id: 'test id', name: 'test name', @@ -103,14 +164,20 @@ describe('./pods/employee/employee.mappers', () => { temporalPassword: 'test password', projects: [ { - id: 'test id', + id: '1', isAssigned: true, + name: 'project test 1', + }, + { + id: '2', + isAssigned: false, + name: 'project test 2', }, ], }; // Act - const result = mapEmployeeFromApiToVm(employee); + const result = mapEmployeeFromApiToVm(employee, projects); // Assert expect(result).toEqual(expectedResult); From 3e7044c12ddba0ea29c1521f63388d7412f7e40b Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Thu, 25 Jun 2020 09:35:38 +0200 Subject: [PATCH 19/32] Add others unit test --- src/pods/employee/employee.mappers.spec.ts | 423 ++++++++++++++------- src/pods/employee/employee.mappers.ts | 18 +- 2 files changed, 303 insertions(+), 138 deletions(-) diff --git a/src/pods/employee/employee.mappers.spec.ts b/src/pods/employee/employee.mappers.spec.ts index d67c497..01d3fcf 100644 --- a/src/pods/employee/employee.mappers.spec.ts +++ b/src/pods/employee/employee.mappers.spec.ts @@ -1,185 +1,346 @@ -import { mapEmployeeFromApiToVm } from './employee.mappers'; +import { + mapEmployeeFromApiToVm, + mapEmployeeFromVmToApi, + mapEmployeeProjectListFromVmToApi, +} from './employee.mappers'; import * as apiModel from './api/employee.api-model'; import * as viewModel from './employee.vm'; describe('./pods/employee/employee.mappers', () => { - it('should return empty employee when feeding null value', () => { - // Arrange - const employee = null; - const projects = null; + describe('mapEmployeeFromApiToVm', () => { + it('should return empty employee when feeding null value', () => { + // Arrange + const employee = null; + const projects = null; - // Act - const result = mapEmployeeFromApiToVm(employee, projects); + // Act + const result = mapEmployeeFromApiToVm(employee, projects); - // Assert - expect(result).toEqual(viewModel.createEmptyEmployee()); - }); + // Assert + expect(result).toEqual(viewModel.createEmptyEmployee()); + }); - it('should return empty employee when feeding undefined value', () => { - // Arrange - const employee = undefined; - const projects = undefined; + it('should return empty employee when feeding undefined value', () => { + // Arrange + const employee = undefined; + const projects = undefined; - // Act - const result = mapEmployeeFromApiToVm(employee, projects); + // Act + const result = mapEmployeeFromApiToVm(employee, projects); - // Assert - expect(result).toEqual(viewModel.createEmptyEmployee()); - }); + // Assert + expect(result).toEqual(viewModel.createEmptyEmployee()); + }); - it('should return expected result but feeding null project list', () => { - // Arrange - const employee: apiModel.Employee = { - id: 'test id', - name: 'test name', - email: 'test@email.com', - isActive: true, - temporalPassword: 'test password', - projects: null, - }; - - const projects: apiModel.Project[] = [ - { - id: '1', - name: 'project test 1', - }, - { - id: '2', - name: 'project test 2', - }, - ]; - - const expectedResult: viewModel.Employee = { - id: 'test id', - name: 'test name', - email: 'test@email.com', - isActive: true, - temporalPassword: 'test password', - projects: [ + it('should return expected result but feeding null project list', () => { + // Arrange + const employee: apiModel.Employee = { + id: 'test id', + name: 'test name', + email: 'test@email.com', + isActive: true, + temporalPassword: 'test password', + projects: null, + }; + + const projects: apiModel.Project[] = [ { id: '1', name: 'project test 1', - isAssigned: false, }, { id: '2', name: 'project test 2', - isAssigned: false, }, - ], - }; + ]; - // Act - const result = mapEmployeeFromApiToVm(employee, projects); + const expectedResult: viewModel.Employee = { + id: 'test id', + name: 'test name', + email: 'test@email.com', + isActive: true, + temporalPassword: 'test password', + projects: [ + { + id: '1', + name: 'project test 1', + isAssigned: false, + }, + { + id: '2', + name: 'project test 2', + isAssigned: false, + }, + ], + }; - // Assert - expect(result).toEqual(expectedResult); - }); + // Act + const result = mapEmployeeFromApiToVm(employee, projects); - it('should return expected result but feeding undefined project list', () => { - // Arrange - const employee: apiModel.Employee = { - id: 'test id', - name: 'test name', - email: 'test@email.com', - isActive: true, - temporalPassword: 'test password', - projects: undefined, - }; - - const expectedResult: viewModel.Employee = { - id: 'test id', - name: 'test name', - email: 'test@email.com', - isActive: true, - temporalPassword: 'test password', - projects: [ + // Assert + expect(result).toEqual(expectedResult); + }); + + it('should return expected result but feeding undefined project list', () => { + // Arrange + const employee: apiModel.Employee = { + id: 'test id', + name: 'test name', + email: 'test@email.com', + isActive: true, + temporalPassword: 'test password', + projects: undefined, + }; + + const expectedResult: viewModel.Employee = { + id: 'test id', + name: 'test name', + email: 'test@email.com', + isActive: true, + temporalPassword: 'test password', + projects: [ + { + id: '1', + name: 'project test 1', + isAssigned: false, + }, + { + id: '2', + name: 'project test 2', + isAssigned: false, + }, + ], + }; + + const projects: apiModel.Project[] = [ + { + id: '1', + name: 'project test 1', + }, + { + id: '2', + name: 'project test 2', + }, + ]; + + // Act + const result = mapEmployeeFromApiToVm(employee, projects); + + // Assert + expect(result).toEqual(expectedResult); + }); + + it('should return expected result feeding correct values', () => { + // Arrange + const employee: apiModel.Employee = { + id: 'test id', + name: 'test name', + email: 'test@email.com', + isActive: true, + temporalPassword: 'test password', + projects: [ + { + id: '1', + isAssigned: true, + }, + { + id: '2', + isAssigned: false, + }, + ], + }; + + const projects: apiModel.Project[] = [ { id: '1', name: 'project test 1', - isAssigned: false, }, { id: '2', name: 'project test 2', - isAssigned: false, }, - ], - }; + ]; + + const expectedResult: viewModel.Employee = { + id: 'test id', + name: 'test name', + email: 'test@email.com', + isActive: true, + temporalPassword: 'test password', + projects: [ + { + id: '1', + isAssigned: true, + name: 'project test 1', + }, + { + id: '2', + isAssigned: false, + name: 'project test 2', + }, + ], + }; + + // Act + const result = mapEmployeeFromApiToVm(employee, projects); + + // Assert + expect(result).toEqual(expectedResult); + }); + }); - const projects: apiModel.Project[] = [ - { + describe('mapEmployeeFromVmToApi', () => { + it('should return empty employee when feeding null', () => { + // Arrange + const employee = null; + + // Act + const result = mapEmployeeFromVmToApi(employee); + + //Assert + expect(result).toEqual(viewModel.createEmptyEmployee()); + }); + + it('should return empty employee when feeding undefined', () => { + // Arrange + const employee = undefined; + + // Act + const result = mapEmployeeFromVmToApi(employee); + + //Assert + expect(result).toEqual(viewModel.createEmptyEmployee()); + }); + + it('should return expected value when feeding employee, and empty project list', () => { + // Arrange + const employee: viewModel.Employee = { + id: '1', + isActive: true, + name: 'test name', + email: 'test@email.com', + temporalPassword: 'test', + projects: [], + }; + + const expectedValue: apiModel.Employee = { + id: '1', + isActive: true, + name: 'test name', + email: 'test@email.com', + temporalPassword: 'test', + }; + + // Act + const result = mapEmployeeFromVmToApi(employee); + + //Assert + expect(result).toEqual(expectedValue); + }); + + it('should return expected value when feeding employee, and project list', () => { + // Arrange + const employee: viewModel.Employee = { + id: '1', + isActive: true, + name: 'test name', + email: 'test@email.com', + temporalPassword: 'test', + projects: [ + { + id: '1', + isAssigned: true, + name: 'test name', + }, + { + id: '2', + isAssigned: false, + name: 'test name', + }, + ], + }; + + const expectedValue: apiModel.Employee = { id: '1', - name: 'project test 1', - }, - { - id: '2', - name: 'project test 2', - }, - ]; - - // Act - const result = mapEmployeeFromApiToVm(employee, projects); - - // Assert - expect(result).toEqual(expectedResult); + isActive: true, + name: 'test name', + email: 'test@email.com', + temporalPassword: 'test', + }; + + // Act + const result = mapEmployeeFromVmToApi(employee); + + //Assert + expect(result).toEqual(expectedValue); + }); }); - it('should return expected result feeding correct values', () => { - // Arrange - const employee: apiModel.Employee = { - id: 'test id', - name: 'test name', - email: 'test@email.com', - isActive: true, - temporalPassword: 'test password', - projects: [ + describe('mapEmployeeProjectListFromVmToApi', () => { + it('should return emtpy array when feeding employee project null', () => { + // Arrange + const employeeProjectList = null; + + // Act + const result = mapEmployeeProjectListFromVmToApi(employeeProjectList); + + // Assert + expect(result).toEqual([]); + }); + + it('should return emtpy array when feeding employee project undefined', () => { + // Arrange + const employeeProjectList = undefined; + + // Act + const result = mapEmployeeProjectListFromVmToApi(employeeProjectList); + + // Assert + expect(result).toEqual([]); + }); + + it('should return emtpy array when feeding employee project empty array', () => { + // Arrange + const employeeProjectList = []; + + // Act + const result = mapEmployeeProjectListFromVmToApi(employeeProjectList); + + // Assert + expect(result).toEqual([]); + }); + + it('should return expected result when feeding employee project list', () => { + // Arrange + const employeeProjectList: viewModel.EmployeeProject[] = [ { id: '1', + name: 'test name', isAssigned: true, }, { id: '2', + name: 'test name', isAssigned: false, }, - ], - }; + ]; - const projects: apiModel.Project[] = [ - { - id: '1', - name: 'project test 1', - }, - { - id: '2', - name: 'project test 2', - }, - ]; - - const expectedResult: viewModel.Employee = { - id: 'test id', - name: 'test name', - email: 'test@email.com', - isActive: true, - temporalPassword: 'test password', - projects: [ + const expectedResult: apiModel.EmployeeProject[] = [ { id: '1', isAssigned: true, - name: 'project test 1', }, { id: '2', isAssigned: false, - name: 'project test 2', }, - ], - }; + ]; - // Act - const result = mapEmployeeFromApiToVm(employee, projects); + // Act + const result = mapEmployeeProjectListFromVmToApi(employeeProjectList); - // Assert - expect(result).toEqual(expectedResult); + // Assert + expect(result).toEqual(expectedResult); + }); }); }); diff --git a/src/pods/employee/employee.mappers.ts b/src/pods/employee/employee.mappers.ts index abc4c55..2f7d128 100644 --- a/src/pods/employee/employee.mappers.ts +++ b/src/pods/employee/employee.mappers.ts @@ -37,13 +37,17 @@ export const mapEmployeeFromApiToVm = ( export const mapEmployeeFromVmToApi = ( employee: viewModel.Employee -): apiModel.Employee => ({ - id: employee.id, - name: employee.name, - email: employee.email, - isActive: employee.isActive, - temporalPassword: employee.temporalPassword, -}); +): apiModel.Employee => { + return Boolean(employee) + ? { + id: employee.id, + name: employee.name, + email: employee.email, + isActive: employee.isActive, + temporalPassword: employee.temporalPassword, + } + : viewModel.createEmptyEmployee(); +}; const mapEmployeeProjectFromVmToApi = ( employeeProject: viewModel.EmployeeProject From 123e5b50cd693880595e94bfc5a8cb68642c14c7 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Thu, 25 Jun 2020 13:02:56 +0200 Subject: [PATCH 20/32] Fix column name --- src/pods/employee/components/project.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pods/employee/components/project.component.tsx b/src/pods/employee/components/project.component.tsx index d8a5846..b9719d2 100644 --- a/src/pods/employee/components/project.component.tsx +++ b/src/pods/employee/components/project.component.tsx @@ -34,7 +34,7 @@ export const ProjectComponent: React.FunctionComponent = ({ return ( <> ) => ( From 22f6251a05cc8bfc6d2c29fc1f41c921d33cf309 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Thu, 25 Jun 2020 13:08:59 +0200 Subject: [PATCH 21/32] Delete lastDateIncurred property from employee api --- src/pods/employee/api/employee.api.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pods/employee/api/employee.api.ts b/src/pods/employee/api/employee.api.ts index 3b521b3..91558b4 100644 --- a/src/pods/employee/api/employee.api.ts +++ b/src/pods/employee/api/employee.api.ts @@ -13,7 +13,6 @@ export const getEmployeeById = async (id: string): Promise => { name isActive email - lastDateIncurred projects { id isAssigned From a59b4defc905192d1afe4ae6817662406b1c3731 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Thu, 25 Jun 2020 13:35:39 +0200 Subject: [PATCH 22/32] Remove Project view model interface --- src/pods/employee/employee.vm.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/pods/employee/employee.vm.ts b/src/pods/employee/employee.vm.ts index 3fa7aac..f8229c6 100644 --- a/src/pods/employee/employee.vm.ts +++ b/src/pods/employee/employee.vm.ts @@ -13,12 +13,6 @@ export interface EmployeeProject { name: string; } -export interface Project { - id: string; - isAssigned?: boolean; - name: string; -} - export interface Report { month: string; year: string; From 3d4f9f1ea8e5cb5f8690f68ca17f5e8ed5f1465a Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Thu, 25 Jun 2020 19:15:08 +0200 Subject: [PATCH 23/32] Fix view model and others methods and remove project list mock data --- src/pods/project-list/api/project-list.api.ts | 38 +++++++++-- .../api/project-list.mock-data.ts | 68 ------------------- src/pods/project/api/employee.api.ts | 6 -- src/pods/project/api/index.ts | 2 +- src/pods/project/api/project.api-model.ts | 10 ++- src/pods/project/api/project.api.ts | 46 +++++++++++++ src/pods/project/api/project.mock-data.ts | 8 +-- .../components/employee-row.component.tsx | 6 +- .../project/components/employee.component.tsx | 6 +- src/pods/project/project.container.tsx | 8 ++- src/pods/project/project.mapper.ts | 32 ++++++--- src/pods/project/project.vm.ts | 14 +++- 12 files changed, 134 insertions(+), 110 deletions(-) delete mode 100644 src/pods/project-list/api/project-list.mock-data.ts delete mode 100644 src/pods/project/api/employee.api.ts create mode 100644 src/pods/project/api/project.api.ts diff --git a/src/pods/project-list/api/project-list.api.ts b/src/pods/project-list/api/project-list.api.ts index 801356b..3dcbd04 100644 --- a/src/pods/project-list/api/project-list.api.ts +++ b/src/pods/project-list/api/project-list.api.ts @@ -1,13 +1,41 @@ +import { graphQLClient } from 'core/api'; import { Project } from './project-list.api-model'; -import { mockProjectList } from './project-list.mock-data'; -let projectList = [...mockProjectList]; +interface GetProjectListReponse { + projects: Project[]; +} export const getProjectList = async (): Promise => { - return projectList; + const query = ` + query { + projects { + id + isActive + code + name + lastDateIncurred + creationDate + } + } + `; + const { projects } = await graphQLClient.request( + query + ); + return projects; }; +interface DeleteProjectResponse { + deleteProject: boolean; +} + export const deleteProject = async (id: string): Promise => { - projectList = projectList.filter(p => p.id !== id); - return true; + const query = ` + mutation { + deleteProject(id: "${id}") + } + `; + const { deleteProject } = await graphQLClient.request( + query + ); + return deleteProject; }; diff --git a/src/pods/project-list/api/project-list.mock-data.ts b/src/pods/project-list/api/project-list.mock-data.ts deleted file mode 100644 index 243cf29..0000000 --- a/src/pods/project-list/api/project-list.mock-data.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Project } from './project-list.api-model'; - -export const mockProjectList: Project[] = [ - { - id: '1', - isActive: true, - code: '23212', - name: 'Bankia', - lastDateIncurred: '02/02/2020', - creationDate: '01/08/2018', - }, - { - id: '2', - isActive: true, - code: '4323', - name: 'Mapfre', - lastDateIncurred: '05/02/2020', - creationDate: '01/04/2018', - }, - { - id: '3', - isActive: true, - code: '002', - name: 'Vacaciones', - lastDateIncurred: '05/02/2020', - creationDate: '01/04/2018', - }, - { - id: '4', - isActive: true, - code: '003', - name: 'Baja Médica', - lastDateIncurred: '05/03/2018', - creationDate: '01/05/2019', - }, - { - id: '5', - isActive: false, - code: '2586', - name: 'Proyecto interno', - lastDateIncurred: '05/08/2020', - creationDate: '01/10/2018', - }, - { - id: '6', - isActive: false, - code: '3025', - name: 'BBVA', - lastDateIncurred: '06/05/2020', - creationDate: '01/03/2019', - }, - { - id: '7', - isActive: false, - code: '8563', - name: 'Baja Médica', - lastDateIncurred: '02/08/2018', - creationDate: '01/11/2020', - }, - { - id: '8', - isActive: true, - code: '4125', - name: 'Microsoft España', - lastDateIncurred: '11/10/2018', - creationDate: '01/07/2020', - }, -]; diff --git a/src/pods/project/api/employee.api.ts b/src/pods/project/api/employee.api.ts deleted file mode 100644 index 3d08210..0000000 --- a/src/pods/project/api/employee.api.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Project } from './project.api-model'; -import { mockProject } from './project.mock-data'; - -export const getProjectById = async (id: string): Promise => { - return mockProject; -}; diff --git a/src/pods/project/api/index.ts b/src/pods/project/api/index.ts index aea94bb..e38a565 100644 --- a/src/pods/project/api/index.ts +++ b/src/pods/project/api/index.ts @@ -1 +1 @@ -export * from './employee.api'; +export * from './project.api'; diff --git a/src/pods/project/api/project.api-model.ts b/src/pods/project/api/project.api-model.ts index 1927eff..5e4e947 100644 --- a/src/pods/project/api/project.api-model.ts +++ b/src/pods/project/api/project.api-model.ts @@ -4,11 +4,15 @@ export interface Project { externalId?: string; comments?: string; isActive: boolean; - employees: EmployeeSummary[]; + employees: ProjectEmployee[]; } -export interface EmployeeSummary { +export interface ProjectEmployee { id: string; isAssigned?: boolean; - employeeName: string; +} + +export interface Employee { + id: string; + name: string; } diff --git a/src/pods/project/api/project.api.ts b/src/pods/project/api/project.api.ts new file mode 100644 index 0000000..8998d71 --- /dev/null +++ b/src/pods/project/api/project.api.ts @@ -0,0 +1,46 @@ +import { Project, Employee } from './project.api-model'; +import { graphQLClient } from 'core/api'; + +interface GetProjectResponse { + project: Project; +} + +export const getProjectById = async (id: string): Promise => { + const query = ` + query { + project(id: "${id}") { + id + name + isActive + externalId + comments + employees { + id + isAssigned + } + } + } + `; + const { project } = await graphQLClient.request(query); + + return project; +}; + +interface GetEmployeeListResponse { + employees: Employee[]; +} + +export const getEmployees = async (): Promise => { + const query = ` + query { + employees { + id + name + } + } + `; + const { employees } = await graphQLClient.request( + query + ); + return employees; +}; diff --git a/src/pods/project/api/project.mock-data.ts b/src/pods/project/api/project.mock-data.ts index aa5e4c6..f9ace0e 100644 --- a/src/pods/project/api/project.mock-data.ts +++ b/src/pods/project/api/project.mock-data.ts @@ -1,24 +1,20 @@ -import { Project, EmployeeSummary } from './project.api-model'; +import { Project, ProjectEmployee } from './project.api-model'; -const mockEmployeeSummaryList: EmployeeSummary[] = [ +const mockEmployeeSummaryList: ProjectEmployee[] = [ { id: '1', - employeeName: 'Daniel Perez', isAssigned: true, }, { id: '2', - employeeName: 'Jose Sanchez', isAssigned: false, }, { id: '3', - employeeName: 'Javier Benitez', isAssigned: false, }, { id: '4', - employeeName: 'María Peña', isAssigned: true, }, ]; diff --git a/src/pods/project/components/employee-row.component.tsx b/src/pods/project/components/employee-row.component.tsx index f653662..907e7be 100644 --- a/src/pods/project/components/employee-row.component.tsx +++ b/src/pods/project/components/employee-row.component.tsx @@ -5,9 +5,9 @@ import { CellComponent, } from 'common/components'; import Checkbox from '@material-ui/core/Checkbox'; -import { EmployeeSummary } from '../project.vm'; +import { ProjectEmployee } from '../project.vm'; -type Props = RowRendererProps; +type Props = RowRendererProps; export const ProjectRowComponent: React.FunctionComponent = ({ row, @@ -17,7 +17,7 @@ export const ProjectRowComponent: React.FunctionComponent = ({ - {row.employeeName} + {row.name} ); }; diff --git a/src/pods/project/components/employee.component.tsx b/src/pods/project/components/employee.component.tsx index 6c2ec95..735b400 100644 --- a/src/pods/project/components/employee.component.tsx +++ b/src/pods/project/components/employee.component.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { TableContainer, RowRendererProps } from 'common/components'; -import { EmployeeSummary } from '../project.vm'; +import { ProjectEmployee } from '../project.vm'; import { CommandFooterComponent } from 'common-app/command-footer'; import { ProjectRowComponent } from './employee-row.component'; interface Props { - employeeSummaryList: EmployeeSummary[]; + employeeSummaryList: ProjectEmployee[]; onCancel: () => void; className: string; } @@ -21,7 +21,7 @@ export const EmployeeComponent: React.FunctionComponent = ({ columns={['Asignado', 'Empleado']} rows={employeeSummaryList} className={className} - rowRenderer={(rowProps: RowRendererProps) => ( + rowRenderer={(rowProps: RowRendererProps) => ( )} /> diff --git a/src/pods/project/project.container.tsx b/src/pods/project/project.container.tsx index 4a5f417..3a7b90a 100644 --- a/src/pods/project/project.container.tsx +++ b/src/pods/project/project.container.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { ProjectComponent } from './project.component'; import { useParams } from 'react-router-dom'; import { useSnackbarContext } from 'common/components'; -import { getProjectById } from './api'; +import { getProjectById, getEmployees } from './api'; import { trackPromise } from 'react-promise-tracker'; import { mapProjectFromApiToVm } from './project.mapper'; import { @@ -22,8 +22,10 @@ export const ProjectContainer: React.FunctionComponent = () => { const onLoadProject = async () => { try { - const apiProject = await trackPromise(getProjectById(id)); - const viewModelProject = mapProjectFromApiToVm(apiProject); + const [apiEmployees, apiProject] = await trackPromise( + Promise.all([getEmployees(), getProjectById(id)]) + ); + const viewModelProject = mapProjectFromApiToVm(apiProject, apiEmployees); setProject(viewModelProject); } catch (error) { error && diff --git a/src/pods/project/project.mapper.ts b/src/pods/project/project.mapper.ts index 68561f6..9f727b4 100644 --- a/src/pods/project/project.mapper.ts +++ b/src/pods/project/project.mapper.ts @@ -3,23 +3,37 @@ import * as apiModel from './api/project.api-model'; import * as viewModel from './project.vm'; const mapEmployeeSummaryFromApiToVm = ( - employeeSummary: apiModel.EmployeeSummary -): viewModel.EmployeeSummary => ({ - ...employeeSummary, -}); + employee: apiModel.Employee, + projectEmployeeList: apiModel.ProjectEmployee[] +): viewModel.ProjectEmployee => { + const projectEmployee = projectEmployeeList + ? projectEmployeeList.find(pe => pe.id === employee.id) + : viewModel.createEmptyProjectEmployee(); + return { + ...employee, + isAssigned: projectEmployee?.isAssigned, + }; +}; const mapEmployeeSummaryListFromApiToVm = ( - employeeSummary: apiModel.EmployeeSummary[] -): viewModel.EmployeeSummary[] => - mapToCollection(employeeSummary, es => mapEmployeeSummaryFromApiToVm(es)); + employees: apiModel.Employee[], + projectEmployees: apiModel.ProjectEmployee[] +): viewModel.ProjectEmployee[] => + mapToCollection(employees, e => + mapEmployeeSummaryFromApiToVm(e, projectEmployees) + ); export const mapProjectFromApiToVm = ( - project: apiModel.Project + project: apiModel.Project, + employees: apiModel.Employee[] ): viewModel.Project => { return Boolean(project) ? { ...project, - employees: mapEmployeeSummaryListFromApiToVm(project.employees), + employees: mapEmployeeSummaryListFromApiToVm( + employees, + project.employees + ), } : viewModel.createEmptyProject(); }; diff --git a/src/pods/project/project.vm.ts b/src/pods/project/project.vm.ts index 6cfc458..de71271 100644 --- a/src/pods/project/project.vm.ts +++ b/src/pods/project/project.vm.ts @@ -1,16 +1,18 @@ +import { Employee } from 'pods/employee-list/employee-list.vm'; + export interface Project { id: string; name: string; externalId?: string; comments?: string; isActive: boolean; - employees: EmployeeSummary[]; + employees: ProjectEmployee[]; } -export interface EmployeeSummary { +export interface ProjectEmployee { id: string; isAssigned?: boolean; - employeeName: string; + name: string; } export interface Report { @@ -31,3 +33,9 @@ export const createEmptyReport = (): Report => ({ month: '', year: '', }); + +export const createEmptyProjectEmployee = (): ProjectEmployee => ({ + id: '', + name: '', + isAssigned: false, +}); From 5c537d9baf1536bd6665742d925c60fcda2ed91c Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Thu, 25 Jun 2020 19:27:09 +0200 Subject: [PATCH 24/32] Fix names --- src/pods/project/components/employee.component.tsx | 6 +++--- src/pods/project/project.component.tsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pods/project/components/employee.component.tsx b/src/pods/project/components/employee.component.tsx index 735b400..56f2f67 100644 --- a/src/pods/project/components/employee.component.tsx +++ b/src/pods/project/components/employee.component.tsx @@ -5,13 +5,13 @@ import { CommandFooterComponent } from 'common-app/command-footer'; import { ProjectRowComponent } from './employee-row.component'; interface Props { - employeeSummaryList: ProjectEmployee[]; + projectEmployeeList: ProjectEmployee[]; onCancel: () => void; className: string; } export const EmployeeComponent: React.FunctionComponent = ({ - employeeSummaryList, + projectEmployeeList, onCancel, className, }) => { @@ -19,7 +19,7 @@ export const EmployeeComponent: React.FunctionComponent = ({ <> ) => ( diff --git a/src/pods/project/project.component.tsx b/src/pods/project/project.component.tsx index 6d040ed..cfcc0dd 100644 --- a/src/pods/project/project.component.tsx +++ b/src/pods/project/project.component.tsx @@ -10,7 +10,7 @@ import { EmployeeComponent, ReportComponent, } from './components'; -import { Project } from './project.vm'; +import { Project, Report } from './project.vm'; import * as classes from './project.styles'; interface Props { @@ -50,7 +50,7 @@ export const ProjectComponent: React.FunctionComponent = ({ From ce3a4c39066051b29cd3f8ce51b169002f3b35b5 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Thu, 25 Jun 2020 19:30:11 +0200 Subject: [PATCH 25/32] Fix a import --- src/pods/project/project.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pods/project/project.component.tsx b/src/pods/project/project.component.tsx index 6d040ed..335a2c0 100644 --- a/src/pods/project/project.component.tsx +++ b/src/pods/project/project.component.tsx @@ -10,7 +10,7 @@ import { EmployeeComponent, ReportComponent, } from './components'; -import { Project } from './project.vm'; +import { Project, Report } from './project.vm'; import * as classes from './project.styles'; interface Props { From facc4fe80b0f3887591451d73a5f11706389afb1 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Fri, 26 Jun 2020 09:12:36 +0200 Subject: [PATCH 26/32] fix name, from Project to project --- src/pods/project/project.container.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pods/project/project.container.tsx b/src/pods/project/project.container.tsx index 4a5f417..ec278d1 100644 --- a/src/pods/project/project.container.tsx +++ b/src/pods/project/project.container.tsx @@ -31,7 +31,7 @@ export const ProjectContainer: React.FunctionComponent = () => { } }; - const handleSave = (Project: Project) => { + const handleSave = (project: Project) => { console.log('Guardado'); }; From 844f2e6c2e6b7c7064ac0cbd3abb6913fca01050 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Fri, 26 Jun 2020 10:53:35 +0200 Subject: [PATCH 27/32] Implement save project function and mappers and fix project view model --- src/pods/project/api/project.api-model.ts | 2 +- src/pods/project/api/project.api.ts | 24 ++++++++++++++++++ src/pods/project/project.component.tsx | 6 ++--- src/pods/project/project.container.tsx | 30 +++++++++++++++++++---- src/pods/project/project.mapper.ts | 14 +++++++++++ src/pods/project/project.vm.ts | 2 -- 6 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/pods/project/api/project.api-model.ts b/src/pods/project/api/project.api-model.ts index 5e4e947..1b735cb 100644 --- a/src/pods/project/api/project.api-model.ts +++ b/src/pods/project/api/project.api-model.ts @@ -4,7 +4,7 @@ export interface Project { externalId?: string; comments?: string; isActive: boolean; - employees: ProjectEmployee[]; + employees?: ProjectEmployee[]; } export interface ProjectEmployee { diff --git a/src/pods/project/api/project.api.ts b/src/pods/project/api/project.api.ts index 8998d71..bb22bcd 100644 --- a/src/pods/project/api/project.api.ts +++ b/src/pods/project/api/project.api.ts @@ -1,5 +1,6 @@ import { Project, Employee } from './project.api-model'; import { graphQLClient } from 'core/api'; +import { ProjectEmployee } from '../project.vm'; interface GetProjectResponse { project: Project; @@ -44,3 +45,26 @@ export const getEmployees = async (): Promise => { ); return employees; }; + +interface SaveProjectResponse { + saveProject: Project; +} + +export const saveProject = async (project: Project): Promise => { + const query = ` + mutation($project: ProjectInput!) { + saveProject(project: $project) { + id + } + } + `; + + const { saveProject } = await graphQLClient.request( + query, + { + project, + } + ); + + return saveProject.id; +}; diff --git a/src/pods/project/project.component.tsx b/src/pods/project/project.component.tsx index cfcc0dd..4e555a0 100644 --- a/src/pods/project/project.component.tsx +++ b/src/pods/project/project.component.tsx @@ -18,7 +18,7 @@ interface Props { project: Project; report: Report; onGenerateExcel: (report: Report) => void; - onSave: (project: Project) => void; + onSaveProject: (project: Project) => void; onCancel: () => void; } @@ -27,7 +27,7 @@ export const ProjectComponent: React.FunctionComponent = ({ project, report, onGenerateExcel, - onSave, + onSaveProject, onCancel, }) => { const [tab, setTab] = React.useState(0); @@ -44,7 +44,7 @@ export const ProjectComponent: React.FunctionComponent = ({ diff --git a/src/pods/project/project.container.tsx b/src/pods/project/project.container.tsx index 9a4b4d7..4269122 100644 --- a/src/pods/project/project.container.tsx +++ b/src/pods/project/project.container.tsx @@ -4,7 +4,8 @@ import { useParams } from 'react-router-dom'; import { useSnackbarContext } from 'common/components'; import { getProjectById, getEmployees } from './api'; import { trackPromise } from 'react-promise-tracker'; -import { mapProjectFromApiToVm } from './project.mapper'; +import { saveProject } from './api'; +import { mapProjectFromApiToVm, mapProjectFromVmToApi } from './project.mapper'; import { Project, createEmptyProject, @@ -12,6 +13,8 @@ import { createEmptyReport, } from './project.vm'; import { isEditModeHelper } from 'common/helpers'; +import { useHistory } from 'react-router'; +import { routes } from 'core/router'; export const ProjectContainer: React.FunctionComponent = () => { const { id } = useParams(); @@ -19,6 +22,7 @@ export const ProjectContainer: React.FunctionComponent = () => { const [isEditMode, setIsEditMode] = React.useState(false); const [report, setReport] = React.useState(createEmptyReport()); const { showMessage } = useSnackbarContext(); + const history = useHistory(); const onLoadProject = async () => { try { @@ -33,12 +37,28 @@ export const ProjectContainer: React.FunctionComponent = () => { } }; - const handleSave = (project: Project) => { - console.log('Guardado'); + const handleSuccessSaveProject = (id: string, newProject: Project) => { + if (id) { + showMessage('Proyecto guardado con éxito', 'success'); + setProject(newProject); + history.push(routes.editProject(id)); + } else { + showMessage('Ha ocurrido un error al guardar el empleado', 'error'); + } + }; + + const handleSaveProject = async (project: Project) => { + try { + const apiProject = mapProjectFromVmToApi(project); + const id = await trackPromise(saveProject(apiProject)); + handleSuccessSaveProject(id, project); + } catch (error) { + error && showMessage(error.message, 'error'); + } }; const handleCancel = () => { - history.back(); + history.goBack(); }; const handleGenerateExcel = (report: Report) => { @@ -59,7 +79,7 @@ export const ProjectContainer: React.FunctionComponent = () => { isEditMode={isEditMode} project={project} report={report} - onSave={handleSave} + onSaveProject={handleSaveProject} onCancel={handleCancel} onGenerateExcel={handleGenerateExcel} /> diff --git a/src/pods/project/project.mapper.ts b/src/pods/project/project.mapper.ts index 9f727b4..e8d84bd 100644 --- a/src/pods/project/project.mapper.ts +++ b/src/pods/project/project.mapper.ts @@ -37,3 +37,17 @@ export const mapProjectFromApiToVm = ( } : viewModel.createEmptyProject(); }; + +export const mapProjectFromVmToApi = ( + project: viewModel.Project +): apiModel.Project => { + return Boolean(project) + ? { + id: project.id, + name: project.name, + isActive: project.isActive, + externalId: project.externalId, + comments: project.comments, + } + : viewModel.createEmptyProject(); +}; diff --git a/src/pods/project/project.vm.ts b/src/pods/project/project.vm.ts index de71271..ea71910 100644 --- a/src/pods/project/project.vm.ts +++ b/src/pods/project/project.vm.ts @@ -1,5 +1,3 @@ -import { Employee } from 'pods/employee-list/employee-list.vm'; - export interface Project { id: string; name: string; From f4593b1d7466453efc190372352554d824ba8bb2 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Fri, 26 Jun 2020 13:52:48 +0200 Subject: [PATCH 28/32] Fix project view model --- src/pods/project/project.vm.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pods/project/project.vm.ts b/src/pods/project/project.vm.ts index ea71910..d45946f 100644 --- a/src/pods/project/project.vm.ts +++ b/src/pods/project/project.vm.ts @@ -1,8 +1,8 @@ export interface Project { id: string; name: string; - externalId?: string; - comments?: string; + externalId: string; + comments: string; isActive: boolean; employees: ProjectEmployee[]; } From 21a2684fe48661b813f896635fae1cd931b7209f Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Fri, 26 Jun 2020 15:51:24 +0200 Subject: [PATCH 29/32] Add handleSaveEmployeeSelection function --- src/pods/project/api/project.api.ts | 17 +++++++- .../components/employee-row.component.tsx | 18 ++++++-- .../project/components/employee.component.tsx | 27 ++++++++++-- src/pods/project/project.component.tsx | 5 ++- src/pods/project/project.container.tsx | 43 ++++++++++++++++--- src/pods/project/project.mapper.ts | 12 ++++++ src/pods/project/project.vm.ts | 4 +- 7 files changed, 108 insertions(+), 18 deletions(-) diff --git a/src/pods/project/api/project.api.ts b/src/pods/project/api/project.api.ts index bb22bcd..3c6f23c 100644 --- a/src/pods/project/api/project.api.ts +++ b/src/pods/project/api/project.api.ts @@ -1,6 +1,5 @@ -import { Project, Employee } from './project.api-model'; +import { Project, Employee, ProjectEmployee } from './project.api-model'; import { graphQLClient } from 'core/api'; -import { ProjectEmployee } from '../project.vm'; interface GetProjectResponse { project: Project; @@ -68,3 +67,17 @@ export const saveProject = async (project: Project): Promise => { return saveProject.id; }; + +export const saveProjectEmployeeList = async ( + id: string, + projectEmployeeList: ProjectEmployee[] +): Promise => { + const query = `mutation($projectEmployeeList: [ProjectEmployeeInput!]!) { + saveProjectEmployeeList(id: "${id}", projectEmployeeList: $projectEmployeeList,) { + id + } + } + `; + + await graphQLClient.request(query, { projectEmployeeList }); +}; diff --git a/src/pods/project/components/employee-row.component.tsx b/src/pods/project/components/employee-row.component.tsx index 907e7be..b07f224 100644 --- a/src/pods/project/components/employee-row.component.tsx +++ b/src/pods/project/components/employee-row.component.tsx @@ -7,15 +7,27 @@ import { import Checkbox from '@material-ui/core/Checkbox'; import { ProjectEmployee } from '../project.vm'; -type Props = RowRendererProps; +interface RowProps extends RowRendererProps { + onChangeEmployee: (employee: ProjectEmployee) => void; +} -export const ProjectRowComponent: React.FunctionComponent = ({ +export const ProjectRowComponent: React.FunctionComponent = ({ row, + onChangeEmployee, }) => { return ( - + + onChangeEmployee({ + ...row, + isAssigned: checked, + }) + } + checked={row.isAssigned} + /> {row.name} diff --git a/src/pods/project/components/employee.component.tsx b/src/pods/project/components/employee.component.tsx index 56f2f67..a6b8970 100644 --- a/src/pods/project/components/employee.component.tsx +++ b/src/pods/project/components/employee.component.tsx @@ -6,26 +6,47 @@ import { ProjectRowComponent } from './employee-row.component'; interface Props { projectEmployeeList: ProjectEmployee[]; + onSave: (projectEmployeeList: ProjectEmployee[]) => void; onCancel: () => void; className: string; } export const EmployeeComponent: React.FunctionComponent = ({ projectEmployeeList, + onSave, onCancel, className, }) => { + const [employeeList, setProjectList] = React.useState( + projectEmployeeList + ); + + React.useEffect(() => { + setProjectList(projectEmployeeList); + }, [projectEmployeeList]); + + const handleChangeEmployee = (id: string) => (employee: ProjectEmployee) => { + const updateEmployeeList = employeeList.map(e => + e.id === id ? employee : e + ); + setProjectList(updateEmployeeList); + }; + + const handleSave = () => onSave(employeeList); return ( <> ) => ( - + )} /> - + ); }; diff --git a/src/pods/project/project.component.tsx b/src/pods/project/project.component.tsx index 4e555a0..937a0b9 100644 --- a/src/pods/project/project.component.tsx +++ b/src/pods/project/project.component.tsx @@ -10,7 +10,7 @@ import { EmployeeComponent, ReportComponent, } from './components'; -import { Project, Report } from './project.vm'; +import { Project, Report, ProjectEmployee } from './project.vm'; import * as classes from './project.styles'; interface Props { @@ -19,6 +19,7 @@ interface Props { report: Report; onGenerateExcel: (report: Report) => void; onSaveProject: (project: Project) => void; + onSaveEmployeeSelection: (employeeProjectList: ProjectEmployee[]) => void; onCancel: () => void; } @@ -28,6 +29,7 @@ export const ProjectComponent: React.FunctionComponent = ({ report, onGenerateExcel, onSaveProject, + onSaveEmployeeSelection, onCancel, }) => { const [tab, setTab] = React.useState(0); @@ -51,6 +53,7 @@ export const ProjectComponent: React.FunctionComponent = ({ diff --git a/src/pods/project/project.container.tsx b/src/pods/project/project.container.tsx index 4269122..f748d7b 100644 --- a/src/pods/project/project.container.tsx +++ b/src/pods/project/project.container.tsx @@ -2,22 +2,31 @@ import React from 'react'; import { ProjectComponent } from './project.component'; import { useParams } from 'react-router-dom'; import { useSnackbarContext } from 'common/components'; -import { getProjectById, getEmployees } from './api'; +import { + getProjectById, + getEmployees, + saveProject, + saveProjectEmployeeList, +} from './api'; import { trackPromise } from 'react-promise-tracker'; -import { saveProject } from './api'; -import { mapProjectFromApiToVm, mapProjectFromVmToApi } from './project.mapper'; +import { + mapProjectFromApiToVm, + mapProjectFromVmToApi, + mapProjectEmployeeListFromVmToApi, +} from './project.mapper'; import { Project, createEmptyProject, Report, createEmptyReport, + ProjectEmployee, } from './project.vm'; import { isEditModeHelper } from 'common/helpers'; import { useHistory } from 'react-router'; -import { routes } from 'core/router'; +import { routes, EditParams } from 'core/router'; export const ProjectContainer: React.FunctionComponent = () => { - const { id } = useParams(); + const params = useParams(); const [project, setProject] = React.useState(createEmptyProject()); const [isEditMode, setIsEditMode] = React.useState(false); const [report, setReport] = React.useState(createEmptyReport()); @@ -27,7 +36,7 @@ export const ProjectContainer: React.FunctionComponent = () => { const onLoadProject = async () => { try { const [apiEmployees, apiProject] = await trackPromise( - Promise.all([getEmployees(), getProjectById(id)]) + Promise.all([getEmployees(), getProjectById(params.id)]) ); const viewModelProject = mapProjectFromApiToVm(apiProject, apiEmployees); setProject(viewModelProject); @@ -57,6 +66,25 @@ export const ProjectContainer: React.FunctionComponent = () => { } }; + const handleSaveEmployeeSelection = async ( + proyectEmployeeList: ProjectEmployee[] + ) => { + if (params.id) { + try { + const apiEmployeeProjectList = mapProjectEmployeeListFromVmToApi( + proyectEmployeeList + ); + await trackPromise( + saveProjectEmployeeList(params.id, apiEmployeeProjectList) + ); + setProject({ ...project, employees: proyectEmployeeList }); + showMessage('Se actualizó con éxito', 'success'); + } catch (error) { + error && showMessage('Ha ocurrido un error al guardar', 'error'); + } + } + }; + const handleCancel = () => { history.goBack(); }; @@ -67,7 +95,7 @@ export const ProjectContainer: React.FunctionComponent = () => { }; React.useEffect(() => { - const isEditMode = isEditModeHelper(id); + const isEditMode = isEditModeHelper(params.id); setIsEditMode(isEditMode); if (isEditMode) { onLoadProject(); @@ -80,6 +108,7 @@ export const ProjectContainer: React.FunctionComponent = () => { project={project} report={report} onSaveProject={handleSaveProject} + onSaveEmployeeSelection={handleSaveEmployeeSelection} onCancel={handleCancel} onGenerateExcel={handleGenerateExcel} /> diff --git a/src/pods/project/project.mapper.ts b/src/pods/project/project.mapper.ts index e8d84bd..1059847 100644 --- a/src/pods/project/project.mapper.ts +++ b/src/pods/project/project.mapper.ts @@ -51,3 +51,15 @@ export const mapProjectFromVmToApi = ( } : viewModel.createEmptyProject(); }; + +const mapProjectEmployeeFromVmToApi = ( + projectEmployee: viewModel.ProjectEmployee +): apiModel.ProjectEmployee => ({ + id: projectEmployee.id, + isAssigned: projectEmployee.isAssigned, +}); + +export const mapProjectEmployeeListFromVmToApi = ( + projectEmployeeList: viewModel.ProjectEmployee[] +): apiModel.ProjectEmployee[] => + mapToCollection(projectEmployeeList, mapProjectEmployeeFromVmToApi); diff --git a/src/pods/project/project.vm.ts b/src/pods/project/project.vm.ts index d45946f..ea71910 100644 --- a/src/pods/project/project.vm.ts +++ b/src/pods/project/project.vm.ts @@ -1,8 +1,8 @@ export interface Project { id: string; name: string; - externalId: string; - comments: string; + externalId?: string; + comments?: string; isActive: boolean; employees: ProjectEmployee[]; } From cec26e30955f1bf2141554b90774b7960df44982 Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Mon, 29 Jun 2020 16:12:33 +0200 Subject: [PATCH 30/32] Remove project mock data and create project mappers spec and project.mappers --- src/pods/project/api/project.mock-data.ts | 29 -- src/pods/project/project.container.tsx | 2 +- src/pods/project/project.mappers.spec.ts | 346 ++++++++++++++++++ .../{project.mapper.ts => project.mappers.ts} | 0 4 files changed, 347 insertions(+), 30 deletions(-) delete mode 100644 src/pods/project/api/project.mock-data.ts create mode 100644 src/pods/project/project.mappers.spec.ts rename src/pods/project/{project.mapper.ts => project.mappers.ts} (100%) diff --git a/src/pods/project/api/project.mock-data.ts b/src/pods/project/api/project.mock-data.ts deleted file mode 100644 index f9ace0e..0000000 --- a/src/pods/project/api/project.mock-data.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Project, ProjectEmployee } from './project.api-model'; - -const mockEmployeeSummaryList: ProjectEmployee[] = [ - { - id: '1', - isAssigned: true, - }, - { - id: '2', - isAssigned: false, - }, - { - id: '3', - isAssigned: false, - }, - { - id: '4', - isAssigned: true, - }, -]; - -export const mockProject: Project = { - id: '1', - name: 'Nombre', - isActive: true, - comments: 'Comentario', - externalId: '1234', - employees: mockEmployeeSummaryList, -}; diff --git a/src/pods/project/project.container.tsx b/src/pods/project/project.container.tsx index f748d7b..0dc6b45 100644 --- a/src/pods/project/project.container.tsx +++ b/src/pods/project/project.container.tsx @@ -13,7 +13,7 @@ import { mapProjectFromApiToVm, mapProjectFromVmToApi, mapProjectEmployeeListFromVmToApi, -} from './project.mapper'; +} from './project.mappers'; import { Project, createEmptyProject, diff --git a/src/pods/project/project.mappers.spec.ts b/src/pods/project/project.mappers.spec.ts new file mode 100644 index 0000000..ef5781e --- /dev/null +++ b/src/pods/project/project.mappers.spec.ts @@ -0,0 +1,346 @@ +import { + mapProjectFromApiToVm, + mapProjectFromVmToApi, + mapProjectEmployeeListFromVmToApi, +} from './project.mappers'; +import * as apiModel from './api/project.api-model'; +import * as viewModel from './project.vm'; + +describe('./pods/project/project.mappers', () => { + describe('mapProjectFromApiToVm', () => { + it('should return empty project when feeding null value', () => { + // Arrange + const project = null; + const employees = null; + + // Act + const result = mapProjectFromApiToVm(project, employees); + + // Assert + expect(result).toEqual(viewModel.createEmptyProject()); + }); + + it('should return empty project when feeding undefined value', () => { + // Arrange + const project = undefined; + const employees = undefined; + + // Act + const result = mapProjectFromApiToVm(project, employees); + + // Assert + expect(result).toEqual(viewModel.createEmptyProject()); + }); + + it('should return expected result but feeding null employee list', () => { + // Arrange + const project: apiModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + employees: null, + }; + + const employees: apiModel.Employee[] = [ + { + id: '1', + name: 'employee test 1', + }, + { + id: '2', + name: 'employee test 2', + }, + ]; + + const expectedResult: viewModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + employees: [ + { + id: '1', + name: 'employee test 1', + isAssigned: false, + }, + { + id: '2', + name: 'employee test 2', + isAssigned: false, + }, + ], + }; + + // Act + const result = mapProjectFromApiToVm(project, employees); + + // Assert + expect(result).toEqual(expectedResult); + }); + + it('should return expected result but feeding undefined employee list', () => { + // Arrange + const project: apiModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + employees: undefined, + }; + + const expectedResult: viewModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + employees: [ + { + id: '1', + name: 'employee test 1', + isAssigned: false, + }, + { + id: '2', + name: 'employee test 2', + isAssigned: false, + }, + ], + }; + + const employees: apiModel.Employee[] = [ + { + id: '1', + name: 'employee test 1', + }, + { + id: '2', + name: 'employee test 2', + }, + ]; + + // Act + const result = mapProjectFromApiToVm(project, employees); + + // Assert + expect(result).toEqual(expectedResult); + }); + + it('should return expected result feeding correct values', () => { + // Arrange + const project: apiModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + employees: [ + { + id: '1', + isAssigned: true, + }, + { + id: '2', + isAssigned: false, + }, + ], + }; + + const employees: apiModel.Employee[] = [ + { + id: '1', + name: 'employee test 1', + }, + { + id: '2', + name: 'employee test 2', + }, + ]; + + const expectedResult: viewModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + employees: [ + { + id: '1', + isAssigned: true, + name: 'employee test 1', + }, + { + id: '2', + isAssigned: false, + name: 'employee test 2', + }, + ], + }; + + // Act + const result = mapProjectFromApiToVm(project, employees); + + // Assert + expect(result).toEqual(expectedResult); + }); + }); + + describe('mapProjectFromVmToApi', () => { + it('should return empty project when feeding null', () => { + // Arrange + const project = null; + + // Act + const result = mapProjectFromVmToApi(project); + + //Assert + expect(result).toEqual(viewModel.createEmptyProject()); + }); + + it('should return empty project when feeding undefined', () => { + // Arrange + const project = undefined; + + // Act + const result = mapProjectFromVmToApi(project); + + //Assert + expect(result).toEqual(viewModel.createEmptyProject()); + }); + + it('should return expected value when feeding employee, and empty employee list', () => { + // Arrange + const project: viewModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + employees: [], + }; + + const expectedValue: apiModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + }; + + // Act + const result = mapProjectFromVmToApi(project); + + //Assert + expect(result).toEqual(expectedValue); + }); + + it('should return expected value when feeding project, and employee list', () => { + // Arrange + const project: viewModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + employees: [ + { + id: '1', + isAssigned: true, + name: 'test name', + }, + { + id: '2', + isAssigned: false, + name: 'test name', + }, + ], + }; + + const expectedValue: apiModel.Project = { + id: 'test id', + name: 'test name', + isActive: true, + comments: 'Test comments', + externalId: '123', + }; + + // Act + const result = mapProjectFromVmToApi(project); + + //Assert + expect(result).toEqual(expectedValue); + }); + }); + + describe('mapProjectEmployeeListFromVmToApi', () => { + it('should return empty array when feeding project employee null', () => { + // Arrange + const projectEmployeeList = null; + + // Act + const result = mapProjectEmployeeListFromVmToApi(projectEmployeeList); + + // Assert + expect(result).toEqual([]); + }); + + it('should return empty array when feeding project employee undefined', () => { + // Arrange + const projectEmployeeList = undefined; + + // Act + const result = mapProjectEmployeeListFromVmToApi(projectEmployeeList); + + // Assert + expect(result).toEqual([]); + }); + + it('should return empty array when feeding project employee empty array', () => { + // Arrange + const projectEmployeeList = []; + + // Act + const result = mapProjectEmployeeListFromVmToApi(projectEmployeeList); + + // Assert + expect(result).toEqual([]); + }); + + it('should return expected result when feeding project employee list', () => { + // Arrange + const projectEmployeeList: viewModel.ProjectEmployee[] = [ + { + id: '1', + name: 'test name', + isAssigned: true, + }, + { + id: '2', + name: 'test name', + isAssigned: false, + }, + ]; + + const expectedResult: apiModel.ProjectEmployee[] = [ + { + id: '1', + isAssigned: true, + }, + { + id: '2', + isAssigned: false, + }, + ]; + + // Act + const result = mapProjectEmployeeListFromVmToApi(projectEmployeeList); + + // Assert + expect(result).toEqual(expectedResult); + }); + }); +}); diff --git a/src/pods/project/project.mapper.ts b/src/pods/project/project.mappers.ts similarity index 100% rename from src/pods/project/project.mapper.ts rename to src/pods/project/project.mappers.ts From a47b8ad0da39ac5d3c2c649c6e07233192a0e0fd Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Mon, 29 Jun 2020 17:49:07 +0200 Subject: [PATCH 31/32] Fix component --- src/pods/project/components/employee.component.tsx | 2 ++ src/pods/project/project.component.tsx | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pods/project/components/employee.component.tsx b/src/pods/project/components/employee.component.tsx index a6b8970..42b859d 100644 --- a/src/pods/project/components/employee.component.tsx +++ b/src/pods/project/components/employee.component.tsx @@ -45,6 +45,8 @@ export const EmployeeComponent: React.FunctionComponent = ({ onChangeEmployee={handleChangeEmployee(rowProps.row.id)} /> )} + enablePagination={true} + pageSize={5} /> diff --git a/src/pods/project/project.component.tsx b/src/pods/project/project.component.tsx index 937a0b9..3fa587d 100644 --- a/src/pods/project/project.component.tsx +++ b/src/pods/project/project.component.tsx @@ -17,20 +17,20 @@ interface Props { isEditMode: boolean; project: Project; report: Report; - onGenerateExcel: (report: Report) => void; onSaveProject: (project: Project) => void; onSaveEmployeeSelection: (employeeProjectList: ProjectEmployee[]) => void; onCancel: () => void; + onGenerateExcel: (report: Report) => void; } export const ProjectComponent: React.FunctionComponent = ({ isEditMode, project, report, - onGenerateExcel, onSaveProject, onSaveEmployeeSelection, onCancel, + onGenerateExcel, }) => { const [tab, setTab] = React.useState(0); return ( From 5cf64f9c1e8c98ef4051545e34e0ef9e79af864c Mon Sep 17 00:00:00 2001 From: Antonio Contreras LEMONCODE Date: Mon, 29 Jun 2020 17:54:22 +0200 Subject: [PATCH 32/32] Fix project component --- src/pods/employee/components/project.component.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pods/employee/components/project.component.tsx b/src/pods/employee/components/project.component.tsx index b9719d2..f9ac0bf 100644 --- a/src/pods/employee/components/project.component.tsx +++ b/src/pods/employee/components/project.component.tsx @@ -43,6 +43,8 @@ export const ProjectComponent: React.FunctionComponent = ({ onChangeProject={handleChangeProject(rowProps.row.id)} /> )} + enablePagination={true} + pageSize={5} />