feat: display label identifier table cell as chip with link to Record… (#3503)
* feat: display label identifier table cell as chip with link to RecordShowPage Closes #3502 * Fix test --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,27 +0,0 @@
|
|||||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
|
||||||
|
|
||||||
import { flatMapAndSortEntityForSelectArrayOfArrayByName } from '../flatMapAndSortEntityForSelectArrayByName';
|
|
||||||
|
|
||||||
describe('flatMapAndSortEntityForSelectArrayOfArrayByName', () => {
|
|
||||||
it('should return the correct value', () => {
|
|
||||||
const entityForSelectArray = [
|
|
||||||
[
|
|
||||||
{ id: 1, name: 'xRya' },
|
|
||||||
{ id: 2, name: 'BrcA' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ id: 3, name: 'aCxd' },
|
|
||||||
{ id: 4, name: 'kp7u' },
|
|
||||||
],
|
|
||||||
] as unknown as EntityForSelect[][];
|
|
||||||
|
|
||||||
const res =
|
|
||||||
flatMapAndSortEntityForSelectArrayOfArrayByName(entityForSelectArray);
|
|
||||||
|
|
||||||
expect(res).toHaveLength(4);
|
|
||||||
expect(res[0].id).toBe(3);
|
|
||||||
expect(res[1].id).toBe(2);
|
|
||||||
expect(res[2].id).toBe(4);
|
|
||||||
expect(res[3].id).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
|
||||||
|
|
||||||
export const flatMapAndSortEntityForSelectArrayOfArrayByName = <
|
|
||||||
T extends EntityForSelect,
|
|
||||||
>(
|
|
||||||
entityForSelectArray: T[][],
|
|
||||||
) => {
|
|
||||||
const sortByName = (a: T, b: T) => a.name.localeCompare(b.name);
|
|
||||||
|
|
||||||
return entityForSelectArray.flatMap((entity) => entity).sort(sortByName);
|
|
||||||
};
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { v4 } from 'uuid';
|
|
||||||
|
|
||||||
import { FieldDoubleText } from '@/object-record/field/types/FieldDoubleText';
|
|
||||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
|
||||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
|
||||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
|
||||||
import { Person } from '@/people/types/Person';
|
|
||||||
import { DoubleTextInput } from '@/ui/field/input/components/DoubleTextInput';
|
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
|
||||||
|
|
||||||
export const StyledInputContainer = styled.div`
|
|
||||||
background-color: transparent;
|
|
||||||
box-shadow: ${({ theme }) => theme.boxShadow.strong};
|
|
||||||
display: flex;
|
|
||||||
gap: ${({ theme }) => theme.spacing(0.5)};
|
|
||||||
width: ${({ theme }) => theme.spacing(62.5)};
|
|
||||||
& input,
|
|
||||||
div {
|
|
||||||
background-color: ${({ theme }) => theme.background.primary};
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
border-radius: ${({ theme }) => theme.spacing(1)};
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
padding: ${({ theme }) => theme.spacing(2)};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type AddPersonToCompanyProps = {
|
|
||||||
companyId: string;
|
|
||||||
onEntitySelected: (entity?: EntityForSelect | undefined) => void;
|
|
||||||
closeDropdown?: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AddPersonToCompany = ({
|
|
||||||
companyId,
|
|
||||||
onEntitySelected,
|
|
||||||
closeDropdown,
|
|
||||||
}: AddPersonToCompanyProps) => {
|
|
||||||
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
|
|
||||||
|
|
||||||
const handleEscape = () => {
|
|
||||||
goBackToPreviousHotkeyScope();
|
|
||||||
closeDropdown?.();
|
|
||||||
};
|
|
||||||
|
|
||||||
const { createOneRecord: createPerson } = useCreateOneRecord<Person>({
|
|
||||||
objectNameSingular: 'person',
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleCreatePerson = async ({
|
|
||||||
firstValue,
|
|
||||||
secondValue,
|
|
||||||
}: FieldDoubleText) => {
|
|
||||||
if (!firstValue && !secondValue) return;
|
|
||||||
|
|
||||||
const person = await createPerson({
|
|
||||||
companyId,
|
|
||||||
id: v4(),
|
|
||||||
name: {
|
|
||||||
firstName: firstValue,
|
|
||||||
lastName: secondValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (person) {
|
|
||||||
const entityForSelect: EntityForSelect = {
|
|
||||||
id: person.id,
|
|
||||||
name: person.name?.firstName ?? '',
|
|
||||||
avatarUrl: person.avatarUrl ?? '',
|
|
||||||
avatarType: 'rounded',
|
|
||||||
record: person,
|
|
||||||
};
|
|
||||||
onEntitySelected(entityForSelect);
|
|
||||||
}
|
|
||||||
goBackToPreviousHotkeyScope();
|
|
||||||
closeDropdown?.();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledInputContainer>
|
|
||||||
<DoubleTextInput
|
|
||||||
firstValue=""
|
|
||||||
secondValue=""
|
|
||||||
firstValuePlaceholder="First Name"
|
|
||||||
secondValuePlaceholder="Last Name"
|
|
||||||
onClickOutside={handleEscape}
|
|
||||||
onEnter={handleCreatePerson}
|
|
||||||
onEscape={handleEscape}
|
|
||||||
hotkeyScope={RelationPickerHotkeyScope.AddNew}
|
|
||||||
/>
|
|
||||||
</StyledInputContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -52,8 +52,7 @@ export const NewOpportunityButton = () => {
|
|||||||
setIsCreatingCard(false);
|
setIsCreatingCard(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { relationPickerSearchFilter, identifiersMapper, searchQuery } =
|
const { relationPickerSearchFilter, searchQuery } = useRelationPicker();
|
||||||
useRelationPicker();
|
|
||||||
|
|
||||||
const filteredSearchEntityResults = useFilteredSearchEntityQuery({
|
const filteredSearchEntityResults = useFilteredSearchEntityQuery({
|
||||||
filters: [
|
filters: [
|
||||||
@ -64,7 +63,6 @@ export const NewOpportunityButton = () => {
|
|||||||
],
|
],
|
||||||
orderByField: 'createdAt',
|
orderByField: 'createdAt',
|
||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
mappingFunction: (record: any) => identifiersMapper?.(record, 'company'),
|
|
||||||
objectNameSingular: CoreObjectNameSingular.Company,
|
objectNameSingular: CoreObjectNameSingular.Company,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'
|
|||||||
export type OpportunityPickerProps = {
|
export type OpportunityPickerProps = {
|
||||||
companyId: string | null;
|
companyId: string | null;
|
||||||
onSubmit: (
|
onSubmit: (
|
||||||
newCompanyId: EntityForSelect | null,
|
newCompany: EntityForSelect | null,
|
||||||
newPipelineStepId: string | null,
|
newPipelineStepId: string | null,
|
||||||
) => void;
|
) => void;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
@ -34,7 +34,7 @@ export const OpportunityPicker = ({
|
|||||||
|
|
||||||
const { searchFilter, handleSearchFilterChange } = useEntitySelectSearch();
|
const { searchFilter, handleSearchFilterChange } = useEntitySelectSearch();
|
||||||
|
|
||||||
const { identifiersMapper, searchQuery } = useRelationPicker();
|
const { searchQuery } = useRelationPicker();
|
||||||
|
|
||||||
const filteredSearchEntityResults = useFilteredSearchEntityQuery({
|
const filteredSearchEntityResults = useFilteredSearchEntityQuery({
|
||||||
filters: [
|
filters: [
|
||||||
@ -45,7 +45,6 @@ export const OpportunityPicker = ({
|
|||||||
],
|
],
|
||||||
orderByField: 'createdAt',
|
orderByField: 'createdAt',
|
||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
mappingFunction: (record: any) => identifiersMapper?.(record, 'company'),
|
|
||||||
objectNameSingular: CoreObjectNameSingular.Company,
|
objectNameSingular: CoreObjectNameSingular.Company,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker';
|
import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker';
|
||||||
import { IdentifiersMapper } from '@/object-record/relation-picker/types/IdentifiersMapper';
|
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
|
||||||
|
|
||||||
export const ObjectMetadataItemsRelationPickerEffect = () => {
|
export const ObjectMetadataItemsRelationPickerEffect = () => {
|
||||||
const { setIdentifiersMapper, setSearchQuery } = useRelationPicker({
|
const { setSearchQuery } = useRelationPicker({
|
||||||
relationPickerScopeId: 'relation-picker',
|
relationPickerScopeId: 'relation-picker',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -21,62 +19,9 @@ export const ObjectMetadataItemsRelationPickerEffect = () => {
|
|||||||
return ['name'];
|
return ['name'];
|
||||||
};
|
};
|
||||||
|
|
||||||
const identifierMapper: IdentifiersMapper = (
|
|
||||||
record: any,
|
|
||||||
objectMetadataItemSingularName: string,
|
|
||||||
) => {
|
|
||||||
if (!record) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectMetadataItemSingularName === 'company') {
|
|
||||||
return {
|
|
||||||
id: record.id,
|
|
||||||
name: record.name,
|
|
||||||
avatarUrl: getLogoUrlFromDomainName(record.domainName ?? ''),
|
|
||||||
avatarType: 'squared',
|
|
||||||
record: record,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
['workspaceMember', 'person'].includes(objectMetadataItemSingularName)
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
id: record.id,
|
|
||||||
name:
|
|
||||||
(record.name?.firstName ?? '') + ' ' + (record.name?.lastName ?? ''),
|
|
||||||
avatarUrl: record.avatarUrl,
|
|
||||||
avatarType: 'rounded',
|
|
||||||
record: record,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (['opportunity'].includes(objectMetadataItemSingularName)) {
|
|
||||||
return {
|
|
||||||
id: record.id,
|
|
||||||
name: record?.company?.name ?? record.name,
|
|
||||||
avatarUrl: record.avatarUrl,
|
|
||||||
avatarType: 'rounded',
|
|
||||||
record: record,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: record.id,
|
|
||||||
name: record.name,
|
|
||||||
avatarUrl: record.avatarUrl,
|
|
||||||
avatarType: 'rounded',
|
|
||||||
record,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIdentifiersMapper(() => identifierMapper);
|
setSearchQuery({ computeFilterFields });
|
||||||
setSearchQuery({
|
}, [setSearchQuery]);
|
||||||
computeFilterFields,
|
|
||||||
});
|
|
||||||
}, [setIdentifiersMapper, setSearchQuery]);
|
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,16 +1,8 @@
|
|||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
|
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier';
|
|
||||||
|
|
||||||
export const useMapToObjectRecordIdentifier = ({
|
export const useMapToObjectRecordIdentifier =
|
||||||
objectMetadataItem,
|
({ objectMetadataItem }: { objectMetadataItem: ObjectMetadataItem }) =>
|
||||||
}: {
|
(record: ObjectRecord) =>
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
getObjectRecordIdentifier({ objectMetadataItem, record });
|
||||||
}): ((record: ObjectRecord) => ObjectRecordIdentifier) => {
|
|
||||||
return (record: ObjectRecord) =>
|
|
||||||
getObjectRecordIdentifier({
|
|
||||||
objectMetadataItem,
|
|
||||||
record,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@ -14,34 +14,25 @@ export const getObjectRecordIdentifier = ({
|
|||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
record: ObjectRecord;
|
record: ObjectRecord;
|
||||||
}): ObjectRecordIdentifier => {
|
}): ObjectRecordIdentifier => {
|
||||||
switch (objectMetadataItem.nameSingular) {
|
if (objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity) {
|
||||||
case CoreObjectNameSingular.Opportunity:
|
return {
|
||||||
return {
|
id: record.id,
|
||||||
id: record.id,
|
name: record?.company?.name ?? record.name,
|
||||||
name: record?.company?.name ?? record.name,
|
avatarUrl: record.avatarUrl,
|
||||||
avatarUrl: record.avatarUrl,
|
avatarType: 'rounded',
|
||||||
avatarType: 'rounded',
|
linkToShowPage: `/opportunities/${record.id}`,
|
||||||
linkToShowPage: `/opportunities/${record.id}`,
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelIdentifierFieldMetadataItem =
|
const labelIdentifierFieldMetadataItem =
|
||||||
getLabelIdentifierFieldMetadataItem(objectMetadataItem);
|
getLabelIdentifierFieldMetadataItem(objectMetadataItem);
|
||||||
|
|
||||||
let labelIdentifierFieldValue = '';
|
const labelIdentifierFieldValue =
|
||||||
|
labelIdentifierFieldMetadataItem?.type === FieldMetadataType.FullName
|
||||||
switch (labelIdentifierFieldMetadataItem?.type) {
|
? `${record.name?.firstName ?? ''} ${record.name?.lastName ?? ''}`
|
||||||
case FieldMetadataType.FullName: {
|
: labelIdentifierFieldMetadataItem?.name
|
||||||
labelIdentifierFieldValue = `${record.name?.firstName ?? ''} ${
|
? (record[labelIdentifierFieldMetadataItem.name] as string | number)
|
||||||
record.name?.lastName ?? ''
|
|
||||||
}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
labelIdentifierFieldValue = labelIdentifierFieldMetadataItem
|
|
||||||
? record[labelIdentifierFieldMetadataItem.name]
|
|
||||||
: '';
|
: '';
|
||||||
}
|
|
||||||
|
|
||||||
const imageIdentifierFieldMetadata = objectMetadataItem.fields.find(
|
const imageIdentifierFieldMetadata = objectMetadataItem.fields.find(
|
||||||
(field) => field.id === objectMetadataItem.imageIdentifierFieldMetadataId,
|
(field) => field.id === objectMetadataItem.imageIdentifierFieldMetadataId,
|
||||||
@ -57,9 +48,9 @@ export const getObjectRecordIdentifier = ({
|
|||||||
: 'rounded';
|
: 'rounded';
|
||||||
|
|
||||||
const avatarUrl =
|
const avatarUrl =
|
||||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Company
|
(objectMetadataItem.nameSingular === CoreObjectNameSingular.Company
|
||||||
? getLogoUrlFromDomainName(record['domainName'] ?? '')
|
? getLogoUrlFromDomainName(record['domainName'] ?? '')
|
||||||
: imageIdentifierFieldValue ?? null;
|
: imageIdentifierFieldValue) ?? '';
|
||||||
|
|
||||||
const basePathToShowPage = getBasePathToShowPage({
|
const basePathToShowPage = getBasePathToShowPage({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
@ -69,7 +60,7 @@ export const getObjectRecordIdentifier = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: record.id,
|
id: record.id,
|
||||||
name: labelIdentifierFieldValue,
|
name: `${labelIdentifierFieldValue}`,
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
avatarType,
|
avatarType,
|
||||||
linkToShowPage,
|
linkToShowPage,
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export const RecordChip = ({ objectNameSingular, record }: RecordChipProps) => {
|
|||||||
entityId={record.id}
|
entityId={record.id}
|
||||||
name={objectRecordIdentifier.name}
|
name={objectRecordIdentifier.name}
|
||||||
avatarType={objectRecordIdentifier.avatarType}
|
avatarType={objectRecordIdentifier.avatarType}
|
||||||
avatarUrl={objectRecordIdentifier.avatarUrl ?? undefined}
|
avatarUrl={objectRecordIdentifier.avatarUrl}
|
||||||
linkToEntity={objectRecordIdentifier.linkToShowPage}
|
linkToEntity={objectRecordIdentifier.linkToShowPage}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -27,39 +27,33 @@ import { isFieldUuid } from '../types/guards/isFieldUuid';
|
|||||||
|
|
||||||
export const FieldDisplay = () => {
|
export const FieldDisplay = () => {
|
||||||
const { fieldDefinition, isLabelIdentifier } = useContext(FieldContext);
|
const { fieldDefinition, isLabelIdentifier } = useContext(FieldContext);
|
||||||
if (
|
|
||||||
isLabelIdentifier &&
|
return isLabelIdentifier &&
|
||||||
(isFieldText(fieldDefinition) || isFieldFullName(fieldDefinition))
|
(isFieldText(fieldDefinition) ||
|
||||||
) {
|
isFieldFullName(fieldDefinition) ||
|
||||||
return <ChipFieldDisplay />;
|
isFieldNumber(fieldDefinition)) ? (
|
||||||
}
|
<ChipFieldDisplay />
|
||||||
return (
|
) : isFieldRelation(fieldDefinition) ? (
|
||||||
<>
|
<RelationFieldDisplay />
|
||||||
{isFieldRelation(fieldDefinition) ? (
|
) : isFieldText(fieldDefinition) ? (
|
||||||
<RelationFieldDisplay />
|
<TextFieldDisplay />
|
||||||
) : isFieldText(fieldDefinition) ? (
|
) : isFieldUuid(fieldDefinition) ? (
|
||||||
<TextFieldDisplay />
|
<UuidFieldDisplay />
|
||||||
) : isFieldUuid(fieldDefinition) ? (
|
) : isFieldEmail(fieldDefinition) ? (
|
||||||
<UuidFieldDisplay />
|
<EmailFieldDisplay />
|
||||||
) : isFieldEmail(fieldDefinition) ? (
|
) : isFieldDateTime(fieldDefinition) ? (
|
||||||
<EmailFieldDisplay />
|
<DateFieldDisplay />
|
||||||
) : isFieldDateTime(fieldDefinition) ? (
|
) : isFieldNumber(fieldDefinition) ? (
|
||||||
<DateFieldDisplay />
|
<NumberFieldDisplay />
|
||||||
) : isFieldNumber(fieldDefinition) ? (
|
) : isFieldLink(fieldDefinition) ? (
|
||||||
<NumberFieldDisplay />
|
<LinkFieldDisplay />
|
||||||
) : isFieldLink(fieldDefinition) ? (
|
) : isFieldCurrency(fieldDefinition) ? (
|
||||||
<LinkFieldDisplay />
|
<CurrencyFieldDisplay />
|
||||||
) : isFieldCurrency(fieldDefinition) ? (
|
) : isFieldFullName(fieldDefinition) ? (
|
||||||
<CurrencyFieldDisplay />
|
<FullNameFieldDisplay />
|
||||||
) : isFieldFullName(fieldDefinition) ? (
|
) : isFieldPhone(fieldDefinition) ? (
|
||||||
<FullNameFieldDisplay />
|
<PhoneFieldDisplay />
|
||||||
) : isFieldPhone(fieldDefinition) ? (
|
) : isFieldSelect(fieldDefinition) ? (
|
||||||
<PhoneFieldDisplay />
|
<SelectFieldDisplay />
|
||||||
) : isFieldSelect(fieldDefinition) ? (
|
) : null;
|
||||||
<SelectFieldDisplay />
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,25 +1,12 @@
|
|||||||
|
import { RecordChip } from '@/object-record/components/RecordChip';
|
||||||
import { useChipField } from '@/object-record/field/meta-types/hooks/useChipField';
|
import { useChipField } from '@/object-record/field/meta-types/hooks/useChipField';
|
||||||
import { EntityChip } from '@/ui/display/chip/components/EntityChip';
|
|
||||||
|
|
||||||
export const ChipFieldDisplay = () => {
|
export const ChipFieldDisplay = () => {
|
||||||
const {
|
const { objectNameSingular, record } = useChipField();
|
||||||
record,
|
|
||||||
entityId,
|
|
||||||
identifiersMapper,
|
|
||||||
objectNameSingular,
|
|
||||||
basePathToShowPage,
|
|
||||||
} = useChipField();
|
|
||||||
|
|
||||||
// TODO: remove this and use ObjectRecordChip instead
|
if (!record) return null;
|
||||||
const identifiers = identifiersMapper?.(record, objectNameSingular ?? '');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityChip
|
<RecordChip objectNameSingular={objectNameSingular || ''} record={record} />
|
||||||
name={identifiers?.name ?? ''}
|
|
||||||
avatarUrl={identifiers?.avatarUrl}
|
|
||||||
avatarType={identifiers?.avatarType}
|
|
||||||
entityId={entityId}
|
|
||||||
linkToEntity={basePathToShowPage + entityId}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,30 +1,18 @@
|
|||||||
import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker';
|
import { RecordChip } from '@/object-record/components/RecordChip';
|
||||||
import { EntityChip } from '@/ui/display/chip/components/EntityChip';
|
|
||||||
|
|
||||||
import { useRelationField } from '../../hooks/useRelationField';
|
import { useRelationField } from '../../hooks/useRelationField';
|
||||||
|
|
||||||
export const RelationFieldDisplay = () => {
|
export const RelationFieldDisplay = () => {
|
||||||
const { fieldValue, fieldDefinition } = useRelationField();
|
const { fieldValue, fieldDefinition } = useRelationField();
|
||||||
|
|
||||||
const { identifiersMapper } = useRelationPicker({
|
if (!fieldValue || !fieldDefinition) return null;
|
||||||
relationPickerScopeId: 'relation-picker',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!fieldValue || !fieldDefinition || !identifiersMapper) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const objectIdentifiers = identifiersMapper(
|
|
||||||
fieldValue,
|
|
||||||
fieldDefinition.metadata.relationObjectMetadataNameSingular,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityChip
|
<RecordChip
|
||||||
entityId={fieldValue.id}
|
objectNameSingular={
|
||||||
name={objectIdentifiers?.name ?? ''}
|
fieldDefinition.metadata.relationObjectMetadataNameSingular
|
||||||
avatarUrl={objectIdentifiers?.avatarUrl}
|
}
|
||||||
avatarType={objectIdentifiers?.avatarType}
|
record={fieldValue}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,31 +3,25 @@ import { useRecoilValue } from 'recoil';
|
|||||||
|
|
||||||
import { entityFieldsFamilyState } from '@/object-record/field/states/entityFieldsFamilyState';
|
import { entityFieldsFamilyState } from '@/object-record/field/states/entityFieldsFamilyState';
|
||||||
import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName';
|
import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName';
|
||||||
|
import { isFieldNumber } from '@/object-record/field/types/guards/isFieldNumber';
|
||||||
import { isFieldText } from '@/object-record/field/types/guards/isFieldText';
|
import { isFieldText } from '@/object-record/field/types/guards/isFieldText';
|
||||||
import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker';
|
|
||||||
|
|
||||||
import { FieldContext } from '../../contexts/FieldContext';
|
import { FieldContext } from '../../contexts/FieldContext';
|
||||||
|
|
||||||
export const useChipField = () => {
|
export const useChipField = () => {
|
||||||
const { entityId, fieldDefinition, basePathToShowPage } =
|
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||||
useContext(FieldContext);
|
|
||||||
|
|
||||||
const objectNameSingular =
|
const objectNameSingular =
|
||||||
isFieldText(fieldDefinition) || isFieldFullName(fieldDefinition)
|
isFieldText(fieldDefinition) ||
|
||||||
|
isFieldFullName(fieldDefinition) ||
|
||||||
|
isFieldNumber(fieldDefinition)
|
||||||
? fieldDefinition.metadata.objectMetadataNameSingular
|
? fieldDefinition.metadata.objectMetadataNameSingular
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const record = useRecoilValue<any | null>(entityFieldsFamilyState(entityId));
|
const record = useRecoilValue(entityFieldsFamilyState(entityId));
|
||||||
|
|
||||||
const { identifiersMapper } = useRelationPicker({
|
|
||||||
relationPickerScopeId: 'relation-picker',
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
basePathToShowPage,
|
|
||||||
entityId,
|
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
record,
|
record,
|
||||||
identifiersMapper,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,20 +1,28 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
import { expect, fn, userEvent, within } from '@storybook/test';
|
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { entityFieldsFamilyState } from '@/object-record/field/states/entityFieldsFamilyState';
|
||||||
|
|
||||||
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
|
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
|
||||||
import { useBooleanField } from '../../../hooks/useBooleanField';
|
|
||||||
import {
|
import {
|
||||||
BooleanFieldInput,
|
BooleanFieldInput,
|
||||||
BooleanFieldInputProps,
|
BooleanFieldInputProps,
|
||||||
} from '../BooleanFieldInput';
|
} from '../BooleanFieldInput';
|
||||||
|
|
||||||
const BooleanFieldValueSetterEffect = ({ value }: { value: boolean }) => {
|
const BooleanFieldValueSetterEffect = ({
|
||||||
const { setFieldValue } = useBooleanField();
|
value,
|
||||||
|
entityId,
|
||||||
|
}: {
|
||||||
|
value: boolean;
|
||||||
|
entityId: string;
|
||||||
|
}) => {
|
||||||
|
const setField = useSetRecoilState(entityFieldsFamilyState(entityId));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFieldValue(value);
|
setField({ id: entityId, Boolean: value });
|
||||||
}, [setFieldValue, value]);
|
}, [entityId, setField, value]);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
@ -42,7 +50,7 @@ const BooleanFieldInputWithContext = ({
|
|||||||
}}
|
}}
|
||||||
entityId={entityId}
|
entityId={entityId}
|
||||||
>
|
>
|
||||||
<BooleanFieldValueSetterEffect value={value} />
|
<BooleanFieldValueSetterEffect value={value} entityId={entityId ?? ''} />
|
||||||
<BooleanFieldInput onSubmit={onSubmit} testId="boolean-field-input" />
|
<BooleanFieldInput onSubmit={onSubmit} testId="boolean-field-input" />
|
||||||
</FieldContextProvider>
|
</FieldContextProvider>
|
||||||
);
|
);
|
||||||
@ -53,6 +61,7 @@ const meta: Meta = {
|
|||||||
component: BooleanFieldInputWithContext,
|
component: BooleanFieldInputWithContext,
|
||||||
args: {
|
args: {
|
||||||
value: true,
|
value: true,
|
||||||
|
entityId: 'id-1',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import { atomFamily } from 'recoil';
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
export const entityFieldsFamilyState = atomFamily<
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
Record<string, unknown> | null,
|
|
||||||
string
|
export const entityFieldsFamilyState = atomFamily<ObjectRecord | null, string>({
|
||||||
>({
|
|
||||||
key: 'entityFieldsFamilyState',
|
key: 'entityFieldsFamilyState',
|
||||||
default: null,
|
default: null,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,8 +11,7 @@ export const entityFieldsFamilySelector = selectorFamily({
|
|||||||
set:
|
set:
|
||||||
<T>({ fieldName, entityId }: { fieldName: string; entityId: string }) =>
|
<T>({ fieldName, entityId }: { fieldName: string; entityId: string }) =>
|
||||||
({ set }, newValue: T) =>
|
({ set }, newValue: T) =>
|
||||||
set(entityFieldsFamilyState(entityId), (prevState) => ({
|
set(entityFieldsFamilyState(entityId), (prevState) =>
|
||||||
...prevState,
|
prevState ? { ...prevState, [fieldName]: newValue } : null,
|
||||||
[fieldName]: newValue,
|
),
|
||||||
})),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -125,7 +125,7 @@ export const RecordRelationFieldCardSection = () => {
|
|||||||
const { relationPickerSearchFilter, setRelationPickerSearchFilter } =
|
const { relationPickerSearchFilter, setRelationPickerSearchFilter } =
|
||||||
useRelationPicker({ relationPickerScopeId: dropdownId });
|
useRelationPicker({ relationPickerScopeId: dropdownId });
|
||||||
|
|
||||||
const { identifiersMapper, searchQuery } = useRelationPicker();
|
const { searchQuery } = useRelationPicker();
|
||||||
|
|
||||||
const entities = useFilteredSearchEntityQuery({
|
const entities = useFilteredSearchEntityQuery({
|
||||||
filters: [
|
filters: [
|
||||||
@ -138,8 +138,6 @@ export const RecordRelationFieldCardSection = () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
orderByField: 'createdAt',
|
orderByField: 'createdAt',
|
||||||
mappingFunction: (recordToMap) =>
|
|
||||||
identifiersMapper?.(recordToMap, relationObjectMetadataNameSingular),
|
|
||||||
selectedIds: relationRecordIds,
|
selectedIds: relationRecordIds,
|
||||||
excludeEntityIds: relationRecordIds,
|
excludeEntityIds: relationRecordIds,
|
||||||
objectNameSingular: relationObjectMetadataNameSingular,
|
objectNameSingular: relationObjectMetadataNameSingular,
|
||||||
|
|||||||
@ -117,17 +117,13 @@ export const RecordTable = ({
|
|||||||
recordTableScopeId={scopeId}
|
recordTableScopeId={scopeId}
|
||||||
onColumnsChange={onColumnsChange}
|
onColumnsChange={onColumnsChange}
|
||||||
>
|
>
|
||||||
<>
|
{!!objectNamePlural && (
|
||||||
{objectNamePlural ? (
|
<StyledTable ref={recordTableRef} className="entity-table-cell">
|
||||||
<StyledTable ref={recordTableRef} className="entity-table-cell">
|
<RecordTableHeader createRecord={createRecord} />
|
||||||
<RecordTableHeader createRecord={createRecord} />
|
<RecordTableBodyEffect objectNamePlural={objectNamePlural} />
|
||||||
<RecordTableBodyEffect objectNamePlural={objectNamePlural} />
|
<RecordTableBody objectNamePlural={objectNamePlural} />
|
||||||
<RecordTableBody objectNamePlural={objectNamePlural} />
|
</StyledTable>
|
||||||
</StyledTable>
|
)}
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</RecordTableScope>
|
</RecordTableScope>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
|
||||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||||
@ -26,9 +26,6 @@ export const RecordTableCellContainer = ({
|
|||||||
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||||
const currentRowId = useContext(RowIdContext);
|
const currentRowId = useContext(RowIdContext);
|
||||||
const { getObjectMetadataConfigState } = useRecordTableStates();
|
|
||||||
|
|
||||||
const objectMetadataConfig = useRecoilValue(getObjectMetadataConfigState());
|
|
||||||
|
|
||||||
const { setCurrentRowSelected } = useCurrentRowSelected();
|
const { setCurrentRowSelected } = useCurrentRowSelected();
|
||||||
|
|
||||||
@ -44,6 +41,11 @@ export const RecordTableCellContainer = ({
|
|||||||
|
|
||||||
const columnDefinition = useContext(ColumnContext);
|
const columnDefinition = useContext(ColumnContext);
|
||||||
|
|
||||||
|
const { basePathToShowPage, objectMetadataItem } = useObjectMetadataItem({
|
||||||
|
objectNameSingular:
|
||||||
|
columnDefinition?.metadata.objectMetadataNameSingular || '',
|
||||||
|
});
|
||||||
|
|
||||||
const updateRecord = useContext(RecordUpdateContext);
|
const updateRecord = useContext(RecordUpdateContext);
|
||||||
|
|
||||||
if (!columnDefinition || !currentRowId) {
|
if (!columnDefinition || !currentRowId) {
|
||||||
@ -65,16 +67,13 @@ export const RecordTableCellContainer = ({
|
|||||||
fieldDefinition: columnDefinition,
|
fieldDefinition: columnDefinition,
|
||||||
useUpdateRecord: () => [updateRecord, {}],
|
useUpdateRecord: () => [updateRecord, {}],
|
||||||
hotkeyScope: customHotkeyScope,
|
hotkeyScope: customHotkeyScope,
|
||||||
basePathToShowPage: objectMetadataConfig?.basePathToShowPage,
|
basePathToShowPage,
|
||||||
isLabelIdentifier: isLabelIdentifierField({
|
isLabelIdentifier: isLabelIdentifierField({
|
||||||
fieldMetadataItem: {
|
fieldMetadataItem: {
|
||||||
id: columnDefinition.fieldMetadataId,
|
id: columnDefinition.fieldMetadataId,
|
||||||
name: columnDefinition.metadata.fieldName,
|
name: columnDefinition.metadata.fieldName,
|
||||||
},
|
},
|
||||||
objectMetadataItem: {
|
objectMetadataItem,
|
||||||
labelIdentifierFieldMetadataId:
|
|
||||||
objectMetadataConfig?.labelIdentifierFieldMetadataId,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -96,6 +96,6 @@ export const RecordTableCell = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
nonEditModeContent={<FieldDisplay />}
|
nonEditModeContent={<FieldDisplay />}
|
||||||
></TableCellContainer>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
|
||||||
import { FieldDefinition } from '@/object-record/field/types/FieldDefinition';
|
import { FieldDefinition } from '@/object-record/field/types/FieldDefinition';
|
||||||
import { FieldRelationMetadata } from '@/object-record/field/types/FieldMetadata';
|
import { FieldRelationMetadata } from '@/object-record/field/types/FieldMetadata';
|
||||||
import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect';
|
import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect';
|
||||||
@ -31,7 +30,6 @@ export const RelationPicker = ({
|
|||||||
const {
|
const {
|
||||||
relationPickerSearchFilter,
|
relationPickerSearchFilter,
|
||||||
setRelationPickerSearchFilter,
|
setRelationPickerSearchFilter,
|
||||||
identifiersMapper,
|
|
||||||
searchQuery,
|
searchQuery,
|
||||||
} = useRelationPicker({ relationPickerScopeId: 'relation-picker' });
|
} = useRelationPicker({ relationPickerScopeId: 'relation-picker' });
|
||||||
|
|
||||||
@ -39,12 +37,6 @@ export const RelationPicker = ({
|
|||||||
setRelationPickerSearchFilter(initialSearchFilter ?? '');
|
setRelationPickerSearchFilter(initialSearchFilter ?? '');
|
||||||
}, [initialSearchFilter, setRelationPickerSearchFilter]);
|
}, [initialSearchFilter, setRelationPickerSearchFilter]);
|
||||||
|
|
||||||
const { objectNameSingular: relationObjectNameSingular } =
|
|
||||||
useObjectNameSingularFromPlural({
|
|
||||||
objectNamePlural:
|
|
||||||
fieldDefinition.metadata.relationObjectMetadataNamePlural,
|
|
||||||
});
|
|
||||||
|
|
||||||
const entities = useFilteredSearchEntityQuery({
|
const entities = useFilteredSearchEntityQuery({
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
@ -56,18 +48,15 @@ export const RelationPicker = ({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
orderByField: 'createdAt',
|
orderByField: 'createdAt',
|
||||||
mappingFunction: (record: any) =>
|
|
||||||
identifiersMapper?.(
|
|
||||||
record,
|
|
||||||
fieldDefinition.metadata.relationObjectMetadataNameSingular,
|
|
||||||
),
|
|
||||||
selectedIds: recordId ? [recordId] : [],
|
selectedIds: recordId ? [recordId] : [],
|
||||||
excludeEntityIds: excludeRecordIds,
|
excludeEntityIds: excludeRecordIds,
|
||||||
objectNameSingular: relationObjectNameSingular,
|
objectNameSingular:
|
||||||
|
fieldDefinition.metadata.relationObjectMetadataNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleEntitySelected = (selectedEntity: any | null | undefined) =>
|
const handleEntitySelected = (
|
||||||
onSubmit(selectedEntity ?? null);
|
selectedEntity: EntityForSelect | null | undefined,
|
||||||
|
) => onSubmit(selectedEntity ?? null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SingleEntitySelect
|
<SingleEntitySelect
|
||||||
|
|||||||
@ -14,6 +14,8 @@ import { SingleEntitySelect } from '../SingleEntitySelect';
|
|||||||
const entities = mockedPeopleData.map<EntityForSelect>((person) => ({
|
const entities = mockedPeopleData.map<EntityForSelect>((person) => ({
|
||||||
id: person.id,
|
id: person.id,
|
||||||
name: person.name.firstName + ' ' + person.name.lastName,
|
name: person.name.firstName + ' ' + person.name.lastName,
|
||||||
|
avatarUrl: person.avatarUrl,
|
||||||
|
avatarType: 'rounded',
|
||||||
record: person,
|
record: person,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,6 @@ export const useRelationPickerScopedStates = (args?: {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
identifiersMapperState,
|
|
||||||
relationPickerSearchFilterState,
|
relationPickerSearchFilterState,
|
||||||
relationPickerPreselectedIdState,
|
relationPickerPreselectedIdState,
|
||||||
searchQueryState,
|
searchQueryState,
|
||||||
@ -23,7 +22,6 @@ export const useRelationPickerScopedStates = (args?: {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
scopeId,
|
scopeId,
|
||||||
identifiersMapperState,
|
|
||||||
relationPickerSearchFilterState,
|
relationPickerSearchFilterState,
|
||||||
relationPickerPreselectedIdState,
|
relationPickerPreselectedIdState,
|
||||||
searchQueryState,
|
searchQueryState,
|
||||||
|
|||||||
@ -15,7 +15,6 @@ export const useRelationPicker = (props?: useRelationPickeProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
identifiersMapperState,
|
|
||||||
searchQueryState,
|
searchQueryState,
|
||||||
relationPickerSearchFilterState,
|
relationPickerSearchFilterState,
|
||||||
relationPickerPreselectedIdState,
|
relationPickerPreselectedIdState,
|
||||||
@ -23,10 +22,6 @@ export const useRelationPicker = (props?: useRelationPickeProps) => {
|
|||||||
relationPickerScopedId: scopeId,
|
relationPickerScopedId: scopeId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [identifiersMapper, setIdentifiersMapper] = useRecoilState(
|
|
||||||
identifiersMapperState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [searchQuery, setSearchQuery] = useRecoilState(searchQueryState);
|
const [searchQuery, setSearchQuery] = useRecoilState(searchQueryState);
|
||||||
|
|
||||||
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
|
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
|
||||||
@ -37,8 +32,6 @@ export const useRelationPicker = (props?: useRelationPickeProps) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
scopeId,
|
scopeId,
|
||||||
identifiersMapper,
|
|
||||||
setIdentifiersMapper,
|
|
||||||
searchQuery,
|
searchQuery,
|
||||||
setSearchQuery,
|
setSearchQuery,
|
||||||
relationPickerSearchFilter,
|
relationPickerSearchFilter,
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
import { IdentifiersMapper } from '@/object-record/relation-picker/types/IdentifiersMapper';
|
|
||||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
|
||||||
|
|
||||||
export const identifiersMapperScopedState =
|
|
||||||
createStateScopeMap<IdentifiersMapper | null>({
|
|
||||||
key: 'identifiersMapperScopedState',
|
|
||||||
defaultValue: null,
|
|
||||||
});
|
|
||||||
@ -1,9 +1,4 @@
|
|||||||
import { AvatarType } from '@/users/components/Avatar';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier';
|
||||||
|
|
||||||
export type EntityForSelect = {
|
export type EntityForSelect = ObjectRecordIdentifier & { record: ObjectRecord };
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
avatarUrl?: string;
|
|
||||||
avatarType?: AvatarType;
|
|
||||||
record: any;
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import { AvatarType } from '@/users/components/Avatar';
|
|
||||||
|
|
||||||
type RecordMappedToIdentifiers = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
avatarUrl?: string;
|
|
||||||
avatarType: AvatarType;
|
|
||||||
record: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type IdentifiersMapper = (
|
|
||||||
record: any,
|
|
||||||
relationPickerType: string,
|
|
||||||
) => RecordMappedToIdentifiers | undefined;
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { identifiersMapperScopedState } from '@/object-record/relation-picker/states/identifiersMapperScopedState';
|
|
||||||
import { relationPickerPreselectedIdScopedState } from '@/object-record/relation-picker/states/relationPickerPreselectedIdScopedState';
|
import { relationPickerPreselectedIdScopedState } from '@/object-record/relation-picker/states/relationPickerPreselectedIdScopedState';
|
||||||
import { relationPickerSearchFilterScopedState } from '@/object-record/relation-picker/states/relationPickerSearchFilterScopedState';
|
import { relationPickerSearchFilterScopedState } from '@/object-record/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||||
import { searchQueryScopedState } from '@/object-record/relation-picker/states/searchQueryScopedState';
|
import { searchQueryScopedState } from '@/object-record/relation-picker/states/searchQueryScopedState';
|
||||||
@ -9,11 +8,6 @@ export const getRelationPickerScopedStates = ({
|
|||||||
}: {
|
}: {
|
||||||
relationPickerScopeId: string;
|
relationPickerScopeId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const identifiersMapperState = getScopedStateDeprecated(
|
|
||||||
identifiersMapperScopedState,
|
|
||||||
relationPickerScopeId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const searchQueryState = getScopedStateDeprecated(
|
const searchQueryState = getScopedStateDeprecated(
|
||||||
searchQueryScopedState,
|
searchQueryScopedState,
|
||||||
relationPickerScopeId,
|
relationPickerScopeId,
|
||||||
@ -30,7 +24,6 @@ export const getRelationPickerScopedStates = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
identifiersMapperState,
|
|
||||||
relationPickerSearchFilterState,
|
relationPickerSearchFilterState,
|
||||||
relationPickerPreselectedIdState,
|
relationPickerPreselectedIdState,
|
||||||
searchQueryState,
|
searchQueryState,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { AvatarType } from '@/users/components/Avatar';
|
|||||||
export type ObjectRecordIdentifier = {
|
export type ObjectRecordIdentifier = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
avatarUrl?: string | null;
|
avatarUrl: string;
|
||||||
avatarType?: AvatarType | null;
|
avatarType?: AvatarType | null;
|
||||||
linkToShowPage?: string;
|
linkToShowPage?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -85,10 +85,6 @@ describe('useFilteredSearchEntityQuery', () => {
|
|||||||
filters: [{ fieldNames: ['name'], filter: 'Entity' }],
|
filters: [{ fieldNames: ['name'], filter: 'Entity' }],
|
||||||
sortOrder: 'AscNullsLast',
|
sortOrder: 'AscNullsLast',
|
||||||
selectedIds: ['1'],
|
selectedIds: ['1'],
|
||||||
mappingFunction: (entity): any => ({
|
|
||||||
value: entity.id,
|
|
||||||
label: entity.name,
|
|
||||||
}),
|
|
||||||
limit: 10,
|
limit: 10,
|
||||||
excludeEntityIds: ['2'],
|
excludeEntityIds: ['2'],
|
||||||
objectNameSingular: 'person',
|
objectNameSingular: 'person',
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
||||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||||
import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect';
|
import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect';
|
||||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { assertNotNull } from '~/utils/assert';
|
import { assertNotNull } from '~/utils/assert';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
@ -19,7 +21,6 @@ export const useFilteredSearchEntityQuery = ({
|
|||||||
filters,
|
filters,
|
||||||
sortOrder = 'AscNullsLast',
|
sortOrder = 'AscNullsLast',
|
||||||
selectedIds,
|
selectedIds,
|
||||||
mappingFunction,
|
|
||||||
limit,
|
limit,
|
||||||
excludeEntityIds = [],
|
excludeEntityIds = [],
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
@ -28,11 +29,18 @@ export const useFilteredSearchEntityQuery = ({
|
|||||||
filters: SearchFilter[];
|
filters: SearchFilter[];
|
||||||
sortOrder?: OrderBy;
|
sortOrder?: OrderBy;
|
||||||
selectedIds: string[];
|
selectedIds: string[];
|
||||||
mappingFunction: (entity: any) => EntityForSelect | undefined;
|
|
||||||
limit?: number;
|
limit?: number;
|
||||||
excludeEntityIds?: string[];
|
excludeEntityIds?: string[];
|
||||||
objectNameSingular: string;
|
objectNameSingular: string;
|
||||||
}): EntitiesForMultipleEntitySelect<EntityForSelect> => {
|
}): EntitiesForMultipleEntitySelect<EntityForSelect> => {
|
||||||
|
const { mapToObjectRecordIdentifier } = useObjectMetadataItem({
|
||||||
|
objectNameSingular,
|
||||||
|
});
|
||||||
|
const mappingFunction = (record: ObjectRecord) => ({
|
||||||
|
...mapToObjectRecordIdentifier(record),
|
||||||
|
record,
|
||||||
|
});
|
||||||
|
|
||||||
const { loading: selectedRecordsLoading, records: selectedRecords } =
|
const { loading: selectedRecordsLoading, records: selectedRecords } =
|
||||||
useFindManyRecords({
|
useFindManyRecords({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
|
|||||||
@ -47,34 +47,34 @@ export const EntityChip = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return isNonEmptyString(name) ? (
|
return (
|
||||||
<Chip
|
isNonEmptyString(name) && (
|
||||||
label={name}
|
<Chip
|
||||||
variant={
|
label={name}
|
||||||
linkToEntity
|
variant={
|
||||||
? variant === EntityChipVariant.Regular
|
linkToEntity
|
||||||
? ChipVariant.Highlighted
|
? variant === EntityChipVariant.Regular
|
||||||
: ChipVariant.Regular
|
? ChipVariant.Highlighted
|
||||||
: ChipVariant.Transparent
|
: ChipVariant.Regular
|
||||||
}
|
: ChipVariant.Transparent
|
||||||
leftComponent={
|
}
|
||||||
LeftIcon ? (
|
leftComponent={
|
||||||
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
LeftIcon ? (
|
||||||
) : (
|
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||||
<Avatar
|
) : (
|
||||||
avatarUrl={avatarUrl}
|
<Avatar
|
||||||
colorId={entityId}
|
avatarUrl={avatarUrl}
|
||||||
placeholder={name}
|
colorId={entityId}
|
||||||
size="sm"
|
placeholder={name}
|
||||||
type={avatarType}
|
size="sm"
|
||||||
/>
|
type={avatarType}
|
||||||
)
|
/>
|
||||||
}
|
)
|
||||||
clickable={!!linkToEntity}
|
}
|
||||||
onClick={handleLinkClick}
|
clickable={!!linkToEntity}
|
||||||
className={className}
|
onClick={handleLinkClick}
|
||||||
/>
|
className={className}
|
||||||
) : (
|
/>
|
||||||
<></>
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user