Support custom object renaming (#7504)
This PR was created by [GitStart](https://gitstart.com/) to address the requirements from this ticket: [TWNTY-5491](https://clients.gitstart.com/twenty/5449/tickets/TWNTY-5491). This ticket was imported from: [TWNTY-5491](https://github.com/twentyhq/twenty/issues/5491) --- ### Description **How To Test:**\ 1. Reset db using `npx nx database:reset twenty-server` on this PR 1. Run both backend and frontend 2. Navigate to `settings/data-model/objects/ `page 3. Select a `Custom `object from the list or create a new `Custom `object 4. Navigate to custom object details page and click on edit button 5. Finally edit the object details. **Issues and bugs** The Typecheck is failing but we could not see this error locally There is a bug after updating the label of a custom object. View title is not updated till refreshing the page. We could not find a consistent way to update this, should we reload the page after editing an object? ### Demo <https://www.loom.com/share/64ecb57efad7498d99085cb11480b5dd?sid=28d0868c-e54f-454d-8432-3f789be9e2b7> ### Refs #5491 --------- Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com> Co-authored-by: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com> Co-authored-by: Marie Stoppa <marie.stoppa@essec.edu> Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
co-authored by
gitstart-twenty
gitstart-twenty
Marie Stoppa
Charles Bochet
Weiko
parent
c6ef14acc4
commit
414f2ac498
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1160,6 +1160,7 @@ export type UpdateObjectPayload = {
|
||||
labelSingular?: InputMaybe<Scalars['String']>;
|
||||
namePlural?: InputMaybe<Scalars['String']>;
|
||||
nameSingular?: InputMaybe<Scalars['String']>;
|
||||
shouldSyncLabelAndName?: InputMaybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
export type UpdateOneObjectInput = {
|
||||
@@ -1476,6 +1477,7 @@ export type Object = {
|
||||
labelSingular: Scalars['String'];
|
||||
namePlural: Scalars['String'];
|
||||
nameSingular: Scalars['String'];
|
||||
shouldSyncLabelAndName: Scalars['Boolean'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
|
||||
+4
-10
@@ -35,9 +35,7 @@ export const EventRowDynamicComponent = ({
|
||||
linkedObjectMetadataItem,
|
||||
authorFullName,
|
||||
}: EventRowDynamicComponentProps) => {
|
||||
const [eventName] = event.name.split('.');
|
||||
|
||||
switch (eventName) {
|
||||
switch (linkedObjectMetadataItem?.nameSingular) {
|
||||
case 'calendarEvent':
|
||||
return (
|
||||
<EventRowCalendarEvent
|
||||
@@ -58,7 +56,7 @@ export const EventRowDynamicComponent = ({
|
||||
authorFullName={authorFullName}
|
||||
/>
|
||||
);
|
||||
case 'linked-task':
|
||||
case 'task':
|
||||
return (
|
||||
<EventRowActivity
|
||||
labelIdentifierValue={labelIdentifierValue}
|
||||
@@ -69,7 +67,7 @@ export const EventRowDynamicComponent = ({
|
||||
objectNameSingular={CoreObjectNameSingular.Task}
|
||||
/>
|
||||
);
|
||||
case 'linked-note':
|
||||
case 'note':
|
||||
return (
|
||||
<EventRowActivity
|
||||
labelIdentifierValue={labelIdentifierValue}
|
||||
@@ -80,7 +78,7 @@ export const EventRowDynamicComponent = ({
|
||||
objectNameSingular={CoreObjectNameSingular.Note}
|
||||
/>
|
||||
);
|
||||
case mainObjectMetadataItem?.nameSingular:
|
||||
default:
|
||||
return (
|
||||
<EventRowMainObject
|
||||
labelIdentifierValue={labelIdentifierValue}
|
||||
@@ -90,9 +88,5 @@ export const EventRowDynamicComponent = ({
|
||||
authorFullName={authorFullName}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
throw new Error(
|
||||
`Cannot find event component for event name ${eventName}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,6 +124,7 @@ describe('useCommandMenu', () => {
|
||||
namePlural: 'tasks',
|
||||
labelSingular: 'Task',
|
||||
labelPlural: 'Tasks',
|
||||
shouldSyncLabelAndName: true,
|
||||
description: 'A task',
|
||||
icon: 'IconCheckbox',
|
||||
isCustom: false,
|
||||
|
||||
@@ -24,6 +24,7 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql`
|
||||
updatedAt
|
||||
labelIdentifierFieldMetadataId
|
||||
imageIdentifierFieldMetadataId
|
||||
shouldSyncLabelAndName
|
||||
indexMetadatas(paging: { first: 100 }) {
|
||||
edges {
|
||||
node {
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ describe('objectMetadataItemSchema', () => {
|
||||
namePlural: 'notCamelCase',
|
||||
nameSingular: 'notCamelCase',
|
||||
updatedAt: 'invalid date',
|
||||
shouldSyncLabelAndName: 'not a boolean',
|
||||
};
|
||||
|
||||
// When
|
||||
|
||||
+1
@@ -26,4 +26,5 @@ export const objectMetadataItemSchema = z.object({
|
||||
namePlural: camelCaseStringSchema,
|
||||
nameSingular: camelCaseStringSchema,
|
||||
updatedAt: z.string().datetime(),
|
||||
shouldSyncLabelAndName: z.boolean(),
|
||||
}) satisfies z.ZodType<ObjectMetadataItem>;
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ const objectMetadataItem: ObjectMetadataItem = {
|
||||
isRemote: false,
|
||||
labelPlural: 'object1s',
|
||||
labelSingular: 'object1',
|
||||
shouldSyncLabelAndName: true,
|
||||
};
|
||||
|
||||
describe('turnSortsIntoOrderBy', () => {
|
||||
|
||||
+1
@@ -26,6 +26,7 @@ describe('useLimitPerMetadataItem', () => {
|
||||
namePlural: 'namePlural',
|
||||
nameSingular: 'nameSingular',
|
||||
updatedAt: 'updatedAt',
|
||||
shouldSyncLabelAndName: false,
|
||||
fields: [],
|
||||
indexMetadatas: [],
|
||||
},
|
||||
|
||||
+1
@@ -34,6 +34,7 @@ const objectData: ObjectMetadataItem[] = [
|
||||
labelSingular: 'labelSingular',
|
||||
namePlural: 'namePlural',
|
||||
nameSingular: 'nameSingular',
|
||||
shouldSyncLabelAndName: false,
|
||||
updatedAt: 'updatedAt',
|
||||
fields: [
|
||||
{
|
||||
|
||||
+285
-62
@@ -1,21 +1,45 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { objectMetadataItemSchema } from '@/object-metadata/validation-schemas/objectMetadataItemSchema';
|
||||
import { OBJECT_NAME_MAXIMUM_LENGTH } from '@/settings/data-model/constants/ObjectNameMaximumLength';
|
||||
import { SyncObjectLabelAndNameToggle } from '@/settings/data-model/objects/forms/components/SyncObjectLabelAndNameToggle';
|
||||
import { useExpandedHeightAnimation } from '@/settings/hooks/useExpandedHeightAnimation';
|
||||
import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||
import { TextArea } from '@/ui/input/components/TextArea';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { plural } from 'pluralize';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import {
|
||||
AppTooltip,
|
||||
IconInfoCircle,
|
||||
IconTool,
|
||||
MAIN_COLORS,
|
||||
TooltipDelay,
|
||||
} from 'twenty-ui';
|
||||
import { z } from 'zod';
|
||||
import { computeMetadataNameFromLabelOrThrow } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const settingsDataModelObjectAboutFormSchema =
|
||||
objectMetadataItemSchema.pick({
|
||||
export const settingsDataModelObjectAboutFormSchema = objectMetadataItemSchema
|
||||
.pick({
|
||||
description: true,
|
||||
icon: true,
|
||||
labelPlural: true,
|
||||
labelSingular: true,
|
||||
});
|
||||
})
|
||||
.merge(
|
||||
objectMetadataItemSchema
|
||||
.pick({
|
||||
nameSingular: true,
|
||||
namePlural: true,
|
||||
shouldSyncLabelAndName: true,
|
||||
})
|
||||
.partial(),
|
||||
);
|
||||
|
||||
type SettingsDataModelObjectAboutFormValues = z.infer<
|
||||
typeof settingsDataModelObjectAboutFormSchema
|
||||
@@ -34,6 +58,41 @@ const StyledInputsContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledInputContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const StyledSectionWrapper = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledAdvancedSettingsSectionInputWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledAdvancedSettingsContainer = styled.div`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const StyledIconToolContainer = styled.div`
|
||||
border-right: 1px solid ${MAIN_COLORS.yellow};
|
||||
display: flex;
|
||||
left: ${({ theme }) => theme.spacing(-5)};
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const StyledIconTool = styled(IconTool)`
|
||||
margin-right: ${({ theme }) => theme.spacing(0.5)};
|
||||
`;
|
||||
|
||||
const StyledLabel = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
font-size: ${({ theme }) => theme.font.size.xs};
|
||||
@@ -41,83 +100,247 @@ const StyledLabel = styled.span`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledInputContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
const infoCircleElementId = 'info-circle-id';
|
||||
|
||||
export const SettingsDataModelObjectAboutForm = ({
|
||||
disabled,
|
||||
disableNameEdit,
|
||||
objectMetadataItem,
|
||||
}: SettingsDataModelObjectAboutFormProps) => {
|
||||
const { control } = useFormContext<SettingsDataModelObjectAboutFormValues>();
|
||||
const { control, watch, setValue } =
|
||||
useFormContext<SettingsDataModelObjectAboutFormValues>();
|
||||
const theme = useTheme();
|
||||
const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState);
|
||||
const { contentRef, motionAnimationVariants } = useExpandedHeightAnimation(
|
||||
isAdvancedModeEnabled,
|
||||
);
|
||||
|
||||
const shouldSyncLabelAndName = watch('shouldSyncLabelAndName');
|
||||
const labelSingular = watch('labelSingular');
|
||||
const labelPlural = watch('labelPlural');
|
||||
const apiNameTooltipText = shouldSyncLabelAndName
|
||||
? 'Deactivate "Synchronize Objects Labels and API Names" to set a custom API name'
|
||||
: 'Input must be in camel case and cannot start with a number';
|
||||
|
||||
const fillLabelPlural = (labelSingular: string) => {
|
||||
const newLabelPluralValue = isDefined(labelSingular)
|
||||
? plural(labelSingular)
|
||||
: '';
|
||||
setValue('labelPlural', newLabelPluralValue, {
|
||||
shouldDirty: isDefined(labelSingular) ? true : false,
|
||||
});
|
||||
if (shouldSyncLabelAndName === true) {
|
||||
fillNamePluralFromLabelPlural(newLabelPluralValue);
|
||||
}
|
||||
};
|
||||
|
||||
const fillNameSingularFromLabelSingular = (labelSingular: string) => {
|
||||
isDefined(labelSingular) &&
|
||||
setValue(
|
||||
'nameSingular',
|
||||
computeMetadataNameFromLabelOrThrow(labelSingular),
|
||||
{ shouldDirty: false },
|
||||
);
|
||||
};
|
||||
|
||||
const fillNamePluralFromLabelPlural = (labelPlural: string) => {
|
||||
isDefined(labelPlural) &&
|
||||
setValue('namePlural', computeMetadataNameFromLabelOrThrow(labelPlural), {
|
||||
shouldDirty: false,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledInputsContainer>
|
||||
<StyledInputContainer>
|
||||
<StyledLabel>Icon</StyledLabel>
|
||||
<StyledSectionWrapper>
|
||||
<StyledInputsContainer>
|
||||
<StyledInputContainer>
|
||||
<StyledLabel>Icon</StyledLabel>
|
||||
<Controller
|
||||
name="icon"
|
||||
control={control}
|
||||
defaultValue={objectMetadataItem?.icon ?? 'IconListNumbers'}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<IconPicker
|
||||
disabled={disabled}
|
||||
selectedIconKey={value}
|
||||
onChange={({ iconKey }) => onChange(iconKey)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</StyledInputContainer>
|
||||
<Controller
|
||||
name="icon"
|
||||
key={`object-labelSingular-text-input`}
|
||||
name={'labelSingular'}
|
||||
control={control}
|
||||
defaultValue={objectMetadataItem?.icon ?? 'IconListNumbers'}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<IconPicker
|
||||
disabled={disabled}
|
||||
selectedIconKey={value}
|
||||
onChange={({ iconKey }) => onChange(iconKey)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</StyledInputContainer>
|
||||
{[
|
||||
{
|
||||
label: 'Singular',
|
||||
fieldName: 'labelSingular' as const,
|
||||
placeholder: 'Listing',
|
||||
defaultValue: objectMetadataItem?.labelSingular,
|
||||
},
|
||||
{
|
||||
label: 'Plural',
|
||||
fieldName: 'labelPlural' as const,
|
||||
placeholder: 'Listings',
|
||||
defaultValue: objectMetadataItem?.labelPlural,
|
||||
},
|
||||
].map(({ defaultValue, fieldName, label, placeholder }) => (
|
||||
<Controller
|
||||
key={`object-${fieldName}-text-input`}
|
||||
name={fieldName}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
defaultValue={objectMetadataItem?.labelSingular}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextInput
|
||||
label={label}
|
||||
placeholder={placeholder}
|
||||
label={'Singular'}
|
||||
placeholder={'Listing'}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onChange={(value) => {
|
||||
onChange(value);
|
||||
fillLabelPlural(value);
|
||||
if (shouldSyncLabelAndName === true) {
|
||||
fillNameSingularFromLabelSingular(value);
|
||||
}
|
||||
}}
|
||||
disabled={disabled || disableNameEdit}
|
||||
fullWidth
|
||||
maxLength={OBJECT_NAME_MAXIMUM_LENGTH}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</StyledInputsContainer>
|
||||
<Controller
|
||||
name="description"
|
||||
control={control}
|
||||
defaultValue={objectMetadataItem?.description ?? null}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextArea
|
||||
placeholder="Write a description"
|
||||
minRows={4}
|
||||
value={value ?? undefined}
|
||||
onChange={(nextValue) => onChange(nextValue ?? null)}
|
||||
disabled={disabled}
|
||||
<Controller
|
||||
key={`object-labelPlural-text-input`}
|
||||
name={'labelPlural'}
|
||||
control={control}
|
||||
defaultValue={objectMetadataItem?.labelPlural}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextInput
|
||||
label={'Plural'}
|
||||
placeholder={'Listings'}
|
||||
value={value}
|
||||
onChange={(value) => {
|
||||
onChange(value);
|
||||
if (shouldSyncLabelAndName === true) {
|
||||
fillNamePluralFromLabelPlural(value);
|
||||
}
|
||||
}}
|
||||
disabled={disabled || disableNameEdit}
|
||||
fullWidth
|
||||
maxLength={OBJECT_NAME_MAXIMUM_LENGTH}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</StyledInputsContainer>
|
||||
<Controller
|
||||
name="description"
|
||||
control={control}
|
||||
defaultValue={objectMetadataItem?.description ?? null}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextArea
|
||||
placeholder="Write a description"
|
||||
minRows={4}
|
||||
value={value ?? undefined}
|
||||
onChange={(nextValue) => onChange(nextValue ?? null)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</StyledSectionWrapper>
|
||||
<AnimatePresence>
|
||||
{isAdvancedModeEnabled && (
|
||||
<motion.div
|
||||
ref={contentRef}
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
exit="exit"
|
||||
variants={motionAnimationVariants}
|
||||
>
|
||||
<StyledAdvancedSettingsContainer>
|
||||
<StyledIconToolContainer>
|
||||
<StyledIconTool size={12} color={MAIN_COLORS.yellow} />
|
||||
</StyledIconToolContainer>
|
||||
<StyledAdvancedSettingsSectionInputWrapper>
|
||||
{[
|
||||
{
|
||||
label: 'API Name (Singular)',
|
||||
fieldName: 'nameSingular' as const,
|
||||
placeholder: 'listing',
|
||||
defaultValue: objectMetadataItem?.nameSingular,
|
||||
disabled:
|
||||
disabled || disableNameEdit || shouldSyncLabelAndName,
|
||||
tooltip: apiNameTooltipText,
|
||||
},
|
||||
{
|
||||
label: 'API Name (Plural)',
|
||||
fieldName: 'namePlural' as const,
|
||||
placeholder: 'listings',
|
||||
defaultValue: objectMetadataItem?.namePlural,
|
||||
disabled:
|
||||
disabled || disableNameEdit || shouldSyncLabelAndName,
|
||||
tooltip: apiNameTooltipText,
|
||||
},
|
||||
].map(
|
||||
({
|
||||
defaultValue,
|
||||
fieldName,
|
||||
label,
|
||||
placeholder,
|
||||
disabled,
|
||||
tooltip,
|
||||
}) => (
|
||||
<StyledInputContainer
|
||||
key={`object-${fieldName}-text-input`}
|
||||
>
|
||||
<Controller
|
||||
name={fieldName}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
label={label}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
fullWidth
|
||||
maxLength={OBJECT_NAME_MAXIMUM_LENGTH}
|
||||
RightIcon={() =>
|
||||
tooltip && (
|
||||
<>
|
||||
<IconInfoCircle
|
||||
id={infoCircleElementId + fieldName}
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
|
||||
<AppTooltip
|
||||
anchorSelect={`#${infoCircleElementId}${fieldName}`}
|
||||
content={tooltip}
|
||||
offset={5}
|
||||
noArrow
|
||||
place="bottom"
|
||||
positionStrategy="absolute"
|
||||
delay={TooltipDelay.shortDelay}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</StyledInputContainer>
|
||||
),
|
||||
)}
|
||||
<Controller
|
||||
name="shouldSyncLabelAndName"
|
||||
control={control}
|
||||
defaultValue={
|
||||
objectMetadataItem?.shouldSyncLabelAndName ?? true
|
||||
}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<SyncObjectLabelAndNameToggle
|
||||
value={value ?? true}
|
||||
onChange={(value) => {
|
||||
onChange(value);
|
||||
if (value === true) {
|
||||
fillNamePluralFromLabelPlural(labelPlural);
|
||||
fillNameSingularFromLabelSingular(labelSingular);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</StyledAdvancedSettingsSectionInputWrapper>
|
||||
</StyledAdvancedSettingsContainer>
|
||||
</motion.div>
|
||||
)}
|
||||
/>
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { IconRefresh, MAIN_COLORS, Toggle } from 'twenty-ui';
|
||||
|
||||
const StyledToggleContainer = styled.div`
|
||||
align-items: center;
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledIconRefreshContainer = styled.div`
|
||||
border: 2px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: 3px;
|
||||
margin-right: ${({ theme }) => theme.spacing(3)};
|
||||
width: ${({ theme }) => theme.spacing(8)};
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const StyledTitleContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.h2`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.h3`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
margin: 0;
|
||||
margin-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
type SyncObjectLabelAndNameToggleProps = {
|
||||
value: boolean;
|
||||
onChange: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export const SyncObjectLabelAndNameToggle = ({
|
||||
value,
|
||||
onChange,
|
||||
}: SyncObjectLabelAndNameToggleProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<StyledToggleContainer>
|
||||
<StyledTitleContainer>
|
||||
<StyledIconRefreshContainer>
|
||||
<IconRefresh size={22.5} color={theme.font.color.tertiary} />
|
||||
</StyledIconRefreshContainer>
|
||||
<div>
|
||||
<StyledTitle>Synchronize Objects Labels and API Names</StyledTitle>
|
||||
<StyledDescription>
|
||||
Should changing an object's label also change the API?
|
||||
</StyledDescription>
|
||||
</div>
|
||||
</StyledTitleContainer>
|
||||
<Toggle onChange={onChange} color={MAIN_COLORS.yellow} value={value} />
|
||||
</StyledToggleContainer>
|
||||
);
|
||||
};
|
||||
+6
-2
@@ -12,6 +12,9 @@ describe('settingsCreateObjectInputSchema', () => {
|
||||
icon: 'IconPlus',
|
||||
labelPlural: ' Labels ',
|
||||
labelSingular: 'Label ',
|
||||
namePlural: 'namePlural',
|
||||
nameSingular: 'nameSingular',
|
||||
shouldSyncLabelAndName: false,
|
||||
};
|
||||
|
||||
// When
|
||||
@@ -24,8 +27,9 @@ describe('settingsCreateObjectInputSchema', () => {
|
||||
icon: validInput.icon,
|
||||
labelPlural: 'Labels',
|
||||
labelSingular: 'Label',
|
||||
namePlural: 'labels',
|
||||
nameSingular: 'label',
|
||||
namePlural: 'namePlural',
|
||||
nameSingular: 'nameSingular',
|
||||
shouldSyncLabelAndName: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+4
-2
@@ -12,6 +12,8 @@ describe('settingsUpdateObjectInputSchema', () => {
|
||||
icon: 'IconName',
|
||||
labelPlural: 'Labels Plural ',
|
||||
labelSingular: ' Label Singular',
|
||||
namePlural: 'namePlural',
|
||||
nameSingular: 'nameSingular',
|
||||
labelIdentifierFieldMetadataId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
|
||||
};
|
||||
|
||||
@@ -26,8 +28,8 @@ describe('settingsUpdateObjectInputSchema', () => {
|
||||
labelIdentifierFieldMetadataId: validInput.labelIdentifierFieldMetadataId,
|
||||
labelPlural: 'Labels Plural',
|
||||
labelSingular: 'Label Singular',
|
||||
namePlural: 'labelsPlural',
|
||||
nameSingular: 'labelSingular',
|
||||
namePlural: 'namePlural',
|
||||
nameSingular: 'nameSingular',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+14
-13
@@ -1,16 +1,17 @@
|
||||
import { objectMetadataItemSchema } from '@/object-metadata/validation-schemas/objectMetadataItemSchema';
|
||||
import { settingsDataModelObjectAboutFormSchema } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectAboutForm';
|
||||
import { CreateObjectInput } from '~/generated-metadata/graphql';
|
||||
import { computeMetadataNameFromLabelOrThrow } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils';
|
||||
|
||||
export const settingsCreateObjectInputSchema = objectMetadataItemSchema
|
||||
.pick({
|
||||
description: true,
|
||||
icon: true,
|
||||
labelPlural: true,
|
||||
labelSingular: true,
|
||||
})
|
||||
.transform<CreateObjectInput>((value) => ({
|
||||
...value,
|
||||
nameSingular: computeMetadataNameFromLabelOrThrow(value.labelSingular),
|
||||
namePlural: computeMetadataNameFromLabelOrThrow(value.labelPlural),
|
||||
}));
|
||||
export const settingsCreateObjectInputSchema =
|
||||
settingsDataModelObjectAboutFormSchema.transform<CreateObjectInput>(
|
||||
(values) => ({
|
||||
...values,
|
||||
nameSingular:
|
||||
values.nameSingular ??
|
||||
computeMetadataNameFromLabelOrThrow(values.labelSingular),
|
||||
namePlural:
|
||||
values.namePlural ??
|
||||
computeMetadataNameFromLabelOrThrow(values.labelPlural),
|
||||
shouldSyncLabelAndName: values.shouldSyncLabelAndName ?? true,
|
||||
}),
|
||||
);
|
||||
|
||||
+11
-22
@@ -1,24 +1,13 @@
|
||||
import { objectMetadataItemSchema } from '@/object-metadata/validation-schemas/objectMetadataItemSchema';
|
||||
import { UpdateObjectPayload } from '~/generated-metadata/graphql';
|
||||
import { computeMetadataNameFromLabelOrThrow } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils';
|
||||
import { settingsDataModelObjectAboutFormSchema } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectAboutForm';
|
||||
|
||||
export const settingsUpdateObjectInputSchema = objectMetadataItemSchema
|
||||
.pick({
|
||||
description: true,
|
||||
icon: true,
|
||||
imageIdentifierFieldMetadataId: true,
|
||||
isActive: true,
|
||||
labelIdentifierFieldMetadataId: true,
|
||||
labelPlural: true,
|
||||
labelSingular: true,
|
||||
})
|
||||
.partial()
|
||||
.transform<UpdateObjectPayload>((value) => ({
|
||||
...value,
|
||||
nameSingular: value.labelSingular
|
||||
? computeMetadataNameFromLabelOrThrow(value.labelSingular)
|
||||
: undefined,
|
||||
namePlural: value.labelPlural
|
||||
? computeMetadataNameFromLabelOrThrow(value.labelPlural)
|
||||
: undefined,
|
||||
}));
|
||||
export const settingsUpdateObjectInputSchema =
|
||||
settingsDataModelObjectAboutFormSchema
|
||||
.merge(
|
||||
objectMetadataItemSchema.pick({
|
||||
imageIdentifierFieldMetadataId: true,
|
||||
isActive: true,
|
||||
labelIdentifierFieldMetadataId: true,
|
||||
}),
|
||||
)
|
||||
.partial();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
|
||||
import { useLastVisitedObjectMetadataItem } from '@/navigation/hooks/useLastVisitedObjectMetadataItem';
|
||||
import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
||||
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
||||
@@ -20,13 +21,16 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import pick from 'lodash.pick';
|
||||
import { useEffect } from 'react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { Button, H2Title, IconArchive } from 'twenty-ui';
|
||||
import { z } from 'zod';
|
||||
import { computeMetadataNameFromLabelOrThrow } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils';
|
||||
|
||||
const objectEditFormSchema = z
|
||||
.object({})
|
||||
@@ -45,6 +49,9 @@ export const SettingsObjectEdit = () => {
|
||||
const { findActiveObjectMetadataItemBySlug } =
|
||||
useFilteredObjectMetadataItems();
|
||||
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
||||
const { lastVisitedObjectMetadataItemId } =
|
||||
useLastVisitedObjectMetadataItem();
|
||||
const { getLastVisitedViewIdFromObjectMetadataItemId } = useLastVisitedView();
|
||||
|
||||
const activeObjectMetadataItem =
|
||||
findActiveObjectMetadataItemBySlug(objectSlug);
|
||||
@@ -56,6 +63,10 @@ export const SettingsObjectEdit = () => {
|
||||
resolver: zodResolver(objectEditFormSchema),
|
||||
});
|
||||
|
||||
const setNavigationMemorizedUrl = useSetRecoilState(
|
||||
navigationMemorizedUrlState,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeObjectMetadataItem) navigate(AppPath.NotFound);
|
||||
}, [activeObjectMetadataItem, navigate]);
|
||||
@@ -65,25 +76,72 @@ export const SettingsObjectEdit = () => {
|
||||
const { isDirty, isValid, isSubmitting } = formConfig.formState;
|
||||
const canSave = isDirty && isValid && !isSubmitting;
|
||||
|
||||
const handleSave = async (
|
||||
const getUpdatePayload = (
|
||||
formValues: SettingsDataModelObjectEditFormValues,
|
||||
) => {
|
||||
let values = formValues;
|
||||
if (
|
||||
formValues.shouldSyncLabelAndName ??
|
||||
activeObjectMetadataItem.shouldSyncLabelAndName
|
||||
) {
|
||||
values = {
|
||||
...values,
|
||||
...(values.labelSingular
|
||||
? {
|
||||
nameSingular: computeMetadataNameFromLabelOrThrow(
|
||||
formValues.labelSingular,
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
...(values.labelPlural
|
||||
? {
|
||||
namePlural: computeMetadataNameFromLabelOrThrow(
|
||||
formValues.labelPlural,
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
const dirtyFieldKeys = Object.keys(
|
||||
formConfig.formState.dirtyFields,
|
||||
) as (keyof SettingsDataModelObjectEditFormValues)[];
|
||||
|
||||
return settingsUpdateObjectInputSchema.parse(
|
||||
pick(values, [
|
||||
...dirtyFieldKeys,
|
||||
...(values.namePlural ? ['namePlural'] : []),
|
||||
...(values.nameSingular ? ['nameSingular'] : []),
|
||||
]),
|
||||
);
|
||||
};
|
||||
|
||||
const handleSave = async (
|
||||
formValues: SettingsDataModelObjectEditFormValues,
|
||||
) => {
|
||||
try {
|
||||
const updatePayload = getUpdatePayload(formValues);
|
||||
await updateOneObjectMetadataItem({
|
||||
idToUpdate: activeObjectMetadataItem.id,
|
||||
updatePayload: settingsUpdateObjectInputSchema.parse(
|
||||
pick(formValues, dirtyFieldKeys),
|
||||
),
|
||||
updatePayload,
|
||||
});
|
||||
|
||||
const objectNamePluralForRedirection =
|
||||
updatePayload.namePlural ?? activeObjectMetadataItem.namePlural;
|
||||
|
||||
if (lastVisitedObjectMetadataItemId === activeObjectMetadataItem.id) {
|
||||
const lastVisitedView = getLastVisitedViewIdFromObjectMetadataItemId(
|
||||
activeObjectMetadataItem.id,
|
||||
);
|
||||
setNavigationMemorizedUrl(
|
||||
`/objects/${objectNamePluralForRedirection}?view=${lastVisitedView}`,
|
||||
);
|
||||
}
|
||||
|
||||
navigate(
|
||||
`${settingsObjectsPagePath}/${getObjectSlug({
|
||||
...formValues,
|
||||
namePlural: formValues.labelPlural,
|
||||
...updatePayload,
|
||||
namePlural: objectNamePluralForRedirection,
|
||||
})}`,
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -142,7 +200,7 @@ export const SettingsObjectEdit = () => {
|
||||
/>
|
||||
<SettingsDataModelObjectAboutForm
|
||||
disabled={!activeObjectMetadataItem.isCustom}
|
||||
disableNameEdit
|
||||
disableNameEdit={!activeObjectMetadataItem.isCustom}
|
||||
objectMetadataItem={activeObjectMetadataItem}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
+3
@@ -2,5 +2,8 @@ import { METADATA_NAME_VALID_PATTERN } from '~/pages/settings/data-model/constan
|
||||
import { transliterateAndFormatOrThrow } from '~/pages/settings/data-model/utils/transliterate-and-format.utils';
|
||||
|
||||
export const computeMetadataNameFromLabelOrThrow = (label: string): string => {
|
||||
if (label === '') {
|
||||
return '';
|
||||
}
|
||||
return transliterateAndFormatOrThrow(label, METADATA_NAME_VALID_PATTERN);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "fd99213f-1b50-4d72-8708-75ba80097736",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "noteTarget",
|
||||
@@ -645,6 +646,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "f98ea433-1b70-46d3-aefa-43eb369925d2",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "messageThread",
|
||||
@@ -830,6 +832,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "f2414140-86ea-4fa3-bc63-ca5dab9f9044",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "workspaceMember",
|
||||
@@ -1864,6 +1867,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "f04a7171-564a-44ec-a061-63938e29f0c5",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "apiKey",
|
||||
@@ -2069,6 +2073,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "edfd2da3-26e4-4e84-b490-c0790848dc23",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "taskTarget",
|
||||
@@ -2706,6 +2711,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "eda936a5-97b9-4b9f-986a-d8e19e8ea882",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "comment",
|
||||
@@ -3079,6 +3085,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "e5915d30-4425-4c4c-a9c4-1b4bff20c469",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "workflowVersion",
|
||||
@@ -3521,6 +3528,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "d828bda6-68e2-47f0-b0aa-b810b1f33981",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "connectedAccount",
|
||||
@@ -4052,6 +4060,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "d00ff1e9-774a-4b08-87fb-03d37c24f174",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "attachment",
|
||||
@@ -5082,6 +5091,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "cb8c8d67-16c0-4a38-a919-b375845abf42",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "favorite",
|
||||
@@ -6157,6 +6167,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "c55193eb-042d-42d5-a6a7-8263fd1433a2",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "viewSort",
|
||||
@@ -6492,6 +6503,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "c46916fc-0528-4331-9766-6ac2247a70fb",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "view",
|
||||
@@ -7016,6 +7028,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "be13cda6-aff5-4003-8fe9-e936011b3325",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "opportunity",
|
||||
@@ -7908,6 +7921,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "b74e80b0-7132-469f-bbd9-6e6fc12f04f8",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "person",
|
||||
@@ -9058,6 +9072,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "b6e22795-68e7-4d18-a242-545afea5a8a9",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "timelineActivity",
|
||||
@@ -10068,6 +10083,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "af56ee43-5666-482f-a980-434fefac00c7",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "calendarEventParticipant",
|
||||
@@ -10640,6 +10656,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "981fd8a9-37a2-4742-98c1-08509d995bd3",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "calendarEvent",
|
||||
@@ -11154,6 +11171,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "92b529f1-b82b-4352-a0d5-18f32f8e47ab",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "messageChannel",
|
||||
@@ -11901,6 +11919,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "8cceadc4-de6b-4ecf-8324-82c6b4eec077",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "calendarChannel",
|
||||
@@ -12575,6 +12594,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "8ae98b12-2ef6-4c20-adc6-240857dd7343",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "blocklist",
|
||||
@@ -12836,6 +12856,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "88f29168-a15b-4330-89a1-680581a2e86b",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "viewFilter",
|
||||
@@ -13166,6 +13187,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "823e8b9d-1947-48f9-9f43-116a2cbceba3",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "webhook",
|
||||
@@ -13371,6 +13393,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "7cab9c82-929f-4ea3-98e1-5c221a12263d",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "workflow",
|
||||
@@ -13814,6 +13837,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "6edf5dd8-ee31-42ec-80f9-728b01c50ff4",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "messageParticipant",
|
||||
@@ -14340,6 +14364,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "6a09bc08-33ae-4321-868a-30064279097f",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "note",
|
||||
@@ -14767,6 +14792,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "681f89d7-0581-42b0-b97d-870e3b2a8359",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "task",
|
||||
@@ -15375,6 +15401,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "673b8cb8-44c1-4c20-9834-7c35d44fd180",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "message",
|
||||
@@ -15803,6 +15830,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "65cce76e-0f4c-4de1-a68a-6cadce4d000e",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "workflowEventListener",
|
||||
@@ -16064,6 +16092,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "631882fd-28e8-4a87-8ceb-f8217006a620",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "messageChannelMessageAssociation",
|
||||
@@ -16509,6 +16538,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "5a1aa92b-1ee9-4a7e-ab08-ca8c1e462d16",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "activity",
|
||||
@@ -17144,6 +17174,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "50f61b05-868d-425b-ab3f-c085e1652d82",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "viewField",
|
||||
@@ -17502,6 +17533,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "45b7e1cf-792c-45fa-8d6a-0d5e67e1fa42",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "workflowRun",
|
||||
@@ -18034,6 +18066,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "43fe0e45-b323-4b6e-ab98-1d9fe30c9af9",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "activityTarget",
|
||||
@@ -18671,6 +18704,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "39d5f2b7-03ce-41e7-afe9-7710aeb766a2",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "company",
|
||||
@@ -19757,6 +19791,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "2590029a-05d7-4908-8b7a-a253967068a1",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "auditLog",
|
||||
@@ -20133,6 +20168,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "1e5ee6b2-67e5-4549-bebc-8d35bc6bc649",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "rocket",
|
||||
@@ -20682,6 +20718,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"__typename": "objectEdge",
|
||||
"node": {
|
||||
"__typename": "object",
|
||||
"shouldSyncLabelAndName": true,
|
||||
"id": "149f1a0d-f528-48a3-a3f8-0203926d07f5",
|
||||
"dataSourceId": "d8a38ce6-6ac9-4c10-b55f-408386f86290",
|
||||
"nameSingular": "calendarChannelEventAssociation",
|
||||
|
||||
Reference in New Issue
Block a user