Marketplace: Revamped UI
This commit is contained in:
parent
6013743705
commit
5543ef3de4
|
|
@ -1223,7 +1223,6 @@ export class App {
|
||||||
// Marketplaces
|
// Marketplaces
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
// Get all chatflows for marketplaces
|
|
||||||
this.app.get('/api/v1/marketplaces/chatflows', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/marketplaces/chatflows', async (req: Request, res: Response) => {
|
||||||
const marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'chatflows')
|
const marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'chatflows')
|
||||||
const jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
|
const jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
|
||||||
|
|
@ -1250,6 +1249,49 @@ export class App {
|
||||||
return res.json(templates)
|
return res.json(templates)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Get all chatflows for marketplaces
|
||||||
|
this.app.get('/api/v1/marketplaces/templates', async (req: Request, res: Response) => {
|
||||||
|
let marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'chatflows')
|
||||||
|
let jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
|
||||||
|
let templates: any[] = []
|
||||||
|
jsonsInDir.forEach((file, index) => {
|
||||||
|
const filePath = path.join(__dirname, '..', 'marketplaces', 'chatflows', file)
|
||||||
|
const fileData = fs.readFileSync(filePath)
|
||||||
|
const fileDataObj = JSON.parse(fileData.toString())
|
||||||
|
const template = {
|
||||||
|
id: index,
|
||||||
|
templateName: file.split('.json')[0],
|
||||||
|
flowData: fileData.toString(),
|
||||||
|
badge: fileDataObj?.badge,
|
||||||
|
type: 'Chatflow',
|
||||||
|
description: fileDataObj?.description || ''
|
||||||
|
}
|
||||||
|
templates.push(template)
|
||||||
|
})
|
||||||
|
|
||||||
|
marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'tools')
|
||||||
|
jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
|
||||||
|
jsonsInDir.forEach((file, index) => {
|
||||||
|
const filePath = path.join(__dirname, '..', 'marketplaces', 'tools', file)
|
||||||
|
const fileData = fs.readFileSync(filePath)
|
||||||
|
const fileDataObj = JSON.parse(fileData.toString())
|
||||||
|
const template = {
|
||||||
|
...fileDataObj,
|
||||||
|
id: index,
|
||||||
|
type: 'Tool',
|
||||||
|
templateName: file.split('.json')[0]
|
||||||
|
}
|
||||||
|
templates.push(template)
|
||||||
|
})
|
||||||
|
const FlowiseDocsQnA = templates.find((tmp) => tmp.name === 'Flowise Docs QnA')
|
||||||
|
const FlowiseDocsQnAIndex = templates.findIndex((tmp) => tmp.name === 'Flowise Docs QnA')
|
||||||
|
if (FlowiseDocsQnA && FlowiseDocsQnAIndex > 0) {
|
||||||
|
templates.splice(FlowiseDocsQnAIndex, 1)
|
||||||
|
templates.unshift(FlowiseDocsQnA)
|
||||||
|
}
|
||||||
|
return res.json(templates.sort((a, b) => a.templateName.localeCompare(b.templateName)))
|
||||||
|
})
|
||||||
|
|
||||||
// Get all tools for marketplaces
|
// Get all tools for marketplaces
|
||||||
this.app.get('/api/v1/marketplaces/tools', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/marketplaces/tools', async (req: Request, res: Response) => {
|
||||||
const marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'tools')
|
const marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'tools')
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
ignoreWarnings: [/Failed to parse source map/] // Ignore warnings about source maps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ import client from './client'
|
||||||
|
|
||||||
const getAllChatflowsMarketplaces = () => client.get('/marketplaces/chatflows')
|
const getAllChatflowsMarketplaces = () => client.get('/marketplaces/chatflows')
|
||||||
const getAllToolsMarketplaces = () => client.get('/marketplaces/tools')
|
const getAllToolsMarketplaces = () => client.get('/marketplaces/tools')
|
||||||
|
const getAllTemplatesFromMarketplaces = () => client.get('/marketplaces/templates')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getAllChatflowsMarketplaces,
|
getAllChatflowsMarketplaces,
|
||||||
getAllToolsMarketplaces
|
getAllToolsMarketplaces,
|
||||||
|
getAllTemplatesFromMarketplaces
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
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'
|
||||||
|
|
||||||
|
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||||
|
[`&.${tableCellClasses.head}`]: {
|
||||||
|
backgroundColor: theme.palette.common.black,
|
||||||
|
color: theme.palette.common.white
|
||||||
|
},
|
||||||
|
[`&.${tableCellClasses.body}`]: {
|
||||||
|
fontSize: 14
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
const StyledTableRow = styled(TableRow)(({ theme }) => ({
|
||||||
|
'&:nth-of-type(odd)': {
|
||||||
|
backgroundColor: theme.palette.action.hover
|
||||||
|
},
|
||||||
|
// hide last border
|
||||||
|
'&:last-child td, &:last-child th': {
|
||||||
|
border: 0
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const MarketplaceTable = ({ data, images, filterFunction, filterByBadge, filterByType }) => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const openTemplate = (selectedTemplate) => {
|
||||||
|
if (selectedTemplate.flowData) {
|
||||||
|
goToCanvas(selectedTemplate)
|
||||||
|
} else {
|
||||||
|
goToTool(selectedTemplate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToTool = (selectedTool) => {
|
||||||
|
const dialogProp = {
|
||||||
|
title: selectedTool.templateName,
|
||||||
|
type: 'TEMPLATE',
|
||||||
|
data: selectedTool
|
||||||
|
}
|
||||||
|
setToolDialogProps(dialogProp)
|
||||||
|
setShowToolDialog(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToCanvas = (selectedChatflow) => {
|
||||||
|
navigate(`/marketplace/${selectedChatflow.id}`, { state: selectedChatflow })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TableContainer style={{ marginTop: '30', border: 1 }} component={Paper}>
|
||||||
|
<Table sx={{ minWidth: 650 }} size='small' aria-label='a dense table'>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow sx={{ marginTop: '10', backgroundColor: 'primary' }}>
|
||||||
|
<StyledTableCell component='th' scope='row' style={{ width: '15%' }} key='0'>
|
||||||
|
Name
|
||||||
|
</StyledTableCell>
|
||||||
|
<StyledTableCell component='th' scope='row' style={{ width: '10%' }} key='1'>
|
||||||
|
Type
|
||||||
|
</StyledTableCell>
|
||||||
|
<StyledTableCell style={{ width: '35%' }} key='2'>
|
||||||
|
Description
|
||||||
|
</StyledTableCell>
|
||||||
|
<StyledTableCell style={{ width: '35%' }} key='3'>
|
||||||
|
Nodes
|
||||||
|
</StyledTableCell>
|
||||||
|
<StyledTableCell component='th' scope='row' style={{ width: '5%' }} key='4'>
|
||||||
|
|
||||||
|
</StyledTableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{data
|
||||||
|
.filter(filterByBadge)
|
||||||
|
.filter(filterByType)
|
||||||
|
.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={() => openTemplate(row)} sx={{ textAlign: 'left' }}>
|
||||||
|
{row.templateName || row.name}
|
||||||
|
</Button>
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell key='1'>
|
||||||
|
<Typography>{row.type}</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell key='1'>
|
||||||
|
<Typography sx={{ overflowWrap: 'break-word', whiteSpace: 'pre-line' }}>
|
||||||
|
{row.description || ''}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell key='2'>
|
||||||
|
{row.type === 'Chatflow' && 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
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<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
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell key='3'>
|
||||||
|
<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>
|
||||||
|
</StyledTableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
MarketplaceTable.propTypes = {
|
||||||
|
data: PropTypes.array,
|
||||||
|
images: PropTypes.object,
|
||||||
|
filterFunction: PropTypes.func,
|
||||||
|
filterByBadge: PropTypes.func,
|
||||||
|
filterByType: PropTypes.func
|
||||||
|
}
|
||||||
|
|
@ -4,9 +4,25 @@ import { useSelector } from 'react-redux'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
// material-ui
|
// material-ui
|
||||||
import { Grid, Box, Stack, Tabs, Tab, Badge } from '@mui/material'
|
import {
|
||||||
|
Grid,
|
||||||
|
Box,
|
||||||
|
Stack,
|
||||||
|
Badge,
|
||||||
|
Toolbar,
|
||||||
|
TextField,
|
||||||
|
InputAdornment,
|
||||||
|
ButtonGroup,
|
||||||
|
ToggleButton,
|
||||||
|
InputLabel,
|
||||||
|
FormControl,
|
||||||
|
Select,
|
||||||
|
OutlinedInput,
|
||||||
|
Checkbox,
|
||||||
|
ListItemText
|
||||||
|
} from '@mui/material'
|
||||||
import { useTheme } from '@mui/material/styles'
|
import { useTheme } from '@mui/material/styles'
|
||||||
import { IconHierarchy, IconTool } from '@tabler/icons'
|
import { IconLayoutGrid, IconList, IconSearch } from '@tabler/icons'
|
||||||
|
|
||||||
// project imports
|
// project imports
|
||||||
import MainCard from 'ui-component/cards/MainCard'
|
import MainCard from 'ui-component/cards/MainCard'
|
||||||
|
|
@ -23,6 +39,10 @@ import useApi from 'hooks/useApi'
|
||||||
|
|
||||||
// const
|
// const
|
||||||
import { baseURL } from 'store/constant'
|
import { baseURL } from 'store/constant'
|
||||||
|
import * as React from 'react'
|
||||||
|
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'
|
||||||
|
import { MarketplaceTable } from '../../ui-component/table/MarketplaceTable'
|
||||||
|
import MenuItem from '@mui/material/MenuItem'
|
||||||
|
|
||||||
function TabPanel(props) {
|
function TabPanel(props) {
|
||||||
const { children, value, index, ...other } = props
|
const { children, value, index, ...other } = props
|
||||||
|
|
@ -45,6 +65,18 @@ TabPanel.propTypes = {
|
||||||
value: PropTypes.number.isRequired
|
value: PropTypes.number.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ITEM_HEIGHT = 48
|
||||||
|
const ITEM_PADDING_TOP = 8
|
||||||
|
const badges = ['POPULAR', 'NEW']
|
||||||
|
const types = ['Chatflow', 'Tool']
|
||||||
|
const MenuProps = {
|
||||||
|
PaperProps: {
|
||||||
|
style: {
|
||||||
|
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
||||||
|
width: 250
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// ==============================|| Marketplace ||============================== //
|
// ==============================|| Marketplace ||============================== //
|
||||||
|
|
||||||
const Marketplace = () => {
|
const Marketplace = () => {
|
||||||
|
|
@ -53,16 +85,62 @@ const Marketplace = () => {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const customization = useSelector((state) => state.customization)
|
const customization = useSelector((state) => state.customization)
|
||||||
|
|
||||||
const [isChatflowsLoading, setChatflowsLoading] = useState(true)
|
const [isLoading, setLoading] = useState(true)
|
||||||
const [isToolsLoading, setToolsLoading] = useState(true)
|
|
||||||
const [images, setImages] = useState({})
|
const [images, setImages] = useState({})
|
||||||
const tabItems = ['Chatflows', 'Tools']
|
|
||||||
const [value, setValue] = useState(0)
|
|
||||||
const [showToolDialog, setShowToolDialog] = useState(false)
|
const [showToolDialog, setShowToolDialog] = useState(false)
|
||||||
const [toolDialogProps, setToolDialogProps] = useState({})
|
const [toolDialogProps, setToolDialogProps] = useState({})
|
||||||
|
|
||||||
const getAllChatflowsMarketplacesApi = useApi(marketplacesApi.getAllChatflowsMarketplaces)
|
const getAllTemplatesMarketplacesApi = useApi(marketplacesApi.getAllTemplatesFromMarketplaces)
|
||||||
const getAllToolsMarketplacesApi = useApi(marketplacesApi.getAllToolsMarketplaces)
|
|
||||||
|
const [view, setView] = React.useState(localStorage.getItem('mpDisplayStyle') || 'card')
|
||||||
|
const [search, setSearch] = useState('')
|
||||||
|
|
||||||
|
const [badgeFilter, setBadgeFilter] = useState([])
|
||||||
|
const [typeFilter, setTypeFilter] = useState([])
|
||||||
|
|
||||||
|
const handleBadgeFilterChange = (event) => {
|
||||||
|
const {
|
||||||
|
target: { value }
|
||||||
|
} = event
|
||||||
|
setBadgeFilter(
|
||||||
|
// On autofill we get a stringified value.
|
||||||
|
typeof value === 'string' ? value.split(',') : value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const handleTypeFilterChange = (event) => {
|
||||||
|
const {
|
||||||
|
target: { value }
|
||||||
|
} = event
|
||||||
|
setTypeFilter(
|
||||||
|
// On autofill we get a stringified value.
|
||||||
|
typeof value === 'string' ? value.split(',') : value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleViewChange = (event, nextView) => {
|
||||||
|
localStorage.setItem('mpDisplayStyle', nextView)
|
||||||
|
setView(nextView)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSearchChange = (event) => {
|
||||||
|
setSearch(event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterFlows(data) {
|
||||||
|
return (
|
||||||
|
data.templateName.toLowerCase().indexOf(search.toLowerCase()) > -1 ||
|
||||||
|
(data.description && data.description.toLowerCase().indexOf(search.toLowerCase()) > -1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterByBadge(data) {
|
||||||
|
return badgeFilter.length > 0 ? badgeFilter.includes(data.badge) : true
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterByType(data) {
|
||||||
|
return typeFilter.length > 0 ? typeFilter.includes(data.type) : true
|
||||||
|
}
|
||||||
|
|
||||||
const onUseTemplate = (selectedTool) => {
|
const onUseTemplate = (selectedTool) => {
|
||||||
const dialogProp = {
|
const dialogProp = {
|
||||||
|
|
@ -90,39 +168,33 @@ const Marketplace = () => {
|
||||||
navigate(`/marketplace/${selectedChatflow.id}`, { state: selectedChatflow })
|
navigate(`/marketplace/${selectedChatflow.id}`, { state: selectedChatflow })
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (event, newValue) => {
|
|
||||||
setValue(newValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getAllChatflowsMarketplacesApi.request()
|
getAllTemplatesMarketplacesApi.request()
|
||||||
getAllToolsMarketplacesApi.request()
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setChatflowsLoading(getAllChatflowsMarketplacesApi.loading)
|
setLoading(getAllTemplatesMarketplacesApi.loading)
|
||||||
}, [getAllChatflowsMarketplacesApi.loading])
|
}, [getAllTemplatesMarketplacesApi.loading])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setToolsLoading(getAllToolsMarketplacesApi.loading)
|
if (getAllTemplatesMarketplacesApi.data) {
|
||||||
}, [getAllToolsMarketplacesApi.loading])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (getAllChatflowsMarketplacesApi.data) {
|
|
||||||
try {
|
try {
|
||||||
const chatflows = getAllChatflowsMarketplacesApi.data
|
const flows = getAllTemplatesMarketplacesApi.data
|
||||||
|
|
||||||
const images = {}
|
const images = {}
|
||||||
for (let i = 0; i < chatflows.length; i += 1) {
|
for (let i = 0; i < flows.length; i += 1) {
|
||||||
const flowDataStr = chatflows[i].flowData
|
if (flows[i].flowData) {
|
||||||
const flowData = JSON.parse(flowDataStr)
|
const flowDataStr = flows[i].flowData
|
||||||
const nodes = flowData.nodes || []
|
const flowData = JSON.parse(flowDataStr)
|
||||||
images[chatflows[i].id] = []
|
const nodes = flowData.nodes || []
|
||||||
for (let j = 0; j < nodes.length; j += 1) {
|
images[flows[i].id] = []
|
||||||
const imageSrc = `${baseURL}/api/v1/node-icon/${nodes[j].data.name}`
|
for (let j = 0; j < nodes.length; j += 1) {
|
||||||
if (!images[chatflows[i].id].includes(imageSrc)) {
|
const imageSrc = `${baseURL}/api/v1/node-icon/${nodes[j].data.name}`
|
||||||
images[chatflows[i].id].push(imageSrc)
|
if (!images[flows[i].id].includes(imageSrc)) {
|
||||||
|
images[flows[i].id].push(imageSrc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,80 +203,161 @@ const Marketplace = () => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [getAllChatflowsMarketplacesApi.data])
|
}, [getAllTemplatesMarketplacesApi.data])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>
|
<MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>
|
||||||
<Stack flexDirection='row'>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
<h1>Marketplace</h1>
|
<Toolbar
|
||||||
</Stack>
|
disableGutters={true}
|
||||||
<Tabs sx={{ mb: 2 }} variant='fullWidth' value={value} onChange={handleChange} aria-label='tabs'>
|
style={{
|
||||||
{tabItems.map((item, index) => (
|
margin: 1,
|
||||||
<Tab
|
padding: 1,
|
||||||
key={index}
|
paddingBottom: 10,
|
||||||
icon={index === 0 ? <IconHierarchy /> : <IconTool />}
|
display: 'flex',
|
||||||
iconPosition='start'
|
justifyContent: 'space-between',
|
||||||
label={<span style={{ fontSize: '1.1rem' }}>{item}</span>}
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h1>Marketplace</h1>
|
||||||
|
<TextField
|
||||||
|
size='small'
|
||||||
|
sx={{ display: { xs: 'none', sm: 'block' }, ml: 3 }}
|
||||||
|
variant='outlined'
|
||||||
|
placeholder='Search name or description'
|
||||||
|
onChange={onSearchChange}
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position='start'>
|
||||||
|
<IconSearch />
|
||||||
|
</InputAdornment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
<FormControl sx={{ m: 1, width: 300 }}>
|
||||||
</Tabs>
|
<InputLabel size='small' id='type-badge'>
|
||||||
{tabItems.map((item, index) => (
|
Type
|
||||||
<TabPanel key={index} value={value} index={index}>
|
</InputLabel>
|
||||||
{item === 'Chatflows' && (
|
<Select
|
||||||
<Grid container spacing={gridSpacing}>
|
size='small'
|
||||||
{!isChatflowsLoading &&
|
labelId='type-badge-checkbox-label'
|
||||||
getAllChatflowsMarketplacesApi.data &&
|
id='type-badge-checkbox'
|
||||||
getAllChatflowsMarketplacesApi.data.map((data, index) => (
|
multiple
|
||||||
<Grid key={index} item lg={3} md={4} sm={6} xs={12}>
|
value={typeFilter}
|
||||||
{data.badge && (
|
onChange={handleTypeFilterChange}
|
||||||
<Badge
|
input={<OutlinedInput label='Badge' />}
|
||||||
sx={{
|
renderValue={(selected) => selected.join(', ')}
|
||||||
'& .MuiBadge-badge': {
|
MenuProps={MenuProps}
|
||||||
right: 20
|
>
|
||||||
}
|
{types.map((name) => (
|
||||||
}}
|
<MenuItem key={name} value={name}>
|
||||||
badgeContent={data.badge}
|
<Checkbox checked={typeFilter.indexOf(name) > -1} />
|
||||||
color={data.badge === 'POPULAR' ? 'primary' : 'error'}
|
<ListItemText primary={name} />
|
||||||
>
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl sx={{ m: 1, width: 300 }}>
|
||||||
|
<InputLabel size='small' id='filter-badge'>
|
||||||
|
Tag
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId='filter-badge-label'
|
||||||
|
id='filter-badge-checkbox'
|
||||||
|
size='small'
|
||||||
|
multiple
|
||||||
|
value={badgeFilter}
|
||||||
|
onChange={handleBadgeFilterChange}
|
||||||
|
input={<OutlinedInput label='Badge' />}
|
||||||
|
renderValue={(selected) => selected.join(', ')}
|
||||||
|
MenuProps={MenuProps}
|
||||||
|
>
|
||||||
|
{badges.map((name) => (
|
||||||
|
<MenuItem key={name} value={name}>
|
||||||
|
<Checkbox checked={badgeFilter.indexOf(name) > -1} />
|
||||||
|
<ListItemText primary={name} />
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<Box sx={{ flexGrow: 1 }} />
|
||||||
|
<ButtonGroup sx={{ maxHeight: 40 }} disableElevation variant='contained' aria-label='outlined primary button group'>
|
||||||
|
<ButtonGroup disableElevation variant='contained' aria-label='outlined primary button group'>
|
||||||
|
<ToggleButtonGroup
|
||||||
|
sx={{ maxHeight: 40 }}
|
||||||
|
value={view}
|
||||||
|
color='primary'
|
||||||
|
exclusive
|
||||||
|
onChange={handleViewChange}
|
||||||
|
>
|
||||||
|
<ToggleButton
|
||||||
|
sx={{ color: theme?.customization?.isDarkMode ? 'white' : 'inherit' }}
|
||||||
|
variant='contained'
|
||||||
|
value='card'
|
||||||
|
title='Card View'
|
||||||
|
>
|
||||||
|
<IconLayoutGrid />
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton
|
||||||
|
sx={{ color: theme?.customization?.isDarkMode ? 'white' : 'inherit' }}
|
||||||
|
variant='contained'
|
||||||
|
value='list'
|
||||||
|
title='List View'
|
||||||
|
>
|
||||||
|
<IconList />
|
||||||
|
</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
</ButtonGroup>
|
||||||
|
</ButtonGroup>
|
||||||
|
</Toolbar>
|
||||||
|
</Box>
|
||||||
|
{!isLoading && (!view || view === 'card') && getAllTemplatesMarketplacesApi.data && (
|
||||||
|
<>
|
||||||
|
<Grid container spacing={gridSpacing}>
|
||||||
|
{getAllTemplatesMarketplacesApi.data
|
||||||
|
.filter(filterByBadge)
|
||||||
|
.filter(filterByType)
|
||||||
|
.filter(filterFlows)
|
||||||
|
.map((data, index) => (
|
||||||
|
<Grid key={index} item lg={3} md={4} sm={6} xs={12}>
|
||||||
|
{data.badge && (
|
||||||
|
<Badge
|
||||||
|
sx={{
|
||||||
|
'& .MuiBadge-badge': {
|
||||||
|
right: 20
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
badgeContent={data.badge}
|
||||||
|
color={data.badge === 'POPULAR' ? 'primary' : 'error'}
|
||||||
|
>
|
||||||
|
{data.type === 'Chatflow' && (
|
||||||
<ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} />
|
<ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} />
|
||||||
</Badge>
|
)}
|
||||||
)}
|
{data.type === 'Tool' && <ItemCard data={data} onClick={() => goToTool(data)} />}
|
||||||
{!data.badge && (
|
</Badge>
|
||||||
<ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} />
|
)}
|
||||||
)}
|
{!data.badge && data.type === 'Chatflow' && (
|
||||||
</Grid>
|
<ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} />
|
||||||
))}
|
)}
|
||||||
</Grid>
|
{!data.badge && data.type === 'Tool' && <ItemCard data={data} onClick={() => goToTool(data)} />}
|
||||||
)}
|
</Grid>
|
||||||
{item === 'Tools' && (
|
))}
|
||||||
<Grid container spacing={gridSpacing}>
|
</Grid>
|
||||||
{!isToolsLoading &&
|
</>
|
||||||
getAllToolsMarketplacesApi.data &&
|
)}
|
||||||
getAllToolsMarketplacesApi.data.map((data, index) => (
|
{!isLoading && view === 'list' && getAllTemplatesMarketplacesApi.data && (
|
||||||
<Grid key={index} item lg={3} md={4} sm={6} xs={12}>
|
<MarketplaceTable
|
||||||
{data.badge && (
|
sx={{ mt: 20 }}
|
||||||
<Badge
|
data={getAllTemplatesMarketplacesApi.data}
|
||||||
sx={{
|
images={images}
|
||||||
'& .MuiBadge-badge': {
|
filterFunction={filterFlows}
|
||||||
right: 20
|
filterByType={filterByType}
|
||||||
}
|
filterByBadge={filterByBadge}
|
||||||
}}
|
/>
|
||||||
badgeContent={data.badge}
|
)}
|
||||||
color={data.badge === 'POPULAR' ? 'primary' : 'error'}
|
|
||||||
>
|
{!isLoading && (!getAllTemplatesMarketplacesApi.data || getAllTemplatesMarketplacesApi.data.length === 0) && (
|
||||||
<ItemCard data={data} onClick={() => goToTool(data)} />
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
{!data.badge && <ItemCard data={data} onClick={() => goToTool(data)} />}
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
</TabPanel>
|
|
||||||
))}
|
|
||||||
{((!isChatflowsLoading && (!getAllChatflowsMarketplacesApi.data || getAllChatflowsMarketplacesApi.data.length === 0)) ||
|
|
||||||
(!isToolsLoading && (!getAllToolsMarketplacesApi.data || getAllToolsMarketplacesApi.data.length === 0))) && (
|
|
||||||
<Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>
|
<Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>
|
||||||
<Box sx={{ p: 2, height: 'auto' }}>
|
<Box sx={{ p: 2, height: 'auto' }}>
|
||||||
<img
|
<img
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue