mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-20 08:28:48 -05:00
selects
This commit is contained in:
@@ -2,6 +2,8 @@ import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { ToggleButtonGroup, ToggleButton } from "@/Components/v2/inputs";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import CircularProgress from "@mui/material/CircularProgress";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
interface MonitorTimeFrameHeaderProps {
|
||||
isLoading?: boolean;
|
||||
@@ -72,6 +74,9 @@ export const HeaderTimeRange = ({
|
||||
alignItems="center"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Box sx={{ width: 20, height: 20, display: "flex", alignItems: "center", justifyContent: "center" }}>
|
||||
{isLoading && <CircularProgress size={16} />}
|
||||
</Box>
|
||||
<Typography variant="body2">
|
||||
Showing statistics for past{" "}
|
||||
{dateRange === "recent"
|
||||
|
||||
@@ -133,6 +133,20 @@ export const DownChecksBox = ({ n }: { n: number }) => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const UpChecksBox = ({ n }: { n: number }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<StatusBox
|
||||
label={t("pages.common.monitors.status.up")}
|
||||
n={n}
|
||||
color={theme.palette.success.light}
|
||||
sx={{
|
||||
maxWidth: "300px",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const InitializingStatusBox = ({ n }: { n: number }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -20,6 +20,7 @@ import { useGet } from "@/Hooks/UseApi";
|
||||
export const ChecksTable = ({
|
||||
monitors,
|
||||
selectedMonitorId,
|
||||
statusFilter,
|
||||
dateRange,
|
||||
page,
|
||||
setPage,
|
||||
@@ -28,6 +29,7 @@ export const ChecksTable = ({
|
||||
}: {
|
||||
monitors: Monitor[] | null;
|
||||
selectedMonitorId: string;
|
||||
statusFilter: string;
|
||||
dateRange: string;
|
||||
page: number;
|
||||
setPage: (page: number) => void;
|
||||
@@ -38,40 +40,36 @@ export const ChecksTable = ({
|
||||
const uiTimezone = useSelector((state: RootState) => state.ui.timezone);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Get selected monitor type
|
||||
const selectedMonitorType = monitors?.find((m) => m.id === selectedMonitorId)?.type;
|
||||
|
||||
// Team checks URL (when selectedMonitorId === "0")
|
||||
const teamChecksUrl = useMemo(() => {
|
||||
if (selectedMonitorId !== "0") return null;
|
||||
const params = new URLSearchParams();
|
||||
params.append("sortOrder", "desc");
|
||||
if (dateRange) params.append("dateRange", dateRange);
|
||||
if (statusFilter) params.append("filter", statusFilter);
|
||||
params.append("page", String(page));
|
||||
params.append("rowsPerPage", String(rowsPerPage));
|
||||
return `/checks/team?${params.toString()}`;
|
||||
}, [selectedMonitorId, dateRange, page, rowsPerPage]);
|
||||
}, [selectedMonitorId, dateRange, statusFilter, page, rowsPerPage]);
|
||||
|
||||
// Monitor checks URL (when specific monitor selected)
|
||||
const monitorChecksUrl = useMemo(() => {
|
||||
if (selectedMonitorId === "0" || !selectedMonitorType) return null;
|
||||
const params = new URLSearchParams();
|
||||
params.append("type", selectedMonitorType);
|
||||
params.append("sortOrder", "desc");
|
||||
params.append("status", "false");
|
||||
if (statusFilter) params.append("filter", statusFilter);
|
||||
if (dateRange) params.append("dateRange", dateRange);
|
||||
params.append("page", String(page));
|
||||
params.append("rowsPerPage", String(rowsPerPage));
|
||||
return `/checks/${selectedMonitorId}?${params.toString()}`;
|
||||
}, [selectedMonitorId, selectedMonitorType, dateRange, page, rowsPerPage]);
|
||||
}, [selectedMonitorId, selectedMonitorType, dateRange, statusFilter, page, rowsPerPage]);
|
||||
|
||||
// Fetch data - null key skips the request
|
||||
const { data: teamData, isLoading: isLoadingTeam } =
|
||||
useGet<ChecksResponse>(teamChecksUrl);
|
||||
const { data: monitorData, isLoading: isLoadingMonitor } =
|
||||
useGet<ChecksResponse>(monitorChecksUrl);
|
||||
|
||||
// Select correct data based on selection
|
||||
const checks =
|
||||
selectedMonitorId === "0" ? (teamData?.checks ?? []) : (monitorData?.checks ?? []);
|
||||
const checksCount =
|
||||
@@ -101,7 +99,7 @@ export const ChecksTable = ({
|
||||
},
|
||||
{
|
||||
id: "date",
|
||||
content: t("checks.table.headers.dateTime"),
|
||||
content: t("common.table.headers.dateTime"),
|
||||
render: (row) => {
|
||||
return formatDateWithTz(
|
||||
row.createdAt,
|
||||
@@ -112,7 +110,7 @@ export const ChecksTable = ({
|
||||
},
|
||||
{
|
||||
id: "statusCode",
|
||||
content: t("checks.table.headers.statusCode"),
|
||||
content: t("pages.checks.table.headers.statusCode"),
|
||||
render: (row) => {
|
||||
const code = row.statusCode;
|
||||
if (!code) return "N/A";
|
||||
@@ -161,7 +159,7 @@ export const ChecksTable = ({
|
||||
onRowClick={(row) => {
|
||||
navigate(`/checks/${row.id}`);
|
||||
}}
|
||||
emptyViewText={t("checks.table.empty")}
|
||||
emptyViewText={t("pages.checks.table.empty")}
|
||||
/>
|
||||
<Pagination
|
||||
component="div"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { BasePage, TotalChecksBox, DownChecksBox } from "@/Components/v2/design-elements";
|
||||
import {
|
||||
BasePage,
|
||||
TotalChecksBox,
|
||||
UpChecksBox,
|
||||
DownChecksBox,
|
||||
} from "@/Components/v2/design-elements";
|
||||
import { HeaderTimeRange } from "@/Components/v2/common";
|
||||
import { Select } from "@/Components/v2/inputs";
|
||||
import { ChecksTable } from "./Components/ChecksTable";
|
||||
@@ -14,12 +19,12 @@ import type { ChecksSummary } from "@/Types/Check";
|
||||
|
||||
const Checks = () => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { monitorId } = useParams<{ monitorId?: string }>();
|
||||
|
||||
// Local state
|
||||
const [selectedMonitor, setSelectedMonitor] = useState<string>(monitorId || "0");
|
||||
const [dateRange, setDateRange] = useState<string>("recent");
|
||||
const [statusFilter, setStatusFilter] = useState<string>("down");
|
||||
const [page, setPage] = useState<number>(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||
|
||||
@@ -27,17 +32,16 @@ const Checks = () => {
|
||||
const monitorsUrl = "/monitors/team";
|
||||
const summaryUrl = `/checks/team/summary?dateRange=${dateRange}`;
|
||||
|
||||
const {
|
||||
data: monitorsResponse,
|
||||
isLoading: isLoadingMonitors,
|
||||
error: monitorsError,
|
||||
} = useGet<Monitor[]>(monitorsUrl);
|
||||
const { data: monitorsResponse, isLoading: isLoadingMonitors } =
|
||||
useGet<Monitor[]>(monitorsUrl);
|
||||
|
||||
const {
|
||||
data: summaryResponse,
|
||||
isLoading: isLoadingSummary,
|
||||
error: summaryError,
|
||||
} = useGet<ChecksSummary>(summaryUrl);
|
||||
const { data: summaryResponse, isLoading: isLoadingSummary } =
|
||||
useGet<ChecksSummary>(summaryUrl);
|
||||
|
||||
const isLoading = isLoadingMonitors || isLoadingSummary;
|
||||
const totalChecks = summaryResponse?.totalChecks || 0;
|
||||
const downChecks = summaryResponse?.downChecks || 0;
|
||||
const upChecks = totalChecks - (summaryResponse?.downChecks || 0);
|
||||
|
||||
return (
|
||||
<BasePage>
|
||||
@@ -45,32 +49,52 @@ const Checks = () => {
|
||||
direction={{ xs: "column", md: "row" }}
|
||||
gap={4}
|
||||
>
|
||||
<TotalChecksBox n={summaryResponse?.totalChecks || 0} />
|
||||
<DownChecksBox n={summaryResponse?.downChecks || 0} />
|
||||
<TotalChecksBox n={totalChecks} />
|
||||
<UpChecksBox n={upChecks} />
|
||||
<DownChecksBox n={downChecks || 0} />
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction={{ xs: "column", md: "row" }}
|
||||
justifyContent={"space-between"}
|
||||
alignItems={"center"}
|
||||
gap={2}
|
||||
>
|
||||
<Select
|
||||
value={selectedMonitor}
|
||||
onChange={(e: any) => {
|
||||
setSelectedMonitor(e.target.value);
|
||||
}}
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={2}
|
||||
>
|
||||
<MenuItem value="0">All monitors</MenuItem>
|
||||
{monitorsResponse?.map((monitor) => (
|
||||
<MenuItem
|
||||
key={monitor.id}
|
||||
value={monitor.id}
|
||||
>
|
||||
{monitor.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<Select
|
||||
value={selectedMonitor}
|
||||
onChange={(e: any) => {
|
||||
setSelectedMonitor(e.target.value);
|
||||
setPage(0);
|
||||
}}
|
||||
>
|
||||
<MenuItem value="0">{t("pages.checks.selects.monitor.all")}</MenuItem>
|
||||
{monitorsResponse?.map((monitor) => (
|
||||
<MenuItem
|
||||
key={monitor.id}
|
||||
value={monitor.id}
|
||||
>
|
||||
{monitor.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<Select
|
||||
value={statusFilter}
|
||||
onChange={(e: any) => {
|
||||
setStatusFilter(e.target.value);
|
||||
setPage(0);
|
||||
}}
|
||||
>
|
||||
<MenuItem value="all">{t("pages.checks.selects.status.all")}</MenuItem>
|
||||
<MenuItem value="up">{t("pages.checks.selects.status.up")}</MenuItem>
|
||||
<MenuItem value="down">{t("pages.checks.selects.status.down")}</MenuItem>
|
||||
</Select>
|
||||
</Stack>
|
||||
<HeaderTimeRange
|
||||
isLoading={isLoading}
|
||||
dateRange={dateRange}
|
||||
setDateRange={setDateRange}
|
||||
/>
|
||||
@@ -79,6 +103,7 @@ const Checks = () => {
|
||||
<ChecksTable
|
||||
monitors={monitorsResponse ?? null}
|
||||
selectedMonitorId={selectedMonitor}
|
||||
statusFilter={statusFilter}
|
||||
dateRange={dateRange}
|
||||
page={page}
|
||||
setPage={setPage}
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
"monitor": "Monitor",
|
||||
"name": "Name",
|
||||
"status": "Status",
|
||||
"type": "Type"
|
||||
"type": "Type",
|
||||
"dateTime": "Date & time"
|
||||
},
|
||||
"empty": "Nothing here"
|
||||
},
|
||||
@@ -177,6 +178,7 @@
|
||||
"up": "up",
|
||||
"down": "down",
|
||||
"paused": "paused",
|
||||
"total": "total",
|
||||
"initializing": "initializing"
|
||||
},
|
||||
"actions": {
|
||||
@@ -223,6 +225,17 @@
|
||||
"headers": {
|
||||
"dateTime": "Date & time",
|
||||
"statusCode": "Status code"
|
||||
},
|
||||
"empty": "No down checks in this time range"
|
||||
},
|
||||
"selects": {
|
||||
"monitor": {
|
||||
"all": "All monitors"
|
||||
},
|
||||
"status": {
|
||||
"all": "All",
|
||||
"down": "Down",
|
||||
"up": "Up"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -221,9 +221,14 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "up":
|
||||
matchStage.status = true;
|
||||
break;
|
||||
case "down":
|
||||
matchStage.status = false;
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.status = false;
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
@@ -271,7 +276,6 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
findByTeamId = async (sortOrder: string, dateRange: string, filter: string, page: number, rowsPerPage: number, teamId: string) => {
|
||||
const matchStage: Record<string, any> = {
|
||||
"metadata.teamId": new mongoose.Types.ObjectId(teamId),
|
||||
status: false,
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
createdAt: {
|
||||
$gte: dateRangeLookup[dateRange],
|
||||
@@ -283,9 +287,14 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "up":
|
||||
matchStage.status = true;
|
||||
break;
|
||||
case "down":
|
||||
matchStage.status = false;
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.status = false;
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -111,7 +111,7 @@ const getMonitorByIdQueryValidation = joi.object({
|
||||
status: joi.boolean(),
|
||||
sortOrder: joi.string().valid("asc", "desc"),
|
||||
limit: joi.number(),
|
||||
dateRange: joi.string().valid("hour", "day", "week", "month", "all"),
|
||||
dateRange: joi.string().valid("recent", "hour", "day", "week", "month", "all"),
|
||||
numToDisplay: joi.number(),
|
||||
normalize: joi.boolean(),
|
||||
});
|
||||
@@ -307,7 +307,7 @@ const getChecksQueryValidation = joi.object({
|
||||
sortOrder: joi.string().valid("asc", "desc"),
|
||||
limit: joi.number(),
|
||||
dateRange: joi.string().valid("recent", "hour", "day", "week", "month", "all"),
|
||||
filter: joi.string().valid("all", "down", "resolve"),
|
||||
filter: joi.string().valid("all", "up", "down", "resolve"),
|
||||
ack: joi.boolean(),
|
||||
page: joi.number(),
|
||||
rowsPerPage: joi.number(),
|
||||
@@ -317,8 +317,8 @@ const getChecksQueryValidation = joi.object({
|
||||
const getTeamChecksQueryValidation = joi.object({
|
||||
sortOrder: joi.string().valid("asc", "desc"),
|
||||
limit: joi.number(),
|
||||
dateRange: joi.string().valid("hour", "day", "week", "month", "all"),
|
||||
filter: joi.string().valid("all", "down", "resolve"),
|
||||
dateRange: joi.string().valid("recent", "hour", "day", "week", "month", "all"),
|
||||
filter: joi.string().valid("all", "up", "down", "resolve"),
|
||||
ack: joi.boolean(),
|
||||
page: joi.number(),
|
||||
rowsPerPage: joi.number(),
|
||||
|
||||
Reference in New Issue
Block a user