diff --git a/.gitignore b/.gitignore index 8047efc..ad61f07 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,6 @@ yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* - -node_modules dist dist-ssr *.local @@ -24,3 +22,4 @@ dist-ssr *.sw? .env *.zip +node_modules diff --git a/public/img/Logo.jpg b/public/img/Logo.jpg new file mode 100644 index 0000000..a2c63de Binary files /dev/null and b/public/img/Logo.jpg differ diff --git a/src/App.css b/src/App.css index 64c7b22..b286249 100644 --- a/src/App.css +++ b/src/App.css @@ -65,12 +65,13 @@ body.dark { } .main-content { + margin-top: 75px; margin-left: 0; /* Pastikan kontennya full width */ } } /* Styling untuk tombol modal hanya tampil di bawah 768px */ -@media (min-width: 768px) { +@media (min-width: 769px) { .menu-toggle-btn, .sidebar-toggle-btn { display: none; } @@ -84,8 +85,7 @@ body.dark { /* Styling modal */ /* Styling untuk tombol modal hanya tampil di bawah 768px */ @media (min-width: 768px) { - .menu-toggle-btn, - .sidebar-toggle-btn { + .menu-toggle-btn { display: none; } } @@ -145,6 +145,20 @@ body.dark { background-color: rgba(255, 255, 255, 0.1); } +/* Default styling for close button */ +.sidebar-modal .btn-close { + transition: filter 0.3s; +} + +/* Close button untuk tema light */ +.light .sidebar-modal .btn-close { + filter: invert(0); /* Warna default (hitam) */ +} + +/* Close button untuk tema dark */ +.dark .sidebar-modal .btn-close { + filter: invert(1); /* Mengubah warna ke putih */ +} .navbar-app { display: flex; @@ -156,6 +170,20 @@ body.dark { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* Default shadow */ } +.navbar-brand img { + mix-blend-mode: multiply; +} + +.navbar-brand span { + font-weight: 900; +} + +@media (max-width: 768px) { + .navbar-brand span { + display: none; + } +} + /* Light Mode Navbar */ .light .navbar-app { background-color: #ffffff; @@ -370,6 +398,14 @@ button:hover { /* METRICS GRID TABLE */ /* ======================= */ +/* Wrapper untuk memungkinkan scroll horizontal */ +.metrics-scroll-wrapper { + overflow-x: auto; + width: 100%; + position: relative; +} + +/* Container utama untuk Metrics Table */ .metrics-container { display: flex; flex-direction: column; @@ -379,20 +415,39 @@ button:hover { transition: background-color 0.3s, color 0.3s; } -.metrics-header, .metrics-row { +/* Header Styling */ +.metrics-header { + position: sticky; + top: 0; + background-color: inherit; + z-index: 10; + font-weight: bold; display: grid; - grid-template-columns: 1fr 2fr 1fr 1fr 1fr 1fr; + grid-template-columns: repeat(6, minmax(200px, 1fr)); + gap: 10px; + padding: 12px; + text-align: center; + width: 1300px; +} + +/* Body Styling */ +.metrics-body { + display: block; + min-width: 1300px; + overflow-y: auto; + max-height: 400px; +} + +/* Row Styling */ +.metrics-row { + display: grid; + grid-template-columns: repeat(6, minmax(200px, 1fr)); gap: 10px; padding: 12px; text-align: center; } -/* Header Styling */ -.metrics-header { - font-weight: bold; -} - -/* LIGHT MODE */ +/* LIGHT MODE Styling */ .light .metrics-container { background-color: #ffffff; color: #1d1d1d; @@ -413,7 +468,7 @@ button:hover { background-color: #eeeeee; } -/* DARK MODE */ +/* DARK MODE Styling */ .dark .metrics-container { background-color: #161616; color: #ffffff; @@ -447,8 +502,8 @@ button:hover { /* Responsive Design */ @media (max-width: 768px) { .metrics-header, .metrics-row { - grid-template-columns: 1fr 1fr 1fr; /* Kurangi jumlah kolom agar lebih rapi di mobile */ - font-size: 14px; + width: 1290px; + grid-template-columns: repeat(6, minmax(200px, 1fr)); } } @@ -456,6 +511,13 @@ button:hover { /* DATA TABLE GRID STYLING */ /* ======================= */ +/* Wrapper agar DataTable bisa scroll ke samping */ +.data-table-scroll-wrapper { + overflow-x: auto; + width: 100%; + position: relative; +} + .data-table-container { display: flex; flex-direction: column; @@ -465,20 +527,74 @@ button:hover { transition: background-color 0.3s, color 0.3s; } -.data-table-header, .data-table-row { +/* Header tetap di atas saat scroll */ +.data-table-header { + position: sticky; + top: 0; + background-color: inherit; + z-index: 10; + font-weight: bold; display: grid; - grid-template-columns: 1fr 2fr 2fr 2fr 2fr 2fr 3fr 3fr; + grid-template-columns: repeat(8, minmax(100px, 1fr)); gap: 10px; padding: 12px; text-align: center; - align-items: center; + width: 1300px; } -/* HEADER */ -.data-table-header { - font-weight: bold; +/* Pastikan body bisa scroll */ +.data-table-body { + display: block; + min-width: 1300px; + overflow-y: auto; + max-height: 400px; } +/* Row styling */ +.data-table-row { + display: grid; + grid-template-columns: repeat(8, minmax(150px, 1fr)); + gap: 10px; + padding: 12px; + text-align: center; +} + +/* Responsive Design */ + +@media (max-width: 1279px) { + .data-table-header, + .data-table-row { + width: 1300px; + grid-template-columns: repeat(8, minmax(120px, 1fr)); + } +} + +/* Di atas 1280px: Tidak perlu scroll horizontal */ +@media (min-width: 1280px) { + .data-table-scroll-wrapper { + overflow-x: hidden; + } + + .data-table-body { + min-width: 100%; + overflow-x: hidden; + } + + .data-table-header, + .data-table-row { + width: 100%; + grid-template-columns: repeat(8, 1fr); + } +} + +@media (max-width: 768px) { + .data-table-header, .data-table-row { + width: 1290px; + grid-template-columns: repeat(8, minmax(100px, 1fr)); + } +} + + /* LIGHT MODE */ .light .data-table-container { background-color: #ffffff; @@ -521,18 +637,17 @@ button:hover { background-color: #2a2a2a; } -/* Responsive Design */ -@media (max-width: 768px) { - .data-table-header, .data-table-row { - grid-template-columns: 1fr 1fr 1fr 1fr; /* Kurangi jumlah kolom di mobile */ - font-size: 14px; - } -} - /* ======================= */ /* DEVICE TABLE GRID STYLE */ /* ======================= */ +/* Wrapper untuk memungkinkan scroll horizontal */ +.device-table-scroll-wrapper { + overflow-x: auto; + width: 100%; + position: relative; +} + .device-table-container { display: flex; flex-direction: column; @@ -542,18 +657,37 @@ button:hover { transition: background-color 0.3s, color 0.3s; } -.device-table-header, .device-table-row { +/* Header tetap di atas saat scroll */ +.device-table-header { + position: sticky; + top: 0; + background-color: inherit; + z-index: 10; + font-weight: bold; display: grid; grid-template-columns: 3fr 3fr 2fr; - gap: 10px; - padding: 12px; + gap: 5px; + padding: 6px; text-align: center; - align-items: center; + width: 800px; } -/* HEADER */ -.device-table-header { - font-weight: bold; +/* Pastikan body bisa scroll */ +.device-table-body { + display: block; + min-width: 800px; /* Memastikan scroll horizontal muncul */ + overflow-y: auto; + max-height: 400px; +} + +/* Row styling */ +.device-table-row { + display: grid; + grid-template-columns: 3fr 3fr 2fr; + gap: 5px; + padding: 6px; + text-align: center; + align-items: center; } /* LIGHT MODE */ @@ -614,10 +748,150 @@ button:hover { color: red; } +@media (max-width: 1023px) { + .device-table-header, + .device-table-row { + width: 800px; /* Memastikan tabel lebih lebar agar scroll muncul */ + grid-template-columns: repeat(3, minmax(150px, 1fr)); + } +} + +/* Di atas 1024px: Tidak perlu scroll horizontal */ +@media (min-width: 1024px) { + .device-table-scroll-wrapper { + overflow-x: hidden; /* Hilangkan scroll horizontal */ + } + + .device-table-body { + min-width: 100%; /* Gunakan lebar penuh */ + overflow-x: hidden; + } + + .device-table-header, + .device-table-row { + width: 100%; /* Gunakan lebar penuh */ + grid-template-columns: 1fr 1fr 1fr; + } +} + /* Responsive Design */ @media (max-width: 768px) { - .device-table-header, .device-table-row { - grid-template-columns: 1fr 1fr; - font-size: 14px; + .device-table-header, + .device-table-row { + width: 800px; /* Memastikan tabel lebih lebar dari layar agar scroll muncul */ + grid-template-columns: repeat(3, minmax(150px, 1fr)); } -} \ No newline at end of file +} + +/* Untuk semua tabel di bawah 768px */ +@media (max-width: 768px) { + .metrics-header, + .metrics-row, + .data-table-header, + .data-table-row, + .device-table-header, + .device-table-row { + font-size: 12px; /* Kurangi ukuran font */ + padding: 5px; /* Kurangi padding */ + } + + /* Pastikan tabel tidak terlalu lebar */ + .metrics-header, .metrics-row, + .data-table-header, .data-table-row, + .device-table-header, .device-table-row { + grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); + } + + /* Jika tabel terlalu besar, pastikan bisa di-scroll */ + .metrics-container, + .data-table-container, + .device-table-container { + overflow-x: auto; + } + + .metrics-body, + .data-table-body, + .device-table-body { + max-height: 300px; /* Kurangi tinggi tabel */ + overflow-y: auto; + } + + /* Kurangi ukuran ikon agar tidak mendominasi */ + .header-item svg, + .row-item svg { + width: 14px; + height: 14px; + } +} + +/* ======================= */ +/* SCROLLBAR STYLING */ +/* ======================= */ + +/* Styling untuk scrollbar horizontal & vertical */ +.metrics-scroll-wrapper::-webkit-scrollbar, +.data-table-scroll-wrapper::-webkit-scrollbar, +.device-table-scroll-wrapper::-webkit-scrollbar, +.metrics-body::-webkit-scrollbar, +.data-table-body::-webkit-scrollbar, +.device-table-body::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +.metrics-scroll-wrapper::-webkit-scrollbar-track, +.data-table-scroll-wrapper::-webkit-scrollbar-track, +.device-table-scroll-wrapper::-webkit-scrollbar-track, +.metrics-body::-webkit-scrollbar-track, +.data-table-body::-webkit-scrollbar-track, +.device-table-body::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 4px; +} + +.metrics-scroll-wrapper::-webkit-scrollbar-thumb, +.data-table-scroll-wrapper::-webkit-scrollbar-thumb, +.device-table-scroll-wrapper::-webkit-scrollbar-thumb, +.metrics-body::-webkit-scrollbar-thumb, +.data-table-body::-webkit-scrollbar-thumb, +.device-table-body::-webkit-scrollbar-thumb { + background: #888; + border-radius: 4px; +} + +.metrics-scroll-wrapper::-webkit-scrollbar-thumb:hover, +.data-table-scroll-wrapper::-webkit-scrollbar-thumb:hover, +.device-table-scroll-wrapper::-webkit-scrollbar-thumb:hover, +.metrics-body::-webkit-scrollbar-thumb:hover, +.data-table-body::-webkit-scrollbar-thumb:hover, +.device-table-body::-webkit-scrollbar-thumb:hover { + background: #555; +} + +/* Dark mode scrollbar */ +.dark .metrics-scroll-wrapper::-webkit-scrollbar-track, +.dark .data-table-scroll-wrapper::-webkit-scrollbar-track, +.dark .device-table-scroll-wrapper::-webkit-scrollbar-track, +.dark .metrics-body::-webkit-scrollbar-track, +.dark .data-table-body::-webkit-scrollbar-track, +.dark .device-table-body::-webkit-scrollbar-track { + background: #2a2a2a; +} + +.dark .metrics-scroll-wrapper::-webkit-scrollbar-thumb, +.dark .data-table-scroll-wrapper::-webkit-scrollbar-thumb, +.dark .device-table-scroll-wrapper::-webkit-scrollbar-thumb, +.dark .metrics-body::-webkit-scrollbar-thumb, +.dark .data-table-body::-webkit-scrollbar-thumb, +.dark .device-table-body::-webkit-scrollbar-thumb { + background: #666; +} + +.dark .metrics-scroll-wrapper::-webkit-scrollbar-thumb:hover, +.dark .data-table-scroll-wrapper::-webkit-scrollbar-thumb:hover, +.dark .device-table-scroll-wrapper::-webkit-scrollbar-thumb:hover, +.dark .metrics-body::-webkit-scrollbar-thumb:hover, +.dark .data-table-body::-webkit-scrollbar-thumb:hover, +.dark .device-table-body::-webkit-scrollbar-thumb:hover { + background: #444; +} diff --git a/src/components/dashboard/AppDevice.jsx b/src/components/dashboard/AppDevice.jsx index 4658261..cfb8158 100644 --- a/src/components/dashboard/AppDevice.jsx +++ b/src/components/dashboard/AppDevice.jsx @@ -3,39 +3,41 @@ import { FaCheckCircle, FaTimesCircle } from "react-icons/fa"; const AppDevice = ({ devices, theme }) => { return ( -
- {/* Header */} -
-
IP Address
-
Device Name
-
Status
-
+
+
+ {/* Header */} +
+
IP Address
+
Device Name
+
Status
+
- {/* Body */} -
- {devices.map((device) => ( -
-
{device.ip}
-
{device.name}
-
- {device.is_online === "Online" ? ( - <> - - Online - - ) : ( - <> - - Offline - - )} + {/* Body */} +
+ {devices.map((device) => ( +
+
{device.ip}
+
{device.name}
+
+ {device.is_online === "Online" ? ( + <> + + Online + + ) : ( + <> + + Offline + + )} +
-
- ))} + ))} +
); diff --git a/src/components/dashboard/LatestData.jsx b/src/components/dashboard/LatestData.jsx index de2b5f5..8087248 100644 --- a/src/components/dashboard/LatestData.jsx +++ b/src/components/dashboard/LatestData.jsx @@ -1,58 +1,58 @@ -/* eslint-disable react/prop-types */ -import { FaDownload, FaUpload, FaTachometerAlt } from 'react-icons/fa'; -import { Card, Col, Row } from 'react-bootstrap'; +// /* eslint-disable react/prop-types */ +// import { FaDownload, FaUpload, FaTachometerAlt } from 'react-icons/fa'; +// import { Card, Col, Row } from 'react-bootstrap'; -const LatestData = ({ data, theme }) => { - const latestData = data.length > 0 ? data[data.length - 20] : null; +// const LatestData = ({ data, theme }) => { +// const latestData = data.length > 0 ? data[data.length - 20] : null; - if (!latestData) return null; +// if (!latestData) return null; - return ( - - - -

Latest Data

-
- - - - - - - Latest Download - - - {(latestData.data.download.bandwidth / 1000000).toFixed(2)} Mbps - - - - - - - - Latest Upload - - - {(latestData.data.upload.bandwidth / 1000000).toFixed(2)} Mbps - - - - - - - - Latest Ping - - - {(latestData.data.ping.latency).toFixed(2)} ms - - - - - -
-
- ); -}; +// return ( +// +// +// +//

Latest Data

+//
+// +// +// +// +// +// +// Latest Download +// +// +// {(latestData.data.download.bandwidth / 1000000).toFixed(2)} Mbps +// +// +// +// +// +// +// +// Latest Upload +// +// +// {(latestData.data.upload.bandwidth / 1000000).toFixed(2)} Mbps +// +// +// +// +// +// +// +// Latest Ping +// +// +// {(latestData.data.ping.latency).toFixed(2)} ms +// +// +// +// +// +//
+//
+// ); +// }; -export default LatestData; +// export default LatestData; diff --git a/src/components/dashboard/MetricsTable.jsx b/src/components/dashboard/MetricsTable.jsx index 1ff7526..a78c8fc 100644 --- a/src/components/dashboard/MetricsTable.jsx +++ b/src/components/dashboard/MetricsTable.jsx @@ -1,32 +1,35 @@ +/* eslint-disable react/prop-types */ const MetricsTable = ({ metrics = [], theme }) => { return (
-
-
Monitor Name
-
URL
-
Response Time (ms)
-
Status
-
Certificate Valid
-
Cert Days Remaining
-
-
- {metrics.map((item, index) => ( -
-
{item.monitor_name}
-
- - {item.monitor_url} - +
+
+
Monitor Name
+
URL
+
Response Time (ms)
+
Status
+
Certificate Valid
+
Cert Days Remaining
+
+
+ {metrics.map((item, index) => ( +
+
{item.monitor_name}
+ +
{item.response_time} ms
+
+ {item.status === 1 ? "UP" : item.status === 0 ? "DOWN" : item.status === 2 ? "PENDING" : "MAINTENANCE"} +
+
{item.cert_valid === 1 ? "Yes" : "No"}
+
{item.cert_days_remaining}
-
{item.response_time} ms
-
- {item.status === 1 ? "UP" : item.status === 0 ? "DOWN" : item.status === 2 ? "PENDING" : "MAINTENANCE"} -
-
{item.cert_valid === 1 ? "Yes" : "No"}
-
{item.cert_days_remaining}
-
- ))} + ))} +
); diff --git a/src/components/dashboard/TableData.jsx b/src/components/dashboard/TableData.jsx index e097255..1c64291 100644 --- a/src/components/dashboard/TableData.jsx +++ b/src/components/dashboard/TableData.jsx @@ -1,61 +1,54 @@ +/* eslint-disable react/prop-types */ import { format } from "date-fns"; import { FaDownload, FaUpload, FaTachometerAlt } from "react-icons/fa"; const DataTable = ({ data = [], theme }) => { return (
- {/* Header */} -
-
No
-
- Bytes -
-
- Elapsed -
-
- Bytes -
-
- Elapsed -
-
- Jitter -
-
Time
-
Server
-
- - {/* Body */} -
- {data.map((item, index) => ( -
-
{index + 1}
-
- {(item.data.download.bytes / 1048576).toFixed(1)} Mbps -
-
- {(item.data.download.elapsed / 1000).toFixed(2)}s -
-
- {(item.data.upload.bytes / 1048576).toFixed(1)} Mbps -
-
- {(item.data.upload.elapsed / 1000).toFixed(2)}s -
-
{item.data.ping.jitter.toFixed(1)}ms
-
- {format(new Date(item.datetime), "eeee, d MMMM yyyy HH:mm:ss")} -
-
- {item.data.isp} ({item.data.server.name},{" "} - {item.data.server.location}) -
+
+ {/* Header */} +
+
No
+
+ Bytes
- ))} +
+ Elapsed +
+
+ Bytes +
+
+ Elapsed +
+
+ Jitter +
+
Time
+
Server
+
+ + {/* Body */} +
+ {data.map((item, index) => ( +
+
{index + 1}
+
{(item.data.download.bytes / 1048576).toFixed(1)} Mbps
+
{(item.data.download.elapsed / 1000).toFixed(2)}s
+
{(item.data.upload.bytes / 1048576).toFixed(1)} Mbps
+
{(item.data.upload.elapsed / 1000).toFixed(2)}s
+
{item.data.ping.jitter.toFixed(1)}ms
+
{format(new Date(item.datetime), "eeee, d MMMM yyyy HH:mm:ss")}
+
+ {item.data.isp} ({item.data.server.name}, {item.data.server.location}) +
+
+ ))} +
); }; export default DataTable; + diff --git a/src/components/layout/Navbar.jsx b/src/components/layout/Navbar.jsx index b80d5e9..9310953 100644 --- a/src/components/layout/Navbar.jsx +++ b/src/components/layout/Navbar.jsx @@ -1,4 +1,5 @@ -import { Navbar, Nav, Modal, Button } from 'react-bootstrap'; +/* eslint-disable react/prop-types */ +import { Navbar, Nav, Modal } from 'react-bootstrap'; import { FaChartLine, FaSun, FaMoon, FaBars } from 'react-icons/fa'; import { useState } from 'react'; import { Link } from 'react-router-dom'; @@ -11,7 +12,8 @@ const AppNavbar = ({ toggleTheme, theme }) => {
- Speedtest + + Speed Data