Feroze Mohideen 2 лет назад
Родитель
Сommit
befd92dcf3

+ 1 - 1
dashboard/src/lib/hooks/useCluster.ts

@@ -666,7 +666,7 @@ const preflightCheckErrorReplacements = {
     "Your cloud provider is currently throttling API requests. Please try again in a few minutes.",
 };
 
-const getErrorMessageFromNetworkCall = (
+export const getErrorMessageFromNetworkCall = (
   err: unknown,
   networkCallDescription: string,
   replaceError?: Record<string, string>

+ 133 - 33
dashboard/src/main/home/database-dashboard/tabs/SettingsTab.tsx

@@ -1,67 +1,167 @@
-import React, { useContext } from "react";
+import React, { useMemo, useState } from "react";
 import styled from "styled-components";
 
 import Button from "components/porter/Button";
+import { Error as ErrorComponent } from "components/porter/Error";
 import Icon from "components/porter/Icon";
+import Input from "components/porter/Input";
+import Link from "components/porter/Link";
+import Modal from "components/porter/Modal";
 import Spacer from "components/porter/Spacer";
 import Text from "components/porter/Text";
+import { type UpdateClusterButtonProps } from "main/home/infrastructure-dashboard/ClusterFormContextProvider";
+import { getErrorMessageFromNetworkCall } from "lib/hooks/useCluster";
 import { useDatastoreMethods } from "lib/hooks/useDatabaseMethods";
 
-import { Context } from "shared/Context";
 import trash from "assets/trash.png";
 
 import { useDatastoreContext } from "../DatabaseContextProvider";
 
 const SettingsTab: React.FC = () => {
-  const { setCurrentOverlay } = useContext(Context);
+  const [showDeleteDatastoreModal, setShowDeleteDatastoreModal] =
+    useState(false);
+
   const { datastore } = useDatastoreContext();
   const { deleteDatastore } = useDatastoreMethods();
-  const handleDeletionSubmit = async (): Promise<void> => {
-    if (setCurrentOverlay == null) {
-      return;
-    }
 
+  return (
+    <div>
+      <StyledTemplateComponent>
+        <Text size={16}>Delete &quot;{datastore.name}&quot;</Text>
+        <Spacer y={0.5} />
+        <Text color="helper">
+          Delete this datastore and all of its resources.
+        </Text>
+        <Spacer y={0.5} />
+        <Button
+          color="#b91133"
+          onClick={() => {
+            setShowDeleteDatastoreModal(true);
+          }}
+        >
+          <Icon src={trash} height={"15px"} />
+          <Spacer inline x={0.5} />
+          Delete {datastore.name}
+        </Button>
+      </StyledTemplateComponent>
+      {showDeleteDatastoreModal && (
+        <DeleteDatastoreModal
+          datastoreName={datastore.name}
+          onClose={() => {
+            setShowDeleteDatastoreModal(false);
+          }}
+          onSubmit={async () => {
+            await deleteDatastore(datastore.name);
+          }}
+        />
+      )}
+    </div>
+  );
+};
+
+export default SettingsTab;
+
+type DeleteDatastoreModalProps = {
+  datastoreName: string;
+  onSubmit: () => Promise<void>;
+  onClose: () => void;
+};
+
+const DeleteDatastoreModal: React.FC<DeleteDatastoreModalProps> = ({
+  datastoreName,
+  onSubmit,
+  onClose,
+}) => {
+  const [inputtedDatastoreName, setInputtedDatastoreName] =
+    useState<string>("");
+  const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
+  const [deleteDatastoreError, setDeleteDatastoreError] = useState<string>("");
+
+  const confirmDeletion = async (): Promise<void> => {
+    setIsSubmitting(true);
     try {
-      await deleteDatastore(datastore.name);
-      setCurrentOverlay(null);
-    } catch (error) {
-      // todo: handle error
+      await onSubmit();
+      onClose();
+    } catch (err) {
+      setDeleteDatastoreError(
+        getErrorMessageFromNetworkCall(err, "Datastore deletion")
+      );
+    } finally {
+      setIsSubmitting(false);
     }
   };
 
-  const handleDeletionClick = async (): Promise<void> => {
-    if (setCurrentOverlay === undefined) {
-      return;
+  const deleteButtonProps: UpdateClusterButtonProps = useMemo(() => {
+    if (isSubmitting) {
+      return {
+        status: "loading",
+        isDisabled: true,
+      };
     }
-
-    setCurrentOverlay({
-      message: `Are you sure you want to delete ${datastore.name}?`,
-      onYes: handleDeletionSubmit,
-      onNo: () => {
-        setCurrentOverlay(null);
-      },
-    });
-  };
+    if (deleteDatastoreError) {
+      return {
+        status: (
+          <ErrorComponent message={deleteDatastoreError} maxWidth="600px" />
+        ),
+        isDisabled: false,
+      };
+    }
+    return {
+      status: "",
+      isDisabled: false,
+    };
+  }, [isSubmitting, deleteDatastoreError]);
 
   return (
-    <StyledTemplateComponent>
-      <Text size={16}>Delete &quot;{datastore.name}&quot;</Text>
+    <Modal closeModal={onClose}>
+      <Text size={16}>Delete {datastoreName}?</Text>
+      <Spacer y={1} />
+
+      <Text size={14} color="red">
+        Attention:
+      </Text>
+      <Spacer y={0.1} />
+      <Text>
+        Destruction of resources sometimes results in dangling resources. To
+        ensure that everything has been properly destroyed, please visit your
+        cloud provider&apos;s console.
+      </Text>
       <Spacer y={0.5} />
+      <Link
+        target="_blank"
+        hasunderline
+        to="https://docs.porter.run/other/deleting-dangling-resources"
+      >
+        Deletion instructions
+      </Link>
+      <Spacer y={1} />
       <Text color="helper">
-        Delete this datastore and all of its resources.
+        To confirm, enter the datastore name below. This action is irreversible.
       </Text>
       <Spacer y={0.5} />
-      <Button color="#b91133" onClick={handleDeletionClick}>
-        <Icon src={trash} height={"15px"} />
-        <Spacer inline x={0.5} />
-        Delete {datastore.name}
+      <Input
+        placeholder={datastoreName}
+        value={inputtedDatastoreName}
+        setValue={setInputtedDatastoreName}
+        width="100%"
+        height="40px"
+      />
+      <Spacer y={1} />
+      <Button
+        color="#b91133"
+        onClick={async () => {
+          await confirmDeletion();
+        }}
+        status={deleteButtonProps.status}
+        disabled={deleteButtonProps.isDisabled}
+        loadingText={"Deleting..."}
+      >
+        Delete
       </Button>
-    </StyledTemplateComponent>
+    </Modal>
   );
 };
 
-export default SettingsTab;
-
 const StyledTemplateComponent = styled.div`
   width: 100%;
   animation: fadeIn 0.3s 0s;

+ 1 - 1
dashboard/src/main/home/infrastructure-dashboard/ClusterFormContextProvider.tsx

@@ -29,7 +29,7 @@ import PreflightChecksModal from "./modals/PreflightChecksModal";
 export type UpdateClusterButtonProps = {
   status: "" | "loading" | JSX.Element | "success";
   isDisabled: boolean;
-  loadingText: string;
+  loadingText?: string;
 };
 
 type ClusterFormContextType = {