diff --git a/front/src/components/table/editable-cell/EditableFullName.tsx b/front/src/components/table/editable-cell/EditableFullName.tsx new file mode 100644 index 000000000..17e0ffd3e --- /dev/null +++ b/front/src/components/table/editable-cell/EditableFullName.tsx @@ -0,0 +1,75 @@ +import styled from '@emotion/styled'; +import { ChangeEvent, useRef, useState } from 'react'; +import EditableCellWrapper from './EditableCellWrapper'; +import PersonChip from '../../chips/PersonChip'; + +type OwnProps = { + firstname: string; + lastname: string; + changeHandler: (firstname: string, lastname: string) => void; +}; + +const StyledContainer = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + + & > input:last-child { + padding-left: ${(props) => props.theme.spacing(2)}; + border-left: 1px solid ${(props) => props.theme.primaryBorder}; + } +`; + +const StyledEditInplaceInput = styled.input` + width: 45%; + border: none; + outline: none; + height: 18px; + + &::placeholder { + font-weight: bold; + color: ${(props) => props.theme.text20}; + } +`; + +function EditableFullName({ firstname, lastname, changeHandler }: OwnProps) { + const firstnameInputRef = useRef(null); + const [firstnameValue, setFirstnameValue] = useState(firstname); + const [lastnameValue, setLastnameValue] = useState(lastname); + const [isEditMode, setIsEditMode] = useState(false); + + return ( + setIsEditMode(editMode)} + > + {isEditMode ? ( + + ) => { + setFirstnameValue(event.target.value); + changeHandler(event.target.value, lastnameValue); + }} + /> + ) => { + setLastnameValue(event.target.value); + changeHandler(firstnameValue, event.target.value); + }} + /> + + ) : ( + + )} + + ); +} + +export default EditableFullName; diff --git a/front/src/components/table/editable-cell/EditableText.tsx b/front/src/components/table/editable-cell/EditableText.tsx index 1a4160a85..798e7486e 100644 --- a/front/src/components/table/editable-cell/EditableText.tsx +++ b/front/src/components/table/editable-cell/EditableText.tsx @@ -41,9 +41,6 @@ function EditableCell({ const onEditModeChange = (isEditMode: boolean) => { setIsEditMode(isEditMode); - if (isEditMode) { - inputRef.current?.focus(); - } }; return ( @@ -55,6 +52,7 @@ function EditableCell({ ) => { diff --git a/front/src/components/table/editable-cell/__stories__/EditableFullName.stories.tsx b/front/src/components/table/editable-cell/__stories__/EditableFullName.stories.tsx new file mode 100644 index 000000000..ab92701ef --- /dev/null +++ b/front/src/components/table/editable-cell/__stories__/EditableFullName.stories.tsx @@ -0,0 +1,39 @@ +import EditableFullName from '../EditableFullName'; +import { ThemeProvider } from '@emotion/react'; +import { lightTheme } from '../../../../layout/styles/themes'; +import { StoryFn } from '@storybook/react'; +import { MemoryRouter } from 'react-router-dom'; + +const component = { + title: 'EditableFullName', + component: EditableFullName, +}; + +type OwnProps = { + firstname: string; + lastname: string; + changeHandler: (firstname: string, lastname: string) => void; +}; + +export default component; + +const Template: StoryFn = (args: OwnProps) => { + return ( + + +
+ +
+
+
+ ); +}; + +export const EditableFullNameStory = Template.bind({}); +EditableFullNameStory.args = { + firstname: 'John', + lastname: 'Doe', + changeHandler: () => { + console.log('changed'); + }, +}; diff --git a/front/src/components/table/editable-cell/__tests__/EditableFullName.test.tsx b/front/src/components/table/editable-cell/__tests__/EditableFullName.test.tsx new file mode 100644 index 000000000..b5979f475 --- /dev/null +++ b/front/src/components/table/editable-cell/__tests__/EditableFullName.test.tsx @@ -0,0 +1,41 @@ +import { fireEvent, render } from '@testing-library/react'; + +import { EditableFullNameStory } from '../__stories__/EditableFullName.stories'; + +it('Checks the EditableFullName editing event bubbles up', async () => { + const func = jest.fn(() => null); + const { getByTestId } = render( + , + ); + + const parent = getByTestId('content-editable-parent'); + + const wrapper = parent.querySelector('div'); + + if (!wrapper) { + throw new Error('Editable input not found'); + } + fireEvent.click(wrapper); + + const firstnameInput = parent.querySelector('input:first-child'); + + if (!firstnameInput) { + throw new Error('Editable input not found'); + } + + fireEvent.change(firstnameInput, { target: { value: 'Jo' } }); + expect(func).toBeCalledWith('Jo', 'Doe'); + + const lastnameInput = parent.querySelector('input:last-child'); + + if (!lastnameInput) { + throw new Error('Editable input not found'); + } + + fireEvent.change(lastnameInput, { target: { value: 'Do' } }); + expect(func).toBeCalledWith('Jo', 'Do'); +}); diff --git a/front/src/interfaces/person.interface.test.ts b/front/src/interfaces/person.interface.test.ts index aa16a90ef..c704314c1 100644 --- a/front/src/interfaces/person.interface.test.ts +++ b/front/src/interfaces/person.interface.test.ts @@ -18,13 +18,14 @@ describe('mapPerson', () => { }, __typename: '', }); - expect(person.fullName).toBe('John Doe'); + expect(person.firstname).toBe('John'); }); it('should map person back', () => { const person = mapGqlPerson({ id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b', - fullName: 'John Doe', + firstname: 'John', + lastname: 'Doe', email: '', phone: '', city: '', diff --git a/front/src/interfaces/person.interface.ts b/front/src/interfaces/person.interface.ts index 1be9f6236..df70f631f 100644 --- a/front/src/interfaces/person.interface.ts +++ b/front/src/interfaces/person.interface.ts @@ -3,7 +3,8 @@ import { Pipe } from './pipe.interface'; export type Person = { id: string; - fullName: string; + firstname: string; + lastname: string; picture?: string; email: string; company: Omit< @@ -47,14 +48,15 @@ export type GraphqlMutationPerson = { }; export const mapPerson = (person: GraphqlQueryPerson): Person => ({ - fullName: `${person.firstname} ${person.lastname}`, + ...person, + firstname: person.firstname, + lastname: person.lastname, creationDate: new Date(person.created_at), pipe: { name: 'coucou', id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b', icon: '💰', }, - ...person, company: { id: person.company.id, name: person.company.name, @@ -64,10 +66,10 @@ export const mapPerson = (person: GraphqlQueryPerson): Person => ({ }); export const mapGqlPerson = (person: Person): GraphqlMutationPerson => ({ - firstname: person.fullName.split(' ').shift() || '', - lastname: person.fullName.split(' ').slice(1).join(' '), + ...(person as Omit), + firstname: person.firstname, + lastname: person.lastname, created_at: person.creationDate.toUTCString(), company_id: person.company.id, - ...(person as Omit), __typename: 'People', }); diff --git a/front/src/pages/people/people-table.tsx b/front/src/pages/people/people-table.tsx index abba205f1..dc697ada2 100644 --- a/front/src/pages/people/people-table.tsx +++ b/front/src/pages/people/people-table.tsx @@ -13,9 +13,7 @@ import { createColumnHelper } from '@tanstack/react-table'; import ClickableCell from '../../components/table/ClickableCell'; import ColumnHead from '../../components/table/ColumnHead'; import Checkbox from '../../components/form/Checkbox'; -import HorizontalyAlignedContainer from '../../layout/containers/HorizontalyAlignedContainer'; import CompanyChip from '../../components/chips/CompanyChip'; -import PersonChip from '../../components/chips/PersonChip'; import { GraphqlQueryPerson, Person } from '../../interfaces/person.interface'; import PipeChip from '../../components/chips/PipeChip'; import EditableText from '../../components/table/editable-cell/EditableText'; @@ -31,6 +29,7 @@ import { } from '../../services/search/search'; import { GraphqlQueryCompany } from '../../interfaces/company.interface'; import EditablePhone from '../../components/table/editable-cell/EditablePhone'; +import EditableFullName from '../../components/table/editable-cell/EditableFullName'; export const availableSorts = [ { @@ -132,21 +131,30 @@ export const availableFilters = [ const columnHelper = createColumnHelper(); export const peopleColumns = [ - columnHelper.accessor('fullName', { + columnHelper.accessor('id', { + header: () => ( + + ), + cell: (props) => ( + + ), + }), + columnHelper.accessor('firstname', { header: () => } />, cell: (props) => ( - <> - - - - - + { + const person = props.row.original; + person.firstname = firstName; + person.lastname = lastName; + updatePerson(person).catch((error) => console.error(error)); // TODO: handle error + }} + /> ), }), columnHelper.accessor('email', {