Skip to content

Commit f99abf0

Browse files
authored
Merge pull request #25 from FlowiseAI/feature/API
Feature/Add API dialog
2 parents c8ef3c0 + d5b6511 commit f99abf0

File tree

7 files changed

+200
-3
lines changed

7 files changed

+200
-3
lines changed

packages/ui/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"prismjs": "^1.28.0",
2626
"prop-types": "^15.7.2",
2727
"react": "^18.2.0",
28+
"react-code-blocks": "^0.0.9-0",
2829
"react-datepicker": "^4.8.0",
2930
"react-device-detect": "^1.17.0",
3031
"react-dom": "^18.2.0",
+1
Loading
Loading
+1
Loading

packages/ui/src/themes/palette.js

-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,6 @@ export default function themePalette(theme) {
8080
main: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.grey50
8181
},
8282
canvasHeader: {
83-
executionLight: theme.colors?.successLight,
84-
executionDark: theme.colors?.successDark,
8583
deployLight: theme.colors?.primaryLight,
8684
deployDark: theme.colors?.primaryDark,
8785
saveLight: theme.colors?.secondaryLight,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { createPortal } from 'react-dom'
2+
import { useState } from 'react'
3+
import PropTypes from 'prop-types'
4+
5+
import { Tabs, Tab, Dialog, DialogContent, DialogTitle, Box } from '@mui/material'
6+
import { CopyBlock, atomOneDark } from 'react-code-blocks'
7+
import { baseURL } from 'store/constant'
8+
import pythonSVG from 'assets/images/python.svg'
9+
import javascriptSVG from 'assets/images/javascript.svg'
10+
import cURLSVG from 'assets/images/cURL.svg'
11+
12+
function TabPanel(props) {
13+
const { children, value, index, ...other } = props
14+
return (
15+
<div
16+
role='tabpanel'
17+
hidden={value !== index}
18+
id={`attachment-tabpanel-${index}`}
19+
aria-labelledby={`attachment-tab-${index}`}
20+
{...other}
21+
>
22+
{value === index && <Box sx={{ p: 1 }}>{children}</Box>}
23+
</div>
24+
)
25+
}
26+
27+
TabPanel.propTypes = {
28+
children: PropTypes.node,
29+
index: PropTypes.number.isRequired,
30+
value: PropTypes.number.isRequired
31+
}
32+
33+
function a11yProps(index) {
34+
return {
35+
id: `attachment-tab-${index}`,
36+
'aria-controls': `attachment-tabpanel-${index}`
37+
}
38+
}
39+
40+
const APICodeDialog = ({ show, dialogProps, onCancel }) => {
41+
const portalElement = document.getElementById('portal')
42+
const codes = ['Python', 'JavaScript', 'cURL']
43+
const [value, setValue] = useState(0)
44+
45+
const handleChange = (event, newValue) => {
46+
setValue(newValue)
47+
}
48+
49+
const getCode = (codeLang) => {
50+
if (codeLang === 'Python') {
51+
return `import requests
52+
53+
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}"
54+
55+
def query(payload):
56+
response = requests.post(API_URL, json=payload)
57+
return response.json()
58+
59+
output = query({
60+
"question": "Hey, how are you?",
61+
})
62+
`
63+
} else if (codeLang === 'JavaScript') {
64+
return `async function query(data) {
65+
const response = await fetch(
66+
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}",
67+
{
68+
method: "POST",
69+
body: {
70+
"question": "Hey, how are you?"
71+
},
72+
}
73+
);
74+
const result = await response.json();
75+
return result;
76+
}
77+
`
78+
} else if (codeLang === 'cURL') {
79+
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
80+
-X POST \\
81+
-d '{"question": "Hey, how are you?"}'`
82+
}
83+
return ''
84+
}
85+
86+
const getLang = (codeLang) => {
87+
if (codeLang === 'Python') {
88+
return 'python'
89+
} else if (codeLang === 'JavaScript') {
90+
return 'javascript'
91+
} else if (codeLang === 'cURL') {
92+
return 'bash'
93+
}
94+
return 'python'
95+
}
96+
97+
const getSVG = (codeLang) => {
98+
if (codeLang === 'Python') {
99+
return pythonSVG
100+
} else if (codeLang === 'JavaScript') {
101+
return javascriptSVG
102+
} else if (codeLang === 'cURL') {
103+
return cURLSVG
104+
}
105+
return pythonSVG
106+
}
107+
108+
const component = show ? (
109+
<Dialog
110+
open={show}
111+
fullWidth
112+
maxWidth='md'
113+
onClose={onCancel}
114+
aria-labelledby='alert-dialog-title'
115+
aria-describedby='alert-dialog-description'
116+
>
117+
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
118+
{dialogProps.title}
119+
</DialogTitle>
120+
<DialogContent>
121+
<Tabs value={value} onChange={handleChange} aria-label='tabs'>
122+
{codes.map((codeLang, index) => (
123+
<Tab
124+
icon={
125+
<img
126+
style={{ objectFit: 'cover', height: 'auto', width: 'auto', marginLeft: 10 }}
127+
src={getSVG(codeLang)}
128+
alt='code'
129+
/>
130+
}
131+
iconPosition='left'
132+
key={index}
133+
label={codeLang}
134+
{...a11yProps(index)}
135+
></Tab>
136+
))}
137+
</Tabs>
138+
<div style={{ marginTop: 10 }}></div>
139+
{codes.map((codeLang, index) => (
140+
<TabPanel key={index} value={value} index={index}>
141+
<CopyBlock
142+
theme={atomOneDark}
143+
text={getCode(codeLang)}
144+
language={getLang(codeLang)}
145+
showLineNumbers={false}
146+
wrapLines
147+
/>
148+
</TabPanel>
149+
))}
150+
</DialogContent>
151+
</Dialog>
152+
) : null
153+
154+
return createPortal(component, portalElement)
155+
}
156+
157+
APICodeDialog.propTypes = {
158+
show: PropTypes.bool,
159+
dialogProps: PropTypes.object,
160+
onCancel: PropTypes.func
161+
}
162+
163+
export default APICodeDialog

packages/ui/src/views/canvas/CanvasHeader.js

+33-1
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { useTheme } from '@mui/material/styles'
88
import { Avatar, Box, ButtonBase, Typography, Stack, TextField } from '@mui/material'
99

1010
// icons
11-
import { IconSettings, IconChevronLeft, IconDeviceFloppy, IconPencil, IconCheck, IconX } from '@tabler/icons'
11+
import { IconSettings, IconChevronLeft, IconDeviceFloppy, IconPencil, IconCheck, IconX, IconWorldWww } from '@tabler/icons'
1212

1313
// project imports
1414
import Settings from 'views/settings'
1515
import SaveChatflowDialog from 'ui-component/dialog/SaveChatflowDialog'
16+
import APICodeDialog from 'ui-component/dialog/APICodeDialog'
1617

1718
// API
1819
import chatflowsApi from 'api/chatflows'
@@ -35,6 +36,8 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
3536
const [flowName, setFlowName] = useState('')
3637
const [isSettingsOpen, setSettingsOpen] = useState(false)
3738
const [flowDialogOpen, setFlowDialogOpen] = useState(false)
39+
const [apiDialogOpen, setAPIDialogOpen] = useState(false)
40+
const [apiDialogProps, setAPIDialogProps] = useState({})
3841

3942
const updateChatflowApi = useApi(chatflowsApi.updateChatflow)
4043
const canvas = useSelector((state) => state.canvas)
@@ -76,6 +79,14 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
7679
}
7780
}
7881

82+
const onAPIDialogClick = () => {
83+
setAPIDialogProps({
84+
title: 'Use this chatflow with API',
85+
chatflowid: chatflow.id
86+
})
87+
setAPIDialogOpen(true)
88+
}
89+
7990
const onSaveChatflowClick = () => {
8091
if (chatflow.id) handleSaveFlow(chatflow.name)
8192
else setFlowDialogOpen(true)
@@ -219,6 +230,26 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
219230
)}
220231
</Box>
221232
<Box>
233+
<ButtonBase title='API Endpoint' sx={{ borderRadius: '50%', mr: 2 }}>
234+
<Avatar
235+
variant='rounded'
236+
sx={{
237+
...theme.typography.commonAvatar,
238+
...theme.typography.mediumAvatar,
239+
transition: 'all .2s ease-in-out',
240+
background: theme.palette.canvasHeader.deployLight,
241+
color: theme.palette.canvasHeader.deployDark,
242+
'&:hover': {
243+
background: theme.palette.canvasHeader.deployDark,
244+
color: theme.palette.canvasHeader.deployLight
245+
}
246+
}}
247+
color='inherit'
248+
onClick={onAPIDialogClick}
249+
>
250+
<IconWorldWww stroke={1.5} size='1.3rem' />
251+
</Avatar>
252+
</ButtonBase>
222253
<ButtonBase title='Save Chatflow' sx={{ borderRadius: '50%', mr: 2 }}>
223254
<Avatar
224255
variant='rounded'
@@ -277,6 +308,7 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
277308
onCancel={() => setFlowDialogOpen(false)}
278309
onConfirm={onConfirmSaveName}
279310
/>
311+
<APICodeDialog show={apiDialogOpen} dialogProps={apiDialogProps} onCancel={() => setAPIDialogOpen(false)} />
280312
</>
281313
)
282314
}

0 commit comments

Comments
 (0)