mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 15:00:57 +03:00
Feature/DocumentStore (#2106)
* datasource: initial commit * datasource: datasource details and chunks * datasource: Document Store Node * more changes * Document Store - Base functionality * Document Store Loader Component * Document Store Loader Component * before merging the modularity PR * after merging the modularity PR * preview mode * initial draft PR * fixes * minor updates and fixes * preview with loader and splitter * preview with credential * show stored chunks * preview update... * edit config * save, preview and other changes * save, preview and other changes * save, process and other changes * save, process and other changes * alpha1 - for internal testing * rerouting urls * bug fix on new leader create * pagination support for chunks * delete document store * Update pnpm-lock.yaml * doc store card view * Update store files to use updated storage functions, Document Store Table View and other changes * ui changes * add expanded chunk dialog, improve ui * change throw Error to InternalError * Bug Fixes and removal of subFolder, adding of view chunks for store * lint fixes * merge changes * DocumentStoreStatus component * ui changes for doc store * add remove metadata key field, add custom document loader * add chatflows used doc store chips * add types/interfaces to DocumentStore Services * document loader list dialog title bar color change * update interfaces * Whereused Chatflow Name and Added chunkNo to retain order of created chunks. * use typeorm order chunkNo, ui changes --------- Co-authored-by: Henry <hzj94@hotmail.com> Co-authored-by: Henry Heng <henryheng@flowiseai.com>
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
// material-ui
|
||||
import { styled } from '@mui/material/styles'
|
||||
import { Box, Grid, Typography, useTheme } from '@mui/material'
|
||||
import { IconVectorBezier2, IconLanguage, IconScissors } from '@tabler/icons'
|
||||
|
||||
// project imports
|
||||
import MainCard from '@/ui-component/cards/MainCard'
|
||||
import DocumentStoreStatus from '@/views/docstore/DocumentStoreStatus'
|
||||
|
||||
import { kFormatter } from '@/utils/genericHelper'
|
||||
|
||||
const CardWrapper = styled(MainCard)(({ theme }) => ({
|
||||
background: theme.palette.card.main,
|
||||
color: theme.darkTextPrimary,
|
||||
overflow: 'auto',
|
||||
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%)'
|
||||
},
|
||||
height: '100%',
|
||||
minHeight: '160px',
|
||||
maxHeight: '300px',
|
||||
width: '100%',
|
||||
overflowWrap: 'break-word',
|
||||
whiteSpace: 'pre-line'
|
||||
}))
|
||||
|
||||
// ===========================|| DOC STORE CARD ||=========================== //
|
||||
|
||||
const DocumentStoreCard = ({ data, images, onClick }) => {
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
return (
|
||||
<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={2}>
|
||||
<Box display='flex' flexDirection='column' sx={{ flex: 1, width: '100%' }}>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
display: '-webkit-box',
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 500,
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
{data.name}
|
||||
</Typography>
|
||||
<DocumentStoreStatus status={data.status} />
|
||||
</div>
|
||||
<span
|
||||
style={{
|
||||
display: '-webkit-box',
|
||||
marginTop: 10,
|
||||
overflowWrap: 'break-word',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
{data.description || ' '}
|
||||
</span>
|
||||
</Box>
|
||||
<Grid container columnGap={2} rowGap={1}>
|
||||
<div
|
||||
style={{
|
||||
paddingLeft: '7px',
|
||||
paddingRight: '7px',
|
||||
paddingTop: '3px',
|
||||
paddingBottom: '3px',
|
||||
fontSize: '11px',
|
||||
width: 'max-content',
|
||||
borderRadius: '25px',
|
||||
boxShadow: customization.isDarkMode
|
||||
? '0 2px 14px 0 rgb(255 255 255 / 20%)'
|
||||
: '0 2px 14px 0 rgb(32 40 45 / 20%)',
|
||||
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<IconVectorBezier2 style={{ marginRight: 5 }} size={15} />
|
||||
{data.whereUsed?.length ?? 0} {data.whereUsed?.length <= 1 ? 'flow' : 'flows'}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
paddingLeft: '7px',
|
||||
paddingRight: '7px',
|
||||
paddingTop: '3px',
|
||||
paddingBottom: '3px',
|
||||
fontSize: '11px',
|
||||
width: 'max-content',
|
||||
borderRadius: '25px',
|
||||
boxShadow: customization.isDarkMode
|
||||
? '0 2px 14px 0 rgb(255 255 255 / 20%)'
|
||||
: '0 2px 14px 0 rgb(32 40 45 / 20%)',
|
||||
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<IconLanguage style={{ marginRight: 5 }} size={15} />
|
||||
{kFormatter(data.totalChars ?? 0)} chars
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
paddingLeft: '7px',
|
||||
paddingRight: '7px',
|
||||
paddingTop: '3px',
|
||||
paddingBottom: '3px',
|
||||
fontSize: '11px',
|
||||
width: 'max-content',
|
||||
borderRadius: '25px',
|
||||
boxShadow: customization.isDarkMode
|
||||
? '0 2px 14px 0 rgb(255 255 255 / 20%)'
|
||||
: '0 2px 14px 0 rgb(32 40 45 / 20%)',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<IconScissors style={{ marginRight: 5 }} size={15} />
|
||||
{kFormatter(data.totalChunks ?? 0)} chunks
|
||||
</div>
|
||||
</Grid>
|
||||
{images && images.length > 0 && (
|
||||
<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
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Grid>
|
||||
</Box>
|
||||
</CardWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
DocumentStoreCard.propTypes = {
|
||||
data: PropTypes.object,
|
||||
images: PropTypes.array,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
export default DocumentStoreCard
|
||||
@@ -144,7 +144,6 @@ const ItemCard = ({ data, images, onClick }) => {
|
||||
}
|
||||
|
||||
ItemCard.propTypes = {
|
||||
isLoading: PropTypes.bool,
|
||||
data: PropTypes.object,
|
||||
images: PropTypes.array,
|
||||
onClick: PropTypes.func
|
||||
|
||||
@@ -23,7 +23,7 @@ const StatsCard = ({ title, stat }) => {
|
||||
|
||||
StatsCard.propTypes = {
|
||||
title: PropTypes.string,
|
||||
stat: PropTypes.string
|
||||
stat: PropTypes.string | PropTypes.number
|
||||
}
|
||||
|
||||
export default StatsCard
|
||||
|
||||
@@ -113,7 +113,7 @@ const ExpandTextDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
lang={languageType}
|
||||
placeholder={inputParam.placeholder}
|
||||
basicSetup={
|
||||
languageType === 'json'
|
||||
languageType !== 'js'
|
||||
? { lineNumbers: false, foldGutter: false, autocompletion: false, highlightActiveLine: false }
|
||||
: {}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ const ManageScrapedLinksDialog = ({ show, dialogProps, onCancel, onSave }) => {
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1.5 }}>
|
||||
<Typography sx={{ fontWeight: 500 }}>Scraped Links</Typography>
|
||||
{selectedLinks.length > 0 ? (
|
||||
<StyledButton
|
||||
<Button
|
||||
sx={{ height: 'max-content', width: 'max-content' }}
|
||||
variant='outlined'
|
||||
color='error'
|
||||
@@ -170,7 +170,7 @@ const ManageScrapedLinksDialog = ({ show, dialogProps, onCancel, onSave }) => {
|
||||
startIcon={<IconEraser />}
|
||||
>
|
||||
Clear All
|
||||
</StyledButton>
|
||||
</Button>
|
||||
) : null}
|
||||
</Box>
|
||||
<>
|
||||
|
||||
@@ -22,7 +22,7 @@ export const MultiDropdown = ({ name, value, options, onSelect, formControlSx =
|
||||
const customization = useSelector((state) => state.customization)
|
||||
const findMatchingOptions = (options = [], internalValue) => {
|
||||
let values = []
|
||||
if (internalValue && typeof internalValue === 'string') values = JSON.parse(internalValue)
|
||||
if ('choose an option' !== internalValue && internalValue && typeof internalValue === 'string') values = JSON.parse(internalValue)
|
||||
else values = internalValue
|
||||
return options.filter((option) => values.includes(option.name))
|
||||
}
|
||||
|
||||
@@ -5,8 +5,21 @@ import { json } from '@codemirror/lang-json'
|
||||
import { vscodeDark } from '@uiw/codemirror-theme-vscode'
|
||||
import { sublime } from '@uiw/codemirror-theme-sublime'
|
||||
import { EditorView } from '@codemirror/view'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
|
||||
export const CodeEditor = ({
|
||||
value,
|
||||
height,
|
||||
theme,
|
||||
lang,
|
||||
placeholder,
|
||||
disabled = false,
|
||||
autoFocus = false,
|
||||
basicSetup = {},
|
||||
onValueChange
|
||||
}) => {
|
||||
const colorTheme = useTheme()
|
||||
|
||||
export const CodeEditor = ({ value, height, theme, lang, placeholder, disabled = false, basicSetup = {}, onValueChange }) => {
|
||||
const customStyle = EditorView.baseTheme({
|
||||
'&': {
|
||||
color: '#191b1f',
|
||||
@@ -14,7 +27,18 @@ export const CodeEditor = ({ value, height, theme, lang, placeholder, disabled =
|
||||
},
|
||||
'.cm-placeholder': {
|
||||
color: 'rgba(120, 120, 120, 0.5)'
|
||||
}
|
||||
},
|
||||
'.cm-content':
|
||||
lang !== 'js'
|
||||
? {
|
||||
fontFamily: 'Roboto, sans-serif',
|
||||
fontSize: '0.95rem',
|
||||
letterSpacing: '0em',
|
||||
fontWeight: 400,
|
||||
lineHeight: '1.5em',
|
||||
color: colorTheme.darkTextPrimary
|
||||
}
|
||||
: {}
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -31,6 +55,8 @@ export const CodeEditor = ({ value, height, theme, lang, placeholder, disabled =
|
||||
onChange={onValueChange}
|
||||
readOnly={disabled}
|
||||
editable={!disabled}
|
||||
// eslint-disable-next-line
|
||||
autoFocus={autoFocus}
|
||||
basicSetup={basicSetup}
|
||||
/>
|
||||
)
|
||||
@@ -43,6 +69,7 @@ CodeEditor.propTypes = {
|
||||
lang: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
autoFocus: PropTypes.bool,
|
||||
basicSetup: PropTypes.object,
|
||||
onValueChange: PropTypes.func
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { styled } from '@mui/material/styles'
|
||||
import Box from '@mui/material/Box'
|
||||
import Slider from '@mui/material/Slider'
|
||||
import { Grid, Input } from '@mui/material'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const BoxShadow = '0 3px 1px rgba(0,0,0,0.1),0 4px 8px rgba(0,0,0,0.13),0 0 0 1px rgba(0,0,0,0.02)'
|
||||
|
||||
const CustomInputSlider = styled(Slider)(({ theme }) => ({
|
||||
color: theme.palette.mode === 'dark' ? '#0a84ff' : '#007bff',
|
||||
height: 5,
|
||||
padding: '15px 0',
|
||||
'& .MuiSlider-thumb': {
|
||||
height: 20,
|
||||
width: 20,
|
||||
backgroundColor: '#333',
|
||||
boxShadow: '0 0 2px 0px rgba(0, 0, 0, 0.1)',
|
||||
'&:focus, &:hover, &.Mui-active': {
|
||||
boxShadow: '0px 0px 3px 1px rgba(0, 0, 0, 0.1)',
|
||||
// Reset on touch devices, it doesn't add specificity
|
||||
'@media (hover: none)': {
|
||||
boxShadow: BoxShadow
|
||||
}
|
||||
},
|
||||
'&:before': {
|
||||
boxShadow: '0px 0px 1px 0px rgba(0,0,0,0.2), 0px 0px 0px 0px rgba(0,0,0,0.14), 0px 0px 1px 0px rgba(0,0,0,0.12)'
|
||||
}
|
||||
},
|
||||
'& .MuiSlider-valueLabel': {
|
||||
fontSize: 12,
|
||||
fontWeight: 'normal',
|
||||
top: -1,
|
||||
backgroundColor: 'unset',
|
||||
color: theme.palette.text.primary,
|
||||
'&::before': {
|
||||
display: 'none'
|
||||
},
|
||||
'& *': {
|
||||
background: 'transparent',
|
||||
color: theme.palette.mode === 'dark' ? '#000' : '#000'
|
||||
}
|
||||
},
|
||||
'& .MuiSlider-track': {
|
||||
border: 'none',
|
||||
height: 5
|
||||
},
|
||||
'& .MuiSlider-rail': {
|
||||
opacity: 0.5,
|
||||
boxShadow: 'inset 0px 0px 4px -2px #000',
|
||||
backgroundColor: '#d0d0d0'
|
||||
}
|
||||
}))
|
||||
|
||||
export const InputSlider = ({ value, onChange }) => {
|
||||
const handleSliderChange = (event, newValue) => onChange(newValue)
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
onChange(event.target.value === '' ? 0 : Number(event.target.value))
|
||||
}
|
||||
|
||||
const handleBlur = () => {
|
||||
if (value < 0) {
|
||||
onChange(0)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Grid container spacing={2} sx={{ mt: 1 }} alignItems='center'>
|
||||
<Grid item xs>
|
||||
<CustomInputSlider
|
||||
value={typeof value === 'number' ? value : 0}
|
||||
onChange={handleSliderChange}
|
||||
valueLabelDisplay='on'
|
||||
aria-labelledby='input-slider'
|
||||
step={10}
|
||||
min={0}
|
||||
max={5000}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Input
|
||||
sx={{ ml: 3, mr: 3 }}
|
||||
value={value}
|
||||
size='small'
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleBlur}
|
||||
inputProps={{
|
||||
step: 10,
|
||||
min: 0,
|
||||
max: 10000,
|
||||
type: 'number',
|
||||
'aria-labelledby': 'input-slider'
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
InputSlider.propTypes = {
|
||||
value: PropTypes.number,
|
||||
onChange: PropTypes.func
|
||||
}
|
||||
Reference in New Issue
Block a user