diff --git a/src/backend/StringOperation.ts b/src/backend/StringOperation.ts new file mode 100644 index 0000000..f9a2cca --- /dev/null +++ b/src/backend/StringOperation.ts @@ -0,0 +1,46 @@ +import axios from 'axios'; + +let token = localStorage.getItem('token'); +let ip = localStorage.getItem('ip'); +let port = localStorage.getItem('port'); + +function loadData(){ + token = localStorage.getItem('token'); + ip = localStorage.getItem('ip'); + port = localStorage.getItem('port'); +} + +// Delete a specific kV +export async function DeleteSingleValue(bucket: string, key: string): Promise { + loadData() + const Url = `http://${ip}:${port}`; + return await axios.delete( + `${Url}/string/delete/${bucket}/${key}?token=${token}`, + ); +} + +// Update a specific kV +export async function UpdateSingleValue(bucket: string, key: string, value: string,ttl:number): Promise { + loadData() + const Url = `http://${ip}:${port}`; + return await axios.post( + `${Url}/string/update/${bucket}/${key}?token=${token}`, + { + value: value, + ttl: ttl + } + ); +} + +// Add a specific kV +export async function AddSingleValue(bucket: string, key: string, value: string,ttl:number): Promise { + loadData() + const Url = `http://${ip}:${port}`; + return await axios.post( + `${Url}/string/update/${bucket}/${key}?token=${token}`, + { + value: value, + ttl: ttl + } + ); +} diff --git a/src/comps/Table.tsx b/src/comps/Table.tsx index 81b714d..d73581c 100644 --- a/src/comps/Table.tsx +++ b/src/comps/Table.tsx @@ -18,10 +18,19 @@ import Tooltip from '@mui/material/Tooltip'; import FormControlLabel from '@mui/material/FormControlLabel'; import Switch from '@mui/material/Switch'; import DeleteIcon from '@mui/icons-material/Delete'; +import AddCircleIcon from '@mui/icons-material/AddCircle'; import { visuallyHidden } from '@mui/utils'; import Message from './Message'; import SearchIcon from '@mui/icons-material/Search'; -import { InputBase } from '@mui/material'; +import { Alert, Button, InputBase, Snackbar } from '@mui/material'; +import TextField from '@mui/material/TextField'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; +import EditIcon from '@mui/icons-material/Edit'; +import { AddSingleValue, UpdateSingleValue } from '../backend/StringOperation'; interface Data { key: string; @@ -172,10 +181,12 @@ function EnhancedTableHead(props: EnhancedTableProps) { interface EnhancedTableToolbarProps { numSelected: number; + ds: string; + selected: string[]; + bucket: string; } - function CustomizedInputBase(props: { prop: any; }) { const [keyword, setKeyword] = React.useState(''); return ( @@ -191,11 +202,11 @@ function CustomizedInputBase(props: { prop: any; }) { onChange={e => setKeyword(e.target.value)} /> { - let { rows, setRows,back} = props.prop; + let { rows, setRows, back } = props.prop; rows = rows as Data[]; - if (keyword==''){ - setRows(back) - return + if (keyword == '') { + setRows(back); + return; } let tmp: Data[] = []; for (let i = 0; i < rows.length; i++) { @@ -214,63 +225,262 @@ function CustomizedInputBase(props: { prop: any; }) { const EnhancedTableToolbar = (props: EnhancedTableToolbarProps) => { - const { numSelected, ...other } = props; + // @ts-ignore + const { numSelected,rows,setRows,ds, selected, bucket, ...other } = props; + const [open, setOpen] = React.useState(false); + const [operateType, setOperateType] = React.useState(''); + const [openAlert, setOpenAlert] = React.useState(false); + + const [level, setLevel] = React.useState(''); + const [message, setMessage] = React.useState(''); + + + const [key, setKey] = React.useState(''); + const [value, setValue] = React.useState(''); + const [ttl, setTTL] = React.useState(0); + + // @ts-ignore + const handleTextInputChange = (type: string, event) => { + switch (type) { + case 'key': + setKey(event.target.value); + break; + case 'value': + setValue(event.target.value); + break; + case 'ttl': + setTTL(event.target.value); + break; + } + }; + + const handleClickOpen = (type: string) => { + if (type == 'update' && numSelected != 1) { + setOpenAlert(true); + setMessage('Please select one item to update'); + setLevel('error'); + setTimeout(() => { + setOpenAlert(false); + }, 2000); + return; + } + setKey(selected[0]); + setOpen(true); + setOperateType(type); + }; + const handleClose = () => { + setOpen(false); + }; + //@ts-ignore + const handleSubmit = (event) => { + let success =false; + event.preventDefault(); + const form = { + key: key, + value: value, + ttl: ttl, + }; + if (operateType == 'add') { + AddSingleValue(bucket, form.key, form.value, form.ttl).then((res) => { + if (res.data.code == 200) { + setOpenAlert(true); + setMessage('Add Success'); + setLevel('success'); + success = true; + } else { + setOpenAlert(true); + setMessage('Add Failed'); + setLevel('error'); + } + }); + } else if (operateType == 'update') { + UpdateSingleValue(bucket, form.key, form.value, form.ttl).then((res) => { + if (res.data.code == 200) { + setOpenAlert(true); + setMessage('Update Success'); + setLevel('success'); + setTimeout(() => { + setOpenAlert(false); + }, 2000); + setOpen(false); + success = true; + } else { + setOpenAlert(true); + setMessage('Update Failed'); + setLevel('error'); + } + }); + } + setTimeout(() => { + setOpenAlert(false); + }, 2000); + + if(success){ + //find key from rows and update + let tmp = rows as Data[]; + let index = tmp.findIndex((item) => item.key == key); + if (index != -1) { + tmp[index].value = value; + tmp[index].length = value.length; + }else { + tmp.push({key:key,value:value,length:value.length}); + } + setRows(tmp); + setTimeout(() => { + setOpenAlert(false); + }, 2000); + setOpen(false); + } + //reset + setKey(''); + setValue(''); + setTTL(0); + setOpen(false);//close dialog + }; + + // @ts-ignore return ( - 0 && { - bgcolor: (theme) => - alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity), - }), - }} - > + <> + 0 && { + bgcolor: (theme) => + alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity), + }), + }} + > + + {numSelected > 0 ? ( + + {numSelected} selected + + ) : ( + + Data List + + + )} + + {numSelected == 0 ? ( + + { + handleClickOpen('add'); + }}> + + + ) + : ( + + { + handleClickOpen('update'); + }}> + + + ) + } - {numSelected > 0 ? ( - - {numSelected} selected - - ) : ( - - Data List - - - )} - - {numSelected > 0 ? ( - - - - - - ) : ( - - - - - - )} - + {numSelected > 0 ? ( + + + + + + ) : ( + + + + + + )} + + + + {operateType.toUpperCase()} + + + + {operateType == 'add' ? 'Add a new key-value pair' : 'Update the selected key-value pair'} + + { + handleTextInputChange('key', e); + }} + /> + { + handleTextInputChange('value', e); + }} + /> + { + handleTextInputChange('value', e); + }} + /> + + + + + + + + + + + + { + setOpenAlert(false); + }} // @ts-ignore + severity={level} sx={{ width: '100%' }}> + {message} + + + ); }; export default function EnhancedTable(props: any) { - const [rows, setRows] = React.useState([]); + let [rows, setRows] = React.useState([]); const [back, setBack] = React.useState([]); const [order, setOrder] = React.useState('asc'); @@ -300,7 +510,7 @@ export default function EnhancedTable(props: any) { let ds = props.condition.ds; if (ds == 'string') { headCells[0].label = 'Key'; - } else if (ds == 'list' || ds == 'set') { + } else if (ds == 'list' || ds == 'set' || ds == 'zset') { headCells[0].label = 'Preview'; } } @@ -355,13 +565,17 @@ export default function EnhancedTable(props: any) { const emptyRows = page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0; + return ( <> + numSelected={selected.length} rows={rows} setRows={setRows} back={back} ds={props.condition.ds} + bucket={props.condition.bucket} + // @ts-ignore + selected={selected} /> 0 && (