mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-05-12 03:14:45 +01:00
feat: Enhance UploadItemsPane with error handling and status icons for file uploads (#2133)
This commit is contained in:
parent
985c744198
commit
0f896f556b
@ -356,7 +356,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
value=""
|
value=""
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField is-required root-110"
|
className="ms-TextField is-required root-116"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField-wrapper"
|
className="ms-TextField-wrapper"
|
||||||
@ -647,7 +647,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
className="ms-Label root-121"
|
className="ms-Label root-127"
|
||||||
htmlFor="TextField0"
|
htmlFor="TextField0"
|
||||||
id="TextFieldLabel2"
|
id="TextFieldLabel2"
|
||||||
>
|
>
|
||||||
@ -656,13 +656,13 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
</LabelBase>
|
</LabelBase>
|
||||||
</StyledLabelBase>
|
</StyledLabelBase>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField-fieldGroup fieldGroup-111"
|
className="ms-TextField-fieldGroup fieldGroup-117"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-invalid={false}
|
aria-invalid={false}
|
||||||
aria-labelledby="TextFieldLabel2"
|
aria-labelledby="TextFieldLabel2"
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
className="ms-TextField-field field-112"
|
className="ms-TextField-field field-118"
|
||||||
id="TextField0"
|
id="TextField0"
|
||||||
name="collectionIdConfirmation"
|
name="collectionIdConfirmation"
|
||||||
onBlur={[Function]}
|
onBlur={[Function]}
|
||||||
@ -2464,7 +2464,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Create"
|
aria-label="Create"
|
||||||
className="ms-Button ms-Button--primary root-122"
|
className="ms-Button ms-Button--primary root-128"
|
||||||
data-is-focusable={true}
|
data-is-focusable={true}
|
||||||
data-test="Panel/OkButton"
|
data-test="Panel/OkButton"
|
||||||
id="sidePanelOkButton"
|
id="sidePanelOkButton"
|
||||||
@ -2477,14 +2477,14 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="ms-Button-flexContainer flexContainer-123"
|
className="ms-Button-flexContainer flexContainer-129"
|
||||||
data-automationid="splitbuttonprimary"
|
data-automationid="splitbuttonprimary"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="ms-Button-textContainer textContainer-124"
|
className="ms-Button-textContainer textContainer-130"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="ms-Button-label label-126"
|
className="ms-Button-label label-132"
|
||||||
id="id__5"
|
id="id__5"
|
||||||
key="id__5"
|
key="id__5"
|
||||||
>
|
>
|
||||||
|
@ -2,9 +2,13 @@ import {
|
|||||||
DetailsList,
|
DetailsList,
|
||||||
DetailsListLayoutMode,
|
DetailsListLayoutMode,
|
||||||
DirectionalHint,
|
DirectionalHint,
|
||||||
|
FontIcon,
|
||||||
IColumn,
|
IColumn,
|
||||||
SelectionMode,
|
SelectionMode,
|
||||||
TooltipHost,
|
TooltipHost,
|
||||||
|
getTheme,
|
||||||
|
mergeStyles,
|
||||||
|
mergeStyleSets,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { Upload } from "Common/Upload/Upload";
|
import { Upload } from "Common/Upload/Upload";
|
||||||
import { UploadDetailsRecord } from "Contracts/ViewModels";
|
import { UploadDetailsRecord } from "Contracts/ViewModels";
|
||||||
@ -14,6 +18,36 @@ import { getErrorMessage } from "../../Tables/Utilities";
|
|||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||||
|
|
||||||
|
const theme = getTheme();
|
||||||
|
const iconClass = mergeStyles({
|
||||||
|
verticalAlign: "middle",
|
||||||
|
maxHeight: "16px",
|
||||||
|
maxWidth: "16px",
|
||||||
|
});
|
||||||
|
|
||||||
|
const classNames = mergeStyleSets({
|
||||||
|
fileIconHeaderIcon: {
|
||||||
|
padding: 0,
|
||||||
|
fontSize: "16px",
|
||||||
|
},
|
||||||
|
fileIconCell: {
|
||||||
|
textAlign: "center",
|
||||||
|
selectors: {
|
||||||
|
"&:before": {
|
||||||
|
content: ".",
|
||||||
|
display: "inline-block",
|
||||||
|
verticalAlign: "middle",
|
||||||
|
height: "100%",
|
||||||
|
width: "0px",
|
||||||
|
visibility: "hidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
error: [{ color: theme.semanticColors.errorIcon }, iconClass],
|
||||||
|
accept: [{ color: theme.semanticColors.successIcon }, iconClass],
|
||||||
|
warning: [{ color: theme.semanticColors.warningIcon }, iconClass],
|
||||||
|
});
|
||||||
|
|
||||||
export const UploadItemsPane: FunctionComponent = () => {
|
export const UploadItemsPane: FunctionComponent = () => {
|
||||||
const [files, setFiles] = useState<FileList>();
|
const [files, setFiles] = useState<FileList>();
|
||||||
const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]);
|
const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]);
|
||||||
@ -60,44 +94,94 @@ export const UploadItemsPane: FunctionComponent = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const columns: IColumn[] = [
|
const columns: IColumn[] = [
|
||||||
|
{
|
||||||
|
key: "icons",
|
||||||
|
name: "",
|
||||||
|
fieldName: "",
|
||||||
|
className: classNames.fileIconCell,
|
||||||
|
iconClassName: classNames.fileIconHeaderIcon,
|
||||||
|
isIconOnly: true,
|
||||||
|
minWidth: 16,
|
||||||
|
maxWidth: 16,
|
||||||
|
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||||
|
if (item.numFailed) {
|
||||||
|
const errorList = (
|
||||||
|
<ul
|
||||||
|
aria-label={"error list"}
|
||||||
|
style={{
|
||||||
|
margin: "5px 0",
|
||||||
|
paddingLeft: "20px",
|
||||||
|
listStyleType: "disc", // Explicitly set to use bullets (dots)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.errors.map((error, i) => (
|
||||||
|
<li key={i} style={{ display: "list-item" }}>
|
||||||
|
{error}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TooltipHost
|
||||||
|
content={errorList}
|
||||||
|
id={`tooltip-${index}-${column.key}`}
|
||||||
|
directionalHint={DirectionalHint.bottomAutoEdge}
|
||||||
|
>
|
||||||
|
<FontIcon iconName="Error" className={classNames.error} aria-label="error" />
|
||||||
|
</TooltipHost>
|
||||||
|
);
|
||||||
|
} else if (item.numThrottled) {
|
||||||
|
return <FontIcon iconName="Warning" className={classNames.warning} aria-label="warning" />;
|
||||||
|
} else {
|
||||||
|
return <FontIcon iconName="Accept" className={classNames.accept} aria-label="accept" />;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "fileName",
|
key: "fileName",
|
||||||
name: "FILE NAME",
|
name: "FILE NAME",
|
||||||
fieldName: "fileName",
|
fieldName: "fileName",
|
||||||
minWidth: 140,
|
minWidth: 120,
|
||||||
maxWidth: 140,
|
maxWidth: 140,
|
||||||
|
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||||
|
const fieldContent = item.fileName;
|
||||||
|
return (
|
||||||
|
<TooltipHost
|
||||||
|
content={fieldContent}
|
||||||
|
id={`tooltip-${index}-${column.key}`}
|
||||||
|
directionalHint={DirectionalHint.bottomAutoEdge}
|
||||||
|
>
|
||||||
|
{fieldContent}
|
||||||
|
</TooltipHost>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "status",
|
key: "status",
|
||||||
name: "STATUS",
|
name: "STATUS",
|
||||||
fieldName: "numSucceeded",
|
fieldName: "numSucceeded",
|
||||||
minWidth: 140,
|
minWidth: 120,
|
||||||
maxWidth: 140,
|
maxWidth: 140,
|
||||||
isRowHeader: true,
|
isRowHeader: true,
|
||||||
isResizable: true,
|
isResizable: true,
|
||||||
data: "string",
|
data: "string",
|
||||||
isPadded: true,
|
isPadded: true,
|
||||||
|
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||||
|
const fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
|
||||||
|
return (
|
||||||
|
<TooltipHost
|
||||||
|
content={fieldContent}
|
||||||
|
id={`tooltip-${index}-${column.key}`}
|
||||||
|
directionalHint={DirectionalHint.bottomAutoEdge}
|
||||||
|
>
|
||||||
|
{fieldContent}
|
||||||
|
</TooltipHost>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const _renderItemColumn = (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
|
||||||
let fieldContent: string;
|
|
||||||
const tooltipId = `tooltip-${index}-${column.key}`;
|
|
||||||
|
|
||||||
switch (column.key) {
|
|
||||||
case "status":
|
|
||||||
fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fieldContent = item.fileName;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<TooltipHost content={fieldContent} id={tooltipId} directionalHint={DirectionalHint.rightCenter}>
|
|
||||||
{fieldContent}
|
|
||||||
</TooltipHost>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RightPaneForm {...props}>
|
<RightPaneForm {...props}>
|
||||||
<div className="paneMainContent">
|
<div className="paneMainContent">
|
||||||
@ -115,7 +199,6 @@ export const UploadItemsPane: FunctionComponent = () => {
|
|||||||
<DetailsList
|
<DetailsList
|
||||||
items={uploadFileData}
|
items={uploadFileData}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onRenderItemColumn={_renderItemColumn}
|
|
||||||
selectionMode={SelectionMode.none}
|
selectionMode={SelectionMode.none}
|
||||||
layoutMode={DetailsListLayoutMode.justified}
|
layoutMode={DetailsListLayoutMode.justified}
|
||||||
isHeaderVisible={true}
|
isHeaderVisible={true}
|
||||||
|
@ -1122,6 +1122,9 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
stats.numSucceeded++;
|
stats.numSucceeded++;
|
||||||
} else if (response.statusCode === 429) {
|
} else if (response.statusCode === 429) {
|
||||||
documentsToAttempt.push(attemptedDocuments[index]);
|
documentsToAttempt.push(attemptedDocuments[index]);
|
||||||
|
} else if (response.statusCode === 409) {
|
||||||
|
stats.numFailed++;
|
||||||
|
stats.errors.push(`Document with id ${attemptedDocuments[index].id} already exists.`);
|
||||||
} else {
|
} else {
|
||||||
stats.numFailed++;
|
stats.numFailed++;
|
||||||
stats.errors.push(JSON.stringify(response.resourceBody));
|
stats.errors.push(JSON.stringify(response.resourceBody));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user