Skip to content
Open
Show file tree
Hide file tree
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
1,611 changes: 1,516 additions & 95 deletions new-frontend/frontend/package-lock.json

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions new-frontend/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/material": "^7.3.2",
"axios": "^1.11.0",
"chart.js": "^4.5.0",
"chartjs-adapter-date-fns": "^3.0.0",
"date-fns": "^4.1.0",
"lucide-react": "^0.542.0",
"react": "^19.1.0",
"react-chartjs-2": "^5.3.0",
"react-datepicker": "^8.4.0",
Expand All @@ -23,13 +27,17 @@
},
"devDependencies": {
"@eslint/js": "^9.30.1",
"@tailwindcss/postcss": "^4.1.12",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.6.0",
"autoprefixer": "^10.4.21",
"eslint": "^9.30.1",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"postcss": "^8.5.6",
"tailwindcss": "^4.1.12",
"vite": "^7.0.6"
}
}
6 changes: 6 additions & 0 deletions new-frontend/frontend/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// postcss.config.js
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
73 changes: 41 additions & 32 deletions new-frontend/frontend/src/App.css
Original file line number Diff line number Diff line change
@@ -1,42 +1,51 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
html, body, #root { height: 100%; }
html.dark #root {
max-width: none;
margin: 0;
padding: 0;
text-align: initial;
background: transparent;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
html.dark .bg-white {
background-color: #000 !important;
backdrop-filter: none !important;
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
/* Borders & dividers — darker in black theme */
html.dark .border-slate-200 { border-color: #262626 !important; } /* neutral-800 */
html.dark .divide-gray-100 > :not([hidden]) ~ :not([hidden]) {
border-color: #262626 !important; /* neutral-800 */
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
/* Text colors — flip dark text to white in black theme */
html.dark .text-slate-900 { color: #fff !important; } /* white */
html.dark .text-slate-700,
html.dark .text-slate-600 { color: #e5e7eb !important; } /* slate-200 */
html.dark .text-slate-500 { color: #cbd5e1 !important; } /* slate-300 */

/* Pills/chips used for anomaly counts */
html.dark .bg-rose-50 { background-color: rgba(244,63,94,0.25) !important; }
html.dark .text-rose-600 { color: #fecdd3 !important; } /* rose-200 */
html.dark .bg-emerald-50 { background-color: rgba(16,185,129,0.25) !important; }
html.dark .text-emerald-600 { color: #bbf7d0 !important; } /* emerald-200 */

.card {
padding: 2em;
/* Stronger shadow reads better on black */
html.dark .shadow-md { box-shadow: 0 12px 30px rgba(0,0,0,0.45) !important; }
/* --- Force hero and section titles to WHITE in dark mode --- */
html.dark #hero h1,
html.dark #hero p,
html.dark #datasets h1,
html.dark #datasets h2,
html.dark #datasets h3 {
color: #fff !important;
opacity: 1 !important; /* neutralize any faded styles */
}
html.dark .app-shell {
background-color: #000 !important;
background-image: none !important;
}

.read-the-docs {
color: #888;
html.dark body {
background-color: #000 !important;
}
30 changes: 9 additions & 21 deletions new-frontend/frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,21 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom';
import 'chartjs-adapter-date-fns';
import HomePage from './pages/HomePage';
import DashboardPage from './pages/DashboardPage';

// import DashboardLearn from './components/DashboardLearn'; //for learning

import FetchData from './components/FetchData';



function App() {
function App({ toggleTheme, isDarkMode }) {
return (
<BrowserRouter>
<div>
<h1>IoT Sensors Dashboard</h1>
<h3>Time-series Sensor Data and Correlation Analysis</h3>
<Routes>


<Route path="/sensorData1" element={<FetchData />} />
<Route path="/" element={<HomePage />} />
<Route path="/dashboard/:id" element={<DashboardPage />} />


</Routes>
</div>
<Routes>
<Route
path="/"
element={<HomePage toggleTheme={toggleTheme} isDarkMode={isDarkMode} />}
/>
<Route path="/sensorData1" element={<FetchData />} />
<Route path="/dashboard/:id" element={<DashboardPage />} />
</Routes>
</BrowserRouter>
);
}



export default App;
3 changes: 2 additions & 1 deletion new-frontend/frontend/src/components/Dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,5 @@ body {
padding: 0 30px 0 20px;
gap: 20px;
transition: all 0.1s ease-in-out;
}
}

73 changes: 73 additions & 0 deletions new-frontend/frontend/src/components/DatasetCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Link } from "react-router-dom";
import StatusBadge from "./StatusBadge";
import Icon from "./Icon";

const GRADIENT = {
temperature: "from-teal-600 to-teal-500",
humidity: "from-orange-600 to-amber-500",
air: "from-violet-600 to-fuchsia-500",
default: "from-slate-600 to-slate-500",
};

export default function DatasetCard({ dataset }) {
const { id, name, description, type, status, anomalies24h, gradient } = dataset;
const g = gradient || GRADIENT[type] || GRADIENT.default;

return (
<Link
to={`/dashboard/${encodeURIComponent(id)}`}
className="
group rounded-2xl overflow-hidden
bg-white border border-slate-200 shadow-md
transition-all hover:-translate-y-0.5 hover:shadow-xl
focus:outline-none focus:ring-4 focus:ring-indigo-300
dark:bg-black dark:border-neutral-800 dark:shadow-black/30
dark:focus:ring-indigo-800/40
"
>
{/* Top gradient header */}
<div className={`bg-gradient-to-r ${g} text-white`}>
<div className="flex items-center justify-between p-6">
<div className="flex items-center gap-3">
<span className="grid h-10 w-10 place-items-center rounded-xl bg-white/15">
<Icon name={type} />
</span>
<div>
<div className="text-[11px] uppercase tracking-wide opacity-90">Dataset</div>
<h3 className="text-2xl font-semibold">{name}</h3>
</div>
</div>
<StatusBadge status={status} />
</div>
<div className="h-2 bg-white/25" />
</div>

{/* Body */}
<div className="p-6 divide-y divide-gray-100 dark:divide-neutral-800">
<div className="pb-4">
<p className="text-slate-600 dark:text-white text-center">{description}</p>
</div>

<div className="py-4 flex items-center justify-between">
<span className="text-sm text-slate-500 dark:text-white">Last 24h anomalies</span>
<span
className={`
rounded-full px-2.5 py-1 text-sm font-semibold
${(anomalies24h ?? 0) > 0
? "bg-rose-50 text-rose-600 dark:bg-rose-600/30 dark:text-rose-200"
: "bg-emerald-50 text-emerald-600 dark:bg-emerald-600/30 dark:text-emerald-200"}
`}
>
{anomalies24h ?? 0}
</span>
</div>

<div className="pt-4">
<span className="inline-flex items-center gap-1 text-indigo-600 dark:text-indigo-300 font-medium group-hover:translate-x-0.5 transition-transform">
View dashboard →
</span>
</div>
</div>
</Link>
);
}
48 changes: 48 additions & 0 deletions new-frontend/frontend/src/components/DatasetsGrid.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import DatasetCard from "./DatasetCard";
import Section from "./Section";

function Skeleton() {
return (
<div className="grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
{[...Array(3)].map((_, i) => (
<div
key={i}
className="
rounded-2xl overflow-hidden
bg-white border border-slate-200 shadow-md
dark:bg-black dark:border-neutral-800
"
>
<div className="h-20 bg-slate-200 dark:bg-neutral-800 animate-pulse" />
<div className="p-6 space-y-3">
<div className="h-4 w-1/3 bg-slate-200 dark:bg-neutral-800 animate-pulse" />
<div className="h-4 w-2/3 bg-slate-200 dark:bg-neutral-800 animate-pulse" />
<div className="h-8 w-24 bg-slate-200 dark:bg-neutral-800 animate-pulse" />
</div>
</div>
))}
</div>
);
}

export default function DatasetsGrid({ datasets = [], loading = false, error = null }) {
return (
<Section id="datasets" title="Available Sensor Datasets" center className="pb-16">
{error && (
<div className="mb-6 rounded-lg bg-rose-50 text-rose-700 p-4 dark:bg-rose-600/20 dark:text-rose-200">
Could not load datasets. Please try again.
</div>
)}

{loading ? (
<Skeleton />
) : (
<div className="grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
{datasets.map((ds) => (
<DatasetCard key={ds.id} dataset={ds} />
))}
</div>
)}
</Section>
);
}
11 changes: 11 additions & 0 deletions new-frontend/frontend/src/components/Footer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function Footer() {
return (
<footer className="w-full border-t border-slate-200 dark:border-neutral-800 bg-transparent">
<div className="mx-auto max-w-7xl px-4 py-6">
<p className="text-center text-sm text-slate-500 dark:text-slate-400">
© 2025 Intelligent IoT · Built with React & Tailwind
</p>
</div>
</footer>
);
}
Loading