diff --git a/front/src/generated/graphql.tsx b/front/src/generated/graphql.tsx index 54da27ecf..7f6964ea0 100644 --- a/front/src/generated/graphql.tsx +++ b/front/src/generated/graphql.tsx @@ -2794,22 +2794,22 @@ export type GetCompanyQueryVariables = Exact<{ export type GetCompanyQuery = { __typename?: 'Query', findUniqueCompany: { __typename?: 'Company', id: string, domainName: string, name: string, createdAt: string, address: string, linkedinUrl?: string | null, employees?: number | null, _activityCount: number, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null, Favorite?: Array<{ __typename?: 'Favorite', id: string, person?: { __typename?: 'Person', id: string } | null, company?: { __typename?: 'Company', id: string } | null }> | null } }; +export type CompanyFieldsFragmentFragment = { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null }; + export type UpdateOneCompanyMutationVariables = Exact<{ where: CompanyWhereUniqueInput; data: CompanyUpdateInput; }>; -export type UpdateOneCompanyMutation = { __typename?: 'Mutation', updateOneCompany?: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null } | null } | null }; - -export type InsertCompanyFragmentFragment = { __typename?: 'Company', domainName: string, address: string, id: string, name: string, createdAt: string }; +export type UpdateOneCompanyMutation = { __typename?: 'Mutation', updateOneCompany?: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null } | null }; export type InsertOneCompanyMutationVariables = Exact<{ data: CompanyCreateInput; }>; -export type InsertOneCompanyMutation = { __typename?: 'Mutation', createOneCompany: { __typename?: 'Company', domainName: string, address: string, id: string, name: string, createdAt: string } }; +export type InsertOneCompanyMutation = { __typename?: 'Mutation', createOneCompany: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null } }; export type DeleteManyCompaniesMutationVariables = Exact<{ ids?: InputMaybe | Scalars['String']>; @@ -3034,7 +3034,7 @@ export type SearchCompanyQueryVariables = Exact<{ }>; -export type SearchCompanyQuery = { __typename?: 'Query', searchResults: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> }; +export type SearchCompanyQuery = { __typename?: 'Query', searchResults: Array<{ __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null }> }; export type SearchActivityQueryVariables = Exact<{ where?: InputMaybe; @@ -3252,13 +3252,21 @@ export const ActivityUpdatePartsFragmentDoc = gql` } } `; -export const InsertCompanyFragmentFragmentDoc = gql` - fragment InsertCompanyFragment on Company { - domainName +export const CompanyFieldsFragmentFragmentDoc = gql` + fragment CompanyFieldsFragment on Company { + accountOwner { + id + email + displayName + avatarUrl + } address + createdAt + domainName + employees + linkedinUrl id name - createdAt } `; export const InsertPersonFragmentFragmentDoc = gql` @@ -4156,23 +4164,10 @@ export type GetCompanyQueryResult = Apollo.QueryResult; /** @@ -4203,10 +4198,10 @@ export type UpdateOneCompanyMutationOptions = Apollo.BaseMutationOptions; /** @@ -5382,12 +5377,10 @@ export type EmptyQueryQueryResult = Apollo.QueryResult + updateEntityMutation({ + variables, + onCompleted: (data) => { + if (!data.updateOneCompany) { + return; + } + upsertEntityTableItem(data.updateOneCompany); + }, + }) + } /> ); diff --git a/front/src/modules/companies/table/components/CompanyTableMockMode.tsx b/front/src/modules/companies/table/components/CompanyTableMockMode.tsx index a7de979c8..83a1128bf 100644 --- a/front/src/modules/companies/table/components/CompanyTableMockMode.tsx +++ b/front/src/modules/companies/table/components/CompanyTableMockMode.tsx @@ -13,7 +13,7 @@ export function CompanyTableMockMode() { viewName="All Companies" viewIcon={} availableSorts={availableSorts} - useUpdateEntityMutation={useUpdateOneCompanyMutation} + updateEntityMutation={[useUpdateOneCompanyMutation()]} /> ); diff --git a/front/src/modules/people/table/components/PeopleTable.tsx b/front/src/modules/people/table/components/PeopleTable.tsx index 8d5ee7f31..4f081e9ab 100644 --- a/front/src/modules/people/table/components/PeopleTable.tsx +++ b/front/src/modules/people/table/components/PeopleTable.tsx @@ -8,12 +8,14 @@ import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIn import { IconList } from '@/ui/icon'; import { EntityTable } from '@/ui/table/components/EntityTable'; import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTableData'; +import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem'; import { TableContext } from '@/ui/table/states/TableContext'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { useTableViewFields } from '@/views/hooks/useTableViewFields'; import { useViewSorts } from '@/views/hooks/useViewSorts'; import { currentViewIdState } from '@/views/states/currentViewIdState'; import { + UpdateOnePersonMutationVariables, useGetPeopleQuery, useUpdateOnePersonMutation, } from '~/generated/graphql'; @@ -25,6 +27,8 @@ import { defaultOrderBy } from '../../queries'; export function PeopleTable() { const currentViewId = useRecoilValue(currentViewIdState); const orderBy = useRecoilScopedValue(sortsOrderByScopedState, TableContext); + const [updateEntityMutation] = useUpdateOnePersonMutation(); + const upsertEntityTableItem = useUpsertEntityTableItem(); const { handleColumnsChange } = useTableViewFields({ objectName: 'person', @@ -56,7 +60,21 @@ export function PeopleTable() { availableSorts={availableSorts} onColumnsChange={handleColumnsChange} onSortsUpdate={currentViewId ? updateSorts : undefined} - useUpdateEntityMutation={useUpdateOnePersonMutation} + updateEntityMutation={({ + variables, + }: { + variables: UpdateOnePersonMutationVariables; + }) => + updateEntityMutation({ + variables, + onCompleted: (data) => { + if (!data.updateOnePerson) { + return; + } + upsertEntityTableItem(data.updateOnePerson); + }, + }) + } /> ); diff --git a/front/src/modules/search/queries/search.ts b/front/src/modules/search/queries/search.ts index 50157768f..9eb90ea8e 100644 --- a/front/src/modules/search/queries/search.ts +++ b/front/src/modules/search/queries/search.ts @@ -64,9 +64,7 @@ export const SEARCH_COMPANY_QUERY = gql` take: $limit orderBy: $orderBy ) { - id - name - domainName + ...CompanyFieldsFragment } } `; diff --git a/front/src/modules/ui/board/card-field/components/BoardCardEditableField.tsx b/front/src/modules/ui/board/card-field/components/BoardCardEditableField.tsx deleted file mode 100644 index 77f5d6374..000000000 --- a/front/src/modules/ui/board/card-field/components/BoardCardEditableField.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ReactElement } from 'react'; - -import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; -import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; - -import { BoardCardFieldContext } from '../states/BoardCardFieldContext'; - -import { BoardCardEditableFieldInternal } from './BoardCardEditableFieldInternal'; - -type OwnProps = { - editModeContent: ReactElement; - nonEditModeContent: ReactElement; - editModeHorizontalAlign?: 'left' | 'right'; - editModeVerticalPosition?: 'over' | 'below'; - editHotkeyScope?: HotkeyScope; -}; - -export function BoardCardEditableField(props: OwnProps) { - return ( - - - - ); -} diff --git a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldDate.tsx b/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldDate.tsx deleted file mode 100644 index 42757dae7..000000000 --- a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldDate.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useMemo, useState } from 'react'; - -import { DateInputDisplay } from '@/ui/input/date/components/DateInputDisplay'; -import { debounce } from '~/utils/debounce'; - -import { BoardCardEditableField } from './BoardCardEditableField'; -import { BoardCardEditableFieldDateEditMode } from './BoardCardEditableFieldDateEditMode'; - -type OwnProps = { - value: Date; - onChange: (newValue: Date) => void; - editModeHorizontalAlign?: 'left' | 'right'; -}; - -export function BoardCardEditableFieldDate({ - value, - onChange, - editModeHorizontalAlign, -}: OwnProps) { - const [internalValue, setInternalValue] = useState(value); - const debouncedOnChange = useMemo(() => { - return debounce(onChange, 200); - }, [onChange]); - return ( - { - setInternalValue(date); - debouncedOnChange(date); - }} - /> - } - nonEditModeContent={} - > - ); -} diff --git a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldDateEditMode.tsx b/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldDateEditMode.tsx deleted file mode 100644 index bb08f7904..000000000 --- a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldDateEditMode.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { DateInputEdit } from '@/ui/input/date/components/DateInputEdit'; - -type OwnProps = { - value: Date; - onChange: (newValue: Date) => void; -}; - -export function BoardCardEditableFieldDateEditMode({ - value, - onChange, -}: OwnProps) { - function handleDateChange(newDate: Date) { - onChange(newDate); - } - - return ; -} diff --git a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldDisplayMode.tsx b/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldDisplayMode.tsx deleted file mode 100644 index a3dfce28d..000000000 --- a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldDisplayMode.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import styled from '@emotion/styled'; - -export const BoardCardFieldDisplayModeOuterContainer = styled.div` - align-items: center; - display: flex; - height: 100%; - overflow: hidden; - - padding-left: ${({ theme }) => theme.spacing(2)}; - padding-right: ${({ theme }) => theme.spacing(1)}; - width: 100%; -`; - -export const BoardCardFieldDisplayModeInnerContainer = styled.div` - align-items: center; - display: flex; - height: 100%; - overflow: hidden; - width: 100%; -`; - -export function BoardCardEditableFieldDisplayMode({ - children, -}: React.PropsWithChildren) { - return ( - - - {children} - - - ); -} diff --git a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldEditMode.tsx b/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldEditMode.tsx deleted file mode 100644 index 180091a13..000000000 --- a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldEditMode.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { ReactElement, useRef } from 'react'; -import styled from '@emotion/styled'; - -import { overlayBackground } from '@/ui/theme/constants/effects'; -import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; -import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; - -import { BoardCardFieldHotkeyScope } from '../types/BoardCardFieldHotkeyScope'; - -export const BoardCardFieldEditModeContainer = styled.div< - Omit ->` - align-items: center; - border: 1px solid ${({ theme }) => theme.border.color.light}; - border-radius: ${({ theme }) => theme.border.radius.sm}; - display: flex; - left: ${(props) => - props.editModeHorizontalAlign === 'right' ? 'auto' : '0'}; - margin-left: -2px; - min-height: 100%; - min-width: calc(100% + 20px); - position: absolute; - - right: ${(props) => - props.editModeHorizontalAlign === 'right' ? '0' : 'auto'}; - top: ${(props) => (props.editModeVerticalPosition === 'over' ? '0' : '100%')}; - z-index: 1; - ${overlayBackground} -`; - -type OwnProps = { - children: ReactElement; - editModeHorizontalAlign?: 'left' | 'right'; - editModeVerticalPosition?: 'over' | 'below'; - onExit: () => void; -}; - -export function BoardCardEditableFieldEditMode({ - editModeHorizontalAlign, - editModeVerticalPosition, - children, - onExit, -}: OwnProps) { - const wrapperRef = useRef(null); - - useListenClickOutside({ - refs: [wrapperRef], - callback: () => { - onExit(); - }, - }); - - useScopedHotkeys( - 'enter', - () => { - onExit(); - }, - BoardCardFieldHotkeyScope.BoardCardFieldEditMode, - [onExit], - ); - - useScopedHotkeys( - 'esc', - () => { - onExit(); - }, - BoardCardFieldHotkeyScope.BoardCardFieldEditMode, - [onExit], - ); - - return ( - - {children} - - ); -} diff --git a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldInternal.tsx b/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldInternal.tsx deleted file mode 100644 index 43fcb0ebc..000000000 --- a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldInternal.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { ReactElement } from 'react'; -import styled from '@emotion/styled'; - -import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; -import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; - -import { useBoardCardField } from '../hooks/useBoardCardField'; -import { BoardCardFieldHotkeyScope } from '../types/BoardCardFieldHotkeyScope'; - -import { BoardCardEditableFieldDisplayMode } from './BoardCardEditableFieldDisplayMode'; -import { BoardCardEditableFieldEditMode } from './BoardCardEditableFieldEditMode'; - -export const BoardCardFieldContainer = styled.div` - align-items: center; - box-sizing: border-box; - cursor: pointer; - display: flex; - height: 32px; - position: relative; - user-select: none; - width: 100%; -`; - -type OwnProps = { - editModeContent: ReactElement; - nonEditModeContent: ReactElement; - editModeHorizontalAlign?: 'left' | 'right'; - editModeVerticalPosition?: 'over' | 'below'; - editHotkeyScope?: HotkeyScope; -}; - -export function BoardCardEditableFieldInternal({ - editModeHorizontalAlign = 'left', - editModeVerticalPosition = 'over', - editModeContent, - nonEditModeContent, - editHotkeyScope, -}: OwnProps) { - const { openBoardCardField, isBoardCardFieldInEditMode } = - useBoardCardField(); - - const { closeBoardCardField } = useBoardCardField(); - - const { - goBackToPreviousHotkeyScope, - setHotkeyScopeAndMemorizePreviousScope, - } = usePreviousHotkeyScope(); - - function handleOnClick() { - if (!isBoardCardFieldInEditMode) { - openBoardCardField(); - setHotkeyScopeAndMemorizePreviousScope( - editHotkeyScope?.scope ?? - BoardCardFieldHotkeyScope.BoardCardFieldEditMode, - editHotkeyScope?.customScopes ?? {}, - ); - } - } - - function handleEditModeExit() { - goBackToPreviousHotkeyScope(); - closeBoardCardField(); - } - - return ( - - {isBoardCardFieldInEditMode ? ( - - {editModeContent} - - ) : ( - - {nonEditModeContent} - - )} - - ); -} diff --git a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldText.tsx b/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldText.tsx deleted file mode 100644 index eb8d8caf9..000000000 --- a/front/src/modules/ui/board/card-field/components/BoardCardEditableFieldText.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { ChangeEvent, useMemo, useState } from 'react'; - -import { TextInputDisplay } from '@/ui/input/text/components/TextInputDisplay'; -import { StyledInput } from '@/ui/table/editable-cell/type/components/TextCellEdit'; -import { debounce } from '~/utils/debounce'; - -import { BoardCardEditableField } from './BoardCardEditableField'; - -type OwnProps = { - placeholder?: string; - value: string; - onChange: (newValue: string) => void; - editModeHorizontalAlign?: 'left' | 'right'; -}; - -export function BoardCardEditableFieldText({ - value, - placeholder, - onChange, - editModeHorizontalAlign, -}: OwnProps) { - const [internalValue, setInternalValue] = useState(value); - - const debouncedOnChange = useMemo(() => { - return debounce(onChange, 200); - }, [onChange]); - - return ( - ) => { - setInternalValue(event.target.value); - debouncedOnChange(event.target.value); - }} - /> - } - nonEditModeContent={{value}} - > - ); -} diff --git a/front/src/modules/ui/board/card-field/hooks/useBoardCardField.ts b/front/src/modules/ui/board/card-field/hooks/useBoardCardField.ts deleted file mode 100644 index 0545e4909..000000000 --- a/front/src/modules/ui/board/card-field/hooks/useBoardCardField.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; - -import { BoardCardFieldContext } from '../states/BoardCardFieldContext'; -import { isBoardCardFieldInEditModeScopedState } from '../states/isBoardCardFieldInEditModeScopedState'; - -export function useBoardCardField() { - const [isBoardCardFieldInEditMode, setIsBoardCardFieldInEditMode] = - useRecoilScopedState( - isBoardCardFieldInEditModeScopedState, - BoardCardFieldContext, - ); - - function openBoardCardField() { - setIsBoardCardFieldInEditMode(true); - } - - function closeBoardCardField() { - setIsBoardCardFieldInEditMode(false); - } - - return { - isBoardCardFieldInEditMode, - openBoardCardField, - closeBoardCardField, - }; -} diff --git a/front/src/modules/ui/board/card-field/states/BoardCardFieldContext.ts b/front/src/modules/ui/board/card-field/states/BoardCardFieldContext.ts deleted file mode 100644 index 6d8beeb4e..000000000 --- a/front/src/modules/ui/board/card-field/states/BoardCardFieldContext.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { createContext } from 'react'; - -export const BoardCardFieldContext = createContext(null); diff --git a/front/src/modules/ui/board/card-field/states/isBoardCardFieldInEditModeScopedState.ts b/front/src/modules/ui/board/card-field/states/isBoardCardFieldInEditModeScopedState.ts deleted file mode 100644 index 2fda71a0c..000000000 --- a/front/src/modules/ui/board/card-field/states/isBoardCardFieldInEditModeScopedState.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { atomFamily } from 'recoil'; - -export const isBoardCardFieldInEditModeScopedState = atomFamily< - boolean, - string ->({ - key: 'isBoardCardFieldInEditModeScopedState', - default: false, -}); diff --git a/front/src/modules/ui/board/card-field/types/BoardCardFieldHotkeyScope.ts b/front/src/modules/ui/board/card-field/types/BoardCardFieldHotkeyScope.ts deleted file mode 100644 index 98b6ade87..000000000 --- a/front/src/modules/ui/board/card-field/types/BoardCardFieldHotkeyScope.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum BoardCardFieldHotkeyScope { - BoardCardFieldEditMode = 'board-card-field-edit-mode', -} diff --git a/front/src/modules/ui/board/components/__tests__/Board.test.ts b/front/src/modules/ui/board/components/__tests__/Board.test.ts deleted file mode 100644 index 8701b3c9f..000000000 --- a/front/src/modules/ui/board/components/__tests__/Board.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -// TODO: refactor this test with Recoil -describe('getOptimisticlyUpdatedBoard', () => { - it('should return a new board with the updated cell', () => { - // const initialColumn1: string[] = ['item-1', 'item-2', 'item-3']; - // const initialColumn2: string[] = ['item-4', 'item-5']; - // const finalColumn1: string[] = ['item-2', 'item-3']; - // const finalColumn2: string[] = ['item-4', 'item-1', 'item-5']; - // const dropResult = { - // source: { - // droppableId: 'column-1', - // index: 0, - // }, - // destination: { - // droppableId: 'column-2', - // index: 1, - // }, - // } as DropResult; - // const initialBoard = [ - // { - // id: 'column-1', - // title: 'My Column', - // pipelineStageId: 'column-1', - // pipelineProgressIds: initialColumn1, - // }, - // { - // id: 'column-2', - // title: 'My Column', - // pipelineStageId: 'column-2', - // pipelineProgressIds: initialColumn2, - // }, - // ]; - // const updatedBoard = u( - // initialBoard, - // dropResult, - // ); - // const finalBoard = [ - // { - // id: 'column-1', - // title: 'My Column', - // pipelineStageId: 'column-1', - // pipelineProgressIds: finalColumn1, - // }, - // { - // id: 'column-2', - // title: 'My Column', - // pipelineStageId: 'column-2', - // pipelineProgressIds: finalColumn2, - // }, - // ]; - // expect(updatedBoard).toEqual(finalBoard); - // expect(updatedBoard).not.toBe(initialBoard); - }); -}); diff --git a/front/src/modules/ui/editable-field/components/EditableFieldEntityText.tsx b/front/src/modules/ui/editable-field/components/EditableFieldEntityText.tsx deleted file mode 100644 index 13b20c2f8..000000000 --- a/front/src/modules/ui/editable-field/components/EditableFieldEntityText.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { EditableField } from '@/ui/editable-field/components/EditableField'; -import { FieldContext } from '@/ui/editable-field/states/FieldContext'; -import { IconMap } from '@/ui/icon'; -import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit'; -import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; -import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql'; - -type OwnProps = { - company: Pick; -}; - -export function CompanyEditableFieldAddress({ company }: OwnProps) { - const [internalValue, setInternalValue] = useState(company.address); - - const [updateCompany] = useUpdateOneCompanyMutation(); - - useEffect(() => { - setInternalValue(company.address); - }, [company.address]); - - async function handleChange(newValue: string) { - setInternalValue(newValue); - } - - async function handleSubmit() { - await updateCompany({ - variables: { - where: { - id: company.id, - }, - data: { - address: internalValue ?? '', - }, - }, - }); - } - - async function handleCancel() { - setInternalValue(company.address); - } - - return ( - - } - editModeContent={ - { - handleChange(newValue); - }} - /> - } - displayModeContent={internalValue !== '' ? internalValue : 'No address'} - /> - - ); -} diff --git a/front/src/modules/ui/editable-field/property-box/components/PropertyBoxItem.tsx b/front/src/modules/ui/editable-field/property-box/components/PropertyBoxItem.tsx deleted file mode 100644 index 13cc1ce13..000000000 --- a/front/src/modules/ui/editable-field/property-box/components/PropertyBoxItem.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { ReactNode } from 'react'; -import styled from '@emotion/styled'; - -const StyledPropertyBoxItem = styled.div` - align-items: center; - display: flex; - gap: ${({ theme }) => theme.spacing(2)}; - padding: ${({ theme }) => theme.spacing(1)}; -`; - -const StyledIconContainer = styled.div` - align-items: center; - display: flex; - - svg { - align-items: center; - display: flex; - height: 16px; - justify-content: center; - width: 16px; - } -`; - -const StyledValueContainer = styled.div` - align-content: flex-start; - align-items: center; - color: ${({ theme }) => theme.font.color.primary}; - display: flex; - flex: 1 0 0; - flex-wrap: wrap; -`; - -const StyledLabelAndIconContainer = styled.div` - align-items: center; - color: ${({ theme }) => theme.font.color.tertiary}; - display: flex; - gap: ${({ theme }) => theme.spacing(1)}; -`; - -export function PropertyBoxItem({ - icon, - label, - value, -}: { - icon: ReactNode; - label?: string; - value: ReactNode; -}) { - return ( - - - {icon} - {label} - - {value} - - ); -} diff --git a/front/src/modules/ui/editable-field/variants/components/TextEditableField.tsx b/front/src/modules/ui/editable-field/variants/components/TextEditableField.tsx deleted file mode 100644 index 7bc3f2ecb..000000000 --- a/front/src/modules/ui/editable-field/variants/components/TextEditableField.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { EditableField } from '@/ui/editable-field/components/EditableField'; -import { FieldContext } from '@/ui/editable-field/states/FieldContext'; -import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit'; -import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; - -import { OverflowingTextWithTooltip } from '../../../tooltip/OverflowingTextWithTooltip'; - -type OwnProps = { - icon?: React.ReactNode; - placeholder?: string; - value: string | null | undefined; - onSubmit?: (newValue: string) => void; -}; - -export function TextEditableField({ - icon, - placeholder, - value, - onSubmit, -}: OwnProps) { - const [internalValue, setInternalValue] = useState(value); - - useEffect(() => { - setInternalValue(value); - }, [value]); - - async function handleChange(newValue: string) { - setInternalValue(newValue); - } - - async function handleSubmit() { - if (!internalValue) return; - - onSubmit?.(internalValue); - } - - async function handleCancel() { - setInternalValue(value); - } - - return ( - - { - handleChange(newValue); - }} - /> - } - displayModeContent={} - isDisplayModeContentEmpty={!(internalValue !== '')} - /> - - ); -} diff --git a/front/src/modules/ui/editable-field/variants/components/__stories__/TextEditableField.stories.tsx b/front/src/modules/ui/editable-field/variants/components/__stories__/TextEditableField.stories.tsx deleted file mode 100644 index fbffa6855..000000000 --- a/front/src/modules/ui/editable-field/variants/components/__stories__/TextEditableField.stories.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { IconUser } from '@tabler/icons-react'; - -import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; - -import { TextEditableField } from '../TextEditableField'; - -const meta: Meta = { - title: 'UI/EditableField/TextEditableField', - component: TextEditableField, - decorators: [ComponentDecorator], - argTypes: { - icon: { - type: 'boolean', - mapping: { - true: , - false: undefined, - }, - }, - }, - args: { - value: 'John Doe', - icon: true, - placeholder: 'Name', - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; diff --git a/front/src/modules/ui/table/components/EntityTable.tsx b/front/src/modules/ui/table/components/EntityTable.tsx index 3565c0f02..66cc7656f 100644 --- a/front/src/modules/ui/table/components/EntityTable.tsx +++ b/front/src/modules/ui/table/components/EntityTable.tsx @@ -1,6 +1,5 @@ import { useRef } from 'react'; import styled from '@emotion/styled'; -import { useRecoilValue } from 'recoil'; import type { ViewFieldDefinition, @@ -12,9 +11,9 @@ import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useLis import { useLeaveTableFocus } from '../hooks/useLeaveTableFocus'; import { useMapKeyboardToSoftFocus } from '../hooks/useMapKeyboardToSoftFocus'; +import { useResetTableRowSelection } from '../hooks/useResetTableRowSelection'; import { useSetRowSelectedState } from '../hooks/useSetRowSelectedState'; -import { EntityUpdateMutationHookContext } from '../states/EntityUpdateMutationHookContext'; -import { tableRowIdsState } from '../states/tableRowIdsState'; +import { EntityUpdateMutationContext } from '../states/EntityUpdateMutationHookContext'; import { TableHeader } from '../table-header/components/TableHeader'; import { EntityTableBody } from './EntityTableBody'; @@ -97,7 +96,7 @@ type OwnProps = { onColumnsChange?: (columns: ViewFieldDefinition[]) => void; onSortsUpdate?: (sorts: Array>) => void; onRowSelectionChange?: (rowSelection: string[]) => void; - useUpdateEntityMutation: any; + updateEntityMutation: any; }; export function EntityTable({ @@ -106,18 +105,12 @@ export function EntityTable({ availableSorts, onColumnsChange, onSortsUpdate, - useUpdateEntityMutation, + updateEntityMutation, }: OwnProps) { const tableBodyRef = useRef(null); - const rowIds = useRecoilValue(tableRowIdsState); const setRowSelectedState = useSetRowSelectedState(); - - function resetSelections() { - for (const rowId of rowIds) { - setRowSelectedState(rowId, false); - } - } + const resetTableRowSelection = useResetTableRowSelection(); useMapKeyboardToSoftFocus(); @@ -131,7 +124,7 @@ export function EntityTable({ }); return ( - + ({ - + ); } diff --git a/front/src/modules/ui/table/components/GenericEntityTableData.tsx b/front/src/modules/ui/table/components/GenericEntityTableData.tsx index 90388589f..58aae7899 100644 --- a/front/src/modules/ui/table/components/GenericEntityTableData.tsx +++ b/front/src/modules/ui/table/components/GenericEntityTableData.tsx @@ -16,7 +16,6 @@ export function GenericEntityTableData({ filterDefinitionArray: FilterDefinition[]; }) { const setEntityTableData = useSetEntityTableData(); - useGetRequest({ variables: { orderBy, where: whereFilters }, onCompleted: (data: any) => { diff --git a/front/src/modules/ui/table/editable-cell/components/HoverableMenuItem.tsx b/front/src/modules/ui/table/editable-cell/components/HoverableMenuItem.tsx deleted file mode 100644 index 33888c329..000000000 --- a/front/src/modules/ui/table/editable-cell/components/HoverableMenuItem.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import styled from '@emotion/styled'; - -export const HoverableMenuItem = styled.div` - align-items: center; - background: ${({ theme }) => theme.background.primary}; - border-radius: ${({ theme }) => theme.border.radius.sm}; - box-sizing: border-box; - cursor: pointer; - display: flex; - height: 100%; - position: relative; - transition: background 0.1s ease; - user-select: none; - width: 100%; - - &:hover { - background: ${({ theme }) => theme.background.transparent.light}; - } -`; diff --git a/front/src/modules/ui/table/hooks/useInitializeEntityTable.ts b/front/src/modules/ui/table/hooks/useInitializeEntityTable.ts deleted file mode 100644 index c16d3ef61..000000000 --- a/front/src/modules/ui/table/hooks/useInitializeEntityTable.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useEffect } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; - -import { numberOfTableRowsState } from '../states/numberOfTableRowsState'; -import { tableRowIdsState } from '../states/tableRowIdsState'; - -import { useResetTableRowSelection } from './useResetTableRowSelection'; - -export function useInitializeEntityTable() { - const resetTableRowSelection = useResetTableRowSelection(); - - const tableRowIds = useRecoilValue(tableRowIdsState); - - useEffect(() => { - resetTableRowSelection(); - }, [resetTableRowSelection]); - - const setNumberOfTableRows = useSetRecoilState(numberOfTableRowsState); - - useEffect(() => { - setNumberOfTableRows(tableRowIds?.length); - }, [tableRowIds, setNumberOfTableRows]); -} diff --git a/front/src/modules/ui/table/hooks/useInitializeEntityTableFilters.ts b/front/src/modules/ui/table/hooks/useInitializeEntityTableFilters.ts deleted file mode 100644 index 130e5e6fb..000000000 --- a/front/src/modules/ui/table/hooks/useInitializeEntityTableFilters.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useEffect } from 'react'; - -import { availableFiltersScopedState } from '@/ui/filter-n-sort/states/availableFiltersScopedState'; -import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition'; -import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; - -import { TableContext } from '../states/TableContext'; - -export function useInitializeEntityTableFilters({ - availableFilters, -}: { - availableFilters: FilterDefinition[]; -}) { - const [, setAvailableFilters] = useRecoilScopedState( - availableFiltersScopedState, - TableContext, - ); - - useEffect(() => { - setAvailableFilters(availableFilters); - }, [setAvailableFilters, availableFilters]); -} diff --git a/front/src/modules/ui/table/hooks/useSetTableRowIds.ts b/front/src/modules/ui/table/hooks/useSetTableRowIds.ts new file mode 100644 index 000000000..9751b56e7 --- /dev/null +++ b/front/src/modules/ui/table/hooks/useSetTableRowIds.ts @@ -0,0 +1,19 @@ +import { useRecoilCallback } from 'recoil'; + +import { tableRowIdsState } from '../states/tableRowIdsState'; + +export function useSetTableRowIds() { + return useRecoilCallback( + ({ set, snapshot }) => + (rowIds: string[]) => { + const currentRowIds = snapshot + .getLoadable(tableRowIdsState) + .valueOrThrow(); + + if (JSON.stringify(rowIds) !== JSON.stringify(currentRowIds)) { + set(tableRowIdsState, rowIds); + } + }, + [], + ); +} diff --git a/front/src/modules/ui/table/hooks/useUpdateEntityField.ts b/front/src/modules/ui/table/hooks/useUpdateEntityField.ts index 5dcd1fbdc..4fbe45c75 100644 --- a/front/src/modules/ui/table/hooks/useUpdateEntityField.ts +++ b/front/src/modules/ui/table/hooks/useUpdateEntityField.ts @@ -17,7 +17,6 @@ import { isViewFieldText } from '@/ui/editable-field/types/guards/isViewFieldTex import { isViewFieldTextValue } from '@/ui/editable-field/types/guards/isViewFieldTextValue'; import { isViewFieldURL } from '@/ui/editable-field/types/guards/isViewFieldURL'; import { isViewFieldURLValue } from '@/ui/editable-field/types/guards/isViewFieldURLValue'; -import { EntityUpdateMutationHookContext } from '@/ui/table/states/EntityUpdateMutationHookContext'; import { isViewFieldChipValue } from '../../editable-field/types/guards/isViewFieldChipValue'; import { @@ -42,11 +41,10 @@ import { ViewFieldURLMetadata, ViewFieldURLValue, } from '../../editable-field/types/ViewField'; +import { EntityUpdateMutationContext } from '../states/EntityUpdateMutationHookContext'; export function useUpdateEntityField() { - const useUpdateEntityMutation = useContext(EntityUpdateMutationHookContext); - - const [updateEntity] = useUpdateEntityMutation(); + const updateEntity = useContext(EntityUpdateMutationContext); return function updatePeopleField< MetadataType extends ViewFieldMetadata, diff --git a/front/src/modules/ui/table/hooks/useUpsertEntityTableItem.ts b/front/src/modules/ui/table/hooks/useUpsertEntityTableItem.ts new file mode 100644 index 000000000..bbf8c3146 --- /dev/null +++ b/front/src/modules/ui/table/hooks/useUpsertEntityTableItem.ts @@ -0,0 +1,19 @@ +import { useRecoilCallback } from 'recoil'; + +import { tableEntitiesFamilyState } from '@/ui/table/states/tableEntitiesFamilyState'; + +export function useUpsertEntityTableItem() { + return useRecoilCallback( + ({ set, snapshot }) => + (entity: T) => { + const currentEntity = snapshot + .getLoadable(tableEntitiesFamilyState(entity.id)) + .valueOrThrow(); + + if (JSON.stringify(currentEntity) !== JSON.stringify(entity)) { + set(tableEntitiesFamilyState(entity.id), entity); + } + }, + [], + ); +} diff --git a/front/src/modules/ui/table/hooks/useUpsertTableRowId.ts b/front/src/modules/ui/table/hooks/useUpsertTableRowId.ts new file mode 100644 index 000000000..a812f811d --- /dev/null +++ b/front/src/modules/ui/table/hooks/useUpsertTableRowId.ts @@ -0,0 +1,17 @@ +import { useRecoilCallback } from 'recoil'; + +import { tableRowIdsState } from '../states/tableRowIdsState'; + +export function useUpsertTableRowId() { + return useRecoilCallback( + ({ set, snapshot }) => + (rowId: string) => { + const currentRowIds = snapshot + .getLoadable(tableRowIdsState) + .valueOrThrow(); + + set(tableRowIdsState, Array.from(new Set([...currentRowIds, rowId]))); + }, + [], + ); +} diff --git a/front/src/modules/ui/table/states/EntityUpdateMutationHookContext.ts b/front/src/modules/ui/table/states/EntityUpdateMutationHookContext.ts index 621130e58..384ee7a45 100644 --- a/front/src/modules/ui/table/states/EntityUpdateMutationHookContext.ts +++ b/front/src/modules/ui/table/states/EntityUpdateMutationHookContext.ts @@ -1,3 +1,3 @@ import { createContext } from 'react'; -export const EntityUpdateMutationHookContext = createContext(null); +export const EntityUpdateMutationContext = createContext(null); diff --git a/front/src/pages/companies/Companies.tsx b/front/src/pages/companies/Companies.tsx index 79d04fd69..99b280bf6 100644 --- a/front/src/pages/companies/Companies.tsx +++ b/front/src/pages/companies/Companies.tsx @@ -1,7 +1,6 @@ import { getOperationName } from '@apollo/client/utilities'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { useRecoilState } from 'recoil'; import { v4 } from 'uuid'; import { CompanyTable } from '@/companies/table/components/CompanyTable'; @@ -11,8 +10,9 @@ import { SEARCH_COMPANY_QUERY } from '@/search/queries/search'; import { IconBuildingSkyscraper } from '@/ui/icon'; import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer'; import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar'; +import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem'; +import { useUpsertTableRowId } from '@/ui/table/hooks/useUpsertTableRowId'; import { TableContext } from '@/ui/table/states/TableContext'; -import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { useInsertOneCompanyMutation } from '~/generated/graphql'; @@ -23,7 +23,8 @@ const StyledTableContainer = styled.div` export function Companies() { const [insertCompany] = useInsertOneCompanyMutation(); - const [tableRowIds, setTableRowIds] = useRecoilState(tableRowIdsState); + const upsertEntityTableItem = useUpsertEntityTableItem(); + const upsertTableRowIds = useUpsertTableRowId(); async function handleAddButtonClick() { const newCompanyId: string = v4(); @@ -44,12 +45,17 @@ export function Companies() { name: '', domainName: '', address: '', - createdAt: '', + createdAt: new Date().toISOString(), + accountOwner: null, + linkedinUrl: '', + employees: null, }, }, update: (cache, { data }) => { - data?.createOneCompany.id && - setTableRowIds([data?.createOneCompany.id, ...tableRowIds]); + if (data?.createOneCompany) { + upsertTableRowIds(data?.createOneCompany.id); + upsertEntityTableItem(data?.createOneCompany); + } }, refetchQueries: [getOperationName(SEARCH_COMPANY_QUERY) ?? ''], }); @@ -58,22 +64,20 @@ export function Companies() { const theme = useTheme(); return ( - <> - } - onAddButtonClick={handleAddButtonClick} - > - - - - - - - - - - - + } + onAddButtonClick={handleAddButtonClick} + > + + + + + + + + + + ); } diff --git a/front/src/pages/people/People.tsx b/front/src/pages/people/People.tsx index 4ed08ad2c..6f0f49efc 100644 --- a/front/src/pages/people/People.tsx +++ b/front/src/pages/people/People.tsx @@ -1,6 +1,5 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { useRecoilState } from 'recoil'; import { v4 } from 'uuid'; import { PeopleTable } from '@/people/table/components/PeopleTable'; @@ -9,8 +8,9 @@ import { TableActionBarButtonDeletePeople } from '@/people/table/components/Tabl import { IconUser } from '@/ui/icon'; import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer'; import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar'; +import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem'; +import { useUpsertTableRowId } from '@/ui/table/hooks/useUpsertTableRowId'; import { TableContext } from '@/ui/table/states/TableContext'; -import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { useInsertOnePersonMutation } from '~/generated/graphql'; @@ -21,7 +21,8 @@ const StyledTableContainer = styled.div` export function People() { const [insertOnePerson] = useInsertOnePersonMutation(); - const [tableRowIds, setTableRowIds] = useRecoilState(tableRowIdsState); + const upsertEntityTableItem = useUpsertEntityTableItem(); + const upsertTableRowIds = useUpsertTableRowId(); async function handleAddButtonClick() { const newPersonId: string = v4(); @@ -45,8 +46,10 @@ export function People() { }, }, update: (cache, { data }) => { - data?.createOnePerson?.id && - setTableRowIds([data?.createOnePerson.id, ...tableRowIds]); + if (data?.createOnePerson) { + upsertTableRowIds(data?.createOnePerson.id); + upsertEntityTableItem(data?.createOnePerson); + } }, }); }