feat: s
This commit is contained in:
parent
cd269b82b3
commit
6a0362e18f
@ -18,6 +18,7 @@
|
|||||||
"@types/node": "^16.18.95",
|
"@types/node": "^16.18.95",
|
||||||
"@types/react": "^18.2.74",
|
"@types/react": "^18.2.74",
|
||||||
"@types/react-dom": "^18.2.24",
|
"@types/react-dom": "^18.2.24",
|
||||||
|
"axios": "^1.6.8",
|
||||||
"babel-jest": "^27.4.2",
|
"babel-jest": "^27.4.2",
|
||||||
"babel-loader": "^8.2.3",
|
"babel-loader": "^8.2.3",
|
||||||
"babel-plugin-named-asset-import": "^0.3.8",
|
"babel-plugin-named-asset-import": "^0.3.8",
|
||||||
@ -52,6 +53,7 @@
|
|||||||
"postcss-normalize": "^10.0.1",
|
"postcss-normalize": "^10.0.1",
|
||||||
"postcss-preset-env": "^7.0.1",
|
"postcss-preset-env": "^7.0.1",
|
||||||
"prompts": "^2.4.2",
|
"prompts": "^2.4.2",
|
||||||
|
"qs": "^6.12.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-app-polyfill": "^3.0.0",
|
"react-app-polyfill": "^3.0.0",
|
||||||
"react-dev-utils": "^12.0.1",
|
"react-dev-utils": "^12.0.1",
|
||||||
@ -102,6 +104,7 @@
|
|||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/lodash": "^4.17.0",
|
"@types/lodash": "^4.17.0",
|
||||||
|
"@types/qs": "^6.9.14",
|
||||||
"monaco-editor-webpack-plugin": "^7.1.0"
|
"monaco-editor-webpack-plugin": "^7.1.0"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
@ -159,5 +162,6 @@
|
|||||||
"presets": [
|
"presets": [
|
||||||
"react-app"
|
"react-app"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"proxy": "http://rcw-mock-lab02.lab.nordigy.ru:8080"
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,12 @@ import ListItem from '@mui/material/ListItem';
|
|||||||
import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
|
import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import FaceIcon from '@mui/icons-material/Face';
|
import FaceIcon from '@mui/icons-material/Face';
|
||||||
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isMe: boolean;
|
isMe: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -43,8 +45,9 @@ const ChatMessageWrap = styled(Box)<{ isMe: boolean }>(({ isMe }) => ({
|
|||||||
|
|
||||||
const ChatMessage = styled(Box)(() => ({
|
const ChatMessage = styled(Box)(() => ({
|
||||||
maxWidth: '80%',
|
maxWidth: '80%',
|
||||||
|
whiteSpace: 'pre-line'
|
||||||
}));
|
}));
|
||||||
export const ChatItem: FC<Props> = ({ isMe, message }) => {
|
export const ChatItem: FC<Props> = ({ isMe, message, loading }) => {
|
||||||
return (
|
return (
|
||||||
<ChatWrap isMe={isMe} alignItems="flex-start">
|
<ChatWrap isMe={isMe} alignItems="flex-start">
|
||||||
<ChatAvatar>
|
<ChatAvatar>
|
||||||
@ -53,7 +56,8 @@ export const ChatItem: FC<Props> = ({ isMe, message }) => {
|
|||||||
</ChatAvatar>
|
</ChatAvatar>
|
||||||
<ChatMessageWrap isMe={isMe}>
|
<ChatMessageWrap isMe={isMe}>
|
||||||
<ChatMessage sx={{ borderRadius: 2, p: 2, bgcolor: isMe ? 'primary.light' : 'secondary.light' }}>
|
<ChatMessage sx={{ borderRadius: 2, p: 2, bgcolor: isMe ? 'primary.light' : 'secondary.light' }}>
|
||||||
{message}
|
{loading && <CircularProgress color="secondary" size={20}/>}
|
||||||
|
{!loading && message}
|
||||||
</ChatMessage>
|
</ChatMessage>
|
||||||
</ChatMessageWrap>
|
</ChatMessageWrap>
|
||||||
</ChatWrap>
|
</ChatWrap>
|
||||||
|
134
src/Create.tsx
134
src/Create.tsx
@ -5,7 +5,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import React, { ChangeEvent, KeyboardEvent, useEffect, useRef, useState } from "react";
|
import React, { ChangeEvent, KeyboardEvent, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
@ -19,21 +19,72 @@ import { ChatItem } from "./ChatItem";
|
|||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import { uniqueId } from 'lodash';
|
import { uniqueId } from 'lodash';
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import { Editor } from "./Editor";
|
import { useLocation } from "react-router-dom";
|
||||||
|
import axios from 'axios';
|
||||||
|
import { Definition, DSL2Json, Json2Preview } from "./loadYml";
|
||||||
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
id: string;
|
id: string;
|
||||||
message: string;
|
message: string;
|
||||||
isMe: boolean;
|
isMe: boolean;
|
||||||
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SupportedBlock = ['BotMessage', 'Chat', 'EmailSender', 'Script'];
|
const SupportedBlock = ['BotMessage', 'Chat', 'GlipSender', 'Script'];
|
||||||
|
|
||||||
export const Create = () => {
|
export const Create = () => {
|
||||||
const [chatList, setChatList] = useState<Message[]>([]);
|
const location = useLocation();
|
||||||
const [workflowName, setWorkFlowName] = useState('My workflow');
|
const id = useMemo(() => new URLSearchParams(location.search).get('id'), [location]);
|
||||||
|
const [formJson, setFormJson] = useState(() => {
|
||||||
|
let value = {
|
||||||
|
id: 'abc',
|
||||||
|
description: '',
|
||||||
|
name: 'My workflow',
|
||||||
|
versionId: '',
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
value = JSON.parse(`create-${id}`);
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
const [chatList, setChatList] = useState<Message[]>([{
|
||||||
|
id: uniqueId('r'),
|
||||||
|
message: [
|
||||||
|
'Hello! Please tell me what kind of workflow you want to generate!\n',
|
||||||
|
'Currently the node types I support are: Chat, BotMessage, GlipSender, Script'].join('\n'),
|
||||||
|
isMe: false
|
||||||
|
}]);
|
||||||
const [chatInput, setChatInput] = useState('');
|
const [chatInput, setChatInput] = useState('');
|
||||||
|
const [workflowContent, setWorkflowContent] = useState('');
|
||||||
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
|
const flowDefinition = useMemo(() => DSL2Json(workflowContent), [workflowContent]);
|
||||||
|
const previewImg = useMemo(() => Json2Preview(flowDefinition), [flowDefinition]);
|
||||||
const chatContentRef = useRef<HTMLUListElement>(null);
|
const chatContentRef = useRef<HTMLUListElement>(null);
|
||||||
|
const hasEmailSender = useMemo(() => workflowContent.includes('"EmailSender"'), [workflowContent]);
|
||||||
|
const hasBotAddin = useMemo(() => workflowContent.includes('"Chat"'), [workflowContent]);
|
||||||
|
const hasScript = useMemo(() => workflowContent.includes('"Script"'), [workflowContent]);
|
||||||
|
|
||||||
|
const [emailData, setEmailData] = useState({
|
||||||
|
to: '',
|
||||||
|
subject: '',
|
||||||
|
content: `${content}`
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(workflowContent);
|
||||||
|
console.log(flowDefinition);
|
||||||
|
}, [flowDefinition]);
|
||||||
|
// useEffect(() => {
|
||||||
|
// axios.post('/chat/start', {
|
||||||
|
// accountId: 10086,
|
||||||
|
// channelId: "10000",
|
||||||
|
// dialogId: "1",
|
||||||
|
// segmentId: "1"
|
||||||
|
// });
|
||||||
|
// }, []);
|
||||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
setChatInput(e.target.value);
|
setChatInput(e.target.value);
|
||||||
};
|
};
|
||||||
@ -41,13 +92,35 @@ export const Create = () => {
|
|||||||
const handleClickChip = (text: string) => () => {
|
const handleClickChip = (text: string) => () => {
|
||||||
setChatInput(s => s + text);
|
setChatInput(s => s + text);
|
||||||
};
|
};
|
||||||
const handleSend = () => {
|
const handleSend = async () => {
|
||||||
|
const rId = uniqueId('r');
|
||||||
|
|
||||||
setChatList([...chatList, {
|
setChatList([...chatList, {
|
||||||
id: uniqueId(),
|
id: uniqueId('m'),
|
||||||
message: chatInput,
|
message: chatInput,
|
||||||
isMe: true
|
isMe: true
|
||||||
|
}, {
|
||||||
|
id: rId,
|
||||||
|
message: '',
|
||||||
|
isMe: false,
|
||||||
|
loading: true
|
||||||
}]);
|
}]);
|
||||||
setChatInput('');
|
setChatInput('');
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post<{ answer: string; workflowContent: string }>(`/bot/workflow/${id}/messages`, {
|
||||||
|
"question": chatInput
|
||||||
|
});
|
||||||
|
setWorkflowContent(data.workflowContent);
|
||||||
|
setChatList(list => list.map(l => {
|
||||||
|
if (l.id === rId) {
|
||||||
|
l.loading = false;
|
||||||
|
l.message = data.answer;
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}));
|
||||||
|
} catch (e) {
|
||||||
|
setChatList(list => list.filter(l => l.id !== rId));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -56,25 +129,54 @@ export const Create = () => {
|
|||||||
}
|
}
|
||||||
}, [chatList]);
|
}, [chatList]);
|
||||||
|
|
||||||
|
const handleRefresh = async () => {
|
||||||
|
await axios.delete(`/bot/workflow/${id}/messages`);
|
||||||
|
setChatList([{
|
||||||
|
id: uniqueId('r'),
|
||||||
|
message: [
|
||||||
|
'Hello! Please tell me what kind of workflow you want to generate!\n',
|
||||||
|
'Currently the node types I support are: Chat, BotMessage, GlipSender, Script'].join('\n'),
|
||||||
|
isMe: false
|
||||||
|
}]);
|
||||||
|
setChatInput('');
|
||||||
|
setWorkflowContent('');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField value={workflowName} focused fullWidth variant="outlined" label="Workflow Name"
|
<TextField value={formJson.name} fullWidth variant="outlined" label="Workflow Name"
|
||||||
placeholder="Please name your workflow" onChange={e => {
|
placeholder="Please name your workflow" onChange={e => {
|
||||||
setWorkFlowName(e.target.value);
|
setFormJson(json => ({
|
||||||
|
...json,
|
||||||
|
name: e.target.value
|
||||||
|
}));
|
||||||
}}/>
|
}}/>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs>
|
<Grid item xs>
|
||||||
<Box sx={{ border: 1, height: 600, borderRadius: 1, mt: 4, borderColor: 'rgba(0,0,0,0.23)' }}>
|
<Box sx={{
|
||||||
|
border: 1,
|
||||||
|
height: 600,
|
||||||
|
borderRadius: 1,
|
||||||
|
mt: 4,
|
||||||
|
borderColor: 'rgba(255,255,255,0.23)',
|
||||||
|
position: 'relative'
|
||||||
|
}}>
|
||||||
|
<Tooltip title="Refresh AI">
|
||||||
|
<IconButton onClick={handleRefresh} style={{ position: 'absolute', right: 10, top: 10, zIndex: 5 }}>
|
||||||
|
<RefreshIcon/>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<List ref={chatContentRef}
|
<List ref={chatContentRef}
|
||||||
style={{ flex: '1 1 auto', overflowY: 'auto', overflowX: 'hidden' }}
|
style={{ flex: '1 1 auto', overflowY: 'auto', overflowX: 'hidden' }}
|
||||||
sx={{ p: 2 }}>
|
sx={{ p: 2 }}>
|
||||||
{chatList.map(chat => <ChatItem isMe={chat.isMe} message={chat.message} key={chat.id}/>)}
|
{chatList.map(chat => <ChatItem isMe={chat.isMe} message={chat.message} key={chat.id}
|
||||||
|
loading={chat.loading}/>)}
|
||||||
</List>
|
</List>
|
||||||
<Box sx={{ display: 'flex', flex: '0 0 auto', p: 2, borderTop: 1, borderColor: 'divider' }}>
|
<Box sx={{ display: 'flex', flex: '0 0 auto', p: 2, borderTop: 1, borderColor: 'divider' }}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
@ -84,7 +186,7 @@ export const Create = () => {
|
|||||||
Supported Action Types:
|
Supported Action Types:
|
||||||
</Typography>
|
</Typography>
|
||||||
{SupportedBlock.map(label => (
|
{SupportedBlock.map(label => (
|
||||||
<Chip label={label} key={label} size="small" clickable
|
<Chip label={label} key={label} size="small" clickable color="primary"
|
||||||
onClick={handleClickChip(label)}/>))}
|
onClick={handleClickChip(label)}/>))}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -96,6 +198,7 @@ export const Create = () => {
|
|||||||
handleSend();
|
handleSend();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
autoFocus
|
||||||
multiline maxRows={3}
|
multiline maxRows={3}
|
||||||
value={chatInput}/>
|
value={chatInput}/>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -110,13 +213,13 @@ export const Create = () => {
|
|||||||
</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(0,0,0,0.23)' }}>
|
<Box sx={{ border: 1, height: 600, borderRadius: 1, p: 2, mt: 4, borderColor: 'rgba(255,255,255,0.23)' }}>
|
||||||
Preview
|
{!previewImg ? 'Workflow Preview...' :
|
||||||
|
<img src={previewImg} style={{ width: '100%', height: '100%' }} alt="preview"/>}
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Divider sx={{ mt: 4, mb: 4 }}/>
|
<Divider sx={{ mt: 4, mb: 4 }}/>
|
||||||
<Editor/>
|
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -129,6 +232,7 @@ export const Create = () => {
|
|||||||
p: 2,
|
p: 2,
|
||||||
boxShadow: 2,
|
boxShadow: 2,
|
||||||
borderTop: 1,
|
borderTop: 1,
|
||||||
|
bgcolor: 'background.default',
|
||||||
borderColor: 'divider',
|
borderColor: 'divider',
|
||||||
display: { xs: 'flex' }, justifyContent: "flex-end"
|
display: { xs: 'flex' }, justifyContent: "flex-end"
|
||||||
}}>
|
}}>
|
||||||
|
@ -15,6 +15,7 @@ import Paper from '@mui/material/Paper';
|
|||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import AccountCircle from '@mui/icons-material/AccountCircle';
|
import AccountCircle from '@mui/icons-material/AccountCircle';
|
||||||
import { styled } from '@mui/material/styles';
|
import { styled } from '@mui/material/styles';
|
||||||
|
import { RCIcon } from './RCIcon';
|
||||||
|
|
||||||
const Layout = styled(Box)(({ theme }) => (
|
const Layout = styled(Box)(({ theme }) => (
|
||||||
{
|
{
|
||||||
@ -36,7 +37,7 @@ const Content = styled(Box)(({ theme }) => ({
|
|||||||
export const Home = () => {
|
export const Home = () => {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<AppBar position="static">
|
<AppBar position="static" enableColorOnDark>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h6"
|
variant="h6"
|
||||||
@ -44,6 +45,7 @@ export const Home = () => {
|
|||||||
component="div"
|
component="div"
|
||||||
sx={{ flexGrow: 1, display: { xs: 'block' } }}
|
sx={{ flexGrow: 1, display: { xs: 'block' } }}
|
||||||
>
|
>
|
||||||
|
<RCIcon style={{ marginRight: 4, verticalAlign: 'middle' }}/>
|
||||||
RingCentral AI Workflow Hub
|
RingCentral AI Workflow Hub
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: { xs: 'block' } }}>
|
<Box sx={{ display: { xs: 'block' } }}>
|
||||||
|
110
src/List.tsx
110
src/List.tsx
@ -5,66 +5,130 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import { styled } from '@mui/material/styles';
|
import { styled } from '@mui/material/styles';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import CardActionArea from '@mui/material/CardActionArea';
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
import Switch from '@mui/material/Switch';
|
import Switch from '@mui/material/Switch';
|
||||||
import { Link } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
import Card from '@mui/material/Card';
|
import Card from '@mui/material/Card';
|
||||||
import CardActions from '@mui/material/CardActions';
|
import CardActions from '@mui/material/CardActions';
|
||||||
import CardContent from '@mui/material/CardContent';
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import { RCIcon } from './RCIcon';
|
||||||
|
import axios from "axios";
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
interface ListItem {
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
id: string;
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
active: boolean;
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
title: string;
|
import DialogContentText from '@mui/material/DialogContentText';
|
||||||
}
|
import TextField from '@mui/material/TextField';
|
||||||
|
|
||||||
const Flex = styled(Box)(() => ({
|
const Flex = styled(Box)(() => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
interface Workflow {
|
||||||
|
description: string;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const List = () => {
|
export const List = () => {
|
||||||
const [list] = useState<ListItem[]>([{
|
const [list, setList] = useState<Workflow[]>([]);
|
||||||
id: 'a',
|
const [showModal, setShowModal] = useState(false);
|
||||||
title: 'Workflow 1',
|
|
||||||
active: true
|
const navigate = useNavigate();
|
||||||
}]);
|
useEffect(() => {
|
||||||
|
axios.get<{
|
||||||
|
total: number
|
||||||
|
workflows: Workflow[]
|
||||||
|
}>('/api/public/wf/config/v1/account/10086/workflows').then(({ data }) => {
|
||||||
|
setList(data.workflows);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
const handleOpen = () => {
|
||||||
|
setShowModal(true);
|
||||||
|
};
|
||||||
|
const handleClose = () => {
|
||||||
|
setShowModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const formData = new FormData(event.currentTarget);
|
||||||
|
const formJson = Object.fromEntries((formData as any).entries());
|
||||||
|
axios.post('/bot/workflow', { name: formJson.name }).then(({ data }) => {
|
||||||
|
sessionStorage.setItem(`create-${data.visibleId}`, JSON.stringify({ ...data, name: formJson.name }));
|
||||||
|
handleClose();
|
||||||
|
navigate(`/create?id=${data.visibleId}`);
|
||||||
|
});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<Container maxWidth="lg">
|
||||||
<Flex style={{ justifyContent: 'flex-end' }}>
|
<Flex style={{ justifyContent: 'flex-end' }}>
|
||||||
<Button startIcon={<AddIcon/>} variant="contained" href={`/create`}>Create Workflow</Button>
|
<Button startIcon={<AddIcon/>} variant="contained" onClick={handleOpen}>Create Workflow</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex sx={{ gap: 2 }}>
|
<Flex sx={{ gap: 4, mt: 4 }}>
|
||||||
{list.map(item => (
|
{list.map(item => (
|
||||||
<Link
|
<Link
|
||||||
key={item.id}
|
key={item.id}
|
||||||
to={`/detail/${item.id}`}
|
to={`/detail/${item.id}`}
|
||||||
>
|
>
|
||||||
<Card sx={{ width: 280, borderRadius: 2, boxShadow: 2 }}>
|
<Card sx={{ width: 263, borderRadius: 2, boxShadow: 2 }}>
|
||||||
<CardActionArea>
|
<CardActionArea>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography gutterBottom variant="h5" component="div">
|
<Typography gutterBottom variant="h5" component="div">
|
||||||
{item.title}
|
{item.name || 'My Workflow'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ minHeight: 60 }}>
|
<Box sx={{ minHeight: 60 }}>
|
||||||
<img style={{ width: 20, height: 20 }}
|
<RCIcon/>
|
||||||
src="https://netstorage.ringcentral.com/appext/gallery-page/images/25e9f677-5605-4092-a6c3-69de29a81d8d.svg"
|
|
||||||
alt="rc"/>
|
|
||||||
</Box>
|
</Box>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardActions sx={{ display: { xs: 'flex' }, justifyContent: "flex-end" }}>
|
<CardActions
|
||||||
|
sx={{ display: { xs: 'flex' }, justifyContent: "flex-end", borderTop: 1, borderColor: 'divider' }}>
|
||||||
<Switch defaultChecked/>
|
<Switch defaultChecked/>
|
||||||
</CardActions>
|
</CardActions>
|
||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
</Card>
|
</Card>
|
||||||
</Link>))}
|
</Link>))}
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
<Dialog
|
||||||
|
open={showModal}
|
||||||
|
onClose={handleClose}
|
||||||
|
PaperProps={{
|
||||||
|
component: 'form',
|
||||||
|
onSubmit: handleSubmit,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle>Create Workflow</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
To create a workflow, please name your workflow first.
|
||||||
|
</DialogContentText>
|
||||||
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
required
|
||||||
|
margin="dense"
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
defaultValue="My workflow"
|
||||||
|
label="Workflow Name"
|
||||||
|
fullWidth
|
||||||
|
focused
|
||||||
|
variant="standard"
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleClose}>Cancel</Button>
|
||||||
|
<Button type="submit">Create</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
16
src/RCIcon.tsx
Normal file
16
src/RCIcon.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* @Author : Eleanor Mao
|
||||||
|
* @Date : 2024-04-11 10:41:51
|
||||||
|
* @LastEditTime : 2024-04-11 10:41:51
|
||||||
|
*
|
||||||
|
* Copyright © RingCentral. All rights reserved.
|
||||||
|
*/
|
||||||
|
import React, { CSSProperties, FC } from "react";
|
||||||
|
|
||||||
|
export const RCIcon: FC<{ size?: number; style?: CSSProperties }> = ({ size = 20, style }) => {
|
||||||
|
return (
|
||||||
|
<img style={{ width: size, height: size, ...style }}
|
||||||
|
src="https://netstorage.ringcentral.com/appext/gallery-page/images/25e9f677-5605-4092-a6c3-69de29a81d8d.svg"
|
||||||
|
alt="rc"/>
|
||||||
|
);
|
||||||
|
};
|
127
src/ajax.ts
Normal file
127
src/ajax.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* @Author : Eleanor Mao
|
||||||
|
* @Date : 2024-04-11 10:14:35
|
||||||
|
* @LastEditTime : 2024-04-11 10:14:35
|
||||||
|
*
|
||||||
|
* Copyright © RingCentral. All rights reserved.
|
||||||
|
*/
|
||||||
|
import axios from 'axios';
|
||||||
|
import type { AxiosHeaders, AxiosError } from 'axios';
|
||||||
|
import qs from 'qs';
|
||||||
|
import { isFunction } from 'lodash';
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
[key: string]: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
urlParams?: {
|
||||||
|
[key: string]: string
|
||||||
|
};
|
||||||
|
paramsSerializer?: (params: Params) => string;
|
||||||
|
timeoutCallback?: (error: any) => void;
|
||||||
|
cancelCallback?: (error: any) => void;
|
||||||
|
arrayFormat?: 'repeat' | 'brackets' | 'comma' | 'indices';
|
||||||
|
allowDots?: boolean;
|
||||||
|
headers?: AxiosHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IFunc = (url: string, data?: any, options?: Props) => Promise<any>
|
||||||
|
|
||||||
|
export interface Ajax {
|
||||||
|
post?: IFunc;
|
||||||
|
get?: IFunc;
|
||||||
|
put?: IFunc;
|
||||||
|
delete?: IFunc;
|
||||||
|
patch?: IFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
type METHODS = ['post', 'get', 'put', 'delete', 'patch']
|
||||||
|
|
||||||
|
const methods: METHODS = ['post', 'get', 'put', 'delete', 'patch'];
|
||||||
|
const ins: Ajax = {};
|
||||||
|
|
||||||
|
|
||||||
|
axios.defaults.headers.post['Content-Type'] = 'application/json';
|
||||||
|
axios.defaults.headers.get.Accept = 'application/json';
|
||||||
|
axios.defaults.timeout = 9500;
|
||||||
|
|
||||||
|
const format = function (
|
||||||
|
url: string,
|
||||||
|
obj: {
|
||||||
|
[key: string]: string
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
let ret = url;
|
||||||
|
for (const key of Object.keys(obj)) {
|
||||||
|
ret = ret.replace('{' + key + '}', obj[key]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
methods.forEach(method => {
|
||||||
|
ins[method] = (url, data, options = {}) => {
|
||||||
|
data = data || {};
|
||||||
|
|
||||||
|
|
||||||
|
if (options.urlParams) {
|
||||||
|
url = format(url, options.urlParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === 'get') {
|
||||||
|
data = { params: data };
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
!(data instanceof FormData) &&
|
||||||
|
options.headers &&
|
||||||
|
options.headers['Content-Type'] === 'application/x-www-form-urlencoded'
|
||||||
|
) {
|
||||||
|
data = qs.stringify(data, {
|
||||||
|
arrayFormat: options.arrayFormat ? options.arrayFormat : 'repeat',
|
||||||
|
allowDots: options.allowDots
|
||||||
|
});
|
||||||
|
}
|
||||||
|
data = { data };
|
||||||
|
}
|
||||||
|
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
return axios({
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
...data,
|
||||||
|
withCredentials: true,
|
||||||
|
timeout: 9500,
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response instanceof Blob) {
|
||||||
|
resolve(response);
|
||||||
|
}
|
||||||
|
if (response.code === 0 || response.code === 200) {
|
||||||
|
resolve(response.data);
|
||||||
|
} else {
|
||||||
|
reject(response);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error: AxiosError) => {
|
||||||
|
if (axios.isCancel(error)) {
|
||||||
|
return isFunction(options.cancelCallback) ? options.cancelCallback(error) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/axios/axios/issues/660
|
||||||
|
// @ts-ignore
|
||||||
|
if (error.code === 'ECONNABORTED' && isFunction(options.timeoutCallback)) {
|
||||||
|
return options.timeoutCallback(error);
|
||||||
|
}
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
@ -7,30 +7,33 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||||
import {} from '@mui/material/colors';
|
|
||||||
|
|
||||||
import CssBaseline from '@mui/material/CssBaseline';
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement
|
document.getElementById('root') as HTMLElement
|
||||||
);
|
);
|
||||||
const theme = createTheme({
|
const theme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
mode: 'light',
|
mode: 'dark',
|
||||||
primary: {
|
primary: {
|
||||||
main: '#3f51b5',
|
main: '#3f51b5',
|
||||||
|
},
|
||||||
|
tonalOffset: 0.6,
|
||||||
|
secondary: {
|
||||||
|
main: '#f50057',
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
paper: '#404041',
|
||||||
|
default: '#242425',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tonalOffset: 0.6,
|
|
||||||
secondary: {
|
|
||||||
main: '#f50057',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline/>
|
<CssBaseline/>
|
||||||
<App/>
|
<App/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
@ -23,8 +23,59 @@ function compress(s: string, pre: string): string {
|
|||||||
return "http://www.plantuml.com/plantuml" + pre + encode64_(compressed);
|
return "http://www.plantuml.com/plantuml" + pre + encode64_(compressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadYml(input: string): string {
|
export interface EventHandlerAction {
|
||||||
const definition = jsYaml.load(input);
|
actionType: string;
|
||||||
let outputValue = transformDSL(definition);
|
targetState?: string;
|
||||||
return compress(outputValue, '/svg/');
|
scriptSource?: string;
|
||||||
|
transitionName?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Action {
|
||||||
|
actionType: string;
|
||||||
|
scriptSource?: string;
|
||||||
|
id?: string;
|
||||||
|
question?: string;
|
||||||
|
score?: null;
|
||||||
|
token?: string;
|
||||||
|
type?: string;
|
||||||
|
content?:string;
|
||||||
|
subject?:string;
|
||||||
|
to?:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventHandler {
|
||||||
|
eventType: string;
|
||||||
|
condition?: any;
|
||||||
|
actions: EventHandlerAction[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
name: string;
|
||||||
|
actions: Action[];
|
||||||
|
eventHandlers: EventHandler[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Definition {
|
||||||
|
states: State[];
|
||||||
|
workflowName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Json2Preview(definition?: Definition): string {
|
||||||
|
if (!definition) return '';
|
||||||
|
try {
|
||||||
|
console.log(transformDSL(definition))
|
||||||
|
return compress(transformDSL(definition), '/svg/');
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DSL2Json(input: string): Definition {
|
||||||
|
return jsYaml.load(input) as Definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
window.ToJson = DSL2Json
|
||||||
|
// @ts-ignore
|
||||||
|
window.transform = transformDSL
|
||||||
|
@ -1,24 +1,18 @@
|
|||||||
|
import { Action, Definition, State } from "./loadYml";
|
||||||
|
|
||||||
const EVENT_COLOR = "#LightYellow";
|
const EVENT_COLOR = "#LightYellow";
|
||||||
const HTTP_COLOR = "#Peru";
|
const HTTP_COLOR = "#Peru";
|
||||||
const SCRIPT_COLOR = "#Olive;text:white";
|
const SCRIPT_COLOR = "#Olive;text:white";
|
||||||
const BOT_COLOR = "#Blue;text:white";
|
const BOT_COLOR = "#Blue;text:white";
|
||||||
const EMAIL_SENDER_COLOR = "#Red;text:white";
|
const EMAIL_SENDER_COLOR = "#Red;text:white";
|
||||||
|
const DEFAULT_COLOR = '#Grey;text;white';
|
||||||
|
|
||||||
const EVENT_NAME = ": Event";
|
const EVENT_NAME = ": Event";
|
||||||
const HTTP_NAME = ": Http";
|
const HTTP_NAME = ": Http";
|
||||||
const SCRIPT_NAME = ": Script";
|
const SCRIPT_NAME = ": Script";
|
||||||
const BOT_NAME = ": Chat";
|
const BOT_NAME = ": Chat";
|
||||||
const EMAIL_SENDER_NAME = ": EmailSender";
|
const EMAIL_SENDER_NAME = ": EmailSender";
|
||||||
|
const EXIT_NAME = ": Exit";
|
||||||
interface Action {
|
|
||||||
actionType: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
name: string;
|
|
||||||
actions: Action[];
|
|
||||||
eventHandlers: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHttp(actions: Action[]): boolean {
|
function isHttp(actions: Action[]): boolean {
|
||||||
return actions
|
return actions
|
||||||
@ -35,6 +29,10 @@ function isChat(actions: Action[]): boolean {
|
|||||||
.some((action) => action.actionType === 'Chat');
|
.some((action) => action.actionType === 'Chat');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isExit(actions: Action[]): boolean {
|
||||||
|
return actions.some((action) => action.actionType === 'Exit');
|
||||||
|
}
|
||||||
|
|
||||||
function isEmailSender(actions: Action[]): boolean {
|
function isEmailSender(actions: Action[]): boolean {
|
||||||
return actions
|
return actions
|
||||||
.some((action) => action.actionType === "EmailSender");
|
.some((action) => action.actionType === "EmailSender");
|
||||||
@ -48,7 +46,7 @@ function isNotBlank(str: string): boolean {
|
|||||||
return str !== undefined && str !== null && str.trim() !== "";
|
return str !== undefined && str !== null && str.trim() !== "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawState(eventState: Map<any, any>, state: State): string {
|
function drawState(eventState: Map<string, Set<string>>, state: State): string {
|
||||||
let sb = "";
|
let sb = "";
|
||||||
const name = state.name;
|
const name = state.name;
|
||||||
const actions = state.actions;
|
const actions = state.actions;
|
||||||
@ -57,7 +55,7 @@ function drawState(eventState: Map<any, any>, state: State): string {
|
|||||||
if (isEmpty(handlers)) {
|
if (isEmpty(handlers)) {
|
||||||
throw Error(`state ${name} handlers must not empty`);
|
throw Error(`state ${name} handlers must not empty`);
|
||||||
}
|
}
|
||||||
let events = new Set();
|
let events = new Set<string>();
|
||||||
for (const handle of handlers) {
|
for (const handle of handlers) {
|
||||||
const stateName = handle.eventType;
|
const stateName = handle.eventType;
|
||||||
events.add(stateName);
|
events.add(stateName);
|
||||||
@ -74,20 +72,21 @@ function drawState(eventState: Map<any, any>, state: State): string {
|
|||||||
} else if (isEmailSender(actions)) {
|
} else if (isEmailSender(actions)) {
|
||||||
sb += `state ${name} ${EMAIL_SENDER_COLOR} ${EMAIL_SENDER_NAME}\n`;
|
sb += `state ${name} ${EMAIL_SENDER_COLOR} ${EMAIL_SENDER_NAME}\n`;
|
||||||
} else {
|
} else {
|
||||||
throw Error(`state ${name} actions is incorrect`);
|
console.log('====', name, actions);
|
||||||
|
// throw Error(`state ${name} actions is incorrect`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
|
|
||||||
function append(from:any, to:any, description:any) {
|
function append(from: any, to: any, description: any) {
|
||||||
if (isNotBlank(description)) {
|
if (isNotBlank(description)) {
|
||||||
description = ": " + description;
|
description = ": " + description;
|
||||||
}
|
}
|
||||||
return `${from} ---> ${to}${description}\n`;
|
return `${from} ---> ${to || ''}${description}\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapCond(cond:any) {
|
function wrapCond(cond: any) {
|
||||||
if (isNotBlank(cond)) {
|
if (isNotBlank(cond)) {
|
||||||
return "(" + cond + ")";
|
return "(" + cond + ")";
|
||||||
} else {
|
} else {
|
||||||
@ -95,7 +94,7 @@ function wrapCond(cond:any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawTransitionCondition(eventCondition:any, transition:any) {
|
function drawTransitionCondition(eventCondition: any, transition: any) {
|
||||||
const list = [];
|
const list = [];
|
||||||
if (eventCondition !== undefined && eventCondition !== null) {
|
if (eventCondition !== undefined && eventCondition !== null) {
|
||||||
list.push(eventCondition);
|
list.push(eventCondition);
|
||||||
@ -111,7 +110,7 @@ function drawTransitionCondition(eventCondition:any, transition:any) {
|
|||||||
return list.join(" && ");
|
return list.join(" && ");
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawExitCondition(eventCondition:any, exit:any) {
|
function drawExitCondition(eventCondition: any, exit: any) {
|
||||||
const list = [];
|
const list = [];
|
||||||
if (eventCondition !== undefined && eventCondition !== null) {
|
if (eventCondition !== undefined && eventCondition !== null) {
|
||||||
list.push(eventCondition);
|
list.push(eventCondition);
|
||||||
@ -122,7 +121,7 @@ function drawExitCondition(eventCondition:any, exit:any) {
|
|||||||
return list.join(" && ");
|
return list.join(" && ");
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawStateLine(eventState:any, state:any) {
|
function drawStateLine(eventState: Map<string, Set<string>>, state: State) {
|
||||||
const handlers = state.eventHandlers;
|
const handlers = state.eventHandlers;
|
||||||
if (isEmpty(handlers)) {
|
if (isEmpty(handlers)) {
|
||||||
return;
|
return;
|
||||||
@ -141,13 +140,13 @@ function drawStateLine(eventState:any, state:any) {
|
|||||||
const actions = handler.actions;
|
const actions = handler.actions;
|
||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
const actionType = action.actionType;
|
const actionType = action.actionType;
|
||||||
let targetState = action.targetState;
|
let targetState = action.targetState || '';
|
||||||
if (actionType === "Transition") {
|
if (actionType === "Transition") {
|
||||||
if (eventState.has(targetState)) {
|
if (eventState.has(targetState)) {
|
||||||
let events = eventState.get(targetState);
|
let events = eventState.get(targetState);
|
||||||
if (events.size > 1) {
|
if (events && events.size > 1) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else if (events) {
|
||||||
targetState = events.values().next().value;
|
targetState = events.values().next().value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,15 +161,15 @@ function drawStateLine(eventState:any, state:any) {
|
|||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformDSL(definition: any): string {
|
export function transformDSL(definition: Definition): string {
|
||||||
let sb = "";
|
let sb = "";
|
||||||
const eventState = new Map();
|
const eventState = new Map<string, Set<string>>();
|
||||||
const states = definition["states"];
|
const states = definition["states"] || [];
|
||||||
for (const state of states) {
|
for (const state of states) {
|
||||||
sb += drawState(eventState, state);
|
sb += drawState(eventState, state) || '';
|
||||||
}
|
}
|
||||||
for (const state of states) {
|
for (const state of states) {
|
||||||
sb += drawStateLine(eventState, state);
|
sb += drawStateLine(eventState, state) || '';
|
||||||
}
|
}
|
||||||
return `@startuml
|
return `@startuml
|
||||||
|
|
||||||
|
34
yarn.lock
34
yarn.lock
@ -2407,7 +2407,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.8.tgz#95f6c6a08f2ad868ba230ead1d2d7f7be3db3837"
|
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.8.tgz#95f6c6a08f2ad868ba230ead1d2d7f7be3db3837"
|
||||||
integrity sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==
|
integrity sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==
|
||||||
|
|
||||||
"@types/qs@*":
|
"@types/qs@*", "@types/qs@^6.9.14":
|
||||||
version "6.9.14"
|
version "6.9.14"
|
||||||
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.14.tgz#169e142bfe493895287bee382af6039795e9b75b"
|
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.14.tgz#169e142bfe493895287bee382af6039795e9b75b"
|
||||||
integrity sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==
|
integrity sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==
|
||||||
@ -3123,6 +3123,15 @@ axe-core@=4.7.0:
|
|||||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
|
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
|
||||||
integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==
|
integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==
|
||||||
|
|
||||||
|
axios@^1.6.8:
|
||||||
|
version "1.6.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66"
|
||||||
|
integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.15.6"
|
||||||
|
form-data "^4.0.0"
|
||||||
|
proxy-from-env "^1.1.0"
|
||||||
|
|
||||||
axobject-query@^3.2.1:
|
axobject-query@^3.2.1:
|
||||||
version "3.2.1"
|
version "3.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
|
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
|
||||||
@ -5062,7 +5071,7 @@ flatted@^3.2.9:
|
|||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
||||||
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
|
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
|
||||||
|
|
||||||
follow-redirects@^1.0.0:
|
follow-redirects@^1.0.0, follow-redirects@^1.15.6:
|
||||||
version "1.15.6"
|
version "1.15.6"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
|
||||||
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
|
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
|
||||||
@ -5110,6 +5119,15 @@ form-data@^3.0.0:
|
|||||||
combined-stream "^1.0.8"
|
combined-stream "^1.0.8"
|
||||||
mime-types "^2.1.12"
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
|
form-data@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||||
|
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||||
|
dependencies:
|
||||||
|
asynckit "^0.4.0"
|
||||||
|
combined-stream "^1.0.8"
|
||||||
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
forwarded@0.2.0:
|
forwarded@0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||||
@ -8161,6 +8179,11 @@ proxy-addr@~2.0.7:
|
|||||||
forwarded "0.2.0"
|
forwarded "0.2.0"
|
||||||
ipaddr.js "1.9.1"
|
ipaddr.js "1.9.1"
|
||||||
|
|
||||||
|
proxy-from-env@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||||
|
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||||
|
|
||||||
psl@^1.1.33:
|
psl@^1.1.33:
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
|
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
|
||||||
@ -8183,6 +8206,13 @@ qs@6.11.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
side-channel "^1.0.4"
|
side-channel "^1.0.4"
|
||||||
|
|
||||||
|
qs@^6.12.0:
|
||||||
|
version "6.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.0.tgz#edd40c3b823995946a8a0b1f208669c7a200db77"
|
||||||
|
integrity sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==
|
||||||
|
dependencies:
|
||||||
|
side-channel "^1.0.6"
|
||||||
|
|
||||||
querystringify@^2.1.1:
|
querystringify@^2.1.1:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
||||||
|
Loading…
Reference in New Issue
Block a user