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
15 changes: 15 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"globals": "^16.0.0",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.17",
"typescript": "^5.9.3",
"vite": "^6.3.5"
}
}
5 changes: 2 additions & 3 deletions frontend/src/App.jsx → frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./pages/Home"
import Login from "./pages/Login";
import Signup from "./pages/Signup";
import Dashboard from "./pages/Dashboard";
import History from "./pages/History";
import ProtectedRoute from "./components/ProtectedRoute";

function App() {

function App(): React.JSX.Element {
return (
<BrowserRouter>
<Routes>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React, { useState, useEffect } from 'react';
import { Loader, Code, Shield } from 'lucide-react';
import { Loader, Code } from 'lucide-react';

// The main App component for the loading page.
const Load = () => {
const Load: React.FC = () => {
// State variables to manage the loading status and the dynamic activity log.
const [statusMessage, setStatusMessage] = useState('Initializing system...');
const [activityLog, setActivityLog] = useState(['']);
const [statusMessage, setStatusMessage] = useState<string>('Initializing system...');
const [activityLog, setActivityLog] = useState<string[]>(['']);

// An array of messages to simulate different, non-finite loading stages.
const loadingMessages = [
const loadingMessages: string[] = [
'Establishing secure connection...',
'Verifying cryptographic keys...',
'Compiling core modules...',
Expand All @@ -26,7 +26,7 @@ const Load = () => {
];

// An array of fake log lines for the activity log.
const logLines = [
const logLines: string[] = [
'> systemd start networkd...',
'[ OK ] Found new protocol stack on iface lo',
'[ INFO ] Establishing connection to gateway...',
Expand All @@ -52,8 +52,8 @@ const Load = () => {

// The useEffect hook simulates the continuous loading process.
useEffect(() => {
let messageInterval;
let logInterval;
let messageInterval: number;
let logInterval: number;

// Interval for updating the status message.
messageInterval = setInterval(() => {
Expand Down Expand Up @@ -104,9 +104,9 @@ const Load = () => {
{/* The new dynamic activity log container */}
<div className="w-full h-40 bg-black/40 border border-green-400/20 rounded-lg p-4 shadow-inner overflow-hidden">
<pre className="text-sm text-left leading-tight whitespace-pre-wrap break-words">
{activityLog.map((line, index) => (
<div key={index} className="opacity-70 transition-all duration-300 transform translate-y-0">{line}</div>
))}
{activityLog.map((line: string, index: number) => (
<div key={index} className="opacity-70 transition-all duration-300 transform translate-y-0">{line}</div>
))}
{/* Blinking cursor for terminal vibe */}
<span className="animate-pulse">_</span>
</pre>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
import React, { useState } from 'react';
import { Link, useNavigate } from "react-router-dom";
import api from "../components/api";LayoutDashboard
// import api from "./api";
import { Shield, Home, History, Info, UserCircle, LogOut, X, LayoutDashboard } from 'lucide-react';
import { handleError } from './utils';
// import { handleError } from './utils';

interface UserData {
firstName?: string;
email?: string;
scansDone?: number;
}

// The Navbar component is designed to be self-contained and reusable.
// It includes a logo, navigation links, and a user profile card.
const Navbar = () => {
const navigate = useNavigate();
const Navbar: React.FC = () => {
const navigate = useNavigate();

const Logout = () => {
const Logout = (): void => {
localStorage.clear();
setTimeout(() => {
navigate("/login");
}, 1500);
};

// State to manage the visibility of the profile card.
const [showProfileCard, setShowProfileCard] = useState(false);
const [userData, setUserData] = useState({});
const [showProfileCard, setShowProfileCard] = useState<boolean>(false);
const [userData] = useState<UserData>({});

const fetchUserData = async () => {
try {
const res = await api.get('/api/user/profile');
const data = await res.data;
setUserData(data);
} catch (err) {
handleError('Error fetching user data:', err);
setTimeout(() => {
Logout();
navigate("/login");
}, 1500);
}
};
// const fetchUserData = async (): Promise<void> => {
// try {
// const res = await api.get('/api/user/profile');
// const data: UserData = await res.data;
// setUserData(data);
// } catch (err) {
// handleError(err);
// setTimeout(() => {
// Logout();
// navigate("/login");
// }, 1500);
// }
// };

// The main JSX for the Navbar.
return (
Expand Down Expand Up @@ -63,7 +70,7 @@ const Navbar = () => {
<div className="relative">
<div
className="text-gray-400 hover:text-white transition-colors cursor-pointer"
onClick={() => setShowProfileCard(!showProfileCard)}
onClick={() => setShowProfileCard((prev) => !prev)}
>
<UserCircle className="w-8 h-8" />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
import { Navigate, useNavigate } from "react-router-dom";
import { jwtDecode } from "jwt-decode";
import { useState, useEffect } from "react";
import { useState, useEffect, type ReactNode } from "react";
import { handleError } from "./utils";
import Load from "./Loader";

function ProtectedRoute({ children }) {
const [isAuthorized, setIsAuthorized] = useState(null);
interface ProtectedRouteProps {
children: ReactNode;
}

interface DecodedToken {
exp: number;
}

function ProtectedRoute({ children }: ProtectedRouteProps): React.JSX.Element {
const [isAuthorized, setIsAuthorized] = useState<boolean | null>(null);
const navigate = useNavigate();

useEffect(() => {
auth().catch(() => setIsAuthorized(false));
}, []);

const auth = async () => {
const auth = async (): Promise<void> => {
const token = localStorage.getItem("ACCESS_TOKEN");
if (!token) {
setIsAuthorized(false);
return;
}
const decoded = jwtDecode(token);
const decoded = jwtDecode(token) as DecodedToken;
const tokenExpiration = decoded.exp;
const now = Date.now() / 1000;

Expand All @@ -39,7 +47,7 @@ function ProtectedRoute({ children }) {
return <Load />;
}

return isAuthorized ? children : <Navigate to="/login" />;
return isAuthorized ? <>{children}</> : <Navigate to="/login" />;
}

export default ProtectedRoute;
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import axios from "axios";
import axios, { type InternalAxiosRequestConfig } from "axios";

const api = axios.create({
baseURL: import.meta.env.VITE_API_URL,
});

api.interceptors.request.use(
(config) => {
(config: InternalAxiosRequestConfig) => {
const token = localStorage.getItem("ACCESS_TOKEN");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
(error: unknown) => {
return Promise.reject(error);
}
);
Expand Down
13 changes: 0 additions & 13 deletions frontend/src/components/utils.js

This file was deleted.

14 changes: 14 additions & 0 deletions frontend/src/components/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { toast } from "react-toastify";

export const handleSuccess = (msg: string): void => {
toast.success(msg, {
position: "top-right",
});
};

export const handleError = (msg: string | Error | unknown): void => {
const message = typeof msg === 'string' ? msg : msg instanceof Error ? msg.message : 'An error occurred';
toast.error(message, {
position: "top-right",
});
};
4 changes: 2 additions & 2 deletions frontend/src/main.jsx → frontend/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
import App from './App'

createRoot(document.getElementById('root')).render(
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
Expand Down
Loading