mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-19 16:19:45 -06:00
@@ -148,7 +148,7 @@
|
||||
height: 22px;
|
||||
}
|
||||
.MuiTablePagination-root svg path {
|
||||
stroke: var(--env-var-color-5);
|
||||
stroke: var(--env-var-color-2);
|
||||
stroke-width: 1.3;
|
||||
}
|
||||
.MuiTablePagination-root .MuiSelect-icon {
|
||||
@@ -175,7 +175,8 @@
|
||||
padding: 4px;
|
||||
min-width: 100px;
|
||||
}
|
||||
.pagination-dropdown li {
|
||||
.pagination-dropdown li,
|
||||
body:has(.pagination-dropdown) p:has(+ .MuiTablePagination-root) {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-2);
|
||||
padding: 4px;
|
||||
@@ -185,3 +186,6 @@
|
||||
.pagination-dropdown li.Mui-selected:hover {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
body:has(.pagination-dropdown) p:has(+ .MuiTablePagination-root) {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ import {
|
||||
Typography,
|
||||
Stack,
|
||||
} from "@mui/material";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useLocation, useParams } from "react-router-dom";
|
||||
import { setRowsPerPage } from "../../Features/UI/uiSlice";
|
||||
import LeftArrowDouble from "../../assets/icons/left-arrow-double.svg?react";
|
||||
import RightArrowDouble from "../../assets/icons/right-arrow-double.svg?react";
|
||||
import LeftArrow from "../../assets/icons/left-arrow.svg?react";
|
||||
@@ -140,10 +143,14 @@ const TablePaginationActions = (props) => {
|
||||
* <BasicTable data={data} rows={rows} paginated={true} />
|
||||
*/
|
||||
|
||||
const BasicTable = ({ data, paginated, reversed, rows = 5 }) => {
|
||||
const BasicTable = ({ data, paginated, reversed }) => {
|
||||
const theme = useTheme();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const uiState = useSelector((state) => state.ui);
|
||||
let table = location.pathname.split("/").pop();
|
||||
let rowsPerPage = uiState[table].rowsPerPage;
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(rows);
|
||||
|
||||
useEffect(() => {
|
||||
setPage(0);
|
||||
@@ -154,7 +161,12 @@ const BasicTable = ({ data, paginated, reversed, rows = 5 }) => {
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (event) => {
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
dispatch(
|
||||
setRowsPerPage({
|
||||
value: parseInt(event.target.value, 10),
|
||||
table: table,
|
||||
})
|
||||
);
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
@@ -211,50 +223,59 @@ const BasicTable = ({ data, paginated, reversed, rows = 5 }) => {
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between">
|
||||
<Typography sx={{ opacity: 0.7 }}>
|
||||
Showing {getRange()} of {data.rows.length} monitor(s)
|
||||
</Typography>
|
||||
<TablePagination
|
||||
component="div"
|
||||
count={data.rows.length}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
rowsPerPage={rowsPerPage}
|
||||
rowsPerPageOptions={[5, 10, 15, 25]}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
ActionsComponent={TablePaginationActions}
|
||||
labelRowsPerPage="Rows per page"
|
||||
labelDisplayedRows={({ page, count }) =>
|
||||
`Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}`
|
||||
}
|
||||
slotProps={{
|
||||
select: {
|
||||
MenuProps: {
|
||||
keepMounted: true,
|
||||
PaperProps: {
|
||||
className: "pagination-dropdown",
|
||||
{paginated && (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Typography sx={{ opacity: 0.7 }}>
|
||||
Showing {getRange()} of {data.rows.length} monitor(s)
|
||||
</Typography>
|
||||
<TablePagination
|
||||
component="div"
|
||||
count={data.rows.length}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
rowsPerPage={rowsPerPage}
|
||||
rowsPerPageOptions={[5, 10, 15, 25]}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
ActionsComponent={TablePaginationActions}
|
||||
labelRowsPerPage="Rows per page"
|
||||
labelDisplayedRows={({ page, count }) =>
|
||||
`Page ${page + 1} of ${Math.max(
|
||||
0,
|
||||
Math.ceil(count / rowsPerPage)
|
||||
)}`
|
||||
}
|
||||
slotProps={{
|
||||
select: {
|
||||
MenuProps: {
|
||||
keepMounted: true,
|
||||
PaperProps: {
|
||||
className: "pagination-dropdown",
|
||||
},
|
||||
transformOrigin: { vertical: "bottom", horizontal: "left" },
|
||||
anchorOrigin: { vertical: "top", horizontal: "left" },
|
||||
sx: { mt: "-4px" },
|
||||
},
|
||||
transformOrigin: { vertical: "bottom", horizontal: "left" },
|
||||
anchorOrigin: { vertical: "top", horizontal: "left" },
|
||||
sx: { mt: "-4px" },
|
||||
},
|
||||
inputProps: { id: "pagination-dropdown" },
|
||||
IconComponent: SelectorVertical,
|
||||
sx: {
|
||||
ml: theme.gap.small,
|
||||
mr: theme.gap.large,
|
||||
minWidth: theme.gap.xl,
|
||||
textAlign: "left",
|
||||
"&.Mui-focused > div": {
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
inputProps: { id: "pagination-dropdown" },
|
||||
IconComponent: SelectorVertical,
|
||||
sx: {
|
||||
ml: theme.gap.small,
|
||||
mr: theme.gap.large,
|
||||
minWidth: theme.gap.xl,
|
||||
textAlign: "left",
|
||||
"&.Mui-focused > div": {
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
sx={{ mt: theme.gap.medium }}
|
||||
/>
|
||||
</Stack>
|
||||
}}
|
||||
sx={{ mt: theme.gap.medium }}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -58,7 +58,7 @@ aside
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-left: solid 1px var(--env-var-color-5);
|
||||
opacity: 0.3;
|
||||
opacity: 0.2;
|
||||
}
|
||||
aside
|
||||
.MuiCollapse-wrapperInner
|
||||
@@ -75,13 +75,13 @@ aside .MuiList-root .MuiListItemText-root + svg {
|
||||
aside .MuiCollapse-wrapperInner .MuiList-root > .MuiListItemButton-root::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
left: -8px;
|
||||
top: 50%;
|
||||
height: 1px;
|
||||
width: 6px;
|
||||
transform: translateY(-50%);
|
||||
height: 3px;
|
||||
width: 3px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--env-var-color-5);
|
||||
opacity: 0.3;
|
||||
background-color: #d6d9dd;
|
||||
}
|
||||
|
||||
aside {
|
||||
|
||||
@@ -19,6 +19,7 @@ import { useLocation, useNavigate } from "react-router";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { clearAuthState } from "../../Features/Auth/authSlice";
|
||||
import { toggleSidebar } from "../../Features/UI/uiSlice";
|
||||
import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import Avatar from "../Avatar";
|
||||
import LockSvg from "../../assets/icons/lock.svg?react";
|
||||
@@ -86,8 +87,9 @@ function Sidebar() {
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const authState = useSelector((state) => state.auth);
|
||||
const { sidebar } = useSelector((state) => state.ui);
|
||||
let collapsed = sidebar.collapsed;
|
||||
const [open, setOpen] = useState({ Dashboard: false, Account: false });
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [popup, setPopup] = useState();
|
||||
|
||||
@@ -157,7 +159,7 @@ function Sidebar() {
|
||||
borderColor: theme.palette.otherColors.graishWhite,
|
||||
},
|
||||
}}
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
onClick={() => dispatch(toggleSidebar())}
|
||||
>
|
||||
{collapsed ? <ArrowRight /> : <ArrowLeft />}
|
||||
</IconButton>
|
||||
@@ -200,6 +202,7 @@ function Sidebar() {
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<ListItemButton
|
||||
className={
|
||||
@@ -233,6 +236,7 @@ function Sidebar() {
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<ListItemButton
|
||||
className={
|
||||
@@ -396,6 +400,7 @@ function Sidebar() {
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<ListItemButton
|
||||
className={
|
||||
@@ -450,6 +455,7 @@ function Sidebar() {
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<IconButton
|
||||
onClick={(event) => openPopup(event, "logout")}
|
||||
|
||||
@@ -322,13 +322,13 @@ const ProfilePanel = () => {
|
||||
<Divider aria-hidden="true" />
|
||||
<form className="delete-profile-form" noValidate spellCheck="false">
|
||||
<div className="delete-profile-form__wrapper">
|
||||
<Stack direction="column" gap="15px">
|
||||
<Stack direction="column" gap="8px">
|
||||
<Typography component="h1">Delete account</Typography>
|
||||
<Typography component="p">
|
||||
Note that deleting your account will remove all data from our
|
||||
system. This is permanent and non-recoverable.
|
||||
</Typography>
|
||||
<Box sx={{ mt: theme.spacing(1) }}>
|
||||
<Box sx={{ mt: theme.spacing(2) }}>
|
||||
<Button
|
||||
level="error"
|
||||
label="Delete account"
|
||||
@@ -399,7 +399,7 @@ const ProfilePanel = () => {
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 400,
|
||||
width: 450,
|
||||
bgcolor: "white",
|
||||
border: "solid 1px #f2f2f2",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
|
||||
31
Client/src/Features/UI/uiSlice.js
Normal file
31
Client/src/Features/UI/uiSlice.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
// Initial state for UI settings.
|
||||
// Add more settings as needed (e.g., theme preferences, user settings)
|
||||
const initialState = {
|
||||
monitors: {
|
||||
rowsPerPage: 5,
|
||||
},
|
||||
team: {
|
||||
rowsPerPage: 5,
|
||||
},
|
||||
sidebar: {
|
||||
collapsed: false,
|
||||
},
|
||||
};
|
||||
|
||||
const uiSlice = createSlice({
|
||||
name: "ui",
|
||||
initialState,
|
||||
reducers: {
|
||||
setRowsPerPage: (state, action) => {
|
||||
state[action.payload.table].rowsPerPage = action.payload.value;
|
||||
},
|
||||
toggleSidebar: (state) => {
|
||||
state.sidebar.collapsed = !state.sidebar.collapsed;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default uiSlice.reducer;
|
||||
export const { setRowsPerPage, toggleSidebar } = uiSlice.actions;
|
||||
@@ -1,14 +1,13 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useSelector } from "react-redux";
|
||||
import { Box, Tab, useTheme } from "@mui/material";
|
||||
import TabContext from "@mui/lab/TabContext";
|
||||
import TabList from "@mui/lab/TabList";
|
||||
import "./index.css";
|
||||
import ProfilePanel from "../../Components/TabPanels/Account/ProfilePanel";
|
||||
import PasswordPanel from "../../Components/TabPanels/Account/PasswordPanel";
|
||||
import TeamPanel from "../../Components/TabPanels/Account/TeamPanel";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useSelector } from "react-redux";
|
||||
import "./index.css";
|
||||
|
||||
/**
|
||||
* Account component renders a settings page with tabs for Profile, Password, and Team settings.
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
}
|
||||
.monitors p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.monitors p.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.monitors .MuiStack-root > button:not(.MuiIconButton-root) {
|
||||
|
||||
@@ -376,7 +376,7 @@ const Monitors = () => {
|
||||
let loading = monitorState.isLoading && monitorState.monitors.length === 0;
|
||||
|
||||
return (
|
||||
<Stack className="monitors" pt={theme.gap.xl} gap={theme.gap.large}>
|
||||
<Stack className="monitors" gap={theme.gap.large}>
|
||||
{loading ? (
|
||||
<SkeletonLayout />
|
||||
) : (
|
||||
@@ -395,10 +395,11 @@ const Monitors = () => {
|
||||
{monitorState.monitors?.length !== 0 && (
|
||||
<Button
|
||||
level="primary"
|
||||
label="Create new monitor"
|
||||
label="Create monitor"
|
||||
onClick={() => {
|
||||
navigate("/monitors/create");
|
||||
}}
|
||||
sx={{ fontWeight: 500 }}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { configureStore, combineReducers } from "@reduxjs/toolkit";
|
||||
import uptimeMonitorsReducer from "./Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import pageSpeedMonitorReducer from "./Features/PageSpeedMonitor/pageSpeedMonitorSlice";
|
||||
import authReducer from "./Features/Auth/authSlice";
|
||||
import uiReducer from "./Features/UI/uiSlice";
|
||||
import storage from "redux-persist/lib/storage";
|
||||
import { persistReducer, persistStore, createTransform } from "redux-persist";
|
||||
|
||||
@@ -20,7 +21,7 @@ const authTransform = createTransform(
|
||||
const persistConfig = {
|
||||
key: "root",
|
||||
storage,
|
||||
whitielist: ["auth", "monitors", "pageSpeed"],
|
||||
whitielist: ["auth", "monitors", "pageSpeed", "ui"],
|
||||
transforms: [authTransform],
|
||||
};
|
||||
|
||||
@@ -28,6 +29,7 @@ const rootReducer = combineReducers({
|
||||
uptimeMonitors: uptimeMonitorsReducer,
|
||||
auth: authReducer,
|
||||
pageSpeedMonitors: pageSpeedMonitorReducer,
|
||||
ui: uiReducer,
|
||||
});
|
||||
|
||||
const persistedReducer = persistReducer(persistConfig, rootReducer);
|
||||
|
||||
Reference in New Issue
Block a user