Resolved conflicts
This commit is contained in:
commit
905c7014d5
19 files changed
+293
-30
No files matched your search
@@ -24,6 +24,7 @@ import withAdminCheck from "./HOC/withAdminCheck";
|
||||
import Configure from "./Pages/Monitors/Configure";
|
||||
import PageSpeed from "./Pages/PageSpeed";
|
||||
import CreatePageSpeed from "./Pages/PageSpeed/CreatePageSpeed";
|
||||
import CreateNewMaintenanceWindow from "./Pages/Maintenance/CreateMaintenanceWindow";
|
||||
import PageSpeedDetails from "./Pages/PageSpeed/Details";
|
||||
|
||||
function App() {
|
||||
@@ -70,6 +71,10 @@ function App() {
|
||||
path="maintenance"
|
||||
element={<ProtectedRoute Component={Maintenance} />}
|
||||
/>
|
||||
<Route
|
||||
path="/maintenance/create"
|
||||
element={<CreateNewMaintenanceWindow />}
|
||||
/>
|
||||
<Route
|
||||
path="settings"
|
||||
element={<ProtectedRoute Component={Settings} />}
|
||||
|
||||
@@ -135,7 +135,9 @@ const BasicTable = ({ data, paginated, reversed, rowsPerPage = 5 }) => {
|
||||
{displayData.map((row) => {
|
||||
return (
|
||||
<TableRow
|
||||
sx={{ cursor: row.handleClick ? "pointer" : "default" }}
|
||||
sx={{
|
||||
cursor: row.handleClick ? "pointer" : "default",
|
||||
}}
|
||||
key={row.id}
|
||||
onClick={row.handleClick ? row.handleClick : null}
|
||||
>
|
||||
|
||||
@@ -8,7 +8,12 @@ const ResponseTimeChart = ({ checks = [] }) => {
|
||||
return (
|
||||
<div className="chart-container">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart width={150} height={40} data={normalizedChecks}>
|
||||
<BarChart
|
||||
width={150}
|
||||
height={40}
|
||||
data={normalizedChecks}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
<Bar maxBarSize={10} dataKey="responseTime">
|
||||
{normalizedChecks.map((check, index) => (
|
||||
<Cell
|
||||
|
||||
@@ -61,7 +61,7 @@ export const update = createAsyncThunk(
|
||||
form.deleteProfileImage &&
|
||||
fd.append("deleteProfileImage", form.deleteProfileImage);
|
||||
|
||||
const res = await axiosInstance.post(`/auth/user/${user._id}`, fd, {
|
||||
const res = await axiosInstance.put(`/auth/user/${user._id}`, fd, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "multipart/form-data",
|
||||
@@ -185,9 +185,7 @@ const handleForgotRejected = (state, action) => {
|
||||
const handleNewPasswordRejected = (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = false;
|
||||
state.msg = action.payload
|
||||
? action.payload.msg
|
||||
: "Failed to reset password.";
|
||||
state.msg = action.payload ? action.payload.msg : "Failed to reset password.";
|
||||
};
|
||||
|
||||
const authSlice = createSlice({
|
||||
|
||||
@@ -77,9 +77,9 @@ export const updateUptimeMonitor = createAsyncThunk(
|
||||
name: monitor.name,
|
||||
description: monitor.description,
|
||||
interval: monitor.interval,
|
||||
notifications: monitor.notifications
|
||||
notifications: monitor.notifications,
|
||||
};
|
||||
const res = await axiosInstance.post(
|
||||
const res = await axiosInstance.put(
|
||||
`/monitors/edit/${monitor._id}`,
|
||||
updatedFields,
|
||||
{
|
||||
@@ -104,16 +104,12 @@ export const deleteUptimeMonitor = createAsyncThunk(
|
||||
async (data, thunkApi) => {
|
||||
try {
|
||||
const { authToken, monitor } = data;
|
||||
const res = await axiosInstance.post(
|
||||
`/monitors/delete/${monitor._id}`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
const res = await axiosInstance.delete(`/monitors/${monitor._id}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
.maintenance-options > .MuiStack-root > .MuiStack-root.select-wrapper {
|
||||
width: 380px;
|
||||
}
|
||||
|
||||
.date-picker .MuiOutlinedInput-root,
|
||||
.time-picker .MuiOutlinedInput-root {
|
||||
height: 34px;
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
|
||||
.date-picker .MuiOutlinedInput-root {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.time-picker .MuiOutlinedInput-root {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.MuiInputAdornment-outlined .MuiIconButton-root svg {
|
||||
width: 20px;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import "./index.css";
|
||||
import React, { useState } from "react";
|
||||
import Button from "../../../Components/Button";
|
||||
import Back from "../../../assets/icons/left-arrow.svg?react";
|
||||
import Select from "../../../Components/Inputs/Select";
|
||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
|
||||
import dayjs from "dayjs";
|
||||
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
|
||||
import { MobileTimePicker } from "@mui/x-date-pickers/MobileTimePicker";
|
||||
|
||||
const directory = {
|
||||
title: "Create a maintenance window",
|
||||
description: "Your pings won’t be sent in this time frame.",
|
||||
};
|
||||
|
||||
const repeatOptions = [
|
||||
{ _id: 1, name: "Don't repeat" },
|
||||
{ _id: 2, name: "Repeat daily" },
|
||||
{ _id: 3, name: "Repeat weekly" },
|
||||
];
|
||||
|
||||
const configOptionTitle = (title, description) => {
|
||||
return (
|
||||
<Stack width="40%" gap={1}>
|
||||
<Typography
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "var(--env-var-font-size-medium)",
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
{description && (
|
||||
<Typography
|
||||
style={{
|
||||
fontSize: "var(--env-var-font-size-small)",
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const CreateNewMaintenanceWindow = () => {
|
||||
const [values, setValues] = useState({
|
||||
repeat: 1,
|
||||
});
|
||||
|
||||
const handleChange = (event, name) => {
|
||||
const { value } = event.target;
|
||||
setValues((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const configOptions = [
|
||||
{
|
||||
title: "Repeat",
|
||||
component: (
|
||||
<Select
|
||||
onChange={(e) => handleChange(e, "repeat")}
|
||||
id="repeat-mode"
|
||||
items={repeatOptions}
|
||||
value={values.repeat}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Date",
|
||||
component: (
|
||||
<LocalizationProvider
|
||||
className="date-localization-provider"
|
||||
dateAdapter={AdapterDayjs}
|
||||
>
|
||||
<DatePicker className="date-picker" defaultValue={dayjs()} />
|
||||
</LocalizationProvider>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Start time",
|
||||
description: "Your pings won’t be sent in this time frame.",
|
||||
component: (
|
||||
<LocalizationProvider
|
||||
className="time-localization-provider"
|
||||
dateAdapter={AdapterDayjs}
|
||||
>
|
||||
<MobileTimePicker className="time-picker" defaultValue={dayjs()} />
|
||||
</LocalizationProvider>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="create-maintenance-window">
|
||||
<Stack gap={3}>
|
||||
<Button
|
||||
id="btn-back"
|
||||
sx={{
|
||||
width: "100px",
|
||||
height: "30px",
|
||||
gap: "10px",
|
||||
backgroundColor: "var(--env-var-color-32)",
|
||||
color: "var(--env-var-color-5)",
|
||||
}}
|
||||
label="Back"
|
||||
level="tertiary"
|
||||
img={<Back />}
|
||||
/>
|
||||
<Box>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "var(--env-var-font-size-large)",
|
||||
fontWeight: "600",
|
||||
color: "var(--env-var-color-5)",
|
||||
}}
|
||||
>
|
||||
{directory.title}
|
||||
</Typography>
|
||||
|
||||
<Typography sx={{ fontSize: "var(--env-var-font-size-medium)" }}>
|
||||
{directory.description}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack
|
||||
gap={5}
|
||||
paddingY={4}
|
||||
paddingX={8}
|
||||
sx={{
|
||||
border: "1px solid var(--env-var-color-16)",
|
||||
borderRadius: "var(--env-var-radius-1)",
|
||||
}}
|
||||
className="maintenance-options"
|
||||
>
|
||||
{configOptions.map((item, index) => (
|
||||
<Stack key={index} display="-webkit-inline-box">
|
||||
{configOptionTitle(item.title, item.description)}
|
||||
{item.component && item.component}
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateNewMaintenanceWindow;
|
||||
@@ -14,6 +14,7 @@ const Maintenance = () => {
|
||||
"Eliminate any misunderstandings",
|
||||
"Stop sending alerts in maintenance windows",
|
||||
]}
|
||||
link="/maintenance/create"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,14 @@ import {
|
||||
const Host = ({ params }) => {
|
||||
return (
|
||||
<Stack direction="row" alignItems="center" className="host">
|
||||
<a href={params.url} target="_blank" rel="noreferrer">
|
||||
<a
|
||||
href={params.url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<OpenInNewPage />
|
||||
</a>
|
||||
<Box>
|
||||
@@ -64,6 +71,7 @@ const Monitors = () => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [actions, setActions] = useState({});
|
||||
const openMenu = (event, id, url) => {
|
||||
event.preventDefault();
|
||||
setAnchorEl(event.currentTarget);
|
||||
setActions({ id: id, url: url });
|
||||
};
|
||||
@@ -141,7 +149,7 @@ const Monitors = () => {
|
||||
return {
|
||||
id: monitor._id,
|
||||
// disabled for now
|
||||
// handleClick: () => navigate(`/monitors/${monitor._id}`),
|
||||
handleClick: () => navigate(`/monitors/${monitor._id}`),
|
||||
data: [
|
||||
{ id: idx, data: <Host params={params} /> },
|
||||
{
|
||||
@@ -167,7 +175,10 @@ const Monitors = () => {
|
||||
<>
|
||||
<IconButton
|
||||
aria-label="monitor actions"
|
||||
onClick={(event) => openMenu(event, monitor._id, monitor.url)}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
openMenu(event, monitor._id, monitor.url);
|
||||
}}
|
||||
sx={{
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
@@ -231,7 +242,9 @@ const Monitors = () => {
|
||||
onClose={closeMenu}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => window.open(actions.url, "_blank", "noreferrer")}
|
||||
onClick={() => {
|
||||
window.open(actions.url, "_blank", "noreferrer");
|
||||
}}
|
||||
>
|
||||
Open site
|
||||
</MenuItem>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17 7H1M1 7L7 13M1 7L7 1" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 223 B |
@@ -50,6 +50,8 @@
|
||||
--env-var-color-28: #f79009;
|
||||
--env-var-color-29: #d0d5dd;
|
||||
--env-var-color-30: #fcfcfd;
|
||||
--env-var-color-31: #1a1919;
|
||||
--env-var-color-32: #f5f5f5;
|
||||
|
||||
--env-var-radius-1: 4px;
|
||||
--env-var-radius-2: 8px;
|
||||
|
||||
@@ -439,7 +439,9 @@ const deleteUserController = async (req, res, next) => {
|
||||
const decodedToken = jwt.decode(token);
|
||||
const { _id, email } = decodedToken;
|
||||
|
||||
// TODO fix this hack
|
||||
const decodedTokenCastedAsRequest = {
|
||||
query: {},
|
||||
params: {
|
||||
userId: _id,
|
||||
},
|
||||
@@ -480,6 +482,7 @@ const deleteUserController = async (req, res, next) => {
|
||||
await req.jobQueue.deleteJob(monitor);
|
||||
await req.db.deleteChecks(monitor._id);
|
||||
await req.db.deleteAlertByMonitorId(monitor._id);
|
||||
await req.db.deletePageSpeedChecksByMonitorId(monitor._id);
|
||||
await req.db.deleteNotificationsByMonitorId(monitor._id);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -182,6 +182,7 @@ const deleteMonitor = async (req, res, next) => {
|
||||
await req.jobQueue.deleteJob(monitor);
|
||||
await req.db.deleteChecks(monitor._id);
|
||||
await req.db.deleteAlertByMonitorId(monitor._id);
|
||||
await req.db.deletePageSpeedChecksByMonitorId(monitor._id);
|
||||
await req.db.deleteNotificationsByMonitorId(monitor._id);
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,7 +79,7 @@ const {
|
||||
const {
|
||||
createPageSpeedCheck,
|
||||
getPageSpeedChecks,
|
||||
deletePageSpeedChecks,
|
||||
deletePageSpeedChecksByMonitorId,
|
||||
} = require("./modules/pageSpeedCheckModule");
|
||||
|
||||
//****************************************
|
||||
@@ -157,7 +157,7 @@ module.exports = {
|
||||
deleteMonitorsByUserId,
|
||||
createPageSpeedCheck,
|
||||
getPageSpeedChecks,
|
||||
deletePageSpeedChecks,
|
||||
deletePageSpeedChecksByMonitorId,
|
||||
createMaintenanceWindow,
|
||||
getMaintenanceWindowsByUserId,
|
||||
getMaintenanceWindowsByMonitorId,
|
||||
|
||||
@@ -48,7 +48,7 @@ const getPageSpeedChecks = async (monitorId) => {
|
||||
* @throws {Error}
|
||||
*/
|
||||
|
||||
const deletePageSpeedChecks = async (monitorId) => {
|
||||
const deletePageSpeedChecksByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await PageSpeedCheck.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
@@ -60,5 +60,5 @@ const deletePageSpeedChecks = async (monitorId) => {
|
||||
module.exports = {
|
||||
createPageSpeedCheck,
|
||||
getPageSpeedChecks,
|
||||
deletePageSpeedChecks,
|
||||
deletePageSpeedChecksByMonitorId,
|
||||
};
|
||||
@@ -1,5 +1,39 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const AuditSchema = mongoose.Schema({
|
||||
id: { type: String, required: true },
|
||||
title: { type: String, required: true },
|
||||
description: { type: String, required: true },
|
||||
score: { type: Number, required: true },
|
||||
scoreDisplayMode: { type: String, required: true },
|
||||
displayValue: { type: String, required: true },
|
||||
numericValue: { type: Number, required: true },
|
||||
numericUnit: { type: String, required: true },
|
||||
});
|
||||
|
||||
const AuditsSchema = mongoose.Schema({
|
||||
cls: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
si: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
fcp: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
lcp: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
tbt: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Mongoose schema for storing metrics from Google Lighthouse.
|
||||
* @typedef {Object} PageSpeedCheck
|
||||
@@ -37,6 +71,10 @@ const PageSpeedCheck = mongoose.Schema(
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
audits: {
|
||||
type: AuditsSchema,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -23,7 +23,7 @@ const {
|
||||
//Auth routes
|
||||
router.post("/register", upload.single("profileImage"), registerController);
|
||||
router.post("/login", loginController);
|
||||
router.post(
|
||||
router.put(
|
||||
"/user/:userId",
|
||||
upload.single("profileImage"),
|
||||
verifyJWT,
|
||||
|
||||
@@ -8,12 +8,12 @@ router.get("/:monitorId", monitorController.getMonitorById);
|
||||
router.get("/user/:userId", monitorController.getMonitorsByUserId);
|
||||
|
||||
router.post("/", monitorController.createMonitor);
|
||||
router.post(
|
||||
"/delete/:monitorId",
|
||||
router.delete(
|
||||
"/:monitorId",
|
||||
verifyOwnership(Monitor, "monitorId"),
|
||||
monitorController.deleteMonitor
|
||||
);
|
||||
router.post(
|
||||
router.put(
|
||||
"/edit/:monitorId",
|
||||
verifyOwnership(Monitor, "monitorId"),
|
||||
monitorController.editMonitor
|
||||
|
||||
@@ -129,6 +129,22 @@ class NetworkService {
|
||||
);
|
||||
const pageSpeedResults = response.data;
|
||||
const categories = pageSpeedResults.lighthouseResult?.categories;
|
||||
const audits = pageSpeedResults.lighthouseResult?.audits;
|
||||
const {
|
||||
"cumulative-layout-shift": cls,
|
||||
"speed-index": si,
|
||||
"first-contentful-paint": fcp,
|
||||
"largest-contentful-paint": lcp,
|
||||
"total-blocking-time": tbt,
|
||||
} = audits;
|
||||
|
||||
// Weights
|
||||
// First Contentful Paint 10%
|
||||
// Speed Index 10%
|
||||
// Largest Contentful Paint 25%
|
||||
// Total Blocking Time 30%
|
||||
// Cumulative Layout Shift 25%
|
||||
|
||||
const checkData = {
|
||||
monitorId: job.data._id,
|
||||
status: true,
|
||||
@@ -136,7 +152,15 @@ class NetworkService {
|
||||
bestPractices: (categories["best-practices"]?.score || 0) * 100,
|
||||
seo: (categories.seo?.score || 0) * 100,
|
||||
performance: (categories.performance?.score || 0) * 100,
|
||||
audits: {
|
||||
cls,
|
||||
si,
|
||||
fcp,
|
||||
lcp,
|
||||
tbt,
|
||||
},
|
||||
};
|
||||
|
||||
this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck);
|
||||
} catch (error) {
|
||||
const checkData = {
|
||||
|
||||
Reference in new issue
Block a user