diff --git a/web/apps/labelstudio/src/components/Modal/Modal.scss b/web/apps/labelstudio/src/components/Modal/Modal.scss index 4e7a43df7127..8a3827f7c3d0 100644 --- a/web/apps/labelstudio/src/components/Modal/Modal.scss +++ b/web/apps/labelstudio/src/components/Modal/Modal.scss @@ -76,7 +76,7 @@ } &__footer { - padding: 1rem 1.5rem; + padding: var(--spacing-base) var(--spacing-wider); text-align: center; font-size: 14px; line-height: 22px; diff --git a/web/apps/labelstudio/src/pages/Settings/DangerZone.jsx b/web/apps/labelstudio/src/pages/Settings/DangerZone.jsx index 578e890b4808..1dbc446bdeff 100644 --- a/web/apps/labelstudio/src/pages/Settings/DangerZone.jsx +++ b/web/apps/labelstudio/src/pages/Settings/DangerZone.jsx @@ -1,9 +1,12 @@ import { useMemo, useState } from "react"; import { useHistory } from "react-router"; -import { Button } from "@humansignal/ui"; +import { Button, Typography, useToast } from "@humansignal/ui"; import { useUpdatePageTitle, createTitleFromSegments } from "@humansignal/core"; import { Label } from "../../components/Form"; -import { confirm } from "../../components/Modal/Modal"; +import { modal } from "../../components/Modal/Modal"; +import { useModalControls } from "../../components/Modal/ModalPopup"; +import Input from "../../components/Form/Elements/Input/Input"; +import { Space } from "../../components/Space/Space"; import { Spinner } from "../../components/Spinner/Spinner"; import { useAPI } from "../../providers/ApiProvider"; import { useProject } from "../../providers/ProjectProvider"; @@ -13,45 +16,143 @@ export const DangerZone = () => { const { project } = useProject(); const api = useAPI(); const history = useHistory(); + const toast = useToast(); const [processing, setProcessing] = useState(null); useUpdatePageTitle(createTitleFromSegments([project?.title, "Danger Zone"])); + const showDangerConfirmation = ({ title, message, requiredWord, buttonText, onConfirm }) => { + const isDev = process.env.NODE_ENV === "development"; + + return modal({ + title, + width: 600, + allowClose: false, + body: () => { + const ctrl = useModalControls(); + const inputValue = ctrl?.state?.inputValue || ""; + + return ( +
+ + {message} + + ctrl?.setState({ inputValue: e.target.value })} + autoFocus + data-testid="danger-zone-confirmation-input" + autoComplete="off" + /> +
+ ); + }, + footer: () => { + const ctrl = useModalControls(); + const inputValue = (ctrl?.state?.inputValue || "").trim().toLowerCase(); + const isValid = isDev || inputValue === requiredWord.toLowerCase(); + + return ( + + + + + ); + }, + }); + }; + const handleOnClick = (type) => () => { - confirm({ - title: "Action confirmation", - body: "You're about to delete all things. This action cannot be undone.", - okText: "Proceed", - buttonLook: "negative", - onOk: async () => { + const actionConfig = { + reset_cache: { + title: "Reset Cache", + message: ( + <> + You are about to reset the cache for {project.title}. This action cannot be undone. + + ), + requiredWord: "cache", + buttonText: "Reset Cache", + }, + tabs: { + title: "Drop All Tabs", + message: ( + <> + You are about to drop all tabs for {project.title}. This action cannot be undone. + + ), + requiredWord: "tabs", + buttonText: "Drop All Tabs", + }, + project: { + title: "Delete Project", + message: ( + <> + You are about to delete the project {project.title}. This action cannot be undone. + + ), + requiredWord: "delete", + buttonText: "Delete Project", + }, + }; + + const config = actionConfig[type]; + + if (!config) { + return; + } + + showDangerConfirmation({ + ...config, + onConfirm: async () => { setProcessing(type); - if (type === "annotations") { - // console.log('delete annotations'); - } else if (type === "tasks") { - // console.log('delete tasks'); - } else if (type === "predictions") { - // console.log('delete predictions'); - } else if (type === "reset_cache") { - await api.callApi("projectResetCache", { - params: { - pk: project.id, - }, - }); - } else if (type === "tabs") { - await api.callApi("deleteTabs", { - body: { - project: project.id, - }, - }); - } else if (type === "project") { - await api.callApi("deleteProject", { - params: { - pk: project.id, - }, - }); - history.replace("/projects"); + try { + if (type === "reset_cache") { + await api.callApi("projectResetCache", { + params: { + pk: project.id, + }, + }); + toast.show({ message: "Cache reset successfully" }); + } else if (type === "tabs") { + await api.callApi("deleteTabs", { + body: { + project: project.id, + }, + }); + toast.show({ message: "All tabs dropped successfully" }); + } else if (type === "project") { + await api.callApi("deleteProject", { + params: { + pk: project.id, + }, + }); + toast.show({ message: "Project deleted successfully" }); + history.replace("/projects"); + } + } catch (error) { + toast.show({ message: `Error: ${error.message}`, type: "error" }); + } finally { + setProcessing(null); } - setProcessing(null); }, }); }; @@ -97,8 +198,13 @@ export const DangerZone = () => { return (
-

Danger Zone

-