From 717e90bda003d43fa355921a83166c3f7bcf8a37 Mon Sep 17 00:00:00 2001 From: Saurabh Asthana Date: Fri, 7 Mar 2025 10:11:26 -0800 Subject: [PATCH] update browse identifiers UI to support deletion --- .../components/confirmation-code-modal.tsx | 46 ++++++++++++ .../client/jsx/components/message-modal.tsx | 30 ++++++++ .../components/names-browse/names-browse.tsx | 36 +++++++-- .../jsx/components/names-browse/toolbar.tsx | 4 +- .../delete-identifiers-button.tsx | 74 +++++++++++++++++++ 5 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 gnomon/lib/client/jsx/components/confirmation-code-modal.tsx create mode 100644 gnomon/lib/client/jsx/components/message-modal.tsx create mode 100644 gnomon/lib/client/jsx/components/names-toolbar/delete-identifiers-button.tsx diff --git a/gnomon/lib/client/jsx/components/confirmation-code-modal.tsx b/gnomon/lib/client/jsx/components/confirmation-code-modal.tsx new file mode 100644 index 0000000000..208fc42156 --- /dev/null +++ b/gnomon/lib/client/jsx/components/confirmation-code-modal.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +export default function ConfirmationCodeModal({open, confirmation, confirmationError, onChange, onClose, onConfirm}:{ open: boolean, confirmation: string, onClose: Function, onConfirm: Function }) { + return ( + + Confirmation Required + + + { confirmationError } + + onChange(e.target.value) } + value={ confirmation } + /> + + + + + + + ); +} diff --git a/gnomon/lib/client/jsx/components/message-modal.tsx b/gnomon/lib/client/jsx/components/message-modal.tsx new file mode 100644 index 0000000000..f2125f5edf --- /dev/null +++ b/gnomon/lib/client/jsx/components/message-modal.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +export default function MessageModal({open, title, message, onClose}:{ open: boolean, title: string, message: string, onClose: Function }) { + return ( + + { title } + + + { message } + + + + + + + ); +} diff --git a/gnomon/lib/client/jsx/components/names-browse/names-browse.tsx b/gnomon/lib/client/jsx/components/names-browse/names-browse.tsx index a6ba06d161..0798bc3679 100644 --- a/gnomon/lib/client/jsx/components/names-browse/names-browse.tsx +++ b/gnomon/lib/client/jsx/components/names-browse/names-browse.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { useAppSelector as useSelector } from '../../hooks'; import { makeStyles } from '@material-ui/core/styles'; @@ -11,7 +11,9 @@ import { useDispatch } from '../../utils/redux'; import { setMagmaNamesListRequest } from '../../actions/names'; import { fetchAndAddRulesFromMagma } from '../../actions/rules'; import { selectMagmaNamesListsByRuleName } from '../../selectors/names'; -import NamesBrowseToolbar from './toolbar'; +import NamesToolbar from '../names-toolbar/toolbar'; +import ExportButton from '../names-toolbar/export-button'; +import DeleteIdentifiersButton from '../names-toolbar/delete-identifiers-button'; import Counts, { Count } from '../names-create/counts'; import Table from '../table'; @@ -89,7 +91,7 @@ const NamesBrowse = ({ project_name }: { project_name: string }) => { addRulesFromMagma(); }, []); - useEffect(() => { + const fetchAllNames = useCallback(() => { const fetchNamesForRule = createFnConcurrencyWrapper(fetchNamesWithRuleAndRegexFromMagma, 4); async function fetchNamesFromMagma(ruleName: string) { @@ -109,6 +111,10 @@ const NamesBrowse = ({ project_name }: { project_name: string }) => { } allRules.forEach(ruleName => fetchNamesFromMagma(ruleName)); + }, [allRules]); + + useEffect( () => { + fetchAllNames() }, [allRules.length]); useEffect(() => { @@ -156,9 +162,24 @@ const NamesBrowse = ({ project_name }: { project_name: string }) => {
- , + { + fetchAllNames(); + setSelection([]); + } } + project_name={project_name} + buttonText={`Delete${selection.length ? ' Selection' : ''}`} + /> + ]} />
@@ -168,7 +189,6 @@ const NamesBrowse = ({ project_name }: { project_name: string }) => { rows={rowData} columns={['name', 'ruleName', 'author', 'createdAt']} selectable={true} - showFocusedCell={true} onSelectionChanged={setSelection} className={classes.table} dataTypeLabel='name' @@ -179,4 +199,4 @@ const NamesBrowse = ({ project_name }: { project_name: string }) => { }; -export default NamesBrowse; \ No newline at end of file +export default NamesBrowse; diff --git a/gnomon/lib/client/jsx/components/names-browse/toolbar.tsx b/gnomon/lib/client/jsx/components/names-browse/toolbar.tsx index 35bd5cd44a..970c272109 100644 --- a/gnomon/lib/client/jsx/components/names-browse/toolbar.tsx +++ b/gnomon/lib/client/jsx/components/names-browse/toolbar.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import ExportButton from '../names-toolbar/export-button'; +import DeleteButton from '../names-toolbar/delete-button'; import NamesToolbar from '../names-toolbar/toolbar'; @@ -22,4 +22,4 @@ const NamesBrowseToolbar = ({ exportData, exportButtonText }: { ); }; -export default NamesBrowseToolbar; \ No newline at end of file +export default NamesBrowseToolbar; diff --git a/gnomon/lib/client/jsx/components/names-toolbar/delete-identifiers-button.tsx b/gnomon/lib/client/jsx/components/names-toolbar/delete-identifiers-button.tsx new file mode 100644 index 0000000000..909630b641 --- /dev/null +++ b/gnomon/lib/client/jsx/components/names-toolbar/delete-identifiers-button.tsx @@ -0,0 +1,74 @@ +import React, { useState, useRef } from 'react'; +import { useAppSelector as useSelector } from '../../hooks'; +import DeleteOutlineOutlinedIcon from '@material-ui/icons/DeleteOutlineOutlined'; + +import { useDispatch } from '../../utils/redux'; +import ToolbarButtonWithPopper from './toolbar-button-with-popper'; +import ConfirmationPopper from '../confirmation-popper'; +import MessageModal from '../message-modal'; +import ConfirmationCodeModal from '../confirmation-code-modal'; +import { json_get, json_post } from 'etna-js/utils/fetch'; +import { magmaPath } from 'etna-js/api/magma_api'; + + + +const DeleteIdentifiersButton = ({ small, project_name, data, buttonText, className, refresh }: { small: boolean, className?: string, data: Array, buttonText: string }) => { + const [confirmation, setConfirmation] = useState(''); + const [confirmationError, setConfirmationError] = useState(''); + const [ message, setMessage ] = useState(''); + const [ messageTitle, setMessageTitle ] = useState(''); + + const handleDelete = () => { + json_post(magmaPath(`gnomon/${project_name}/delete`), + { identifiers: data.map(d => d.name), confirmation }).then( + ({success}) => { + setMessage(success); + setMessageTitle('Deleted Identifiers'); + setConfirmation(''); + refresh(); + } + ).catch( + (error) => error.then( + ({error}) => { + setConfirmation(''); + if (error.includes('confirmation')) setConfirmationError(error); + else { + setMessage(error.length > 100 ? `${error.substring(0,100)}...` : error); + setMessageTitle('Deletion Failed'); + } + } + ) + ) + }; + + return ( + + } + variant={small ? 'compact' : 'full'} + color="#FF0000" + onClickOrPopperChange={handleDelete} + disabled={data.length == 0} + className={className} + /> + { + setConfirmationError(''); + setConfirmation(''); + } } + onConfirm={ () => { + setConfirmationError(''); + handleDelete(); + }} + /> + setMessage('') } message={message}/> + + ); +}; + +export default DeleteIdentifiersButton;