feat(applications): remove the application custom settings tab (#22156)

## Summary

Removes the application **custom settings tab** feature. This is one
half of #22059, split out so it can be reviewed/merged independently
from the variable-types enrichment.

## Changes

- Remove the `SettingsApplicationCustomTab` component and its tab
entry/rendering in `SettingsApplicationDetails`.
- Stop syncing `settingsCustomTabFrontComponent` from application
manifests — `ApplicationManifestMigrationService` now only syncs the
default role.
- Deprecate the now-unused fields (kept for backward compatibility, no
longer read or synced):
- `ApplicationDTO.settingsCustomTabFrontComponentId` (GraphQL
`@deprecated`)
-
`ApplicationManifest.settingsCustomTabFrontComponentUniversalIdentifier`
- the `settingsCustomTabFrontComponentId` column comment on
`ApplicationEntity`

The DB column is intentionally **not dropped**, so existing
installations upgrade cleanly.
This commit is contained in:
martmull
2026-06-25 12:29:45 +02:00
committed by GitHub
parent 5a657129f0
commit a20ebfa880
9 changed files with 33 additions and 85 deletions
@@ -714,7 +714,7 @@ type Application {
applicationRegistrationId: UUID
canBeUninstalled: Boolean!
defaultRoleId: String
settingsCustomTabFrontComponentId: UUID
settingsCustomTabFrontComponentId: UUID @deprecated(reason: "Custom settings tabs are no longer supported. This field is ignored.")
defaultLogicFunctionRole: Role
agents: [Agent!]!
frontComponents: [FrontComponent!]!
@@ -475,6 +475,7 @@ export interface Application {
applicationRegistrationId?: Scalars['UUID']
canBeUninstalled: Scalars['Boolean']
defaultRoleId?: Scalars['String']
/** @deprecated Custom settings tabs are no longer supported. This field is ignored. */
settingsCustomTabFrontComponentId?: Scalars['UUID']
defaultLogicFunctionRole?: Role
agents: Agent[]
@@ -3510,6 +3511,7 @@ export interface ApplicationGenqlSelection{
applicationRegistrationId?: boolean | number
canBeUninstalled?: boolean | number
defaultRoleId?: boolean | number
/** @deprecated Custom settings tabs are no longer supported. This field is ignored. */
settingsCustomTabFrontComponentId?: boolean | number
defaultLogicFunctionRole?: RoleGenqlSelection
agents?: AgentGenqlSelection
@@ -302,6 +302,7 @@ export type Application = {
objects: Array<Object>;
packageJsonChecksum?: Maybe<Scalars['String']['output']>;
packageJsonFileId?: Maybe<Scalars['UUID']['output']>;
/** @deprecated Custom settings tabs are no longer supported. This field is ignored. */
settingsCustomTabFrontComponentId?: Maybe<Scalars['UUID']['output']>;
universalIdentifier: Scalars['String']['output'];
version?: Maybe<Scalars['String']['output']>;
@@ -22,7 +22,6 @@ import { type Manifest } from 'twenty-shared/application';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
import {
IconApps,
IconBox,
IconCommand,
IconGraph,
@@ -44,7 +43,6 @@ import { SettingsSectionSkeletonLoader } from '@/settings/components/SettingsSec
import { CUSTOM_APPLICATION_ILLUSTRATIONS } from '~/pages/settings/applications/constants/CustomApplicationIllustrations';
import { STANDARD_APPLICATION_ILLUSTRATIONS } from '~/pages/settings/applications/constants/StandardApplicationIllustrations';
import { useFindApplicationConnectionProviders } from '~/pages/settings/applications/hooks/useFindApplicationConnectionProviders';
import { SettingsApplicationCustomTab } from '~/pages/settings/applications/tabs/SettingsApplicationCustomTab';
import { SettingsApplicationDetailAboutTab } from '~/pages/settings/applications/tabs/SettingsApplicationDetailAboutTab';
import { SettingsApplicationDetailContentTab } from '~/pages/settings/applications/tabs/SettingsApplicationDetailContentTab';
import { SettingsApplicationDetailSettingsTab } from '~/pages/settings/applications/tabs/SettingsApplicationDetailSettingsTab';
@@ -102,9 +100,6 @@ export const SettingsApplicationDetails = () => {
const screenshots = getScreenshots();
const settingsCustomTabFrontComponentId =
application?.settingsCustomTabFrontComponentId;
const { upgrade, isUpgrading } = useUpgradeApplication();
const canInstallMarketplaceApps = useHasPermissionFlag(
@@ -245,9 +240,6 @@ export const SettingsApplicationDetails = () => {
disabled: hasNothingToConfigure,
};
})(),
...(isDefined(settingsCustomTabFrontComponentId)
? [{ id: 'custom', title: t`Custom`, Icon: IconApps }]
: []),
];
const renderActiveTabContent = () => {
@@ -312,16 +304,6 @@ export const SettingsApplicationDetails = () => {
return (
<SettingsApplicationDetailSettingsTab application={application} />
);
case 'custom':
return isDefined(settingsCustomTabFrontComponentId) ? (
<SettingsApplicationCustomTab
settingsCustomTabFrontComponentId={
settingsCustomTabFrontComponentId
}
/>
) : (
<></>
);
default:
return <></>;
}
@@ -1,32 +0,0 @@
import { styled } from '@linaria/react';
import { Suspense, lazy } from 'react';
const FrontComponentRenderer = lazy(() =>
import('@/front-components/components/FrontComponentRenderer').then(
(module) => ({ default: module.FrontComponentRenderer }),
),
);
const StyledContainer = styled.div`
height: 100%;
overflow: auto;
width: 100%;
`;
type SettingsApplicationCustomTabProps = {
settingsCustomTabFrontComponentId: string;
};
export const SettingsApplicationCustomTab = ({
settingsCustomTabFrontComponentId,
}: SettingsApplicationCustomTabProps) => {
return (
<StyledContainer>
<Suspense fallback={null}>
<FrontComponentRenderer
frontComponentId={settingsCustomTabFrontComponentId}
/>
</Suspense>
</StyledContainer>
);
};
@@ -257,7 +257,7 @@ export class ApplicationManifestMigrationService {
);
if (!dryRun) {
await this.syncDefaultRoleAndSettingsCustomTab({
await this.syncDefaultRole({
manifest,
workspaceId,
ownerFlatApplication,
@@ -270,7 +270,7 @@ export class ApplicationManifestMigrationService {
};
}
private async syncDefaultRoleAndSettingsCustomTab({
private async syncDefaultRole({
manifest,
workspaceId,
ownerFlatApplication,
@@ -279,13 +279,10 @@ export class ApplicationManifestMigrationService {
workspaceId: string;
ownerFlatApplication: FlatApplication;
}) {
const {
flatRoleMaps: refreshedFlatRoleMaps,
flatFrontComponentMaps: refreshedFlatFrontComponentMaps,
} = await this.workspaceCacheService.getOrRecompute(workspaceId, [
'flatRoleMaps',
'flatFrontComponentMaps',
]);
const { flatRoleMaps: refreshedFlatRoleMaps } =
await this.workspaceCacheService.getOrRecompute(workspaceId, [
'flatRoleMaps',
]);
let defaultRoleId: string | null = null;
@@ -310,31 +307,11 @@ export class ApplicationManifestMigrationService {
}
}
let settingsCustomTabFrontComponentId: string | null = null;
const settingsCustomTabUniversalIdentifier =
manifest.application.settingsCustomTabFrontComponentUniversalIdentifier;
if (isDefined(settingsCustomTabUniversalIdentifier)) {
const flatFrontComponent = findFlatEntityByUniversalIdentifier({
flatEntityMaps: refreshedFlatFrontComponentMaps,
universalIdentifier: settingsCustomTabUniversalIdentifier,
if (isDefined(defaultRoleId)) {
await this.applicationService.update(ownerFlatApplication.id, {
workspaceId,
defaultRoleId,
});
if (!isDefined(flatFrontComponent)) {
throw new ApplicationException(
`Failed to resolve front component for settingsCustomTabFrontComponentUniversalIdentifier ${settingsCustomTabUniversalIdentifier}`,
ApplicationExceptionCode.ENTITY_NOT_FOUND,
);
}
settingsCustomTabFrontComponentId = flatFrontComponent.id;
}
await this.applicationService.update(ownerFlatApplication.id, {
workspaceId,
settingsCustomTabFrontComponentId,
...(isDefined(defaultRoleId) ? { defaultRoleId } : {}),
});
}
}
@@ -102,6 +102,11 @@ export class ApplicationEntity extends WorkspaceRelatedEntity {
@Field(() => RoleDTO, { nullable: true })
defaultRole: RoleDTO | null;
/**
* @deprecated Custom settings tabs are no longer supported. The column is
* kept (not dropped) so existing installations upgrade cleanly, but the
* value is no longer read or synced from manifests.
*/
@Column({ nullable: true, type: 'uuid' })
settingsCustomTabFrontComponentId: string | null;
@@ -86,9 +86,17 @@ export class ApplicationDTO {
@Field({ nullable: true })
defaultRoleId?: string;
/**
* @deprecated Custom settings tabs are no longer supported. Kept for
* backward compatibility with existing installations; the value is ignored.
*/
@IsOptional()
@IsUUID()
@Field(() => UUIDScalarType, { nullable: true })
@Field(() => UUIDScalarType, {
nullable: true,
deprecationReason:
'Custom settings tabs are no longer supported. This field is ignored.',
})
settingsCustomTabFrontComponentId?: string;
@IsOptional()
@@ -21,6 +21,11 @@ export type ApplicationManifest = SyncableEntityOptions & {
issueReportUrl?: string;
postInstallLogicFunction?: PostInstallLogicFunctionApplicationManifest;
preInstallLogicFunction?: PreInstallLogicFunctionApplicationManifest;
/**
* @deprecated Custom settings tabs are no longer supported. This property is
* kept for backward compatibility with older manifests but is now ignored.
* Use typed `applicationVariables` / `serverVariables` instead.
*/
settingsCustomTabFrontComponentUniversalIdentifier?: string;
packageJsonChecksum: string | null;
yarnLockChecksum: string | null;