Implement select v1 (#3312)
* Implement select v1 * Implement select v1
This commit is contained in:
@ -28,6 +28,7 @@ export const useMapFieldMetadataToGraphQLQuery = () => {
|
||||
'EMAIL',
|
||||
'NUMBER',
|
||||
'BOOLEAN',
|
||||
'SELECT',
|
||||
] as FieldType[]
|
||||
).includes(fieldType);
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ export const formatFieldMetadataItemAsColumnDefinition = ({
|
||||
relationObjectMetadataNamePlural:
|
||||
relationObjectMetadataItem?.namePlural ?? '',
|
||||
objectMetadataNameSingular: objectMetadataItem.nameSingular ?? '',
|
||||
options: field.options,
|
||||
},
|
||||
iconName: field.icon ?? 'Icon123',
|
||||
isVisible: true,
|
||||
|
||||
@ -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 { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
|
||||
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 { IconBuildingSkyscraper } from '@/ui/display/icon';
|
||||
import { PageBody } from '@/ui/layout/page/PageBody';
|
||||
@ -51,12 +50,13 @@ export const RecordShowPage = () => {
|
||||
throw new Error(`Object name is not defined`);
|
||||
}
|
||||
|
||||
const { objectMetadataItem, labelIdentifierFieldMetadata } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { identifiersMapper } = useRelationPicker();
|
||||
const {
|
||||
objectMetadataItem,
|
||||
labelIdentifierFieldMetadata,
|
||||
mapToObjectRecordIdentifier,
|
||||
} = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { favorites, createFavorite, deleteFavorite } = useFavorites();
|
||||
|
||||
@ -107,11 +107,6 @@ export const RecordShowPage = () => {
|
||||
? record?.name.firstName + ' ' + record?.name.lastName
|
||||
: record?.name;
|
||||
|
||||
const recordIdentifiers = identifiersMapper?.(
|
||||
record,
|
||||
objectMetadataItem?.nameSingular ?? '',
|
||||
);
|
||||
|
||||
const onUploadPicture = async (file: File) => {
|
||||
if (objectNameSingular !== 'person') {
|
||||
return;
|
||||
@ -201,8 +196,12 @@ export const RecordShowPage = () => {
|
||||
<>
|
||||
<ShowPageSummaryCard
|
||||
id={record.id}
|
||||
logoOrAvatar={recordIdentifiers?.avatarUrl}
|
||||
avatarPlaceholder={recordIdentifiers?.name ?? ''}
|
||||
logoOrAvatar={
|
||||
mapToObjectRecordIdentifier(record).avatarUrl ?? ''
|
||||
}
|
||||
avatarPlaceholder={
|
||||
mapToObjectRecordIdentifier(record).name ?? ''
|
||||
}
|
||||
date={record.createdAt ?? ''}
|
||||
title={
|
||||
<FieldContext.Provider
|
||||
@ -232,7 +231,10 @@ export const RecordShowPage = () => {
|
||||
<RecordInlineCell />
|
||||
</FieldContext.Provider>
|
||||
}
|
||||
avatarType={recordIdentifiers?.avatarType ?? 'rounded'}
|
||||
avatarType={
|
||||
mapToObjectRecordIdentifier(record).avatarType ??
|
||||
'rounded'
|
||||
}
|
||||
onUploadPicture={
|
||||
objectNameSingular === 'person'
|
||||
? onUploadPicture
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
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 { isFieldSelect } from '@/object-record/field/types/guards/isFieldSelect';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
@ -120,6 +122,8 @@ export const FieldInput = ({
|
||||
<BooleanFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldRating(fieldDefinition) ? (
|
||||
<RatingFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldSelect(fieldDefinition) ? (
|
||||
<SelectFieldInput onSubmit={onSubmit} />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
@ -3,6 +3,8 @@ import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName';
|
||||
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 { entityFieldsFamilySelector } from '../states/selectors/entityFieldsFamilySelector';
|
||||
@ -77,6 +79,9 @@ export const usePersistField = () => {
|
||||
const fieldIsPhone =
|
||||
isFieldPhone(fieldDefinition) && isFieldPhoneValue(valueToPersist);
|
||||
|
||||
const fieldIsSelect =
|
||||
isFieldSelect(fieldDefinition) && isFieldSelectValue(valueToPersist);
|
||||
|
||||
if (fieldIsRelation) {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
@ -104,7 +109,8 @@ export const usePersistField = () => {
|
||||
fieldIsPhone ||
|
||||
fieldIsLink ||
|
||||
fieldIsCurrency ||
|
||||
fieldIsFullName
|
||||
fieldIsFullName ||
|
||||
fieldIsSelect
|
||||
) {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
set(
|
||||
|
||||
@ -3,7 +3,15 @@ import { Tag } from '@/ui/display/tag/components/Tag';
|
||||
import { useSelectField } from '../../hooks/useSelectField';
|
||||
|
||||
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} />
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
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 { FieldContext } from '../../contexts/FieldContext';
|
||||
@ -25,23 +25,18 @@ export const useSelectField = () => {
|
||||
fieldName: fieldName,
|
||||
}),
|
||||
);
|
||||
const fieldSelectValue = isFieldSelectValue(fieldValue)
|
||||
? fieldValue
|
||||
: { color: 'green' as ThemeColor, label: '' };
|
||||
|
||||
const fieldSelectValue = isFieldSelectValue(fieldValue) ? fieldValue : null;
|
||||
|
||||
const fieldInitialValue = useFieldInitialValue();
|
||||
|
||||
const initialValue = {
|
||||
color: 'green' as ThemeColor,
|
||||
label: fieldInitialValue?.isEmpty
|
||||
? ''
|
||||
: fieldInitialValue?.value ?? fieldSelectValue?.label ?? '',
|
||||
};
|
||||
const persistField = usePersistField();
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
persistField,
|
||||
fieldValue: fieldSelectValue,
|
||||
initialValue,
|
||||
initialValue: fieldInitialValue,
|
||||
setFieldValue,
|
||||
hotkeyScope,
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
@ -87,6 +87,7 @@ export type FieldRelationMetadata = {
|
||||
export type FieldSelectMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
options: { label: string; color: ThemeColor; value: string }[];
|
||||
};
|
||||
|
||||
export type FieldMetadata =
|
||||
@ -119,6 +120,6 @@ export type FieldCurrencyValue = {
|
||||
};
|
||||
export type FieldFullNameValue = { firstName: string; lastName: string };
|
||||
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;
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
import { z } from 'zod';
|
||||
import { isString } from '@sniptt/guards';
|
||||
|
||||
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
|
||||
|
||||
const selectValueSchema = z.object({
|
||||
color: themeColorSchema,
|
||||
label: z.string(),
|
||||
});
|
||||
import { FieldSelectValue } from '@/object-record/field/types/FieldMetadata';
|
||||
|
||||
export const isFieldSelectValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is z.infer<typeof selectValueSchema> =>
|
||||
selectValueSchema.safeParse(fieldValue).success;
|
||||
): fieldValue is FieldSelectValue => isString(fieldValue);
|
||||
|
||||
@ -14,6 +14,7 @@ import { isFieldRating } from '@/object-record/field/types/guards/isFieldRating'
|
||||
import { isFieldRelation } from '@/object-record/field/types/guards/isFieldRelation';
|
||||
import { isFieldRelationValue } from '@/object-record/field/types/guards/isFieldRelationValue';
|
||||
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 { isFieldUuid } from '@/object-record/field/types/guards/isFieldUuid';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
@ -34,8 +35,7 @@ export const isFieldValueEmpty = ({
|
||||
isFieldNumber(fieldDefinition) ||
|
||||
isFieldRating(fieldDefinition) ||
|
||||
isFieldEmail(fieldDefinition) ||
|
||||
isFieldBoolean(fieldDefinition) ||
|
||||
isFieldSelect(fieldDefinition)
|
||||
isFieldBoolean(fieldDefinition)
|
||||
//|| isFieldPhone(fieldDefinition)
|
||||
) {
|
||||
return isValueEmpty(fieldValue);
|
||||
@ -45,6 +45,10 @@ export const isFieldValueEmpty = ({
|
||||
return isFieldRelationValue(fieldValue) && isValueEmpty(fieldValue);
|
||||
}
|
||||
|
||||
if (isFieldSelect(fieldDefinition)) {
|
||||
return isFieldSelectValue(fieldValue) && !assertNotNull(fieldValue);
|
||||
}
|
||||
|
||||
if (isFieldCurrency(fieldDefinition)) {
|
||||
return (
|
||||
!isFieldCurrencyValue(fieldValue) ||
|
||||
|
||||
@ -41,7 +41,7 @@ class Support {
|
||||
|
||||
@ObjectType()
|
||||
class Sentry {
|
||||
@Field(() => String)
|
||||
@Field(() => String, { nullable: true })
|
||||
dsn: string | undefined;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user