[FRONT COMPONENTS] Introduce conditionalAvailabilityExpression to command menu items (#18319)
## PR Description - Uses `expr-eval` to enable front components (SDK plugins) to define conditional availability as declarative expressions. - Moves shared types and constants to `twenty-shared` - Introduces a `conditionalAvailabilityExpression` field on `CommandMenuItemEntity`, allowing command menu items to store an `expr-eval` compatible expression string that is evaluated against a CommandMenuContext to determine if the item should be shown. - Creates an esbuild transform plugin `conditional-availability-transform-plugin` in `twenty-sdk` that converts TypeScript conditional availability expressions into `expr-eval` compatible syntax at build time, so SDK developers can write natural TS expressions that get transformed to evaluable strings. - Removes deprecated `forceRegisteredActionsByKey` state and its usage. - Creates `useCommandMenuContext` hook that builds the full `CommandMenuContext` object from React state, which is then passed to `useCommandMenuItemFrontComponentActions` for evaluating conditional availability expressions.
This commit is contained in:
+1
-2
@@ -6,7 +6,6 @@ import { useObjectNavItemColor } from '@/navigation-menu-item/hooks/useObjectNav
|
||||
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
|
||||
import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems';
|
||||
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { NavigationDrawerItemsCollapsableContainer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemsCollapsableContainer';
|
||||
@@ -18,7 +17,7 @@ import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomState
|
||||
import { coreViewsFromObjectMetadataItemFamilySelector } from '@/views/states/selectors/coreViewsFromObjectMetadataItemFamilySelector';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { AppPath, CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { getAppPath, isDefined } from 'twenty-shared/utils';
|
||||
import { Avatar, useIcons } from 'twenty-ui/display';
|
||||
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { useWorkspaceNavigationMenuItems } from '@/navigation-menu-item/hooks/us
|
||||
import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
|
||||
import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { NavigationDrawerItemForObjectMetadataItem } from '@/object-metadata/components/NavigationDrawerItemForObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
export enum CoreObjectNameSingular {
|
||||
Activity = 'activity',
|
||||
ActivityTarget = 'activityTarget',
|
||||
ApiKey = 'apiKey',
|
||||
Attachment = 'attachment',
|
||||
Blocklist = 'blocklist',
|
||||
CalendarChannel = 'calendarChannel',
|
||||
CalendarEvent = 'calendarEvent',
|
||||
Comment = 'comment',
|
||||
Company = 'company',
|
||||
ConnectedAccount = 'connectedAccount',
|
||||
Dashboard = 'dashboard',
|
||||
TimelineActivity = 'timelineActivity',
|
||||
Favorite = 'favorite',
|
||||
FavoriteFolder = 'favoriteFolder',
|
||||
Message = 'message',
|
||||
MessageChannel = 'messageChannel',
|
||||
MessageParticipant = 'messageParticipant',
|
||||
MessageFolder = 'messageFolder',
|
||||
MessageThread = 'messageThread',
|
||||
Note = 'note',
|
||||
NoteTarget = 'noteTarget',
|
||||
Opportunity = 'opportunity',
|
||||
Person = 'person',
|
||||
Task = 'task',
|
||||
TaskTarget = 'taskTarget',
|
||||
Webhook = 'webhook',
|
||||
WorkspaceMember = 'workspaceMember',
|
||||
MessageThreadSubscriber = 'messageThreadSubscriber',
|
||||
Workflow = 'workflow',
|
||||
MessageChannelMessageAssociation = 'messageChannelMessageAssociation',
|
||||
WorkflowVersion = 'workflowVersion',
|
||||
WorkflowRun = 'workflowRun',
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
|
||||
export const getAvatarType = (objectNameSingular: string) => {
|
||||
if (objectNameSingular === CoreObjectNameSingular.WorkspaceMember) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type Company } from '@/companies/types/Company';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
export const isObjectMetadataAvailableForRelation = (
|
||||
|
||||
Reference in New Issue
Block a user