Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/fe/statuspage 1 #1306

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Client/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { logger } from "./Utils/Logger"; // Import the logger
import { networkService } from "./main";
import { Infrastructure } from "./Pages/Infrastructure";
import InfrastructureDetails from "./Pages/Infrastructure/Details";
import CreateStatus from "./Pages/Status/CreateStatus";
function App() {
const AdminCheckedRegister = withAdminCheck(Register);
const MonitorsWithAdminProp = withAdminProp(Monitors);
Expand Down Expand Up @@ -148,6 +149,10 @@ function App() {
path="status"
element={<ProtectedRoute Component={Status} />}
/>
<Route
path="status/create"
element={<ProtectedRoute Component={CreateStatus} />}
/>
<Route
path="integrations"
element={<ProtectedRoute Component={Integrations} />}
Expand Down
7 changes: 6 additions & 1 deletion Client/src/Components/Inputs/Checkbox/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import "./index.css";
* @param {string} [props.value] - The value of the checkbox input.
* @param {function} [props.onChange] - The function to call when the checkbox value changes.
* @param {boolean} [props.isDisabled] - Whether the checkbox is disabled or not.
* @param {boolean} [props.alignSelf] - Whether the checkbox label should be positioned on flex-start.
*
* @returns {JSX.Element}
*
Expand All @@ -25,6 +26,7 @@ import "./index.css";
* isChecked={checks.type === "ping"}
* value="ping"
* onChange={handleChange}
* alignSelf = {alignSelf}
* />
*/

Expand All @@ -36,10 +38,12 @@ const Checkbox = ({
value,
onChange,
isDisabled,
alignSelf
}) => {
/* TODO move sizes to theme */
const sizes = { small: "14px", medium: "16px", large: "18px" };
const theme = useTheme();
const override = alignSelf? { alignSelf: "flex-start" } : {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what this is doing, can you provide a print screen @shemy-gan ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use case is when the label is an element instead of string, it will align the checkbox on top of the container vertically; Alex suggested to expose this explicitly, so the fix was here :54c2826

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show the two cases in a print screen?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move on to feat/fe/statuspage 2, the layout is as below
Screenshot from 2024-12-11 19-28-38

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Please move on to feat/fe/statuspage 2" what do you mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot from 2024-12-11 20-53-32
Screenshot from 2024-12-11 20-53-05

This part layout has already been updated in the next branch feat/fe/statuspage 2, and there is a PR for that also

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, ok. So should we close this PR, since it is already contemplated in the other one?

return (
<FormControlLabel
className="checkbox-wrapper"
Expand All @@ -57,7 +61,7 @@ const Checkbox = ({
sx={{
"&:hover": { backgroundColor: "transparent" },
"& svg": { width: sizes[size], height: sizes[size] },
alignSelf: "flex-start",
...override
}}
/>
}
Expand Down Expand Up @@ -95,6 +99,7 @@ Checkbox.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func,
isDisabled: PropTypes.bool,
alignSelf: PropTypes.bool,
};

export default Checkbox;
3 changes: 2 additions & 1 deletion Client/src/Components/Sidebar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import DotsVertical from "../../assets/icons/dots-vertical.svg?react";
import ChangeLog from "../../assets/icons/changeLog.svg?react";
import Docs from "../../assets/icons/docs.svg?react";
import Folder from "../../assets/icons/folder.svg?react";
import StatusPages from "../../assets/icons/status-pages.svg?react";

import "./index.css";

Expand All @@ -51,7 +52,7 @@ const menu = [
{ name: "Pagespeed", path: "pagespeed", icon: <PageSpeed /> },
{ name: "Infrastructure", path: "infrastructure", icon: <Integrations /> },
{ name: "Incidents", path: "incidents", icon: <Incidents /> },
// { name: "Status pages", path: "status", icon: <StatusPages /> },
{ name: "Status pages", path: "status", icon: <StatusPages /> },
{ name: "Maintenance", path: "maintenance", icon: <Maintenance /> },
// { name: "Integrations", path: "integrations", icon: <Integrations /> },
{
Expand Down
66 changes: 66 additions & 0 deletions Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useState, useRef } from "react";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Yo! We got some unused imports and incomplete state setup here!

  1. useRef is imported but not used
  2. useDispatch is imported but not used
  3. localData state is initialized as an empty object without any properties
- import { useState, useRef } from "react";
+ import { useState } from "react";

- import { useDispatch, useSelector } from "react-redux";
+ import { useSelector } from "react-redux";

  const [localData, setLocalData] = useState({
+   isPublished: false,
+   title: "",
+   description: ""
  });

Also applies to: 6-6, 11-19

import { Box, Stack, Typography } from "@mui/material";
import { ConfigBox } from "../../../Pages/Monitors/styled";
import Checkbox from "../../Inputs/Checkbox";
import { useTheme } from "@emotion/react";
import { useDispatch, useSelector } from "react-redux";
import TabPanel from "@mui/lab/TabPanel";

const GeneralSettingsPanel = () => {
const theme = useTheme();
const dispatch = useDispatch();

//redux state
const { user, authToken, isLoading } = useSelector((state) => state.auth);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Yo! We need to handle loading states, fam!

The component imports isLoading from Redux but doesn't use it to show loading states.

  <TabPanel
    value="general settings"
    sx={{
      "& h1, & p, & input": {
        color: theme.palette.text.tertiary,
      },
+     opacity: isLoading ? 0.5 : 1,
+     pointerEvents: isLoading ? 'none' : 'auto'
    }}
  >
+   {isLoading && (
+     <Box sx={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }}>
+       <CircularProgress />
+     </Box>
+   )}

Also applies to: 25-33


// Local state for form data, errors, and file handling
const [localData, setLocalData] = useState({
});
const [errors, setErrors] = useState({});
const [file, setFile] = useState();

const handleSubmit = () => {};

const handleChange = () => {};

const handleBlur = () => {};
return (
<TabPanel
value="general settings"
sx={{
"& h1, & p, & input": {
color: theme.palette.text.tertiary,
},
}}
>
<Stack
component="form"
className="status-general-settings-form"
onSubmit={handleSubmit}
noValidate
spellCheck="false"
gap={theme.spacing(12)}
mt={theme.spacing(6)}
>
<ConfigBox>
<Box>
<Stack gap={theme.spacing(6)}>
<Typography component="h2">Access</Typography>
<Typography component="p">
If your status page is ready, you can mark it as published.
</Typography>
</Stack>
</Box>
<Checkbox
jennifer-gan marked this conversation as resolved.
Show resolved Hide resolved
id="published-to-public"
label={`Published and visible to the public`}
onChange={(e) => handleChange(e)}
jennifer-gan marked this conversation as resolved.
Show resolved Hide resolved
onBlur={handleBlur}
jennifer-gan marked this conversation as resolved.
Show resolved Hide resolved
/>
</ConfigBox>
</Stack>
</TabPanel>
);
};

export default GeneralSettingsPanel;
82 changes: 82 additions & 0 deletions Client/src/Pages/Status/CreateStatus/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import PropTypes from "prop-types";
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 GeneralSettingsPanel from "../../../Components/TabPanels/Status/GeneralSettingsPanel";

/**
* CreateStatus page renders a page with tabs for general settings and contents.
* @param {string} [props.open] - Specifies the initially open tab: 'general settings' or 'content'.
* @returns {JSX.Element}
*/

const CreateStatus = ({ open = "general settings" }) => {
const theme = useTheme();
const navigate = useNavigate();
const tab = open;
const handleTabChange = (event, newTab) => {
navigate(`/status/${newTab}`);
};
Comment on lines +19 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are calling this without passing the new tab. I suggest deleting the second parameter, adding a name to the input, and getting the name from the event target, then passing it to to navigate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, the onChange of TabList has the (event, value) as param/signature, so those were passed on to the handleTabChange

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shemy-gan Please avoid resolving conversations ahead of time.
HandleTabChange is called on line 46 without the second parameter. I am suggesting removing the second parameter altogether, an deal with that data through the name attribute from the input.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The document/signature of onChange has event and value,

(property) onChange?: ((event: React.SyntheticEvent, value: any) => void) | undefined
Callback fired when the value changes.

so here
the handleTabChange did have the second param

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jennifer-gan I believe the point here is that if we use the name attribute we don't have to use the second parameter.

Similar to the implementation in the refactor of the various create pages I pushed the other day.

Always nice to reduce the number of parameters needed if at all possible

const { user } = useSelector((state) => state.auth);

const requiredRoles = ["superadmin", "admin"];
let tabList = ["General Settings", "Contents"];

return (
<Box
className="status"
px={theme.spacing(20)}
py={theme.spacing(12)}
border={1}
borderColor={theme.palette.border.light}
borderRadius={theme.shape.borderRadius}
backgroundColor={theme.palette.background.main}
>
<TabContext value={tab}>
<Box
sx={{
borderBottom: 1,
borderColor: theme.palette.border.light,
"& .MuiTabs-root": { height: "fit-content", minHeight: "0" },
}}
>
<TabList
onChange={handleTabChange}
aria-label="status tabs"
>
{tabList.map((label, index) => (
<Tab
label={label}
key={index}
value={label.toLowerCase()}
sx={{
fontSize: 13,
color: theme.palette.text.tertiary,
textTransform: "none",
minWidth: "fit-content",
minHeight: 0,
paddingLeft: 0,
paddingY: theme.spacing(4),
fontWeight: 400,
marginRight: theme.spacing(8),
"&:focus": {
outline: "none",
},
}}
/>
))}
</TabList>
</Box>
<GeneralSettingsPanel />
</TabContext>
Comment on lines +37 to +73
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

This tab context needs some error boundaries and loading states!

The tab navigation could break if something goes wrong, and users won't know what's happening.

Here's a more robust implementation:

+const [isLoading, setIsLoading] = useState(false);
+const [error, setError] = useState(null);

+if (error) {
+  return <Box>Error loading status page: {error.message}</Box>;
+}

 <TabContext value={tab}>
+  {isLoading ? (
+    <Box>Loading...</Box>
+  ) : (
     <Box
       sx={{
         borderBottom: 1,
         borderColor: theme.palette.border.light,
         "& .MuiTabs-root": { height: "fit-content", minHeight: "0" },
       }}
     >
       // ... rest of the code
     </Box>
+  )}
 </TabContext>

Committable suggestion skipped: line range outside the PR's diff.

</Box>
);
};

CreateStatus.propTypes = {
open: PropTypes.oneOf(["general settings", "contents"]),
};

export default CreateStatus;
Empty file removed Client/src/Pages/Status/index.css
Empty file.
20 changes: 18 additions & 2 deletions Client/src/Pages/Status/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Box } from "@mui/material";
import { useTheme } from "@emotion/react";
import Fallback from "../../Components/Fallback";
import { Box, Button, Stack } from "@mui/material";
import { useNavigate } from "react-router-dom";

const Status = () => {
const theme = useTheme();

const navigate = useNavigate();
return (
<Box
className="status"
Expand All @@ -28,6 +29,21 @@ const Status = () => {
"Build trust with your customers",
]}
/>
<Stack
alignItems="center"
mt={theme.spacing(10)}
>
<Button
variant="contained"
color="primary"
onClick={() => {
navigate("/status/create");
}}
sx={{ fontWeight: 500 }}
>
Let's create your status page
</Button>
</Stack>
</Box>
);
};
Expand Down
Loading