diff --git a/public/favicon.ico b/public/favicon.ico index a11777c..f416549 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html index 68c5af4..d02eafd 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,6 @@ - React App + RingCentral@Workflow Hub diff --git a/src/AIBot.tsx b/src/AIBot.tsx index e8f2c09..57e433e 100644 --- a/src/AIBot.tsx +++ b/src/AIBot.tsx @@ -23,7 +23,7 @@ export const AIBot: FC<{ Intelligence Admin Portal - diff --git a/src/ChatItem.tsx b/src/ChatItem.tsx index d27e205..b794c80 100644 --- a/src/ChatItem.tsx +++ b/src/ChatItem.tsx @@ -40,7 +40,8 @@ const ChatMessageWrap = styled(Box)<{ isMe: boolean }>(({ isMe }) => ({ marginTop: 4, marginBottom: 4, 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)(() => ({ diff --git a/src/Create.tsx b/src/Create.tsx index b57e9b6..42b07f2 100644 --- a/src/Create.tsx +++ b/src/Create.tsx @@ -12,10 +12,8 @@ import Button from '@mui/material/Button'; import TextField from '@mui/material/TextField'; import SendIcon from '@mui/icons-material/Send'; import List from '@mui/material/List'; -import Chip from '@mui/material/Chip'; import Divider from '@mui/material/Divider'; import { ChatItem } from "./ChatItem"; -import Stack from '@mui/material/Stack'; import { cloneDeep, uniqueId } from 'lodash'; import Typography from "@mui/material/Typography"; import { useLocation, useNavigate, useParams } from "react-router-dom"; @@ -29,7 +27,11 @@ import { AIBot } from "./AIBot"; import { GlipSender } from "./GlipSender"; import { ScriptBlock } from "./Script"; 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 = [ '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 } = { bot: '', script: '', - email: '', + email: '{"to":"","subject":"","content":"${localData.Desc}"}', glip: '' }; @@ -66,7 +68,48 @@ interface Message { 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 }) => { const { id } = useParams(); @@ -88,22 +131,16 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { const flowDefinition = useMemo(() => DSL2Json(workflowContent), [workflowContent]); const previewImg = useMemo(() => Json2Preview(flowDefinition), [flowDefinition]); const chatContentRef = useRef(null); - const actions = useMemo(() => ((flowDefinition || {}).states || []).reduce>(() => ((flowDefinition || {}).states || []).reduce>((l, s) => { + }>>((l, s) => { l.push(...s.actions.map(a => ({ ...a, stateName: s.name }))); return l; }, []), [flowDefinition]); - const hasEmailSender = useMemo(() => actions.some(a => a.actionType === "EmailSender"), [actions]); - const botAddinAction = useMemo(() => actions.find(a => a.actionType === "Chat"), [actions]); - const scriptAction = useMemo(() => actions.find(a => a.actionType === "Script"), [actions]); - const glipSenderAction = useMemo(() => actions.find(a => a.actionType === "GlipSender"), [actions]); + const emailSenderAction = useMemo(() => actions.find(a => a.actionType === "EmailSender"), [actions]); + const botAddinAction = useMemo(() => actions.find(a => a.actionType === "Chat"), [actions]); + const scriptAction = useMemo(() => actions.find(a => a.actionType === "Script"), [actions]); + const glipSenderAction = useMemo(() => actions.find(a => a.actionType === "GlipSender"), [actions]); const [aibot, setAIBot] = useState(''); const [script, setScript] = useState(''); const [emailData, setEmailData] = useState({ @@ -112,14 +149,11 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { // eslint-disable-next-line no-template-curly-in-string content: '${localData.Desc}', }); - const [glipSenderData, setGlipSenderData] = useState({ - clientId: '', - clientSecret: '', - // eslint-disable-next-line no-template-curly-in-string - message: '' - }); - const [channelId, setChannelId] = useState('78394499078-4034902020'); + const [glipSenderData, setGlipSenderData] = useState(''); + const [isImageMode, setImageMode] = useState(true); + const creatorId = '4034902020'; const location = useLocation(); + const [teamLink, setTeamLink] = useState(''); const debug = useMemo(() => { return (new URLSearchParams(location.search)).get('debug') === 'true'; }, [location]); @@ -148,9 +182,6 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { setChatInput(e.target.value); }; - const handleClickChip = (text: string) => () => { - setChatInput(s => s + text); - }; const handleSend = async () => { const rId = uniqueId('r'); @@ -185,24 +216,15 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { useEffect(() => { if (glipSenderAction) { let actionValueString = glipSenderAction.message; - let crtValueString = glipSenderData.message; - if (actionValueString !== preValueObj.glip && crtValueString === preValueObj.glip) { - setGlipSenderData({ - clientSecret: '', - clientId: '', - message: actionValueString || '', - }); + if (actionValueString !== preValueObj.glip && glipSenderData === preValueObj.glip) { + setGlipSenderData(actionValueString || ''); preValueObj.glip = actionValueString || ''; } } else { if (preValueObj.glip) { preValueObj.glip = ''; - setGlipSenderData({ - clientSecret: '', - clientId: '', - // eslint-disable-next-line no-template-curly-in-string - message: '${localData.Desc}' - }); + // eslint-disable-next-line no-template-curly-in-string + setGlipSenderData('${localData.Desc}'); } } // 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 }, [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(() => { if (chatContentRef.current) { chatContentRef.current.scrollTop = chatContentRef.current.scrollHeight - chatContentRef.current.offsetHeight; @@ -250,15 +300,7 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { setWorkflowContent(''); }; - useEffect(() => { - if (!hasEmailSender) - setEmailData({ - to: '', - subject: '', - // eslint-disable-next-line no-template-curly-in-string - content: '${localData.Desc}', - }); - }, [hasEmailSender]); + const navigate = useNavigate(); const handleSubmit = async () => { if (!flowDefinition) return; @@ -278,11 +320,14 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { } if (action.actionType === 'GlipSender') { // 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)); + const channelId = (teamLink.replace(/[^\d]+/, '')) + '-' + creatorId; + console.log(channelId); await axios.put(`/bot/workflow/${id}`, { name: formJson.name, content: Json2Yml(newJson) @@ -291,7 +336,7 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { await axios.delete(`/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(() => { navigate(`/${debug ? '?debug=true' : ''}`); }, 3000); @@ -356,6 +401,9 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { } }} autoFocus + InputProps={{ + startAdornment: + }} variant="outlined" focused placeholder="Enter your goal ..." multiline maxRows={3} @@ -372,21 +420,56 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { - - {!previewImg ? <>👋 The flow chart will be previewed here!
😻 Tell the AI robot what you want to - do! : - preview} + + {!previewImg ? +
👋 The flow chart will be previewed here!
😻 Tell the AI robot what + you want to + do!
: + ( + <> + + Raw + setImageMode(checked)}/> + Image + + {isImageMode ? + preview : +
{workflowContent}
+ } + + ) + }
- {!!scriptAction || hasEmailSender || !!botAddinAction ? + {!!scriptAction || !!emailSenderAction || !!botAddinAction || !!glipSenderAction ? - Please fill in the following information related to your workflow nodes. 👇🏻👇🏻 : null} - {hasEmailSender && } + Please fill in the following information related to your workflow nodes. 👇🏻👇🏻 : null} + {!!emailSenderAction && } {!!botAddinAction && } {!!scriptAction && } - {!!glipSenderAction && } + {!!glipSenderAction && + { + if (isTeamLink) { + setTeamLink(value); + } else { + setGlipSenderData(value); + } + }}/>}
@@ -404,7 +487,7 @@ export const Create: FC<{ edit?: boolean }> = ({ edit }) => { borderColor: 'divider', display: { xs: 'flex' }, justifyContent: "flex-end" }}> - + ); diff --git a/src/Editor.tsx b/src/Editor.tsx index f74bffb..de7f6b9 100644 --- a/src/Editor.tsx +++ b/src/Editor.tsx @@ -6,7 +6,7 @@ * */ -import React, { useState } from 'react'; +import React from 'react'; import MonacoEditor from 'react-monaco-editor'; export const Editor: React.FC<{ value: string; onChange: (value: string) => void }> = ({ value, onChange }) => { diff --git a/src/EmailSender.tsx b/src/EmailSender.tsx index a1d336d..61b0059 100644 --- a/src/EmailSender.tsx +++ b/src/EmailSender.tsx @@ -24,15 +24,15 @@ export const EmailSender: FC<{ Email Sender diff --git a/src/GlipSender.tsx b/src/GlipSender.tsx index 81283df..ef73b76 100644 --- a/src/GlipSender.tsx +++ b/src/GlipSender.tsx @@ -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 Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; 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<{ + value: string; debug?: boolean - value: { clientId: string; clientSecret: string; message: string }; - onChange: (value: { - clientId: string; clientSecret: string; message: string - }) => void -}> = ({ value, debug, onChange }) => { + edit?: boolean + id?: string + teamLink: string + onChange: (value: string, isTeamLink?: boolean) => void +}> = ({ value, id,teamLink, debug, edit, onChange }) => { const handleChange = (e: ChangeEvent) => { - onChange({ - ...value, - [e.target.name]: e.target.value - }); + onChange(e.target.value); }; + const [showPassword, setShowPassword] = React.useState(edit ? false : true); + + const handleClickShowPassword = () => setShowPassword((show) => !show); + + const handleMouseDownPassword = (event: React.MouseEvent) => { + event.preventDefault(); + }; + + const [senderType, setSenderType] = useState('default'); + const handleChangeType = (e: SelectChangeEvent) => { + setSenderType(e.target.value); + }; + + const handleChangeTeam = (event: React.ChangeEvent) => { + onChange(event.target.value, true) + }; + + return ( - GlipSender - - WFL Hub will send messages via RingCentral Bot, please fill in: - + GlipSender {!debug && <> - - + + {senderType === 'default' ? <> + + WFL Hub will send message via default bot @David-Bot + + + : + <> + + WFL Hub will send messages via your RingCentral Bot, please + fill in: + + + + + {showPassword ? : } + + + ) + }} + /> + } + } diff --git a/src/List.tsx b/src/List.tsx index 4fef18b..669c2b6 100644 --- a/src/List.tsx +++ b/src/List.tsx @@ -39,6 +39,7 @@ interface Workflow { id: string; name: string; lastModifiedTime: string; + emoji?: string; } export const List = () => { @@ -52,7 +53,10 @@ export const List = () => { total: number workflows: Workflow[] }>('/api/public/wf/config/v1/account/10086/workflows').then(({ data }) => { - setList(data.workflows); + setList(data.workflows.map(it => ({ + ...it, + emoji: getRandomEmoji() + }))); }); }, []); const handleOpen = () => { @@ -84,7 +88,7 @@ export const List = () => { event.preventDefault(); const formData = new FormData(event.currentTarget); 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.post(`/bot/workflow/${showBindModal}/bind`, { channelId }); message.success('Success!'); @@ -105,7 +109,7 @@ export const List = () => { - {getRandomEmoji()} {item.name || 'My Workflow'} + {item.emoji} {item.name || 'My Workflow'} { : - } + }}>Bind : } + {/**/} @@ -147,7 +151,6 @@ export const List = () => { margin="dense" id="name" name="name" - defaultValue="My workflow" label="Workflow Name" fullWidth focused @@ -169,10 +172,17 @@ export const List = () => { + diff --git a/src/index.css b/src/index.css index 0846f2c..6e535b3 100644 --- a/src/index.css +++ b/src/index.css @@ -30,3 +30,7 @@ a { .MuiButton-contained:hover { background-color: rgb(118, 126, 168) !important; } + +.Mui-focused .MuiOutlinedInput-notchedOutline { + /*color: #aab5f1*/ +} diff --git a/src/loadYml.ts b/src/loadYml.ts index 6b8bfb7..0afe03e 100644 --- a/src/loadYml.ts +++ b/src/loadYml.ts @@ -71,7 +71,6 @@ export interface Definition { export function Json2Preview(definition?: Definition): string { if (!definition) return ''; try { - console.log(transformDSL(definition)); return compress(transformDSL(definition), '/svg/'); } catch (e) { console.error(e); diff --git a/src/transform.ts b/src/transform.ts index 940bf73..ed14765 100644 --- a/src/transform.ts +++ b/src/transform.ts @@ -1,13 +1,12 @@ import { Action, Definition, State } from "./loadYml"; -const EVENT_COLOR = "#LightYellow"; -const HTTP_COLOR = "#Peru"; -const SCRIPT_COLOR = "#Olive;text:white"; -const BOT_COLOR = "#Blue;text:white"; -const EMAIL_SENDER_COLOR = "#Red;text:white"; -const CHAT_BOT_COLOR = "#Orange;text:white"; -const GLIP_COLOR = "#Pink;text:white"; -const DEFAULT_COLOR = '#Grey;text;white'; +const EVENT_COLOR = "#IndianRed"; +const HTTP_COLOR = "#Sienna"; +const SCRIPT_COLOR = "#Olive"; +const BOT_COLOR = "#RoyalBlue"; +const EMAIL_SENDER_COLOR = "#Crimson"; +const CHAT_BOT_COLOR = "#OrangeRed"; +const GLIP_COLOR = "#DeepPink"; const EVENT_NAME = ": Event"; const HTTP_NAME = ": Http"; @@ -15,33 +14,28 @@ const SCRIPT_NAME = ": Script"; const BOT_NAME = ": Chat"; const EMAIL_SENDER_NAME = ": EmailSender"; const GLIP_SENDER_NAME = ': GlipSender'; -const EXIT_NAME = ": Exit"; -function isHttp(actions: Action[]): boolean { +export function isHttp(actions: Action[]): boolean { return actions .some((action) => action.actionType === "HttpRequest"); } -function isScript(actions: Action[]): boolean { +export function isScript(actions: Action[]): boolean { return actions .some((action) => action.actionType === "Script"); } -function isChat(actions: Action[]): boolean { +export function isChat(actions: Action[]): boolean { return actions .some((action) => action.actionType === 'ChatBot'); } -function isGlipSender(actions: Action[]): boolean { +export function isGlipSender(actions: Action[]): boolean { return actions .some((action) => action.actionType === 'GlipSender'); } -function isExit(actions: Action[]): boolean { - return actions.some((action) => action.actionType === 'Exit'); -} - -function isEmailSender(actions: Action[]): boolean { +export function isEmailSender(actions: Action[]): boolean { return actions .some((action) => action.actionType === "EmailSender"); } @@ -188,8 +182,22 @@ export function transformDSL(definition: Definition): string { sb += drawStateLine(eventState, state) || ''; } return `@startuml - + skinparam backgroundColor #242425 + skinparam state { + FontColor white + BackgroundColor DimGray + BorderColor #AAB5F1 + ArrowColor #AAB5F1 + ArrowFontColor #AAB5F1 + } + + skinparam activity { + StartColor Silver + EndColor Silver + } + ${sb} + @enduml `; }