Skip to content
Closed
Changes from all 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
70 changes: 51 additions & 19 deletions components/IntegrationsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import React from 'react';
import { ExternalLink, BarChart3, FileText, Box, Server, Database } from 'lucide-react';
import React, { useEffect, useState } from "react";
import {
ExternalLink,
BarChart3,
FileText,
Box,
Server,
Database,
} from "lucide-react";

const IntegrationsSection: React.FC = () => {
const [isMobile, setIsMobile] = useState(false);
const [showAll, setShowAll] = useState(false);

useEffect(() => {
const checkScreen = () => setIsMobile(window.innerWidth < 768);
checkScreen();
window.addEventListener("resize", checkScreen);
return () => window.removeEventListener("resize", checkScreen);
}, []);
Comment on lines +12 to +20
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Address SSR hydration mismatch and optimize resize handler.

Two issues to address:

  1. SSR Hydration Mismatch: Initializing isMobile as false can cause a hydration mismatch in SSR environments (Next.js). The server renders the desktop view, but if the client is mobile, it flashes desktop content before switching to mobile.

  2. Performance: The resize listener fires on every pixel change, causing unnecessary re-renders even when the viewport doesn't cross the 768px threshold.

Apply this diff to fix both issues:

-  const [isMobile, setIsMobile] = useState(false);
+  const [isMobile, setIsMobile] = useState(() => {
+    if (typeof window !== 'undefined') {
+      return window.innerWidth < 768;
+    }
+    return false;
+  });
   const [showAll, setShowAll] = useState(false);

   useEffect(() => {
-    const checkScreen = () => setIsMobile(window.innerWidth < 768);
+    const checkScreen = () => {
+      const mobile = window.innerWidth < 768;
+      setIsMobile(prev => prev !== mobile ? mobile : prev);
+    };
     checkScreen();
     window.addEventListener("resize", checkScreen);
     return () => window.removeEventListener("resize", checkScreen);
   }, []);
📝 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 [isMobile, setIsMobile] = useState(false);
const [showAll, setShowAll] = useState(false);
useEffect(() => {
const checkScreen = () => setIsMobile(window.innerWidth < 768);
checkScreen();
window.addEventListener("resize", checkScreen);
return () => window.removeEventListener("resize", checkScreen);
}, []);
const [isMobile, setIsMobile] = useState(() => {
if (typeof window !== 'undefined') {
return window.innerWidth < 768;
}
return false;
});
const [showAll, setShowAll] = useState(false);
useEffect(() => {
const checkScreen = () => {
const mobile = window.innerWidth < 768;
setIsMobile(prev => prev !== mobile ? mobile : prev);
};
checkScreen();
window.addEventListener("resize", checkScreen);
return () => window.removeEventListener("resize", checkScreen);
}, []);

const integrations = [
{
name: "Grafana",
Expand Down Expand Up @@ -223,7 +239,10 @@ const IntegrationsSection: React.FC = () => {
d="M26.744,12v1.231H28.7v5.538H26.744V20H30V12Z"
style={{ fill: "#201a26" }}
></path>
<path d="M17.628,16l5.21-2.769v5.538Z" style={{ fill: "#30d475" }}></path>
<path
d="M17.628,16l5.21-2.769v5.538Z"
style={{ fill: "#30d475" }}
></path>
<ellipse
cx="12.093"
cy="16"
Expand All @@ -237,43 +256,50 @@ const IntegrationsSection: React.FC = () => {
link: "https://opsimate.vercel.app/docs/providers-services/services/systemd-services",
},
];
const items = !isMobile || showAll ? integrations : integrations.slice(0, 4);

return (
<section id="integrations" className="section-padding bg-surface-50 dark:bg-surface-950">
<section
id="integrations"
className="section-padding bg-surface-50 dark:bg-surface-950"
>
<div className="container-max">
{/* Section Header */}
<div className="text-center mb-16">
<h2 className="text-3xl md:text-4xl font-bold text-surface-900 dark:text-surface-100 mb-4">
Integrate with Your{' '}
Integrate with Your{" "}
<span className="gradient-text">Existing Tools</span>
</h2>
<p className="text-xl text-surface-600 dark:text-surface-400 max-w-3xl mx-auto leading-relaxed">
OpsiMate seamlessly connects with your current monitoring and infrastructure tools.
No need to replace what's working - enhance it.
OpsiMate seamlessly connects with your current monitoring and
infrastructure tools. No need to replace what's working - enhance
it.
</p>
</div>

{/* Integrations Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-16">
{integrations.map((integration, index) => (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 md:gap-6">
{items.map((integration, index) => (
<div
key={index}
className="bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-xl p-6 hover:shadow-lg dark:hover:shadow-white/10 hover:border-blue-200 dark:hover:border-gray-300 transition-all duration-300 group"
>
{/* Integration Icon */}
<div className="w-12 h-12 bg-surface-200 dark:bg-surface-700 rounded-lg mb-4 flex items-center justify-center group-hover:bg-blue-50 dark:group-hover:bg-gray-600 transition-colors duration-300">
{integration.icon }
{integration.icon}
</div>

<div className="mb-2 ">
<h3 className="font-semibold text-surface-900 dark:text-surface-100">{integration.name}</h3>
<h3 className="font-semibold text-surface-900 dark:text-surface-100">
{integration.name}
</h3>
</div>

<div className="text-surface-600 dark:text-surface-400 text-sm mb-4 leading-relaxed ">
{integration.description}
</div>
<a

<a
href={integration.link}
target="_blank"
rel="noopener noreferrer"
Expand All @@ -285,10 +311,16 @@ const IntegrationsSection: React.FC = () => {
</div>
))}
</div>




{isMobile && integrations.length > 4 && (
<button
className="mt-5 mb-11 w-full text-blue-600 hover:text-blue-700 text-sm font-medium "
onClick={() => setShowAll(!showAll)}
aria-expanded={showAll}
aria-controls="integrations-grid"
>
{!showAll ? "View more" : "View less"}
</button>
)}
</div>
</section>
);
Expand Down