mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-18 07:39:54 -06:00
Merge pull request #2534 from bluewave-labs/issue-2150-move-status-page-delete-button
Move Status Page Delete Button
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Components
|
||||
import { TabContext } from "@mui/lab";
|
||||
import { Tab, useTheme } from "@mui/material";
|
||||
import { Tab } from "@mui/material";
|
||||
import Settings from "./Settings";
|
||||
import Content from "./Content";
|
||||
|
||||
@@ -22,8 +22,12 @@ const Tabs = ({
|
||||
tab,
|
||||
setTab,
|
||||
TAB_LIST,
|
||||
handleDelete,
|
||||
isDeleteOpen,
|
||||
setIsDeleteOpen,
|
||||
isDeleting,
|
||||
isLoading,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<TabContext value={TAB_LIST[tab]}>
|
||||
<CustomTabList
|
||||
@@ -32,7 +36,7 @@ const Tabs = ({
|
||||
}}
|
||||
aria-label="status page tabs"
|
||||
>
|
||||
{TAB_LIST.map((tabLabel, idx) => (
|
||||
{TAB_LIST.map((tabLabel) => (
|
||||
<Tab
|
||||
key={tabLabel}
|
||||
label={tabLabel}
|
||||
@@ -50,6 +54,11 @@ const Tabs = ({
|
||||
removeLogo={removeLogo}
|
||||
errors={errors}
|
||||
isCreate={isCreate}
|
||||
handleDelete={handleDelete}
|
||||
isDeleteOpen={isDeleteOpen}
|
||||
setIsDeleteOpen={setIsDeleteOpen}
|
||||
isDeleting={isDeleting}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
) : (
|
||||
<Content
|
||||
@@ -67,6 +76,7 @@ const Tabs = ({
|
||||
};
|
||||
|
||||
Tabs.propTypes = {
|
||||
isCreate: PropTypes.bool,
|
||||
form: PropTypes.object,
|
||||
errors: PropTypes.object,
|
||||
monitors: PropTypes.array,
|
||||
@@ -79,6 +89,11 @@ Tabs.propTypes = {
|
||||
tab: PropTypes.number,
|
||||
setTab: PropTypes.func,
|
||||
TAB_LIST: PropTypes.array,
|
||||
handleDelete: PropTypes.func,
|
||||
isDeleteOpen: PropTypes.bool,
|
||||
setIsDeleteOpen: PropTypes.func,
|
||||
isDeleting: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Tabs;
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Stack, Button, Typography } from "@mui/material";
|
||||
import Tabs from "./Components/Tabs";
|
||||
import GenericFallback from "../../../Components/GenericFallback";
|
||||
import SkeletonLayout from "./Components/Skeleton";
|
||||
import Dialog from "../../../Components/Dialog";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
//Utils
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
@@ -15,9 +17,8 @@ import { useNavigate } from "react-router-dom";
|
||||
import { useStatusPageFetch } from "../Status/Hooks/useStatusPageFetch";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useStatusPageDelete } from "../Status/Hooks/useStatusPageDelete";
|
||||
//Constants
|
||||
const TAB_LIST = ["General settings", "Contents"];
|
||||
|
||||
const ERROR_TAB_MAPPING = [
|
||||
["companyName", "url", "timezone", "color", "isPublished", "logo"],
|
||||
["monitors", "showUptimePercentage", "showCharts", "showAdminLoginLink"],
|
||||
@@ -28,6 +29,7 @@ const CreateStatusPage = () => {
|
||||
//Local state
|
||||
const [tab, setTab] = useState(0);
|
||||
const [progress, setProgress] = useState({ value: 0, isLoading: false });
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
const [form, setForm] = useState({
|
||||
isPublished: false,
|
||||
companyName: "",
|
||||
@@ -52,13 +54,13 @@ const CreateStatusPage = () => {
|
||||
//Utils
|
||||
const theme = useTheme();
|
||||
const [monitors, isLoading, networkError] = useMonitorsFetch();
|
||||
const [createStatusPage, createStatusIsLoading, createStatusPageNetworkError] =
|
||||
useCreateStatusPage(isCreate);
|
||||
const [createStatusPage] = useCreateStatusPage(isCreate);
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [statusPage, statusPageMonitors, statusPageIsLoading, statusPageNetworkError] =
|
||||
const [statusPage, statusPageMonitors, statusPageIsLoading, , fetchStatusPage] =
|
||||
useStatusPageFetch(isCreate, url);
|
||||
const [deleteStatusPage, isDeleting] = useStatusPageDelete(fetchStatusPage, url);
|
||||
|
||||
console.log(JSON.stringify(form, null, 2));
|
||||
// Handlers
|
||||
@@ -124,6 +126,19 @@ const CreateStatusPage = () => {
|
||||
setProgress({ value: 0, isLoading: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle status page deletion with optimistic UI update
|
||||
* Immediately navigates away without waiting for the deletion to complete
|
||||
* to prevent unnecessary network requests for the deleted page
|
||||
*/
|
||||
const handleDelete = async () => {
|
||||
setIsDeleteOpen(false);
|
||||
// Start deletion process but don't wait for it
|
||||
deleteStatusPage();
|
||||
// Immediately navigate away to prevent additional fetches for the deleted page
|
||||
navigate("/status");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
let toSubmit = {
|
||||
...form,
|
||||
@@ -137,9 +152,7 @@ const CreateStatusPage = () => {
|
||||
const success = await createStatusPage({ form });
|
||||
if (success) {
|
||||
createToast({
|
||||
body: isCreate
|
||||
? "Status page created successfully"
|
||||
: "Status page updated successfully",
|
||||
body: isCreate ? t("statusPage.createSuccess") : t("statusPage.updateSuccess"),
|
||||
});
|
||||
navigate(`/status/uptime/${form.url}`);
|
||||
}
|
||||
@@ -162,7 +175,7 @@ const CreateStatusPage = () => {
|
||||
|
||||
// If we get -1, there's an unknown error
|
||||
if (errorTabs[0] === -1) {
|
||||
createToast({ body: "Unknown error" });
|
||||
createToast({ body: t("common.toasts.unknownError") });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -223,6 +236,37 @@ const CreateStatusPage = () => {
|
||||
// Load fields
|
||||
return (
|
||||
<Stack gap={theme.spacing(10)}>
|
||||
<Breadcrumbs
|
||||
list={[
|
||||
{ name: t("statusBreadCrumbsStatusPages", "Status"), path: "/status" },
|
||||
{ name: t("statusBreadCrumbsDetails", "Details"), path: `/status/${url}` },
|
||||
{ name: t("configure", "Configure"), path: `/status/create/${url}` },
|
||||
]}
|
||||
/>
|
||||
{!isCreate && (
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
loading={isDeleting}
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => setIsDeleteOpen(true)}
|
||||
>
|
||||
{t("remove")}
|
||||
</Button>
|
||||
<Dialog
|
||||
title={t("deleteStatusPage")}
|
||||
onConfirm={handleDelete}
|
||||
onCancel={() => setIsDeleteOpen(false)}
|
||||
open={isDeleteOpen}
|
||||
confirmationButtonLabel={t("deleteStatusPageConfirm")}
|
||||
description={t("deleteStatusPageDescription")}
|
||||
isLoading={isDeleting || statusPageIsLoading}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
<Tabs
|
||||
form={form}
|
||||
errors={errors}
|
||||
@@ -235,8 +279,16 @@ const CreateStatusPage = () => {
|
||||
removeLogo={removeLogo}
|
||||
tab={tab}
|
||||
setTab={setTab}
|
||||
TAB_LIST={TAB_LIST}
|
||||
TAB_LIST={[
|
||||
t("statusPage.generalSettings", "General settings"),
|
||||
t("statusPage.contents", "Contents"),
|
||||
]}
|
||||
isCreate={isCreate}
|
||||
handleDelete={handleDelete}
|
||||
isDeleteOpen={isDeleteOpen}
|
||||
setIsDeleteOpen={setIsDeleteOpen}
|
||||
isDeleting={isDeleting}
|
||||
isLoading={statusPageIsLoading}
|
||||
/>
|
||||
<Stack
|
||||
direction="row"
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useLocation } from "react-router-dom";
|
||||
import PropTypes from "prop-types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const Controls = ({ isDeleteOpen, setIsDeleteOpen, isDeleting, url, type }) => {
|
||||
const Controls = ({ url, type }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
@@ -27,16 +27,6 @@ const Controls = ({ isDeleteOpen, setIsDeleteOpen, isDeleting, url, type }) => {
|
||||
direction="row"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => setIsDeleteOpen(!isDeleteOpen)}
|
||||
loading={isDeleting}
|
||||
>
|
||||
{t("delete")}
|
||||
</Button>
|
||||
</Box>
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -65,21 +55,10 @@ const Controls = ({ isDeleteOpen, setIsDeleteOpen, isDeleting, url, type }) => {
|
||||
|
||||
Controls.propTypes = {
|
||||
type: PropTypes.string,
|
||||
isDeleting: PropTypes.bool,
|
||||
url: PropTypes.string,
|
||||
isDeleteOpen: PropTypes.bool.isRequired,
|
||||
setIsDeleteOpen: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const ControlsHeader = ({
|
||||
statusPage,
|
||||
isPublic,
|
||||
isDeleting,
|
||||
isDeleteOpen,
|
||||
setIsDeleteOpen,
|
||||
url,
|
||||
type = "uptime",
|
||||
}) => {
|
||||
const ControlsHeader = ({ statusPage, isPublic, url, type = "uptime" }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const publicUrl = `/status/uptime/public/${url}`;
|
||||
@@ -137,9 +116,6 @@ const ControlsHeader = ({
|
||||
)}
|
||||
</Stack>
|
||||
<Controls
|
||||
isDeleting={isDeleting}
|
||||
isDeleteOpen={isDeleteOpen}
|
||||
setIsDeleteOpen={setIsDeleteOpen}
|
||||
url={url}
|
||||
type={type}
|
||||
/>
|
||||
@@ -152,9 +128,6 @@ ControlsHeader.propTypes = {
|
||||
url: PropTypes.string,
|
||||
statusPage: PropTypes.object,
|
||||
isPublic: PropTypes.bool,
|
||||
isDeleting: PropTypes.bool,
|
||||
isDeleteOpen: PropTypes.bool.isRequired,
|
||||
setIsDeleteOpen: PropTypes.func.isRequired,
|
||||
type: PropTypes.string,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,19 +1,35 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import { useState } from "react";
|
||||
import { networkService } from "../../../../main";
|
||||
import { networkService } from "../../../../Utils/NetworkService";
|
||||
import { createToast } from "../../../../Utils/toastUtils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
/**
|
||||
* Hook for deleting a status page with optimistic UI update
|
||||
* @param {Function} fetchStatusPage - Function to fetch status page data
|
||||
* @param {string} url - URL of the status page
|
||||
* @returns {Array} - [deleteStatusPage function, isLoading state]
|
||||
*/
|
||||
const useStatusPageDelete = (fetchStatusPage, url) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
/**
|
||||
* Delete a status page with optimistic UI update
|
||||
* @returns {Promise<boolean>} - Success status
|
||||
*/
|
||||
const deleteStatusPage = async () => {
|
||||
// We don't need to call fetchStatusPage after deletion
|
||||
// This prevents the 404 error when trying to fetch a deleted status page
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await networkService.deleteStatusPage({ url });
|
||||
fetchStatusPage?.();
|
||||
createToast({
|
||||
body: t("statusPage.deleteSuccess", "Status page deleted successfully"),
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
createToast({
|
||||
body: error.message,
|
||||
body: t("statusPage.deleteFailed", "Failed to delete status page"),
|
||||
});
|
||||
return false;
|
||||
} finally {
|
||||
|
||||
@@ -6,7 +6,6 @@ import ControlsHeader from "./Components/ControlsHeader";
|
||||
import SkeletonLayout from "./Components/Skeleton";
|
||||
import StatusBar from "./Components/StatusBar";
|
||||
import MonitorsList from "./Components/MonitorsList";
|
||||
import Dialog from "../../../Components/Dialog";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs/index.jsx";
|
||||
import TextLink from "../../../Components/TextLink";
|
||||
|
||||
@@ -15,27 +14,19 @@ import { useStatusPageFetch } from "./Hooks/useStatusPageFetch";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useStatusPageDelete } from "./Hooks/useStatusPageDelete";
|
||||
import { useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const PublicStatus = () => {
|
||||
const { url } = useParams();
|
||||
|
||||
// Local state
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
// Utils
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const isAdmin = useIsAdmin();
|
||||
|
||||
const [statusPage, monitors, isLoading, networkError, fetchStatusPage] =
|
||||
useStatusPageFetch(false, url);
|
||||
const [deleteStatusPage, isDeleting] = useStatusPageDelete(fetchStatusPage, url);
|
||||
const [statusPage, monitors, isLoading, networkError] = useStatusPageFetch(false, url);
|
||||
|
||||
// Breadcrumbs
|
||||
const crumbs = [
|
||||
@@ -158,9 +149,6 @@ const PublicStatus = () => {
|
||||
{!isPublic && <Breadcrumbs list={crumbs} />}
|
||||
<ControlsHeader
|
||||
statusPage={statusPage}
|
||||
isDeleting={isDeleting}
|
||||
isDeleteOpen={isDeleteOpen}
|
||||
setIsDeleteOpen={setIsDeleteOpen}
|
||||
url={url}
|
||||
isPublic={isPublic}
|
||||
/>
|
||||
@@ -168,21 +156,6 @@ const PublicStatus = () => {
|
||||
<StatusBar monitors={monitors} />
|
||||
<MonitorsList monitors={monitors} />
|
||||
{link}
|
||||
<Dialog
|
||||
title={t("deleteStatusPage")}
|
||||
onConfirm={() => {
|
||||
deleteStatusPage();
|
||||
setIsDeleteOpen(false);
|
||||
navigate("/status");
|
||||
}}
|
||||
onCancel={() => {
|
||||
setIsDeleteOpen(false);
|
||||
}}
|
||||
open={isDeleteOpen}
|
||||
confirmationButtonLabel={t("deleteStatusPageConfirm")}
|
||||
description={t("deleteStatusPageDescription")}
|
||||
isLoading={isDeleting || isLoading}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -802,6 +802,14 @@
|
||||
"statusPageStatusNoPage": "There's no status page here.",
|
||||
"statusPageStatusNotPublic": "This status page is not public.",
|
||||
"statusPageStatusServiceStatus": "Service status",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "Status page deleted successfully",
|
||||
"deleteFailed": "Failed to delete status page",
|
||||
"createSuccess": "Status page created successfully",
|
||||
"updateSuccess": "Status page updated successfully",
|
||||
"generalSettings": "General settings",
|
||||
"contents": "Contents"
|
||||
},
|
||||
"submit": "Submit",
|
||||
"teamPanel": {
|
||||
"cancel": "Cancel",
|
||||
|
||||
Reference in New Issue
Block a user