Implement select v1 (#3312)

* Implement select v1

* Implement select v1
This commit is contained in:
Charles Bochet
2024-01-08 20:55:45 +01:00
committed by GitHub
parent ea2cb8938f
commit 67b14824a4
12 changed files with 96 additions and 42 deletions

View File

@ -28,6 +28,7 @@ export const useMapFieldMetadataToGraphQLQuery = () => {
'EMAIL', 'EMAIL',
'NUMBER', 'NUMBER',
'BOOLEAN', 'BOOLEAN',
'SELECT',
] as FieldType[] ] as FieldType[]
).includes(fieldType); ).includes(fieldType);

View File

@ -40,6 +40,7 @@ export const formatFieldMetadataItemAsColumnDefinition = ({
relationObjectMetadataNamePlural: relationObjectMetadataNamePlural:
relationObjectMetadataItem?.namePlural ?? '', relationObjectMetadataItem?.namePlural ?? '',
objectMetadataNameSingular: objectMetadataItem.nameSingular ?? '', objectMetadataNameSingular: objectMetadataItem.nameSingular ?? '',
options: field.options,
}, },
iconName: field.icon ?? 'Icon123', iconName: field.icon ?? 'Icon123',
isVisible: true, isVisible: true,

View File

@ -15,7 +15,6 @@ import { RecordInlineCell } from '@/object-record/record-inline-cell/components/
import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox'; import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox';
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope'; import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
import { RecordRelationFieldCardSection } from '@/object-record/record-relation-card/components/RecordRelationFieldCardSection'; import { RecordRelationFieldCardSection } from '@/object-record/record-relation-card/components/RecordRelationFieldCardSection';
import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker';
import { isFieldMetadataItemAvailable } from '@/object-record/utils/isFieldMetadataItemAvailable'; import { isFieldMetadataItemAvailable } from '@/object-record/utils/isFieldMetadataItemAvailable';
import { IconBuildingSkyscraper } from '@/ui/display/icon'; import { IconBuildingSkyscraper } from '@/ui/display/icon';
import { PageBody } from '@/ui/layout/page/PageBody'; import { PageBody } from '@/ui/layout/page/PageBody';
@ -51,12 +50,13 @@ export const RecordShowPage = () => {
throw new Error(`Object name is not defined`); throw new Error(`Object name is not defined`);
} }
const { objectMetadataItem, labelIdentifierFieldMetadata } = const {
useObjectMetadataItem({ objectMetadataItem,
objectNameSingular, labelIdentifierFieldMetadata,
}); mapToObjectRecordIdentifier,
} = useObjectMetadataItem({
const { identifiersMapper } = useRelationPicker(); objectNameSingular,
});
const { favorites, createFavorite, deleteFavorite } = useFavorites(); const { favorites, createFavorite, deleteFavorite } = useFavorites();
@ -107,11 +107,6 @@ export const RecordShowPage = () => {
? record?.name.firstName + ' ' + record?.name.lastName ? record?.name.firstName + ' ' + record?.name.lastName
: record?.name; : record?.name;
const recordIdentifiers = identifiersMapper?.(
record,
objectMetadataItem?.nameSingular ?? '',
);
const onUploadPicture = async (file: File) => { const onUploadPicture = async (file: File) => {
if (objectNameSingular !== 'person') { if (objectNameSingular !== 'person') {
return; return;
@ -201,8 +196,12 @@ export const RecordShowPage = () => {
<> <>
<ShowPageSummaryCard <ShowPageSummaryCard
id={record.id} id={record.id}
logoOrAvatar={recordIdentifiers?.avatarUrl} logoOrAvatar={
avatarPlaceholder={recordIdentifiers?.name ?? ''} mapToObjectRecordIdentifier(record).avatarUrl ?? ''
}
avatarPlaceholder={
mapToObjectRecordIdentifier(record).name ?? ''
}
date={record.createdAt ?? ''} date={record.createdAt ?? ''}
title={ title={
<FieldContext.Provider <FieldContext.Provider
@ -232,7 +231,10 @@ export const RecordShowPage = () => {
<RecordInlineCell /> <RecordInlineCell />
</FieldContext.Provider> </FieldContext.Provider>
} }
avatarType={recordIdentifiers?.avatarType ?? 'rounded'} avatarType={
mapToObjectRecordIdentifier(record).avatarType ??
'rounded'
}
onUploadPicture={ onUploadPicture={
objectNameSingular === 'person' objectNameSingular === 'person'
? onUploadPicture ? onUploadPicture

View File

@ -1,7 +1,9 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { FullNameFieldInput } from '@/object-record/field/meta-types/input/components/FullNameFieldInput'; import { FullNameFieldInput } from '@/object-record/field/meta-types/input/components/FullNameFieldInput';
import { SelectFieldInput } from '@/object-record/field/meta-types/input/components/SelectFieldInput';
import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName'; import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName';
import { isFieldSelect } from '@/object-record/field/types/guards/isFieldSelect';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { FieldContext } from '../contexts/FieldContext'; import { FieldContext } from '../contexts/FieldContext';
@ -120,6 +122,8 @@ export const FieldInput = ({
<BooleanFieldInput onSubmit={onSubmit} /> <BooleanFieldInput onSubmit={onSubmit} />
) : isFieldRating(fieldDefinition) ? ( ) : isFieldRating(fieldDefinition) ? (
<RatingFieldInput onSubmit={onSubmit} /> <RatingFieldInput onSubmit={onSubmit} />
) : isFieldSelect(fieldDefinition) ? (
<SelectFieldInput onSubmit={onSubmit} />
) : ( ) : (
<></> <></>
)} )}

View File

@ -3,6 +3,8 @@ import { useRecoilCallback } from 'recoil';
import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName'; import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName';
import { isFieldFullNameValue } from '@/object-record/field/types/guards/isFieldFullNameValue'; import { isFieldFullNameValue } from '@/object-record/field/types/guards/isFieldFullNameValue';
import { isFieldSelect } from '@/object-record/field/types/guards/isFieldSelect';
import { isFieldSelectValue } from '@/object-record/field/types/guards/isFieldSelectValue';
import { FieldContext } from '../contexts/FieldContext'; import { FieldContext } from '../contexts/FieldContext';
import { entityFieldsFamilySelector } from '../states/selectors/entityFieldsFamilySelector'; import { entityFieldsFamilySelector } from '../states/selectors/entityFieldsFamilySelector';
@ -77,6 +79,9 @@ export const usePersistField = () => {
const fieldIsPhone = const fieldIsPhone =
isFieldPhone(fieldDefinition) && isFieldPhoneValue(valueToPersist); isFieldPhone(fieldDefinition) && isFieldPhoneValue(valueToPersist);
const fieldIsSelect =
isFieldSelect(fieldDefinition) && isFieldSelectValue(valueToPersist);
if (fieldIsRelation) { if (fieldIsRelation) {
const fieldName = fieldDefinition.metadata.fieldName; const fieldName = fieldDefinition.metadata.fieldName;
@ -104,7 +109,8 @@ export const usePersistField = () => {
fieldIsPhone || fieldIsPhone ||
fieldIsLink || fieldIsLink ||
fieldIsCurrency || fieldIsCurrency ||
fieldIsFullName fieldIsFullName ||
fieldIsSelect
) { ) {
const fieldName = fieldDefinition.metadata.fieldName; const fieldName = fieldDefinition.metadata.fieldName;
set( set(

View File

@ -3,7 +3,15 @@ import { Tag } from '@/ui/display/tag/components/Tag';
import { useSelectField } from '../../hooks/useSelectField'; import { useSelectField } from '../../hooks/useSelectField';
export const SelectFieldDisplay = () => { export const SelectFieldDisplay = () => {
const { fieldValue } = useSelectField(); const { fieldValue, fieldDefinition } = useSelectField();
return <Tag color={fieldValue.color} text={fieldValue.label} />; const selectedOption = fieldDefinition.metadata.options.find(
(option) => option.value === fieldValue,
);
return selectedOption ? (
<Tag color={selectedOption.color} text={selectedOption.label} />
) : (
<></>
);
}; };

View File

@ -1,7 +1,7 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { ThemeColor } from '@/ui/theme/constants/colors'; import { usePersistField } from '@/object-record/field/hooks/usePersistField';
import { FieldMetadataType } from '~/generated/graphql'; import { FieldMetadataType } from '~/generated/graphql';
import { FieldContext } from '../../contexts/FieldContext'; import { FieldContext } from '../../contexts/FieldContext';
@ -25,23 +25,18 @@ export const useSelectField = () => {
fieldName: fieldName, fieldName: fieldName,
}), }),
); );
const fieldSelectValue = isFieldSelectValue(fieldValue)
? fieldValue const fieldSelectValue = isFieldSelectValue(fieldValue) ? fieldValue : null;
: { color: 'green' as ThemeColor, label: '' };
const fieldInitialValue = useFieldInitialValue(); const fieldInitialValue = useFieldInitialValue();
const initialValue = { const persistField = usePersistField();
color: 'green' as ThemeColor,
label: fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value ?? fieldSelectValue?.label ?? '',
};
return { return {
fieldDefinition, fieldDefinition,
persistField,
fieldValue: fieldSelectValue, fieldValue: fieldSelectValue,
initialValue, initialValue: fieldInitialValue,
setFieldValue, setFieldValue,
hotkeyScope, hotkeyScope,
}; };

View File

@ -0,0 +1,38 @@
import styled from '@emotion/styled';
import { MenuItem } from 'tsup.ui.index';
import { useSelectField } from '@/object-record/field/meta-types/hooks/useSelectField';
import { FieldInputEvent } from '@/object-record/field/types/FieldInputEvent';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
const StyledRelationPickerContainer = styled.div`
left: -1px;
position: absolute;
top: -1px;
`;
export type SelectFieldInputProps = {
onSubmit?: FieldInputEvent;
};
export const SelectFieldInput = ({ onSubmit }: SelectFieldInputProps) => {
const { persistField, fieldDefinition } = useSelectField();
return (
<StyledRelationPickerContainer>
<DropdownMenu data-select-disable>
<DropdownMenuItemsContainer>
{fieldDefinition.metadata.options.map((option) => {
return (
<MenuItem
text={option.label}
onClick={() => onSubmit?.(() => persistField(option.value))}
/>
);
})}
</DropdownMenuItemsContainer>
</DropdownMenu>
</StyledRelationPickerContainer>
);
};

View File

@ -87,6 +87,7 @@ export type FieldRelationMetadata = {
export type FieldSelectMetadata = { export type FieldSelectMetadata = {
objectMetadataNameSingular?: string; objectMetadataNameSingular?: string;
fieldName: string; fieldName: string;
options: { label: string; color: ThemeColor; value: string }[];
}; };
export type FieldMetadata = export type FieldMetadata =
@ -119,6 +120,6 @@ export type FieldCurrencyValue = {
}; };
export type FieldFullNameValue = { firstName: string; lastName: string }; export type FieldFullNameValue = { firstName: string; lastName: string };
export type FieldRatingValue = '1' | '2' | '3' | '4' | '5'; export type FieldRatingValue = '1' | '2' | '3' | '4' | '5';
export type FieldSelectValue = { color: ThemeColor; label: string }; export type FieldSelectValue = string | null;
export type FieldRelationValue = EntityForSelect | null; export type FieldRelationValue = EntityForSelect | null;

View File

@ -1,13 +1,7 @@
import { z } from 'zod'; import { isString } from '@sniptt/guards';
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema'; import { FieldSelectValue } from '@/object-record/field/types/FieldMetadata';
const selectValueSchema = z.object({
color: themeColorSchema,
label: z.string(),
});
export const isFieldSelectValue = ( export const isFieldSelectValue = (
fieldValue: unknown, fieldValue: unknown,
): fieldValue is z.infer<typeof selectValueSchema> => ): fieldValue is FieldSelectValue => isString(fieldValue);
selectValueSchema.safeParse(fieldValue).success;

View File

@ -14,6 +14,7 @@ import { isFieldRating } from '@/object-record/field/types/guards/isFieldRating'
import { isFieldRelation } from '@/object-record/field/types/guards/isFieldRelation'; import { isFieldRelation } from '@/object-record/field/types/guards/isFieldRelation';
import { isFieldRelationValue } from '@/object-record/field/types/guards/isFieldRelationValue'; import { isFieldRelationValue } from '@/object-record/field/types/guards/isFieldRelationValue';
import { isFieldSelect } from '@/object-record/field/types/guards/isFieldSelect'; import { isFieldSelect } from '@/object-record/field/types/guards/isFieldSelect';
import { isFieldSelectValue } from '@/object-record/field/types/guards/isFieldSelectValue';
import { isFieldText } from '@/object-record/field/types/guards/isFieldText'; import { isFieldText } from '@/object-record/field/types/guards/isFieldText';
import { isFieldUuid } from '@/object-record/field/types/guards/isFieldUuid'; import { isFieldUuid } from '@/object-record/field/types/guards/isFieldUuid';
import { assertNotNull } from '~/utils/assert'; import { assertNotNull } from '~/utils/assert';
@ -34,8 +35,7 @@ export const isFieldValueEmpty = ({
isFieldNumber(fieldDefinition) || isFieldNumber(fieldDefinition) ||
isFieldRating(fieldDefinition) || isFieldRating(fieldDefinition) ||
isFieldEmail(fieldDefinition) || isFieldEmail(fieldDefinition) ||
isFieldBoolean(fieldDefinition) || isFieldBoolean(fieldDefinition)
isFieldSelect(fieldDefinition)
//|| isFieldPhone(fieldDefinition) //|| isFieldPhone(fieldDefinition)
) { ) {
return isValueEmpty(fieldValue); return isValueEmpty(fieldValue);
@ -45,6 +45,10 @@ export const isFieldValueEmpty = ({
return isFieldRelationValue(fieldValue) && isValueEmpty(fieldValue); return isFieldRelationValue(fieldValue) && isValueEmpty(fieldValue);
} }
if (isFieldSelect(fieldDefinition)) {
return isFieldSelectValue(fieldValue) && !assertNotNull(fieldValue);
}
if (isFieldCurrency(fieldDefinition)) { if (isFieldCurrency(fieldDefinition)) {
return ( return (
!isFieldCurrencyValue(fieldValue) || !isFieldCurrencyValue(fieldValue) ||

View File

@ -41,7 +41,7 @@ class Support {
@ObjectType() @ObjectType()
class Sentry { class Sentry {
@Field(() => String) @Field(() => String, { nullable: true })
dsn: string | undefined; dsn: string | undefined;
} }