1518 extensibility front add an application section in settings (#15056)
Protected by IS_APPLICATION_ENABLED featureFlag Add `Application` section in settings <img width="301" height="137" alt="image" src="https://github.com/user-attachments/assets/ee53bdd2-36f6-45c6-8646-17b1e08abf00" /> A `settings/applications` route listing all installed applications <img width="661" height="428" alt="image" src="https://github.com/user-attachments/assets/69d534c4-4e9e-452a-a3d9-ded0223bb457" /> Introduce a new Tag for application managed items <img width="885" height="759" alt="image" src="https://github.com/user-attachments/assets/19767be5-61e5-4bd2-a51d-54ed9bfb1923" /> A `settings/applications/<application_id>` details setting page listing all objects, serverlessFunctions and agents created by the application: <img width="917" height="778" alt="image" src="https://github.com/user-attachments/assets/7fc056a6-1d73-4242-b2eb-6f8955d8597d" /> A `settings/applications/<application_id>/<serverless_function_id>` <img width="905" height="652" alt="image" src="https://github.com/user-attachments/assets/56ca0021-26bf-42cb-9abf-34879f16050a" /> Add trigger tab in serverless function details (readonly for now) <img width="899" height="724" alt="image" src="https://github.com/user-attachments/assets/5eeefa35-f2a4-4fd8-a640-7b5c5891f226" /> Set object, serverless and agent setting detail pages readonly for managed items <img width="1075" height="859" alt="image" src="https://github.com/user-attachments/assets/57c73d69-4980-47a2-b752-8dc5ab494530" /> <img width="648" height="582" alt="image" src="https://github.com/user-attachments/assets/5ad5f3f7-3bc3-4e40-870a-4981c6492524" /> <img width="982" height="692" alt="image" src="https://github.com/user-attachments/assets/7ad756c4-5d33-4a0a-9eb8-416c040362b9" /> <img width="1077" height="647" alt="image" src="https://github.com/user-attachments/assets/e086b9f5-4062-4d10-82a9-4023de3cad3f" />
This commit is contained in:
+2
-4
@@ -2,13 +2,12 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SettingsDataModelObjectTypeTag } from '@/settings/data-model/objects/components/SettingsDataModelObjectTypeTag';
|
||||
import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
|
||||
import {
|
||||
IconBox,
|
||||
OverflowingTextWithTooltip,
|
||||
useIcons,
|
||||
} from 'twenty-ui/display';
|
||||
import { SettingsItemTypeTag } from '@/settings/components/SettingsItemTypeTag';
|
||||
|
||||
export type SettingsDataModelObjectPreviewProps = {
|
||||
className?: string;
|
||||
@@ -69,7 +68,6 @@ const SettingsDataModelObjectPreviewItem = ({
|
||||
const theme = useTheme();
|
||||
const { getIcon } = useIcons();
|
||||
const ObjectIcon = getIcon(objectMetadataItem.icon);
|
||||
const objectTypeLabel = getObjectTypeLabel(objectMetadataItem);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -90,7 +88,7 @@ const SettingsDataModelObjectPreviewItem = ({
|
||||
}
|
||||
/>
|
||||
</StyledObjectName>
|
||||
<SettingsDataModelObjectTypeTag objectTypeLabel={objectTypeLabel} />
|
||||
<SettingsItemTypeTag item={objectMetadataItem} />
|
||||
</StyledObjectPreview>
|
||||
</>
|
||||
);
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
import { type ObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
|
||||
import { Tag } from 'twenty-ui/components';
|
||||
|
||||
type SettingsDataModelObjectTypeTagProps = {
|
||||
objectTypeLabel: ObjectTypeLabel;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const SettingsDataModelObjectTypeTag = ({
|
||||
className,
|
||||
objectTypeLabel,
|
||||
}: SettingsDataModelObjectTypeTagProps) => {
|
||||
return (
|
||||
<Tag
|
||||
className={className}
|
||||
color={objectTypeLabel.labelColor}
|
||||
text={objectTypeLabel.labelText}
|
||||
weight="medium"
|
||||
/>
|
||||
);
|
||||
};
|
||||
+14
-6
@@ -139,7 +139,11 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<IconPicker
|
||||
selectedIconKey={value}
|
||||
disabled={disableEdition}
|
||||
onChange={({ iconKey }) => {
|
||||
if (disableEdition) {
|
||||
return;
|
||||
}
|
||||
onChange(iconKey);
|
||||
onNewDirtyField?.();
|
||||
}}
|
||||
@@ -170,9 +174,10 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
}}
|
||||
onBlur={() => onNewDirtyField?.()}
|
||||
disabled={
|
||||
objectMetadataItem &&
|
||||
!objectMetadataItem?.isCustom &&
|
||||
isLabelSyncedWithName
|
||||
disableEdition ||
|
||||
(objectMetadataItem &&
|
||||
!objectMetadataItem?.isCustom &&
|
||||
isLabelSyncedWithName)
|
||||
}
|
||||
fullWidth
|
||||
maxLength={OBJECT_NAME_MAXIMUM_LENGTH}
|
||||
@@ -201,9 +206,10 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
}}
|
||||
onBlur={() => onNewDirtyField?.()}
|
||||
disabled={
|
||||
objectMetadataItem &&
|
||||
!objectMetadataItem?.isCustom &&
|
||||
isLabelSyncedWithName
|
||||
disableEdition ||
|
||||
(objectMetadataItem &&
|
||||
!objectMetadataItem?.isCustom &&
|
||||
isLabelSyncedWithName)
|
||||
}
|
||||
fullWidth
|
||||
maxLength={OBJECT_NAME_MAXIMUM_LENGTH}
|
||||
@@ -222,6 +228,7 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
value={value ?? undefined}
|
||||
onChange={(nextValue) => onChange(nextValue ?? null)}
|
||||
onBlur={() => onNewDirtyField?.()}
|
||||
disabled={disableEdition}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@@ -326,6 +333,7 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
description={t`Should changing an object's label also change the API?`}
|
||||
checked={value ?? true}
|
||||
advancedMode
|
||||
disabled={disableEdition}
|
||||
onChange={(value) => {
|
||||
onChange(value);
|
||||
const isCustomObject =
|
||||
|
||||
+3
-1
@@ -16,6 +16,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { isLabelIdentifierFieldMetadataTypes } from 'twenty-shared/utils';
|
||||
import { IconCircleOff, IconPlus, useIcons } from 'twenty-ui/display';
|
||||
import { type SelectOption } from 'twenty-ui/input';
|
||||
import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObjectMetadataReadOnly';
|
||||
|
||||
export const settingsDataModelObjectIdentifiersFormSchema =
|
||||
objectMetadataItemSchema.pick({
|
||||
@@ -44,6 +45,7 @@ const StyledContainer = styled.div`
|
||||
export const SettingsDataModelObjectIdentifiersForm = ({
|
||||
objectMetadataItem,
|
||||
}: SettingsDataModelObjectIdentifiersFormProps) => {
|
||||
const readonly = isObjectMetadataReadOnly({ objectMetadataItem });
|
||||
const formConfig = useForm<SettingsDataModelObjectIdentifiersFormValues>({
|
||||
mode: 'onTouched',
|
||||
resolver: zodResolver(settingsDataModelObjectIdentifiersFormSchema),
|
||||
@@ -134,7 +136,7 @@ export const SettingsDataModelObjectIdentifiersForm = ({
|
||||
options={options}
|
||||
value={value}
|
||||
withSearchInput={label === t`Record label`}
|
||||
disabled={!objectMetadataItem.isCustom}
|
||||
disabled={!objectMetadataItem.isCustom || readonly}
|
||||
callToActionButton={
|
||||
label === t`Record label`
|
||||
? {
|
||||
|
||||
Reference in New Issue
Block a user