diff --git a/front/src/modules/activities/components/ActivityAssigneePicker.tsx b/front/src/modules/activities/components/ActivityAssigneePicker.tsx
index 58c0ee2a7..f6b6f429c 100644
--- a/front/src/modules/activities/components/ActivityAssigneePicker.tsx
+++ b/front/src/modules/activities/components/ActivityAssigneePicker.tsx
@@ -97,13 +97,11 @@ export function ActivityAssigneePicker({
return (
);
}
diff --git a/front/src/modules/companies/components/CompanyPicker.tsx b/front/src/modules/companies/components/CompanyPicker.tsx
index 4666e6c80..a75fb57b3 100644
--- a/front/src/modules/companies/components/CompanyPicker.tsx
+++ b/front/src/modules/companies/components/CompanyPicker.tsx
@@ -34,13 +34,11 @@ export function CompanyPicker({ companyId, onSubmit, onCancel }: OwnProps) {
return (
);
}
diff --git a/front/src/modules/companies/components/CompanyPickerCell.tsx b/front/src/modules/companies/components/CompanyPickerCell.tsx
index adb9f0691..9adf76532 100644
--- a/front/src/modules/companies/components/CompanyPickerCell.tsx
+++ b/front/src/modules/companies/components/CompanyPickerCell.tsx
@@ -1,4 +1,5 @@
import { useFilteredSearchCompanyQuery } from '@/companies/hooks/useFilteredSearchCompanyQuery';
+import { IconBuildingSkyscraper } from '@/ui/icon';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
@@ -77,34 +78,25 @@ export function CompanyPickerCell({
});
setIsCreateMode(false);
}
- const noCompany: CompanyPickerSelectedCompany = {
- entityType: Entity.Company,
- id: '',
- name: 'No Company',
- avatarType: 'rounded',
- domainName: '',
- avatarUrl: '',
- };
return isCreateMode ? (
) : (
);
}
diff --git a/front/src/modules/companies/components/CompanyProgressPicker.tsx b/front/src/modules/companies/components/CompanyProgressPicker.tsx
index 4ff2c92fc..19b4706b7 100644
--- a/front/src/modules/companies/components/CompanyProgressPicker.tsx
+++ b/front/src/modules/companies/components/CompanyProgressPicker.tsx
@@ -114,13 +114,11 @@ export function CompanyProgressPicker({
>
diff --git a/front/src/modules/companies/components/NewCompanyProgressButton.tsx b/front/src/modules/companies/components/NewCompanyProgressButton.tsx
index 95acacf45..ce8caed3e 100644
--- a/front/src/modules/companies/components/NewCompanyProgressButton.tsx
+++ b/front/src/modules/companies/components/NewCompanyProgressButton.tsx
@@ -64,14 +64,12 @@ export function NewCompanyProgressButton() {
{isCreatingCard ? (
handleEntitySelect(value)}
+ disableBackgroundBlur
+ entitiesToSelect={companies.entitiesToSelect}
+ loading={companies.loading}
onCancel={handleCancel}
- entities={{
- entitiesToSelect: companies.entitiesToSelect,
- selectedEntity: companies.selectedEntities[0],
- loading: companies.loading,
- }}
- disableBackgroundBlur={true}
+ onEntitySelected={handleEntitySelect}
+ selectedEntity={companies.selectedEntities[0]}
/>
) : (
diff --git a/front/src/modules/people/components/PeoplePicker.tsx b/front/src/modules/people/components/PeoplePicker.tsx
index 227ea4685..c6871f854 100644
--- a/front/src/modules/people/components/PeoplePicker.tsx
+++ b/front/src/modules/people/components/PeoplePicker.tsx
@@ -68,14 +68,12 @@ export function PeoplePicker({
return (
);
}
diff --git a/front/src/modules/ui/board/components/BoardColumnMenu.tsx b/front/src/modules/ui/board/components/BoardColumnMenu.tsx
index 4ffcac132..c92c8562d 100644
--- a/front/src/modules/ui/board/components/BoardColumnMenu.tsx
+++ b/front/src/modules/ui/board/components/BoardColumnMenu.tsx
@@ -157,14 +157,12 @@ export function BoardColumnMenu({
)}
{currentMenu === 'add' && (
handleCompanySelected(value)}
+ disableBackgroundBlur
+ entitiesToSelect={companies.entitiesToSelect}
+ loading={companies.loading}
onCancel={closeMenu}
- entities={{
- entitiesToSelect: companies.entitiesToSelect,
- selectedEntity: companies.selectedEntities[0],
- loading: companies.loading,
- }}
- disableBackgroundBlur={true}
+ onEntitySelected={handleCompanySelected}
+ selectedEntity={companies.selectedEntities[0]}
/>
)}
diff --git a/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts b/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts
index de809610f..1e8c6a078 100644
--- a/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts
+++ b/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts
@@ -2,7 +2,7 @@ import { useContext } from 'react';
import { EditableFieldMutationContext } from '../contexts/EditableFieldMutationContext';
import { FieldDefinition } from '../types/FieldDefinition';
-import {
+import type {
FieldBooleanMetadata,
FieldBooleanValue,
FieldChipMetadata,
@@ -82,163 +82,85 @@ export function useUpdateGenericEntityField() {
>(
currentEntityId: string,
field: FieldDefinition,
- newFieldValue: ValueType,
+ newFieldValue: ValueType | null,
) {
- const newFieldValueUnknown = newFieldValue as unknown;
// TODO: improve type guards organization, maybe with a common typeguard for all fields
// taking an object of options as parameter ?
//
// The goal would be to check that the field value not only is valid,
// but also that it is validated against the corresponding field type
- // Relation
- if (isFieldRelation(field) && isFieldRelationValue(newFieldValueUnknown)) {
- const newSelectedEntity = newFieldValueUnknown;
-
- const fieldName = field.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 (isFieldChip(field) && isFieldChipValue(newFieldValueUnknown)) {
- const newContent = newFieldValueUnknown;
-
- updateEntity({
- variables: {
- where: { id: currentEntityId },
- data: { [field.metadata.contentFieldName]: newContent },
- },
- });
- // Text
- } else if (isFieldText(field) && isFieldTextValue(newFieldValueUnknown)) {
- const newContent = newFieldValueUnknown;
-
- updateEntity({
- variables: {
- where: { id: currentEntityId },
- data: { [field.metadata.fieldName]: newContent },
- },
- });
- // Double text
- } else if (
- isFieldDoubleText(field) &&
- isFieldDoubleTextValue(newFieldValueUnknown)
+ if (
+ // Relation
+ isFieldRelation(field) &&
+ isFieldRelationValue(newFieldValue)
) {
- const newContent = newFieldValueUnknown;
-
updateEntity({
variables: {
where: { id: currentEntityId },
data: {
- [field.metadata.firstValueFieldName]: newContent.firstValue,
- [field.metadata.secondValueFieldName]: newContent.secondValue,
+ [field.metadata.fieldName]: newFieldValue
+ ? { connect: { id: newFieldValue.id } }
+ : { disconnect: true },
},
},
});
- // Double Text Chip
- } else if (
- isFieldDoubleTextChip(field) &&
- isFieldDoubleTextChipValue(newFieldValueUnknown)
- ) {
- const newContent = newFieldValueUnknown;
-
- updateEntity({
- variables: {
- where: { id: currentEntityId },
- data: {
- [field.metadata.firstValueFieldName]: newContent.firstValue,
- [field.metadata.secondValueFieldName]: newContent.secondValue,
- },
- },
- });
- // Phone
- } else if (isFieldPhone(field) && isFieldPhoneValue(newFieldValueUnknown)) {
- const newContent = newFieldValueUnknown;
-
- updateEntity({
- variables: {
- where: { id: currentEntityId },
- data: { [field.metadata.fieldName]: newContent },
- },
- });
- // URL
- } else if (isFieldURL(field) && isFieldURLValue(newFieldValueUnknown)) {
- const newContent = newFieldValueUnknown;
-
- updateEntity({
- variables: {
- where: { id: currentEntityId },
- data: { [field.metadata.fieldName]: newContent },
- },
- });
- // Number
- } else if (
- isFieldNumber(field) &&
- isFieldNumberValue(newFieldValueUnknown)
- ) {
- const newContent = newFieldValueUnknown;
-
- updateEntity({
- variables: {
- where: { id: currentEntityId },
- data: { [field.metadata.fieldName]: newContent },
- },
- });
- // Date
- } else if (isFieldDate(field) && isFieldDateValue(newFieldValueUnknown)) {
- const newContent = newFieldValueUnknown;
-
- updateEntity({
- variables: {
- where: { id: currentEntityId },
- data: { [field.metadata.fieldName]: newContent },
- },
- });
- } else if (
- isFieldProbability(field) &&
- isFieldProbabilityValue(newFieldValueUnknown)
- ) {
- const newContent = newFieldValueUnknown;
-
- updateEntity({
- variables: {
- where: { id: currentEntityId },
- data: { [field.metadata.fieldName]: newContent },
- },
- });
+ return;
}
- // Boolean
- else if (
- isFieldBoolean(field) &&
- isFieldBooleanValue(newFieldValueUnknown)
- ) {
- const newContent = newFieldValueUnknown;
+ if (
+ // Chip
+ isFieldChip(field) &&
+ isFieldChipValue(newFieldValue)
+ ) {
updateEntity({
variables: {
where: { id: currentEntityId },
- data: { [field.metadata.fieldName]: newContent },
+ data: { [field.metadata.contentFieldName]: newFieldValue },
+ },
+ });
+ return;
+ }
+
+ if (
+ // Text
+ (isFieldText(field) && isFieldTextValue(newFieldValue)) ||
+ // Phone
+ (isFieldPhone(field) && isFieldPhoneValue(newFieldValue)) ||
+ // URL
+ (isFieldURL(field) && isFieldURLValue(newFieldValue)) ||
+ // Number
+ (isFieldNumber(field) && isFieldNumberValue(newFieldValue)) ||
+ // Date
+ (isFieldDate(field) && isFieldDateValue(newFieldValue)) ||
+ // Probability
+ (isFieldProbability(field) && isFieldProbabilityValue(newFieldValue)) ||
+ // Boolean
+ (isFieldBoolean(field) && isFieldBooleanValue(newFieldValue))
+ ) {
+ updateEntity({
+ variables: {
+ where: { id: currentEntityId },
+ data: { [field.metadata.fieldName]: newFieldValue },
+ },
+ });
+ return;
+ }
+
+ if (
+ // Double text
+ (isFieldDoubleText(field) && isFieldDoubleTextValue(newFieldValue)) ||
+ // Double Text Chip
+ (isFieldDoubleTextChip(field) &&
+ isFieldDoubleTextChipValue(newFieldValue))
+ ) {
+ updateEntity({
+ variables: {
+ where: { id: currentEntityId },
+ data: {
+ [field.metadata.firstValueFieldName]: newFieldValue.firstValue,
+ [field.metadata.secondValueFieldName]: newFieldValue.secondValue,
+ },
},
});
}
diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldRelationValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldRelationValue.ts
index 5624e0c25..c96be88e0 100644
--- a/front/src/modules/ui/editable-field/types/guards/isFieldRelationValue.ts
+++ b/front/src/modules/ui/editable-field/types/guards/isFieldRelationValue.ts
@@ -4,9 +4,5 @@ import { FieldRelationValue } from '../FieldMetadata';
export function isFieldRelationValue(
fieldValue: unknown,
): fieldValue is FieldRelationValue {
- return (
- fieldValue !== null &&
- fieldValue !== undefined &&
- typeof fieldValue === 'object'
- );
+ return fieldValue !== undefined && typeof fieldValue === 'object';
}
diff --git a/front/src/modules/ui/editable-field/types/guards/isViewFieldRelationValue.ts b/front/src/modules/ui/editable-field/types/guards/isViewFieldRelationValue.ts
index 62f80f816..122bbe7e9 100644
--- a/front/src/modules/ui/editable-field/types/guards/isViewFieldRelationValue.ts
+++ b/front/src/modules/ui/editable-field/types/guards/isViewFieldRelationValue.ts
@@ -4,9 +4,5 @@ import { ViewFieldRelationValue } from '../ViewField';
export function isViewFieldRelationValue(
fieldValue: unknown,
): fieldValue is ViewFieldRelationValue {
- return (
- fieldValue !== null &&
- fieldValue !== undefined &&
- typeof fieldValue === 'object'
- );
+ return fieldValue !== undefined && typeof fieldValue === 'object';
}
diff --git a/front/src/modules/ui/input/relation-picker/components/SingleEntitySelect.tsx b/front/src/modules/ui/input/relation-picker/components/SingleEntitySelect.tsx
index ece4d9a56..ebb44fe46 100644
--- a/front/src/modules/ui/input/relation-picker/components/SingleEntitySelect.tsx
+++ b/front/src/modules/ui/input/relation-picker/components/SingleEntitySelect.tsx
@@ -13,29 +13,36 @@ import { useEntitySelectSearch } from '../hooks/useEntitySelectSearch';
import { EntityForSelect } from '../types/EntityForSelect';
import {
- EntitiesForSingleEntitySelect,
SingleEntitySelectBase,
+ type SingleEntitySelectBaseProps,
} from './SingleEntitySelectBase';
+export type SingleEntitySelectProps<
+ CustomEntityForSelect extends EntityForSelect,
+> = {
+ disableBackgroundBlur?: boolean;
+ onCreate?: () => void;
+ width?: number;
+} & Pick<
+ SingleEntitySelectBaseProps,
+ | 'EmptyIcon'
+ | 'emptyLabel'
+ | 'entitiesToSelect'
+ | 'loading'
+ | 'onCancel'
+ | 'onEntitySelected'
+ | 'selectedEntity'
+>;
+
export function SingleEntitySelect<
CustomEntityForSelect extends EntityForSelect,
>({
- entities,
- onEntitySelected,
- onCreate,
- onCancel,
- width,
disableBackgroundBlur = false,
- noUser,
-}: {
- onCancel?: () => void;
- onCreate?: () => void;
- entities: EntitiesForSingleEntitySelect;
- onEntitySelected: (entity: CustomEntityForSelect | null | undefined) => void;
- disableBackgroundBlur?: boolean;
- width?: number;
- noUser?: CustomEntityForSelect;
-}) {
+ onCancel,
+ onCreate,
+ width,
+ ...props
+}: SingleEntitySelectProps) {
const containerRef = useRef(null);
const { searchFilter, handleSearchFilterChange } = useEntitySelectSearch();
@@ -64,12 +71,7 @@ export function SingleEntitySelect<
autoFocus
/>
-
+
{showCreateButton && (
<>
diff --git a/front/src/modules/ui/input/relation-picker/components/SingleEntitySelectBase.tsx b/front/src/modules/ui/input/relation-picker/components/SingleEntitySelectBase.tsx
index a91e10b06..d7098c697 100644
--- a/front/src/modules/ui/input/relation-picker/components/SingleEntitySelectBase.tsx
+++ b/front/src/modules/ui/input/relation-picker/components/SingleEntitySelectBase.tsx
@@ -2,49 +2,47 @@ import { useRef } from 'react';
import { Key } from 'ts-key-enum';
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
-import { IconBuildingSkyscraper, IconUserCircle } from '@/ui/icon';
+import type { IconComponent } from '@/ui/icon/types/IconComponent';
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { MenuItemSelectAvatar } from '@/ui/menu-item/components/MenuItemSelectAvatar';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { Avatar } from '@/users/components/Avatar';
-import { isDefined } from '~/utils/isDefined';
+import { assertNotNull } from '~/utils/assert';
import { isNonEmptyString } from '~/utils/isNonEmptyString';
import { useEntitySelectScroll } from '../hooks/useEntitySelectScroll';
import { EntityForSelect } from '../types/EntityForSelect';
-import { Entity } from '../types/EntityTypeForSelect';
import { RelationPickerHotkeyScope } from '../types/RelationPickerHotkeyScope';
import { DropdownMenuSkeletonItem } from './skeletons/DropdownMenuSkeletonItem';
-export type EntitiesForSingleEntitySelect<
+export type SingleEntitySelectBaseProps<
CustomEntityForSelect extends EntityForSelect,
> = {
- selectedEntity: CustomEntityForSelect;
+ EmptyIcon?: IconComponent;
+ emptyLabel?: string;
entitiesToSelect: CustomEntityForSelect[];
- loading: boolean;
+ loading?: boolean;
+ onCancel?: () => void;
+ onEntitySelected: (entity?: CustomEntityForSelect) => void;
+ selectedEntity?: CustomEntityForSelect;
};
export function SingleEntitySelectBase<
CustomEntityForSelect extends EntityForSelect,
>({
- entities,
- onEntitySelected,
+ EmptyIcon,
+ emptyLabel,
+ entitiesToSelect,
+ loading,
onCancel,
- noUser,
-}: {
- entities: EntitiesForSingleEntitySelect;
- onEntitySelected: (entity: CustomEntityForSelect | null | undefined) => void;
- onCancel?: () => void;
- noUser?: CustomEntityForSelect;
-}) {
+ onEntitySelected,
+ selectedEntity,
+}: SingleEntitySelectBaseProps) {
const containerRef = useRef(null);
- let entitiesInDropdown = isDefined(entities.selectedEntity)
- ? [entities.selectedEntity, ...(entities.entitiesToSelect ?? [])]
- : entities.entitiesToSelect ?? [];
-
- entitiesInDropdown = entitiesInDropdown.filter((entity) =>
- isNonEmptyString(entity.name),
+ const entitiesInDropdown = [selectedEntity, ...entitiesToSelect].filter(
+ (entity): entity is CustomEntityForSelect =>
+ assertNotNull(entity) && isNonEmptyString(entity.name.trim()),
);
const { hoveredIndex, resetScroll } = useEntitySelectScroll({
@@ -71,25 +69,16 @@ export function SingleEntitySelectBase<
[onCancel],
);
- entitiesInDropdown = entitiesInDropdown.filter((entity) =>
- isNonEmptyString(entity.name.trim()),
- );
-
- const NoUserIcon =
- noUser?.entityType === Entity.User
- ? IconUserCircle
- : IconBuildingSkyscraper;
-
return (
- {noUser && (
+ {emptyLabel && (