mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 17:01:00 +03:00
UI Improvements (#1935)
* Update styles for dashboard page * Fix grid in chatflows and marketplaces pages * Update styles for main routes * Create ViewHeader component and use it in chatflows and marketplace * Use viewheader in all main routes views and make the styles consistent * Update table styles for chatflow and marketplace views * Update table and grid styles in all main routes views * Make backgrounds, borders, and colors everywhere * Apply text ellipsis for titles in cards and tables * Update credentials list dialog styles * Update tools dialog styles * Update styles for inputs and dialogs * Show skeleton loaders for main routes * Apply text ellipsis to chatflow title in canvas page * Update icons for load and export buttons in tools and assistants * Fix issue where table header is shown when number of elements is zero * Add error boundary component to main routes * Capture errors from all requests in main routes * Fix id for add api key and add variable buttons * Fix missing th tag in variables table body
This commit is contained in:
@@ -72,7 +72,7 @@ const StyledMenu = styled((props) => (
|
||||
}
|
||||
}))
|
||||
|
||||
export default function FlowListMenu({ chatflow, updateFlowsApi }) {
|
||||
export default function FlowListMenu({ chatflow, setError, updateFlowsApi }) {
|
||||
const { confirm } = useConfirm()
|
||||
const dispatch = useDispatch()
|
||||
const updateChatflowApi = useApi(chatflowsApi.updateChatflow)
|
||||
@@ -153,6 +153,7 @@ export default function FlowListMenu({ chatflow, updateFlowsApi }) {
|
||||
await updateChatflowApi.request(chatflow.id, updateBody)
|
||||
await updateFlowsApi.request()
|
||||
} catch (error) {
|
||||
setError(error)
|
||||
enqueueSnackbar({
|
||||
message: error.response.data.message,
|
||||
options: {
|
||||
@@ -191,6 +192,7 @@ export default function FlowListMenu({ chatflow, updateFlowsApi }) {
|
||||
await updateChatflowApi.request(chatflow.id, updateBody)
|
||||
await updateFlowsApi.request()
|
||||
} catch (error) {
|
||||
setError(error)
|
||||
enqueueSnackbar({
|
||||
message: error.response.data.message,
|
||||
options: {
|
||||
@@ -222,6 +224,7 @@ export default function FlowListMenu({ chatflow, updateFlowsApi }) {
|
||||
await chatflowsApi.deleteChatflow(chatflow.id)
|
||||
await updateFlowsApi.request()
|
||||
} catch (error) {
|
||||
setError(error)
|
||||
enqueueSnackbar({
|
||||
message: error.response.data.message,
|
||||
options: {
|
||||
@@ -370,5 +373,6 @@ export default function FlowListMenu({ chatflow, updateFlowsApi }) {
|
||||
|
||||
FlowListMenu.propTypes = {
|
||||
chatflow: PropTypes.object,
|
||||
setError: PropTypes.func,
|
||||
updateFlowsApi: PropTypes.object
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
// material-ui
|
||||
import { styled } from '@mui/material/styles'
|
||||
import { Box, Grid, Typography } from '@mui/material'
|
||||
import { Box, Grid, Typography, useTheme } 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,
|
||||
@@ -19,101 +19,115 @@ const CardWrapper = styled(MainCard)(({ theme }) => ({
|
||||
background: theme.palette.card.hover,
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 20%)'
|
||||
},
|
||||
height: '100%',
|
||||
minHeight: '160px',
|
||||
maxHeight: '300px',
|
||||
maxWidth: '300px',
|
||||
width: '100%',
|
||||
overflowWrap: 'break-word',
|
||||
whiteSpace: 'pre-line'
|
||||
}))
|
||||
|
||||
// ===========================|| CONTRACT CARD ||=========================== //
|
||||
|
||||
const ItemCard = ({ isLoading, data, images, onClick }) => {
|
||||
const ItemCard = ({ data, images, onClick }) => {
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
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'
|
||||
}}
|
||||
>
|
||||
{data.iconSrc && (
|
||||
<div
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
marginRight: 10,
|
||||
borderRadius: '50%',
|
||||
background: `url(${data.iconSrc})`,
|
||||
backgroundSize: 'contain',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'center center'
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
{!data.iconSrc && data.color && (
|
||||
<div
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
marginRight: 10,
|
||||
borderRadius: '50%',
|
||||
background: data.color
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
<Typography
|
||||
sx={{ fontSize: '1.5rem', fontWeight: 500, overflowWrap: 'break-word', whiteSpace: 'pre-line' }}
|
||||
>
|
||||
{data.templateName || data.name}
|
||||
</Typography>
|
||||
</div>
|
||||
{data.description && (
|
||||
<span style={{ marginTop: 10, overflowWrap: 'break-word', whiteSpace: 'pre-line' }}>
|
||||
{data.description}
|
||||
</span>
|
||||
)}
|
||||
{images && (
|
||||
<CardWrapper content={false} onClick={onClick} sx={{ border: 1, borderColor: theme.palette.grey[900] + 25, borderRadius: 2 }}>
|
||||
<Box sx={{ height: '100%', p: 2.25 }}>
|
||||
<Grid container justifyContent='space-between' direction='column' sx={{ height: '100%', gap: 3 }}>
|
||||
<Box display='flex' flexDirection='column' sx={{ width: '100%' }}>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
{data.iconSrc && (
|
||||
<div
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 5
|
||||
flexShrink: 0,
|
||||
marginRight: 10,
|
||||
borderRadius: '50%',
|
||||
background: `url(${data.iconSrc})`,
|
||||
backgroundSize: 'contain',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'center center'
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
{!data.iconSrc && data.color && (
|
||||
<div
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
display: 'flex',
|
||||
flexShrink: 0,
|
||||
marginRight: 10,
|
||||
borderRadius: '50%',
|
||||
background: data.color
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
display: '-webkit-box',
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 500,
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
{data.templateName || data.name}
|
||||
</Typography>
|
||||
</div>
|
||||
{data.description && (
|
||||
<span style={{ marginTop: 10, overflowWrap: 'break-word', whiteSpace: 'pre-line' }}>{data.description}</span>
|
||||
)}
|
||||
</Box>
|
||||
{images && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'start',
|
||||
gap: 1
|
||||
}}
|
||||
>
|
||||
{images.slice(0, images.length > 3 ? 3 : images.length).map((img) => (
|
||||
<Box
|
||||
key={img}
|
||||
sx={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: customization.isDarkMode
|
||||
? theme.palette.common.white
|
||||
: theme.palette.grey[300] + 75
|
||||
}}
|
||||
>
|
||||
{images.map((img) => (
|
||||
<div
|
||||
key={img}
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
marginRight: 5,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'white',
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }}
|
||||
alt=''
|
||||
src={img}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<img style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }} alt='' src={img} />
|
||||
</Box>
|
||||
))}
|
||||
{images.length > 3 && (
|
||||
<Typography sx={{ alignItems: 'center', display: 'flex', fontSize: '.9rem', fontWeight: 200 }}>
|
||||
+ {images.length - 3} More
|
||||
</Typography>
|
||||
)}
|
||||
</Grid>
|
||||
</Box>
|
||||
</CardWrapper>
|
||||
)}
|
||||
</>
|
||||
</Box>
|
||||
)}
|
||||
</Grid>
|
||||
</Box>
|
||||
</CardWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ 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
|
||||
@@ -14,12 +13,14 @@ const headerSX = {
|
||||
|
||||
const MainCard = forwardRef(function MainCard(
|
||||
{
|
||||
border = true,
|
||||
boxShadow,
|
||||
children,
|
||||
content = true,
|
||||
contentClass = '',
|
||||
contentSX = {},
|
||||
contentSX = {
|
||||
px: 2,
|
||||
py: 0
|
||||
},
|
||||
darkTitle,
|
||||
secondary,
|
||||
shadow,
|
||||
@@ -29,18 +30,17 @@ const MainCard = forwardRef(function MainCard(
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const theme = useTheme()
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={ref}
|
||||
{...others}
|
||||
sx={{
|
||||
border: border ? '1px solid' : 'none',
|
||||
borderColor: theme.palette.primary[200] + 75,
|
||||
background: 'transparent',
|
||||
':hover': {
|
||||
boxShadow: boxShadow ? shadow || '0 2px 14px 0 rgb(32 40 45 / 8%)' : 'inherit'
|
||||
},
|
||||
maxWidth: '1280px',
|
||||
mx: 'auto',
|
||||
...sx
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -114,7 +114,7 @@ export const AsyncDropdown = ({
|
||||
disabled={disabled}
|
||||
disableClearable={disableClearable}
|
||||
size='small'
|
||||
sx={{ width: '100%' }}
|
||||
sx={{ width: '100%', height: '52px' }}
|
||||
open={open}
|
||||
onOpen={() => {
|
||||
setOpen(true)
|
||||
@@ -148,6 +148,7 @@ export const AsyncDropdown = ({
|
||||
</Fragment>
|
||||
)
|
||||
}}
|
||||
sx={{ height: '100%', '& .MuiInputBase-root': { height: '100%' } }}
|
||||
/>
|
||||
)}
|
||||
renderOption={(props, option) => (
|
||||
|
||||
@@ -25,7 +25,7 @@ export const Dropdown = ({ name, value, options, onSelect, disabled = false, dis
|
||||
let [internalValue, setInternalValue] = useState(value ?? 'choose an option')
|
||||
|
||||
return (
|
||||
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
|
||||
<FormControl sx={{ width: '100%', height: '52px' }} size='small'>
|
||||
<Autocomplete
|
||||
id={name}
|
||||
disabled={disabled}
|
||||
@@ -39,7 +39,9 @@ export const Dropdown = ({ name, value, options, onSelect, disabled = false, dis
|
||||
onSelect(value)
|
||||
}}
|
||||
PopperComponent={StyledPopper}
|
||||
renderInput={(params) => <TextField {...params} value={internalValue} />}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} value={internalValue} sx={{ height: '100%', '& .MuiInputBase-root': { height: '100%' } }} />
|
||||
)}
|
||||
renderOption={(props, option) => (
|
||||
<Box component='li' {...props}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
@@ -50,6 +52,7 @@ export const Dropdown = ({ name, value, options, onSelect, disabled = false, dis
|
||||
</div>
|
||||
</Box>
|
||||
)}
|
||||
sx={{ height: '100%' }}
|
||||
/>
|
||||
</FormControl>
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ export const MultiDropdown = ({ name, value, options, onSelect, formControlSx =
|
||||
let [internalValue, setInternalValue] = useState(value ?? [])
|
||||
|
||||
return (
|
||||
<FormControl sx={{ mt: 1, width: '100%', ...formControlSx }} size='small'>
|
||||
<FormControl sx={{ width: '100%', height: '52px', ...formControlSx }} size='small'>
|
||||
<Autocomplete
|
||||
id={name}
|
||||
disabled={disabled}
|
||||
@@ -53,7 +53,9 @@ export const MultiDropdown = ({ name, value, options, onSelect, formControlSx =
|
||||
onSelect(value)
|
||||
}}
|
||||
PopperComponent={StyledPopper}
|
||||
renderInput={(params) => <TextField {...params} value={internalValue} />}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} value={internalValue} sx={{ height: '100%', '& .MuiInputBase-root': { height: '100%' } }} />
|
||||
)}
|
||||
renderOption={(props, option) => (
|
||||
<Box component='li' {...props}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
@@ -64,6 +66,7 @@ export const MultiDropdown = ({ name, value, options, onSelect, formControlSx =
|
||||
</div>
|
||||
</Box>
|
||||
)}
|
||||
sx={{ height: '100%' }}
|
||||
/>
|
||||
</FormControl>
|
||||
)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { DataGrid } from '@mui/x-data-grid'
|
||||
import { IconPlus } from '@tabler/icons'
|
||||
import { Button } from '@mui/material'
|
||||
|
||||
export const Grid = ({ columns, rows, style, disabled = false, onRowUpdate, addNewRow }) => {
|
||||
export const Grid = ({ columns, rows, style, disabled = false, onRowUpdate }) => {
|
||||
const handleProcessRowUpdate = (newRow) => {
|
||||
onRowUpdate(newRow)
|
||||
return newRow
|
||||
@@ -11,11 +9,6 @@ export const Grid = ({ columns, rows, style, disabled = false, onRowUpdate, addN
|
||||
|
||||
return (
|
||||
<>
|
||||
{!disabled && (
|
||||
<Button variant='outlined' onClick={addNewRow} startIcon={<IconPlus />}>
|
||||
Add Item
|
||||
</Button>
|
||||
)}
|
||||
{rows && columns && (
|
||||
<div style={{ marginTop: 10, height: 300, width: '100%', ...style }}>
|
||||
<DataGrid
|
||||
@@ -38,6 +31,5 @@ Grid.propTypes = {
|
||||
columns: PropTypes.array,
|
||||
style: PropTypes.any,
|
||||
disabled: PropTypes.bool,
|
||||
addNewRow: PropTypes.func,
|
||||
onRowUpdate: PropTypes.func
|
||||
}
|
||||
|
||||
@@ -1,50 +1,61 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useSelector } from 'react-redux'
|
||||
import moment from 'moment'
|
||||
import { styled } from '@mui/material/styles'
|
||||
import Table from '@mui/material/Table'
|
||||
import TableBody from '@mui/material/TableBody'
|
||||
import TableCell, { tableCellClasses } from '@mui/material/TableCell'
|
||||
import TableContainer from '@mui/material/TableContainer'
|
||||
import TableHead from '@mui/material/TableHead'
|
||||
import TableRow from '@mui/material/TableRow'
|
||||
import Paper from '@mui/material/Paper'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import { Button, Stack, Typography } from '@mui/material'
|
||||
import {
|
||||
Box,
|
||||
Chip,
|
||||
Paper,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material'
|
||||
import { tableCellClasses } from '@mui/material/TableCell'
|
||||
import FlowListMenu from '../button/FlowListMenu'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||
borderColor: theme.palette.grey[900] + 25,
|
||||
|
||||
[`&.${tableCellClasses.head}`]: {
|
||||
backgroundColor: theme.palette.common.black,
|
||||
color: theme.palette.common.white
|
||||
color: theme.palette.grey[900]
|
||||
},
|
||||
[`&.${tableCellClasses.body}`]: {
|
||||
fontSize: 14
|
||||
fontSize: 14,
|
||||
height: 64
|
||||
}
|
||||
}))
|
||||
|
||||
const StyledTableRow = styled(TableRow)(({ theme }) => ({
|
||||
'&:nth-of-type(odd)': {
|
||||
backgroundColor: theme.palette.action.hover
|
||||
},
|
||||
const StyledTableRow = styled(TableRow)(() => ({
|
||||
// hide last border
|
||||
'&:last-child td, &:last-child th': {
|
||||
border: 0
|
||||
}
|
||||
}))
|
||||
|
||||
export const FlowListTable = ({ data, images, filterFunction, updateFlowsApi }) => {
|
||||
const navigate = useNavigate()
|
||||
const goToCanvas = (selectedChatflow) => {
|
||||
navigate(`/canvas/${selectedChatflow.id}`)
|
||||
}
|
||||
export const FlowListTable = ({ data, images, isLoading, filterFunction, updateFlowsApi, setError }) => {
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableContainer style={{ marginTop: '30', border: 1 }} component={Paper}>
|
||||
<TableContainer sx={{ border: 1, borderColor: theme.palette.grey[900] + 25, borderRadius: 2 }} component={Paper}>
|
||||
<Table sx={{ minWidth: 650 }} size='small' aria-label='a dense table'>
|
||||
<TableHead>
|
||||
<TableRow sx={{ marginTop: '10', backgroundColor: 'primary' }}>
|
||||
<TableHead
|
||||
sx={{
|
||||
backgroundColor: customization.isDarkMode ? theme.palette.common.black : theme.palette.grey[100],
|
||||
height: 56
|
||||
}}
|
||||
>
|
||||
<TableRow>
|
||||
<StyledTableCell component='th' scope='row' style={{ width: '20%' }} key='0'>
|
||||
Name
|
||||
</StyledTableCell>
|
||||
@@ -63,82 +74,150 @@ export const FlowListTable = ({ data, images, filterFunction, updateFlowsApi })
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{data.filter(filterFunction).map((row, index) => (
|
||||
<StyledTableRow key={index}>
|
||||
<TableCell key='0'>
|
||||
<Typography
|
||||
sx={{ fontSize: '1.2rem', fontWeight: 500, overflowWrap: 'break-word', whiteSpace: 'pre-line' }}
|
||||
>
|
||||
<Button onClick={() => goToCanvas(row)} sx={{ textAlign: 'left' }}>
|
||||
{row.templateName || row.name}
|
||||
</Button>
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell key='1'>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
|
||||
{row.category &&
|
||||
row.category
|
||||
.split(';')
|
||||
.map((tag, index) => (
|
||||
<Chip key={index} label={tag} style={{ marginRight: 5, marginBottom: 5 }} />
|
||||
))}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell key='2'>
|
||||
{images[row.id] && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
{images[row.id].slice(0, images[row.id].length > 5 ? 5 : images[row.id].length).map((img) => (
|
||||
<div
|
||||
key={img}
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
marginRight: 5,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'white',
|
||||
marginTop: 5
|
||||
{isLoading ? (
|
||||
<>
|
||||
<StyledTableRow>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
</StyledTableRow>
|
||||
<StyledTableRow>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
</StyledTableRow>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{data?.filter(filterFunction).map((row, index) => (
|
||||
<StyledTableRow key={index}>
|
||||
<StyledTableCell key='0'>
|
||||
<Tooltip title={row.templateName || row.name}>
|
||||
<Typography
|
||||
sx={{
|
||||
display: '-webkit-box',
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }}
|
||||
alt=''
|
||||
src={img}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{images[row.id].length > 5 && (
|
||||
<Typography
|
||||
sx={{ alignItems: 'center', display: 'flex', fontSize: '.8rem', fontWeight: 200 }}
|
||||
>
|
||||
+ {images[row.id].length - 5} More
|
||||
<Link to={`/canvas/${row.id}`} style={{ color: '#2196f3', textDecoration: 'none' }}>
|
||||
{row.templateName || row.name}
|
||||
</Link>
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell key='1'>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
|
||||
{row.category &&
|
||||
row.category
|
||||
.split(';')
|
||||
.map((tag, index) => (
|
||||
<Chip key={index} label={tag} style={{ marginRight: 5, marginBottom: 5 }} />
|
||||
))}
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell key='2'>
|
||||
{images[row.id] && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'start',
|
||||
gap: 1
|
||||
}}
|
||||
>
|
||||
{images[row.id]
|
||||
.slice(0, images[row.id].length > 5 ? 5 : images[row.id].length)
|
||||
.map((img) => (
|
||||
<Box
|
||||
key={img}
|
||||
sx={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: customization.isDarkMode
|
||||
? theme.palette.common.white
|
||||
: theme.palette.grey[300] + 75
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: 5,
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
alt=''
|
||||
src={img}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
{images[row.id].length > 5 && (
|
||||
<Typography
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
fontSize: '.9rem',
|
||||
fontWeight: 200
|
||||
}}
|
||||
>
|
||||
+ {images[row.id].length - 5} More
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell key='3'>{moment(row.updatedDate).format('MMMM Do, YYYY')}</TableCell>
|
||||
<TableCell key='4'>
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1} justifyContent='center' alignItems='center'>
|
||||
<FlowListMenu chatflow={row} updateFlowsApi={updateFlowsApi} />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
))}
|
||||
</StyledTableCell>
|
||||
<StyledTableCell key='3'>{moment(row.updatedDate).format('MMMM Do, YYYY')}</StyledTableCell>
|
||||
<StyledTableCell key='4'>
|
||||
<Stack
|
||||
direction={{ xs: 'column', sm: 'row' }}
|
||||
spacing={1}
|
||||
justifyContent='center'
|
||||
alignItems='center'
|
||||
>
|
||||
<FlowListMenu chatflow={row} setError={setError} updateFlowsApi={updateFlowsApi} />
|
||||
</Stack>
|
||||
</StyledTableCell>
|
||||
</StyledTableRow>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
@@ -149,6 +228,8 @@ export const FlowListTable = ({ data, images, filterFunction, updateFlowsApi })
|
||||
FlowListTable.propTypes = {
|
||||
data: PropTypes.array,
|
||||
images: PropTypes.object,
|
||||
isLoading: PropTypes.bool,
|
||||
filterFunction: PropTypes.func,
|
||||
updateFlowsApi: PropTypes.object
|
||||
updateFlowsApi: PropTypes.object,
|
||||
setError: PropTypes.func
|
||||
}
|
||||
|
||||
@@ -1,36 +1,54 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { styled } from '@mui/material/styles'
|
||||
import Table from '@mui/material/Table'
|
||||
import TableBody from '@mui/material/TableBody'
|
||||
import TableCell, { tableCellClasses } from '@mui/material/TableCell'
|
||||
import TableContainer from '@mui/material/TableContainer'
|
||||
import TableHead from '@mui/material/TableHead'
|
||||
import TableRow from '@mui/material/TableRow'
|
||||
import Paper from '@mui/material/Paper'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import { Button, Typography } from '@mui/material'
|
||||
import { tableCellClasses } from '@mui/material/TableCell'
|
||||
import {
|
||||
Button,
|
||||
Chip,
|
||||
Paper,
|
||||
Skeleton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material'
|
||||
|
||||
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||
borderColor: theme.palette.grey[900] + 25,
|
||||
|
||||
[`&.${tableCellClasses.head}`]: {
|
||||
backgroundColor: theme.palette.common.black,
|
||||
color: theme.palette.common.white
|
||||
color: theme.palette.grey[900]
|
||||
},
|
||||
[`&.${tableCellClasses.body}`]: {
|
||||
fontSize: 14
|
||||
fontSize: 14,
|
||||
height: 64
|
||||
}
|
||||
}))
|
||||
|
||||
const StyledTableRow = styled(TableRow)(({ theme }) => ({
|
||||
'&:nth-of-type(odd)': {
|
||||
backgroundColor: theme.palette.action.hover
|
||||
},
|
||||
const StyledTableRow = styled(TableRow)(() => ({
|
||||
// hide last border
|
||||
'&:last-child td, &:last-child th': {
|
||||
border: 0
|
||||
}
|
||||
}))
|
||||
|
||||
export const MarketplaceTable = ({ data, filterFunction, filterByBadge, filterByType, filterByFramework, goToCanvas, goToTool }) => {
|
||||
export const MarketplaceTable = ({
|
||||
data,
|
||||
filterFunction,
|
||||
filterByBadge,
|
||||
filterByType,
|
||||
filterByFramework,
|
||||
goToCanvas,
|
||||
goToTool,
|
||||
isLoading
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
const openTemplate = (selectedTemplate) => {
|
||||
if (selectedTemplate.flowData) {
|
||||
goToCanvas(selectedTemplate)
|
||||
@@ -41,10 +59,15 @@ export const MarketplaceTable = ({ data, filterFunction, filterByBadge, filterBy
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableContainer style={{ marginTop: '30', border: 1 }} component={Paper}>
|
||||
<TableContainer sx={{ border: 1, borderColor: theme.palette.grey[900] + 25, borderRadius: 2 }} component={Paper}>
|
||||
<Table sx={{ minWidth: 650 }} size='small' aria-label='a dense table'>
|
||||
<TableHead>
|
||||
<TableRow sx={{ marginTop: '10', backgroundColor: 'primary' }}>
|
||||
<TableHead
|
||||
sx={{
|
||||
backgroundColor: customization.isDarkMode ? theme.palette.common.black : theme.palette.grey[100],
|
||||
height: 56
|
||||
}}
|
||||
>
|
||||
<TableRow>
|
||||
<StyledTableCell component='th' scope='row' style={{ width: '15%' }} key='0'>
|
||||
Name
|
||||
</StyledTableCell>
|
||||
@@ -63,71 +86,120 @@ export const MarketplaceTable = ({ data, filterFunction, filterByBadge, filterBy
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{data
|
||||
.filter(filterByBadge)
|
||||
.filter(filterByType)
|
||||
.filter(filterFunction)
|
||||
.filter(filterByFramework)
|
||||
.map((row, index) => (
|
||||
<StyledTableRow key={index}>
|
||||
<TableCell key='0'>
|
||||
<Typography
|
||||
sx={{ fontSize: '1.2rem', fontWeight: 500, overflowWrap: 'break-word', whiteSpace: 'pre-line' }}
|
||||
>
|
||||
<Button onClick={() => openTemplate(row)} sx={{ textAlign: 'left' }}>
|
||||
{row.templateName || row.name}
|
||||
</Button>
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell key='1'>
|
||||
<Typography>{row.type}</Typography>
|
||||
</TableCell>
|
||||
<TableCell key='2'>
|
||||
<Typography sx={{ overflowWrap: 'break-word', whiteSpace: 'pre-line' }}>
|
||||
{row.description || ''}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell key='3'>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
{row.categories &&
|
||||
row.categories
|
||||
.split(',')
|
||||
.map((tag, index) => (
|
||||
<Chip
|
||||
variant='outlined'
|
||||
key={index}
|
||||
size='small'
|
||||
label={tag.toUpperCase()}
|
||||
style={{ marginRight: 3, marginBottom: 3 }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell key='4'>
|
||||
<Typography>
|
||||
{row.badge &&
|
||||
row.badge
|
||||
.split(';')
|
||||
.map((tag, index) => (
|
||||
<Chip
|
||||
color={tag === 'POPULAR' ? 'primary' : 'error'}
|
||||
key={index}
|
||||
size='small'
|
||||
label={tag.toUpperCase()}
|
||||
style={{ marginRight: 5, marginBottom: 5 }}
|
||||
/>
|
||||
))}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<StyledTableRow>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
</StyledTableRow>
|
||||
))}
|
||||
<StyledTableRow>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Skeleton variant='text' />
|
||||
</StyledTableCell>
|
||||
</StyledTableRow>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{data
|
||||
?.filter(filterByBadge)
|
||||
.filter(filterByType)
|
||||
.filter(filterFunction)
|
||||
.filter(filterByFramework)
|
||||
.map((row, index) => (
|
||||
<StyledTableRow key={index}>
|
||||
<StyledTableCell key='0'>
|
||||
<Typography
|
||||
sx={{
|
||||
display: '-webkit-box',
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => openTemplate(row)} sx={{ textAlign: 'left' }}>
|
||||
{row.templateName || row.name}
|
||||
</Button>
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell key='1'>
|
||||
<Typography>{row.type}</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell key='2'>
|
||||
<Typography sx={{ overflowWrap: 'break-word', whiteSpace: 'pre-line' }}>
|
||||
{row.description || ''}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell key='3'>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
{row.categories &&
|
||||
row.categories
|
||||
.split(',')
|
||||
.map((tag, index) => (
|
||||
<Chip
|
||||
variant='outlined'
|
||||
key={index}
|
||||
size='small'
|
||||
label={tag.toUpperCase()}
|
||||
style={{ marginRight: 3, marginBottom: 3 }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell key='4'>
|
||||
<Typography>
|
||||
{row.badge &&
|
||||
row.badge
|
||||
.split(';')
|
||||
.map((tag, index) => (
|
||||
<Chip
|
||||
color={tag === 'POPULAR' ? 'primary' : 'error'}
|
||||
key={index}
|
||||
size='small'
|
||||
label={tag.toUpperCase()}
|
||||
style={{ marginRight: 5, marginBottom: 5 }}
|
||||
/>
|
||||
))}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
</StyledTableRow>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
@@ -142,5 +214,6 @@ MarketplaceTable.propTypes = {
|
||||
filterByType: PropTypes.func,
|
||||
filterByFramework: PropTypes.func,
|
||||
goToTool: PropTypes.func,
|
||||
goToCanvas: PropTypes.func
|
||||
goToCanvas: PropTypes.func,
|
||||
isLoading: PropTypes.bool
|
||||
}
|
||||
|
||||
@@ -4,15 +4,15 @@ import parser from 'html-react-parser'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
export const TooltipWithParser = ({ title, style }) => {
|
||||
export const TooltipWithParser = ({ title, sx }) => {
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
return (
|
||||
<Tooltip title={parser(title)} placement='right'>
|
||||
<IconButton sx={{ height: 15, width: 15 }}>
|
||||
<IconButton sx={{ height: 15, width: 15, ml: 2, mt: -0.5 }}>
|
||||
<Info
|
||||
style={{
|
||||
...style,
|
||||
sx={{
|
||||
...sx,
|
||||
background: 'transparent',
|
||||
color: customization.isDarkMode ? 'white' : 'inherit',
|
||||
height: 15,
|
||||
@@ -26,5 +26,5 @@ export const TooltipWithParser = ({ title, style }) => {
|
||||
|
||||
TooltipWithParser.propTypes = {
|
||||
title: PropTypes.node,
|
||||
style: PropTypes.any
|
||||
sx: PropTypes.any
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user