mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-18 17:12:53 +03:00
[PersonShow] use fieldDefinition for editable fields (#1178)
* [PersonShow] use fieldDefinition for editable fields * remove unused files * fix company chip display field
This commit is contained in:
parent
007e42a2e6
commit
a30222fe76
41
front/src/modules/companies/components/CompanyPicker.tsx
Normal file
41
front/src/modules/companies/components/CompanyPicker.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||||
|
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||||
|
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
|
|
||||||
|
import { useFilteredSearchCompanyQuery } from '../queries';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
companyId: string | null;
|
||||||
|
onSubmit: (newCompanyId: EntityForSelect | null) => void;
|
||||||
|
onCancel?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CompanyPicker({ companyId, onSubmit, onCancel }: OwnProps) {
|
||||||
|
const [searchFilter] = useRecoilScopedState(
|
||||||
|
relationPickerSearchFilterScopedState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const companies = useFilteredSearchCompanyQuery({
|
||||||
|
searchFilter,
|
||||||
|
selectedIds: companyId ? [companyId] : [],
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleEntitySelected(
|
||||||
|
selectedCompany: EntityForSelect | null | undefined,
|
||||||
|
) {
|
||||||
|
onSubmit(selectedCompany ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SingleEntitySelect
|
||||||
|
onEntitySelected={handleEntitySelected}
|
||||||
|
onCancel={onCancel}
|
||||||
|
entities={{
|
||||||
|
loading: companies.loading,
|
||||||
|
entitiesToSelect: companies.entitiesToSelect,
|
||||||
|
selectedEntity: companies.selectedEntities[0],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -1,73 +0,0 @@
|
|||||||
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
|
|
||||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
|
||||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
|
||||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
|
||||||
import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
|
|
||||||
import { isCreateModeScopedState } from '@/ui/table/editable-cell/states/isCreateModeScopedState';
|
|
||||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
import {
|
|
||||||
Company,
|
|
||||||
Person,
|
|
||||||
useUpdateOnePersonMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
export type OwnProps = {
|
|
||||||
people: Pick<Person, 'id'> & { company?: Pick<Company, 'id'> | null };
|
|
||||||
};
|
|
||||||
|
|
||||||
export function PeopleCompanyPicker({ people }: OwnProps) {
|
|
||||||
const [, setIsCreating] = useRecoilScopedState(isCreateModeScopedState);
|
|
||||||
|
|
||||||
const [searchFilter] = useRecoilScopedState(
|
|
||||||
relationPickerSearchFilterScopedState,
|
|
||||||
);
|
|
||||||
const [updatePerson] = useUpdateOnePersonMutation();
|
|
||||||
|
|
||||||
const { closeEditableCell } = useEditableCell();
|
|
||||||
|
|
||||||
const addToScopeStack = useSetHotkeyScope();
|
|
||||||
|
|
||||||
const companies = useFilteredSearchCompanyQuery({
|
|
||||||
searchFilter,
|
|
||||||
selectedIds: people.company?.id ? [people.company.id] : [],
|
|
||||||
});
|
|
||||||
|
|
||||||
async function handleEntitySelected(
|
|
||||||
entity: EntityForSelect | null | undefined,
|
|
||||||
) {
|
|
||||||
if (entity) {
|
|
||||||
await updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: people.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
company: { connect: { id: entity.id } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeEditableCell();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCreate() {
|
|
||||||
setIsCreating(true);
|
|
||||||
addToScopeStack(TableHotkeyScope.CellDoubleTextInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SingleEntitySelect
|
|
||||||
onCreate={handleCreate}
|
|
||||||
onCancel={() => closeEditableCell()}
|
|
||||||
onEntitySelected={handleEntitySelected}
|
|
||||||
entities={{
|
|
||||||
entitiesToSelect: companies.entitiesToSelect,
|
|
||||||
selectedEntity: companies.selectedEntities[0],
|
|
||||||
loading: companies.loading,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
import {
|
|
||||||
IconCalendar,
|
|
||||||
IconMail,
|
|
||||||
IconMap,
|
|
||||||
IconPhone,
|
|
||||||
} from '@tabler/icons-react';
|
|
||||||
|
|
||||||
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
|
|
||||||
import { DateEditableField } from '@/ui/editable-field/variants/components/DateEditableField';
|
|
||||||
import { PhoneEditableField } from '@/ui/editable-field/variants/components/PhoneEditableField';
|
|
||||||
import { TextEditableField } from '@/ui/editable-field/variants/components/TextEditableField';
|
|
||||||
import {
|
|
||||||
Company,
|
|
||||||
Person,
|
|
||||||
useUpdateOnePersonMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { PeopleCompanyEditableField } from '../editable-field/components/PeopleCompanyEditableField';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
person: Pick<
|
|
||||||
Person,
|
|
||||||
'id' | 'city' | 'email' | 'displayName' | 'phone' | 'createdAt'
|
|
||||||
> & {
|
|
||||||
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function PersonPropertyBox({ person }: OwnProps) {
|
|
||||||
const [updatePerson] = useUpdateOnePersonMutation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PropertyBox extraPadding={true}>
|
|
||||||
<TextEditableField
|
|
||||||
value={person.email}
|
|
||||||
icon={<IconMail />}
|
|
||||||
placeholder={'Email'}
|
|
||||||
onSubmit={(newEmail) => {
|
|
||||||
updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: person.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
email: newEmail,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PhoneEditableField
|
|
||||||
value={person.phone}
|
|
||||||
icon={<IconPhone />}
|
|
||||||
placeholder={'Phone'}
|
|
||||||
onSubmit={(newPhone) => {
|
|
||||||
updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: person.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
phone: newPhone,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<DateEditableField
|
|
||||||
value={person.createdAt}
|
|
||||||
icon={<IconCalendar />}
|
|
||||||
onSubmit={(newDate) => {
|
|
||||||
updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: person.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
createdAt: newDate,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PeopleCompanyEditableField people={person} />
|
|
||||||
<TextEditableField
|
|
||||||
value={person.city}
|
|
||||||
icon={<IconMap />}
|
|
||||||
placeholder={'City'}
|
|
||||||
onSubmit={(newCity) => {
|
|
||||||
updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: person.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
city: newCity,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PropertyBox>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
import { BrowserRouter } from 'react-router-dom';
|
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
|
||||||
|
|
||||||
import { mockedPeopleData } from '~/testing/mock-data/people';
|
|
||||||
|
|
||||||
import { PeopleCompanyEditableField } from '../../editable-field/components/PeopleCompanyEditableField';
|
|
||||||
|
|
||||||
const meta: Meta<typeof PeopleCompanyEditableField> = {
|
|
||||||
title: 'Modules/People/EditableFields/PeopleCompanyEditableField',
|
|
||||||
component: PeopleCompanyEditableField,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default meta;
|
|
||||||
type Story = StoryObj<typeof PeopleCompanyEditableField>;
|
|
||||||
|
|
||||||
export const Default: Story = {
|
|
||||||
render: () => (
|
|
||||||
<BrowserRouter>
|
|
||||||
<PeopleCompanyEditableField people={mockedPeopleData[0]} />
|
|
||||||
</BrowserRouter>
|
|
||||||
),
|
|
||||||
};
|
|
@ -1,49 +0,0 @@
|
|||||||
import { IconBuildingSkyscraper } from '@tabler/icons-react';
|
|
||||||
|
|
||||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
|
||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
|
||||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
|
||||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { Company, Person } from '~/generated/graphql';
|
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
|
||||||
|
|
||||||
import { PeopleCompanyEditableFieldEditMode } from './PeopleCompanyEditableFieldEditMode';
|
|
||||||
|
|
||||||
export type OwnProps = {
|
|
||||||
people: Pick<Person, 'id'> & {
|
|
||||||
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function PeopleCompanyEditableField({ people }: OwnProps) {
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<RecoilScope>
|
|
||||||
<EditableField
|
|
||||||
useEditButton
|
|
||||||
customEditHotkeyScope={{
|
|
||||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
|
||||||
}}
|
|
||||||
iconLabel={<IconBuildingSkyscraper />}
|
|
||||||
editModeContent={
|
|
||||||
<PeopleCompanyEditableFieldEditMode people={people} />
|
|
||||||
}
|
|
||||||
displayModeContent={
|
|
||||||
people.company ? (
|
|
||||||
<CompanyChip
|
|
||||||
id={people.company.id}
|
|
||||||
name={people.company.name}
|
|
||||||
pictureUrl={getLogoUrlFromDomainName(people.company.domainName)}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
isDisplayModeContentEmpty={!people.company}
|
|
||||||
isDisplayModeFixHeight
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
|
|
||||||
import { useEditableField } from '@/ui/editable-field/hooks/useEditableField';
|
|
||||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
|
||||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
|
||||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
import {
|
|
||||||
Company,
|
|
||||||
Person,
|
|
||||||
useUpdateOnePersonMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
export type OwnProps = {
|
|
||||||
people: Pick<Person, 'id'> & { company?: Pick<Company, 'id'> | null };
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
|
||||||
left: 0px;
|
|
||||||
position: absolute;
|
|
||||||
top: -8px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export function PeopleCompanyEditableFieldEditMode({ people }: OwnProps) {
|
|
||||||
const { closeEditableField } = useEditableField();
|
|
||||||
|
|
||||||
const [searchFilter] = useRecoilScopedState(
|
|
||||||
relationPickerSearchFilterScopedState,
|
|
||||||
);
|
|
||||||
const [updatePerson] = useUpdateOnePersonMutation();
|
|
||||||
|
|
||||||
const companies = useFilteredSearchCompanyQuery({
|
|
||||||
searchFilter,
|
|
||||||
selectedIds: people.company?.id ? [people.company.id] : [],
|
|
||||||
});
|
|
||||||
|
|
||||||
async function handleEntitySelected(
|
|
||||||
entity: EntityForSelect | null | undefined,
|
|
||||||
) {
|
|
||||||
if (entity) {
|
|
||||||
await updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: people.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
company: { connect: { id: entity.id } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeEditableField();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCancel() {
|
|
||||||
closeEditableField();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledContainer>
|
|
||||||
<SingleEntitySelect
|
|
||||||
onEntitySelected={handleEntitySelected}
|
|
||||||
entities={{
|
|
||||||
entitiesToSelect: companies.entitiesToSelect,
|
|
||||||
selectedEntity: companies.selectedEntities[0],
|
|
||||||
loading: companies.loading,
|
|
||||||
}}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
/>
|
|
||||||
</StyledContainer>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,5 +1,7 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState';
|
||||||
import { useGetPersonQuery } from '~/generated/graphql';
|
import { useGetPersonQuery } from '~/generated/graphql';
|
||||||
|
|
||||||
export const GET_PERSON = gql`
|
export const GET_PERSON = gql`
|
||||||
@ -37,5 +39,13 @@ export const GET_PERSON = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function usePersonQuery(id: string) {
|
export function usePersonQuery(id: string) {
|
||||||
return useGetPersonQuery({ variables: { id } });
|
const updatePersonShowPage = useSetRecoilState(
|
||||||
|
genericEntitiesFamilyState(id),
|
||||||
|
);
|
||||||
|
return useGetPersonQuery({
|
||||||
|
variables: { id },
|
||||||
|
onCompleted: (data) => {
|
||||||
|
updatePersonShowPage(data?.findUniquePerson);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import { useContext } from 'react';
|
|||||||
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||||
|
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||||
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
||||||
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
||||||
import { isFieldText } from '../types/guards/isFieldText';
|
import { isFieldText } from '../types/guards/isFieldText';
|
||||||
@ -10,6 +11,7 @@ import { isFieldURL } from '../types/guards/isFieldURL';
|
|||||||
|
|
||||||
import { GenericEditableDateField } from './GenericEditableDateField';
|
import { GenericEditableDateField } from './GenericEditableDateField';
|
||||||
import { GenericEditableNumberField } from './GenericEditableNumberField';
|
import { GenericEditableNumberField } from './GenericEditableNumberField';
|
||||||
|
import { GenericEditablePhoneField } from './GenericEditablePhoneField';
|
||||||
import { GenericEditableRelationField } from './GenericEditableRelationField';
|
import { GenericEditableRelationField } from './GenericEditableRelationField';
|
||||||
import { GenericEditableTextField } from './GenericEditableTextField';
|
import { GenericEditableTextField } from './GenericEditableTextField';
|
||||||
import { GenericEditableURLField } from './GenericEditableURLField';
|
import { GenericEditableURLField } from './GenericEditableURLField';
|
||||||
@ -30,6 +32,8 @@ export function GenericEditableField() {
|
|||||||
return <GenericEditableURLField />;
|
return <GenericEditableURLField />;
|
||||||
} else if (isFieldText(fieldDefinition)) {
|
} else if (isFieldText(fieldDefinition)) {
|
||||||
return <GenericEditableTextField />;
|
return <GenericEditableTextField />;
|
||||||
|
} else if (isFieldPhone(fieldDefinition)) {
|
||||||
|
return <GenericEditablePhoneField />;
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown field metadata type: ${fieldDefinition.type} in GenericEditableField`,
|
`Unknown field metadata type: ${fieldDefinition.type} in GenericEditableField`,
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { PhoneInputDisplay } from '@/ui/input/phone/components/PhoneInputDisplay';
|
||||||
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
|
|
||||||
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
|
import { FieldContext } from '../states/FieldContext';
|
||||||
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
|
import { FieldPhoneMetadata } from '../types/FieldMetadata';
|
||||||
|
|
||||||
|
import { EditableField } from './EditableField';
|
||||||
|
import { GenericEditablePhoneFieldEditMode } from './GenericEditablePhoneFieldEditMode';
|
||||||
|
|
||||||
|
export function GenericEditablePhoneField() {
|
||||||
|
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||||
|
const currentEditableFieldDefinition = useContext(
|
||||||
|
EditableFieldDefinitionContext,
|
||||||
|
) as FieldDefinition<FieldPhoneMetadata>;
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
|
<EditableField
|
||||||
|
useEditButton
|
||||||
|
iconLabel={currentEditableFieldDefinition.icon}
|
||||||
|
editModeContent={<GenericEditablePhoneFieldEditMode />}
|
||||||
|
displayModeContent={<PhoneInputDisplay value={fieldValue} />}
|
||||||
|
isDisplayModeContentEmpty={!fieldValue}
|
||||||
|
/>
|
||||||
|
</RecoilScope>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
import { useContext, useRef, useState } from 'react';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
||||||
|
|
||||||
|
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers';
|
||||||
|
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||||
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
|
import { FieldPhoneMetadata } from '../types/FieldMetadata';
|
||||||
|
|
||||||
|
export function GenericEditablePhoneFieldEditMode() {
|
||||||
|
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||||
|
const currentEditableFieldDefinition = useContext(
|
||||||
|
EditableFieldDefinitionContext,
|
||||||
|
) as FieldDefinition<FieldPhoneMetadata>;
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [internalValue, setInternalValue] = useState(fieldValue);
|
||||||
|
|
||||||
|
const updateField = useUpdateGenericEntityField();
|
||||||
|
|
||||||
|
const wrapperRef = useRef(null);
|
||||||
|
|
||||||
|
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel);
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
if (internalValue === fieldValue) return;
|
||||||
|
|
||||||
|
setFieldValue(internalValue);
|
||||||
|
|
||||||
|
if (currentEditableFieldEntityId && updateField) {
|
||||||
|
updateField(
|
||||||
|
currentEditableFieldEntityId,
|
||||||
|
currentEditableFieldDefinition,
|
||||||
|
internalValue,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancel() {
|
||||||
|
setFieldValue(fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(newValue: string) {
|
||||||
|
setInternalValue(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={wrapperRef}>
|
||||||
|
<TextInputEdit
|
||||||
|
autoFocus
|
||||||
|
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||||
|
value={internalValue}
|
||||||
|
onChange={(newValue: string) => {
|
||||||
|
handleChange(newValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||||
import { PersonChip } from '@/people/components/PersonChip';
|
import { PersonChip } from '@/people/components/PersonChip';
|
||||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
import { UserChip } from '@/users/components/UserChip';
|
import { UserChip } from '@/users/components/UserChip';
|
||||||
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
@ -45,6 +47,19 @@ export function GenericEditableRelationFieldDisplayMode() {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case Entity.Company: {
|
||||||
|
return (
|
||||||
|
<CompanyChip
|
||||||
|
id={fieldValue?.id ?? ''}
|
||||||
|
name={fieldValue?.name ?? ''}
|
||||||
|
pictureUrl={
|
||||||
|
fieldValue?.domainName
|
||||||
|
? getLogoUrlFromDomainName(fieldValue.domainName)
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown relation type: "${currentEditableFieldDefinition.metadata.relationType}"
|
`Unknown relation type: "${currentEditableFieldDefinition.metadata.relationType}"
|
||||||
|
@ -2,6 +2,7 @@ import { useContext } from 'react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { CompanyPicker } from '@/companies/components/CompanyPicker';
|
||||||
import { PeoplePicker } from '@/people/components/PeoplePicker';
|
import { PeoplePicker } from '@/people/components/PeoplePicker';
|
||||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
@ -54,6 +55,15 @@ function RelationPicker({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case Entity.Company: {
|
||||||
|
return (
|
||||||
|
<CompanyPicker
|
||||||
|
companyId={fieldValue ? fieldValue.id : ''}
|
||||||
|
onSubmit={handleEntitySubmit}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationField`,
|
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationField`,
|
||||||
|
@ -24,7 +24,7 @@ import { getLogoUrlFromDomainName } from '~/utils';
|
|||||||
import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField';
|
import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField';
|
||||||
import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer';
|
import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer';
|
||||||
|
|
||||||
import { companyShowFieldsDefinition } from './constants/companyShowFieldsDefinition';
|
import { companyShowFieldDefinition } from './constants/companyShowFieldDefinition';
|
||||||
|
|
||||||
export function CompanyShow() {
|
export function CompanyShow() {
|
||||||
const companyId = useParams().companyId ?? '';
|
const companyId = useParams().companyId ?? '';
|
||||||
@ -33,11 +33,12 @@ export function CompanyShow() {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { data } = useCompanyQuery(companyId);
|
const { data } = useCompanyQuery(companyId);
|
||||||
const company = data?.findUniqueCompany;
|
const company = data?.findUniqueCompany;
|
||||||
const isFavorite =
|
|
||||||
company?.Favorite && company?.Favorite?.length > 0 ? true : false;
|
|
||||||
|
|
||||||
if (!company) return <></>;
|
if (!company) return <></>;
|
||||||
|
|
||||||
|
const isFavorite =
|
||||||
|
company.Favorite && company.Favorite?.length > 0 ? true : false;
|
||||||
|
|
||||||
async function handleFavoriteButtonClick() {
|
async function handleFavoriteButtonClick() {
|
||||||
if (isFavorite) deleteCompanyFavorite(companyId);
|
if (isFavorite) deleteCompanyFavorite(companyId);
|
||||||
else insertCompanyFavorite(companyId);
|
else insertCompanyFavorite(companyId);
|
||||||
@ -45,7 +46,7 @@ export function CompanyShow() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<WithTopBarContainer
|
<WithTopBarContainer
|
||||||
title={company?.name ?? ''}
|
title={company.name ?? ''}
|
||||||
hasBackButton
|
hasBackButton
|
||||||
isFavorite={isFavorite}
|
isFavorite={isFavorite}
|
||||||
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
|
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
|
||||||
@ -54,10 +55,10 @@ export function CompanyShow() {
|
|||||||
<ShowPageContainer>
|
<ShowPageContainer>
|
||||||
<ShowPageLeftContainer>
|
<ShowPageLeftContainer>
|
||||||
<ShowPageSummaryCard
|
<ShowPageSummaryCard
|
||||||
id={company?.id}
|
id={company.id}
|
||||||
logoOrAvatar={getLogoUrlFromDomainName(company?.domainName ?? '')}
|
logoOrAvatar={getLogoUrlFromDomainName(company.domainName ?? '')}
|
||||||
title={company?.name ?? 'No name'}
|
title={company.name ?? 'No name'}
|
||||||
date={company?.createdAt ?? ''}
|
date={company.createdAt ?? ''}
|
||||||
renderTitleEditComponent={() => (
|
renderTitleEditComponent={() => (
|
||||||
<CompanyNameEditableField company={company} />
|
<CompanyNameEditableField company={company} />
|
||||||
)}
|
)}
|
||||||
@ -67,7 +68,7 @@ export function CompanyShow() {
|
|||||||
value={useUpdateOneCompanyMutation}
|
value={useUpdateOneCompanyMutation}
|
||||||
>
|
>
|
||||||
<EditableFieldEntityIdContext.Provider value={company.id}>
|
<EditableFieldEntityIdContext.Provider value={company.id}>
|
||||||
{companyShowFieldsDefinition.map((fieldDefinition) => {
|
{companyShowFieldDefinition.map((fieldDefinition) => {
|
||||||
return (
|
return (
|
||||||
<EditableFieldDefinitionContext.Provider
|
<EditableFieldDefinitionContext.Provider
|
||||||
value={fieldDefinition}
|
value={fieldDefinition}
|
||||||
@ -84,7 +85,7 @@ export function CompanyShow() {
|
|||||||
</ShowPageLeftContainer>
|
</ShowPageLeftContainer>
|
||||||
<ShowPageRightContainer>
|
<ShowPageRightContainer>
|
||||||
<Timeline
|
<Timeline
|
||||||
entity={{ id: company?.id ?? '', type: CommentableType.Company }}
|
entity={{ id: company.id ?? '', type: CommentableType.Company }}
|
||||||
/>
|
/>
|
||||||
</ShowPageRightContainer>
|
</ShowPageRightContainer>
|
||||||
</ShowPageContainer>
|
</ShowPageContainer>
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
} from '@/ui/icon';
|
} from '@/ui/icon';
|
||||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
|
|
||||||
export const companyShowFieldsDefinition: FieldDefinition<FieldMetadata>[] = [
|
export const companyShowFieldDefinition: FieldDefinition<FieldMetadata>[] = [
|
||||||
{
|
{
|
||||||
id: 'domainName',
|
id: 'domainName',
|
||||||
label: 'Domain name',
|
label: 'Domain name',
|
@ -4,8 +4,12 @@ import { useTheme } from '@emotion/react';
|
|||||||
|
|
||||||
import { Timeline } from '@/activities/timeline/components/Timeline';
|
import { Timeline } from '@/activities/timeline/components/Timeline';
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
import { PersonPropertyBox } from '@/people/components/PersonPropertyBox';
|
|
||||||
import { GET_PERSON, usePersonQuery } from '@/people/queries';
|
import { GET_PERSON, usePersonQuery } from '@/people/queries';
|
||||||
|
import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField';
|
||||||
|
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
|
||||||
|
import { EditableFieldDefinitionContext } from '@/ui/editable-field/states/EditableFieldDefinitionContext';
|
||||||
|
import { EditableFieldEntityIdContext } from '@/ui/editable-field/states/EditableFieldEntityIdContext';
|
||||||
|
import { EditableFieldMutationContext } from '@/ui/editable-field/states/EditableFieldMutationContext';
|
||||||
import { IconUser } from '@/ui/icon';
|
import { IconUser } from '@/ui/icon';
|
||||||
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
||||||
import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer';
|
import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer';
|
||||||
@ -13,25 +17,30 @@ import { ShowPageRightContainer } from '@/ui/layout/show-page/components/ShowPag
|
|||||||
import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard';
|
import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard';
|
||||||
import {
|
import {
|
||||||
CommentableType,
|
CommentableType,
|
||||||
|
useUpdateOnePersonMutation,
|
||||||
useUploadPersonPictureMutation,
|
useUploadPersonPictureMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
import { PeopleFullNameEditableField } from '../../modules/people/editable-field/components/PeopleFullNameEditableField';
|
import { PeopleFullNameEditableField } from '../../modules/people/editable-field/components/PeopleFullNameEditableField';
|
||||||
import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer';
|
import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer';
|
||||||
|
|
||||||
|
import { personShowFieldDefinition } from './constants/personShowFieldDefinition';
|
||||||
|
|
||||||
export function PersonShow() {
|
export function PersonShow() {
|
||||||
const personId = useParams().personId ?? '';
|
const personId = useParams().personId ?? '';
|
||||||
|
|
||||||
const { insertPersonFavorite, deletePersonFavorite } = useFavorites();
|
const { insertPersonFavorite, deletePersonFavorite } = useFavorites();
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
const { data } = usePersonQuery(personId);
|
const { data } = usePersonQuery(personId);
|
||||||
const person = data?.findUniquePerson;
|
const person = data?.findUniquePerson;
|
||||||
const isFavorite =
|
|
||||||
person?.Favorite && person?.Favorite?.length > 0 ? true : false;
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
const [uploadPicture] = useUploadPersonPictureMutation();
|
const [uploadPicture] = useUploadPersonPictureMutation();
|
||||||
|
|
||||||
|
if (!person) return <></>;
|
||||||
|
|
||||||
|
const isFavorite =
|
||||||
|
person.Favorite && person.Favorite?.length > 0 ? true : false;
|
||||||
|
|
||||||
async function onUploadPicture(file: File) {
|
async function onUploadPicture(file: File) {
|
||||||
if (!file || !person?.id) {
|
if (!file || !person?.id) {
|
||||||
return;
|
return;
|
||||||
@ -39,7 +48,7 @@ export function PersonShow() {
|
|||||||
await uploadPicture({
|
await uploadPicture({
|
||||||
variables: {
|
variables: {
|
||||||
file,
|
file,
|
||||||
id: person?.id,
|
id: person.id,
|
||||||
},
|
},
|
||||||
refetchQueries: [getOperationName(GET_PERSON) ?? ''],
|
refetchQueries: [getOperationName(GET_PERSON) ?? ''],
|
||||||
});
|
});
|
||||||
@ -52,7 +61,7 @@ export function PersonShow() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<WithTopBarContainer
|
<WithTopBarContainer
|
||||||
title={person?.firstName ?? ''}
|
title={person.firstName ?? ''}
|
||||||
icon={<IconUser size={theme.icon.size.md} />}
|
icon={<IconUser size={theme.icon.size.md} />}
|
||||||
hasBackButton
|
hasBackButton
|
||||||
isFavorite={isFavorite}
|
isFavorite={isFavorite}
|
||||||
@ -61,20 +70,37 @@ export function PersonShow() {
|
|||||||
<ShowPageContainer>
|
<ShowPageContainer>
|
||||||
<ShowPageLeftContainer>
|
<ShowPageLeftContainer>
|
||||||
<ShowPageSummaryCard
|
<ShowPageSummaryCard
|
||||||
id={person?.id}
|
id={person.id}
|
||||||
title={person?.displayName ?? 'No name'}
|
title={person.displayName ?? 'No name'}
|
||||||
logoOrAvatar={person?.avatarUrl ?? undefined}
|
logoOrAvatar={person.avatarUrl ?? undefined}
|
||||||
date={person?.createdAt ?? ''}
|
date={person.createdAt ?? ''}
|
||||||
renderTitleEditComponent={() =>
|
renderTitleEditComponent={() =>
|
||||||
person ? <PeopleFullNameEditableField people={person} /> : <></>
|
person ? <PeopleFullNameEditableField people={person} /> : <></>
|
||||||
}
|
}
|
||||||
onUploadPicture={onUploadPicture}
|
onUploadPicture={onUploadPicture}
|
||||||
/>
|
/>
|
||||||
{person && <PersonPropertyBox person={person} />}
|
<PropertyBox extraPadding={true}>
|
||||||
|
<EditableFieldMutationContext.Provider
|
||||||
|
value={useUpdateOnePersonMutation}
|
||||||
|
>
|
||||||
|
<EditableFieldEntityIdContext.Provider value={person.id}>
|
||||||
|
{personShowFieldDefinition.map((fieldDefinition) => {
|
||||||
|
return (
|
||||||
|
<EditableFieldDefinitionContext.Provider
|
||||||
|
value={fieldDefinition}
|
||||||
|
key={fieldDefinition.id}
|
||||||
|
>
|
||||||
|
<GenericEditableField />
|
||||||
|
</EditableFieldDefinitionContext.Provider>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</EditableFieldEntityIdContext.Provider>
|
||||||
|
</EditableFieldMutationContext.Provider>
|
||||||
|
</PropertyBox>
|
||||||
</ShowPageLeftContainer>
|
</ShowPageLeftContainer>
|
||||||
<ShowPageRightContainer>
|
<ShowPageRightContainer>
|
||||||
<Timeline
|
<Timeline
|
||||||
entity={{ id: person?.id ?? '', type: CommentableType.Person }}
|
entity={{ id: person.id ?? '', type: CommentableType.Person }}
|
||||||
/>
|
/>
|
||||||
</ShowPageRightContainer>
|
</ShowPageRightContainer>
|
||||||
</ShowPageContainer>
|
</ShowPageContainer>
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
import { FieldDefinition } from '@/ui/editable-field/types/FieldDefinition';
|
||||||
|
import {
|
||||||
|
FieldDateMetadata,
|
||||||
|
FieldMetadata,
|
||||||
|
FieldPhoneMetadata,
|
||||||
|
FieldRelationMetadata,
|
||||||
|
FieldTextMetadata,
|
||||||
|
} from '@/ui/editable-field/types/FieldMetadata';
|
||||||
|
import {
|
||||||
|
IconBuildingSkyscraper,
|
||||||
|
IconCalendar,
|
||||||
|
IconMail,
|
||||||
|
IconMap,
|
||||||
|
IconPhone,
|
||||||
|
} from '@/ui/icon';
|
||||||
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
|
|
||||||
|
export const personShowFieldDefinition: FieldDefinition<FieldMetadata>[] = [
|
||||||
|
{
|
||||||
|
id: 'email',
|
||||||
|
label: 'Email',
|
||||||
|
icon: <IconMail />,
|
||||||
|
type: 'text',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'email',
|
||||||
|
placeHolder: 'Email',
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldTextMetadata>,
|
||||||
|
{
|
||||||
|
id: 'phone',
|
||||||
|
label: 'Phone',
|
||||||
|
icon: <IconPhone />,
|
||||||
|
type: 'phone',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'phone',
|
||||||
|
placeHolder: 'Phone',
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldPhoneMetadata>,
|
||||||
|
{
|
||||||
|
id: 'createdAt',
|
||||||
|
label: 'Created at',
|
||||||
|
icon: <IconCalendar />,
|
||||||
|
type: 'date',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'createdAt',
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldDateMetadata>,
|
||||||
|
{
|
||||||
|
id: 'company',
|
||||||
|
label: 'Company',
|
||||||
|
icon: <IconBuildingSkyscraper />,
|
||||||
|
type: 'relation',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'company',
|
||||||
|
relationType: Entity.Company,
|
||||||
|
useEditButton: true,
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldRelationMetadata>,
|
||||||
|
{
|
||||||
|
id: 'city',
|
||||||
|
label: 'City',
|
||||||
|
icon: <IconMap />,
|
||||||
|
type: 'text',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'city',
|
||||||
|
placeHolder: 'City',
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldTextMetadata>,
|
||||||
|
];
|
Loading…
Reference in New Issue
Block a user