Implement delete multiple docs

This commit is contained in:
Laurent Nguyen 2024-03-27 18:23:17 +01:00
parent b686501df8
commit 2f85c4f84a
1 changed files with 109 additions and 80 deletions

View File

@ -113,7 +113,6 @@ const DocumentsTabComponent: React.FunctionComponent<{
const [documentIds, setDocumentIds] = useState<DocumentId[]>([]); const [documentIds, setDocumentIds] = useState<DocumentId[]>([]);
const [isExecuting, setIsExecuting] = useState<boolean>(false); // TODO isExecuting is a member of TabsBase. We may need to update this field. const [isExecuting, setIsExecuting] = useState<boolean>(false); // TODO isExecuting is a member of TabsBase. We may need to update this field.
const [dataContentsGridScrollHeight, setDataContentsGridScrollHeight] = useState<string>(undefined); const [dataContentsGridScrollHeight, setDataContentsGridScrollHeight] = useState<string>(undefined);
const [shouldShowEditor, setShouldShowEditor] = useState<boolean>(false);
// Query // Query
const [documentsIterator, setDocumentsIterator] = useState<{ const [documentsIterator, setDocumentsIterator] = useState<{
@ -133,6 +132,7 @@ const DocumentsTabComponent: React.FunctionComponent<{
const [selectedDocumentContentBaseline, setSelectedDocumentContentBaseline] = useState<string>(undefined); const [selectedDocumentContentBaseline, setSelectedDocumentContentBaseline] = useState<string>(undefined);
const [selectedDocumentId, setSelectedDocumentId] = useState<DocumentId>(undefined); const [selectedDocumentId, setSelectedDocumentId] = useState<DocumentId>(undefined);
const [multiSelectedDocumentIds, setMultiSelectedDocumentIds] = useState<DocumentId[]>([]);
// Command buttons // Command buttons
const [editorState, setEditorState] = useState<ViewModels.DocumentExplorerState>( const [editorState, setEditorState] = useState<ViewModels.DocumentExplorerState>(
@ -328,7 +328,11 @@ const DocumentsTabComponent: React.FunctionComponent<{
}, []); }, []);
// If editor state changes, update the nav // If editor state changes, update the nav
useEffect(() => updateNavbarWithTabsButtons(), [editorState, selectedDocumentContent, initialDocumentContent]); // TODO Put whatever the buttons callback use in the dependency array
useEffect(
() => updateNavbarWithTabsButtons(),
[editorState, selectedDocumentContent, initialDocumentContent, multiSelectedDocumentIds, documentIds],
);
useEffect(() => { useEffect(() => {
if (documentsIterator) { if (documentsIterator) {
@ -336,10 +340,6 @@ const DocumentsTabComponent: React.FunctionComponent<{
} }
}, [documentsIterator]); }, [documentsIterator]);
useEffect(() => {
setShouldShowEditor(!!selectedDocumentContent);
}, [selectedDocumentContent]);
const onNewDocumentClick = useCallback((): void => { const onNewDocumentClick = useCallback((): void => {
if (isEditorDirty()) { if (isEditorDirty()) {
useDialog useDialog
@ -491,74 +491,93 @@ const DocumentsTabComponent: React.FunctionComponent<{
// setEditorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits); // setEditorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
}; };
const onDeleteExisitingDocumentClick = async (): Promise<void> => { const onDeleteExisitingDocumentsClick = async (): Promise<void> => {
// const selectedDocumentId = this.selectedDocumentId(); // const selectedDocumentId = this.selectedDocumentId();
const msg = !isPreferredApiMongoDB
? "Are you sure you want to delete the selected item ?"
: "Are you sure you want to delete the selected document ?";
useDialog // TODO: Rework this for localization
.getState() const isPlural = multiSelectedDocumentIds.length > 1;
.showOkCancelModalDialog( const documentName = !isPreferredApiMongoDB
"Confirm delete", ? isPlural
msg, ? `the selected ${multiSelectedDocumentIds.length} items`
"Delete", : "the selected item"
async () => await _deleteDocument(selectedDocumentId), : isPlural
"Cancel", ? `the selected ${multiSelectedDocumentIds.length} documents`
undefined, : "the selected document";
); const msg = `Are you sure you want to delete ${documentName}?`;
useDialog.getState().showOkCancelModalDialog(
"Confirm delete",
msg,
"Delete",
// async () => await _deleteDocuments(selectedDocumentId),
() => deleteDocuments(multiSelectedDocumentIds),
"Cancel",
undefined,
);
}; };
const __deleteDocument = (documentId: DocumentId): Promise<void> => { const deleteDocuments = (toDeleteDocumentIds: DocumentId[]): void => {
return deleteDocument(props.collection, documentId);
};
const _deleteDocument = (selectedDocumentId: DocumentId): Promise<void> => {
setIsExecutionError(false); setIsExecutionError(false);
setIsExecuting(true);
const promises = toDeleteDocumentIds.map((documentId) => _deleteDocuments(documentId));
Promise.all(promises)
.then((deletedDocumentIds: DocumentId[]) => {
const newDocumentIds = [...documentIds];
deletedDocumentIds.forEach((deletedDocumentId) => {
if (deletedDocumentId !== undefined) {
// documentIds.remove((documentId: DocumentId) => documentId.rid === selectedDocumentId.rid);
const index = toDeleteDocumentIds.findIndex((documentId) => documentId.rid === deletedDocumentId.rid);
if (index !== -1) {
newDocumentIds.splice(index, 1);
}
}
});
setDocumentIds(newDocumentIds);
setSelectedDocumentContent(undefined);
setSelectedDocumentId(undefined);
setMultiSelectedDocumentIds([]);
setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected);
})
.finally(() => setIsExecuting(false));
};
const _deleteDocuments = (documentId: DocumentId): Promise<DocumentId> => {
// setIsExecutionError(false);
const startKey: number = TelemetryProcessor.traceStart(Action.DeleteDocument, { const startKey: number = TelemetryProcessor.traceStart(Action.DeleteDocument, {
dataExplorerArea: Constants.Areas.Tab, dataExplorerArea: Constants.Areas.Tab,
tabTitle: props.tabTitle, tabTitle: props.tabTitle,
}); });
setIsExecuting(true); // setIsExecuting(true);
return __deleteDocument(selectedDocumentId) return deleteDocument(props.collection, documentId).then(
.then( () => {
() => { TelemetryProcessor.traceSuccess(
// documentIds.remove((documentId: DocumentId) => documentId.rid === selectedDocumentId.rid); Action.DeleteDocument,
const index = documentIds.findIndex((documentId) => documentId.rid === selectedDocumentId.rid); {
if (index !== -1) { dataExplorerArea: Constants.Areas.Tab,
const newDocumentIds = [...documentIds]; tabTitle: props.tabTitle,
newDocumentIds.splice(index, 1); },
setDocumentIds(newDocumentIds); startKey,
} );
return documentId;
setSelectedDocumentContent(""); },
setSelectedDocumentId(null); (error) => {
setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected); setIsExecutionError(true);
TelemetryProcessor.traceSuccess( console.error(error);
Action.DeleteDocument, TelemetryProcessor.traceFailure(
{ Action.DeleteDocument,
dataExplorerArea: Constants.Areas.Tab, {
tabTitle: props.tabTitle, dataExplorerArea: Constants.Areas.Tab,
}, tabTitle: props.tabTitle,
startKey, error: getErrorMessage(error),
); errorStack: getErrorStack(error),
}, },
(error) => { startKey,
setIsExecutionError(true); );
console.error(error); return undefined;
TelemetryProcessor.traceFailure( },
Action.DeleteDocument, );
{ // .finally(() => setIsExecuting(false));
dataExplorerArea: Constants.Areas.Tab,
tabTitle: props.tabTitle,
error: getErrorMessage(error),
errorStack: getErrorStack(error),
},
startKey,
);
},
)
.finally(() => setIsExecuting(false));
}; };
const onShowFilterClick = () => { const onShowFilterClick = () => {
@ -910,7 +929,7 @@ const DocumentsTabComponent: React.FunctionComponent<{
buttons.push({ buttons.push({
iconSrc: DeleteDocumentIcon, iconSrc: DeleteDocumentIcon,
iconAlt: label, iconAlt: label,
onCommandClick: onDeleteExisitingDocumentClick, onCommandClick: onDeleteExisitingDocumentsClick,
commandButtonLabel: label, commandButtonLabel: label,
ariaLabel: label, ariaLabel: label,
hasPopup: false, hasPopup: false,
@ -993,9 +1012,16 @@ const DocumentsTabComponent: React.FunctionComponent<{
} }
}; };
const onDocumentsSelectionChange = (selectedItemsIndices: Set<number>) => { const onDocumentsSelectionChange = useCallback(
// TODO: Update some state? (selectedItemsIndices: Set<number>) => {
}; if (documentIds.length === 0) {
return;
}
setMultiSelectedDocumentIds(Array.from(selectedItemsIndices).map((index) => documentIds[index]));
},
[documentIds],
);
const loadDocument = (documentId: DocumentId) => const loadDocument = (documentId: DocumentId) =>
(_isQueryCopilotSampleContainer ? readSampleDocument(documentId) : readDocument(props.collection, documentId)).then( (_isQueryCopilotSampleContainer ? readSampleDocument(documentId) : readDocument(props.collection, documentId)).then(
@ -1116,7 +1142,7 @@ const DocumentsTabComponent: React.FunctionComponent<{
<button <button
className="filterbtnstyle queryButton" className="filterbtnstyle queryButton"
onClick={onShowFilterClick} onClick={onShowFilterClick}
/*data-bind="click: onShowFilterClick"*/ /*data-bind="click: onShowFilterClick"*/
> >
Edit Filter Edit Filter
</button> </button>
@ -1166,14 +1192,14 @@ const DocumentsTabComponent: React.FunctionComponent<{
} }
value={filterContent} value={filterContent}
onChange={(e) => setFilterContent(e.target.value)} onChange={(e) => setFilterContent(e.target.value)}
/* /*
data-bind=" data-bind="
W attr:{ W attr:{
placeholder:isPreferredApiMongoDB?'Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents.':'Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents.' placeholder:isPreferredApiMongoDB?'Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents.':'Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents.'
}, },
css: { placeholderVisible: filterContent().length === 0 }, css: { placeholderVisible: filterContent().length === 0 },
textInput: filterContent" textInput: filterContent"
*/ */
/> />
<datalist id="filtersList" /*data-bind="foreach: lastFilterContents"*/> <datalist id="filtersList" /*data-bind="foreach: lastFilterContents"*/>
@ -1219,7 +1245,7 @@ const DocumentsTabComponent: React.FunctionComponent<{
tabIndex={0} tabIndex={0}
onClick={onHideFilterClick} onClick={onHideFilterClick}
onKeyDown={onCloseButtonKeyDown} onKeyDown={onCloseButtonKeyDown}
/*data-bind="click: onHideFilterClick, event: { keydown: onCloseButtonKeyDown }"*/ /*data-bind="click: onHideFilterClick, event: { keydown: onCloseButtonKeyDown }"*/
> >
<img src={CloseIcon} style={{ height: 14, width: 14 }} alt="Hide filter" /> <img src={CloseIcon} style={{ height: 14, width: 14 }} alt="Hide filter" />
</span> </span>
@ -1250,7 +1276,7 @@ const DocumentsTabComponent: React.FunctionComponent<{
</a> </a>
</div> </div>
<div style={{ minWidth: "20%", width: "100%" }}> <div style={{ minWidth: "20%", width: "100%" }}>
{shouldShowEditor && ( {selectedDocumentContent && multiSelectedDocumentIds.length === 1 && (
<EditorReact <EditorReact
language={"json"} language={"json"}
content={selectedDocumentContent} content={selectedDocumentContent}
@ -1261,6 +1287,9 @@ const DocumentsTabComponent: React.FunctionComponent<{
onContentChanged={_onEditorContentChange} onContentChanged={_onEditorContentChange}
/> />
)} )}
{multiSelectedDocumentIds.length > 1 && (
<span style={{ margin: 10 }}>Number of selected documents: {multiSelectedDocumentIds.length}</span>
)}
</div> </div>
</Split> </Split>
</div> </div>