Skip to content

Commit a02c8ed

Browse files
committed
feat: ai conversation (wip)
1 parent 00b49d5 commit a02c8ed

File tree

4 files changed

+549
-0
lines changed

4 files changed

+549
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
import { gql } from '@apollo/client'
2+
import { Alert, Button, Modal } from 'antd'
3+
import { useCallback, useMemo, useState } from 'react'
4+
import { Integration, IntegrationAccount, SendPromptPayload } from '../../graphql'
5+
import { useCreateWorkflowPrompt } from '../../src/services/AiHooks'
6+
import { useGetIntegrationActions } from '../../src/services/IntegrationActionHooks'
7+
import { useGetIntegrationTriggerById } from '../../src/services/IntegrationTriggerHooks'
8+
import { Loading } from '../common/RequestStates/Loading'
9+
import { SelectCredentials } from '../workflow-nodes/drawer/steps/credentials/SelectCredentials'
10+
11+
interface Props {
12+
promptData: SendPromptPayload
13+
visible: boolean
14+
onCreateWorkflow: (workflowId: string) => any
15+
onClose: () => any
16+
}
17+
18+
const workflowFragment = gql`
19+
fragment ForkWorkflowModal on Workflow {
20+
id
21+
templateSchema
22+
trigger {
23+
id
24+
inputs
25+
integrationTrigger {
26+
id
27+
skipAuth
28+
integration {
29+
id
30+
name
31+
logo
32+
integrationAccount {
33+
...SelectCredentials_IntegrationAccount
34+
}
35+
}
36+
}
37+
}
38+
actions {
39+
edges {
40+
node {
41+
id
42+
inputs
43+
integrationAction {
44+
id
45+
skipAuth
46+
integration {
47+
id
48+
name
49+
logo
50+
integrationAccount {
51+
...SelectCredentials_IntegrationAccount
52+
}
53+
}
54+
}
55+
}
56+
}
57+
}
58+
}
59+
${SelectCredentials.fragments.IntegrationAccount}
60+
`
61+
62+
const integrationTriggerFragment = gql`
63+
fragment ForkWorkflowModal_IntegrationTrigger on IntegrationTrigger {
64+
id
65+
schemaRequest
66+
integration {
67+
id
68+
logo
69+
integrationAccount {
70+
id
71+
}
72+
}
73+
}
74+
`
75+
76+
const integrationActionFragment = gql`
77+
fragment ForkWorkflowModal_IntegrationAction on IntegrationAction {
78+
id
79+
schemaRequest
80+
integration {
81+
id
82+
logo
83+
integrationAccount {
84+
id
85+
}
86+
}
87+
}
88+
`
89+
90+
export const CreateWorkflowModal = ({ promptData, visible, onCreateWorkflow: onWorkflowFork, onClose }: Props) => {
91+
const [createLoading, setCreateLoading] = useState(false)
92+
const [createError, setCreateError] = useState<Error | null>(null)
93+
const [createWorkflowPrompt] = useCreateWorkflowPrompt()
94+
const [templateInputs, setTemplateInputs] = useState<Record<string, any>>()
95+
const [credentialIds, setCredentialIds] = useState<Record<string, string>>({})
96+
97+
const { data: integrationTriggerData } = useGetIntegrationTriggerById(integrationTriggerFragment, {
98+
variables: {
99+
id: promptData.trigger.id,
100+
},
101+
})
102+
const integrationTrigger = integrationTriggerData?.integrationTrigger
103+
104+
const { data: integrationActionsData } = useGetIntegrationActions(integrationActionFragment, {
105+
skip: !promptData.actions.length,
106+
variables: {
107+
filter: {
108+
id: {
109+
in: promptData.actions.map((item) => item.id),
110+
},
111+
},
112+
},
113+
})
114+
const integrationActions = useMemo(
115+
() => integrationActionsData?.integrationActions?.edges.map((item) => item.node) || [],
116+
[integrationActionsData?.integrationActions?.edges],
117+
)
118+
119+
const handleCreateWorkflow = async () => {
120+
// check if all required credentials are selected
121+
const noConnectedAccounts = integrationsWithAccounts.filter((item) => !credentialIds[item.account.id])
122+
if (noConnectedAccounts.length) {
123+
setCreateError(new Error(`Please connect ${noConnectedAccounts.map((item) => item.account.name).join(' and ')}.`))
124+
return
125+
}
126+
127+
setCreateError(null)
128+
setCreateLoading(true)
129+
try {
130+
const res = await createWorkflowPrompt({ variables: { id: promptData.id, credentialIds } })
131+
const data = res.data?.createWorkflowPrompt
132+
if (!data?.id) {
133+
throw new Error('Unexpected error, please try again')
134+
}
135+
onWorkflowFork(data.id)
136+
} catch (e) {
137+
setCreateError(e as Error)
138+
}
139+
setCreateLoading(false)
140+
}
141+
142+
const handleTemplateInputsChange = useCallback(
143+
(inputs: Record<string, any>) => {
144+
setTemplateInputs({
145+
...templateInputs,
146+
...inputs,
147+
})
148+
setCreateError(null)
149+
},
150+
[templateInputs],
151+
)
152+
153+
const handleCredentialSelect = useCallback(
154+
(account: IntegrationAccount, id: string) => {
155+
if (credentialIds[account.id] !== id) {
156+
setCredentialIds({
157+
...credentialIds,
158+
[account.id]: id,
159+
})
160+
setCreateError(null)
161+
}
162+
},
163+
[credentialIds],
164+
)
165+
166+
// list of unique integrations that require accounts
167+
const integrationsWithAccounts = useMemo(() => {
168+
if (!integrationTrigger) {
169+
return []
170+
}
171+
let integrations: { integration: Integration; account: IntegrationAccount }[] = []
172+
if (integrationTrigger?.integration?.integrationAccount?.id && !integrationTrigger.skipAuth) {
173+
integrations.push({
174+
integration: integrationTrigger.integration,
175+
account: integrationTrigger.integration.integrationAccount,
176+
})
177+
}
178+
for (const integrationAction of integrationActions) {
179+
if (integrationAction?.integration?.integrationAccount?.id && !integrationAction.skipAuth) {
180+
if (!integrations.some((a) => a.integration.id === integrationAction.integration.id)) {
181+
integrations.push({
182+
integration: integrationAction.integration,
183+
account: integrationAction.integration.integrationAccount,
184+
})
185+
}
186+
}
187+
}
188+
return integrations
189+
}, [integrationActions, integrationTrigger])
190+
191+
const isLoading = createLoading
192+
193+
return (
194+
<Modal
195+
title={`Create Workflow`}
196+
open={visible}
197+
onCancel={onClose}
198+
footer={null}
199+
width={Math.min(window.innerWidth, 800)}
200+
>
201+
{createError && (
202+
<Alert
203+
style={{ marginBottom: 16 }}
204+
message="Error"
205+
description={createError?.message}
206+
type="error"
207+
showIcon
208+
closable
209+
onClose={() => setCreateError(null)}
210+
/>
211+
)}
212+
{createLoading && <Loading />}
213+
{integrationsWithAccounts.map((item, i) => (
214+
<div className="mb-8 border-l-4 border-indigo-500" key={i}>
215+
<div className="flex flex-row gap-2 mb-2 ">
216+
{item.integration.logo && (
217+
<img src={item.integration.logo} width={24} height={24} alt={item.integration.name} />
218+
)}
219+
<strong>{item.integration.name} Account</strong>
220+
</div>
221+
<SelectCredentials
222+
integrationAccount={item.account}
223+
onCredentialsSelected={(id) => handleCredentialSelect(item.account, id)}
224+
hideNameInput
225+
hideSubmitButton
226+
/>
227+
</div>
228+
))}
229+
{/* {!templateSchemaEmpty && (
230+
<SchemaForm
231+
schema={templateSchema}
232+
initialInputs={templateInputs ?? {}}
233+
onChange={handleTemplateInputsChange}
234+
onSubmit={handleFork}
235+
loading={createLoading}
236+
submitButtonText={workflow.isTemplate ? 'Use Template' : 'Fork'}
237+
/>
238+
)} */}
239+
{!isLoading && (
240+
<Button type="primary" key="deploy" onClick={() => handleCreateWorkflow()} loading={createLoading}>
241+
Create Workflow
242+
</Button>
243+
)}
244+
</Modal>
245+
)
246+
}

graphql.ts

+29
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,33 @@ export interface AccountCredentialConnection {
12021202
edges: AccountCredentialEdge[];
12031203
}
12041204

1205+
export interface ActionSendPrompt {
1206+
integrationId: string;
1207+
integrationName: string;
1208+
integrationLogo?: Nullable<string>;
1209+
id: string;
1210+
name: string;
1211+
description?: Nullable<string>;
1212+
inputs: JSONObject;
1213+
}
1214+
1215+
export interface TriggerSendPrompt {
1216+
integrationId: string;
1217+
integrationName: string;
1218+
integrationLogo?: Nullable<string>;
1219+
id: string;
1220+
name: string;
1221+
description?: Nullable<string>;
1222+
inputs: JSONObject;
1223+
integrationAccountId?: Nullable<string>;
1224+
}
1225+
1226+
export interface SendPromptPayload {
1227+
id: string;
1228+
trigger: TriggerSendPrompt;
1229+
actions: ActionSendPrompt[];
1230+
}
1231+
12051232
export interface AsyncSchema {
12061233
schemas: JSONObject;
12071234
schemaExtension: JSONObject;
@@ -1252,6 +1279,8 @@ export interface IQuery {
12521279
}
12531280

12541281
export interface IMutation {
1282+
sendPrompt(prompt: string): SendPromptPayload | Promise<SendPromptPayload>;
1283+
createWorkflowPrompt(id: string, credentialIds: JSONObject): SendPromptPayload | Promise<SendPromptPayload>;
12551284
updateOneUser(input: UpdateOneUserInput): User | Promise<User>;
12561285
createCheckoutSession(priceId: string): UserCheckoutSessionPayload | Promise<UserCheckoutSessionPayload>;
12571286
resumeSubscription(): ResultPayload | Promise<ResultPayload>;

0 commit comments

Comments
 (0)