diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
index d2e049d16af..d1bcde89179 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
+++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
@@ -1724,6 +1724,7 @@ enum FeatureFlagKey {
IS_AI_ENABLED
IS_COMMAND_MENU_ITEM_ENABLED
IS_MARKETPLACE_ENABLED
+ IS_MARKETPLACE_SETTING_TAB_VISIBLE
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED
IS_PUBLIC_DOMAIN_ENABLED
IS_EMAILING_DOMAIN_ENABLED
diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts
index 3d2d71a5b29..dcc9001b10f 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts
+++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts
@@ -1424,7 +1424,7 @@ export interface PublicFeatureFlag {
__typename: 'PublicFeatureFlag'
}
-export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_USAGE_ANALYTICS_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_RECORD_TABLE_WIDGET_ENABLED' | 'IS_DATASOURCE_MIGRATED'
+export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_USAGE_ANALYTICS_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_RECORD_TABLE_WIDGET_ENABLED' | 'IS_DATASOURCE_MIGRATED'
export interface ClientConfigMaintenanceMode {
startAt: Scalars['DateTime']
@@ -9092,6 +9092,7 @@ export const enumFeatureFlagKey = {
IS_AI_ENABLED: 'IS_AI_ENABLED' as const,
IS_COMMAND_MENU_ITEM_ENABLED: 'IS_COMMAND_MENU_ITEM_ENABLED' as const,
IS_MARKETPLACE_ENABLED: 'IS_MARKETPLACE_ENABLED' as const,
+ IS_MARKETPLACE_SETTING_TAB_VISIBLE: 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' as const,
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' as const,
IS_PUBLIC_DOMAIN_ENABLED: 'IS_PUBLIC_DOMAIN_ENABLED' as const,
IS_EMAILING_DOMAIN_ENABLED: 'IS_EMAILING_DOMAIN_ENABLED' as const,
diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts
index d82b144a653..f0209c00725 100644
--- a/packages/twenty-front/src/generated-metadata/graphql.ts
+++ b/packages/twenty-front/src/generated-metadata/graphql.ts
@@ -1729,6 +1729,7 @@ export enum FeatureFlagKey {
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
IS_JUNCTION_RELATIONS_ENABLED = 'IS_JUNCTION_RELATIONS_ENABLED',
IS_MARKETPLACE_ENABLED = 'IS_MARKETPLACE_ENABLED',
+ IS_MARKETPLACE_SETTING_TAB_VISIBLE = 'IS_MARKETPLACE_SETTING_TAB_VISIBLE',
IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED',
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED = 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED',
IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED = 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED',
diff --git a/packages/twenty-front/src/pages/settings/applications/SettingsApplications.tsx b/packages/twenty-front/src/pages/settings/applications/SettingsApplications.tsx
index 9e2e69ce735..e522f12a696 100644
--- a/packages/twenty-front/src/pages/settings/applications/SettingsApplications.tsx
+++ b/packages/twenty-front/src/pages/settings/applications/SettingsApplications.tsx
@@ -11,7 +11,7 @@ import { getSettingsPath } from 'twenty-shared/utils';
import { IconApps, IconCode, IconDownload } from 'twenty-ui/display';
import { useQuery } from '@apollo/client/react';
import {
- type FeatureFlagKey,
+ FeatureFlagKey,
PermissionFlagType,
FindManyApplicationsDocument,
} from '~/generated-metadata/graphql';
@@ -33,6 +33,10 @@ export const SettingsApplications = () => {
'IS_MARKETPLACE_ENABLED' as FeatureFlagKey,
);
+ const isMarketplaceSettingTabVisible = useIsFeatureEnabled(
+ FeatureFlagKey.IS_MARKETPLACE_SETTING_TAB_VISIBLE,
+ );
+
const activeTabId = useAtomComponentStateValue(
activeTabIdComponentState,
APPLICATIONS_TAB_LIST_ID,
@@ -65,7 +69,9 @@ export const SettingsApplications = () => {
}
const tabs = [
- { id: 'marketplace', title: t`Marketplace`, Icon: IconDownload },
+ ...(isMarketplaceSettingTabVisible
+ ? [{ id: 'marketplace', title: t`Marketplace`, Icon: IconDownload }]
+ : []),
{ id: 'installed', title: t`Installed`, Icon: IconApps },
...(hasDeveloperAccess
? [{ id: 'developer', title: t`Developer`, Icon: IconCode }]
@@ -81,7 +87,11 @@ export const SettingsApplications = () => {
case 'developer':
return ;
default:
- return ;
+ return isMarketplaceSettingTabVisible ? (
+
+ ) : (
+
+ );
}
};
@@ -97,11 +107,7 @@ export const SettingsApplications = () => {
]}
>
-
+
{renderActiveTabContent()}
diff --git a/packages/twenty-front/src/pages/settings/applications/components/SettingsApplicationsTable.tsx b/packages/twenty-front/src/pages/settings/applications/components/SettingsApplicationsTable.tsx
index 893318b475b..317731637d1 100644
--- a/packages/twenty-front/src/pages/settings/applications/components/SettingsApplicationsTable.tsx
+++ b/packages/twenty-front/src/pages/settings/applications/components/SettingsApplicationsTable.tsx
@@ -22,8 +22,9 @@ const StyledTableContainer = styled.div`
margin-top: ${themeCssVariables.spacing[3]};
`;
-const StyledTableHeaderRowContainer = styled.div`
- margin-bottom: ${themeCssVariables.spacing[2]};
+const StyledTableRowsContainer = styled.div`
+ border-bottom: 1px solid ${themeCssVariables.border.color.light};
+ padding: ${themeCssVariables.spacing[2]} 0;
`;
const StyledSearchInputContainer = styled.div`
@@ -64,47 +65,47 @@ export const SettingsApplicationsTable = ({
-
-
- {t`Name`}
- {t`Description`}
- {''}
-
-
-
- {filteredApplications.map((application) => {
- const isNpmApp =
- application.applicationRegistration?.sourceType ===
- ApplicationRegistrationSourceType.NPM;
+
+ {t`Name`}
+ {t`Description`}
+ {''}
+
+
+
+ {filteredApplications.map((application) => {
+ const isNpmApp =
+ application.applicationRegistration?.sourceType ===
+ ApplicationRegistrationSourceType.NPM;
- const latestVersion =
- application.applicationRegistration?.latestAvailableVersion;
+ const latestVersion =
+ application.applicationRegistration?.latestAvailableVersion;
- const hasUpdate =
- isNpmApp &&
- isDefined(latestVersion) &&
- isDefined(application.version) &&
- isNewerSemver(latestVersion, application.version);
+ const hasUpdate =
+ isNpmApp &&
+ isDefined(latestVersion) &&
+ isDefined(application.version) &&
+ isNewerSemver(latestVersion, application.version);
- return (
-
- }
- link={getSettingsPath(SettingsPath.ApplicationDetail, {
- applicationId: application.id,
- })}
- />
- );
- })}
+ return (
+
+ }
+ link={getSettingsPath(SettingsPath.ApplicationDetail, {
+ applicationId: application.id,
+ })}
+ />
+ );
+ })}
+
diff --git a/packages/twenty-front/src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx b/packages/twenty-front/src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
index 6528a059bf2..763c5bea551 100644
--- a/packages/twenty-front/src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
+++ b/packages/twenty-front/src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
@@ -1,34 +1,68 @@
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { SettingsListCard } from '@/settings/components/SettingsListCard';
+import {
+ StyledActionTableCell,
+ StyledNameTableCell,
+} from '@/settings/data-model/object-details/components/SettingsObjectItemTableRowStyledComponents';
import { getDocumentationUrl } from '@/support/utils/getDocumentationUrl';
+import { Table } from '@/ui/layout/table/components/Table';
+import { TableCell } from '@/ui/layout/table/components/TableCell';
+import { TableHeader } from '@/ui/layout/table/components/TableHeader';
+import { TableRow } from '@/ui/layout/table/components/TableRow';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useQuery } from '@apollo/client/react';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
+import { useContext, useMemo, useState } from 'react';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import { Tag } from 'twenty-ui/components';
import {
+ Avatar,
CommandBlock,
H2Title,
IconApps,
+ IconChevronRight,
IconCopy,
IconFileInfo,
+ OverflowingTextWithTooltip,
} from 'twenty-ui/display';
-import { Button } from 'twenty-ui/input';
+import { Button, SearchInput } from 'twenty-ui/input';
import { Section } from 'twenty-ui/layout';
-import { themeCssVariables } from 'twenty-ui/theme-constants';
+import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import {
type ApplicationRegistrationFragmentFragment,
ApplicationRegistrationSourceType,
+ FeatureFlagKey,
FindManyApplicationRegistrationsDocument,
} from '~/generated-metadata/graphql';
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
+import { useMarketplaceApps } from '~/modules/marketplace/hooks/useMarketplaceApps';
const StyledButtonContainer = styled.div`
+ display: flex;
+ justify-content: flex-end;
margin: ${themeCssVariables.spacing[2]} 0;
`;
+const StyledSearchInputContainer = styled.div`
+ padding-bottom: ${themeCssVariables.spacing[2]};
+`;
+
+const StyledEmptyState = styled.div`
+ color: ${themeCssVariables.font.color.tertiary};
+ padding: ${themeCssVariables.spacing[8]};
+ text-align: center;
+`;
+
+const StyledTableRowsContainer = styled.div`
+ border-bottom: 1px solid ${themeCssVariables.border.color.light};
+ padding: ${themeCssVariables.spacing[2]} 0;
+`;
+
+const NPM_PACKAGES_GRID_COLUMNS = '200px 1fr 36px';
+
const SOURCE_TYPE_BADGE_CONFIG: Record<
ApplicationRegistrationSourceType,
{ label: string; color: 'gray' | 'blue' | 'green' }
@@ -53,12 +87,34 @@ const SOURCE_TYPE_BADGE_CONFIG: Record<
export const SettingsApplicationsDeveloperTab = () => {
const { t } = useLingui();
+ const { theme } = useContext(ThemeContext);
const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
const { copyToClipboard } = useCopyToClipboard();
const { data, loading } = useQuery(FindManyApplicationRegistrationsDocument);
+ const isMarketplaceSettingTabVisible = useIsFeatureEnabled(
+ FeatureFlagKey.IS_MARKETPLACE_SETTING_TAB_VISIBLE,
+ );
+
+ const [npmSearchTerm, setNpmSearchTerm] = useState('');
+ const { data: marketplaceApps } = useMarketplaceApps();
+
+ const filteredMarketplaceApps = useMemo(() => {
+ if (!npmSearchTerm) {
+ return marketplaceApps;
+ }
+
+ const lowerSearch = npmSearchTerm.toLowerCase();
+
+ return marketplaceApps.filter(
+ (application) =>
+ application.name.toLowerCase().includes(lowerSearch) ||
+ application.description.toLowerCase().includes(lowerSearch),
+ );
+ }, [marketplaceApps, npmSearchTerm]);
+
const registrations: ApplicationRegistrationFragmentFragment[] =
data?.findManyApplicationRegistrations ?? [];
@@ -123,8 +179,8 @@ export const SettingsApplicationsDeveloperTab = () => {
<>
@@ -146,7 +202,7 @@ export const SettingsApplicationsDeveloperTab = () => {
{registrations.length > 0 ? (
@@ -164,6 +220,74 @@ export const SettingsApplicationsDeveloperTab = () => {
)
)}
+
+ {!isMarketplaceSettingTabVisible && (
+
+
+
+
+
+ {filteredMarketplaceApps.length === 0 ? (
+ {t`No application found`}
+ ) : (
+
+
+ {t`Name`}
+ {t`Description`}
+
+
+
+ {filteredMarketplaceApps.map((application) => (
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ )}
+
+ )}
>
);
};
diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts
index aff69507958..46a54090530 100644
--- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts
+++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts
@@ -234,6 +234,7 @@ describe('WorkspaceEntityManager', () => {
IS_JSON_FILTER_ENABLED: false,
IS_AI_ENABLED: false,
IS_MARKETPLACE_ENABLED: false,
+ IS_MARKETPLACE_SETTING_TAB_VISIBLE: false,
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: false,
IS_PUBLIC_DOMAIN_ENABLED: false,
IS_EMAILING_DOMAIN_ENABLED: false,
diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts
index 51f3df2b1ab..0746494adbc 100644
--- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts
@@ -50,6 +50,11 @@ export const seedFeatureFlags = async ({
workspaceId: workspaceId,
value: true,
},
+ {
+ key: FeatureFlagKey.IS_MARKETPLACE_SETTING_TAB_VISIBLE,
+ workspaceId: workspaceId,
+ value: true,
+ },
{
key: FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
workspaceId: workspaceId,
diff --git a/packages/twenty-shared/src/types/FeatureFlagKey.ts b/packages/twenty-shared/src/types/FeatureFlagKey.ts
index 88406d6167a..635fbf6fc79 100644
--- a/packages/twenty-shared/src/types/FeatureFlagKey.ts
+++ b/packages/twenty-shared/src/types/FeatureFlagKey.ts
@@ -4,6 +4,7 @@ export enum FeatureFlagKey {
IS_AI_ENABLED = 'IS_AI_ENABLED',
IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED',
IS_MARKETPLACE_ENABLED = 'IS_MARKETPLACE_ENABLED',
+ IS_MARKETPLACE_SETTING_TAB_VISIBLE = 'IS_MARKETPLACE_SETTING_TAB_VISIBLE',
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED = 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED',
IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED',
IS_EMAILING_DOMAIN_ENABLED = 'IS_EMAILING_DOMAIN_ENABLED',
diff --git a/packages/twenty-ui/src/display/command-block/components/CommandBlock.tsx b/packages/twenty-ui/src/display/command-block/components/CommandBlock.tsx
index 55882d63fee..547d1710e37 100644
--- a/packages/twenty-ui/src/display/command-block/components/CommandBlock.tsx
+++ b/packages/twenty-ui/src/display/command-block/components/CommandBlock.tsx
@@ -5,7 +5,7 @@ import { themeCssVariables } from '@ui/theme-constants';
const StyledContainer = styled.div`
border-radius: ${themeCssVariables.border.radius.sm};
border: 1px solid ${themeCssVariables.border.color.medium};
- background: ${themeCssVariables.background.transparent.secondary};
+ background: ${themeCssVariables.background.secondary};
display: flex;
justify-content: space-between;
padding: ${themeCssVariables.spacing[3]};