update usePatch, implement actions
This commit is contained in:
1 parent
aae1fefb31
commit
f9d0ab5e07
7 files changed
+181
-131
No files matched your search
@@ -15,11 +15,13 @@ export const ActionsMenu = ({ items }: { items: ActionMenuItem[] }) => {
|
||||
const [anchorEl, setAnchorEl] = useState<null | any>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<any>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
const handleClick = (e: React.MouseEvent<any>) => {
|
||||
e.stopPropagation();
|
||||
setAnchorEl(e.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
const handleClose = (e: React.MouseEvent<any>) => {
|
||||
e.stopPropagation();
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
@@ -36,8 +38,9 @@ export const ActionsMenu = ({ items }: { items: ActionMenuItem[] }) => {
|
||||
{items.map((item) => (
|
||||
<MenuItem
|
||||
key={item.id}
|
||||
onClick={() => {
|
||||
if (item.closeMenu) handleClose();
|
||||
onClick={(e: React.MouseEvent<HTMLLIElement>) => {
|
||||
e.stopPropagation();
|
||||
if (item.closeMenu) handleClose(e);
|
||||
item.action();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -28,12 +28,16 @@ export type Header<T> = {
|
||||
type DataTableProps<T extends { id?: string | number; _id?: string | number }> = {
|
||||
headers: Header<T>[];
|
||||
data: T[];
|
||||
onRowClick?: (row: T) => void;
|
||||
};
|
||||
|
||||
export function DataTable<T extends { id?: string | number; _id?: string | number }>({
|
||||
headers,
|
||||
data,
|
||||
}: DataTableProps<T>) {
|
||||
export function DataTable<
|
||||
T extends {
|
||||
id?: string | number;
|
||||
_id?: string | number;
|
||||
onRowClick?: (row: T) => void;
|
||||
},
|
||||
>({ headers, data, onRowClick }: DataTableProps<T>) {
|
||||
const theme = useTheme();
|
||||
if (data.length === 0 || headers.length === 0) return <div>No data</div>;
|
||||
return (
|
||||
@@ -77,7 +81,11 @@ export function DataTable<T extends { id?: string | number; _id?: string | numbe
|
||||
const key = row.id || row._id || Math.random();
|
||||
|
||||
return (
|
||||
<TableRow key={key}>
|
||||
<TableRow
|
||||
key={key}
|
||||
sx={{ cursor: onRowClick ? "pointer" : "default" }}
|
||||
onClick={() => (onRowClick ? onRowClick(row) : null)}
|
||||
>
|
||||
{headers.map((header, index) => {
|
||||
return (
|
||||
<TableCell
|
||||
|
||||
@@ -50,7 +50,7 @@ export const HeaderControls = ({
|
||||
<Button
|
||||
loading={isPatching}
|
||||
onClick={async () => {
|
||||
await patch();
|
||||
await patch(`/monitors/${monitor._id}/active`);
|
||||
refetch();
|
||||
}}
|
||||
startIcon={
|
||||
|
||||
@@ -58,11 +58,15 @@ export const usePost = <B = any, R = any>(endpoint: string) => {
|
||||
return { post: postFn, loading, error };
|
||||
};
|
||||
|
||||
export const usePatch = <B = any, R = any>(endpoint: string) => {
|
||||
export const usePatch = <B = any, R = any>() => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const patchFn = async (body: B, config?: AxiosRequestConfig): Promise<R | null> => {
|
||||
const patchFn = async (
|
||||
endpoint: string,
|
||||
body?: B,
|
||||
config?: AxiosRequestConfig
|
||||
): Promise<R | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
||||
@@ -52,11 +52,7 @@ const UptimeDetailsPage = () => {
|
||||
{}
|
||||
);
|
||||
|
||||
const {
|
||||
patch,
|
||||
loading: isPatching,
|
||||
error: postError,
|
||||
} = usePatch<ApiResponse>(`/monitors/${id}/active`);
|
||||
const { patch, loading: isPatching, error: postError } = usePatch<ApiResponse>();
|
||||
|
||||
const monitor: IMonitor = response?.data?.monitor;
|
||||
|
||||
|
||||
@@ -10,122 +10,133 @@ import { useTranslation } from "react-i18next";
|
||||
import { useMediaQuery } from "@mui/material";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { usePatch } from "@/Hooks/v2/UseApi";
|
||||
import type { ApiResponse } from "@/Hooks/v2/UseApi";
|
||||
|
||||
import type { IMonitor } from "@/Types/Monitor";
|
||||
import type { ActionMenuItem } from "@/Components/v2/ActionsMenu";
|
||||
const getActions = (
|
||||
theme: any,
|
||||
monitor: IMonitor,
|
||||
navigate: Function
|
||||
): ActionMenuItem[] => {
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
label: "Open site",
|
||||
action: () => {
|
||||
console.log("Open site");
|
||||
},
|
||||
closeMenu: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: "Details",
|
||||
action: () => {
|
||||
navigate(`${monitor._id}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
label: "Incidents",
|
||||
action: () => {
|
||||
console.log("Open incidents");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
label: "Configure",
|
||||
action: () => {
|
||||
console.log("Open configure");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
label: "Clone",
|
||||
action: () => {
|
||||
console.log("Open clone");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
label: "Pause",
|
||||
action: () => {
|
||||
console.log("Open pause");
|
||||
},
|
||||
closeMenu: true,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
label: <Typography color={theme.palette.error.main}>Remove</Typography>,
|
||||
action: () => {
|
||||
console.log("Open delete");
|
||||
},
|
||||
closeMenu: true,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const getHeaders = (theme: any, t: Function, navigate: Function) => {
|
||||
const headers: Header<IMonitor>[] = [
|
||||
{
|
||||
id: "name",
|
||||
content: t("host"),
|
||||
render: (row) => {
|
||||
return row.name;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
content: t("status"),
|
||||
render: (row) => {
|
||||
return <StatusLabel status={row.status} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "histogram",
|
||||
content: t("responseTime"),
|
||||
render: (row) => {
|
||||
return (
|
||||
<Stack alignItems={"center"}>
|
||||
<HistogramResponseTime checks={row.latestChecks} />
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "type",
|
||||
content: t("type"),
|
||||
render: (row) => {
|
||||
return row.type;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
content: t("actions"),
|
||||
render: (row) => {
|
||||
return <ActionsMenu items={getActions(theme, row, navigate)} />;
|
||||
},
|
||||
},
|
||||
];
|
||||
return headers;
|
||||
};
|
||||
|
||||
export const MonitorTable = ({ monitors }: { monitors: IMonitor[] }) => {
|
||||
export const MonitorTable = ({
|
||||
monitors,
|
||||
refetch,
|
||||
}: {
|
||||
monitors: IMonitor[];
|
||||
refetch: Function;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
patch,
|
||||
// loading: isPatching,
|
||||
// error: postError,
|
||||
} = usePatch<ApiResponse>();
|
||||
|
||||
let headers = getHeaders(theme, t, navigate);
|
||||
const getActions = (monitor: IMonitor): ActionMenuItem[] => {
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
label: "Open site",
|
||||
action: () => {
|
||||
window.open(monitor.url, "_blank", "noreferrer");
|
||||
},
|
||||
closeMenu: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: "Details",
|
||||
action: () => {
|
||||
navigate(`${monitor._id}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
label: "Incidents",
|
||||
action: () => {
|
||||
navigate(`/v2/incidents/${monitor._id}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
label: "Configure",
|
||||
action: () => {
|
||||
console.log("Open configure");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
label: "Clone",
|
||||
action: () => {
|
||||
console.log("Open clone");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
label: monitor.isActive ? "Pause" : "Resume",
|
||||
action: async () => {
|
||||
await patch(`/monitors/${monitor._id}/active`);
|
||||
refetch();
|
||||
},
|
||||
closeMenu: true,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
label: <Typography color={theme.palette.error.main}>Remove</Typography>,
|
||||
action: () => {
|
||||
console.log("Open delete");
|
||||
},
|
||||
closeMenu: true,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const getHeaders = () => {
|
||||
const headers: Header<IMonitor>[] = [
|
||||
{
|
||||
id: "name",
|
||||
content: t("host"),
|
||||
render: (row) => {
|
||||
return row.name;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
content: t("status"),
|
||||
render: (row) => {
|
||||
return <StatusLabel status={row.status} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "histogram",
|
||||
content: t("responseTime"),
|
||||
render: (row) => {
|
||||
return (
|
||||
<Stack alignItems={"center"}>
|
||||
<HistogramResponseTime checks={row.latestChecks} />
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "type",
|
||||
content: t("type"),
|
||||
render: (row) => {
|
||||
return row.type;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
content: t("actions"),
|
||||
render: (row) => {
|
||||
return <ActionsMenu items={getActions(row)} />;
|
||||
},
|
||||
},
|
||||
];
|
||||
return headers;
|
||||
};
|
||||
|
||||
let headers = getHeaders();
|
||||
|
||||
if (isSmall) {
|
||||
headers = headers.filter((h) => h.id !== "histogram");
|
||||
@@ -134,6 +145,9 @@ export const MonitorTable = ({ monitors }: { monitors: IMonitor[] }) => {
|
||||
<Table
|
||||
headers={headers}
|
||||
data={monitors}
|
||||
onRowClick={(row) => {
|
||||
navigate(row._id);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -18,8 +18,30 @@ const UptimeMonitors = () => {
|
||||
const theme = useTheme();
|
||||
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
||||
|
||||
const { response, loading } = useGet<ApiResponse>("/monitors?embedChecks=true", {});
|
||||
const monitors = response?.data ?? ([] as IMonitor[]);
|
||||
const { response, loading, refetch } = useGet<ApiResponse>(
|
||||
"/monitors?embedChecks=true",
|
||||
{},
|
||||
{ refreshInterval: 30000 }
|
||||
);
|
||||
const monitors: IMonitor[] = response?.data ?? ([] as IMonitor[]);
|
||||
|
||||
const monitorStatuses = monitors.reduce(
|
||||
(acc, monitor) => {
|
||||
if (monitor.status === "up") {
|
||||
acc.up += 1;
|
||||
} else if (monitor.status === "down") {
|
||||
acc.down += 1;
|
||||
} else if (monitor.isActive === false) {
|
||||
acc.paused += 1;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
up: 0,
|
||||
down: 0,
|
||||
paused: 0,
|
||||
}
|
||||
);
|
||||
|
||||
if (monitors.length === 0 && !loading) {
|
||||
return "No monitors found";
|
||||
@@ -35,11 +57,14 @@ const UptimeMonitors = () => {
|
||||
direction={isSmall ? "column" : "row"}
|
||||
gap={theme.spacing(8)}
|
||||
>
|
||||
<UpStatusBox n={1} />
|
||||
<DownStatusBox n={1} />
|
||||
<PausedStatusBox n={1} />
|
||||
<UpStatusBox n={monitorStatuses.up} />
|
||||
<DownStatusBox n={monitorStatuses.down} />
|
||||
<PausedStatusBox n={monitorStatuses.paused} />
|
||||
</Stack>
|
||||
<MonitorTable monitors={monitors} />
|
||||
<MonitorTable
|
||||
monitors={monitors}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</BasePage>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user