diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index 4fd762cb9d..34a78fa083 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -2653,6 +2653,7 @@ type ToolIndexEntry { description: String! category: String! objectName: String + icon: String inputSchema: JSON } diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index c3dbbf7937..ebebe0dc48 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -2297,6 +2297,7 @@ export interface ToolIndexEntry { description: Scalars['String'] category: Scalars['String'] objectName?: Scalars['String'] + icon?: Scalars['String'] inputSchema?: Scalars['JSON'] __typename: 'ToolIndexEntry' } @@ -5475,6 +5476,7 @@ export interface ToolIndexEntryGenqlSelection{ description?: boolean | number category?: boolean | number objectName?: boolean | number + icon?: boolean | number inputSchema?: boolean | number __typename?: boolean | number __scalar?: boolean | number diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index 288c95b5da..cd5a1fbae6 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -5179,6 +5179,9 @@ export default { "objectName": [ 1 ], + "icon": [ + 1 + ], "inputSchema": [ 15 ], diff --git a/packages/twenty-front/public/images/ai/ai-tools-cover-dark.png b/packages/twenty-front/public/images/ai/ai-tools-cover-dark.png new file mode 100644 index 0000000000..6ab502be13 Binary files /dev/null and b/packages/twenty-front/public/images/ai/ai-tools-cover-dark.png differ diff --git a/packages/twenty-front/public/images/ai/ai-tools-cover-light.png b/packages/twenty-front/public/images/ai/ai-tools-cover-light.png new file mode 100644 index 0000000000..b29ed89a50 Binary files /dev/null and b/packages/twenty-front/public/images/ai/ai-tools-cover-light.png differ diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 5097aa51a9..c459509c12 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -5141,6 +5141,7 @@ export type ToolIndexEntry = { __typename?: 'ToolIndexEntry'; category: Scalars['String']; description: Scalars['String']; + icon?: Maybe; inputSchema?: Maybe; name: Scalars['String']; objectName?: Maybe; @@ -6404,7 +6405,7 @@ export type GetChatThreadsQuery = { __typename?: 'Query', chatThreads: { __typen export type GetToolIndexQueryVariables = Exact<{ [key: string]: never; }>; -export type GetToolIndexQuery = { __typename?: 'Query', getToolIndex: Array<{ __typename?: 'ToolIndexEntry', name: string, description: string, category: string, objectName?: string | null }> }; +export type GetToolIndexQuery = { __typename?: 'Query', getToolIndex: Array<{ __typename?: 'ToolIndexEntry', name: string, description: string, category: string, objectName?: string | null, icon?: string | null }> }; export type GetToolInputSchemaQueryVariables = Exact<{ toolName: Scalars['String']; @@ -8261,7 +8262,7 @@ export const FindOneSkillDocument = {"kind":"Document","definitions":[{"kind":"O export const GetAgentTurnsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAgentTurns"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"agentTurns"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"agentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"threadId"}},{"kind":"Field","name":{"kind":"Name","value":"agentId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"evaluations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"score"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"messages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"parts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"textContent"}},{"kind":"Field","name":{"kind":"Name","value":"reasoningContent"}},{"kind":"Field","name":{"kind":"Name","value":"toolName"}},{"kind":"Field","name":{"kind":"Name","value":"toolCallId"}},{"kind":"Field","name":{"kind":"Name","value":"toolInput"}},{"kind":"Field","name":{"kind":"Name","value":"toolOutput"}},{"kind":"Field","name":{"kind":"Name","value":"errorMessage"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"errorDetails"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlUrl"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlTitle"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentMediaType"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentTitle"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentFilename"}},{"kind":"Field","name":{"kind":"Name","value":"fileMediaType"}},{"kind":"Field","name":{"kind":"Name","value":"fileFilename"}},{"kind":"Field","name":{"kind":"Name","value":"fileUrl"}},{"kind":"Field","name":{"kind":"Name","value":"providerMetadata"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetChatMessagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChatMessages"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chatMessages"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"threadId"}},{"kind":"Field","name":{"kind":"Name","value":"turnId"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"parts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"messageId"}},{"kind":"Field","name":{"kind":"Name","value":"orderIndex"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"textContent"}},{"kind":"Field","name":{"kind":"Name","value":"reasoningContent"}},{"kind":"Field","name":{"kind":"Name","value":"toolName"}},{"kind":"Field","name":{"kind":"Name","value":"toolCallId"}},{"kind":"Field","name":{"kind":"Name","value":"toolInput"}},{"kind":"Field","name":{"kind":"Name","value":"toolOutput"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"errorMessage"}},{"kind":"Field","name":{"kind":"Name","value":"errorDetails"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlUrl"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlTitle"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentMediaType"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentTitle"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentFilename"}},{"kind":"Field","name":{"kind":"Name","value":"fileMediaType"}},{"kind":"Field","name":{"kind":"Name","value":"fileFilename"}},{"kind":"Field","name":{"kind":"Name","value":"fileUrl"}},{"kind":"Field","name":{"kind":"Name","value":"fileId"}},{"kind":"Field","name":{"kind":"Name","value":"providerMetadata"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"chatStreamCatchupChunks"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chunks"}},{"kind":"Field","name":{"kind":"Name","value":"maxSeq"}}]}}]}}]} as unknown as DocumentNode; export const GetChatThreadsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChatThreads"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"paging"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CursorPaging"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chatThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"Variable","name":{"kind":"Name","value":"paging"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"totalInputTokens"}},{"kind":"Field","name":{"kind":"Name","value":"totalOutputTokens"}},{"kind":"Field","name":{"kind":"Name","value":"contextWindowTokens"}},{"kind":"Field","name":{"kind":"Name","value":"conversationSize"}},{"kind":"Field","name":{"kind":"Name","value":"totalInputCredits"}},{"kind":"Field","name":{"kind":"Name","value":"totalOutputCredits"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"endCursor"}},{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetToolIndexDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetToolIndex"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getToolIndex"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"objectName"}}]}}]}}]} as unknown as DocumentNode; +export const GetToolIndexDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetToolIndex"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getToolIndex"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"objectName"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}}]}}]}}]} as unknown as DocumentNode; export const GetToolInputSchemaDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetToolInputSchema"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"toolName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getToolInputSchema"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"toolName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"toolName"}}}]}]}}]} as unknown as DocumentNode; export const OnAgentChatEventDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnAgentChatEvent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"onAgentChatEvent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"threadId"}},{"kind":"Field","name":{"kind":"Name","value":"event"}}]}}]}}]} as unknown as DocumentNode; export const TrackAnalyticsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"TrackAnalytics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"type"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AnalyticsType"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"event"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"properties"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"trackAnalytics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"type"},"value":{"kind":"Variable","name":{"kind":"Name","value":"type"}}},{"kind":"Argument","name":{"kind":"Name","value":"event"},"value":{"kind":"Variable","name":{"kind":"Name","value":"event"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"properties"},"value":{"kind":"Variable","name":{"kind":"Name","value":"properties"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/ai/graphql/queries/getToolIndex.ts b/packages/twenty-front/src/modules/ai/graphql/queries/getToolIndex.ts index 03688c350c..b19e94e723 100644 --- a/packages/twenty-front/src/modules/ai/graphql/queries/getToolIndex.ts +++ b/packages/twenty-front/src/modules/ai/graphql/queries/getToolIndex.ts @@ -7,6 +7,7 @@ export const GET_TOOL_INDEX = gql` description category objectName + icon } } `; diff --git a/packages/twenty-front/src/modules/ai/hooks/useGetToolIndex.ts b/packages/twenty-front/src/modules/ai/hooks/useGetToolIndex.ts index 9cf5334d8a..4b30323714 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useGetToolIndex.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useGetToolIndex.ts @@ -1,16 +1,7 @@ import { GET_TOOL_INDEX } from '@/ai/graphql/queries/getToolIndex'; import { useQuery } from '@apollo/client/react'; -type ToolIndexEntry = { - name: string; - description: string; - category: string; - objectName?: string; -}; - -type GetToolIndexQuery = { - getToolIndex: ToolIndexEntry[]; -}; +import { type GetToolIndexQuery } from '~/generated-metadata/graphql'; export const useGetToolIndex = () => { const { data, loading, error } = useQuery(GET_TOOL_INDEX); diff --git a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx index 7d98740ccf..f13f24d414 100644 --- a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx +++ b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx @@ -161,6 +161,12 @@ const SettingsAIUsageUserDetail = lazy(() => })), ); +const SettingsToolDetail = lazy(() => + import('~/pages/settings/ai/SettingsToolDetail').then((module) => ({ + default: module.SettingsToolDetail, + })), +); + const SettingsApplications = lazy(() => import('~/pages/settings/applications/SettingsApplications').then( (module) => ({ @@ -546,6 +552,10 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => ( path={SettingsPath.AIUsageUserDetail} element={} /> + } + /> } diff --git a/packages/twenty-front/src/modules/logic-functions/hooks/useGetOneLogicFunction.ts b/packages/twenty-front/src/modules/logic-functions/hooks/useGetOneLogicFunction.ts index afca69892a..c8564ee01d 100644 --- a/packages/twenty-front/src/modules/logic-functions/hooks/useGetOneLogicFunction.ts +++ b/packages/twenty-front/src/modules/logic-functions/hooks/useGetOneLogicFunction.ts @@ -6,7 +6,10 @@ import { type LogicFunctionIdInput, } from '~/generated-metadata/graphql'; -export const useGetOneLogicFunction = ({ id }: LogicFunctionIdInput) => { +export const useGetOneLogicFunction = ({ + id, + skip, +}: LogicFunctionIdInput & { skip?: boolean }) => { const { data, loading } = useQuery< FindOneLogicFunctionQuery, FindOneLogicFunctionQueryVariables @@ -14,6 +17,7 @@ export const useGetOneLogicFunction = ({ id }: LogicFunctionIdInput) => { variables: { input: { id }, }, + skip, }); return { diff --git a/packages/twenty-front/src/modules/logic-functions/states/logicFunctionsSelector.ts b/packages/twenty-front/src/modules/logic-functions/states/logicFunctionsSelector.ts new file mode 100644 index 0000000000..88bf1ee795 --- /dev/null +++ b/packages/twenty-front/src/modules/logic-functions/states/logicFunctionsSelector.ts @@ -0,0 +1,12 @@ +import { metadataStoreState } from '@/metadata-store/states/metadataStoreState'; +import { type LogicFunction } from '@/logic-functions/types/LogicFunction'; +import { createAtomSelector } from '@/ui/utilities/state/jotai/utils/createAtomSelector'; + +export const logicFunctionsSelector = createAtomSelector({ + key: 'logicFunctionsSelector', + get: ({ get }) => { + const storeItem = get(metadataStoreState, 'logicFunctions'); + + return storeItem.current as LogicFunction[]; + }, +}); diff --git a/packages/twenty-front/src/modules/logic-functions/types/LogicFunction.ts b/packages/twenty-front/src/modules/logic-functions/types/LogicFunction.ts new file mode 100644 index 0000000000..e6ee508dc1 --- /dev/null +++ b/packages/twenty-front/src/modules/logic-functions/types/LogicFunction.ts @@ -0,0 +1,4 @@ +import { type FindManyLogicFunctionsQuery } from '~/generated-metadata/graphql'; + +export type LogicFunction = + FindManyLogicFunctionsQuery['findManyLogicFunctions'][number]; diff --git a/packages/twenty-front/src/modules/metadata-store/hooks/useLoadStaleMetadataEntities.ts b/packages/twenty-front/src/modules/metadata-store/hooks/useLoadStaleMetadataEntities.ts index f4e2afc9af..29decdef41 100644 --- a/packages/twenty-front/src/modules/metadata-store/hooks/useLoadStaleMetadataEntities.ts +++ b/packages/twenty-front/src/modules/metadata-store/hooks/useLoadStaleMetadataEntities.ts @@ -5,10 +5,8 @@ import { splitPageLayoutWithRelated } from '@/metadata-store/utils/splitPageLayo import { splitViewWithRelated } from '@/metadata-store/utils/splitViewWithRelated'; import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries'; import { transformPageLayout } from '@/page-layout/utils/transformPageLayout'; -import { logicFunctionsState } from '@/settings/logic-functions/states/logicFunctionsState'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useApolloClient } from '@apollo/client/react'; -import { useStore } from 'jotai'; import { useCallback } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { @@ -56,7 +54,6 @@ const hasOverlap = ( export const useLoadStaleMetadataEntities = () => { const client = useApolloClient(); - const store = useStore(); const { replaceDraft, applyChanges } = useUpdateMetadataStoreDraft(); const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); @@ -166,10 +163,6 @@ export const useLoadStaleMetadataEntities = () => { return; } - store.set( - logicFunctionsState.atom, - result.data.findManyLogicFunctions, - ); replaceDraft( 'logicFunctions', result.data.findManyLogicFunctions, @@ -240,7 +233,7 @@ export const useLoadStaleMetadataEntities = () => { await Promise.all(fetchPromises); applyChanges(); }, - [client, store, replaceDraft, applyChanges, isAiEnabled], + [client, replaceDraft, applyChanges, isAiEnabled], ); return { loadStaleMetadataEntities }; diff --git a/packages/twenty-front/src/modules/metadata-store/types/FlatLogicFunction.ts b/packages/twenty-front/src/modules/metadata-store/types/FlatLogicFunction.ts index 981daa6a23..26d6d6fdb1 100644 --- a/packages/twenty-front/src/modules/metadata-store/types/FlatLogicFunction.ts +++ b/packages/twenty-front/src/modules/metadata-store/types/FlatLogicFunction.ts @@ -1,3 +1,3 @@ -import { type LogicFunction } from '@/settings/logic-functions/states/logicFunctionsState'; +import { type LogicFunction } from '@/logic-functions/types/LogicFunction'; export type FlatLogicFunction = LogicFunction; diff --git a/packages/twenty-front/src/modules/settings/logic-functions/states/logicFunctionsState.ts b/packages/twenty-front/src/modules/settings/logic-functions/states/logicFunctionsState.ts deleted file mode 100644 index 94acc0a8ac..0000000000 --- a/packages/twenty-front/src/modules/settings/logic-functions/states/logicFunctionsState.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; -import { type FindManyLogicFunctionsQuery } from '~/generated-metadata/graphql'; - -export type LogicFunction = - FindManyLogicFunctionsQuery['findManyLogicFunctions'][number]; - -export const logicFunctionsState = createAtomState({ - key: 'logicFunctionsState', - defaultValue: [], -}); diff --git a/packages/twenty-front/src/modules/side-panel/pages/workflow/action/components/SidePanelWorkflowSelectAction.tsx b/packages/twenty-front/src/modules/side-panel/pages/workflow/action/components/SidePanelWorkflowSelectAction.tsx index 3cea913751..9eb061f43f 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/workflow/action/components/SidePanelWorkflowSelectAction.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/workflow/action/components/SidePanelWorkflowSelectAction.tsx @@ -1,5 +1,6 @@ import { WorkflowActionMenuItems } from '@/side-panel/pages/workflow/action/components/WorkflowActionMenuItems'; -import { logicFunctionsState } from '@/settings/logic-functions/states/logicFunctionsState'; +import { logicFunctionsSelector } from '@/logic-functions/states/logicFunctionsSelector'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { type WorkflowActionType } from '@/workflow/types/Workflow'; import { SidePanelStepListContainer } from '@/workflow/workflow-steps/components/SidePanelWorkflowSelectStepContainer'; import { SidePanelWorkflowSelectStepTitle } from '@/workflow/workflow-steps/components/SidePanelWorkflowSelectStepTitle'; @@ -10,7 +11,6 @@ import { HUMAN_INPUT_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/ import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions'; import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useLingui } from '@lingui/react/macro'; import { IconFunction } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; @@ -33,7 +33,7 @@ export const SidePanelWorkflowSelectAction = ({ const { t } = useLingui(); - const logicFunctions = useAtomStateValue(logicFunctionsState); + const logicFunctions = useAtomStateValue(logicFunctionsSelector); const toolFunctions = logicFunctions.filter((fn) => fn.isTool === true); diff --git a/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx b/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx index 0779e2fa17..2b09494022 100644 --- a/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx +++ b/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx @@ -1,10 +1,15 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { usePersistLogicFunction } from '@/logic-functions/hooks/usePersistLogicFunction'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; import { SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; +import { getSettingsPath, isDefined } from 'twenty-shared/utils'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { FeatureFlagKey } from '~/generated-metadata/graphql'; @@ -12,18 +17,26 @@ import { t } from '@lingui/core/macro'; import { IconChartBar, IconCpu, + IconPlus, IconSettingsBolt, IconSparkles, IconTool, } from 'twenty-ui/display'; +import { Button } from 'twenty-ui/input'; +import { UndecoratedLink } from 'twenty-ui/navigation'; import { SettingsAIMoreTab } from '~/pages/settings/ai/components/SettingsAIMoreTab'; +import { SettingsAgentToolsTab } from '~/pages/settings/ai/components/SettingsAgentToolsTab'; import { SettingsAIModelsTab } from './components/SettingsAIModelsTab'; import { SettingsAIUsageTab } from './components/SettingsAIUsageTab'; import { SettingsAgentSkills } from './components/SettingsAgentSkills'; -import { SettingsToolsTable } from './components/SettingsToolsTable'; import { SETTINGS_AI_TABS } from './constants/SettingsAiTabs'; export const SettingsAI = () => { + const navigate = useNavigate(); + const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar(); + const { createLogicFunction } = usePersistLogicFunction(); + const [isCreatingTool, setIsCreatingTool] = useState(false); + const activeTabId = useAtomComponentStateValue( activeTabIdComponentState, SETTINGS_AI_TABS.COMPONENT_INSTANCE_ID, @@ -33,6 +46,44 @@ export const SettingsAI = () => { FeatureFlagKey.IS_USAGE_ANALYTICS_ENABLED, ); + const handleCreateTool = async () => { + setIsCreatingTool(true); + try { + const result = await createLogicFunction({ + input: { + name: 'new-tool', + isTool: true, + }, + }); + + if (result.status === 'successful' && isDefined(result.response?.data)) { + const newLogicFunction = result.response.data.createOneLogicFunction; + enqueueSuccessSnackBar({ message: t`Tool created` }); + + const applicationId = (newLogicFunction as { applicationId?: string }) + .applicationId; + if (isDefined(applicationId)) { + navigate( + getSettingsPath(SettingsPath.ApplicationLogicFunctionDetail, { + applicationId, + logicFunctionId: newLogicFunction.id, + }), + ); + } else { + navigate( + getSettingsPath(SettingsPath.LogicFunctionDetail, { + logicFunctionId: newLogicFunction.id, + }), + ); + } + } else { + enqueueErrorSnackBar({ message: t`Failed to create tool` }); + } + } finally { + setIsCreatingTool(false); + } + }; + const tabs = [ { id: SETTINGS_AI_TABS.TABS_IDS.MODELS, @@ -74,6 +125,27 @@ export const SettingsAI = () => { return ( +