+ let progressBar;
+
+ let statusDiv = (
+
+
+ {progressData?.message && (
+
+ )}
+ {percentage >= 98 ? (
+
Almost done, finalizing your bundle...
+ ) : (
+
Analyzing {progressData?.message}
+ )}
+
+
+ );
+
+ if (progressData.collectorsCompleted > 0) {
+ moveBar(progressData);
+ progressBar = (
+
+ );
+ } else {
+ percentage = 0;
+ progressBar = (
+
+ );
+ }
+
+ return (
+
+
+
+
+
handleBundleClick(bundle)}
+ >
+
+ {!props.isCustomer ? (
+
+
-
- Collected on{" "}
-
- {dayjs(bundle.createdAt).format(
- "MMMM D, YYYY @ h:mm a"
- )}
-
+ Collected on{" "}
+
+ {dayjs(bundle.createdAt).format(
+ "MMMM D, YYYY @ h:mm a"
+ )}
- {this.renderSharedContext()}
-
- )}
-
-
- {this.props.loadingBundle ? (
- statusDiv
- ) : bundle?.analysis?.insights?.length ? (
-
- {errorInsights && errorInsights.length > 0 && (
-
-
- {errorInsights.length} error
- {errorInsights.length > 1 ? "s" : ""} found
-
- )}
- {warningInsights && warningInsights.length > 0 && (
-
-
- {warningInsights.length} warning
- {warningInsights.length > 1 ? "s" : ""} found
-
- )}
- {otherInsights && otherInsights.length > 0 && (
-
-
- {otherInsights.length} informational and debugging
- insight{otherInsights.length > 1 ? "s" : ""} found
+
+
+ ) : (
+
+
+
+ Collected on{" "}
+
+ {dayjs(bundle.createdAt).format(
+ "MMMM D, YYYY @ h:mm a"
+ )}
- )}
-
- ) : (
- noInsightsMessage
- )}
-
-
-
- {this.state.sendingBundleErrMsg && (
-
- {this.state.sendingBundleErrMsg}
-
- )}
- {this.props.bundle.sharedAt ? (
-
-
-
- Sent to vendor on{" "}
- {Utilities.dateFormat(bundle.sharedAt, "MM/DD/YYYY")}
+
+ {renderSharedContext()}
- ) : this.state.sendingBundle ? (
-
- ) : showSendSupportBundleLink && !loadingBundle ? (
-
- this.sendBundleToVendor(this.props.bundle.slug)
- }
- >
-
-
- ) : null}
- {this.state.downloadBundleErrMsg && (
-
- {this.state.downloadBundleErrMsg}
-
)}
- {this.state.downloadingBundle ? (
-
- ) : this.props.loadingBundle ||
- this.props.progressData?.collectorsCompleted > 0 ? (
-
-
- {percentage.toString() + "%"}
-
- {progressBar}
-
- 100%
-
+
+
+ {props.loadingBundle ? (
+ statusDiv
+ ) : bundle?.analysis?.insights?.length ? (
+
+ {errorInsights && errorInsights.length > 0 && (
+
+
+ {errorInsights.length} error
+ {errorInsights.length > 1 ? "s" : ""} found
+
+ )}
+ {warningInsights && warningInsights.length > 0 && (
+
+
+ {warningInsights.length} warning
+ {warningInsights.length > 1 ? "s" : ""} found
+
+ )}
+ {otherInsights && otherInsights.length > 0 && (
+
+
+ {otherInsights.length} informational and debugging
+ insight{otherInsights.length > 1 ? "s" : ""} found
+
+ )}
) : (
-
this.downloadBundle(bundle)}
- >
-
-
+ noInsightsMessage
)}
+
+ {state.sendingBundleErrMsg && (
+
+ {state.sendingBundleErrMsg}
+
+ )}
+ {props.bundle.sharedAt ? (
+
+
+
+ Sent to vendor on{" "}
+ {Utilities.dateFormat(bundle.sharedAt, "MM/DD/YYYY")}
+
+
+ ) : state.sendingBundle ? (
+
+ ) : showSendSupportBundleLink && !loadingBundle ? (
+
sendBundleToVendor(props.bundle.slug)}
+ >
+
+
+ ) : null}
+ {state.downloadBundleErrMsg && (
+
+ {state.downloadBundleErrMsg}
+
+ )}
+ {state.downloadingBundle ? (
+
+ ) : props.loadingBundle ||
+ props.progressData?.collectorsCompleted > 0 ? (
+
+
+ {percentage.toString() + "%"}
+
+ {progressBar}
+
+ 100%
+
+
+ ) : (
+
downloadBundle(bundle)}
+ >
+
+
+ )}
+
deleteBundle(bundle)}
+ >
+
+
+
- );
- }
-}
+
+ );
+};
/* eslint-disable */
// @ts-ignore
diff --git a/web/src/context/ToastContext.tsx b/web/src/context/ToastContext.tsx
new file mode 100644
index 0000000000..ca07a6293f
--- /dev/null
+++ b/web/src/context/ToastContext.tsx
@@ -0,0 +1,39 @@
+import React, { createContext, ReactNode, useState } from "react";
+
+interface ToastContextProps {
+ isToastVisible: boolean;
+ setIsToastVisible: (val: boolean) => void;
+ isCancelled: boolean;
+ setIsCancelled: (val: boolean) => void;
+ deleteBundleId: string;
+ setDeleteBundleId: (val: string) => void;
+ toastMessage: string;
+ setToastMessage: (val: string) => void;
+}
+
+const ToastContext = createContext({} as ToastContextProps);
+
+const ToastProvider = ({ children }: { children: ReactNode }) => {
+ const [isToastVisible, setIsToastVisible] = useState(false);
+ const [isCancelled, setIsCancelled] = useState(false);
+ const [deleteBundleId, setDeleteBundleId] = useState("");
+ const [toastMessage, setToastMessage] = useState("");
+ return (
+
+ {children}
+
+ );
+};
+
+export { ToastContext, ToastProvider };
diff --git a/web/src/scss/components/troubleshoot/SupportBundleList.scss b/web/src/scss/components/troubleshoot/SupportBundleList.scss
index a20216bede..d726a5374d 100644
--- a/web/src/scss/components/troubleshoot/SupportBundleList.scss
+++ b/web/src/scss/components/troubleshoot/SupportBundleList.scss
@@ -86,6 +86,16 @@
}
}
}
+.deleting {
+ animation: blinker 3s linear infinite;
+ cursor: not-allowed !important;
+}
+
+@keyframes blinker {
+ 50% {
+ opacity: 0.2;
+ }
+}
.CommunityLicenseBundle--wrapper {
max-height: 94px;
diff --git a/web/src/scss/utilities/base.scss b/web/src/scss/utilities/base.scss
index ead31a26da..fddef1f017 100644
--- a/web/src/scss/utilities/base.scss
+++ b/web/src/scss/utilities/base.scss
@@ -512,3 +512,49 @@ and `background-position: [value]`
color: $text-color-primary;
line-height: 1.7;
}
+
+@mixin keyframes($animation-name) {
+ @-webkit-keyframes #{$animation-name} {
+ @content;
+ }
+ @keyframes #{$animation-name} {
+ @content;
+ }
+}
+
+@mixin animation($str) {
+ -webkit-animation: #{$str};
+ animation: #{$str};
+}
+
+@include keyframes(fadein) {
+ from {
+ bottom: 0;
+ opacity: 0;
+ }
+ to {
+ bottom: 10px;
+ opacity: 1;
+ }
+}
+
+@include keyframes(fadeout) {
+ from {
+ bottom: 10px;
+ opacity: 1;
+ }
+ to {
+ bottom: 0;
+ opacity: 0;
+ }
+}
+
+.toast {
+ visibility: hidden;
+ bottom: 3px;
+}
+
+.toast.visible {
+ visibility: visible;
+ @include animation("fadein 0.5s, fadeout 0.5s 5s");
+}
diff --git a/web/src/stories/Toast.stories.tsx b/web/src/stories/Toast.stories.tsx
new file mode 100644
index 0000000000..b8b7fca355
--- /dev/null
+++ b/web/src/stories/Toast.stories.tsx
@@ -0,0 +1,64 @@
+import React from "react";
+import { ComponentStory, ComponentMeta } from "@storybook/react";
+import Toast from "@src/components/shared/Toast";
+import Icon from "@src/components/Icon";
+
+export default {
+ title: "Example/Toast",
+ component: Toast,
+} as ComponentMeta
;
+
+const Template: ComponentStory = () => (
+
+
+
+
+
Success!
+
alert("close toast")}
+ />
+
+
+
+
+
+
+
Deleting item
+
alert("undo")}
+ className="tw-underline tw-cursor-pointer"
+ >
+ undo
+
+
alert("close toast")}
+ />
+
+
+
+
+
+
+
Error! Please do something!
+
alert("close toast")}
+ />
+
+
+
+
+);
+
+export const ToastExample = Template.bind({});