feat: update

This commit is contained in:
Eleanor Mao 2024-04-15 09:54:59 +08:00
parent 20481ca406
commit 6511dc8941
12 changed files with 277 additions and 120 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -7,7 +7,6 @@
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
content="Web site created using create-react-app"
/> />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- <!--
@ -25,7 +24,7 @@
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<script src="https://cdn-0.plantuml.com/synchro2.min.js"></script> <script src="https://cdn-0.plantuml.com/synchro2.min.js"></script>
<title>React App</title> <title>RingCentral@Workflow Hub</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -23,7 +23,7 @@ export const AIBot: FC<{
Intelligence Admin Portal</a> Intelligence Admin Portal</a>
</Typography> </Typography>
<Box> <Box>
<TextField label="AI Robot ID" onChange={handleChange} value={value} <TextField label="AI Robot ID" fullWidth onChange={handleChange} value={value}
/> />
</Box> </Box>
</Paper> </Paper>

View File

@ -40,7 +40,8 @@ const ChatMessageWrap = styled(Box)<{ isMe: boolean }>(({ isMe }) => ({
marginTop: 4, marginTop: 4,
marginBottom: 4, marginBottom: 4,
display: 'flex', display: 'flex',
justifyContent: isMe ? 'flex-end' : 'flex-start' justifyContent: isMe ? 'flex-end' : 'flex-start',
color: isMe ? 'rgba(0, 0, 0, 0.87)' : 'white'
})); }));
const ChatMessage = styled(Box)(() => ({ const ChatMessage = styled(Box)(() => ({

View File

@ -12,10 +12,8 @@ import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField'; import TextField from '@mui/material/TextField';
import SendIcon from '@mui/icons-material/Send'; import SendIcon from '@mui/icons-material/Send';
import List from '@mui/material/List'; import List from '@mui/material/List';
import Chip from '@mui/material/Chip';
import Divider from '@mui/material/Divider'; import Divider from '@mui/material/Divider';
import { ChatItem } from "./ChatItem"; import { ChatItem } from "./ChatItem";
import Stack from '@mui/material/Stack';
import { cloneDeep, uniqueId } from 'lodash'; import { cloneDeep, uniqueId } from 'lodash';
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { useLocation, useNavigate, useParams } from "react-router-dom"; import { useLocation, useNavigate, useParams } from "react-router-dom";
@ -29,7 +27,11 @@ import { AIBot } from "./AIBot";
import { GlipSender } from "./GlipSender"; import { GlipSender } from "./GlipSender";
import { ScriptBlock } from "./Script"; import { ScriptBlock } from "./Script";
import { message } from 'mui-message'; import { message } from 'mui-message';
import { isChatBot } from "./transform"; import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
import InputAdornment from "@mui/material/InputAdornment";
import Stack from '@mui/material/Stack';
import Switch from '@mui/material/Switch';
import { styled } from "@mui/material/styles";
const DefaultMessage = [ const DefaultMessage = [
'Hello! Please tell me what kind of workflow you want to generate!\n', 'Hello! Please tell me what kind of workflow you want to generate!\n',
@ -55,7 +57,7 @@ type PreValue = string | undefined | null
const preValueObj: { bot: PreValue; script: PreValue; email: PreValue; glip: PreValue } = { const preValueObj: { bot: PreValue; script: PreValue; email: PreValue; glip: PreValue } = {
bot: '', bot: '',
script: '', script: '',
email: '', email: '{"to":"","subject":"","content":"${localData.Desc}"}',
glip: '' glip: ''
}; };
@ -66,7 +68,48 @@ interface Message {
loading?: boolean; loading?: boolean;
} }
const SupportedBlock = ['BotMessage', 'Chat', 'GlipSender', 'Script']; interface ActionWithStateName extends Action {
stateName: string;
}
const AntSwitch = styled(Switch)(({ theme }) => ({
width: 28,
height: 16,
padding: 0,
display: 'flex',
'&:active': {
'& .MuiSwitch-thumb': {
width: 15,
},
'& .MuiSwitch-switchBase.Mui-checked': {
transform: 'translateX(9px)',
},
},
'& .MuiSwitch-switchBase': {
padding: 2,
'&.Mui-checked': {
transform: 'translateX(12px)',
color: '#fff',
'& + .MuiSwitch-track': {
opacity: 1,
},
},
},
'& .MuiSwitch-thumb': {
boxShadow: '0 2px 4px 0 rgb(0 35 11 / 20%)',
width: 12,
height: 12,
borderRadius: 6,
transition: theme.transitions.create(['width'], {
duration: 200,
}),
},
'& .MuiSwitch-track': {
borderRadius: 16 / 2,
opacity: 1,
boxSizing: 'border-box',
},
}));
export const Create: FC<{ edit?: boolean }> = ({ edit }) => { export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
const { id } = useParams(); const { id } = useParams();
@ -88,22 +131,16 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
const flowDefinition = useMemo<Definition | undefined>(() => DSL2Json(workflowContent), [workflowContent]); const flowDefinition = useMemo<Definition | undefined>(() => DSL2Json(workflowContent), [workflowContent]);
const previewImg = useMemo<string>(() => Json2Preview(flowDefinition), [flowDefinition]); const previewImg = useMemo<string>(() => Json2Preview(flowDefinition), [flowDefinition]);
const chatContentRef = useRef<HTMLUListElement>(null); const chatContentRef = useRef<HTMLUListElement>(null);
const actions = useMemo<Array<Action & { const actions = useMemo<ActionWithStateName[]>(() => ((flowDefinition || {}).states || []).reduce<Array<Action & {
stateName: string stateName: string
}>>(() => ((flowDefinition || {}).states || []).reduce<Array<Action & { stateName: string }>>((l, s) => { }>>((l, s) => {
l.push(...s.actions.map(a => ({ ...a, stateName: s.name }))); l.push(...s.actions.map(a => ({ ...a, stateName: s.name })));
return l; return l;
}, []), [flowDefinition]); }, []), [flowDefinition]);
const hasEmailSender = useMemo(() => actions.some(a => a.actionType === "EmailSender"), [actions]); const emailSenderAction = useMemo<ActionWithStateName | undefined>(() => actions.find(a => a.actionType === "EmailSender"), [actions]);
const botAddinAction = useMemo<Action & { const botAddinAction = useMemo<ActionWithStateName | undefined>(() => actions.find(a => a.actionType === "Chat"), [actions]);
stateName: string const scriptAction = useMemo<ActionWithStateName | undefined>(() => actions.find(a => a.actionType === "Script"), [actions]);
} | undefined>(() => actions.find(a => a.actionType === "Chat"), [actions]); const glipSenderAction = useMemo<ActionWithStateName | undefined>(() => actions.find(a => a.actionType === "GlipSender"), [actions]);
const scriptAction = useMemo<Action & {
stateName: string
} | undefined>(() => actions.find(a => a.actionType === "Script"), [actions]);
const glipSenderAction = useMemo<Action & {
stateName: string
} | undefined>(() => actions.find(a => a.actionType === "GlipSender"), [actions]);
const [aibot, setAIBot] = useState(''); const [aibot, setAIBot] = useState('');
const [script, setScript] = useState(''); const [script, setScript] = useState('');
const [emailData, setEmailData] = useState<EmailValue>({ const [emailData, setEmailData] = useState<EmailValue>({
@ -112,14 +149,11 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
// eslint-disable-next-line no-template-curly-in-string // eslint-disable-next-line no-template-curly-in-string
content: '${localData.Desc}', content: '${localData.Desc}',
}); });
const [glipSenderData, setGlipSenderData] = useState({ const [glipSenderData, setGlipSenderData] = useState('');
clientId: '', const [isImageMode, setImageMode] = useState(true);
clientSecret: '', const creatorId = '4034902020';
// eslint-disable-next-line no-template-curly-in-string
message: ''
});
const [channelId, setChannelId] = useState('78394499078-4034902020');
const location = useLocation(); const location = useLocation();
const [teamLink, setTeamLink] = useState('');
const debug = useMemo(() => { const debug = useMemo(() => {
return (new URLSearchParams(location.search)).get('debug') === 'true'; return (new URLSearchParams(location.search)).get('debug') === 'true';
}, [location]); }, [location]);
@ -148,9 +182,6 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
setChatInput(e.target.value); setChatInput(e.target.value);
}; };
const handleClickChip = (text: string) => () => {
setChatInput(s => s + text);
};
const handleSend = async () => { const handleSend = async () => {
const rId = uniqueId('r'); const rId = uniqueId('r');
@ -185,24 +216,15 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
useEffect(() => { useEffect(() => {
if (glipSenderAction) { if (glipSenderAction) {
let actionValueString = glipSenderAction.message; let actionValueString = glipSenderAction.message;
let crtValueString = glipSenderData.message; if (actionValueString !== preValueObj.glip && glipSenderData === preValueObj.glip) {
if (actionValueString !== preValueObj.glip && crtValueString === preValueObj.glip) { setGlipSenderData(actionValueString || '');
setGlipSenderData({
clientSecret: '',
clientId: '',
message: actionValueString || '',
});
preValueObj.glip = actionValueString || ''; preValueObj.glip = actionValueString || '';
} }
} else { } else {
if (preValueObj.glip) { if (preValueObj.glip) {
preValueObj.glip = ''; preValueObj.glip = '';
setGlipSenderData({
clientSecret: '',
clientId: '',
// eslint-disable-next-line no-template-curly-in-string // eslint-disable-next-line no-template-curly-in-string
message: '${localData.Desc}' setGlipSenderData('${localData.Desc}');
});
} }
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@ -232,7 +254,35 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [botAddinAction]); }, [botAddinAction]);
useEffect(() => {
if (emailSenderAction) {
const newValueStr = JSON.stringify({
to: emailSenderAction.to || '',
subject: emailSenderAction.subject || '',
content: emailSenderAction.content || ''
});
const crtValueStr = JSON.stringify({
to: emailData.to,
subject: emailData.subject,
content: emailData.content
});
if (newValueStr !== preValueObj.email && crtValueStr === preValueObj.email) {
setEmailData({
to: emailSenderAction.to || '',
subject: emailSenderAction.subject || '',
content: emailSenderAction.content || ''
});
preValueObj.email = newValueStr || '';
}
} else {
setEmailData({
to: '',
subject: '',
// eslint-disable-next-line no-template-curly-in-string
content: '${localData.Desc}',
});
}
}, [emailSenderAction]);
useEffect(() => { useEffect(() => {
if (chatContentRef.current) { if (chatContentRef.current) {
chatContentRef.current.scrollTop = chatContentRef.current.scrollHeight - chatContentRef.current.offsetHeight; chatContentRef.current.scrollTop = chatContentRef.current.scrollHeight - chatContentRef.current.offsetHeight;
@ -250,15 +300,7 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
setWorkflowContent(''); setWorkflowContent('');
}; };
useEffect(() => {
if (!hasEmailSender)
setEmailData({
to: '',
subject: '',
// eslint-disable-next-line no-template-curly-in-string
content: '${localData.Desc}',
});
}, [hasEmailSender]);
const navigate = useNavigate(); const navigate = useNavigate();
const handleSubmit = async () => { const handleSubmit = async () => {
if (!flowDefinition) return; if (!flowDefinition) return;
@ -278,11 +320,14 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
} }
if (action.actionType === 'GlipSender') { if (action.actionType === 'GlipSender') {
// action.url = glipSenderData.url; // action.url = glipSenderData.url;
action.message = glipSenderData.message || '${localData.Desc}'; // eslint-disable-next-line no-template-curly-in-string
action.message = glipSenderData || '${localData.Desc}';
} }
}); });
}); });
console.log(Json2Yml(newJson)); console.log(Json2Yml(newJson));
const channelId = (teamLink.replace(/[^\d]+/, '')) + '-' + creatorId;
console.log(channelId);
await axios.put(`/bot/workflow/${id}`, { await axios.put(`/bot/workflow/${id}`, {
name: formJson.name, name: formJson.name,
content: Json2Yml(newJson) content: Json2Yml(newJson)
@ -291,7 +336,7 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
await axios.delete(`/bot/workflow/${id}/bind/${channelId}`); await axios.delete(`/bot/workflow/${id}/bind/${channelId}`);
await axios.post(`/bot/workflow/${id}/bind`, { channelId }); await axios.post(`/bot/workflow/${id}/bind`, { channelId });
} }
message.success(`Saved! ${debug ? 'Back to List and go bind your channel!' : ''}`); message.success(`Publish Success! ${debug ? 'Back to List and go bind your channel!' : ''}`);
setTimeout(() => { setTimeout(() => {
navigate(`/${debug ? '?debug=true' : ''}`); navigate(`/${debug ? '?debug=true' : ''}`);
}, 3000); }, 3000);
@ -356,6 +401,9 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
} }
}} }}
autoFocus autoFocus
InputProps={{
startAdornment: <InputAdornment position="start"><AutoAwesomeIcon color="primary"/></InputAdornment>
}}
variant="outlined" variant="outlined"
focused placeholder="Enter your goal ..." focused placeholder="Enter your goal ..."
multiline maxRows={3} multiline maxRows={3}
@ -372,21 +420,56 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
</Box> </Box>
</Grid> </Grid>
<Grid item xs={4}> <Grid item xs={4}>
<Box sx={{ border: 1, height: 600, borderRadius: 1, p: 2, mt: 4, borderColor: 'rgba(255,255,255,0.23)' }}> <Box sx={{
{!previewImg ? <>👋 The flow chart will be previewed here! <br/>😻 Tell the AI robot what you want to border: 1,
do! </> : height: 600,
<img src={previewImg} style={{ width: '100%', height: '100%' }} alt="preview"/>} borderRadius: 1,
mt: 4,
borderColor: 'rgba(255,255,255,0.23)',
textAlign: 'center'
}}>
{!previewImg ?
<div style={{ padding: 16 }}>👋 The flow chart will be previewed here! <br/>😻 Tell the AI robot what
you want to
do! </div> :
(
<>
<Stack sx={{ p: 2 }} direction="row" spacing={1} alignItems="center" justifyContent="flex-end">
<Typography variant="body2">Raw</Typography>
<AntSwitch checked={isImageMode} onChange={(_, checked) => setImageMode(checked)}/>
<Typography variant="body2">Image</Typography>
</Stack>
{isImageMode ?
<img src={previewImg} style={{ maxWidth: '100%', maxHeight: '100%' }} alt="preview"/> :
<pre style={{ height: 546, overflow: 'auto', marginTop: 0 }}><code style={{
fontSize: 12,
textAlign: 'left',
display: 'block',
padding: 16,
}}>{workflowContent}</code></pre>
}
</>
)
}
</Box> </Box>
</Grid> </Grid>
</Grid> </Grid>
<Divider sx={{ mt: 4, mb: 4 }}/> <Divider sx={{ mt: 4, mb: 4 }}/>
{!!scriptAction || hasEmailSender || !!botAddinAction ? {!!scriptAction || !!emailSenderAction || !!botAddinAction || !!glipSenderAction ?
<Typography variant="subtitle2" sx={{ mb: 2 }}> <Typography variant="subtitle2" sx={{ mb: 2 }}>
Please fill in the following information related to your workflow nodes. 👇🏻👇🏻 </Typography> : null} Please fill in the following information related to your workflow nodes. 👇🏻👇🏻 </Typography> : null}
{hasEmailSender && <EmailSender value={emailData} onChange={setEmailData}/>} {!!emailSenderAction && <EmailSender value={emailData} onChange={setEmailData}/>}
{!!botAddinAction && <AIBot name={botAddinAction.stateName} value={aibot} onChange={setAIBot}/>} {!!botAddinAction && <AIBot name={botAddinAction.stateName} value={aibot} onChange={setAIBot}/>}
{!!scriptAction && <ScriptBlock name={scriptAction.stateName} value={script} onChange={setScript}/>} {!!scriptAction && <ScriptBlock name={scriptAction.stateName} value={script} onChange={setScript}/>}
{!!glipSenderAction && <GlipSender debug={debug} value={glipSenderData} onChange={setGlipSenderData}/>} {!!glipSenderAction &&
<GlipSender debug={debug} teamLink={teamLink} id={id} edit={edit} value={glipSenderData}
onChange={(value, isTeamLink) => {
if (isTeamLink) {
setTeamLink(value);
} else {
setGlipSenderData(value);
}
}}/>}
</Grid> </Grid>
</Grid> </Grid>
<div style={{ height: 80 }}></div> <div style={{ height: 80 }}></div>
@ -404,7 +487,7 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => {
borderColor: 'divider', borderColor: 'divider',
display: { xs: 'flex' }, justifyContent: "flex-end" display: { xs: 'flex' }, justifyContent: "flex-end"
}}> }}>
<Button variant="contained" style={{ width: 100 }} onClick={handleSubmit}>Save</Button> <Button variant="contained" style={{ width: 100 }} onClick={handleSubmit}>Publish</Button>
</Box> </Box>
</> </>
); );

View File

@ -6,7 +6,7 @@
* *
*/ */
import React, { useState } from 'react'; import React from 'react';
import MonacoEditor from 'react-monaco-editor'; import MonacoEditor from 'react-monaco-editor';
export const Editor: React.FC<{ value: string; onChange: (value: string) => void }> = ({ value, onChange }) => { export const Editor: React.FC<{ value: string; onChange: (value: string) => void }> = ({ value, onChange }) => {

View File

@ -24,15 +24,15 @@ export const EmailSender: FC<{
<Typography variant="subtitle1" sx={{mb: 1}}>Email Sender</Typography> <Typography variant="subtitle1" sx={{mb: 1}}>Email Sender</Typography>
<Box> <Box>
<TextField label="Send To" placeholder="Please enter your email address" type="email" <TextField label="Send To" placeholder="Please enter your email address" type="email"
value={value} name="to" fullWidth sx={{mb: 4}} value={value.to} name="to" fullWidth sx={{mb: 4}}
onChange={handleChange} onChange={handleChange}
/> />
<TextField label="Subject" placeholder="Please enter your email subject" <TextField label="Subject" placeholder="Please enter your email subject"
value={value} name="subject" fullWidth sx={{mb: 4}} value={value.subject} name="subject" fullWidth sx={{mb: 4}}
onChange={handleChange} onChange={handleChange}
/> />
<TextField label="Content" placeholder="Please enter your email content" <TextField label="Content" placeholder="Please enter your email content"
value={value} name="content" fullWidth sx={{mb: 4}} value={value.content} name="content" fullWidth sx={{mb: 4}}
onChange={handleChange} maxRows={8} minRows={3} multiline onChange={handleChange} maxRows={8} minRows={3} multiline
/> />
</Box> </Box>

View File

@ -1,41 +1,94 @@
import React, { ChangeEvent, FC, useEffect, useState } from 'react'; import React, { ChangeEvent, FC, useState } from 'react';
import Paper from '@mui/material/Paper'; import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import TextField from '@mui/material/TextField'; import TextField from '@mui/material/TextField';
import InputAdornment from "@mui/material/InputAdornment";
import IconButton from '@mui/material/IconButton';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import { MenuItem, Select, SelectChangeEvent } from "@mui/material";
export const GlipSender: FC<{ export const GlipSender: FC<{
value: string;
debug?: boolean debug?: boolean
value: { clientId: string; clientSecret: string; message: string }; edit?: boolean
onChange: (value: { id?: string
clientId: string; clientSecret: string; message: string teamLink: string
}) => void onChange: (value: string, isTeamLink?: boolean) => void
}> = ({ value, debug, onChange }) => { }> = ({ value, id,teamLink, debug, edit, onChange }) => {
const handleChange = (e: ChangeEvent<HTMLInputElement>) => { const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
onChange({ onChange(e.target.value);
...value,
[e.target.name]: e.target.value
});
}; };
const [showPassword, setShowPassword] = React.useState(edit ? false : true);
const handleClickShowPassword = () => setShowPassword((show) => !show);
const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
};
const [senderType, setSenderType] = useState('default');
const handleChangeType = (e: SelectChangeEvent) => {
setSenderType(e.target.value);
};
const handleChangeTeam = (event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event.target.value, true)
};
return ( return (
<Paper sx={{ p: 2, mb: 2 }}> <Paper sx={{ p: 2, mb: 2 }}>
<Typography variant="subtitle1">GlipSender</Typography> <Typography variant="subtitle1" sx={{ mb: 1 }}>GlipSender</Typography>
<Typography variant="caption" component="div" sx={{ mb: 1 }}>
WFL Hub will send messages via <a style={{ color: '#f50057' }}
href="https://developers.ringcentral.com/guide/team-messaging/bots/walkthrough"
target="_blank" rel="noreferrer">RingCentral Bot</a>, please fill in:
</Typography>
<Box> <Box>
{!debug && {!debug &&
<> <>
<TextField label="RingCentral Bot Client ID" fullWidth name="clientId" onChange={handleChange} <Select value={senderType} sx={{ mb: 1 }} onChange={handleChangeType}>
value={value.clientId} sx={{ mb: 4 }}/> <MenuItem value="default">Use built-in bot (only supports team message)</MenuItem>
<TextField label="RingCentral Bot Client Secret" sx={{ mb: 4 }} <MenuItem value="custom">Use custom bot</MenuItem>
value={value.clientSecret} name="clientSecret" fullWidth onChange={handleChange} </Select>
{senderType === 'default' ? <>
<Typography sx={{ mb: 2 }} variant="caption" component="div">
WFL Hub will send message via default bot <a style={{ color: '#f50057' }}>@David-Bot</a>
</Typography>
<TextField label="Team Link" fullWidth name="teamLink" id="teamLink" key="1" sx={{ mb: 4 }} value={teamLink}
onChange={handleChangeTeam}
defaultValue={edit ? 'https://app.ringcentral.com/l/messages/134751420422' : ''}/>
</> :
<>
<Typography variant="caption" component="div" sx={{ mb: 2 }}>
WFL Hub will send messages via your <a style={{ color: '#f50057' }}
href="https://developers.ringcentral.com/guide/team-messaging/bots/walkthrough"
target="_blank" rel="noreferrer">RingCentral Bot</a>, please
fill in:
</Typography>
<TextField label="RingCentral Bot Client ID" fullWidth name="clientId" key="2" id="clientId" sx={{ mb: 4 }}
defaultValue={edit ? '0w3wgTW5dkdfGjdseGDJ2y' : ''}/>
<TextField label="RingCentral Bot Client Secret" sx={{ mb: 4 }} key="3" name="clientSecret" id="clientSecret"
fullWidth
defaultValue={edit ? '6oGJ8Nko0socCF6rzFqKsk7FbNyT33ouDebXsOhACDhp' : ''}
type={showPassword ? 'text' : 'password'}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{showPassword ? <VisibilityOff/> : <Visibility/>}
</IconButton>
</InputAdornment>
)
}}
/> />
</>} </>}
</>}
<TextField label="Message format" placeholder="Please enter your message format" <TextField label="Message format" placeholder="Please enter your message format"
value={value.message} name="message" fullWidth value={value} name="message" fullWidth
onChange={handleChange} maxRows={5} minRows={2} multiline onChange={handleChange} maxRows={5} minRows={2} multiline
/> />
</Box> </Box>

View File

@ -39,6 +39,7 @@ interface Workflow {
id: string; id: string;
name: string; name: string;
lastModifiedTime: string; lastModifiedTime: string;
emoji?: string;
} }
export const List = () => { export const List = () => {
@ -52,7 +53,10 @@ export const List = () => {
total: number total: number
workflows: Workflow[] workflows: Workflow[]
}>('/api/public/wf/config/v1/account/10086/workflows').then(({ data }) => { }>('/api/public/wf/config/v1/account/10086/workflows').then(({ data }) => {
setList(data.workflows); setList(data.workflows.map(it => ({
...it,
emoji: getRandomEmoji()
})));
}); });
}, []); }, []);
const handleOpen = () => { const handleOpen = () => {
@ -84,7 +88,7 @@ export const List = () => {
event.preventDefault(); event.preventDefault();
const formData = new FormData(event.currentTarget); const formData = new FormData(event.currentTarget);
const formJson = Object.fromEntries((formData as any).entries()); const formJson = Object.fromEntries((formData as any).entries());
const channelId = formJson.channelId.trim(); const channelId = `${formJson.groupId.trim()}-${formJson.creatorId.trim()}`;
await axios.delete(`/bot/workflow/${showBindModal}/bind/${channelId}`); await axios.delete(`/bot/workflow/${showBindModal}/bind/${channelId}`);
await axios.post(`/bot/workflow/${showBindModal}/bind`, { channelId }); await axios.post(`/bot/workflow/${showBindModal}/bind`, { channelId });
message.success('Success!'); message.success('Success!');
@ -105,7 +109,7 @@ export const List = () => {
<CardActionArea> <CardActionArea>
<CardContent style={{ height: 100 }}> <CardContent style={{ height: 100 }}>
<Typography gutterBottom variant="h5" component="div"> <Typography gutterBottom variant="h5" component="div">
{getRandomEmoji()} {item.name || 'My Workflow'} {item.emoji} {item.name || 'My Workflow'}
</Typography> </Typography>
</CardContent> </CardContent>
<CardActions <CardActions
@ -121,8 +125,8 @@ export const List = () => {
<Button size="small" onClick={(e: any) => { <Button size="small" onClick={(e: any) => {
e.preventDefault(); e.preventDefault();
handleOpenBind(item.id); handleOpenBind(item.id);
}}>Bind</Button> : }}>Bind</Button> : <Button size="small">Copy</Button>}
<Switch defaultChecked/>} {/*<Switch defaultChecked/>*/}
</CardActions> </CardActions>
</CardActionArea> </CardActionArea>
</Card> </Card>
@ -147,7 +151,6 @@ export const List = () => {
margin="dense" margin="dense"
id="name" id="name"
name="name" name="name"
defaultValue="My workflow"
label="Workflow Name" label="Workflow Name"
fullWidth fullWidth
focused focused
@ -169,10 +172,17 @@ export const List = () => {
<TextField <TextField
required required
margin="dense" margin="dense"
id="channelId" focused id="groupId" focused
variant="outlined" variant="outlined"
label="Channel Id" fullWidth name="channelId" label="Group Id" fullWidth name="groupId"
helperText="ChannelId is ${groupId}-${creatorId}" helperText="You can get team groupId from team link"
/>
<TextField
required
margin="dense"
id="creatorId" focused
variant="outlined"
label="Creator Id" fullWidth name="creatorId"
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>

View File

@ -30,3 +30,7 @@ a {
.MuiButton-contained:hover { .MuiButton-contained:hover {
background-color: rgb(118, 126, 168) !important; background-color: rgb(118, 126, 168) !important;
} }
.Mui-focused .MuiOutlinedInput-notchedOutline {
/*color: #aab5f1*/
}

View File

@ -71,7 +71,6 @@ export interface Definition {
export function Json2Preview(definition?: Definition): string { export function Json2Preview(definition?: Definition): string {
if (!definition) return ''; if (!definition) return '';
try { try {
console.log(transformDSL(definition));
return compress(transformDSL(definition), '/svg/'); return compress(transformDSL(definition), '/svg/');
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -1,13 +1,12 @@
import { Action, Definition, State } from "./loadYml"; import { Action, Definition, State } from "./loadYml";
const EVENT_COLOR = "#LightYellow"; const EVENT_COLOR = "#IndianRed";
const HTTP_COLOR = "#Peru"; const HTTP_COLOR = "#Sienna";
const SCRIPT_COLOR = "#Olive;text:white"; const SCRIPT_COLOR = "#Olive";
const BOT_COLOR = "#Blue;text:white"; const BOT_COLOR = "#RoyalBlue";
const EMAIL_SENDER_COLOR = "#Red;text:white"; const EMAIL_SENDER_COLOR = "#Crimson";
const CHAT_BOT_COLOR = "#Orange;text:white"; const CHAT_BOT_COLOR = "#OrangeRed";
const GLIP_COLOR = "#Pink;text:white"; const GLIP_COLOR = "#DeepPink";
const DEFAULT_COLOR = '#Grey;text;white';
const EVENT_NAME = ": Event"; const EVENT_NAME = ": Event";
const HTTP_NAME = ": Http"; const HTTP_NAME = ": Http";
@ -15,33 +14,28 @@ const SCRIPT_NAME = ": Script";
const BOT_NAME = ": Chat"; const BOT_NAME = ": Chat";
const EMAIL_SENDER_NAME = ": EmailSender"; const EMAIL_SENDER_NAME = ": EmailSender";
const GLIP_SENDER_NAME = ': GlipSender'; const GLIP_SENDER_NAME = ': GlipSender';
const EXIT_NAME = ": Exit";
function isHttp(actions: Action[]): boolean { export function isHttp(actions: Action[]): boolean {
return actions return actions
.some((action) => action.actionType === "HttpRequest"); .some((action) => action.actionType === "HttpRequest");
} }
function isScript(actions: Action[]): boolean { export function isScript(actions: Action[]): boolean {
return actions return actions
.some((action) => action.actionType === "Script"); .some((action) => action.actionType === "Script");
} }
function isChat(actions: Action[]): boolean { export function isChat(actions: Action[]): boolean {
return actions return actions
.some((action) => action.actionType === 'ChatBot'); .some((action) => action.actionType === 'ChatBot');
} }
function isGlipSender(actions: Action[]): boolean { export function isGlipSender(actions: Action[]): boolean {
return actions return actions
.some((action) => action.actionType === 'GlipSender'); .some((action) => action.actionType === 'GlipSender');
} }
function isExit(actions: Action[]): boolean { export function isEmailSender(actions: Action[]): boolean {
return actions.some((action) => action.actionType === 'Exit');
}
function isEmailSender(actions: Action[]): boolean {
return actions return actions
.some((action) => action.actionType === "EmailSender"); .some((action) => action.actionType === "EmailSender");
} }
@ -188,8 +182,22 @@ export function transformDSL(definition: Definition): string {
sb += drawStateLine(eventState, state) || ''; sb += drawStateLine(eventState, state) || '';
} }
return `@startuml return `@startuml
skinparam backgroundColor #242425
skinparam state {
FontColor white
BackgroundColor DimGray
BorderColor #AAB5F1
ArrowColor #AAB5F1
ArrowFontColor #AAB5F1
}
skinparam activity {
StartColor Silver
EndColor Silver
}
${sb} ${sb}
@enduml @enduml
`; `;
} }