Skip to content

Commit

Permalink
UI - upload content modal (#659)
Browse files Browse the repository at this point in the history
* Implement upload modal
  • Loading branch information
lucasjacks0n authored Jun 17, 2024
1 parent aff072f commit 81a5512
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 17 deletions.
8 changes: 4 additions & 4 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@types/react-router-dom": "^5.3.3",
"axios": "^1.6.7",
"crypto": "^1.0.1",
"getindexify": "^0.0.45",
"getindexify": "^0.0.46",
"moment": "^2.30.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
105 changes: 105 additions & 0 deletions ui/src/components/Inputs/LabelsInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { Box, TextField, IconButton, Typography } from '@mui/material'
import { Add, Delete } from '@mui/icons-material'
import { useState } from 'react'

interface LabelsInputProps {
onChange: (labels: Record<string, string>) => void
disabled: boolean
}

const LabelsInput = ({ onChange, disabled }: LabelsInputProps) => {
const [labels, setLabels] = useState<Record<string, string>>({})
const [newKey, setNewKey] = useState('')
const [newValue, setNewValue] = useState('')

const handleAddLabel = () => {
if (newKey && newValue) {
const updatedLabels = { ...labels, [newKey]: newValue }
setLabels(updatedLabels)
onChange(updatedLabels)
setNewKey('')
setNewValue('')
}
}

const handleDeleteLabel = (key: string) => {
const { [key]: _, ...remainingLabels } = labels
setLabels(remainingLabels)
onChange(remainingLabels)
}

const handleChange =
(setValue: React.Dispatch<React.SetStateAction<string>>) =>
(e: React.ChangeEvent<HTMLInputElement>) => {
const regex = /^[a-zA-Z0-9-_]*$/
if (regex.test(e.target.value)) {
setValue(e.target.value)
}
}

return (
<Box>
<Typography variant="h6" component="h2" sx={{ mt: 2 }}>
Labels
</Typography>
<Box display="flex" gap={1} sx={{ mt: 2 }}>
<TextField
disabled={disabled}
label="Key"
value={newKey}
onChange={handleChange(setNewKey)}
variant="outlined"
/>
<TextField
disabled={disabled}
label="Value"
value={newValue}
onChange={handleChange(setNewValue)}
variant="outlined"
/>
<IconButton
disabled={disabled}
color="primary"
onClick={handleAddLabel}
>
<Add />
</IconButton>
</Box>
{Object.entries(labels).map(([key, value]) => (
<Box
display="flex"
alignItems="center"
gap={1}
key={key}
sx={{ mt: 2 }}
>
<TextField
label="Key"
value={key}
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
<TextField
label="Value"
value={value}
InputProps={{
readOnly: true,
}}
variant="outlined"
/>
<IconButton
disabled={disabled}
color="secondary"
onClick={() => handleDeleteLabel(key)}
>
<Delete />
</IconButton>
</Box>
))}
</Box>
)
}

export default LabelsInput
154 changes: 154 additions & 0 deletions ui/src/components/UploadButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import UploadIcon from '@mui/icons-material/Upload'
import {
Box,
Button,
Modal,
Typography,
MenuItem,
Select,
Paper,
CircularProgress,
} from '@mui/material'
import { ExtractionGraph, IndexifyClient } from 'getindexify'
import { useState } from 'react'
import LabelsInput from './Inputs/LabelsInput'

interface Props {
client: IndexifyClient
}

const UploadButton = ({ client }: Props) => {
const [open, setOpen] = useState(false)
const [file, setFile] = useState<Blob | null>(null)
const [labels, setLabels] = useState<Record<string, string>>({})
const [fileName, setFileName] = useState('')
const [extractionGraphName, setExtractionGraphName] = useState('')
const [loading, setLoading] = useState(false)
const [extractionGraphs, setExtractionGraphs] = useState<ExtractionGraph[]>(
client.extractionGraphs
)

const handleOpen = () => setOpen(true)
const handleClose = () => setOpen(false)

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files && event.target.files[0]) {
setFile(event.target.files[0])
setFileName(event.target.files[0].name)
}
}

const modalStyle = {
position: 'absolute' as 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '80%',
maxWidth: '800px',
bgcolor: 'background.paper',
maxHeight: '100%',
overflow: 'scroll',
boxShadow: 24,
p: 4,
}

const upload = async () => {
if (file && extractionGraphName) {
setLoading(true)
await client.uploadFile(extractionGraphName, file, labels)
window.location.reload()
}
}

const updateExtractionGraphs = async () => {
const graphs = await client.getExtractionGraphs()
setExtractionGraphs(graphs)
}

return (
<>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Paper sx={modalStyle}>
<Typography id="modal-modal-title" variant="h6" component="h2">
Upload Content
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
Select a file to upload and choose an extraction graph.
</Typography>
<Select
disabled={loading}
onFocus={updateExtractionGraphs}
value={extractionGraphName}
onChange={(e) => setExtractionGraphName(e.target.value)}
displayEmpty
fullWidth
sx={{ mt: 2 }}
>
<MenuItem value="" disabled>
Select Extraction Graph
</MenuItem>
{extractionGraphs.map((graph) => (
<MenuItem key={graph.name} value={graph.name}>
{graph.name}
</MenuItem>
))}
</Select>
<Box display="flex" alignItems={'center'} gap={2}>
<Button
disabled={loading}
variant="contained"
component="label"
sx={{ mt: 2 }}
>
Choose File
<input type="file" hidden onChange={handleFileChange} />
</Button>
{fileName && (
<Typography variant="body2" sx={{ mt: 1 }}>
Selected File: {fileName}
</Typography>
)}
</Box>
<LabelsInput
disabled={loading}
onChange={(val) => {
setLabels(val)
}}
/>

<Box sx={{ mt: 2, position: 'relative' }}>
<Button
variant="contained"
onClick={upload}
disabled={!file || !extractionGraphName || loading}
>
Upload
</Button>
{loading && (
<CircularProgress
size={24}
sx={{
position: 'absolute',
top: '50%',
left: '50%',
marginTop: '-12px',
marginLeft: '-12px',
}}
/>
)}
</Box>
</Paper>
</Modal>
<Button onClick={handleOpen} size="small" endIcon={<UploadIcon />}>
Upload
</Button>
</>
)
}

export default UploadButton
12 changes: 6 additions & 6 deletions ui/src/components/tables/ContentTable.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DataGrid, GridColDef } from '@mui/x-data-grid'
import { IContentMetadata, IExtractionPolicy } from 'getindexify'
import { IContentMetadata, IndexifyClient } from 'getindexify'
import {
Alert,
Button,
Expand All @@ -17,12 +17,13 @@ import moment from 'moment'
import { Link } from 'react-router-dom'
import CopyText from '../CopyText'
import { IContentMetadataExtended } from '../../types'
import UploadButton from '../UploadButton'

const ContentTable = ({
extractionPolicies,
loadData,
client,
}: {
extractionPolicies: IExtractionPolicy[]
client: IndexifyClient,
loadData: ({
pageSize,
parentId,
Expand Down Expand Up @@ -136,9 +137,7 @@ const ContentTable = ({
headerName: '',
width: 100,
renderCell: (params) => (
<Link
to={`/${params.row.namespace}/content/${params.row.id}`}
>
<Link to={`/${params.row.namespace}/content/${params.row.id}`}>
<Button sx={{ py: 0.5, px: 2 }} variant="outlined">
View
</Button>
Expand Down Expand Up @@ -269,6 +268,7 @@ const ContentTable = ({
<InfoIcon fontSize="small" />
</IconButton>
</Typography>
<UploadButton client={client} />
</Stack>
<Box justifyContent={'space-between'} display={'flex'}>
<Tabs
Expand Down
4 changes: 1 addition & 3 deletions ui/src/routes/Namespace/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ const ContentsPage = () => {
<Box>
<ContentTable
loadData={contentLoader}
extractionPolicies={client.extractionGraphs
.map((graph) => graph.extraction_policies)
.flat()}
client={client}
/>
</Box>
)
Expand Down
4 changes: 1 addition & 3 deletions ui/src/routes/Namespace/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ const NamespacePage = () => {
<SchemasTable schemas={schemas} />
<ContentTable
loadData={contentLoader}
extractionPolicies={client.extractionGraphs
.map((graph) => graph.extraction_policies)
.flat()}
client={client}
/>
<ExtractorsTable extractors={extractors} />
</Stack>
Expand Down

0 comments on commit 81a5512

Please sign in to comment.