14/02/2025

This commit is contained in:
damarrsyh 2025-02-14 16:45:12 +07:00
parent 1e135f763f
commit 760aec0638
18 changed files with 152 additions and 116 deletions

View File

@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"dev": "vite --host",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview",

View File

@ -1,40 +1,78 @@
/* Navbar styling jika diperlukan */
body {
padding-top: 56px; /* Sesuaikan dengan tinggi Navbar */
padding-left: 250px; /* Sesuaikan dengan tinggi Navbar */
font-family: 'Poppins', sans-serif !important;
transition: background-color 0.3s, color 0.3s;
padding-top: 70px; /* Sesuaikan dengan tinggi Navbar */
padding-left: 250px;
}
.navbar {
background-color: #ffffff !important;
body.light {
background-color: #ffffff;
color: #1d1d1d;
}
body.dark {
background-color: #161616;
color: #ffffff;
}
.navbar-app {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 10px;
padding-bottom: 10px;
font-family: 'Poppins', sans-serif !important;
}
/* Tambahan untuk Bootstrap override */
.bg-light {
background-color: #f1f1f1 !important;
.light.navbar-app {
background-color: #ffffff;
transition: background-color 0.3s, color 0.3s;
}
.text-dark {
color: #fff !important;
.light.navbar-brand {
color: #161616;
transition: background-color 0.3s, color 0.3s;
}
.dark.navbar-app {
background-color: #161616;
transition: background-color 0.3s, color 0.3s;
}
.dark.navbar-brand {
transition: background-color 0.3s, color 0.3s;
color: #ffffff;
}
button {
padding: 4px 8px;
border: none;
cursor: pointer;
background-color: #474747;
color: white;
border-radius: 4px;
}
button:hover {
opacity: 0.8;
}
/* Styling Sidebar */
.sidebar {
.sidebar-app {
width: 250px;
height: 100vh;
position: fixed;
left: 0;
top: 0;
padding-top: 75px;
overflow-y: auto;
border-right: 1px solid #ddd;/* Warna sidebar */
padding-top: 100px;
overflow-y: auto;
transition: background-color 0.3s, color 0.3s;
/* border-right: 1px solid #414141; */
}
/* Styling Default Link */
.sidebar .nav-link {
margin: 0 15px 0 15px;
.sidebar-app .nav-link {
margin: 5px 15px;
font-size: 16px;
padding: 12px 15px;
border-radius: 5px;
@ -43,16 +81,13 @@ body {
}
/* Hover Effect */
.sidebar .nav-link:hover {
.sidebar-app .nav-link:hover {
background-color: rgba(255, 255, 255, 0.2);
}
/* Halaman yang aktif */
.sidebar .nav-link.active {
background-color:rgb(65, 157, 255); /* Warna biru saat aktif */
.sidebar-app .nav-link.active {
background-color:rgb(71, 71, 71); /* Warna biru saat aktif */
color: white !important;
font-weight: bold;
}

View File

@ -6,29 +6,39 @@ import Dashboard from './pages/Dashboard';
import DevicesPage from './pages/DevicesPage';
import ReportPage from "./pages/ReportPage";
import SettingsPage from "./pages/SettingsPage";
import { useEffect, useState } from "react";
import './App.css';
function App() {
const [theme, setTheme] = useState(localStorage.getItem("theme") || "light");
useEffect(() => {
document.body.className = theme;
localStorage.setItem("theme", theme);
}, [theme]);
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === "light" ? "dark" : "light"));
};
return (
<Router>
<div className="flex">
<Sidebar />
<div className="w-full">
<Navbar />
<div className="content p-4">
<Routes>
<Route path="/" element={<Navigate to="/dashboard" />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/devices" element={<DevicesPage />} />
<Route path="/reports" element={<ReportPage />} />
<Route path="/settings" element={<SettingsPage />} />
</Routes>
</div>
<div className={`app-container ${theme}`}>
<Navbar toggleTheme={toggleTheme} theme={theme} className={`${theme}`}/>
<Sidebar />
<div className={`main-content ${theme}`}>
<Routes>
<Route path="/" element={<Navigate to="/dashboard" />}/>
<Route path="/dashboard" element={<Dashboard />}/>
<Route path="/devices" element={<DevicesPage />}/>
<Route path="/reports" element={<ReportPage />}/>
<Route path="/settings" element={<SettingsPage />}/>
</Routes>
</div>
</div>
</div>
</Router>
);
}
export default App;

View File

@ -15,37 +15,37 @@ const DownloadChart = ({ data }) => {
chart: {
height: 250,
type: 'area',
background: '#ececec',
background: '#1e1e1e',
toolbar: { show: false },
},
dataLabels: { enabled: false },
stroke: {
curve: 'smooth',
colors: ['#28a745'],
colors: ['#4CAF50'],
},
xaxis: {
type: 'datetime',
labels: { style: { fontFamily: 'Poppins, sans-serif' } },
axisBorder: { color: '#555555' },
axisTicks: { color: '#555555' },
labels: { style: { fontFamily: 'Poppins, sans-serif', colors: '#ffffff' } },
axisBorder: { color: '#777777' },
axisTicks: { color: '#777777' },
},
yaxis: {
labels: { style: { fontFamily: 'Poppins, sans-serif' } },
labels: { style: { fontFamily: 'Poppins, sans-serif', colors: '#ffffff' } },
},
grid: {
borderColor: '#555555',
borderColor: '#777777',
strokeDashArray: 4,
},
tooltip: {
theme: 'light',
theme: 'dark',
x: { format: 'dd/MM/yy HH:mm' },
y: { formatter: (val) => `${val} Mbps` },
},
colors: ['#28a745'],
colors: ['#4CAF50'],
fill: {
type: 'gradient',
gradient: {
shade: 'light',
shade: 'dark',
type: 'vertical',
gradientToColors: ['#28a745'],
stops: [0, 100],

View File

@ -16,7 +16,7 @@ const PingChart = ({ data }) => {
chart: {
height: 250,
type: 'area',
background: '#ececec',
background: '#1e1e1e',
toolbar: { show: false },
},
dataLabels: { enabled: false },
@ -26,15 +26,15 @@ const PingChart = ({ data }) => {
},
xaxis: {
type: 'datetime',
labels: { style: { fontFamily: 'Poppins, sans-serif' } },
axisBorder: { color: '#555555' },
axisTicks: { color: '#555555' },
labels: { style: { fontFamily: 'Poppins, sans-serif', colors: '#ffffff' } },
axisBorder: { color: '#777777' },
axisTicks: { color: '#777777' },
},
yaxis: {
labels: { style: { fontFamily: 'Poppins, sans-serif' } },
labels: { style: { fontFamily: 'Poppins, sans-serif', colors: '#ffffff' } },
},
grid: {
borderColor: '#555555',
borderColor: '#777777',
strokeDashArray: 4,
},
tooltip: {

View File

@ -15,7 +15,7 @@ const UploadChart = ({ data }) => {
chart: {
height: 250,
type: 'area',
background: '#ececec',
background: '#1e1e1e',
toolbar: { show: false },
},
dataLabels: { enabled: false },
@ -25,15 +25,15 @@ const UploadChart = ({ data }) => {
},
xaxis: {
type: 'datetime',
labels: { style: { fontFamily: 'Poppins, sans-serif' } },
axisBorder: { color: '#555555' },
axisTicks: { color: '#555555' },
labels: { style: { fontFamily: 'Poppins, sans-serif', colors: '#ffffff' } },
axisBorder: { color: '#777777' },
axisTicks: { color: '#777777' },
},
yaxis: {
labels: { style: { fontFamily: 'Poppins, sans-serif' } },
labels: { style: { fontFamily: 'Poppins, sans-serif', colors: '#ffffff' } },
},
grid: {
borderColor: '#555555',
borderColor: '#777777',
strokeDashArray: 4,
},
tooltip: {

View File

@ -5,7 +5,7 @@ import { FaCheckCircle, FaTimesCircle } from "react-icons/fa";
const AppDevice = ({ devices }) => {
return (
<>
<Table striped bordered hover responsive style={{ fontFamily: 'Poppins, sans-serif' }}>
<Table striped bordered hover responsive>
<thead>
<tr>
<th>IP Address</th>

View File

@ -24,7 +24,7 @@ const AverageData = ({ data }) => {
return (
<Row>
<Col md={4}>
<Card className="mb-4 shadow-sm">
<Card className="mb-4 shadow-sm bg-dark text-light">
<Card.Header as="h6">
<FaDownload style={{ marginRight: '10px', width: '15px', color: '#28a745' }} />
Average Download
@ -35,7 +35,7 @@ const AverageData = ({ data }) => {
</Card>
</Col>
<Col md={4}>
<Card className="mb-4 shadow-sm">
<Card className="mb-4 shadow-sm bg-dark text-light">
<Card.Header as="h6">
<FaUpload style={{ marginRight: '10px', width: '15px', color: '#00bcd4' }} />
Average Upload
@ -46,7 +46,7 @@ const AverageData = ({ data }) => {
</Card>
</Col>
<Col md={4}>
<Card className="mb-4 shadow-sm">
<Card className="mb-4 shadow-sm bg-dark text-light">
<Card.Header as="h6">
<FaTachometerAlt style={{ marginRight: '10px', width: '15px', color: '#ffc107' }} />
Average Ping

View File

@ -5,12 +5,12 @@ import UploadChart from '../charts/UploadChart';
import PingChart from '../charts/PingChart';
import { FaDownload, FaUpload, FaTachometerAlt } from 'react-icons/fa';
const Chart = ({ data }) => {
const Chart = ({ data, theme }) => {
return (
<>
<Row>
<Col md="4">
<Card className="mb-4 shadow-sm">
<Card className={`mb-4 shadow-sm ${theme}`}>
<Card.Body>
<Card.Title>
<FaDownload style={{ marginRight: '10px', color: '#28a745'}} />
@ -21,7 +21,7 @@ const Chart = ({ data }) => {
</Card>
</Col>
<Col md="4">
<Card className="mb-4 shadow-sm">
<Card className={`mb-4 shadow-sm ${theme}`}>
<Card.Body>
<Card.Title>
<FaUpload style={{ marginRight: '10px', width: '15px', color: '#00bcd4' }} />
@ -32,7 +32,7 @@ const Chart = ({ data }) => {
</Card>
</Col>
<Col md="4">
<Card className="mb-4 shadow-sm">
<Card className={`mb-4 shadow-sm ${theme}`}>
<Card.Body>
<Card.Title>
<FaTachometerAlt style={{ marginRight: '10px', width: '15px', color: '#ffc107' }} />

View File

@ -2,21 +2,21 @@
import { FaDownload, FaUpload, FaTachometerAlt } from 'react-icons/fa';
import { Card, Col, Row } from 'react-bootstrap';
const LatestData = ({ data }) => {
const LatestData = ({ data, theme }) => {
const latestData = data.length > 0 ? data[data.length - 20] : null;
if (!latestData) return null;
return (
<Row>
<Card className='mb-4 shadow-sm border-light bg-dark text-light'>
<Card className='mb-4 shadow-sm border-light'>
<Card.Header>
<h3 style={{marginTop: '20px', marginBottom: '20px'}}>Latest Data</h3>
</Card.Header>
<Card.Body>
<Row>
<Col md={4}>
<Card className="mb-4 shadow-sm bg-dark text-light">
<Card className={`${theme} mb-4 shadow-sm`}>
<Card.Header as="h6">
<FaDownload style={{ marginRight: '10px', width: '15px', color: '#28a745' }} />
Latest Download
@ -27,7 +27,7 @@ const LatestData = ({ data }) => {
</Card>
</Col>
<Col md={4}>
<Card className="mb-4 shadow-sm bg-dark text-light">
<Card className={`${theme} mb-4 shadow-sm`}>
<Card.Header as="h6">
<FaUpload style={{ marginRight: '10px', width: '15px', color: '#00bcd4' }} />
Latest Upload
@ -38,7 +38,7 @@ const LatestData = ({ data }) => {
</Card>
</Col>
<Col md={4}>
<Card className="mb-4 shadow-sm bg-dark text-light">
<Card className={`${theme} mb-4 shadow-sm`}>
<Card.Header as="h6">
<FaTachometerAlt style={{ marginRight: '10px', width: '15px', color: '#ffc107' }} />
Latest Ping

View File

@ -2,10 +2,10 @@ import Table from 'react-bootstrap/Table';
const MetricsTable = ({ metrics = [] }) => {
return (
<div style={{ overflowX: "auto", maxHeight: "400px", overflowY: "auto", position: "relative", borderTop: "1px solid #ddd" }}>
<Table striped bordered hover responsive style={{ fontFamily: 'Poppins, sans-serif', minWidth: "900px"}}>
<div style={{ overflowX: "auto", maxHeight: "450px", overflowY: "auto", position: "relative", borderTop: "1px solid #444", backgroundColor: "#1e1e1e" }}>
<Table striped bordered hover responsive variant='dark' style={{ fontFamily: 'Poppins, sans-serif', minWidth: "900px", backgroundColor: "#1e1e1e", color: "#ffffff" }}>
<thead>
<tr>
<tr style={{ backgroundColor: "#333333", color: "#ffffff" }}>
<th className="py-3 text-center align-middle">Monitor Name</th>
<th className="py-3 text-center align-middle">URL</th>
<th className="py-3 text-center align-middle">Response Time (ms)</th>
@ -16,9 +16,9 @@ const MetricsTable = ({ metrics = [] }) => {
</thead>
<tbody>
{metrics.map((item, index) => (
<tr key={index}>
<tr key={index} style={{ backgroundColor: "#222222", color: "#ffffff" }}>
<td>{item.monitor_name}</td>
<td><a href={item.monitor_url} target="_blank" rel="noopener noreferrer">{item.monitor_url}</a></td>
<td><a href={item.monitor_url} target="_blank" rel="noopener noreferrer" style={{ color: "#4CAF50" }}>{item.monitor_url}</a></td>
<td>{item.response_time} ms</td>
<td>
{item.status === 1 ? 'UP' :

View File

@ -3,11 +3,11 @@ import { Table } from 'react-bootstrap';
import { format } from 'date-fns';
import { FaDownload, FaUpload, FaTachometerAlt } from "react-icons/fa";
const DataTable = ({ data = [] }) => {
const DataTable = ({ data = [], theme }) => {
return (
<div style={{ overflowX: "auto", maxHeight: "400px", overflowY: "auto", position: "relative", borderTop: "1px solid #ddd", marginBottom: "20px"}}>
<Table striped bordered hover responsive style={{ fontFamily: 'Poppins, sans-serif', minWidth: "900px"}}>
<thead className="bg-secondary text-center" style={{ position: "sticky", top: 0, zIndex: 10, backgroundColor: "#6c757d" }}>
<div style={{ overflowX: "auto", maxHeight: "400px", overflowY: "auto", position: "relative", marginBottom: "20px"}}>
<Table striped bordered hover responsive style={{ fontFamily: 'Poppins, sans-serif', minWidth: "900px"}} className={`${theme}`}>
<thead className="bg-secondary text-center" style={{ position: "sticky", top: 0, zIndex: 10, backgroundColor: "#343a40" }}>
<tr>
<th className="py-3 text-center align-middle">No</th>
<th className="py-3 text-center align-middle" style={{ color: '#28a745' }}>

View File

@ -1,30 +1,20 @@
import { Navbar, Nav} from 'react-bootstrap';
import { FaChartLine } from 'react-icons/fa';
import { Navbar, Nav } from 'react-bootstrap';
import { FaChartLine, FaSun, FaMoon } from 'react-icons/fa';
const AppNavbar = () => {
const AppNavbar = ({ toggleTheme, theme }) => {
return (
<Navbar
className="fixed-top w-100 shadow-sm d-flex justify-content-between"
className={`${theme} navbar-app fixed-top w-100 shadow-sm d-flex justify-content-between`}
style={{ zIndex: 1030, padding: '15px 20px' }}
>
<Navbar.Brand href="#home" style={{ marginLeft: '20px', fontWeight: 'bold'}}>
<Navbar.Brand className={`${theme}`} href="#home" style={{ marginLeft: '20px'}}>
<FaChartLine style={{ marginRight: '10px', color: '#b31e1e' }} />
Speedtest Tracker
Speedtest Tracker
</Navbar.Brand>
<Nav>
<Nav.Item className="d-flex align-items-center" style={{ fontWeight: 'bold' }}>
Damar
<img
src="/src/assets/react.svg"
alt="Profile"
style={{
width: '30px',
height: '30px',
borderRadius: '50%',
marginLeft: '10px'
}}
/>
</Nav.Item>
<Nav className="ml-auto" style={{ marginRight: '20px' }}>
<button onClick={toggleTheme} className="theme-toggle-btn">
{theme === 'light' ? <FaMoon size={20} /> : <FaSun size={20} />}
</button>
</Nav>
</Navbar>
);

View File

@ -2,13 +2,12 @@ import { Navbar, Nav } from 'react-bootstrap';
import { Link, useLocation } from 'react-router-dom';
import { FaTachometerAlt, FaDesktop, FaChartBar, FaCog } from "react-icons/fa";
const Sidebar = () => {
const Sidebar = ({theme}) => {
const location = useLocation();
return (
<div className='sidebar'>
<Navbar expand="lg" className="flex-column">
<Navbar expand="lg" className={`sidebar-app flex-column ${theme}`}>
<Nav className="flex-column w-100">
<Nav.Link
as={Link}
@ -43,7 +42,6 @@ const Sidebar = () => {
</Nav.Link>
</Nav>
</Navbar>
</div>
);
}

View File

@ -28,7 +28,7 @@ const Dashboard = () => {
if (loading) {
return (
<Container fluid className="text-center mt-5">
<Spinner animation="border" variant="danger" />
<Spinner animation="border" variant="info" />
</Container>
);
}
@ -42,7 +42,7 @@ const Dashboard = () => {
}
return (
<Container fluid className='d-flex' style={{ fontFamily: 'Poppins, sans-serif'}}>
<Container fluid className='flex'>
<Col className="p-3 ms-auto">
<h4 className='my-3'>Dashboard</h4>
<Row>

View File

@ -35,9 +35,8 @@ function DevicesPage() {
if (loading) {
return (
<Container fluid className="text-center mt-5 text-light">
<Spinner animation="border" variant="light" />
<p>Loading...</p>
<Container fluid className="text-center mt-5">
<Spinner animation="border" variant="info"/>
</Container>
);
}

View File

@ -1,14 +1,14 @@
import axios from 'axios';
const API_URL = import.meta.env.VITE_API_URL;
const API_JSON_URL = import.meta.env.VITE_API_JSON_URL;
const API_SPEED_URL = import.meta.env.VITE_API_SPEED_URL;
const API_PING_URL = import.meta.env.VITE_API_PING_URL;
const API_METRICS_URL = import.meta.env.VITE_API_METRICS_URL;
// console.log(import.meta.env.VITE_API_URL)
export const fetchSpeedTestData = async () => {
try {
const response = await axios.get(API_URL, {
const response = await axios.get(API_SPEED_URL, {
headers: {
'Author': 'vedeom',
},
@ -26,7 +26,7 @@ export const fetchSpeedTestData = async () => {
export const fetchActiveDevices = async () => {
try {
const response = await axios.get(API_JSON_URL);
const response = await axios.get(API_PING_URL);
// console.log('Devices API Response:', response.data);

View File

@ -2,5 +2,9 @@ import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()]
plugins: [react()],
server: {
host: '0.0.0.0',
port: 5173,
}
});