Feat/generic editable cell all types (#987)
* Added generic relation cell * Deactivated debug * Added default warning * Put back display component * Removed unused types * wip * Renamed to view field * Use new view field structure to have chip working * Finished * Added a temp feature flag * Added double text chip cell * Ok * Finished tables * Fixed icon size * Fixed bug on date field * Use icon index * Fix * Fixed naming * Fix * removed file from merge * Fixed tests * Coverage
This commit is contained in:
@ -167,8 +167,8 @@
|
|||||||
"workerDirectory": "public"
|
"workerDirectory": "public"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"statements": 70,
|
"statements": 65,
|
||||||
"lines": 70,
|
"lines": 65,
|
||||||
"functions": 60,
|
"functions": 60,
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"src/generated/**/*"
|
"src/generated/**/*"
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import { AppInternalHooks } from '~/sync-hooks/AppInternalHooks';
|
|||||||
import { SignInUp } from './pages/auth/SignInUp';
|
import { SignInUp } from './pages/auth/SignInUp';
|
||||||
|
|
||||||
// TEMP FEATURE FLAG FOR VIEW FIELDS
|
// TEMP FEATURE FLAG FOR VIEW FIELDS
|
||||||
export const ACTIVATE_VIEW_FIELDS = false;
|
export const ACTIVATE_VIEW_FIELDS = true;
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
import { IconBuildingSkyscraper } from '@tabler/icons-react';
|
|
||||||
|
|
||||||
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
|
||||||
import {
|
|
||||||
ViewFieldChipMetadata,
|
|
||||||
ViewFieldDefinition,
|
|
||||||
} from '@/ui/table/types/ViewField';
|
|
||||||
|
|
||||||
export const companyViewFields: ViewFieldDefinition<unknown>[] = [
|
|
||||||
{
|
|
||||||
columnLabel: 'Name',
|
|
||||||
columnIcon: <IconBuildingSkyscraper size={16} />,
|
|
||||||
columnSize: 150,
|
|
||||||
type: 'chip',
|
|
||||||
columnOrder: 1,
|
|
||||||
metadata: {
|
|
||||||
urlFieldName: 'domainName',
|
|
||||||
contentFieldName: 'name',
|
|
||||||
relationType: Entity.Company,
|
|
||||||
},
|
|
||||||
} as ViewFieldDefinition<ViewFieldChipMetadata>,
|
|
||||||
];
|
|
||||||
105
front/src/modules/companies/constants/companyViewFields.tsx
Normal file
105
front/src/modules/companies/constants/companyViewFields.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import {
|
||||||
|
IconBuildingSkyscraper,
|
||||||
|
IconCalendarEvent,
|
||||||
|
IconLink,
|
||||||
|
IconMap,
|
||||||
|
IconUser,
|
||||||
|
IconUsers,
|
||||||
|
} from '@/ui/icon/index';
|
||||||
|
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
||||||
|
import {
|
||||||
|
ViewFieldChipMetadata,
|
||||||
|
ViewFieldDateMetadata,
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
ViewFieldNumberMetadata,
|
||||||
|
ViewFieldRelationMetadata,
|
||||||
|
ViewFieldTextMetadata,
|
||||||
|
ViewFieldURLMetadata,
|
||||||
|
} from '@/ui/table/types/ViewField';
|
||||||
|
|
||||||
|
export const companyViewFields: ViewFieldDefinition<ViewFieldMetadata>[] = [
|
||||||
|
{
|
||||||
|
id: 'name',
|
||||||
|
columnLabel: 'Name',
|
||||||
|
columnIcon: <IconBuildingSkyscraper />,
|
||||||
|
columnSize: 180,
|
||||||
|
columnOrder: 1,
|
||||||
|
metadata: {
|
||||||
|
type: 'chip',
|
||||||
|
urlFieldName: 'domainName',
|
||||||
|
contentFieldName: 'name',
|
||||||
|
relationType: Entity.Company,
|
||||||
|
},
|
||||||
|
} as ViewFieldDefinition<ViewFieldChipMetadata>,
|
||||||
|
{
|
||||||
|
id: 'domainName',
|
||||||
|
columnLabel: 'URL',
|
||||||
|
columnIcon: <IconLink />,
|
||||||
|
columnSize: 100,
|
||||||
|
columnOrder: 2,
|
||||||
|
metadata: {
|
||||||
|
type: 'url',
|
||||||
|
fieldName: 'domainName',
|
||||||
|
placeHolder: 'example.com',
|
||||||
|
},
|
||||||
|
} as ViewFieldDefinition<ViewFieldURLMetadata>,
|
||||||
|
{
|
||||||
|
id: 'accountOwner',
|
||||||
|
columnLabel: 'Account Owner',
|
||||||
|
columnIcon: <IconUser />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 3,
|
||||||
|
metadata: {
|
||||||
|
type: 'relation',
|
||||||
|
fieldName: 'accountOwner',
|
||||||
|
relationType: Entity.User,
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldRelationMetadata>,
|
||||||
|
{
|
||||||
|
id: 'createdAt',
|
||||||
|
columnLabel: 'Creation',
|
||||||
|
columnIcon: <IconCalendarEvent />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 4,
|
||||||
|
metadata: {
|
||||||
|
type: 'date',
|
||||||
|
fieldName: 'createdAt',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldDateMetadata>,
|
||||||
|
{
|
||||||
|
id: 'employees',
|
||||||
|
columnLabel: 'Employees',
|
||||||
|
columnIcon: <IconUsers />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 5,
|
||||||
|
metadata: {
|
||||||
|
type: 'number',
|
||||||
|
fieldName: 'employees',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldNumberMetadata>,
|
||||||
|
{
|
||||||
|
id: 'linkedin',
|
||||||
|
columnLabel: 'LinkedIn',
|
||||||
|
columnIcon: <IconMap />,
|
||||||
|
columnSize: 170,
|
||||||
|
columnOrder: 6,
|
||||||
|
metadata: {
|
||||||
|
type: 'url',
|
||||||
|
fieldName: 'linkedinUrl',
|
||||||
|
placeHolder: 'LinkedIn URL',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldURLMetadata>,
|
||||||
|
{
|
||||||
|
id: 'address',
|
||||||
|
columnLabel: 'Address',
|
||||||
|
columnIcon: <IconMap />,
|
||||||
|
columnSize: 170,
|
||||||
|
columnOrder: 7,
|
||||||
|
metadata: {
|
||||||
|
type: 'text',
|
||||||
|
fieldName: 'address',
|
||||||
|
placeHolder: 'Address',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldTextMetadata>,
|
||||||
|
];
|
||||||
@ -22,8 +22,20 @@ export function CompanyCreatedAtEditableField({ company }: OwnProps) {
|
|||||||
setInternalValue(company.createdAt);
|
setInternalValue(company.createdAt);
|
||||||
}, [company.createdAt]);
|
}, [company.createdAt]);
|
||||||
|
|
||||||
|
// TODO: refactor change and submit
|
||||||
async function handleChange(newValue: string) {
|
async function handleChange(newValue: string) {
|
||||||
setInternalValue(newValue);
|
setInternalValue(newValue);
|
||||||
|
|
||||||
|
await updateCompany({
|
||||||
|
variables: {
|
||||||
|
where: {
|
||||||
|
id: company.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
createdAt: newValue ?? '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { companyViewFields } from '@/companies/constants/companyFieldMetadataArray';
|
import { companyViewFields } from '@/companies/constants/companyViewFields';
|
||||||
import { CompaniesSelectedSortType, defaultOrderBy } from '@/companies/queries';
|
import { CompaniesSelectedSortType, defaultOrderBy } from '@/companies/queries';
|
||||||
import { GenericEntityTableData } from '@/people/components/GenericEntityTableData';
|
|
||||||
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
|
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
|
||||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||||
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
||||||
import { IconList } from '@/ui/icon';
|
import { IconList } from '@/ui/icon';
|
||||||
import { useRecoilScopedValue } from '@/ui/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import { EntityTable } from '@/ui/table/components/EntityTableV2';
|
import { EntityTable } from '@/ui/table/components/EntityTableV2';
|
||||||
|
import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTableData';
|
||||||
import { TableContext } from '@/ui/table/states/TableContext';
|
import { TableContext } from '@/ui/table/states/TableContext';
|
||||||
import {
|
import {
|
||||||
CompanyOrderByWithRelationInput,
|
CompanyOrderByWithRelationInput,
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
import {
|
|
||||||
IconBriefcase,
|
|
||||||
IconBuildingSkyscraper,
|
|
||||||
IconMap,
|
|
||||||
} from '@tabler/icons-react';
|
|
||||||
|
|
||||||
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
|
||||||
import {
|
|
||||||
ViewFieldDefinition,
|
|
||||||
ViewFieldRelationMetadata,
|
|
||||||
ViewFieldTextMetadata,
|
|
||||||
} from '@/ui/table/types/ViewField';
|
|
||||||
|
|
||||||
export const peopleViewFields: ViewFieldDefinition<unknown>[] = [
|
|
||||||
{
|
|
||||||
id: 'city',
|
|
||||||
columnLabel: 'City',
|
|
||||||
columnIcon: <IconMap size={16} />,
|
|
||||||
columnSize: 150,
|
|
||||||
type: 'text',
|
|
||||||
columnOrder: 1,
|
|
||||||
metadata: {
|
|
||||||
fieldName: 'city',
|
|
||||||
placeHolder: 'City',
|
|
||||||
},
|
|
||||||
} as ViewFieldDefinition<ViewFieldTextMetadata>,
|
|
||||||
{
|
|
||||||
id: 'jobTitle',
|
|
||||||
columnLabel: 'Job title',
|
|
||||||
columnIcon: <IconBriefcase size={16} />,
|
|
||||||
columnSize: 150,
|
|
||||||
type: 'text',
|
|
||||||
columnOrder: 2,
|
|
||||||
metadata: {
|
|
||||||
fieldName: 'jobTitle',
|
|
||||||
placeHolder: 'Job title',
|
|
||||||
},
|
|
||||||
} as ViewFieldDefinition<ViewFieldTextMetadata>,
|
|
||||||
{
|
|
||||||
id: 'company',
|
|
||||||
columnLabel: 'Company',
|
|
||||||
columnIcon: <IconBuildingSkyscraper size={16} />,
|
|
||||||
columnSize: 150,
|
|
||||||
type: 'relation',
|
|
||||||
relationType: Entity.Company,
|
|
||||||
columnOrder: 3,
|
|
||||||
metadata: {
|
|
||||||
fieldName: 'company',
|
|
||||||
relationType: Entity.Company,
|
|
||||||
},
|
|
||||||
} as ViewFieldDefinition<ViewFieldRelationMetadata>,
|
|
||||||
];
|
|
||||||
122
front/src/modules/people/constants/peopleViewFields.tsx
Normal file
122
front/src/modules/people/constants/peopleViewFields.tsx
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import {
|
||||||
|
IconBrandLinkedin,
|
||||||
|
IconBriefcase,
|
||||||
|
IconBuildingSkyscraper,
|
||||||
|
IconCalendarEvent,
|
||||||
|
IconMail,
|
||||||
|
IconMap,
|
||||||
|
IconPhone,
|
||||||
|
IconUser,
|
||||||
|
} from '@/ui/icon/index';
|
||||||
|
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
||||||
|
import {
|
||||||
|
ViewFieldDateMetadata,
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldDoubleTextChipMetadata,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
ViewFieldPhoneMetadata,
|
||||||
|
ViewFieldRelationMetadata,
|
||||||
|
ViewFieldTextMetadata,
|
||||||
|
ViewFieldURLMetadata,
|
||||||
|
} from '@/ui/table/types/ViewField';
|
||||||
|
|
||||||
|
export const peopleViewFields: ViewFieldDefinition<ViewFieldMetadata>[] = [
|
||||||
|
{
|
||||||
|
id: 'displayName',
|
||||||
|
columnLabel: 'People',
|
||||||
|
columnIcon: <IconUser />,
|
||||||
|
columnSize: 210,
|
||||||
|
columnOrder: 1,
|
||||||
|
metadata: {
|
||||||
|
type: 'double-text-chip',
|
||||||
|
firstValueFieldName: 'firstName',
|
||||||
|
secondValueFieldName: 'lastName',
|
||||||
|
firstValuePlaceholder: 'First name',
|
||||||
|
secondValuePlaceholder: 'Last name',
|
||||||
|
entityType: Entity.Person,
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldDoubleTextChipMetadata>,
|
||||||
|
{
|
||||||
|
id: 'email',
|
||||||
|
columnLabel: 'Email',
|
||||||
|
columnIcon: <IconMail />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 2,
|
||||||
|
metadata: {
|
||||||
|
type: 'text',
|
||||||
|
fieldName: 'email',
|
||||||
|
placeHolder: 'Email',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldTextMetadata>,
|
||||||
|
{
|
||||||
|
id: 'company',
|
||||||
|
columnLabel: 'Company',
|
||||||
|
columnIcon: <IconBuildingSkyscraper />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 3,
|
||||||
|
metadata: {
|
||||||
|
type: 'relation',
|
||||||
|
fieldName: 'company',
|
||||||
|
relationType: Entity.Company,
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldRelationMetadata>,
|
||||||
|
{
|
||||||
|
id: 'phone',
|
||||||
|
columnLabel: 'Phone',
|
||||||
|
columnIcon: <IconPhone />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 4,
|
||||||
|
metadata: {
|
||||||
|
type: 'phone',
|
||||||
|
fieldName: 'phone',
|
||||||
|
placeHolder: 'Phone',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldPhoneMetadata>,
|
||||||
|
{
|
||||||
|
id: 'createdAt',
|
||||||
|
columnLabel: 'Creation',
|
||||||
|
columnIcon: <IconCalendarEvent />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 5,
|
||||||
|
metadata: {
|
||||||
|
type: 'date',
|
||||||
|
fieldName: 'createdAt',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldDateMetadata>,
|
||||||
|
{
|
||||||
|
id: 'city',
|
||||||
|
columnLabel: 'City',
|
||||||
|
columnIcon: <IconMap />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 6,
|
||||||
|
metadata: {
|
||||||
|
type: 'text',
|
||||||
|
fieldName: 'city',
|
||||||
|
placeHolder: 'City',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldTextMetadata>,
|
||||||
|
{
|
||||||
|
id: 'jobTitle',
|
||||||
|
columnLabel: 'Job title',
|
||||||
|
columnIcon: <IconBriefcase />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 7,
|
||||||
|
metadata: {
|
||||||
|
type: 'text',
|
||||||
|
fieldName: 'jobTitle',
|
||||||
|
placeHolder: 'Job title',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldTextMetadata>,
|
||||||
|
{
|
||||||
|
id: 'linkedin',
|
||||||
|
columnLabel: 'LinkedIn',
|
||||||
|
columnIcon: <IconBrandLinkedin />,
|
||||||
|
columnSize: 150,
|
||||||
|
columnOrder: 8,
|
||||||
|
metadata: {
|
||||||
|
type: 'url',
|
||||||
|
fieldName: 'linkedinUrl',
|
||||||
|
placeHolder: 'LinkedIn',
|
||||||
|
},
|
||||||
|
} satisfies ViewFieldDefinition<ViewFieldURLMetadata>,
|
||||||
|
];
|
||||||
@ -1,80 +0,0 @@
|
|||||||
import { useContext } from 'react';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect';
|
|
||||||
import { EntityUpdateMutationHookContext } from '@/ui/table/states/EntityUpdateMutationHookContext';
|
|
||||||
import { viewFieldsState } from '@/ui/table/states/viewFieldsState';
|
|
||||||
import { isViewFieldChip } from '@/ui/table/types/guards/isViewFieldChip';
|
|
||||||
import { isViewFieldRelation } from '@/ui/table/types/guards/isViewFieldRelation';
|
|
||||||
import { isViewFieldText } from '@/ui/table/types/guards/isViewFieldText';
|
|
||||||
|
|
||||||
export function useUpdateEntityField() {
|
|
||||||
const useUpdateEntityMutation = useContext(EntityUpdateMutationHookContext);
|
|
||||||
|
|
||||||
const [updateEntity] = useUpdateEntityMutation();
|
|
||||||
|
|
||||||
const viewFields = useRecoilValue(viewFieldsState);
|
|
||||||
|
|
||||||
return function updatePeopleField(
|
|
||||||
currentEntityId: string,
|
|
||||||
viewFieldId: string,
|
|
||||||
newFieldValue: unknown,
|
|
||||||
) {
|
|
||||||
const viewField = viewFields.find(
|
|
||||||
(metadata) => metadata.id === viewFieldId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!viewField) {
|
|
||||||
throw new Error(`View field not found for id ${viewFieldId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: improve type narrowing here with validation maybe ? Also validate the newFieldValue with linked type guards
|
|
||||||
if (isViewFieldRelation(viewField)) {
|
|
||||||
const newSelectedEntity = newFieldValue as EntityForSelect | null;
|
|
||||||
|
|
||||||
const fieldName = viewField.metadata.fieldName;
|
|
||||||
|
|
||||||
if (!newSelectedEntity) {
|
|
||||||
updateEntity({
|
|
||||||
variables: {
|
|
||||||
where: { id: currentEntityId },
|
|
||||||
data: {
|
|
||||||
[fieldName]: {
|
|
||||||
disconnect: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
updateEntity({
|
|
||||||
variables: {
|
|
||||||
where: { id: currentEntityId },
|
|
||||||
data: {
|
|
||||||
[fieldName]: {
|
|
||||||
connect: { id: newSelectedEntity.id },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (isViewFieldChip(viewField)) {
|
|
||||||
const newContent = newFieldValue as string;
|
|
||||||
|
|
||||||
updateEntity({
|
|
||||||
variables: {
|
|
||||||
where: { id: currentEntityId },
|
|
||||||
data: { [viewField.metadata.contentFieldName]: newContent },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else if (isViewFieldText(viewField)) {
|
|
||||||
const newContent = newFieldValue as string;
|
|
||||||
|
|
||||||
updateEntity({
|
|
||||||
variables: {
|
|
||||||
where: { id: currentEntityId },
|
|
||||||
data: { [viewField.metadata.fieldName]: newContent },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,8 +1,7 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { defaultOrderBy } from '@/companies/queries';
|
import { defaultOrderBy } from '@/companies/queries';
|
||||||
import { GenericEntityTableData } from '@/people/components/GenericEntityTableData';
|
import { peopleViewFields } from '@/people/constants/peopleViewFields';
|
||||||
import { peopleViewFields } from '@/people/constants/peopleFieldMetadataArray';
|
|
||||||
import { PeopleSelectedSortType } from '@/people/queries';
|
import { PeopleSelectedSortType } from '@/people/queries';
|
||||||
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
|
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
|
||||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||||
@ -10,6 +9,7 @@ import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIn
|
|||||||
import { IconList } from '@/ui/icon';
|
import { IconList } from '@/ui/icon';
|
||||||
import { useRecoilScopedValue } from '@/ui/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import { EntityTable } from '@/ui/table/components/EntityTableV2';
|
import { EntityTable } from '@/ui/table/components/EntityTableV2';
|
||||||
|
import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTableData';
|
||||||
import { TableContext } from '@/ui/table/states/TableContext';
|
import { TableContext } from '@/ui/table/states/TableContext';
|
||||||
import {
|
import {
|
||||||
PersonOrderByWithRelationInput,
|
PersonOrderByWithRelationInput,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ChangeEvent, useMemo, useState } from 'react';
|
import { ChangeEvent, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { InplaceInputTextDisplayMode } from '@/ui/display/component/InplaceInputTextDisplayMode';
|
import { InplaceInputTextDisplayMode } from '@/ui/display/component/InplaceInputTextDisplayMode';
|
||||||
import { StyledInput } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
import { StyledInput } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
import { debounce } from '~/utils/debounce';
|
import { debounce } from '~/utils/debounce';
|
||||||
|
|
||||||
import { BoardCardEditableField } from './BoardCardEditableField';
|
import { BoardCardEditableField } from './BoardCardEditableField';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { formatToHumanReadableDate } from '~/utils';
|
import { formatToHumanReadableDate } from '~/utils';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
value: Date | null;
|
value: Date | string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function InplaceInputDateDisplayMode({ value }: OwnProps) {
|
export function InplaceInputDateDisplayMode({ value }: OwnProps) {
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { MouseEvent } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { RawLink } from '@/ui/link/components/RawLink';
|
||||||
|
|
||||||
|
const StyledRawLink = styled(RawLink)`
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
a {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function InplaceInputURLDisplayMode({ value }: OwnProps) {
|
||||||
|
function handleClick(event: MouseEvent<HTMLElement>) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
const absoluteUrl = value
|
||||||
|
? value.startsWith('http')
|
||||||
|
? value
|
||||||
|
: 'https://' + value
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledRawLink href={absoluteUrl} onClick={handleClick}>
|
||||||
|
{value}
|
||||||
|
</StyledRawLink>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ import { useRecoilCallback } from 'recoil';
|
|||||||
|
|
||||||
import { internalHotkeysEnabledScopesState } from '../states/internal/internalHotkeysEnabledScopesState';
|
import { internalHotkeysEnabledScopesState } from '../states/internal/internalHotkeysEnabledScopesState';
|
||||||
|
|
||||||
const DEBUG_HOTKEY_SCOPE = false;
|
const DEBUG_HOTKEY_SCOPE = true;
|
||||||
|
|
||||||
export function useScopedHotkeyCallback() {
|
export function useScopedHotkeyCallback() {
|
||||||
return useRecoilCallback(
|
return useRecoilCallback(
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ChangeEvent } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { StyledInput } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
import { StyledInput } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
firstValue: string;
|
firstValue: string;
|
||||||
@ -11,7 +11,7 @@ type OwnProps = {
|
|||||||
onChange: (firstValue: string, secondValue: string) => void;
|
onChange: (firstValue: string, secondValue: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
export const StyledDoubleTextContainer = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -30,7 +30,7 @@ export function InplaceInputDoubleText({
|
|||||||
onChange,
|
onChange,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledDoubleTextContainer>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder={firstValuePlaceholder}
|
placeholder={firstValuePlaceholder}
|
||||||
@ -47,6 +47,6 @@ export function InplaceInputDoubleText({
|
|||||||
onChange(firstValue, event.target.value);
|
onChange(firstValue, event.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledDoubleTextContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,79 @@
|
|||||||
|
import { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { textInputStyle } from '@/ui/themes/effects';
|
||||||
|
|
||||||
|
import { useRegisterCloseCellHandlers } from '../../table/editable-cell/hooks/useRegisterCloseCellHandlers';
|
||||||
|
|
||||||
|
import { StyledDoubleTextContainer } from './InplaceInputDoubleText';
|
||||||
|
|
||||||
|
export const StyledInput = styled.input`
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
${textInputStyle}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
firstValuePlaceholder?: string;
|
||||||
|
secondValuePlaceholder?: string;
|
||||||
|
firstValue: string;
|
||||||
|
secondValue: string;
|
||||||
|
onSubmit: (newFirstValue: string, newSecondValue: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function InplaceInputDoubleTextCellEditMode({
|
||||||
|
firstValue,
|
||||||
|
secondValue,
|
||||||
|
firstValuePlaceholder,
|
||||||
|
secondValuePlaceholder,
|
||||||
|
onSubmit,
|
||||||
|
}: OwnProps) {
|
||||||
|
const [internalFirstValue, setInternalFirstValue] = useState(firstValue);
|
||||||
|
const [internalSecondValue, setInternalSecondValue] = useState(secondValue);
|
||||||
|
|
||||||
|
const wrapperRef = useRef(null);
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
onSubmit(internalFirstValue, internalSecondValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
setInternalFirstValue(firstValue);
|
||||||
|
setInternalSecondValue(secondValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFirstValueChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
|
setInternalFirstValue(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSecondValueChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
|
setInternalSecondValue(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInternalFirstValue(firstValue);
|
||||||
|
}, [firstValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInternalSecondValue(secondValue);
|
||||||
|
}, [secondValue]);
|
||||||
|
|
||||||
|
useRegisterCloseCellHandlers(wrapperRef, handleSubmit, handleCancel);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledDoubleTextContainer ref={wrapperRef}>
|
||||||
|
<StyledInput
|
||||||
|
autoFocus
|
||||||
|
placeholder={firstValuePlaceholder}
|
||||||
|
value={internalFirstValue}
|
||||||
|
onChange={handleFirstValueChange}
|
||||||
|
/>
|
||||||
|
<StyledInput
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder={secondValuePlaceholder}
|
||||||
|
value={internalSecondValue}
|
||||||
|
onChange={handleSecondValueChange}
|
||||||
|
/>
|
||||||
|
</StyledDoubleTextContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -18,7 +18,7 @@ type OwnProps = {
|
|||||||
onSubmit: (newText: string) => void;
|
onSubmit: (newText: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function InplaceInputTextEditMode({
|
export function InplaceInputTextCellEditMode({
|
||||||
placeholder,
|
placeholder,
|
||||||
autoFocus,
|
autoFocus,
|
||||||
value,
|
value,
|
||||||
@ -11,13 +11,18 @@ const StyledTitle = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||||
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
height: ${({ theme }) => theme.spacing(8)};
|
height: ${({ theme }) => theme.spacing(8)};
|
||||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledIcon = styled.div`
|
const StyledIcon = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
|
||||||
|
& > svg {
|
||||||
|
height: ${({ theme }) => theme.icon.size.md}px;
|
||||||
|
width: ${({ theme }) => theme.icon.size.md}px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function ColumnHead({ viewName, viewIcon }: OwnProps) {
|
export function ColumnHead({ viewName, viewIcon }: OwnProps) {
|
||||||
|
|||||||
@ -25,9 +25,9 @@ export function EntityTableCell({ cellIndex }: { cellIndex: number }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const entityFieldMetadata = useContext(ViewFieldContext);
|
const viewField = useContext(ViewFieldContext);
|
||||||
|
|
||||||
if (!entityFieldMetadata) {
|
if (!viewField) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,12 +37,12 @@ export function EntityTableCell({ cellIndex }: { cellIndex: number }) {
|
|||||||
<td
|
<td
|
||||||
onContextMenu={(event) => handleContextMenu(event)}
|
onContextMenu={(event) => handleContextMenu(event)}
|
||||||
style={{
|
style={{
|
||||||
width: entityFieldMetadata.columnSize,
|
width: viewField.columnSize,
|
||||||
minWidth: entityFieldMetadata.columnSize,
|
minWidth: viewField.columnSize,
|
||||||
maxWidth: entityFieldMetadata.columnSize,
|
maxWidth: viewField.columnSize,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<GenericEditableCell fieldDefinition={entityFieldMetadata} />
|
<GenericEditableCell viewField={viewField} />
|
||||||
</td>
|
</td>
|
||||||
</ColumnIndexContext.Provider>
|
</ColumnIndexContext.Provider>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { viewFieldsState } from '../states/viewFieldsState';
|
import { viewFieldsFamilyState } from '../states/viewFieldsState';
|
||||||
|
|
||||||
import { ColumnHead } from './ColumnHead';
|
import { ColumnHead } from './ColumnHead';
|
||||||
import { SelectAllCheckbox } from './SelectAllCheckbox';
|
import { SelectAllCheckbox } from './SelectAllCheckbox';
|
||||||
|
|
||||||
export function EntityTableHeader() {
|
export function EntityTableHeader() {
|
||||||
const viewFields = useRecoilValue(viewFieldsState);
|
const viewFields = useRecoilValue(viewFieldsFamilyState);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import styled from '@emotion/styled';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { ViewFieldContext } from '../states/ViewFieldContext';
|
import { ViewFieldContext } from '../states/ViewFieldContext';
|
||||||
import { viewFieldsState } from '../states/viewFieldsState';
|
import { viewFieldsFamilyState } from '../states/viewFieldsState';
|
||||||
|
|
||||||
import { CheckboxCell } from './CheckboxCell';
|
import { CheckboxCell } from './CheckboxCell';
|
||||||
import { EntityTableCell } from './EntityTableCellV2';
|
import { EntityTableCell } from './EntityTableCellV2';
|
||||||
@ -13,18 +13,18 @@ const StyledRow = styled.tr<{ selected: boolean }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function EntityTableRow({ rowId }: { rowId: string }) {
|
export function EntityTableRow({ rowId }: { rowId: string }) {
|
||||||
const entityFieldMetadataArray = useRecoilValue(viewFieldsState);
|
const viewFields = useRecoilValue(viewFieldsFamilyState);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledRow data-testid={`row-id-${rowId}`} selected={false}>
|
<StyledRow data-testid={`row-id-${rowId}`} selected={false}>
|
||||||
<td>
|
<td>
|
||||||
<CheckboxCell />
|
<CheckboxCell />
|
||||||
</td>
|
</td>
|
||||||
{entityFieldMetadataArray.map((entityFieldMetadata, columnIndex) => {
|
{viewFields.map((viewField, columnIndex) => {
|
||||||
return (
|
return (
|
||||||
<ViewFieldContext.Provider
|
<ViewFieldContext.Provider
|
||||||
value={entityFieldMetadata}
|
value={viewField}
|
||||||
key={entityFieldMetadata.columnOrder}
|
key={viewField.columnOrder}
|
||||||
>
|
>
|
||||||
<EntityTableCell cellIndex={columnIndex} />
|
<EntityTableCell cellIndex={columnIndex} />
|
||||||
</ViewFieldContext.Provider>
|
</ViewFieldContext.Provider>
|
||||||
|
|||||||
@ -1,37 +1,54 @@
|
|||||||
import { ViewFieldDefinition } from '@/ui/table/types/ViewField';
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/table/types/ViewField';
|
||||||
|
|
||||||
import { isViewFieldChip } from '../types/guards/isViewFieldChip';
|
import { isViewFieldChip } from '../types/guards/isViewFieldChip';
|
||||||
|
import { isViewFieldDate } from '../types/guards/isViewFieldDate';
|
||||||
|
import { isViewFieldDoubleText } from '../types/guards/isViewFieldDoubleText';
|
||||||
|
import { isViewFieldDoubleTextChip } from '../types/guards/isViewFieldDoubleTextChip';
|
||||||
|
import { isViewFieldNumber } from '../types/guards/isViewFieldNumber';
|
||||||
|
import { isViewFieldPhone } from '../types/guards/isViewFieldPhone';
|
||||||
import { isViewFieldRelation } from '../types/guards/isViewFieldRelation';
|
import { isViewFieldRelation } from '../types/guards/isViewFieldRelation';
|
||||||
import { isViewFieldText } from '../types/guards/isViewFieldText';
|
import { isViewFieldText } from '../types/guards/isViewFieldText';
|
||||||
|
import { isViewFieldURL } from '../types/guards/isViewFieldURL';
|
||||||
|
|
||||||
import { GenericEditableChipCell } from './GenericEditableChipCell';
|
import { GenericEditableChipCell } from './GenericEditableChipCell';
|
||||||
|
import { GenericEditableDateCell } from './GenericEditableDateCell';
|
||||||
|
import { GenericEditableDoubleTextCell } from './GenericEditableDoubleTextCell';
|
||||||
|
import { GenericEditableDoubleTextChipCell } from './GenericEditableDoubleTextChipCell';
|
||||||
|
import { GenericEditableNumberCell } from './GenericEditableNumberCell';
|
||||||
|
import { GenericEditablePhoneCell } from './GenericEditablePhoneCell';
|
||||||
import { GenericEditableRelationCell } from './GenericEditableRelationCell';
|
import { GenericEditableRelationCell } from './GenericEditableRelationCell';
|
||||||
import { GenericEditableTextCell } from './GenericEditableTextCell';
|
import { GenericEditableTextCell } from './GenericEditableTextCell';
|
||||||
|
import { GenericEditableURLCell } from './GenericEditableURLCell';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
fieldDefinition: ViewFieldDefinition<unknown>;
|
viewField: ViewFieldDefinition<ViewFieldMetadata>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function GenericEditableCell({ fieldDefinition }: OwnProps) {
|
export function GenericEditableCell({ viewField: fieldDefinition }: OwnProps) {
|
||||||
if (isViewFieldText(fieldDefinition)) {
|
if (isViewFieldText(fieldDefinition)) {
|
||||||
return (
|
return <GenericEditableTextCell viewField={fieldDefinition} />;
|
||||||
<GenericEditableTextCell
|
|
||||||
viewField={fieldDefinition}
|
|
||||||
editModeHorizontalAlign="left"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (isViewFieldRelation(fieldDefinition)) {
|
} else if (isViewFieldRelation(fieldDefinition)) {
|
||||||
return <GenericEditableRelationCell fieldDefinition={fieldDefinition} />;
|
return <GenericEditableRelationCell fieldDefinition={fieldDefinition} />;
|
||||||
|
} else if (isViewFieldDoubleTextChip(fieldDefinition)) {
|
||||||
|
return <GenericEditableDoubleTextChipCell viewField={fieldDefinition} />;
|
||||||
|
} else if (isViewFieldDoubleText(fieldDefinition)) {
|
||||||
|
return <GenericEditableDoubleTextCell viewField={fieldDefinition} />;
|
||||||
|
} else if (isViewFieldPhone(fieldDefinition)) {
|
||||||
|
return <GenericEditablePhoneCell viewField={fieldDefinition} />;
|
||||||
|
} else if (isViewFieldURL(fieldDefinition)) {
|
||||||
|
return <GenericEditableURLCell viewField={fieldDefinition} />;
|
||||||
|
} else if (isViewFieldDate(fieldDefinition)) {
|
||||||
|
return <GenericEditableDateCell viewField={fieldDefinition} />;
|
||||||
|
} else if (isViewFieldNumber(fieldDefinition)) {
|
||||||
|
return <GenericEditableNumberCell viewField={fieldDefinition} />;
|
||||||
} else if (isViewFieldChip(fieldDefinition)) {
|
} else if (isViewFieldChip(fieldDefinition)) {
|
||||||
return (
|
return <GenericEditableChipCell viewField={fieldDefinition} />;
|
||||||
<GenericEditableChipCell
|
|
||||||
viewField={fieldDefinition}
|
|
||||||
editModeHorizontalAlign="left"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown field type: ${fieldDefinition.type} in GenericEditableCell`,
|
`Unknown field metadata type: ${fieldDefinition.metadata.type} in GenericEditableCell`,
|
||||||
);
|
);
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
|||||||
import { ViewFieldChipMetadata, ViewFieldDefinition } from '../types/ViewField';
|
import { ViewFieldChipMetadata, ViewFieldDefinition } from '../types/ViewField';
|
||||||
|
|
||||||
import { GenericEditableChipCellDisplayMode } from './GenericEditableChipCellDisplayMode';
|
import { GenericEditableChipCellDisplayMode } from './GenericEditableChipCellDisplayMode';
|
||||||
import { GenericEditableTextCellEditMode } from './GenericEditableTextCellEditMode';
|
import { GenericEditableChipCellEditMode } from './GenericEditableChipCellEditMode';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
viewField: ViewFieldDefinition<ViewFieldChipMetadata>;
|
viewField: ViewFieldDefinition<ViewFieldChipMetadata>;
|
||||||
@ -14,17 +14,12 @@ type OwnProps = {
|
|||||||
export function GenericEditableChipCell({
|
export function GenericEditableChipCell({
|
||||||
viewField,
|
viewField,
|
||||||
editModeHorizontalAlign,
|
editModeHorizontalAlign,
|
||||||
placeholder,
|
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
return (
|
return (
|
||||||
<EditableCell
|
<EditableCell
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
editModeContent={
|
editModeContent={
|
||||||
<GenericEditableTextCellEditMode
|
<GenericEditableChipCellEditMode viewField={viewField} />
|
||||||
fieldName={viewField.metadata.contentFieldName}
|
|
||||||
viewFieldId={viewField.id}
|
|
||||||
placeholder={placeholder}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
nonEditModeContent={
|
nonEditModeContent={
|
||||||
<GenericEditableChipCellDisplayMode fieldDefinition={viewField} />
|
<GenericEditableChipCellDisplayMode fieldDefinition={viewField} />
|
||||||
|
|||||||
@ -0,0 +1,45 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import { ViewFieldChipMetadata, ViewFieldDefinition } from '../types/ViewField';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldChipMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableChipCellEditMode({ viewField }: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.contentFieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateField = useUpdateEntityField();
|
||||||
|
|
||||||
|
function handleSubmit(newText: string) {
|
||||||
|
if (newText === fieldValue) return;
|
||||||
|
|
||||||
|
setFieldValue(newText);
|
||||||
|
|
||||||
|
if (currentRowEntityId && updateField) {
|
||||||
|
updateField(currentRowEntityId, viewField, newText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InplaceInputTextCellEditMode
|
||||||
|
placeholder={viewField.metadata.placeHolder ?? ''}
|
||||||
|
autoFocus
|
||||||
|
value={fieldValue ?? ''}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputDateDisplayMode } from '@/ui/display/component/InplaceInputDateDisplayMode';
|
||||||
|
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import { ViewFieldDateMetadata, ViewFieldDefinition } from '../types/ViewField';
|
||||||
|
|
||||||
|
import { GenericEditableDateCellEditMode } from './GenericEditableDateCellEditMode';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldDateMetadata>;
|
||||||
|
editModeHorizontalAlign?: 'left' | 'right';
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableDateCell({
|
||||||
|
viewField,
|
||||||
|
editModeHorizontalAlign,
|
||||||
|
}: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.fieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCell
|
||||||
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
|
editModeContent={
|
||||||
|
<GenericEditableDateCellEditMode viewField={viewField} />
|
||||||
|
}
|
||||||
|
nonEditModeContent={<InplaceInputDateDisplayMode value={fieldValue} />}
|
||||||
|
></EditableCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import { EditableCellDateEditMode } from '../editable-cell/types/EditableCellDateEditMode';
|
||||||
|
import { ViewFieldDateMetadata, ViewFieldDefinition } from '../types/ViewField';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldDateMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableDateCellEditMode({ viewField }: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.fieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateField = useUpdateEntityField();
|
||||||
|
|
||||||
|
function handleSubmit(newDate: Date) {
|
||||||
|
const fieldValueDate = fieldValue
|
||||||
|
? DateTime.fromISO(fieldValue).toJSDate()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const newDateISO = DateTime.fromJSDate(newDate).toISO();
|
||||||
|
|
||||||
|
if (newDate === fieldValueDate || !newDateISO) return;
|
||||||
|
|
||||||
|
setFieldValue(newDateISO);
|
||||||
|
|
||||||
|
if (currentRowEntityId && updateField && newDateISO) {
|
||||||
|
updateField(currentRowEntityId, viewField, newDateISO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCellDateEditMode
|
||||||
|
value={DateTime.fromISO(fieldValue).toJSDate()}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputTextDisplayMode } from '@/ui/display/component/InplaceInputTextDisplayMode';
|
||||||
|
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldDoubleTextMetadata,
|
||||||
|
} from '../types/ViewField';
|
||||||
|
|
||||||
|
import { GenericEditableDoubleTextCellEditMode } from './GenericEditableDoubleTextCellEditMode';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldDoubleTextMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableDoubleTextCell({ viewField }: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const firstValue = useRecoilValue<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.firstValueFieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const secondValue = useRecoilValue<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.secondValueFieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const displayName = `${firstValue ?? ''} ${secondValue ?? ''}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCell
|
||||||
|
editModeContent={
|
||||||
|
<GenericEditableDoubleTextCellEditMode viewField={viewField} />
|
||||||
|
}
|
||||||
|
nonEditModeContent={
|
||||||
|
<InplaceInputTextDisplayMode>{displayName}</InplaceInputTextDisplayMode>
|
||||||
|
}
|
||||||
|
></EditableCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputDoubleTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputDoubleTextCellEditMode';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldDoubleTextMetadata,
|
||||||
|
} from '../types/ViewField';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldDoubleTextMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableDoubleTextCellEditMode({ viewField }: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [firstValue, setFirstValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.firstValueFieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [secondValue, setSecondValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.firstValueFieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateField = useUpdateEntityField();
|
||||||
|
|
||||||
|
function handleSubmit(newFirstValue: string, newSecondValue: string) {
|
||||||
|
if (newFirstValue === firstValue && newSecondValue === secondValue) return;
|
||||||
|
|
||||||
|
setFirstValue(newFirstValue);
|
||||||
|
setSecondValue(newSecondValue);
|
||||||
|
|
||||||
|
if (currentRowEntityId && updateField) {
|
||||||
|
updateField(currentRowEntityId, viewField, {
|
||||||
|
firstValue: newFirstValue,
|
||||||
|
secondValue: newSecondValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InplaceInputDoubleTextCellEditMode
|
||||||
|
firstValuePlaceholder={viewField.metadata.firstValuePlaceholder}
|
||||||
|
secondValuePlaceholder={viewField.metadata.secondValuePlaceholder}
|
||||||
|
firstValue={firstValue ?? ''}
|
||||||
|
secondValue={secondValue ?? ''}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||||
|
|
||||||
|
import { TableHotkeyScope } from '../types/TableHotkeyScope';
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldDoubleTextChipMetadata,
|
||||||
|
} from '../types/ViewField';
|
||||||
|
|
||||||
|
import { GenericEditableDoubleTextChipCellDisplayMode } from './GenericEditableDoubleTextChipCellDisplayMode';
|
||||||
|
import { GenericEditableDoubleTextChipCellEditMode } from './GenericEditableDoubleTextChipCellEditMode';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldDoubleTextChipMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableDoubleTextChipCell({ viewField }: OwnProps) {
|
||||||
|
return (
|
||||||
|
<EditableCell
|
||||||
|
editHotkeyScope={{ scope: TableHotkeyScope.CellDoubleTextInput }}
|
||||||
|
editModeContent={
|
||||||
|
<GenericEditableDoubleTextChipCellEditMode viewField={viewField} />
|
||||||
|
}
|
||||||
|
nonEditModeContent={
|
||||||
|
<GenericEditableDoubleTextChipCellDisplayMode viewField={viewField} />
|
||||||
|
}
|
||||||
|
></EditableCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||||
|
import { PersonChip } from '@/people/components/PersonChip';
|
||||||
|
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldDoubleTextChipMetadata,
|
||||||
|
} from '@/ui/table/types/ViewField';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldDoubleTextChipMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableDoubleTextChipCellDisplayMode({
|
||||||
|
viewField,
|
||||||
|
}: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const [firstValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.firstValueFieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [secondValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.secondValueFieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const displayName = `${firstValue} ${secondValue}`;
|
||||||
|
|
||||||
|
switch (viewField.metadata.entityType) {
|
||||||
|
case Entity.Company: {
|
||||||
|
return <CompanyChip id={currentRowEntityId ?? ''} name={displayName} />;
|
||||||
|
}
|
||||||
|
case Entity.Person: {
|
||||||
|
return <PersonChip id={currentRowEntityId ?? ''} name={displayName} />;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
console.warn(
|
||||||
|
`Unknown relation type: "${viewField.metadata.entityType}" in GenericEditableDoubleTextChipCellDisplayMode`,
|
||||||
|
);
|
||||||
|
return <> </>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import { EditableCellDoubleTextEditMode } from '../editable-cell/types/EditableCellDoubleTextEditMode';
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldDoubleTextChipMetadata,
|
||||||
|
} from '../types/ViewField';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldDoubleTextChipMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableDoubleTextChipCellEditMode({
|
||||||
|
viewField,
|
||||||
|
}: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [firstValue, setFirstValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.firstValueFieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [secondValue, setSecondValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.secondValueFieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateField = useUpdateEntityField();
|
||||||
|
|
||||||
|
function handleSubmit(newFirstValue: string, newSecondValue: string) {
|
||||||
|
const firstValueChanged = newFirstValue !== firstValue;
|
||||||
|
const secondValueChanged = newSecondValue !== secondValue;
|
||||||
|
|
||||||
|
if (firstValueChanged) {
|
||||||
|
setFirstValue(newFirstValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secondValueChanged) {
|
||||||
|
setSecondValue(newSecondValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentRowEntityId &&
|
||||||
|
updateField &&
|
||||||
|
(firstValueChanged || secondValueChanged)
|
||||||
|
) {
|
||||||
|
updateField(currentRowEntityId, viewField, {
|
||||||
|
firstValue: firstValueChanged ? newFirstValue : firstValue,
|
||||||
|
secondValue: secondValueChanged ? newSecondValue : secondValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCellDoubleTextEditMode
|
||||||
|
firstValuePlaceholder={viewField.metadata.firstValuePlaceholder}
|
||||||
|
secondValuePlaceholder={viewField.metadata.secondValuePlaceholder}
|
||||||
|
firstValue={firstValue ?? ''}
|
||||||
|
secondValue={secondValue ?? ''}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldNumberMetadata,
|
||||||
|
} from '../types/ViewField';
|
||||||
|
|
||||||
|
import { GenericEditableNumberCellEditMode } from './GenericEditableNumberCellEditMode';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldNumberMetadata>;
|
||||||
|
editModeHorizontalAlign?: 'left' | 'right';
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableNumberCell({
|
||||||
|
viewField,
|
||||||
|
editModeHorizontalAlign,
|
||||||
|
}: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.fieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCell
|
||||||
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
|
editModeContent={
|
||||||
|
<GenericEditableNumberCellEditMode viewField={viewField} />
|
||||||
|
}
|
||||||
|
nonEditModeContent={<>{fieldValue}</>}
|
||||||
|
></EditableCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldNumberMetadata,
|
||||||
|
} from '../types/ViewField';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldNumberMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableNumberCellEditMode({ viewField }: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.fieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateField = useUpdateEntityField();
|
||||||
|
|
||||||
|
function handleSubmit(newText: string) {
|
||||||
|
if (newText === fieldValue) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const numberValue = parseInt(newText);
|
||||||
|
|
||||||
|
if (isNaN(numberValue)) {
|
||||||
|
throw new Error('Not a number');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: find a way to store this better in DB
|
||||||
|
if (numberValue > 2000000000) {
|
||||||
|
throw new Error('Number too big');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log({ numberValue });
|
||||||
|
|
||||||
|
setFieldValue(numberValue.toString());
|
||||||
|
|
||||||
|
if (currentRowEntityId && updateField) {
|
||||||
|
updateField(currentRowEntityId, viewField, numberValue);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`In GenericEditableNumberCellEditMode, Invalid number: ${newText}, ${error}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InplaceInputTextCellEditMode
|
||||||
|
autoFocus
|
||||||
|
value={fieldValue ?? ''}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputPhoneDisplayMode } from '@/ui/display/component/InplaceInputPhoneDisplayMode';
|
||||||
|
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldPhoneMetadata,
|
||||||
|
} from '../types/ViewField';
|
||||||
|
|
||||||
|
import { GenericEditablePhoneCellEditMode } from './GenericEditablePhoneCellEditMode';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldPhoneMetadata>;
|
||||||
|
editModeHorizontalAlign?: 'left' | 'right';
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditablePhoneCell({
|
||||||
|
viewField,
|
||||||
|
editModeHorizontalAlign,
|
||||||
|
}: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.fieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCell
|
||||||
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
|
editModeContent={
|
||||||
|
<GenericEditablePhoneCellEditMode viewField={viewField} />
|
||||||
|
}
|
||||||
|
nonEditModeContent={<InplaceInputPhoneDisplayMode value={fieldValue} />}
|
||||||
|
></EditableCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldPhoneMetadata,
|
||||||
|
} from '../types/ViewField';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldPhoneMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditablePhoneCellEditMode({ viewField }: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.fieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateField = useUpdateEntityField();
|
||||||
|
|
||||||
|
function handleSubmit(newText: string) {
|
||||||
|
if (newText === fieldValue) return;
|
||||||
|
|
||||||
|
setFieldValue(newText);
|
||||||
|
|
||||||
|
if (currentRowEntityId && updateField) {
|
||||||
|
updateField(currentRowEntityId, viewField, newText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InplaceInputTextCellEditMode
|
||||||
|
placeholder={viewField.metadata.placeHolder ?? ''}
|
||||||
|
autoFocus
|
||||||
|
value={fieldValue ?? ''}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -24,9 +24,7 @@ export function GenericEditableRelationCell({
|
|||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
editHotkeyScope={{ scope: RelationPickerHotkeyScope.RelationPicker }}
|
editHotkeyScope={{ scope: RelationPickerHotkeyScope.RelationPicker }}
|
||||||
editModeContent={
|
editModeContent={
|
||||||
<GenericEditableRelationCellEditMode
|
<GenericEditableRelationCellEditMode viewField={fieldDefinition} />
|
||||||
viewFieldDefinition={fieldDefinition}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
nonEditModeContent={
|
nonEditModeContent={
|
||||||
<GenericEditableRelationCellDisplayMode
|
<GenericEditableRelationCellDisplayMode
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
ViewFieldDefinition,
|
ViewFieldDefinition,
|
||||||
ViewFieldRelationMetadata,
|
ViewFieldRelationMetadata,
|
||||||
} from '@/ui/table/types/ViewField';
|
} from '@/ui/table/types/ViewField';
|
||||||
|
import { UserChip } from '@/users/components/UserChip';
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -21,6 +22,7 @@ export function GenericEditableRelationCellDisplayMode({
|
|||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const currentRowEntityId = useCurrentRowEntityId();
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
// TODO: type value with generic getter
|
||||||
const fieldValue = useRecoilValue<any | null>(
|
const fieldValue = useRecoilValue<any | null>(
|
||||||
tableEntityFieldFamilySelector({
|
tableEntityFieldFamilySelector({
|
||||||
entityId: currentRowEntityId ?? '',
|
entityId: currentRowEntityId ?? '',
|
||||||
@ -38,6 +40,14 @@ export function GenericEditableRelationCellDisplayMode({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case Entity.User: {
|
||||||
|
return (
|
||||||
|
<UserChip
|
||||||
|
id={fieldValue?.id ?? ''}
|
||||||
|
name={fieldValue?.displayName ?? ''}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationCellEditMode`,
|
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationCellEditMode`,
|
||||||
|
|||||||
@ -1,24 +1,23 @@
|
|||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { CompanyPickerCell } from '@/companies/components/CompanyPickerCell';
|
import { CompanyPickerCell } from '@/companies/components/CompanyPickerCell';
|
||||||
import { useUpdateEntityField } from '@/people/hooks/useUpdateEntityField';
|
|
||||||
import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect';
|
import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect';
|
||||||
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
||||||
import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
|
import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
|
||||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
import {
|
import {
|
||||||
ViewFieldDefinition,
|
ViewFieldDefinition,
|
||||||
ViewFieldRelationMetadata,
|
ViewFieldRelationMetadata,
|
||||||
} from '@/ui/table/types/ViewField';
|
} from '@/ui/table/types/ViewField';
|
||||||
|
import { UserPicker } from '@/users/components/UserPicker';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
viewFieldDefinition: ViewFieldDefinition<ViewFieldRelationMetadata>;
|
viewField: ViewFieldDefinition<ViewFieldRelationMetadata>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function GenericEditableRelationCellEditMode({
|
export function GenericEditableRelationCellEditMode({ viewField }: OwnProps) {
|
||||||
viewFieldDefinition,
|
|
||||||
}: OwnProps) {
|
|
||||||
const currentRowEntityId = useCurrentRowEntityId();
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
const { closeEditableCell } = useEditableCell();
|
const { closeEditableCell } = useEditableCell();
|
||||||
@ -26,7 +25,7 @@ export function GenericEditableRelationCellEditMode({
|
|||||||
const [fieldValueEntity] = useRecoilState<any | null>(
|
const [fieldValueEntity] = useRecoilState<any | null>(
|
||||||
tableEntityFieldFamilySelector({
|
tableEntityFieldFamilySelector({
|
||||||
entityId: currentRowEntityId ?? '',
|
entityId: currentRowEntityId ?? '',
|
||||||
fieldName: viewFieldDefinition.metadata.fieldName,
|
fieldName: viewField.metadata.fieldName,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -38,11 +37,7 @@ export function GenericEditableRelationCellEditMode({
|
|||||||
currentRowEntityId &&
|
currentRowEntityId &&
|
||||||
updateEntityField
|
updateEntityField
|
||||||
) {
|
) {
|
||||||
updateEntityField(
|
updateEntityField(currentRowEntityId, viewField, newFieldEntity);
|
||||||
currentRowEntityId,
|
|
||||||
viewFieldDefinition.id,
|
|
||||||
newFieldEntity,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closeEditableCell();
|
closeEditableCell();
|
||||||
@ -52,7 +47,7 @@ export function GenericEditableRelationCellEditMode({
|
|||||||
closeEditableCell();
|
closeEditableCell();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (viewFieldDefinition.metadata.relationType) {
|
switch (viewField.metadata.relationType) {
|
||||||
case Entity.Company: {
|
case Entity.Company: {
|
||||||
return (
|
return (
|
||||||
<CompanyPickerCell
|
<CompanyPickerCell
|
||||||
@ -62,9 +57,18 @@ export function GenericEditableRelationCellEditMode({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case Entity.User: {
|
||||||
|
return (
|
||||||
|
<UserPicker
|
||||||
|
userId={fieldValueEntity?.id ?? null}
|
||||||
|
onSubmit={handleEntitySubmit}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown relation type: "${viewFieldDefinition.metadata.relationType}" in GenericEditableRelationCellEditMode`,
|
`Unknown relation type: "${viewField.metadata.relationType}" in GenericEditableRelationCellEditMode`,
|
||||||
);
|
);
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,13 +12,11 @@ import { GenericEditableTextCellEditMode } from './GenericEditableTextCellEditMo
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
viewField: ViewFieldDefinition<ViewFieldTextMetadata>;
|
viewField: ViewFieldDefinition<ViewFieldTextMetadata>;
|
||||||
editModeHorizontalAlign?: 'left' | 'right';
|
editModeHorizontalAlign?: 'left' | 'right';
|
||||||
placeholder?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function GenericEditableTextCell({
|
export function GenericEditableTextCell({
|
||||||
viewField,
|
viewField,
|
||||||
editModeHorizontalAlign,
|
editModeHorizontalAlign,
|
||||||
placeholder,
|
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const currentRowEntityId = useCurrentRowEntityId();
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
@ -33,11 +31,7 @@ export function GenericEditableTextCell({
|
|||||||
<EditableCell
|
<EditableCell
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
editModeContent={
|
editModeContent={
|
||||||
<GenericEditableTextCellEditMode
|
<GenericEditableTextCellEditMode viewField={viewField} />
|
||||||
fieldName={viewField.metadata.fieldName}
|
|
||||||
viewFieldId={viewField.id}
|
|
||||||
placeholder={placeholder}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
nonEditModeContent={
|
nonEditModeContent={
|
||||||
<InplaceInputTextDisplayMode>{fieldValue}</InplaceInputTextDisplayMode>
|
<InplaceInputTextDisplayMode>{fieldValue}</InplaceInputTextDisplayMode>
|
||||||
|
|||||||
@ -1,28 +1,24 @@
|
|||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { useUpdateEntityField } from '@/people/hooks/useUpdateEntityField';
|
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
|
||||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import { ViewFieldDefinition, ViewFieldTextMetadata } from '../types/ViewField';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
fieldName: string;
|
viewField: ViewFieldDefinition<ViewFieldTextMetadata>;
|
||||||
viewFieldId: string;
|
|
||||||
placeholder?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function GenericEditableTextCellEditMode({
|
export function GenericEditableTextCellEditMode({ viewField }: OwnProps) {
|
||||||
fieldName,
|
|
||||||
viewFieldId,
|
|
||||||
placeholder,
|
|
||||||
}: OwnProps) {
|
|
||||||
const currentRowEntityId = useCurrentRowEntityId();
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
// TODO: we could use a hook that would return the field value with the right type
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
tableEntityFieldFamilySelector({
|
tableEntityFieldFamilySelector({
|
||||||
entityId: currentRowEntityId ?? '',
|
entityId: currentRowEntityId ?? '',
|
||||||
fieldName,
|
fieldName: viewField.metadata.fieldName,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -34,13 +30,13 @@ export function GenericEditableTextCellEditMode({
|
|||||||
setFieldValue(newText);
|
setFieldValue(newText);
|
||||||
|
|
||||||
if (currentRowEntityId && updateField) {
|
if (currentRowEntityId && updateField) {
|
||||||
updateField(currentRowEntityId, viewFieldId, newText);
|
updateField(currentRowEntityId, viewField, newText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InplaceInputTextEditMode
|
<InplaceInputTextCellEditMode
|
||||||
placeholder={placeholder ?? ''}
|
placeholder={viewField.metadata.placeHolder ?? ''}
|
||||||
autoFocus
|
autoFocus
|
||||||
value={fieldValue ?? ''}
|
value={fieldValue ?? ''}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputURLDisplayMode } from '@/ui/display/component/InplaceInputURLDisplayMode';
|
||||||
|
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import { ViewFieldDefinition, ViewFieldURLMetadata } from '../types/ViewField';
|
||||||
|
|
||||||
|
import { GenericEditableURLCellEditMode } from './GenericEditableURLCellEditMode';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldURLMetadata>;
|
||||||
|
editModeHorizontalAlign?: 'left' | 'right';
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableURLCell({
|
||||||
|
viewField,
|
||||||
|
editModeHorizontalAlign,
|
||||||
|
}: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.fieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCell
|
||||||
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
|
editModeContent={<GenericEditableURLCellEditMode viewField={viewField} />}
|
||||||
|
nonEditModeContent={<InplaceInputURLDisplayMode value={fieldValue} />}
|
||||||
|
></EditableCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||||
|
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||||
|
|
||||||
|
import { ViewFieldDefinition, ViewFieldURLMetadata } from '../types/ViewField';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
viewField: ViewFieldDefinition<ViewFieldURLMetadata>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function GenericEditableURLCellEditMode({ viewField }: OwnProps) {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
tableEntityFieldFamilySelector({
|
||||||
|
entityId: currentRowEntityId ?? '',
|
||||||
|
fieldName: viewField.metadata.fieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateField = useUpdateEntityField();
|
||||||
|
|
||||||
|
function handleSubmit(newText: string) {
|
||||||
|
if (newText === fieldValue) return;
|
||||||
|
|
||||||
|
setFieldValue(newText);
|
||||||
|
|
||||||
|
if (currentRowEntityId && updateField) {
|
||||||
|
updateField(currentRowEntityId, viewField, newText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InplaceInputTextCellEditMode
|
||||||
|
placeholder={viewField.metadata.placeHolder ?? ''}
|
||||||
|
autoFocus
|
||||||
|
value={fieldValue ?? ''}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,8 +1,11 @@
|
|||||||
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
|
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
|
||||||
import { ViewFieldDefinition } from '@/ui/table/types/ViewField';
|
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
|
||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/table/types/ViewField';
|
||||||
|
|
||||||
import { useSetEntityTableData } from '../hooks/useSetEntityTableData';
|
import { defaultOrderBy } from '../../../people/queries';
|
||||||
import { defaultOrderBy } from '../queries';
|
|
||||||
|
|
||||||
export function GenericEntityTableData({
|
export function GenericEntityTableData({
|
||||||
useGetRequest,
|
useGetRequest,
|
||||||
@ -16,7 +19,7 @@ export function GenericEntityTableData({
|
|||||||
getRequestResultKey: string;
|
getRequestResultKey: string;
|
||||||
orderBy?: any;
|
orderBy?: any;
|
||||||
whereFilters?: any;
|
whereFilters?: any;
|
||||||
viewFields: ViewFieldDefinition<unknown>[];
|
viewFields: ViewFieldDefinition<ViewFieldMetadata>[];
|
||||||
filterDefinitionArray: FilterDefinition[];
|
filterDefinitionArray: FilterDefinition[];
|
||||||
}) {
|
}) {
|
||||||
const setEntityTableData = useSetEntityTableData();
|
const setEntityTableData = useSetEntityTableData();
|
||||||
@ -20,7 +20,7 @@ export function EditableCellDate({
|
|||||||
<EditableCell
|
<EditableCell
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
editModeContent={
|
editModeContent={
|
||||||
<EditableCellDateEditMode onChange={onChange} value={value} />
|
<EditableCellDateEditMode onSubmit={onChange} value={value} />
|
||||||
}
|
}
|
||||||
nonEditModeContent={<InplaceInputDateDisplayMode value={value} />}
|
nonEditModeContent={<InplaceInputDateDisplayMode value={value} />}
|
||||||
editHotkeyScope={{ scope: TableHotkeyScope.CellDateEditMode }}
|
editHotkeyScope={{ scope: TableHotkeyScope.CellDateEditMode }}
|
||||||
|
|||||||
@ -16,17 +16,17 @@ const EditableCellDateEditModeContainer = styled.div`
|
|||||||
|
|
||||||
export type EditableDateProps = {
|
export type EditableDateProps = {
|
||||||
value: Date;
|
value: Date;
|
||||||
onChange: (date: Date) => void;
|
onSubmit: (date: Date) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EditableCellDateEditMode({
|
export function EditableCellDateEditMode({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onSubmit,
|
||||||
}: EditableDateProps) {
|
}: EditableDateProps) {
|
||||||
const { closeEditableCell } = useEditableCell();
|
const { closeEditableCell } = useEditableCell();
|
||||||
|
|
||||||
function handleDateChange(newDate: Date) {
|
function handleDateChange(newDate: Date) {
|
||||||
onChange(newDate);
|
onSubmit(newDate);
|
||||||
|
|
||||||
closeEditableCell();
|
closeEditableCell();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import styled from '@emotion/styled';
|
|||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
|
|
||||||
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
|
||||||
import { StyledInput } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
import { StyledInput } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
|
|
||||||
import { useMoveSoftFocus } from '../../hooks/useMoveSoftFocus';
|
import { useMoveSoftFocus } from '../../hooks/useMoveSoftFocus';
|
||||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { InplaceInputPhoneDisplayMode } from '@/ui/display/component/InplaceInputPhoneDisplayMode';
|
import { InplaceInputPhoneDisplayMode } from '@/ui/display/component/InplaceInputPhoneDisplayMode';
|
||||||
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
|
|
||||||
import { EditableCell } from '../components/EditableCell';
|
import { EditableCell } from '../components/EditableCell';
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ export function EditableCellPhone({ value, placeholder, onSubmit }: OwnProps) {
|
|||||||
return (
|
return (
|
||||||
<EditableCell
|
<EditableCell
|
||||||
editModeContent={
|
editModeContent={
|
||||||
<InplaceInputTextEditMode
|
<InplaceInputTextCellEditMode
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder={placeholder || ''}
|
placeholder={placeholder || ''}
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { InplaceInputTextDisplayMode } from '@/ui/display/component/InplaceInputTextDisplayMode';
|
import { InplaceInputTextDisplayMode } from '@/ui/display/component/InplaceInputTextDisplayMode';
|
||||||
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
|
|
||||||
import { CellSkeleton } from '../components/CellSkeleton';
|
import { CellSkeleton } from '../components/CellSkeleton';
|
||||||
import { EditableCell } from '../components/EditableCell';
|
import { EditableCell } from '../components/EditableCell';
|
||||||
@ -23,7 +23,7 @@ export function EditableCellText({
|
|||||||
<EditableCell
|
<EditableCell
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
editModeContent={
|
editModeContent={
|
||||||
<InplaceInputTextEditMode
|
<InplaceInputTextCellEditMode
|
||||||
placeholder={placeholder || ''}
|
placeholder={placeholder || ''}
|
||||||
autoFocus
|
autoFocus
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
|
|
||||||
import { RawLink } from '../../../link/components/RawLink';
|
import { RawLink } from '../../../link/components/RawLink';
|
||||||
import { CellSkeleton } from '../components/CellSkeleton';
|
import { CellSkeleton } from '../components/CellSkeleton';
|
||||||
@ -25,7 +25,7 @@ export function EditableCellURL({
|
|||||||
<EditableCell
|
<EditableCell
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
editModeContent={
|
editModeContent={
|
||||||
<InplaceInputTextEditMode
|
<InplaceInputTextCellEditMode
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
autoFocus
|
autoFocus
|
||||||
value={url}
|
value={url}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ReactNode, useEffect, useState } from 'react';
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { InplaceInputTextEditMode } from '../../../inplace-input/components/InplaceInputTextEditMode';
|
import { InplaceInputTextCellEditMode } from '../../../inplace-input/components/InplaceInputTextCellEditMode';
|
||||||
import { EditableCell } from '../components/EditableCell';
|
import { EditableCell } from '../components/EditableCell';
|
||||||
|
|
||||||
export type EditableChipProps = {
|
export type EditableChipProps = {
|
||||||
@ -52,7 +52,7 @@ export function EditableCellChip({
|
|||||||
<EditableCell
|
<EditableCell
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||||
editModeContent={
|
editModeContent={
|
||||||
<InplaceInputTextEditMode
|
<InplaceInputTextCellEditMode
|
||||||
placeholder={placeholder || ''}
|
placeholder={placeholder || ''}
|
||||||
autoFocus
|
autoFocus
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { availableFiltersScopedState } from '@/ui/filter-n-sort/states/availableFiltersScopedState';
|
||||||
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
|
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
|
||||||
|
import { useContextScopeId } from '@/ui/recoil-scope/hooks/useContextScopeId';
|
||||||
|
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
|
||||||
|
import { entityTableDimensionsState } from '@/ui/table/states/entityTableDimensionsState';
|
||||||
|
import { isFetchingEntityTableDataState } from '@/ui/table/states/isFetchingEntityTableDataState';
|
||||||
|
import { TableContext } from '@/ui/table/states/TableContext';
|
||||||
import { tableEntitiesFamilyState } from '@/ui/table/states/tableEntitiesFamilyState';
|
import { tableEntitiesFamilyState } from '@/ui/table/states/tableEntitiesFamilyState';
|
||||||
import { viewFieldsState } from '@/ui/table/states/viewFieldsState';
|
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
||||||
import { ViewFieldDefinition } from '@/ui/table/types/ViewField';
|
import { viewFieldsFamilyState } from '@/ui/table/states/viewFieldsState';
|
||||||
|
import {
|
||||||
import { availableFiltersScopedState } from '../../ui/filter-n-sort/states/availableFiltersScopedState';
|
ViewFieldDefinition,
|
||||||
import { useContextScopeId } from '../../ui/recoil-scope/hooks/useContextScopeId';
|
ViewFieldMetadata,
|
||||||
import { useResetTableRowSelection } from '../../ui/table/hooks/useResetTableRowSelection';
|
} from '@/ui/table/types/ViewField';
|
||||||
import { entityTableDimensionsState } from '../../ui/table/states/entityTableDimensionsState';
|
|
||||||
import { isFetchingEntityTableDataState } from '../../ui/table/states/isFetchingEntityTableDataState';
|
|
||||||
import { TableContext } from '../../ui/table/states/TableContext';
|
|
||||||
import { tableRowIdsState } from '../../ui/table/states/tableRowIdsState';
|
|
||||||
|
|
||||||
export function useSetEntityTableData() {
|
export function useSetEntityTableData() {
|
||||||
const resetTableRowSelection = useResetTableRowSelection();
|
const resetTableRowSelection = useResetTableRowSelection();
|
||||||
@ -22,7 +24,7 @@ export function useSetEntityTableData() {
|
|||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
<T extends { id: string }>(
|
<T extends { id: string }>(
|
||||||
newEntityArray: T[],
|
newEntityArray: T[],
|
||||||
viewFields: ViewFieldDefinition<unknown>[],
|
viewFields: ViewFieldDefinition<ViewFieldMetadata>[],
|
||||||
filters: FilterDefinition[],
|
filters: FilterDefinition[],
|
||||||
) => {
|
) => {
|
||||||
for (const entity of newEntityArray) {
|
for (const entity of newEntityArray) {
|
||||||
@ -54,7 +56,7 @@ export function useSetEntityTableData() {
|
|||||||
|
|
||||||
set(availableFiltersScopedState(tableContextScopeId), filters);
|
set(availableFiltersScopedState(tableContextScopeId), filters);
|
||||||
|
|
||||||
set(viewFieldsState, viewFields);
|
set(viewFieldsFamilyState, viewFields);
|
||||||
|
|
||||||
set(isFetchingEntityTableDataState, false);
|
set(isFetchingEntityTableDataState, false);
|
||||||
},
|
},
|
||||||
228
front/src/modules/ui/table/hooks/useUpdateEntityField.ts
Normal file
228
front/src/modules/ui/table/hooks/useUpdateEntityField.ts
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
|
import { EntityUpdateMutationHookContext } from '@/ui/table/states/EntityUpdateMutationHookContext';
|
||||||
|
import { isViewFieldChip } from '@/ui/table/types/guards/isViewFieldChip';
|
||||||
|
import { isViewFieldRelation } from '@/ui/table/types/guards/isViewFieldRelation';
|
||||||
|
import { isViewFieldText } from '@/ui/table/types/guards/isViewFieldText';
|
||||||
|
|
||||||
|
import { isViewFieldChipValue } from '../types/guards/isViewFieldChipValue';
|
||||||
|
import { isViewFieldDate } from '../types/guards/isViewFieldDate';
|
||||||
|
import { isViewFieldDateValue } from '../types/guards/isViewFieldDateValue';
|
||||||
|
import { isViewFieldDoubleText } from '../types/guards/isViewFieldDoubleText';
|
||||||
|
import { isViewFieldDoubleTextChip } from '../types/guards/isViewFieldDoubleTextChip';
|
||||||
|
import { isViewFieldDoubleTextChipValue } from '../types/guards/isViewFieldDoubleTextChipValue';
|
||||||
|
import { isViewFieldDoubleTextValue } from '../types/guards/isViewFieldDoubleTextValue';
|
||||||
|
import { isViewFieldNumber } from '../types/guards/isViewFieldNumber';
|
||||||
|
import { isViewFieldNumberValue } from '../types/guards/isViewFieldNumberValue';
|
||||||
|
import { isViewFieldPhone } from '../types/guards/isViewFieldPhone';
|
||||||
|
import { isViewFieldPhoneValue } from '../types/guards/isViewFieldPhoneValue';
|
||||||
|
import { isViewFieldRelationValue } from '../types/guards/isViewFieldRelationValue';
|
||||||
|
import { isViewFieldTextValue } from '../types/guards/isViewFieldTextValue';
|
||||||
|
import { isViewFieldURL } from '../types/guards/isViewFieldURL';
|
||||||
|
import { isViewFieldURLValue } from '../types/guards/isViewFieldURLValue';
|
||||||
|
import {
|
||||||
|
ViewFieldChipMetadata,
|
||||||
|
ViewFieldChipValue,
|
||||||
|
ViewFieldDateMetadata,
|
||||||
|
ViewFieldDateValue,
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldDoubleTextChipMetadata,
|
||||||
|
ViewFieldDoubleTextChipValue,
|
||||||
|
ViewFieldDoubleTextMetadata,
|
||||||
|
ViewFieldDoubleTextValue,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
ViewFieldNumberMetadata,
|
||||||
|
ViewFieldNumberValue,
|
||||||
|
ViewFieldPhoneMetadata,
|
||||||
|
ViewFieldPhoneValue,
|
||||||
|
ViewFieldRelationMetadata,
|
||||||
|
ViewFieldRelationValue,
|
||||||
|
ViewFieldTextMetadata,
|
||||||
|
ViewFieldTextValue,
|
||||||
|
ViewFieldURLMetadata,
|
||||||
|
ViewFieldURLValue,
|
||||||
|
} from '../types/ViewField';
|
||||||
|
|
||||||
|
export function useUpdateEntityField() {
|
||||||
|
const useUpdateEntityMutation = useContext(EntityUpdateMutationHookContext);
|
||||||
|
|
||||||
|
const [updateEntity] = useUpdateEntityMutation();
|
||||||
|
|
||||||
|
return function updatePeopleField<
|
||||||
|
MetadataType extends ViewFieldMetadata,
|
||||||
|
ValueType extends MetadataType extends ViewFieldDoubleTextMetadata
|
||||||
|
? ViewFieldDoubleTextValue
|
||||||
|
: MetadataType extends ViewFieldTextMetadata
|
||||||
|
? ViewFieldTextValue
|
||||||
|
: MetadataType extends ViewFieldPhoneMetadata
|
||||||
|
? ViewFieldPhoneValue
|
||||||
|
: MetadataType extends ViewFieldURLMetadata
|
||||||
|
? ViewFieldURLValue
|
||||||
|
: MetadataType extends ViewFieldNumberMetadata
|
||||||
|
? ViewFieldNumberValue
|
||||||
|
: MetadataType extends ViewFieldDateMetadata
|
||||||
|
? ViewFieldDateValue
|
||||||
|
: MetadataType extends ViewFieldChipMetadata
|
||||||
|
? ViewFieldChipValue
|
||||||
|
: MetadataType extends ViewFieldDoubleTextChipMetadata
|
||||||
|
? ViewFieldDoubleTextChipValue
|
||||||
|
: MetadataType extends ViewFieldRelationMetadata
|
||||||
|
? ViewFieldRelationValue
|
||||||
|
: unknown,
|
||||||
|
>(
|
||||||
|
currentEntityId: string,
|
||||||
|
viewField: ViewFieldDefinition<MetadataType>,
|
||||||
|
newFieldValue: ValueType,
|
||||||
|
) {
|
||||||
|
const newFieldValueUnknown = newFieldValue as unknown;
|
||||||
|
// TODO: improve type guards organization, maybe with a common typeguard for all view fields
|
||||||
|
// taking an object of options as parameter ?
|
||||||
|
//
|
||||||
|
// The goal would be to check that the view field value not only is valid,
|
||||||
|
// but also that it is validated against the corresponding view field type
|
||||||
|
|
||||||
|
// Relation
|
||||||
|
if (
|
||||||
|
isViewFieldRelation(viewField) &&
|
||||||
|
isViewFieldRelationValue(newFieldValueUnknown)
|
||||||
|
) {
|
||||||
|
const newSelectedEntity = newFieldValueUnknown;
|
||||||
|
|
||||||
|
const fieldName = viewField.metadata.fieldName;
|
||||||
|
|
||||||
|
if (!newSelectedEntity) {
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: {
|
||||||
|
[fieldName]: {
|
||||||
|
disconnect: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: {
|
||||||
|
[fieldName]: {
|
||||||
|
connect: { id: newSelectedEntity.id },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Chip
|
||||||
|
} else if (
|
||||||
|
isViewFieldChip(viewField) &&
|
||||||
|
isViewFieldChipValue(newFieldValueUnknown)
|
||||||
|
) {
|
||||||
|
const newContent = newFieldValueUnknown;
|
||||||
|
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: { [viewField.metadata.contentFieldName]: newContent },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Text
|
||||||
|
} else if (
|
||||||
|
isViewFieldText(viewField) &&
|
||||||
|
isViewFieldTextValue(newFieldValueUnknown)
|
||||||
|
) {
|
||||||
|
const newContent = newFieldValueUnknown;
|
||||||
|
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: { [viewField.metadata.fieldName]: newContent },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Double text
|
||||||
|
} else if (
|
||||||
|
isViewFieldDoubleText(viewField) &&
|
||||||
|
isViewFieldDoubleTextValue(newFieldValueUnknown)
|
||||||
|
) {
|
||||||
|
const newContent = newFieldValueUnknown;
|
||||||
|
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: {
|
||||||
|
[viewField.metadata.firstValueFieldName]: newContent.firstValue,
|
||||||
|
[viewField.metadata.secondValueFieldName]: newContent.secondValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Double Text Chip
|
||||||
|
} else if (
|
||||||
|
isViewFieldDoubleTextChip(viewField) &&
|
||||||
|
isViewFieldDoubleTextChipValue(newFieldValueUnknown)
|
||||||
|
) {
|
||||||
|
const newContent = newFieldValueUnknown;
|
||||||
|
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: {
|
||||||
|
[viewField.metadata.firstValueFieldName]: newContent.firstValue,
|
||||||
|
[viewField.metadata.secondValueFieldName]: newContent.secondValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Phone
|
||||||
|
} else if (
|
||||||
|
isViewFieldPhone(viewField) &&
|
||||||
|
isViewFieldPhoneValue(newFieldValueUnknown)
|
||||||
|
) {
|
||||||
|
const newContent = newFieldValueUnknown;
|
||||||
|
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: { [viewField.metadata.fieldName]: newContent },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// URL
|
||||||
|
} else if (
|
||||||
|
isViewFieldURL(viewField) &&
|
||||||
|
isViewFieldURLValue(newFieldValueUnknown)
|
||||||
|
) {
|
||||||
|
const newContent = newFieldValueUnknown;
|
||||||
|
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: { [viewField.metadata.fieldName]: newContent },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Number
|
||||||
|
} else if (
|
||||||
|
isViewFieldNumber(viewField) &&
|
||||||
|
isViewFieldNumberValue(newFieldValueUnknown)
|
||||||
|
) {
|
||||||
|
const newContent = newFieldValueUnknown;
|
||||||
|
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: { [viewField.metadata.fieldName]: newContent },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Date
|
||||||
|
} else if (
|
||||||
|
isViewFieldDate(viewField) &&
|
||||||
|
isViewFieldDateValue(newFieldValueUnknown)
|
||||||
|
) {
|
||||||
|
const newContent = newFieldValueUnknown;
|
||||||
|
|
||||||
|
updateEntity({
|
||||||
|
variables: {
|
||||||
|
where: { id: currentEntityId },
|
||||||
|
data: { [viewField.metadata.fieldName]: newContent },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
import { ViewFieldDefinition } from '../types/ViewField';
|
import { ViewFieldDefinition, ViewFieldMetadata } from '../types/ViewField';
|
||||||
|
|
||||||
export const ViewFieldContext =
|
export const ViewFieldContext =
|
||||||
createContext<ViewFieldDefinition<unknown> | null>(null);
|
createContext<ViewFieldDefinition<ViewFieldMetadata> | null>(null);
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import { atom } from 'recoil';
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
import { ViewFieldDefinition } from '../types/ViewField';
|
import { ViewFieldDefinition, ViewFieldMetadata } from '../types/ViewField';
|
||||||
|
|
||||||
export const viewFieldsState = atom<ViewFieldDefinition<unknown>[]>({
|
export const viewFieldsFamilyState = atom<
|
||||||
key: 'viewFieldsState',
|
ViewFieldDefinition<ViewFieldMetadata>[]
|
||||||
|
>({
|
||||||
|
key: 'viewFieldsFamilyState',
|
||||||
default: [],
|
default: [],
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,36 +1,114 @@
|
|||||||
|
import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect';
|
||||||
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
||||||
|
|
||||||
export type ViewFieldType = 'text' | 'relation' | 'chip';
|
export type ViewFieldType =
|
||||||
|
| 'text'
|
||||||
|
| 'relation'
|
||||||
|
| 'chip'
|
||||||
|
| 'double-text-chip'
|
||||||
|
| 'double-text'
|
||||||
|
| 'number'
|
||||||
|
| 'date'
|
||||||
|
| 'phone'
|
||||||
|
| 'url';
|
||||||
|
|
||||||
export type ViewFieldTextMetadata = {
|
export type ViewFieldTextMetadata = {
|
||||||
|
type: 'text';
|
||||||
placeHolder: string;
|
placeHolder: string;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ViewFieldPhoneMetadata = {
|
||||||
|
type: 'phone';
|
||||||
|
placeHolder: string;
|
||||||
|
fieldName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ViewFieldURLMetadata = {
|
||||||
|
type: 'url';
|
||||||
|
placeHolder: string;
|
||||||
|
fieldName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ViewFieldDateMetadata = {
|
||||||
|
type: 'date';
|
||||||
|
fieldName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ViewFieldNumberMetadata = {
|
||||||
|
type: 'number';
|
||||||
|
fieldName: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ViewFieldRelationMetadata = {
|
export type ViewFieldRelationMetadata = {
|
||||||
|
type: 'relation';
|
||||||
relationType: Entity;
|
relationType: Entity;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ViewFieldChipMetadata = {
|
export type ViewFieldChipMetadata = {
|
||||||
|
type: 'chip';
|
||||||
relationType: Entity;
|
relationType: Entity;
|
||||||
contentFieldName: string;
|
contentFieldName: string;
|
||||||
urlFieldName: string;
|
urlFieldName: string;
|
||||||
|
placeHolder: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ViewFieldDefinition<
|
export type ViewFieldDoubleTextMetadata = {
|
||||||
T extends
|
type: 'double-text';
|
||||||
| ViewFieldTextMetadata
|
firstValueFieldName: string;
|
||||||
| ViewFieldRelationMetadata
|
firstValuePlaceholder: string;
|
||||||
| ViewFieldChipMetadata
|
secondValueFieldName: string;
|
||||||
| unknown,
|
secondValuePlaceholder: string;
|
||||||
> = {
|
};
|
||||||
|
|
||||||
|
export type ViewFieldDoubleTextChipMetadata = {
|
||||||
|
type: 'double-text-chip';
|
||||||
|
firstValueFieldName: string;
|
||||||
|
firstValuePlaceholder: string;
|
||||||
|
secondValueFieldName: string;
|
||||||
|
secondValuePlaceholder: string;
|
||||||
|
entityType: Entity;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ViewFieldMetadata = { type: ViewFieldType } & (
|
||||||
|
| ViewFieldTextMetadata
|
||||||
|
| ViewFieldRelationMetadata
|
||||||
|
| ViewFieldChipMetadata
|
||||||
|
| ViewFieldDoubleTextChipMetadata
|
||||||
|
| ViewFieldDoubleTextMetadata
|
||||||
|
| ViewFieldPhoneMetadata
|
||||||
|
| ViewFieldURLMetadata
|
||||||
|
| ViewFieldNumberMetadata
|
||||||
|
| ViewFieldDateMetadata
|
||||||
|
);
|
||||||
|
|
||||||
|
export type ViewFieldDefinition<T extends ViewFieldMetadata | unknown> = {
|
||||||
id: string;
|
id: string;
|
||||||
columnLabel: string;
|
columnLabel: string;
|
||||||
columnSize: number;
|
columnSize: number;
|
||||||
columnOrder: number;
|
columnOrder: number;
|
||||||
columnIcon?: JSX.Element;
|
columnIcon?: JSX.Element;
|
||||||
filterIcon?: JSX.Element;
|
filterIcon?: JSX.Element;
|
||||||
type: ViewFieldType;
|
|
||||||
metadata: T;
|
metadata: T;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ViewFieldTextValue = string;
|
||||||
|
|
||||||
|
export type ViewFieldChipValue = string;
|
||||||
|
export type ViewFieldDateValue = string;
|
||||||
|
export type ViewFieldPhoneValue = string;
|
||||||
|
export type ViewFieldURLValue = string;
|
||||||
|
export type ViewFieldNumberValue = number;
|
||||||
|
|
||||||
|
export type ViewFieldDoubleTextValue = {
|
||||||
|
firstValue: string;
|
||||||
|
secondValue: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ViewFieldDoubleTextChipValue = {
|
||||||
|
firstValue: string;
|
||||||
|
secondValue: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ViewFieldRelationValue = EntityForSelect | null;
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import { ViewFieldChipMetadata, ViewFieldDefinition } from '../ViewField';
|
import {
|
||||||
|
ViewFieldChipMetadata,
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '../ViewField';
|
||||||
|
|
||||||
export function isViewFieldChip(
|
export function isViewFieldChip(
|
||||||
field: ViewFieldDefinition<unknown>,
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
): field is ViewFieldDefinition<ViewFieldChipMetadata> {
|
): field is ViewFieldDefinition<ViewFieldChipMetadata> {
|
||||||
return field.type === 'chip';
|
return field.metadata.type === 'chip';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { ViewFieldChipValue } from '../ViewField';
|
||||||
|
|
||||||
|
// TODO: add yup
|
||||||
|
export function isViewFieldChipValue(
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is ViewFieldChipValue {
|
||||||
|
return (
|
||||||
|
fieldValue !== null &&
|
||||||
|
fieldValue !== undefined &&
|
||||||
|
typeof fieldValue === 'string'
|
||||||
|
);
|
||||||
|
}
|
||||||
11
front/src/modules/ui/table/types/guards/isViewFieldDate.ts
Normal file
11
front/src/modules/ui/table/types/guards/isViewFieldDate.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
ViewFieldDateMetadata,
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '../ViewField';
|
||||||
|
|
||||||
|
export function isViewFieldDate(
|
||||||
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
|
): field is ViewFieldDefinition<ViewFieldDateMetadata> {
|
||||||
|
return field.metadata.type === 'date';
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { ViewFieldDateValue } from '../ViewField';
|
||||||
|
|
||||||
|
// TODO: add yup
|
||||||
|
export function isViewFieldDateValue(
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is ViewFieldDateValue {
|
||||||
|
return (
|
||||||
|
fieldValue !== null &&
|
||||||
|
fieldValue !== undefined &&
|
||||||
|
typeof fieldValue === 'string'
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldDoubleTextMetadata,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '../ViewField';
|
||||||
|
|
||||||
|
export function isViewFieldDoubleText(
|
||||||
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
|
): field is ViewFieldDefinition<ViewFieldDoubleTextMetadata> {
|
||||||
|
return field.metadata.type === 'double-text';
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldDoubleTextChipMetadata,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '../ViewField';
|
||||||
|
|
||||||
|
export function isViewFieldDoubleTextChip(
|
||||||
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
|
): field is ViewFieldDefinition<ViewFieldDoubleTextChipMetadata> {
|
||||||
|
return field.metadata.type === 'double-text-chip';
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { ViewFieldDoubleTextChipValue } from '../ViewField';
|
||||||
|
|
||||||
|
// TODO: add yup
|
||||||
|
export function isViewFieldDoubleTextChipValue(
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is ViewFieldDoubleTextChipValue {
|
||||||
|
return (
|
||||||
|
fieldValue !== null &&
|
||||||
|
fieldValue !== undefined &&
|
||||||
|
typeof fieldValue === 'object'
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { ViewFieldDoubleTextValue } from '../ViewField';
|
||||||
|
|
||||||
|
// TODO: add yup
|
||||||
|
export function isViewFieldDoubleTextValue(
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is ViewFieldDoubleTextValue {
|
||||||
|
return (
|
||||||
|
fieldValue !== null &&
|
||||||
|
fieldValue !== undefined &&
|
||||||
|
typeof fieldValue === 'object'
|
||||||
|
);
|
||||||
|
}
|
||||||
11
front/src/modules/ui/table/types/guards/isViewFieldNumber.ts
Normal file
11
front/src/modules/ui/table/types/guards/isViewFieldNumber.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
ViewFieldNumberMetadata,
|
||||||
|
} from '../ViewField';
|
||||||
|
|
||||||
|
export function isViewFieldNumber(
|
||||||
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
|
): field is ViewFieldDefinition<ViewFieldNumberMetadata> {
|
||||||
|
return field.metadata.type === 'number';
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { ViewFieldNumberValue } from '../ViewField';
|
||||||
|
|
||||||
|
// TODO: add yup
|
||||||
|
export function isViewFieldNumberValue(
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is ViewFieldNumberValue {
|
||||||
|
return (
|
||||||
|
fieldValue !== null &&
|
||||||
|
fieldValue !== undefined &&
|
||||||
|
typeof fieldValue === 'number'
|
||||||
|
);
|
||||||
|
}
|
||||||
11
front/src/modules/ui/table/types/guards/isViewFieldPhone.ts
Normal file
11
front/src/modules/ui/table/types/guards/isViewFieldPhone.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
ViewFieldPhoneMetadata,
|
||||||
|
} from '../ViewField';
|
||||||
|
|
||||||
|
export function isViewFieldPhone(
|
||||||
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
|
): field is ViewFieldDefinition<ViewFieldPhoneMetadata> {
|
||||||
|
return field.metadata.type === 'phone';
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { ViewFieldPhoneValue } from '../ViewField';
|
||||||
|
|
||||||
|
// TODO: add yup
|
||||||
|
export function isViewFieldPhoneValue(
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is ViewFieldPhoneValue {
|
||||||
|
return (
|
||||||
|
fieldValue !== null &&
|
||||||
|
fieldValue !== undefined &&
|
||||||
|
typeof fieldValue === 'string'
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,7 +1,11 @@
|
|||||||
import { ViewFieldDefinition, ViewFieldRelationMetadata } from '../ViewField';
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
ViewFieldRelationMetadata,
|
||||||
|
} from '../ViewField';
|
||||||
|
|
||||||
export function isViewFieldRelation(
|
export function isViewFieldRelation(
|
||||||
field: ViewFieldDefinition<unknown>,
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
): field is ViewFieldDefinition<ViewFieldRelationMetadata> {
|
): field is ViewFieldDefinition<ViewFieldRelationMetadata> {
|
||||||
return field.type === 'relation';
|
return field.metadata.type === 'relation';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { ViewFieldRelationValue } from '../ViewField';
|
||||||
|
|
||||||
|
// TODO: add yup
|
||||||
|
export function isViewFieldRelationValue(
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is ViewFieldRelationValue {
|
||||||
|
return (
|
||||||
|
fieldValue !== null &&
|
||||||
|
fieldValue !== undefined &&
|
||||||
|
typeof fieldValue === 'object'
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,7 +1,11 @@
|
|||||||
import { ViewFieldDefinition, ViewFieldTextMetadata } from '../ViewField';
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
ViewFieldTextMetadata,
|
||||||
|
} from '../ViewField';
|
||||||
|
|
||||||
export function isViewFieldText(
|
export function isViewFieldText(
|
||||||
field: ViewFieldDefinition<unknown>,
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
): field is ViewFieldDefinition<ViewFieldTextMetadata> {
|
): field is ViewFieldDefinition<ViewFieldTextMetadata> {
|
||||||
return field.type === 'text';
|
return field.metadata.type === 'text';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { ViewFieldTextValue } from '../ViewField';
|
||||||
|
|
||||||
|
// TODO: add yup
|
||||||
|
export function isViewFieldTextValue(
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is ViewFieldTextValue {
|
||||||
|
return (
|
||||||
|
fieldValue !== null &&
|
||||||
|
fieldValue !== undefined &&
|
||||||
|
typeof fieldValue === 'string'
|
||||||
|
);
|
||||||
|
}
|
||||||
11
front/src/modules/ui/table/types/guards/isViewFieldURL.ts
Normal file
11
front/src/modules/ui/table/types/guards/isViewFieldURL.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
ViewFieldURLMetadata,
|
||||||
|
} from '../ViewField';
|
||||||
|
|
||||||
|
export function isViewFieldURL(
|
||||||
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
|
): field is ViewFieldDefinition<ViewFieldURLMetadata> {
|
||||||
|
return field.metadata.type === 'url';
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { ViewFieldURLValue } from '../ViewField';
|
||||||
|
|
||||||
|
// TODO: add yup
|
||||||
|
export function isViewFieldURLValue(
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is ViewFieldURLValue {
|
||||||
|
return (
|
||||||
|
fieldValue !== null &&
|
||||||
|
fieldValue !== undefined &&
|
||||||
|
typeof fieldValue === 'string'
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ import { useSearchUserQuery } from '~/generated/graphql';
|
|||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
userId: string;
|
userId: string;
|
||||||
onSubmit: (newUserId: string) => void;
|
onSubmit: (newUser: EntityForSelect | null) => void;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ export function UserPicker({ userId, onSubmit, onCancel }: OwnProps) {
|
|||||||
|
|
||||||
const users = useFilteredSearchEntityQuery({
|
const users = useFilteredSearchEntityQuery({
|
||||||
queryHook: useSearchUserQuery,
|
queryHook: useSearchUserQuery,
|
||||||
selectedIds: [userId],
|
selectedIds: userId ? [userId] : [],
|
||||||
searchFilter: searchFilter,
|
searchFilter: searchFilter,
|
||||||
mappingFunction: (user) => ({
|
mappingFunction: (user) => ({
|
||||||
entityType: Entity.User,
|
entityType: Entity.User,
|
||||||
@ -39,7 +39,7 @@ export function UserPicker({ userId, onSubmit, onCancel }: OwnProps) {
|
|||||||
async function handleEntitySelected(
|
async function handleEntitySelected(
|
||||||
selectedUser: UserForSelect | null | undefined,
|
selectedUser: UserForSelect | null | undefined,
|
||||||
) {
|
) {
|
||||||
onSubmit(selectedUser?.id ?? '');
|
onSubmit(selectedUser ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -34,11 +34,11 @@ export const FilterByName: Story = {
|
|||||||
const filterButton = await canvas.findByText('Filter');
|
const filterButton = await canvas.findByText('Filter');
|
||||||
await userEvent.click(filterButton);
|
await userEvent.click(filterButton);
|
||||||
|
|
||||||
const nameFilterButton = canvas
|
const nameFilterButton = (
|
||||||
.queryAllByTestId('dropdown-menu-item')
|
await canvas.findAllByTestId('dropdown-menu-item')
|
||||||
.find((item) => {
|
).find((item) => {
|
||||||
return item.textContent === 'Name';
|
return item.textContent === 'Name';
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(nameFilterButton);
|
assert(nameFilterButton);
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ export const FilterByName: Story = {
|
|||||||
delay: 200,
|
delay: 200,
|
||||||
});
|
});
|
||||||
|
|
||||||
await sleep(1000);
|
await sleep(50);
|
||||||
|
|
||||||
expect(await canvas.findByText('Airbnb')).toBeInTheDocument();
|
expect(await canvas.findByText('Airbnb')).toBeInTheDocument();
|
||||||
expect(await canvas.findByText('Aircall')).toBeInTheDocument();
|
expect(await canvas.findByText('Aircall')).toBeInTheDocument();
|
||||||
@ -88,11 +88,11 @@ export const FilterByAccountOwner: Story = {
|
|||||||
|
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
|
|
||||||
const charlesChip = canvas
|
const charlesChip = (
|
||||||
.getAllByTestId('dropdown-menu-item')
|
await canvas.findAllByTestId('dropdown-menu-item')
|
||||||
.find((item) => {
|
).find((item) => {
|
||||||
return item.textContent?.includes('Charles Test');
|
return item.textContent?.includes('Charles Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(charlesChip);
|
assert(charlesChip);
|
||||||
|
|
||||||
|
|||||||
@ -34,22 +34,23 @@ export const Email: Story = {
|
|||||||
const filterButton = await canvas.findByText('Filter');
|
const filterButton = await canvas.findByText('Filter');
|
||||||
await userEvent.click(filterButton);
|
await userEvent.click(filterButton);
|
||||||
|
|
||||||
const emailFilterButton = canvas
|
const emailFilterButton = (
|
||||||
.getAllByTestId('dropdown-menu-item')
|
await canvas.findAllByTestId('dropdown-menu-item')
|
||||||
.find((item) => {
|
).find((item) => {
|
||||||
return item.textContent?.includes('Email');
|
return item.textContent?.includes('Email');
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(emailFilterButton);
|
assert(emailFilterButton);
|
||||||
|
|
||||||
await userEvent.click(emailFilterButton);
|
await userEvent.click(emailFilterButton);
|
||||||
|
|
||||||
const emailInput = canvas.getByPlaceholderText('Email');
|
const emailInput = canvas.getByPlaceholderText('Email');
|
||||||
|
|
||||||
await userEvent.type(emailInput, 'al', {
|
await userEvent.type(emailInput, 'al', {
|
||||||
delay: 200,
|
delay: 200,
|
||||||
});
|
});
|
||||||
|
|
||||||
await sleep(1000);
|
await sleep(50);
|
||||||
|
|
||||||
expect(await canvas.findByText('Alexandre Prot')).toBeInTheDocument();
|
expect(await canvas.findByText('Alexandre Prot')).toBeInTheDocument();
|
||||||
await expect(canvas.queryAllByText('John Doe')).toStrictEqual([]);
|
await expect(canvas.queryAllByText('John Doe')).toStrictEqual([]);
|
||||||
@ -68,11 +69,11 @@ export const CompanyName: Story = {
|
|||||||
const filterButton = await canvas.findByText('Filter');
|
const filterButton = await canvas.findByText('Filter');
|
||||||
await userEvent.click(filterButton);
|
await userEvent.click(filterButton);
|
||||||
|
|
||||||
const companyFilterButton = canvas
|
const companyFilterButton = (
|
||||||
.getAllByTestId('dropdown-menu-item')
|
await canvas.findAllByTestId('dropdown-menu-item')
|
||||||
.find((item) => {
|
).find((item) => {
|
||||||
return item.textContent?.includes('Company');
|
return item.textContent?.includes('Company');
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(companyFilterButton);
|
assert(companyFilterButton);
|
||||||
|
|
||||||
@ -85,11 +86,11 @@ export const CompanyName: Story = {
|
|||||||
|
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
|
|
||||||
const qontoChip = canvas
|
const qontoChip = (await canvas.findAllByTestId('dropdown-menu-item')).find(
|
||||||
.getAllByTestId('dropdown-menu-item')
|
(item) => {
|
||||||
.find((item) => {
|
|
||||||
return item.textContent?.includes('Qonto');
|
return item.textContent?.includes('Qonto');
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
expect(qontoChip).toBeInTheDocument();
|
expect(qontoChip).toBeInTheDocument();
|
||||||
|
|
||||||
|
|||||||
@ -191,7 +191,7 @@ export const EditRelation: Story = {
|
|||||||
|
|
||||||
await step('Click on second row company cell', async () => {
|
await step('Click on second row company cell', async () => {
|
||||||
const secondRowCompanyCell = await canvas.findByText(
|
const secondRowCompanyCell = await canvas.findByText(
|
||||||
mockedPeopleData[1].company.name,
|
mockedPeopleData[2].company.name,
|
||||||
);
|
);
|
||||||
|
|
||||||
await userEvent.click(
|
await userEvent.click(
|
||||||
@ -262,11 +262,24 @@ export const SelectRelationWithKeys: Story = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await userEvent.type(relationInput, '{arrowdown}');
|
await userEvent.type(relationInput, '{arrowdown}');
|
||||||
|
|
||||||
|
await sleep(50);
|
||||||
|
|
||||||
await userEvent.type(relationInput, '{arrowup}');
|
await userEvent.type(relationInput, '{arrowup}');
|
||||||
|
|
||||||
|
await sleep(50);
|
||||||
|
|
||||||
await userEvent.type(relationInput, '{arrowdown}');
|
await userEvent.type(relationInput, '{arrowdown}');
|
||||||
|
|
||||||
|
await sleep(50);
|
||||||
|
|
||||||
await userEvent.type(relationInput, '{arrowdown}');
|
await userEvent.type(relationInput, '{arrowdown}');
|
||||||
|
|
||||||
|
await sleep(50);
|
||||||
|
|
||||||
await userEvent.type(relationInput, '{enter}');
|
await userEvent.type(relationInput, '{enter}');
|
||||||
sleep(25);
|
|
||||||
|
await sleep(50);
|
||||||
|
|
||||||
const allAirbns = await canvas.findAllByText('Aircall');
|
const allAirbns = await canvas.findAllByText('Aircall');
|
||||||
expect(allAirbns.length).toBe(1);
|
expect(allAirbns.length).toBe(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user