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:
martmull
2025-10-15 17:13:29 +02:00
committed by GitHub
parent c0ed246a03
commit b16ab1b7c9
109 changed files with 2464 additions and 1357 deletions
@@ -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>
</>
);
@@ -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"
/>
);
};
@@ -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 =
@@ -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`
? {