FieldDisplay & FieldInput (#1708)
* Removed view field duplicate types * wip * wip 2 * wip 3 * Unified state for fields * Renaming * Wip * Post merge * Post post merge * wip * Delete unused file * Boolean and Probability * Finished InlineCell * Renamed EditableCell to TableCell * Finished double texts * Finished MoneyField * Fixed bug inline cell click outside * Fixed hotkey scope * Final fixes * Phone * Fix url and number input validation * Fix * Fix position * wip refactor activity editor * Fixed activity editor --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -7,10 +7,9 @@ import { useBoardContext } from '@/ui/board/hooks/useBoardContext';
|
||||
import { useCurrentCardSelected } from '@/ui/board/hooks/useCurrentCardSelected';
|
||||
import { visibleBoardCardFieldsScopedSelector } from '@/ui/board/states/selectors/visibleBoardCardFieldsScopedSelector';
|
||||
import { EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
||||
import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField';
|
||||
import { EditableFieldDefinitionContext } from '@/ui/editable-field/contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '@/ui/editable-field/contexts/EditableFieldEntityIdContext';
|
||||
import { EditableFieldMutationContext } from '@/ui/editable-field/contexts/EditableFieldMutationContext';
|
||||
import { InlineCell } from '@/ui/editable-field/components/InlineCell';
|
||||
import { EditableFieldHotkeyScope } from '@/ui/editable-field/types/EditableFieldHotkeyScope';
|
||||
import { FieldContext } from '@/ui/field/contexts/FieldContext';
|
||||
import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
|
||||
@ -158,27 +157,28 @@ export const CompanyBoardCard = () => {
|
||||
</StyledCheckboxContainer>
|
||||
</StyledBoardCardHeader>
|
||||
<StyledBoardCardBody>
|
||||
<EditableFieldMutationContext.Provider
|
||||
value={useUpdateOnePipelineProgressMutation}
|
||||
>
|
||||
<EditableFieldEntityIdContext.Provider value={boardCardId}>
|
||||
{visibleBoardCardFields.map((viewField) => (
|
||||
<PreventSelectOnClickContainer key={viewField.key}>
|
||||
<EditableFieldDefinitionContext.Provider
|
||||
value={{
|
||||
key: viewField.key,
|
||||
name: viewField.name,
|
||||
Icon: viewField.Icon,
|
||||
type: viewField.metadata.type,
|
||||
metadata: viewField.metadata,
|
||||
}}
|
||||
>
|
||||
<GenericEditableField />
|
||||
</EditableFieldDefinitionContext.Provider>
|
||||
</PreventSelectOnClickContainer>
|
||||
))}
|
||||
</EditableFieldEntityIdContext.Provider>
|
||||
</EditableFieldMutationContext.Provider>
|
||||
{visibleBoardCardFields.map((viewField) => (
|
||||
<PreventSelectOnClickContainer key={viewField.key}>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: boardCardId,
|
||||
recoilScopeId: boardCardId + viewField.key,
|
||||
fieldDefinition: {
|
||||
key: viewField.key,
|
||||
name: viewField.name,
|
||||
Icon: viewField.Icon,
|
||||
type: viewField.type,
|
||||
metadata: viewField.metadata,
|
||||
useEditButton: viewField.useEditButton,
|
||||
},
|
||||
useUpdateEntityMutation: useUpdateOnePipelineProgressMutation,
|
||||
hotkeyScope: EditableFieldHotkeyScope.EditableField,
|
||||
}}
|
||||
>
|
||||
<InlineCell />
|
||||
</FieldContext.Provider>
|
||||
</PreventSelectOnClickContainer>
|
||||
))}
|
||||
</StyledBoardCardBody>
|
||||
</StyledBoardCard>
|
||||
</StyledBoardCardWrapper>
|
||||
|
||||
@ -1,102 +0,0 @@
|
||||
import { useFilteredSearchCompanyQuery } from '@/companies/hooks/useFilteredSearchCompanyQuery';
|
||||
import { IconBuildingSkyscraper } from '@/ui/icon';
|
||||
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 { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { isCreateModeScopedState } from '@/ui/table/editable-cell/states/isCreateModeScopedState';
|
||||
import { DoubleTextCellEdit } from '@/ui/table/editable-cell/type/components/DoubleTextCellEdit';
|
||||
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 { useInsertOneCompanyMutation } from '~/generated/graphql';
|
||||
|
||||
export type OwnProps = {
|
||||
companyId: string | null;
|
||||
onSubmit: (newCompany: CompanyPickerSelectedCompany | null) => void;
|
||||
onCancel?: () => void;
|
||||
createModeEnabled?: boolean;
|
||||
width?: number;
|
||||
};
|
||||
|
||||
export type CompanyPickerSelectedCompany = EntityForSelect & {
|
||||
domainName: string;
|
||||
};
|
||||
|
||||
export const CompanyPickerCell = ({
|
||||
companyId,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
createModeEnabled,
|
||||
width,
|
||||
}: OwnProps) => {
|
||||
const [isCreateMode, setIsCreateMode] = useRecoilScopedState(
|
||||
isCreateModeScopedState,
|
||||
);
|
||||
|
||||
const [insertCompany] = useInsertOneCompanyMutation();
|
||||
|
||||
const [relationPickerSearchFilter] = useRecoilScopedState(
|
||||
relationPickerSearchFilterScopedState,
|
||||
);
|
||||
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
const companies = useFilteredSearchCompanyQuery({
|
||||
searchFilter: relationPickerSearchFilter,
|
||||
selectedIds: [companyId ?? ''],
|
||||
});
|
||||
|
||||
const handleCompanySelected = async (
|
||||
company: CompanyPickerSelectedCompany | null | undefined,
|
||||
) => {
|
||||
onSubmit(company ?? null);
|
||||
};
|
||||
|
||||
const handleStartCreation = () => {
|
||||
setIsCreateMode(true);
|
||||
setHotkeyScope(TableHotkeyScope.CellDoubleTextInput);
|
||||
};
|
||||
|
||||
const handleCreate = async (firstValue: string, secondValue: string) => {
|
||||
const insertCompanyRequest = await insertCompany({
|
||||
variables: {
|
||||
data: {
|
||||
name: firstValue,
|
||||
domainName: secondValue,
|
||||
address: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
const companyCreated = insertCompanyRequest.data?.createOneCompany;
|
||||
companyCreated &&
|
||||
onSubmit({
|
||||
id: companyCreated.id,
|
||||
name: companyCreated.name,
|
||||
entityType: Entity.Company,
|
||||
domainName: companyCreated.domainName,
|
||||
});
|
||||
setIsCreateMode(false);
|
||||
};
|
||||
return isCreateMode ? (
|
||||
<DoubleTextCellEdit
|
||||
firstValue={relationPickerSearchFilter}
|
||||
secondValue=""
|
||||
firstValuePlaceholder="Name"
|
||||
secondValuePlaceholder="Url"
|
||||
onSubmit={handleCreate}
|
||||
/>
|
||||
) : (
|
||||
<SingleEntitySelect
|
||||
EmptyIcon={IconBuildingSkyscraper}
|
||||
emptyLabel="No Company"
|
||||
entitiesToSelect={companies.entitiesToSelect}
|
||||
loading={companies.loading}
|
||||
onCancel={onCancel}
|
||||
onCreate={createModeEnabled ? handleStartCreation : undefined}
|
||||
onEntitySelected={handleCompanySelected}
|
||||
selectedEntity={companies.selectedEntities[0]}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,14 +1,14 @@
|
||||
import {
|
||||
ViewFieldBooleanMetadata,
|
||||
ViewFieldChipMetadata,
|
||||
ViewFieldDateMetadata,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldMoneyMetadata,
|
||||
ViewFieldNumberMetadata,
|
||||
ViewFieldRelationMetadata,
|
||||
ViewFieldTextMetadata,
|
||||
ViewFieldURLMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
FieldBooleanMetadata,
|
||||
FieldChipMetadata,
|
||||
FieldDateMetadata,
|
||||
FieldMetadata,
|
||||
FieldMoneyMetadata,
|
||||
FieldNumberMetadata,
|
||||
FieldRelationMetadata,
|
||||
FieldTextMetadata,
|
||||
FieldURLMetadata,
|
||||
} from '@/ui/field/types/FieldMetadata';
|
||||
import {
|
||||
IconBrandLinkedin,
|
||||
IconBrandX,
|
||||
@ -24,7 +24,7 @@ import {
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { ColumnDefinition } from '@/ui/table/types/ColumnDefinition';
|
||||
|
||||
export const companiesAvailableColumnDefinitions: ColumnDefinition<ViewFieldMetadata>[] =
|
||||
export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[] =
|
||||
[
|
||||
{
|
||||
key: 'name',
|
||||
@ -32,125 +32,131 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<ViewFieldMeta
|
||||
Icon: IconBuildingSkyscraper,
|
||||
size: 180,
|
||||
index: 0,
|
||||
type: 'chip',
|
||||
metadata: {
|
||||
type: 'chip',
|
||||
urlFieldName: 'domainName',
|
||||
contentFieldName: 'name',
|
||||
relationType: Entity.Company,
|
||||
placeHolder: 'Company Name',
|
||||
},
|
||||
isVisible: true,
|
||||
} as ColumnDefinition<ViewFieldChipMetadata>,
|
||||
} satisfies ColumnDefinition<FieldChipMetadata>,
|
||||
{
|
||||
key: 'domainName',
|
||||
name: 'URL',
|
||||
Icon: IconLink,
|
||||
size: 100,
|
||||
index: 1,
|
||||
type: 'url',
|
||||
metadata: {
|
||||
type: 'url',
|
||||
fieldName: 'domainName',
|
||||
placeHolder: 'example.com',
|
||||
},
|
||||
isVisible: true,
|
||||
} as ColumnDefinition<ViewFieldURLMetadata>,
|
||||
useEditButton: true,
|
||||
} satisfies ColumnDefinition<FieldURLMetadata>,
|
||||
{
|
||||
key: 'accountOwner',
|
||||
name: 'Account Owner',
|
||||
Icon: IconUserCircle,
|
||||
size: 150,
|
||||
index: 2,
|
||||
type: 'relation',
|
||||
metadata: {
|
||||
type: 'relation',
|
||||
fieldName: 'accountOwner',
|
||||
relationType: Entity.User,
|
||||
},
|
||||
isVisible: true,
|
||||
} satisfies ColumnDefinition<ViewFieldRelationMetadata>,
|
||||
} satisfies ColumnDefinition<FieldRelationMetadata>,
|
||||
{
|
||||
key: 'createdAt',
|
||||
name: 'Creation',
|
||||
Icon: IconCalendarEvent,
|
||||
size: 150,
|
||||
index: 3,
|
||||
type: 'date',
|
||||
metadata: {
|
||||
type: 'date',
|
||||
fieldName: 'createdAt',
|
||||
},
|
||||
isVisible: true,
|
||||
} satisfies ColumnDefinition<ViewFieldDateMetadata>,
|
||||
} satisfies ColumnDefinition<FieldDateMetadata>,
|
||||
{
|
||||
key: 'employees',
|
||||
name: 'Employees',
|
||||
Icon: IconUsers,
|
||||
size: 150,
|
||||
index: 4,
|
||||
type: 'number',
|
||||
metadata: {
|
||||
type: 'number',
|
||||
fieldName: 'employees',
|
||||
isPositive: true,
|
||||
placeHolder: 'Employees',
|
||||
},
|
||||
isVisible: true,
|
||||
} satisfies ColumnDefinition<ViewFieldNumberMetadata>,
|
||||
} satisfies ColumnDefinition<FieldNumberMetadata>,
|
||||
{
|
||||
key: 'linkedin',
|
||||
name: 'LinkedIn',
|
||||
Icon: IconBrandLinkedin,
|
||||
size: 170,
|
||||
index: 5,
|
||||
type: 'url',
|
||||
metadata: {
|
||||
type: 'url',
|
||||
fieldName: 'linkedinUrl',
|
||||
placeHolder: 'LinkedIn URL',
|
||||
},
|
||||
isVisible: true,
|
||||
} satisfies ColumnDefinition<ViewFieldURLMetadata>,
|
||||
useEditButton: true,
|
||||
} satisfies ColumnDefinition<FieldURLMetadata>,
|
||||
{
|
||||
key: 'address',
|
||||
name: 'Address',
|
||||
Icon: IconMap,
|
||||
size: 170,
|
||||
index: 6,
|
||||
type: 'text',
|
||||
metadata: {
|
||||
type: 'text',
|
||||
fieldName: 'address',
|
||||
placeHolder: 'Address', // Hack: Fake character to prevent password-manager from filling the field
|
||||
},
|
||||
isVisible: true,
|
||||
} satisfies ColumnDefinition<ViewFieldTextMetadata>,
|
||||
} satisfies ColumnDefinition<FieldTextMetadata>,
|
||||
{
|
||||
key: 'idealCustomerProfile',
|
||||
name: 'ICP',
|
||||
Icon: IconTarget,
|
||||
size: 150,
|
||||
index: 7,
|
||||
type: 'boolean',
|
||||
metadata: {
|
||||
type: 'boolean',
|
||||
fieldName: 'idealCustomerProfile',
|
||||
},
|
||||
isVisible: false,
|
||||
} satisfies ColumnDefinition<ViewFieldBooleanMetadata>,
|
||||
} satisfies ColumnDefinition<FieldBooleanMetadata>,
|
||||
{
|
||||
key: 'annualRecurringRevenue',
|
||||
name: 'ARR',
|
||||
Icon: IconMoneybag,
|
||||
size: 150,
|
||||
index: 8,
|
||||
type: 'moneyAmount',
|
||||
metadata: {
|
||||
type: 'moneyAmount',
|
||||
fieldName: 'annualRecurringRevenue',
|
||||
placeHolder: 'ARR',
|
||||
},
|
||||
} satisfies ColumnDefinition<ViewFieldMoneyMetadata>,
|
||||
} satisfies ColumnDefinition<FieldMoneyMetadata>,
|
||||
{
|
||||
key: 'xUrl',
|
||||
name: 'Twitter',
|
||||
Icon: IconBrandX,
|
||||
size: 150,
|
||||
index: 9,
|
||||
type: 'url',
|
||||
metadata: {
|
||||
type: 'url',
|
||||
fieldName: 'xUrl',
|
||||
placeHolder: 'X',
|
||||
},
|
||||
isVisible: false,
|
||||
} satisfies ColumnDefinition<ViewFieldURLMetadata>,
|
||||
useEditButton: true,
|
||||
} satisfies ColumnDefinition<FieldURLMetadata>,
|
||||
];
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState';
|
||||
import { entityFieldsFamilyState } from '@/ui/field/states/entityFieldsFamilyState';
|
||||
import { useGetCompanyQuery } from '~/generated/graphql';
|
||||
|
||||
export const useCompanyQuery = (id: string) => {
|
||||
const updateCompanyShowPage = useSetRecoilState(
|
||||
genericEntitiesFamilyState(id),
|
||||
);
|
||||
const updateCompanyShowPage = useSetRecoilState(entityFieldsFamilyState(id));
|
||||
|
||||
return useGetCompanyQuery({
|
||||
variables: { where: { id } },
|
||||
onCompleted: (data) => {
|
||||
|
||||
@ -28,6 +28,7 @@ export const useFilteredSearchCompanyQuery = ({
|
||||
avatarUrl: getLogoUrlFromDomainName(company.domainName),
|
||||
domainName: company.domainName,
|
||||
avatarType: 'squared',
|
||||
originalEntity: company,
|
||||
}),
|
||||
selectedIds: selectedIds,
|
||||
limit,
|
||||
|
||||
@ -5,7 +5,7 @@ import { boardCardIdsByColumnIdFamilyState } from '@/ui/board/states/boardCardId
|
||||
import { boardColumnsState } from '@/ui/board/states/boardColumnsState';
|
||||
import { savedBoardColumnsState } from '@/ui/board/states/savedBoardColumnsState';
|
||||
import { BoardColumnDefinition } from '@/ui/board/types/BoardColumnDefinition';
|
||||
import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState';
|
||||
import { entityFieldsFamilyState } from '@/ui/field/states/entityFieldsFamilyState';
|
||||
import { isThemeColor } from '@/ui/theme/utils/castStringAsThemeColor';
|
||||
import { Pipeline } from '~/generated/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
@ -72,10 +72,7 @@ export const useUpdateCompanyBoard = () =>
|
||||
|
||||
if (!isDeeplyEqual(currentCompanyProgress, companyProgress)) {
|
||||
set(companyProgressesFamilyState(id), companyProgress);
|
||||
set(
|
||||
genericEntitiesFamilyState(id),
|
||||
companyProgress.pipelineProgress,
|
||||
);
|
||||
set(entityFieldsFamilyState(id), companyProgress.pipelineProgress);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ export const CompanyTableMockMode = () => {
|
||||
ViewBarRecoilScopeContext: TableRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<EntityTable updateEntityMutation={[useUpdateOneCompanyMutation()]} />
|
||||
<EntityTable updateEntityMutation={useUpdateOneCompanyMutation} />
|
||||
</ViewBarContext.Provider>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user