Initial push

This commit is contained in:
Henry
2023-04-06 22:17:34 +01:00
commit 05c86ff9c5
162 changed files with 9112 additions and 0 deletions
@@ -0,0 +1,97 @@
import PropTypes from 'prop-types'
import { forwardRef } from 'react'
// third-party
import { motion, useCycle } from 'framer-motion'
// ==============================|| ANIMATION BUTTON ||============================== //
const AnimateButton = forwardRef(function AnimateButton({ children, type, direction, offset, scale }, ref) {
let offset1
let offset2
switch (direction) {
case 'up':
case 'left':
offset1 = offset
offset2 = 0
break
case 'right':
case 'down':
default:
offset1 = 0
offset2 = offset
break
}
const [x, cycleX] = useCycle(offset1, offset2)
const [y, cycleY] = useCycle(offset1, offset2)
switch (type) {
case 'rotate':
return (
<motion.div
ref={ref}
animate={{ rotate: 360 }}
transition={{
repeat: Infinity,
repeatType: 'loop',
duration: 2,
repeatDelay: 0
}}
>
{children}
</motion.div>
)
case 'slide':
if (direction === 'up' || direction === 'down') {
return (
<motion.div
ref={ref}
animate={{ y: y !== undefined ? y : '' }}
onHoverEnd={() => cycleY()}
onHoverStart={() => cycleY()}
>
{children}
</motion.div>
)
}
return (
<motion.div ref={ref} animate={{ x: x !== undefined ? x : '' }} onHoverEnd={() => cycleX()} onHoverStart={() => cycleX()}>
{children}
</motion.div>
)
case 'scale':
default:
if (typeof scale === 'number') {
scale = {
hover: scale,
tap: scale
}
}
return (
<motion.div ref={ref} whileHover={{ scale: scale?.hover }} whileTap={{ scale: scale?.tap }}>
{children}
</motion.div>
)
}
})
AnimateButton.propTypes = {
children: PropTypes.node,
offset: PropTypes.number,
type: PropTypes.oneOf(['slide', 'scale', 'rotate']),
direction: PropTypes.oneOf(['up', 'down', 'left', 'right']),
scale: PropTypes.oneOfType([PropTypes.number, PropTypes.object])
}
AnimateButton.defaultProps = {
type: 'scale',
offset: 10,
direction: 'right',
scale: {
hover: 1,
tap: 0.9
}
}
export default AnimateButton
@@ -0,0 +1,11 @@
import { styled } from '@mui/material/styles'
import { Button } from '@mui/material'
export const StyledButton = styled(Button)(({ theme, color = 'primary' }) => ({
color: 'white',
backgroundColor: theme.palette[color].main,
'&:hover': {
backgroundColor: theme.palette[color].main,
backgroundImage: `linear-gradient(rgb(0 0 0/10%) 0 0)`
}
}))
@@ -0,0 +1,11 @@
import { styled } from '@mui/material/styles'
import { Fab } from '@mui/material'
export const StyledFab = styled(Fab)(({ theme, color = 'primary' }) => ({
color: 'white',
backgroundColor: theme.palette[color].main,
'&:hover': {
backgroundColor: theme.palette[color].main,
backgroundImage: `linear-gradient(rgb(0 0 0/10%) 0 0)`
}
}))
@@ -0,0 +1,95 @@
import PropTypes from 'prop-types'
// material-ui
import { styled, useTheme } from '@mui/material/styles'
import { Box, Grid, Chip, Typography } from '@mui/material'
// project imports
import MainCard from 'ui-component/cards/MainCard'
import SkeletonChatflowCard from 'ui-component/cards/Skeleton/ChatflowCard'
const CardWrapper = styled(MainCard)(({ theme }) => ({
background: theme.palette.card.main,
color: theme.darkTextPrimary,
overflow: 'hidden',
position: 'relative',
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
cursor: 'pointer',
'&:hover': {
background: theme.palette.card.hover,
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 20%)'
}
}))
// ===========================|| CONTRACT CARD ||=========================== //
const ItemCard = ({ isLoading, data, images, onClick }) => {
const theme = useTheme()
const chipSX = {
height: 24,
padding: '0 6px'
}
const activeChatflowSX = {
...chipSX,
color: 'white',
backgroundColor: theme.palette.success.dark
}
return (
<>
{isLoading ? (
<SkeletonChatflowCard />
) : (
<CardWrapper border={false} content={false} onClick={onClick}>
<Box sx={{ p: 2.25 }}>
<Grid container direction='column'>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
<Typography sx={{ fontSize: '1.5rem', fontWeight: 500 }}>{data.name}</Typography>
</div>
<Grid sx={{ mt: 1, mb: 1 }} container direction='row'>
{data.deployed && (
<Grid item>
<Chip label='Deployed' sx={activeChatflowSX} />
</Grid>
)}
</Grid>
{images && (
<div style={{ display: 'flex', flexDirection: 'row', marginTop: 10 }}>
{images.map((img) => (
<div
key={img}
style={{
width: 40,
height: 40,
marginRight: 5,
borderRadius: '50%',
backgroundColor: 'white'
}}
>
<img
style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }}
alt=''
src={img}
/>
</div>
))}
</div>
)}
</Grid>
</Box>
</CardWrapper>
)}
</>
)
}
ItemCard.propTypes = {
isLoading: PropTypes.bool,
data: PropTypes.object,
images: PropTypes.array,
onClick: PropTypes.func
}
export default ItemCard
@@ -0,0 +1,79 @@
import PropTypes from 'prop-types'
import { forwardRef } from 'react'
// material-ui
import { useTheme } from '@mui/material/styles'
import { Card, CardContent, CardHeader, Divider, Typography } from '@mui/material'
// constant
const headerSX = {
'& .MuiCardHeader-action': { mr: 0 }
}
// ==============================|| CUSTOM MAIN CARD ||============================== //
const MainCard = forwardRef(function MainCard(
{
border = true,
boxShadow,
children,
content = true,
contentClass = '',
contentSX = {},
darkTitle,
secondary,
shadow,
sx = {},
title,
...others
},
ref
) {
const theme = useTheme()
return (
<Card
ref={ref}
{...others}
sx={{
border: border ? '1px solid' : 'none',
borderColor: theme.palette.primary[200] + 75,
':hover': {
boxShadow: boxShadow ? shadow || '0 2px 14px 0 rgb(32 40 45 / 8%)' : 'inherit'
},
...sx
}}
>
{/* card header and action */}
{!darkTitle && title && <CardHeader sx={headerSX} title={title} action={secondary} />}
{darkTitle && title && <CardHeader sx={headerSX} title={<Typography variant='h3'>{title}</Typography>} action={secondary} />}
{/* content & header divider */}
{title && <Divider />}
{/* card content */}
{content && (
<CardContent sx={contentSX} className={contentClass}>
{children}
</CardContent>
)}
{!content && children}
</Card>
)
})
MainCard.propTypes = {
border: PropTypes.bool,
boxShadow: PropTypes.bool,
children: PropTypes.node,
content: PropTypes.bool,
contentClass: PropTypes.string,
contentSX: PropTypes.object,
darkTitle: PropTypes.bool,
secondary: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),
shadow: PropTypes.string,
sx: PropTypes.object,
title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object])
}
export default MainCard
@@ -0,0 +1,32 @@
// material-ui
import { Card, CardContent, Grid } from '@mui/material'
import Skeleton from '@mui/material/Skeleton'
// ==============================|| SKELETON - BRIDGE CARD ||============================== //
const ChatflowCard = () => (
<Card>
<CardContent>
<Grid container direction='column'>
<Grid item>
<Grid container justifyContent='space-between'>
<Grid item>
<Skeleton variant='rectangular' width={44} height={44} />
</Grid>
<Grid item>
<Skeleton variant='rectangular' width={34} height={34} />
</Grid>
</Grid>
</Grid>
<Grid item>
<Skeleton variant='rectangular' sx={{ my: 2 }} height={40} />
</Grid>
<Grid item>
<Skeleton variant='rectangular' height={30} />
</Grid>
</Grid>
</CardContent>
</Card>
)
export default ChatflowCard
@@ -0,0 +1,39 @@
import { createPortal } from 'react-dom'
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material'
import useConfirm from 'hooks/useConfirm'
import { StyledButton } from 'ui-component/button/StyledButton'
const ConfirmDialog = () => {
const { onConfirm, onCancel, confirmState } = useConfirm()
const portalElement = document.getElementById('portal')
const component = confirmState.show ? (
<Dialog
fullWidth
maxWidth='xs'
open={confirmState.show}
onClose={onCancel}
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
>
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
{confirmState.title}
</DialogTitle>
<DialogContent>
<DialogContentText sx={{ color: 'black' }} id='alert-dialog-description'>
{confirmState.description}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onCancel}>{confirmState.cancelButtonName}</Button>
<StyledButton variant='contained' onClick={onConfirm}>
{confirmState.confirmButtonName}
</StyledButton>
</DialogActions>
</Dialog>
) : null
return createPortal(component, portalElement)
}
export default ConfirmDialog
@@ -0,0 +1,61 @@
import { createPortal } from 'react-dom'
import { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import { Button, Dialog, DialogActions, DialogContent, OutlinedInput, DialogTitle } from '@mui/material'
import { StyledButton } from 'ui-component/button/StyledButton'
const SaveChatflowDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
const portalElement = document.getElementById('portal')
const [chatflowName, setChatflowName] = useState('')
const [isReadyToSave, setIsReadyToSave] = useState(false)
useEffect(() => {
if (chatflowName) setIsReadyToSave(true)
else setIsReadyToSave(false)
}, [chatflowName])
const component = show ? (
<Dialog
open={show}
fullWidth
maxWidth='xs'
onClose={onCancel}
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
>
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
{dialogProps.title}
</DialogTitle>
<DialogContent>
<OutlinedInput
sx={{ mt: 1 }}
id='chatflow-name'
type='text'
fullWidth
placeholder='My New Chatflow'
value={chatflowName}
onChange={(e) => setChatflowName(e.target.value)}
/>
</DialogContent>
<DialogActions>
<Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>
<StyledButton disabled={!isReadyToSave} variant='contained' onClick={() => onConfirm(chatflowName)}>
{dialogProps.confirmButtonName}
</StyledButton>
</DialogActions>
</Dialog>
) : null
return createPortal(component, portalElement)
}
SaveChatflowDialog.propTypes = {
show: PropTypes.bool,
dialogProps: PropTypes.object,
onCancel: PropTypes.func,
onConfirm: PropTypes.func
}
export default SaveChatflowDialog
@@ -0,0 +1,61 @@
import { useState } from 'react'
import { useSelector } from 'react-redux'
import { Popper, FormControl, TextField, Box, Typography } from '@mui/material'
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete'
import { styled } from '@mui/material/styles'
import PropTypes from 'prop-types'
const StyledPopper = styled(Popper)({
boxShadow: '0px 8px 10px -5px rgb(0 0 0 / 20%), 0px 16px 24px 2px rgb(0 0 0 / 14%), 0px 6px 30px 5px rgb(0 0 0 / 12%)',
borderRadius: '10px',
[`& .${autocompleteClasses.listbox}`]: {
boxSizing: 'border-box',
'& ul': {
padding: 10,
margin: 10
}
}
})
export const Dropdown = ({ name, value, options, onSelect }) => {
const customization = useSelector((state) => state.customization)
const findMatchingOptions = (options = [], value) => options.find((option) => option.name === value)
const getDefaultOptionValue = () => ''
let [internalValue, setInternalValue] = useState(value ?? 'choose an option')
return (
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
<Autocomplete
id={name}
size='small'
options={options || []}
value={findMatchingOptions(options, internalValue) || getDefaultOptionValue()}
onChange={(e, selection) => {
const value = selection ? selection.name : ''
setInternalValue(value)
onSelect(value)
}}
PopperComponent={StyledPopper}
renderInput={(params) => <TextField {...params} value={internalValue} />}
renderOption={(props, option) => (
<Box component='li' {...props}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Typography variant='h5'>{option.label}</Typography>
{option.description && (
<Typography sx={{ color: customization.isDarkMode ? '#9e9e9e' : '' }}>{option.description}</Typography>
)}
</div>
</Box>
)}
/>
</FormControl>
)
}
Dropdown.propTypes = {
name: PropTypes.string,
value: PropTypes.string,
options: PropTypes.array,
onSelect: PropTypes.func
}
@@ -0,0 +1,40 @@
import Editor from 'react-simple-code-editor'
import { highlight, languages } from 'prismjs/components/prism-core'
import 'prismjs/components/prism-clike'
import 'prismjs/components/prism-javascript'
import 'prismjs/components/prism-json'
import 'prismjs/components/prism-markup'
import './prism-dark.css'
import PropTypes from 'prop-types'
import { useTheme } from '@mui/material/styles'
export const DarkCodeEditor = ({ value, placeholder, type, style, onValueChange, onMouseUp, onBlur }) => {
const theme = useTheme()
return (
<Editor
value={value}
placeholder={placeholder}
highlight={(code) => highlight(code, type === 'json' ? languages.json : languages.js)}
padding={10}
onValueChange={onValueChange}
onMouseUp={onMouseUp}
onBlur={onBlur}
style={{
...style,
background: theme.palette.codeEditor.main
}}
textareaClassName='editor__textarea'
/>
)
}
DarkCodeEditor.propTypes = {
value: PropTypes.string,
placeholder: PropTypes.string,
type: PropTypes.string,
style: PropTypes.object,
onValueChange: PropTypes.func,
onMouseUp: PropTypes.func,
onBlur: PropTypes.func
}
@@ -0,0 +1,40 @@
import Editor from 'react-simple-code-editor'
import { highlight, languages } from 'prismjs/components/prism-core'
import 'prismjs/components/prism-clike'
import 'prismjs/components/prism-javascript'
import 'prismjs/components/prism-json'
import 'prismjs/components/prism-markup'
import './prism-light.css'
import PropTypes from 'prop-types'
import { useTheme } from '@mui/material/styles'
export const LightCodeEditor = ({ value, placeholder, type, style, onValueChange, onMouseUp, onBlur }) => {
const theme = useTheme()
return (
<Editor
value={value}
placeholder={placeholder}
highlight={(code) => highlight(code, type === 'json' ? languages.json : languages.js)}
padding={10}
onValueChange={onValueChange}
onMouseUp={onMouseUp}
onBlur={onBlur}
style={{
...style,
background: theme.palette.card.main
}}
textareaClassName='editor__textarea'
/>
)
}
LightCodeEditor.propTypes = {
value: PropTypes.string,
placeholder: PropTypes.string,
type: PropTypes.string,
style: PropTypes.object,
onValueChange: PropTypes.func,
onMouseUp: PropTypes.func,
onBlur: PropTypes.func
}
@@ -0,0 +1,275 @@
pre[class*='language-'],
code[class*='language-'] {
color: #d4d4d4;
font-size: 13px;
text-shadow: none;
font-family: Menlo, Monaco, Consolas, 'Andale Mono', 'Ubuntu Mono', 'Courier New', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*='language-']::selection,
code[class*='language-']::selection,
pre[class*='language-'] *::selection,
code[class*='language-'] *::selection {
text-shadow: none;
background: #264f78;
}
@media print {
pre[class*='language-'],
code[class*='language-'] {
text-shadow: none;
}
}
pre[class*='language-'] {
padding: 1em;
margin: 0.5em 0;
overflow: auto;
background: #1e1e1e;
}
:not(pre) > code[class*='language-'] {
padding: 0.1em 0.3em;
border-radius: 0.3em;
color: #db4c69;
background: #1e1e1e;
}
/*********************************************************
* Tokens
*/
.namespace {
opacity: 0.7;
}
.token.doctype .token.doctype-tag {
color: #569cd6;
}
.token.doctype .token.name {
color: #9cdcfe;
}
.token.comment,
.token.prolog {
color: #6a9955;
}
.token.punctuation,
.language-html .language-css .token.punctuation,
.language-html .language-javascript .token.punctuation {
color: #d4d4d4;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.inserted,
.token.unit {
color: #b5cea8;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.deleted {
color: #ce9178;
}
.language-css .token.string.url {
text-decoration: underline;
}
.token.operator,
.token.entity {
color: #d4d4d4;
}
.token.operator.arrow {
color: #569cd6;
}
.token.atrule {
color: #ce9178;
}
.token.atrule .token.rule {
color: #c586c0;
}
.token.atrule .token.url {
color: #9cdcfe;
}
.token.atrule .token.url .token.function {
color: #dcdcaa;
}
.token.atrule .token.url .token.punctuation {
color: #d4d4d4;
}
.token.keyword {
color: #569cd6;
}
.token.keyword.module,
.token.keyword.control-flow {
color: #c586c0;
}
.token.function,
.token.function .token.maybe-class-name {
color: #dcdcaa;
}
.token.regex {
color: #d16969;
}
.token.important {
color: #569cd6;
}
.token.italic {
font-style: italic;
}
.token.constant {
color: #9cdcfe;
}
.token.class-name,
.token.maybe-class-name {
color: #4ec9b0;
}
.token.console {
color: #9cdcfe;
}
.token.parameter {
color: #9cdcfe;
}
.token.interpolation {
color: #9cdcfe;
}
.token.punctuation.interpolation-punctuation {
color: #569cd6;
}
.token.boolean {
color: #569cd6;
}
.token.property,
.token.variable,
.token.imports .token.maybe-class-name,
.token.exports .token.maybe-class-name {
color: #9cdcfe;
}
.token.selector {
color: #d7ba7d;
}
.token.escape {
color: #d7ba7d;
}
.token.tag {
color: #569cd6;
}
.token.tag .token.punctuation {
color: #808080;
}
.token.cdata {
color: #808080;
}
.token.attr-name {
color: #9cdcfe;
}
.token.attr-value,
.token.attr-value .token.punctuation {
color: #ce9178;
}
.token.attr-value .token.punctuation.attr-equals {
color: #d4d4d4;
}
.token.entity {
color: #569cd6;
}
.token.namespace {
color: #4ec9b0;
}
/*********************************************************
* Language Specific
*/
pre[class*='language-javascript'],
code[class*='language-javascript'],
pre[class*='language-jsx'],
code[class*='language-jsx'],
pre[class*='language-typescript'],
code[class*='language-typescript'],
pre[class*='language-tsx'],
code[class*='language-tsx'] {
color: #9cdcfe;
}
pre[class*='language-css'],
code[class*='language-css'] {
color: #ce9178;
}
pre[class*='language-html'],
code[class*='language-html'] {
color: #d4d4d4;
}
.language-regex .token.anchor {
color: #dcdcaa;
}
.language-html .token.punctuation {
color: #808080;
}
/*********************************************************
* Line highlighting
*/
pre[class*='language-'] > code[class*='language-'] {
position: relative;
z-index: 1;
}
.line-highlight.line-highlight {
background: #f7ebc6;
box-shadow: inset 5px 0 0 #f7d87c;
z-index: 0;
}
@@ -0,0 +1,207 @@
code[class*='language-'],
pre[class*='language-'] {
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
color: #90a4ae;
background: #fafafa;
font-family: Roboto Mono, monospace;
font-size: 1em;
line-height: 1.5em;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
code[class*='language-']::-moz-selection,
pre[class*='language-']::-moz-selection,
code[class*='language-'] ::-moz-selection,
pre[class*='language-'] ::-moz-selection {
background: #cceae7;
color: #263238;
}
code[class*='language-']::selection,
pre[class*='language-']::selection,
code[class*='language-'] ::selection,
pre[class*='language-'] ::selection {
background: #cceae7;
color: #263238;
}
:not(pre) > code[class*='language-'] {
white-space: normal;
border-radius: 0.2em;
padding: 0.1em;
}
pre[class*='language-'] {
overflow: auto;
position: relative;
margin: 0.5em 0;
padding: 1.25em 1em;
}
.language-css > code,
.language-sass > code,
.language-scss > code {
color: #f76d47;
}
[class*='language-'] .namespace {
opacity: 0.7;
}
.token.atrule {
color: #7c4dff;
}
.token.attr-name {
color: #39adb5;
}
.token.attr-value {
color: #f6a434;
}
.token.attribute {
color: #f6a434;
}
.token.boolean {
color: #7c4dff;
}
.token.builtin {
color: #39adb5;
}
.token.cdata {
color: #39adb5;
}
.token.char {
color: #39adb5;
}
.token.class {
color: #39adb5;
}
.token.class-name {
color: #6182b8;
}
.token.comment {
color: #aabfc9;
}
.token.constant {
color: #7c4dff;
}
.token.deleted {
color: #e53935;
}
.token.doctype {
color: #aabfc9;
}
.token.entity {
color: #e53935;
}
.token.function {
color: #7c4dff;
}
.token.hexcode {
color: #f76d47;
}
.token.id {
color: #7c4dff;
font-weight: bold;
}
.token.important {
color: #7c4dff;
font-weight: bold;
}
.token.inserted {
color: #39adb5;
}
.token.keyword {
color: #7c4dff;
}
.token.number {
color: #f76d47;
}
.token.operator {
color: #39adb5;
}
.token.prolog {
color: #aabfc9;
}
.token.property {
color: #39adb5;
}
.token.pseudo-class {
color: #f6a434;
}
.token.pseudo-element {
color: #f6a434;
}
.token.punctuation {
color: #39adb5;
}
.token.regex {
color: #6182b8;
}
.token.selector {
color: #e53935;
}
.token.string {
color: #f6a434;
}
.token.symbol {
color: #7c4dff;
}
.token.tag {
color: #e53935;
}
.token.unit {
color: #f76d47;
}
.token.url {
color: #e53935;
}
.token.variable {
color: #e53935;
}
@@ -0,0 +1,72 @@
import PropTypes from 'prop-types'
// material-ui
import { useTheme } from '@mui/material/styles'
import MuiAvatar from '@mui/material/Avatar'
// ==============================|| AVATAR ||============================== //
const Avatar = ({ color, outline, size, sx, ...others }) => {
const theme = useTheme()
const colorSX = color && !outline && { color: theme.palette.background.paper, bgcolor: `${color}.main` }
const outlineSX = outline && {
color: color ? `${color}.main` : `primary.main`,
bgcolor: theme.palette.background.paper,
border: '2px solid',
borderColor: color ? `${color}.main` : `primary.main`
}
let sizeSX = {}
switch (size) {
case 'badge':
sizeSX = {
width: theme.spacing(3.5),
height: theme.spacing(3.5)
}
break
case 'xs':
sizeSX = {
width: theme.spacing(4.25),
height: theme.spacing(4.25)
}
break
case 'sm':
sizeSX = {
width: theme.spacing(5),
height: theme.spacing(5)
}
break
case 'lg':
sizeSX = {
width: theme.spacing(9),
height: theme.spacing(9)
}
break
case 'xl':
sizeSX = {
width: theme.spacing(10.25),
height: theme.spacing(10.25)
}
break
case 'md':
sizeSX = {
width: theme.spacing(7.5),
height: theme.spacing(7.5)
}
break
default:
sizeSX = {}
}
return <MuiAvatar sx={{ ...colorSX, ...outlineSX, ...sizeSX, ...sx }} {...others} />
}
Avatar.propTypes = {
className: PropTypes.string,
color: PropTypes.string,
outline: PropTypes.bool,
size: PropTypes.string,
sx: PropTypes.object
}
export default Avatar
@@ -0,0 +1,184 @@
import PropTypes from 'prop-types'
import { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
// material-ui
import { useTheme } from '@mui/material/styles'
import { Box, Card, Divider, Grid, Typography } from '@mui/material'
import MuiBreadcrumbs from '@mui/material/Breadcrumbs'
// project imports
import config from 'config'
import { gridSpacing } from 'store/constant'
// assets
import { IconTallymark1 } from '@tabler/icons'
import AccountTreeTwoToneIcon from '@mui/icons-material/AccountTreeTwoTone'
import HomeIcon from '@mui/icons-material/Home'
import HomeTwoToneIcon from '@mui/icons-material/HomeTwoTone'
const linkSX = {
display: 'flex',
color: 'grey.900',
textDecoration: 'none',
alignContent: 'center',
alignItems: 'center'
}
// ==============================|| BREADCRUMBS ||============================== //
const Breadcrumbs = ({ card, divider, icon, icons, maxItems, navigation, rightAlign, separator, title, titleBottom, ...others }) => {
const theme = useTheme()
const iconStyle = {
marginRight: theme.spacing(0.75),
marginTop: `-${theme.spacing(0.25)}`,
width: '1rem',
height: '1rem',
color: theme.palette.secondary.main
}
const [main, setMain] = useState()
const [item, setItem] = useState()
// set active item state
const getCollapse = (menu) => {
if (menu.children) {
menu.children.filter((collapse) => {
if (collapse.type && collapse.type === 'collapse') {
getCollapse(collapse)
} else if (collapse.type && collapse.type === 'item') {
if (document.location.pathname === config.basename + collapse.url) {
setMain(menu)
setItem(collapse)
}
}
return false
})
}
}
useEffect(() => {
navigation?.items?.map((menu) => {
if (menu.type && menu.type === 'group') {
getCollapse(menu)
}
return false
})
})
// item separator
const SeparatorIcon = separator
const separatorIcon = separator ? <SeparatorIcon stroke={1.5} size='1rem' /> : <IconTallymark1 stroke={1.5} size='1rem' />
let mainContent
let itemContent
let breadcrumbContent = <Typography />
let itemTitle = ''
let CollapseIcon
let ItemIcon
// collapse item
if (main && main.type === 'collapse') {
CollapseIcon = main.icon ? main.icon : AccountTreeTwoToneIcon
mainContent = (
<Typography component={Link} to='#' variant='subtitle1' sx={linkSX}>
{icons && <CollapseIcon style={iconStyle} />}
{main.title}
</Typography>
)
}
// items
if (item && item.type === 'item') {
itemTitle = item.title
ItemIcon = item.icon ? item.icon : AccountTreeTwoToneIcon
itemContent = (
<Typography
variant='subtitle1'
sx={{
display: 'flex',
textDecoration: 'none',
alignContent: 'center',
alignItems: 'center',
color: 'grey.500'
}}
>
{icons && <ItemIcon style={iconStyle} />}
{itemTitle}
</Typography>
)
// main
if (item.breadcrumbs !== false) {
breadcrumbContent = (
<Card
sx={{
border: 'none'
}}
{...others}
>
<Box sx={{ p: 2, pl: card === false ? 0 : 2 }}>
<Grid
container
direction={rightAlign ? 'row' : 'column'}
justifyContent={rightAlign ? 'space-between' : 'flex-start'}
alignItems={rightAlign ? 'center' : 'flex-start'}
spacing={1}
>
{title && !titleBottom && (
<Grid item>
<Typography variant='h3' sx={{ fontWeight: 500 }}>
{item.title}
</Typography>
</Grid>
)}
<Grid item>
<MuiBreadcrumbs
sx={{ '& .MuiBreadcrumbs-separator': { width: 16, ml: 1.25, mr: 1.25 } }}
aria-label='breadcrumb'
maxItems={maxItems || 8}
separator={separatorIcon}
>
<Typography component={Link} to='/' color='inherit' variant='subtitle1' sx={linkSX}>
{icons && <HomeTwoToneIcon sx={iconStyle} />}
{icon && <HomeIcon sx={{ ...iconStyle, mr: 0 }} />}
{!icon && 'Dashboard'}
</Typography>
{mainContent}
{itemContent}
</MuiBreadcrumbs>
</Grid>
{title && titleBottom && (
<Grid item>
<Typography variant='h3' sx={{ fontWeight: 500 }}>
{item.title}
</Typography>
</Grid>
)}
</Grid>
</Box>
{card === false && divider !== false && <Divider sx={{ borderColor: theme.palette.primary.main, mb: gridSpacing }} />}
</Card>
)
}
}
return breadcrumbContent
}
Breadcrumbs.propTypes = {
card: PropTypes.bool,
divider: PropTypes.bool,
icon: PropTypes.bool,
icons: PropTypes.bool,
maxItems: PropTypes.number,
navigation: PropTypes.object,
rightAlign: PropTypes.bool,
separator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
title: PropTypes.bool,
titleBottom: PropTypes.bool
}
export default Breadcrumbs
@@ -0,0 +1,22 @@
import logo from 'assets/images/flowise_logo.png'
import logoDark from 'assets/images/flowise_logo_dark.png'
import { useSelector } from 'react-redux'
// ==============================|| LOGO ||============================== //
const Logo = () => {
const customization = useSelector((state) => state.customization)
return (
<div style={{ alignItems: 'center', display: 'flex', flexDirection: 'row' }}>
<img
style={{ objectFit: 'contain', height: 'auto', width: 150 }}
src={customization.isDarkMode ? logoDark : logo}
alt='Flowise'
/>
</div>
)
}
export default Logo
@@ -0,0 +1,107 @@
import PropTypes from 'prop-types'
import { forwardRef } from 'react'
// material-ui
import { Collapse, Fade, Box, Grow, Slide, Zoom } from '@mui/material'
// ==============================|| TRANSITIONS ||============================== //
const Transitions = forwardRef(function Transitions({ children, position, type, direction, ...others }, ref) {
let positionSX = {
transformOrigin: '0 0 0'
}
switch (position) {
case 'top-right':
positionSX = {
transformOrigin: 'top right'
}
break
case 'top':
positionSX = {
transformOrigin: 'top'
}
break
case 'bottom-left':
positionSX = {
transformOrigin: 'bottom left'
}
break
case 'bottom-right':
positionSX = {
transformOrigin: 'bottom right'
}
break
case 'bottom':
positionSX = {
transformOrigin: 'bottom'
}
break
case 'top-left':
default:
positionSX = {
transformOrigin: '0 0 0'
}
break
}
return (
<Box ref={ref}>
{type === 'grow' && (
<Grow {...others}>
<Box sx={positionSX}>{children}</Box>
</Grow>
)}
{type === 'collapse' && (
<Collapse {...others} sx={positionSX}>
{children}
</Collapse>
)}
{type === 'fade' && (
<Fade
{...others}
timeout={{
appear: 500,
enter: 600,
exit: 400
}}
>
<Box sx={positionSX}>{children}</Box>
</Fade>
)}
{type === 'slide' && (
<Slide
{...others}
timeout={{
appear: 0,
enter: 400,
exit: 200
}}
direction={direction}
>
<Box sx={positionSX}>{children}</Box>
</Slide>
)}
{type === 'zoom' && (
<Zoom {...others}>
<Box sx={positionSX}>{children}</Box>
</Zoom>
)}
</Box>
)
})
Transitions.propTypes = {
children: PropTypes.node,
type: PropTypes.oneOf(['grow', 'fade', 'collapse', 'slide', 'zoom']),
position: PropTypes.oneOf(['top-left', 'top-right', 'top', 'bottom-left', 'bottom-right', 'bottom']),
direction: PropTypes.oneOf(['up', 'down', 'left', 'right'])
}
Transitions.defaultProps = {
type: 'grow',
position: 'top-left',
direction: 'up'
}
export default Transitions
@@ -0,0 +1,32 @@
import { useState } from 'react'
import PropTypes from 'prop-types'
import { FormControl, OutlinedInput } from '@mui/material'
export const Input = ({ inputParam, value, onChange }) => {
const [myValue, setMyValue] = useState(value ?? '')
return (
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
<OutlinedInput
id={inputParam.name}
size='small'
type={inputParam.type === 'string' ? 'text' : inputParam.type}
placeholder={inputParam.placeholder}
multiline={!!inputParam.rows}
maxRows={inputParam.rows || 0}
minRows={inputParam.rows || 0}
value={myValue}
name={inputParam.name}
onChange={(e) => {
setMyValue(e.target.value)
onChange(e.target.value)
}}
/>
</FormControl>
)
}
Input.propTypes = {
inputParam: PropTypes.object,
value: PropTypes.string,
onChange: PropTypes.func
}
@@ -0,0 +1,17 @@
import { Suspense } from 'react'
// project imports
import Loader from './Loader'
// ==============================|| LOADABLE - LAZY LOADING ||============================== //
const Loadable = (Component) =>
function WithLoader(props) {
return (
<Suspense fallback={<Loader />}>
<Component {...props} />
</Suspense>
)
}
export default Loadable
@@ -0,0 +1,21 @@
// material-ui
import LinearProgress from '@mui/material/LinearProgress'
import { styled } from '@mui/material/styles'
// styles
const LoaderWrapper = styled('div')({
position: 'fixed',
top: 0,
left: 0,
zIndex: 1301,
width: '100%'
})
// ==============================|| LOADER ||============================== //
const Loader = () => (
<LoaderWrapper>
<LinearProgress color='primary' />
</LoaderWrapper>
)
export default Loader
@@ -0,0 +1,25 @@
import { Info } from '@mui/icons-material'
import { IconButton, Tooltip } from '@mui/material'
import parser from 'html-react-parser'
import PropTypes from 'prop-types'
import { useSelector } from 'react-redux'
export const TooltipWithParser = ({ title }) => {
const customization = useSelector((state) => state.customization)
return (
<Tooltip title={parser(title)} placement='right'>
<div style={{ display: 'flex', alignItems: 'center' }}>
<IconButton sx={{ height: 25, width: 25 }}>
<Info
style={{ background: 'transparent', color: customization.isDarkMode ? 'white' : 'inherit', height: 18, width: 18 }}
/>
</IconButton>
</div>
</Tooltip>
)
}
TooltipWithParser.propTypes = {
title: PropTypes.node
}