mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-26 15:37:02 +00:00
Implement delete multiple docs
This commit is contained in:
parent
b686501df8
commit
2f85c4f84a
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user