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

fix: fe/pagespeed details refactor #1657

Merged
merged 9 commits into from
Jan 29, 2025
Merged

Conversation

ajhollid
Copy link
Collaborator

@ajhollid ajhollid commented Jan 28, 2025

This PR refactors the PageSpeedDetails page for readability, maintainability, and reliability. The original page was very hard to read and understand.

  • Move network operations to hook
  • Refactor duplicated components
  • Use a standard directory structure
  • Use standard layouts wherever possible
  • Remove as much inline styling as possible
  • Simplify layouts
  • Remove ternary renders
<Stack gap={theme.spacing(10)}>
	<Breadcrumbs list={BREADCRUMBS} />
	<MonitorStatusHeader
		isAdmin={isAdmin}
		shouldRender={!isLoading}
		monitor={monitor}
	/>
	<PageSpeedStatusBoxes
		shouldRender={!isLoading}
		monitor={monitor}
	/>
	<Typography
		variant="body2"
		my={theme.spacing(8)}
	>
		Showing statistics for past 24 hours.
	</Typography>
	<PageSpeedAreaChart
		shouldRender={!isLoading}
		monitor={monitor}
		metrics={metrics}
		handleMetrics={handleMetrics}
	/>
	<PerformanceReport
		shouldRender={!isLoading}
		audits={audits}
	/>
</Stack>

vs

<Stack
	className="page-speed-details"
	gap={theme.spacing(10)}
>
	{loading ? (
		<SkeletonLayout />
	) : (
		<>
			<Breadcrumbs
				list={[
					{ name: "pagespeed", path: "/pagespeed" },
					{ name: "details", path: `/pagespeed/${monitorId}` },
				]}
			/>
			<Stack
				direction="row"
				gap={theme.spacing(2)}
			>
				<Box>
					<Typography
						component="h1"
						variant="h1"
					>
						{monitor.name}
					</Typography>
					<Stack
						direction="row"
						alignItems="center"
						height="fit-content"
						gap={theme.spacing(2)}
					>
						<Tooltip
							title={pagespeedStatusMsg[determineState(monitor)]}
							disableInteractive
							slotProps={{
								popper: {
									modifiers: [
										{
											name: "offset",
											options: {
												offset: [0, -8],
											},
										},
									],
								},
							}}
						>
							<Box>
								<PulseDot color={statusColor[determineState(monitor)]} />
							</Box>
						</Tooltip>
						<Typography
							component="h2"
							variant="h2"
						>
							{monitor?.url}
						</Typography>
						<Typography
							position="relative"
							variant="body2"
							mt={theme.spacing(1)}
							ml={theme.spacing(6)}
							sx={{
								"&:before": {
									position: "absolute",
									content: `""`,
									width: 4,
									height: 4,
									borderRadius: "50%",
									backgroundColor: theme.palette.primary.contrastTextTertiary,
									opacity: 0.8,
									left: -9,
									top: "50%",
									transform: "translateY(-50%)",
								},
							}}
						>
							Checking every {formatDurationRounded(monitor?.interval)}.
						</Typography>
					</Stack>
				</Box>
				{isAdmin && (
					<Button
						variant="contained"
						color="secondary"
						onClick={() => navigate(`/pagespeed/configure/${monitorId}`)}
						sx={{
							ml: "auto",
							alignSelf: "flex-end",
							px: theme.spacing(5),
							"& svg": {
								mr: theme.spacing(3),
								"& path": {
									stroke: theme.palette.primary.contrastTextTertiary,
								},
							},
						}}
					>
						<SettingsIcon />
						Configure
					</Button>
				)}
			</Stack>
			<Stack
				direction="row"
				gap={theme.spacing(8)}
			>
				<StatBox
					heading="checks since"
					subHeading={
						<>
							{splitDuration(monitor?.uptimeDuration)}
							<Typography component="span">ago</Typography>
						</>
					}
				/>
				<StatBox
					heading="last check"
					subHeading={
						<>
							{splitDuration(monitor?.lastChecked)}
							<Typography component="span">ago</Typography>
						</>
					}
				/>
			</Stack>
			<Box>
				<Typography
					variant="body2"
					my={theme.spacing(8)}
				>
					Showing statistics for past 24 hours.
				</Typography>
				<ChartBox sx={{ gridTemplateColumns: "75% 25%" }}>
					<Stack
						direction="row"
						alignItems="center"
						gap={theme.spacing(6)}
					>
						<IconBox>
							<ScoreIcon />
						</IconBox>
						<Typography component="h2">Score history</Typography>
					</Stack>
					<Box>
						<PagespeedDetailsAreaChart
							data={data}
							interval={monitor?.interval}
							metrics={metrics}
						/>
					</Box>
					<Box>
						<Stack
							direction="row"
							alignItems="center"
							gap={theme.spacing(6)}
						>
							<IconBox>
								<MetricsIcon />
							</IconBox>
							<Typography component="h2">Metrics</Typography>
						</Stack>
						<Stack
							gap={theme.spacing(4)}
							mt={theme.spacing(16)}
							sx={{
								"& label": { pl: theme.spacing(6) },
							}}
						>
							<Box>
								<Typography
									fontSize={11}
									fontWeight={500}
								>
									Shown
								</Typography>
								<Divider sx={{ mt: theme.spacing(2) }} />
							</Box>
							<Checkbox
								id="accessibility-toggle"
								label="Accessibility"
								isChecked={metrics.accessibility}
								onChange={() => handleMetrics("accessibility")}
							/>
							<Divider />
							<Checkbox
								id="best-practices-toggle"
								label="Best Practices"
								isChecked={metrics.bestPractices}
								onChange={() => handleMetrics("bestPractices")}
							/>
							<Divider />
							<Checkbox
								id="performance-toggle"
								label="Performance"
								isChecked={metrics.performance}
								onChange={() => handleMetrics("performance")}
							/>
							<Divider />
							<Checkbox
								id="seo-toggle"
								label="Search Engine Optimization"
								isChecked={metrics.seo}
								onChange={() => handleMetrics("seo")}
							/>
							<Divider />
						</Stack>
					</Box>
				</ChartBox>
			</Box>
			<ChartBox
				flex={1}
				/* TODO apply 1fr 1fr for columns, and auto 1fr for Rows */
				sx={{ gridTemplateColumns: "50% 50%", gridTemplateRows: "15% 85%" }}
			>
				<Stack
					direction="row"
					alignItems="center"
					gap={theme.spacing(6)}
				>
					<IconBox>
						<PerformanceIcon />
					</IconBox>
					<Typography component="h2">Performance report</Typography>
				</Stack>
				<Stack
					alignItems="center"
					textAlign="center"
					minWidth="300px"
					flex={1}
					pt={theme.spacing(6)}
					pb={theme.spacing(12)}
				>
					<PieChart audits={audits} />
					<Typography
						variant="body1"
						mt="auto"
					>
						Values are estimated and may vary.{" "}
						<Typography
							component="span"
							fontSize="inherit"
							sx={{
								color: theme.palette.primary.main,
								fontWeight: 500,
								textDecoration: "underline",
								textUnderlineOffset: 2,
								transition: "all 200ms",
								cursor: "pointer",
								"&:hover": {
									textUnderlineOffset: 4,
								},
							}}
						>
							See calculator
						</Typography>
					</Typography>
				</Stack>
				<Box
					px={theme.spacing(20)}
					py={theme.spacing(8)}
					height="100%"
				>
					<Stack
						direction="row"
						alignItems="center"
						gap={theme.spacing(6)}
					>
						<IconBox>
							<SpedometerIcon />
						</IconBox>
						<Typography component="h2">Performance Metrics</Typography>
					</Stack>
					<Stack
						flexWrap="wrap"
						mt={theme.spacing(4)}
						gap={theme.spacing(4)}
					>
						{Object.keys(audits).map((key) => {
							if (key === "_id") return;

							let audit = audits[key];
							let score = audit.score * 100;
							let bg =
								score >= 90
									? theme.palette.success.main
									: score >= 50
										? theme.palette.warning.main
										: score >= 0
											? theme.palette.error.contrastText
											: theme.palette.tertiary.main;

							// Find the position where the number ends and the unit begins
							const match = audit.displayValue.match(/(\d+\.?\d*)\s*([a-zA-Z]+)/);
							let value;
							let unit;
							if (match) {
								value = match[1];
								match[2] === "s" ? (unit = "seconds") : (unit = match[2]);
							} else {
								value = audit.displayValue;
							}

							return (
								<Stack
									key={`${key}-box`}
									justifyContent="space-between"
									direction="row"
									gap={theme.spacing(4)}
									p={theme.spacing(3)}
									border={1}
									borderColor={theme.palette.primary.lowContrast}
									borderRadius={4}
								>
									<Box>
										<Typography
											fontSize={12}
											fontWeight={500}
											lineHeight={1}
											mb={1}
											textTransform="uppercase"
										>
											{audit.title}
										</Typography>
										<Typography
											component="span"
											fontSize={14}
											fontWeight={500}
											color={theme.palette.primary.contrastText}
										>
											{value}
											<Typography
												component="span"
												variant="body2"
												ml={2}
											>
												{unit}
											</Typography>
										</Typography>
									</Box>
									<Box
										width={4}
										backgroundColor={bg}
										borderRadius={4}
									/>
								</Stack>
							);
						})}
					</Stack>
				</Box>
			</ChartBox>
		</>
	)}
</Stack>

Copy link

@llamapreview llamapreview bot left a comment

Choose a reason for hiding this comment

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

Auto Pull Request Review from LlamaPReview

Large PR Notification

Dear contributor,

Thank you for your substantial contribution to this project. LlamaPReview has detected that this Pull Request contains a large volume of changes, which exceeds our current processing capacity.

Details:

  • PR and related contents total size: Approximately 111,406 characters
  • Current limit: 50,000 characters

Next steps:

  1. Consider breaking this PR into smaller, more focused changes if possible.
  2. For manual review, please reach out to your team members or maintainers.

We appreciate your understanding and commitment to improving this project. Your contributions are valuable, and we want to ensure they receive the attention they deserve.

LlamaPReview is continuously evolving to better serve the community. Share your thoughts on handling large PRs in our GitHub Discussions - your feedback helps us improve and expand our capabilities.

If you have any questions or need assistance, our community and support team are here to help.

Best regards,
LlamaPReview Team

Copy link

coderabbitai bot commented Jan 28, 2025

Walkthrough

This pull request introduces a comprehensive refactoring and enhancement of various components across the client-side application, focusing on improving code organization, modularity, and user interface consistency. The changes span multiple directories, introducing new components like ChartBox, LegendBox, StatusBoxes, and refactoring existing components in the PageSpeed and Uptime sections. The modifications aim to standardize component structures, improve loading states, and create more reusable UI elements.

Changes

File Path Change Summary
Client/src/Components/Charts/ Added new ChartBox and LegendBox components with standardized prop types and styling
Client/src/Components/StatusBoxes/ Introduced new StatusBoxes and SkeletonLayout components for consistent loading states
Client/src/Pages/PageSpeed/Details/ Major refactoring of components, introducing new chart and status box components with improved data fetching and rendering
Client/src/Pages/Uptime/Details/ Updated component imports and structures, replacing existing components with more modular alternatives
Client/src/Utils/toastUtils.jsx Modified default toast variant from "secondary" to "info"

Possibly related PRs

Suggested reviewers

  • mertssmnoglu

Sequence Diagram

sequenceDiagram
    participant User
    participant PageSpeedDetails
    participant MonitorFetch
    participant ChartBox
    participant AreaChart
    participant PerformanceReport

    User->>PageSpeedDetails: Access page
    PageSpeedDetails->>MonitorFetch: Fetch monitor data
    MonitorFetch-->>PageSpeedDetails: Return monitor and audits
    PageSpeedDetails->>ChartBox: Render with icon and header
    ChartBox->>AreaChart: Display chart data
    ChartBox->>PerformanceReport: Show performance metrics
Loading
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🔭 Outside diff range comments (2)
Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx (1)

Line range hint 8-66: Add prop types validation for type safety.

The component is missing prop types validation:

+ import PropTypes from 'prop-types';

const UptimeStatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => {
  // ... component implementation ...
};

+ UptimeStatusBoxes.propTypes = {
+   shouldRender: PropTypes.bool.isRequired,
+   monitor: PropTypes.shape({
+     uptimeStreak: PropTypes.number,
+     timeSinceLastCheck: PropTypes.number,
+     latestResponseTime: PropTypes.number,
+   }).isRequired,
+   certificateExpiry: PropTypes.string.isRequired,
+ };

export default UptimeStatusBoxes;
Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx (1)

Line range hint 171-173: Optimize performance calculations

The getPieData function recalculates on every render. Consider memoizing the calculations using useMemo.

+ const pieData = useMemo(() => {
+   return getPieData(audits);
+ }, [audits]);
- const pieData = getPieData(audits);
🧹 Nitpick comments (19)
Client/src/Components/Inputs/Search/index.jsx (1)

Line range hint 86-89: Clean up those CAIO_REVIEW comments, they're making me nervous!

The styling looks solid with proper theme token usage. We can safely remove these review markers - they're just collecting dust like mom's spaghetti.

 "& .MuiAutocomplete-tag": {
-    // CAIO_REVIEW
     color: theme.palette.primary.contrastText,
     backgroundColor: theme.palette.primary.lowContrast,
 },
 "& .MuiChip-deleteIcon": {
-    color: theme.palette.primary.contrastText, // CAIO_REVIEW
+    color: theme.palette.primary.contrastText,
 },

Also applies to: 92-94

Client/src/Pages/PageSpeed/Details/index.jsx (2)

16-21: Breadcrumbs design

Leaving that commented line might cause confusion in a high-pressure moment. Remove it if you no longer need it.

-  // { name: "details", path: `/pagespeed/${monitorId}` }, // Not needed?

34-34: Remove the console.log

Keep it clean to avoid a spaghetti meltdown in production.

-	console.log(monitor, audits, isLoading);
Client/src/Components/StatusBoxes/index.jsx (1)

7-21: Yo dawg, let's make these props required and add default values!

The shouldRender prop should be required since it's critical for component behavior. Also, consider adding defaultProps for cleaner code.

-const StatusBoxes = ({ shouldRender, children }) => {
+const StatusBoxes = ({ shouldRender = false, children }) => {

// Later in the file:
 StatusBoxes.propTypes = {
-  shouldRender: PropTypes.bool,
+  shouldRender: PropTypes.bool.isRequired,
   children: PropTypes.node,
 };
Client/src/Components/Charts/LegendBox/index.jsx (1)

13-22: These styles are fire but they need a home in the theme! 🏠

Move these styles to the theme for better maintainability and reusability.

// In your theme file
+const legendBoxStyles = {
+  borderLeftStyle: "solid",
+  borderLeftWidth: 1,
+  borderLeftColor: palette.primary.lowContrast,
+  backgroundColor: palette.primary.main,
+  background: `linear-gradient(325deg, ${palette.tertiary.main} 20%, ${palette.primary.main} 45%)`,
+};

// In LegendBox
-sx={{
-  ...sx,
-  "& label": { pl: theme.spacing(6) },
-  borderLeftStyle: "solid",
-  borderLeftWidth: 1,
-  borderLeftColor: theme.palette.primary.lowContrast,
-  backgroundColor: theme.palette.primary.main,
-  padding: theme.spacing(8),
-  background: `linear-gradient(325deg, ${theme.palette.tertiary.main} 20%, ${theme.palette.primary.main} 45%)`,
-}}
+sx={{
+  ...theme.components.LegendBox,
+  ...sx,
+  "& label": { pl: theme.spacing(6) },
+  padding: theme.spacing(8),
+}}
Client/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx (1)

1-4: Yo dawg, these import paths are deeper than Eminem's lyrics! 🍝

Consider moving common components to a shared directory to reduce the depth of import paths. Five levels of nesting (../../../../../) is a bit much and makes the code harder to maintain.

-import StatusBoxes from "../../../../../Components/StatusBoxes";
-import StatBox from "../../../../../Components/StatBox";
-import { Typography } from "@mui/material";
-import { getHumanReadableDuration } from "../../../../../Utils/timeUtils";
+import { StatusBoxes, StatBox } from "@/components";
+import { Typography } from "@mui/material";
+import { getHumanReadableDuration } from "@/utils/timeUtils";
Client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx (1)

15-23: These magic numbers are making me lose myself! 🎵

Extract API parameters into constants to improve maintainability and make the code more self-documenting.

+const MONITOR_STATS_CONFIG = {
+  SORT_ORDER: "desc",
+  DEFAULT_LIMIT: 50,
+  DEFAULT_DATE_RANGE: "day",
+};
+
 const res = await networkService.getStatsByMonitorId({
   authToken: authToken,
   monitorId: monitorId,
-  sortOrder: "desc",
-  limit: 50,
-  dateRange: "day",
+  sortOrder: MONITOR_STATS_CONFIG.SORT_ORDER,
+  limit: MONITOR_STATS_CONFIG.DEFAULT_LIMIT,
+  dateRange: MONITOR_STATS_CONFIG.DEFAULT_DATE_RANGE,
   numToDisplay: null,
   normalize: null,
 });
Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx (1)

36-46: These styles are dropping harder than a beat! But let's move them to the theme! 🎵

Consider moving inline styles to the theme for better maintainability and reusability.

+// Add to theme.js
+const theme = {
+  components: {
+    CalculatorLink: {
+      color: theme.palette.primary.main,
+      fontWeight: 500,
+      textDecoration: "underline",
+      textUnderlineOffset: 2,
+      transition: "all 200ms",
+      cursor: "pointer",
+      "&:hover": {
+        textUnderlineOffset: 4,
+      },
+    },
+  },
+};

// In component
-sx={{
-  color: theme.palette.primary.main,
-  fontWeight: 500,
-  textDecoration: "underline",
-  textUnderlineOffset: 2,
-  transition: "all 200ms",
-  cursor: "pointer",
-  "&:hover": {
-    textUnderlineOffset: 4,
-  },
-}}
+sx={theme.components.CalculatorLink}
Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx (3)

52-57: Strengthen prop type validation for better type safety.

The monitor prop type is too generic. Consider defining a more specific shape:

-  monitor: PropTypes.object,
+  monitor: PropTypes.shape({
+    checks: PropTypes.arrayOf(PropTypes.shape({
+      // Add specific check properties here
+    })),
+  }),

22-22: Memoize data processing for performance optimization.

The data processing could be memoized to prevent unnecessary array operations on re-renders.

+ import { useMemo } from 'react';
// ...
-  const data = monitor?.checks ? [...monitor.checks].reverse() : [];
+  const data = useMemo(() => 
+    monitor?.checks ? [...monitor.checks].reverse() : [],
+    [monitor?.checks]
+  );

27-27: Consider using a design token for spacing.

The spacing value of 10 seems arbitrary. Consider defining a design token for consistent spacing across components.

Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx (1)

17-18: Consider extracting repeated patterns.

The Divider pattern and font styles are repeated. Consider creating a custom component:

+ const MetricItem = ({ id, label, isChecked, onChange }) => (
+   <>
+     <Checkbox
+       id={id}
+       label={label}
+       isChecked={isChecked}
+       onChange={onChange}
+     />
+     <Divider />
+   </>
+ );

Also applies to: 30-30, 37-37, 44-44, 51-51

Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx (1)

Line range hint 19-62: Consider creating a TimeDisplay component.

The time display pattern with Typography is repeated. Consider abstracting it:

+ const TimeDisplay = ({ value, unit, suffix }) => (
+   <>
+     {value}
+     <Typography component="span">{unit}</Typography>
+     {suffix && <Typography component="span">{suffix}</Typography>}
+   </>
+ );
Client/src/Components/Charts/ChartBox/index.jsx (2)

40-64: Consider extracting styles to a separate file.

The CSS-in-JS styling is becoming complex. Consider moving it to a separate styles file for better maintainability:

+ // styles.js
+ export const getChartBoxStyles = (theme) => ({
+   heading: {
+     color: theme.palette.primary.contrastTextSecondary,
+     fontSize: 15,
+     fontWeight: 500,
+   },
+   // ... other styles
+ });

10-13: Extract default values to constants.

Consider moving default values to named constants for better maintainability:

+ const DEFAULT_HEIGHT = '300px';
+ const DEFAULT_JUSTIFY_CONTENT = 'space-between';
+ const DEFAULT_BORDER_RADIUS = 4;

const ChartBox = ({
  children,
  icon,
  header,
- height = "300px",
- justifyContent = "space-between",
+ height = DEFAULT_HEIGHT,
+ justifyContent = DEFAULT_JUSTIFY_CONTENT,
  Legend,
- borderRadiusRight = 4,
+ borderRadiusRight = DEFAULT_BORDER_RADIUS,
}) => {
Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx (2)

31-40: Yo dawg, let's make this regex pattern more robust!

The current regex pattern might miss some edge cases. Consider these improvements:

  • Handle decimal numbers with optional decimals
  • Support multiple units (e.g., "ms", "KB/s")
  • Separate unit conversion logic for better maintainability
-const match = audit.displayValue.match(/(\d+\.?\d*)\s*([a-zA-Z]+)/);
+const match = audit.displayValue.match(/(-?\d*\.?\d+)\s*([a-zA-Z/]+)/);
 let value;
 let unit;
 if (match) {
   value = match[1];
-  match[2] === "s" ? (unit = "seconds") : (unit = match[2]);
+  unit = getFormattedUnit(match[2]); // Extract to separate function
 } else {
   value = audit.displayValue;
+  unit = "";
 }
🧰 Tools
🪛 Biome (1.9.4)

[error] 37-37: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 37-37: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


42-53: Mom's spaghetti says we should clean up these styles!

Consider extracting these inline styles to a styled component or theme:

+const StyledStack = styled(Stack)(({ theme }) => ({
+  flex: 1,
+  justifyContent: "space-between",
+  flexDirection: "row",
+  gap: theme.spacing(4),
+  padding: theme.spacing(3),
+  border: 1,
+  borderColor: theme.palette.primary.lowContrast,
+  borderRadius: theme.shape.borderRadius,
+}));

-<Stack
-  flex={1}
-  justifyContent="space-between"
-  direction="row"
-  gap={theme.spacing(4)}
-  p={theme.spacing(3)}
-  border={1}
-  borderColor={theme.palette.primary.lowContrast}
-  borderRadius={4}
->
+<StyledStack>
Client/src/Components/Fallback/index.jsx (1)

Line range hint 73-78: Let's make these keys more unique than mom's spaghetti recipe!

While optional chaining is good, the key generation could be more robust:

-{checks?.map((check, index) => (
-  <Check
-    text={check}
-    key={`${title.trim().split(" ")[0]}-${index}`}
-    outlined={true}
-  />
-))}
+{checks?.map((check, index) => {
+  const sanitizedTitle = title?.trim().split(" ")[0] ?? "fallback";
+  return (
+    <Check
+      text={check}
+      key={`${sanitizedTitle}-check-${index}`}
+      outlined={true}
+    />
+  );
+})}
Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx (1)

118-121: Good defensive programming with null check!

The null check for audits prevents rendering issues. However, consider using useCallback for the event handlers to prevent unnecessary rerenders.

+ const handleExpand = useCallback((value) => {
+   setExpand(value);
+ }, []);
+ 
+ const handleHighlight = useCallback((item) => {
+   setHighLightedItem(item);
+ }, []);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b6e9b2 and a33c0c1.

⛔ Files ignored due to path filters (1)
  • Client/src/assets/icons/speedometer-icon.svg is excluded by !**/*.svg
📒 Files selected for processing (33)
  • Client/src/Components/Charts/ChartBox/index.jsx (1 hunks)
  • Client/src/Components/Charts/LegendBox/index.jsx (1 hunks)
  • Client/src/Components/Fallback/index.jsx (2 hunks)
  • Client/src/Components/Inputs/Search/index.jsx (2 hunks)
  • Client/src/Components/MonitorStatusHeader/ConfigButton/index.jsx (1 hunks)
  • Client/src/Components/MonitorStatusHeader/index.jsx (1 hunks)
  • Client/src/Components/StatusBoxes/index.jsx (1 hunks)
  • Client/src/Components/StatusBoxes/skeleton.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChart.jsx (4 hunks)
  • Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/index.css (0 hunks)
  • Client/src/Pages/PageSpeed/Details/index.jsx (1 hunks)
  • Client/src/Pages/PageSpeed/Details/skeleton.jsx (0 hunks)
  • Client/src/Pages/PageSpeed/Details/styled.jsx (0 hunks)
  • Client/src/Pages/PageSpeed/Monitors/index.jsx (1 hunks)
  • Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx (3 hunks)
  • Client/src/Pages/Uptime/Details/Components/Charts/ChartBox.jsx (0 hunks)
  • Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx (1 hunks)
  • Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx (1 hunks)
  • Client/src/Pages/Uptime/Details/Components/StatusBoxes/skeleton.jsx (0 hunks)
  • Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx (2 hunks)
  • Client/src/Pages/Uptime/Details/index.jsx (2 hunks)
  • Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx (4 hunks)
  • Client/src/Pages/Uptime/Monitors/index.jsx (1 hunks)
  • Client/src/Utils/toastUtils.jsx (1 hunks)
💤 Files with no reviewable changes (5)
  • Client/src/Pages/PageSpeed/Details/index.css
  • Client/src/Pages/Uptime/Details/Components/StatusBoxes/skeleton.jsx
  • Client/src/Pages/PageSpeed/Details/skeleton.jsx
  • Client/src/Pages/PageSpeed/Details/styled.jsx
  • Client/src/Pages/Uptime/Details/Components/Charts/ChartBox.jsx
✅ Files skipped from review due to trivial changes (4)
  • Client/src/Pages/PageSpeed/Monitors/index.jsx
  • Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx
  • Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx
  • Client/src/Components/MonitorStatusHeader/ConfigButton/index.jsx
🧰 Additional context used
🪛 Biome (1.9.4)
Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx

[error] 37-37: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 37-37: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

🔇 Additional comments (31)
Client/src/Components/Inputs/Search/index.jsx (3)

Line range hint 39-39: Yo, this TODO seems kinda sus

Keeping search state inside the component would make it less reusable. The current controlled component pattern is the React way - let's not fix what ain't broken, know what I'm sayin'?

Would you like me to demonstrate a comparison between the current implementation and an internal state version to show why the current approach is better?


201-201: Check if anyone's counting on that label, fam!

Making label optional is a breaking change if any parent components assume it's always there. Let's make sure we're not leaving anyone hanging.

✅ Verification successful

Yo, we're good - label prop change is safe! 🍝

After dropping into the codebase like mom's spaghetti, I found only one spot actually using the label prop ("Add monitors"), and the rest are living their best life without it. Making it optional won't break anyone's flow.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find all Search component usages to verify label prop handling
echo "Checking Search component usage patterns..."

# Look for Search components without label prop
rg -B 5 -A 5 "<Search\s+(?!.*label=)" --type jsx

# Look for Search components with label prop
rg -B 5 -A 5 "<Search\s+.*label=" --type jsx

Length of output: 458


Script:

#!/bin/bash
# Try multiple search strategies
echo "Strategy 1: Simple grep for Search component"
rg -B 3 -A 3 "<Search" --type ts --type js --type tsx

echo -e "\nStrategy 2: AST-based search"
ast-grep --pattern 'element Search {
  $$$
}'

echo -e "\nStrategy 3: Find all JSX files that might contain Search"
fd -e jsx -e tsx -e js -e ts | xargs cat | grep -B 3 -A 3 "<Search"

Length of output: 1261


142-144: Yo dawg, we need to verify this breaking change!

The switch from option.id to option._id could break existing implementations. Let's make sure we're not dropping mom's spaghetti here.

✅ Verification successful

Yo dawg, this change is straight fire! 🔥

The switch to option._id is actually the right move - it's following the MongoDB convention that's used consistently throughout the entire codebase. No instances of option.id were found, so this ain't breaking anything.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for potential usages of the Search component with .id property
echo "Checking for potential breaking changes..."

# Look for Search component usage
rg -A 5 "<Search\s+" --type jsx

# Look for direct id access on options
rg "option\.id" --type jsx --type js

Length of output: 223


Script:

#!/bin/bash
# Search with proper file extensions and patterns
echo "Yo, let's find those Search components..."

# Look for Search component imports and usage
rg -t js -t ts -t jsx -t tsx "import.*Search.*from.*Components/Inputs/Search|<Search\b" -A 5

# Look for option handling patterns
rg -t js -t ts -t jsx -t tsx "getOptionKey|option\.(id|_id)" -B 2 -A 2

# Check for MongoDB-style _id usage
rg -t js -t ts -t jsx -t tsx "_id['\"]?\s*[:=]" -B 2 -A 2

Length of output: 409


Script:

#!/bin/bash
# First, let's see what files we're dealing with
echo "Checking file structure..."
fd -e js -e jsx -e tsx -e ts

echo -e "\nNow searching for component usage..."
rg "import.*Search.*from|<Search\b" -B 1 -A 3

echo -e "\nLooking for option handling..."
rg "getOptionKey|option\." -B 1 -A 3

echo -e "\nChecking _id patterns..."
rg "_id\s*[=:]" -B 1 -A 3

Length of output: 67808

Client/src/Pages/PageSpeed/Details/index.jsx (11)

1-2: Nice streamlined imports

No spaghetti code here, everything remains steady.


4-9: Smooth usage of refactored components

You've harnessed each new piece with clarity. Keep it tight, so your code doesn't trip.


10-10: Embrace the theming

No reason to sweat: using useTheme from @emotion/react can pair well with MUI.


12-15: All set with React Router and Redux

Your usage of useParams and useSelector is straightforward. Keep that flow going; no need to let knees get weak.


29-32: Lean custom hook usage

Neat approach for consolidated fetching. The code's more maintainable, so you won't choke.


42-42: Toggle metrics elegantly

Your function's logic looks stable. No sweaty palms here.


48-54: Consistent layout

Using Stack and MonitorStatusHeader to structure your details is on point. It’s all gravy.


55-58: Efficient status boxes

This keeps your code organized, ensuring you don’t fumble the ball.


59-64: Informative text

Clear messaging is always key. No sign of code vomit here.


65-70: Sweet chart integration

The chart is conditionally rendered with shouldRender={!isLoading}. That’s tight.


71-74: Performance details

Great method to convey deeper insights. The final step to keep the flow unstoppable.

Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx (2)

1-2: Neat usage of MUI components

Your import is small but robust.


3-17: Simple skeleton layout

This layout keeps the user’s eyes from getting dizzy during loading. Solid approach, no meltdown.

Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx (2)

1-2: MUI imports

All good, no shaky arms here.


3-17: Congrats on reusability

This skeleton fosters consistent placeholders, preventing confusion for the end-user.

Client/src/Components/StatusBoxes/index.jsx (1)

14-19: These Stack props are straight fire! 🔥

Clean usage of MUI Stack with proper theme spacing. The horizontal layout with consistent gaps will make the status boxes look tight!

Client/src/Components/StatusBoxes/skeleton.jsx (1)

13-13: Watch out for that width calculation, fam!

The width calculation could break with extreme values of numBoxes. Consider adding a minimum width.

-const width = `${100 / numBoxes}%`;
+const width = `${Math.max(100 / numBoxes, 20)}%`;
Client/src/Utils/toastUtils.jsx (1)

15-15: That default variant change is sus without tests! 🤔

Changing default variant from "secondary" to "info" could affect existing toast behavior. Let's verify this change.

Client/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx (1)

6-39: Clean component structure, straight fire! 🔥

The component follows React best practices with:

  • Proper prop destructuring
  • Appropriate use of optional chaining
  • Clean JSX structure with semantic Typography components
Client/src/Components/MonitorStatusHeader/index.jsx (1)

Line range hint 1-55: This component is cleaner than mom's spaghetti! 🍝

Well-structured component with:

  • Proper prop validation
  • Clean conditional rendering
  • Good use of Material-UI components
Client/src/Components/Fallback/index.jsx (1)

37-39: Clean styling move, homie!

Moving borderStyle to sx prop follows Material-UI best practices for style organization.

Client/src/Pages/Uptime/Details/index.jsx (1)

83-91: These component updates are straight fire! 🔥

The replacement of components with their standardized versions aligns perfectly with the PR objectives for better organization and maintainability.

Client/src/Pages/Uptime/Monitors/index.jsx (1)

103-103: Smooth loading state handling, no cap!

Adding the monitorsAreLoading check prevents the Fallback component from showing prematurely during data fetching. This is a solid improvement to the user experience.

Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx (2)

3-3: Nice refactoring of component organization and layout!

Good job moving ChartBox to a shared components directory and using horizontal stacking for better layout organization.

Also applies to: 43-46


135-135: Well-documented prop types!

Good addition of the shouldRender prop type for better type safety.

Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx (2)

218-225: Clean header implementation with Stack!

Good use of Stack component to organize the header elements with proper spacing and alignment.


259-259: Verify loading state handling

The addition of monitorsAreLoading prop type is good, but let's verify the loading state handling across the component.

✅ Verification successful

Loading states are properly implemented

The monitorsAreLoading prop type addition is verified and works correctly with the existing loading state implementation. The component handles both data fetching and UI interaction loading states appropriately.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for loading state handling patterns
rg -A 5 'monitorsAreLoading|isLoading' Client/src/Pages/Uptime/Monitors/

Length of output: 8752

Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChart.jsx (1)

214-214: Great job on consistent naming convention!

The rename to PageSpeedAreaChart aligns better with the codebase's naming patterns.

Comment on lines +12 to +22
{Array.from({ length: numBoxes }).map((_, index) => {
const width = `${100 / numBoxes}%`;
return (
<Skeleton
variant="rounded"
width={width}
height={50}
key={index}
/>
);
})}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Mom's spaghetti moment: Using index as key! 😰

Using array index as key can cause issues with React's reconciliation. Also, let's add validation for numBoxes.

+SkeletonLayout.defaultProps = {
+  numBoxes: 1,
+};

 SkeletonLayout.propTypes = {
-  numBoxes: PropTypes.number,
+  numBoxes: PropTypes.number.isRequired,
+};

+const isPositiveInteger = (props, propName, componentName) => {
+  const value = props[propName];
+  if (value <= 0 || !Number.isInteger(value)) {
+    return new Error(
+      `Invalid prop ${propName} supplied to ${componentName}. Expected positive integer.`
+    );
+  }
+};

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

Comment on lines +36 to +40
LegendBox.propTypes = {
children: PropTypes.node,
icon: PropTypes.node,
header: PropTypes.string,
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Props validation looking kinda weak! 💪

Add missing sx prop type and make header required since it's essential for accessibility.

 LegendBox.propTypes = {
   children: PropTypes.node,
   icon: PropTypes.node,
-  header: PropTypes.string,
+  header: PropTypes.string.isRequired,
+  sx: PropTypes.object,
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LegendBox.propTypes = {
children: PropTypes.node,
icon: PropTypes.node,
header: PropTypes.string,
};
LegendBox.propTypes = {
children: PropTypes.node,
icon: PropTypes.node,
header: PropTypes.string.isRequired,
sx: PropTypes.object,
};

Comment on lines +28 to +30
<IconBox>{icon}</IconBox>
<Typography component="h2">{header}</Typography>
</Stack>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Accessibility check - we need some ARIA love! 🎵

Add proper ARIA attributes for better screen reader support.

-<IconBox>{icon}</IconBox>
+<IconBox aria-hidden="true">{icon}</IconBox>
-<Typography component="h2">{header}</Typography>
+<Typography component="h2" role="heading" aria-level="2">{header}</Typography>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<IconBox>{icon}</IconBox>
<Typography component="h2">{header}</Typography>
</Stack>
<IconBox aria-hidden="true">{icon}</IconBox>
<Typography component="h2" role="heading" aria-level="2">{header}</Typography>
</Stack>

Comment on lines +27 to +28
logger.error(logger);
navigate("/not-found", { replace: true });
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, this error logging is busted like a broken mic! 🎤

The error logger is being passed itself instead of the actual error object.

-logger.error(logger);
+logger.error(error);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.error(logger);
navigate("/not-found", { replace: true });
logger.error(error);
navigate("/not-found", { replace: true });

import PieChartLegend from "../Charts/PieChartLegend";
import SkeletonLayout from "./skeleton";
const PerformanceReport = ({ shouldRender, audits }) => {
console.log("shouldRender", shouldRender);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Cleanup on aisle 9! Console.log is still in production! 🧹

Remove debug console.log statement before shipping to production.

-console.log("shouldRender", shouldRender);

Comment on lines +8 to +54
const AreaChartLegend = ({ metrics, handleMetrics }) => {
const theme = useTheme();
return (
<LegendBox
icon={<MetricsIcon />}
header="Metrics"
>
<Box>
<Typography
fontSize={11}
fontWeight={500}
>
Shown
</Typography>
<Divider sx={{ mt: theme.spacing(2) }} />
</Box>
<Checkbox
id="accessibility-toggle"
label="Accessibility"
isChecked={metrics.accessibility}
onChange={() => handleMetrics("accessibility")}
/>
<Divider />
<Checkbox
id="best-practices-toggle"
label="Best Practices"
isChecked={metrics.bestPractices}
onChange={() => handleMetrics("bestPractices")}
/>
<Divider />
<Checkbox
id="performance-toggle"
label="Performance"
isChecked={metrics.performance}
onChange={() => handleMetrics("performance")}
/>
<Divider />
<Checkbox
id="seo-toggle"
label="Search Engine Optimization"
isChecked={metrics.seo}
onChange={() => handleMetrics("seo")}
/>
<Divider />
</LegendBox>
);
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add prop types validation for type safety.

The component is missing prop types validation. Add PropTypes to ensure proper usage:

+ import PropTypes from 'prop-types';

const AreaChartLegend = ({ metrics, handleMetrics }) => {
  // ... component implementation ...
};

+ AreaChartLegend.propTypes = {
+   metrics: PropTypes.shape({
+     accessibility: PropTypes.bool.isRequired,
+     bestPractices: PropTypes.bool.isRequired,
+     performance: PropTypes.bool.isRequired,
+     seo: PropTypes.bool.isRequired,
+   }).isRequired,
+   handleMetrics: PropTypes.func.isRequired,
+ };

export default AreaChartLegend;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const AreaChartLegend = ({ metrics, handleMetrics }) => {
const theme = useTheme();
return (
<LegendBox
icon={<MetricsIcon />}
header="Metrics"
>
<Box>
<Typography
fontSize={11}
fontWeight={500}
>
Shown
</Typography>
<Divider sx={{ mt: theme.spacing(2) }} />
</Box>
<Checkbox
id="accessibility-toggle"
label="Accessibility"
isChecked={metrics.accessibility}
onChange={() => handleMetrics("accessibility")}
/>
<Divider />
<Checkbox
id="best-practices-toggle"
label="Best Practices"
isChecked={metrics.bestPractices}
onChange={() => handleMetrics("bestPractices")}
/>
<Divider />
<Checkbox
id="performance-toggle"
label="Performance"
isChecked={metrics.performance}
onChange={() => handleMetrics("performance")}
/>
<Divider />
<Checkbox
id="seo-toggle"
label="Search Engine Optimization"
isChecked={metrics.seo}
onChange={() => handleMetrics("seo")}
/>
<Divider />
</LegendBox>
);
};
import PropTypes from 'prop-types';
const AreaChartLegend = ({ metrics, handleMetrics }) => {
const theme = useTheme();
return (
<LegendBox
icon={<MetricsIcon />}
header="Metrics"
>
<Box>
<Typography
fontSize={11}
fontWeight={500}
>
Shown
</Typography>
<Divider sx={{ mt: theme.spacing(2) }} />
</Box>
<Checkbox
id="accessibility-toggle"
label="Accessibility"
isChecked={metrics.accessibility}
onChange={() => handleMetrics("accessibility")}
/>
<Divider />
<Checkbox
id="best-practices-toggle"
label="Best Practices"
isChecked={metrics.bestPractices}
onChange={() => handleMetrics("bestPractices")}
/>
<Divider />
<Checkbox
id="performance-toggle"
label="Performance"
isChecked={metrics.performance}
onChange={() => handleMetrics("performance")}
/>
<Divider />
<Checkbox
id="seo-toggle"
label="Search Engine Optimization"
isChecked={metrics.seo}
onChange={() => handleMetrics("seo")}
/>
<Divider />
</LegendBox>
);
};
AreaChartLegend.propTypes = {
metrics: PropTypes.shape({
accessibility: PropTypes.bool.isRequired,
bestPractices: PropTypes.bool.isRequired,
performance: PropTypes.bool.isRequired,
seo: PropTypes.bool.isRequired,
}).isRequired,
handleMetrics: PropTypes.func.isRequired,
};
export default AreaChartLegend;

Comment on lines +85 to +90
ChartBox.propTypes = {
children: PropTypes.node,
icon: PropTypes.node.isRequired,
header: PropTypes.string.isRequired,
height: PropTypes.string,
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Complete prop types validation.

Add missing prop types for Legend and borderRadiusRight:

ChartBox.propTypes = {
  children: PropTypes.node,
  icon: PropTypes.node.isRequired,
  header: PropTypes.string.isRequired,
  height: PropTypes.string,
+ justifyContent: PropTypes.string,
+ Legend: PropTypes.node,
+ borderRadiusRight: PropTypes.number,
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ChartBox.propTypes = {
children: PropTypes.node,
icon: PropTypes.node.isRequired,
header: PropTypes.string.isRequired,
height: PropTypes.string,
};
ChartBox.propTypes = {
children: PropTypes.node,
icon: PropTypes.node.isRequired,
header: PropTypes.string.isRequired,
height: PropTypes.string,
justifyContent: PropTypes.string,
Legend: PropTypes.node,
borderRadiusRight: PropTypes.number,
};

Comment on lines +20 to +29
let audit = audits[key];
let score = audit.score * 100;
let bg =
score >= 90
? theme.palette.success.main
: score >= 50
? theme.palette.warning.main
: score >= 0
? theme.palette.error.main
: theme.palette.tertiary.main;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

These nested ternaries are making me nervous already!

Let's improve readability and maintainability:

+const SCORE_THRESHOLDS = {
+  HIGH: 90,
+  MEDIUM: 50,
+  LOW: 0,
+};
+
+const getScoreColor = (score, theme) => {
+  if (score >= SCORE_THRESHOLDS.HIGH) return theme.palette.success.main;
+  if (score >= SCORE_THRESHOLDS.MEDIUM) return theme.palette.warning.main;
+  if (score >= SCORE_THRESHOLDS.LOW) return theme.palette.error.main;
+  return theme.palette.tertiary.main;
+};

 let audit = audits[key];
 let score = audit.score * 100;
-let bg =
-  score >= 90
-    ? theme.palette.success.main
-    : score >= 50
-      ? theme.palette.warning.main
-      : score >= 0
-        ? theme.palette.error.main
-        : theme.palette.tertiary.main;
+let bg = getScoreColor(score, theme);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let audit = audits[key];
let score = audit.score * 100;
let bg =
score >= 90
? theme.palette.success.main
: score >= 50
? theme.palette.warning.main
: score >= 0
? theme.palette.error.main
: theme.palette.tertiary.main;
const SCORE_THRESHOLDS = {
HIGH: 90,
MEDIUM: 50,
LOW: 0,
};
const getScoreColor = (score, theme) => {
if (score >= SCORE_THRESHOLDS.HIGH) return theme.palette.success.main;
if (score >= SCORE_THRESHOLDS.MEDIUM) return theme.palette.warning.main;
if (score >= SCORE_THRESHOLDS.LOW) return theme.palette.error.main;
return theme.palette.tertiary.main;
};
let audit = audits[key];
let score = audit.score * 100;
let bg = getScoreColor(score, theme);

Comment on lines +217 to +220
const memoizedData = useMemo(
() => processDataWithGaps(data, monitor.interval),
[data[0]]
);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize memoization dependency

The memoization dependency [data[0]] might cause unnecessary rerenders. Consider using the entire data array or specific required properties.

-  [data[0]]
+  [JSON.stringify(data), monitor.interval]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const memoizedData = useMemo(
() => processDataWithGaps(data, monitor.interval),
[data[0]]
);
const memoizedData = useMemo(
() => processDataWithGaps(data, monitor.interval),
[JSON.stringify(data), monitor.interval]
);

@ajhollid ajhollid merged commit c55ca3f into develop Jan 29, 2025
3 checks passed
@ajhollid ajhollid deleted the fix/fe/pagespeed-details-refactor branch January 29, 2025 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant