feat(applications): restore the application custom settings tab (#23256)
## Summary Restores the application **custom settings tab** feature that was removed in #22156. This reverts that removal so applications can again expose a custom settings tab via a front component. ## Changes - Restore the `SettingsApplicationCustomTab` component and its tab entry/rendering in `SettingsApplicationDetails`. - `ApplicationManifestMigrationService` syncs `settingsCustomTabFrontComponent` from application manifests again (`syncDefaultRoleAndSettingsCustomTab`), resolving the front component from `settingsCustomTabFrontComponentUniversalIdentifier`. - Remove the deprecation annotations added by #22156: - `ApplicationDTO.settingsCustomTabFrontComponentId` (drop GraphQL `@deprecated`) - `ApplicationManifest.settingsCustomTabFrontComponentUniversalIdentifier` - the `settingsCustomTabFrontComponentId` column comment on `ApplicationEntity` - Regenerate the corresponding GraphQL schema/types to drop the `@deprecated` reason. The DB column was never dropped, so no schema migration is required. --- _Generated by [Claude Code](https://claude.ai/code/session_01A6aoLa5kZjba9C3uwo6nay)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23256?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
+1
@@ -84,6 +84,7 @@ exports[`stub-twenty-sdk-define plugin > matches the recorded export partition 1
|
||||
"definePostInstallLogicFunction",
|
||||
"definePreInstallLogicFunction",
|
||||
"defineRole",
|
||||
"defineSettingsFrontComponent",
|
||||
"defineSkill",
|
||||
"defineUninstallLogicFunction",
|
||||
"defineView",
|
||||
|
||||
+26
@@ -227,6 +227,32 @@ describe('manifestValidate', () => {
|
||||
expect(result.isValid).toBe(true);
|
||||
expect(result.errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should not flag a front component referenced via settingsFrontComponent as a duplicate', () => {
|
||||
const frontComponentId = '550e8400-e29b-41d4-a716-446655440050';
|
||||
|
||||
const frontComponent = {
|
||||
universalIdentifier: frontComponentId,
|
||||
name: 'app-settings',
|
||||
componentName: 'AppSettings',
|
||||
sourceComponentPath: 'src/front-components/app-settings.tsx',
|
||||
builtComponentPath: 'dist/app-settings.mjs',
|
||||
builtComponentChecksum: '00000000-0000-4000-8000-000000000000',
|
||||
isHeadless: false,
|
||||
} as unknown as Manifest['frontComponents'][number];
|
||||
|
||||
const result = manifestValidate({
|
||||
...validManifest,
|
||||
application: {
|
||||
...validApplication,
|
||||
settingsFrontComponent: { universalIdentifier: frontComponentId },
|
||||
},
|
||||
frontComponents: [frontComponent],
|
||||
});
|
||||
|
||||
expect(result.isValid).toBe(true);
|
||||
expect(result.errors).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('relation field validation', () => {
|
||||
|
||||
@@ -122,6 +122,7 @@ export const buildManifest = async (
|
||||
[];
|
||||
const uninstallLogicFunctions: UninstallLogicFunctionApplicationManifest[] =
|
||||
[];
|
||||
const settingsFrontComponentUniversalIdentifiers: string[] = [];
|
||||
const applicationRoleUniversalIdentifiers: string[] = [];
|
||||
const applicationFilePaths: string[] = [];
|
||||
const objectsFilePaths: string[] = [];
|
||||
@@ -377,6 +378,14 @@ export const buildManifest = async (
|
||||
frontComponents.push(config);
|
||||
frontComponentsFilePaths.push(relativePath);
|
||||
|
||||
if (
|
||||
targetFunctionName === TargetFunction.DefineSettingsFrontComponent
|
||||
) {
|
||||
settingsFrontComponentUniversalIdentifiers.push(
|
||||
extract.config.universalIdentifier,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ManifestEntityKey.Views: {
|
||||
@@ -559,6 +568,12 @@ export const buildManifest = async (
|
||||
);
|
||||
}
|
||||
|
||||
if (settingsFrontComponentUniversalIdentifiers.length > 1) {
|
||||
errors.push(
|
||||
'Only one settings front component is allowed per application',
|
||||
);
|
||||
}
|
||||
|
||||
if (applicationRoleUniversalIdentifiers.length > 1) {
|
||||
errors.push('Only one defineApplicationRole is allowed per application');
|
||||
}
|
||||
@@ -611,6 +626,14 @@ export const buildManifest = async (
|
||||
...(uninstallLogicFunctions.length >= 1
|
||||
? { uninstallLogicFunction: uninstallLogicFunctions[0] }
|
||||
: {}),
|
||||
...(settingsFrontComponentUniversalIdentifiers.length >= 1
|
||||
? {
|
||||
settingsFrontComponent: {
|
||||
universalIdentifier:
|
||||
settingsFrontComponentUniversalIdentifiers[0],
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
})()
|
||||
: undefined;
|
||||
|
||||
@@ -16,6 +16,7 @@ export enum TargetFunction {
|
||||
DefineAgent = 'defineAgent',
|
||||
DefineConnectionProvider = 'defineConnectionProvider',
|
||||
DefineFrontComponent = 'defineFrontComponent',
|
||||
DefineSettingsFrontComponent = 'defineSettingsFrontComponent',
|
||||
DefineView = 'defineView',
|
||||
DefineViewField = 'defineViewField',
|
||||
DefineNavigationMenuItem = 'defineNavigationMenuItem',
|
||||
@@ -70,6 +71,8 @@ export const TARGET_FUNCTION_TO_ENTITY_KEY_MAPPING: Record<
|
||||
[TargetFunction.DefineConnectionProvider]:
|
||||
ManifestEntityKey.ConnectionProviders,
|
||||
[TargetFunction.DefineFrontComponent]: ManifestEntityKey.FrontComponents,
|
||||
[TargetFunction.DefineSettingsFrontComponent]:
|
||||
ManifestEntityKey.FrontComponents,
|
||||
[TargetFunction.DefineView]: ManifestEntityKey.Views,
|
||||
[TargetFunction.DefineViewField]: ManifestEntityKey.ViewFields,
|
||||
[TargetFunction.DefineNavigationMenuItem]:
|
||||
|
||||
@@ -74,7 +74,8 @@ const findUniversalIdentifiers = (obj: object): string[] => {
|
||||
key === 'postInstallLogicFunction' ||
|
||||
key === 'preInstallLogicFunction' ||
|
||||
key === 'uninstallLogicFunction' ||
|
||||
key === 'onConnectLogicFunction'
|
||||
key === 'onConnectLogicFunction' ||
|
||||
key === 'settingsFrontComponent'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user