0% found this document useful (0 votes)
6 views24 pages

Document Track1

The document outlines a React application for a document tracking system, featuring user authentication, role-based dashboards, and document management functionalities. It includes components for user login, admin and user dashboards, and backend Flask routes for handling login, password changes, and document retrieval. The application utilizes local storage for token management and implements role checks to control access to different parts of the application.

Uploaded by

roomcard06780
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views24 pages

Document Track1

The document outlines a React application for a document tracking system, featuring user authentication, role-based dashboards, and document management functionalities. It includes components for user login, admin and user dashboards, and backend Flask routes for handling login, password changes, and document retrieval. The application utilizes local storage for token management and implements role checks to control access to different parts of the application.

Uploaded by

roomcard06780
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 24

Document_track/src/App.

jsx

import React, { useEffect, useState } from 'react';

import { BrowserRouter as Router, Routes, Route, Navigate, useNavigate } from 'react-router-


dom';

import Header from './Header.jsx';

import Footer from './Footer.jsx';

import Home from './pages/Home.jsx';

import LoginPage from './pages/LoginPage.jsx';

import DashboardPage from './pages/DashboardPage.jsx';

import AdminDashboard from './components/Dashboard/AdminDashboard.jsx';

import UploadPage from './pages/UploadPage.jsx';

import AlertsPage from './pages/AlertsPage.jsx';

import './index.css';

function App() {

const [user, setUser] = useState(null);

const [loading, setLoading] = useState(true);

useEffect(() => {

const token = localStorage.getItem('token');

if (token) {

fetch('http://localhost:5000/profile', {

headers: { Authorization: `Bearer ${token}` },

})

.then((res) => res.json())

.then((data) => {

if (data.username) setUser(data);

})
.finally(() => setLoading(false));

} else {

setLoading(false);

}, []);

const handleLogout = () => {

localStorage.removeItem('token');

setUser(null);

window.location.href = '/';

};

if (loading) return <div>Loading...</div>;

return (

<Router>

<div className="App">

<Header user={user} onLogout={handleLogout} />

<Routes>

<Route path="/" element={<Home />} />

<Route path="/login" element={<LoginPage />} />

<Route path="/dashboard" element={user ? <AdminDashboard /> : <Navigate


to="/login" />} />

<Route path="/upload" element={user ? <UploadPage /> : <Navigate to="/login" />} />

<Route path="/alerts" element={user ? <AlertsPage /> : <Navigate to="/login" />} />

</Routes>

<Footer />

</div>
</Router>

);

export default App;

Document_track/src/Pages/DashboardPage.jsx

import React, { useEffect, useState } from 'react';

import AdminDashboard from '../components/Dashboard/AdminDashboard';

import UserDashboard from '../components/Dashboard/UserDashboard';

const DashboardPage = () => {

const [role, setRole] = useState(null);

useEffect(() => {

const fetchRole = async () => {

const token = localStorage.getItem('token');

const res = await fetch('http://localhost:5000/get-role', {

headers: { Authorization: `Bearer ${token}` },

});

const data = await res.json();

setRole(data.role);

};

fetchRole();

}, []);

if (!role) return <p>Loading...</p>;


return role === 'admin' ? <AdminDashboard /> : <UserDashboard />;

};

export default DashboardPage;

Document_track/src/components/Doashboard/AdminDashboard.jsx

import React, { useEffect, useState } from 'react';

import './Dashboard.css';

const AdminDashboard = () => {

const [docs, setDocs] = useState([]);

const [search, setSearch] = useState('');

const [statusFilter, setStatusFilter] = useState('all');

useEffect(() => {

fetch('http://localhost:5000/documents', {

headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },

})

.then(res => res.json())

.then(setDocs);

}, []);

const toggleVisibility = async (id) => {

await fetch(`http://localhost:5000/documents/${id}/toggle-visibility`, {

method: 'PATCH',

headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },

});
setDocs(prev => prev.map(d => d.id === id ? { ...d, visible: !d.visible } : d));

};

const filteredDocs = docs.filter(doc => {

const matchesSearch = doc.name.toLowerCase().includes(search.toLowerCase());

const matchesStatus = statusFilter === 'all' || doc.status === statusFilter;

return matchesSearch && matchesStatus;

});

const total = docs.length;

const activeCount = docs.filter(d => d.status === 'active').length;

const expiredCount = docs.filter(d => d.status === 'expired').length;

return (

<div className="dashboard-container">

<h2>Admin Dashboard</h2>

<div className="summary-cards">

<div className="card total">Total: {total}</div>

<div className="card active">Active: {activeCount}</div>

<div className="card expired">Expired: {expiredCount}</div>

</div>

<div className="filter-section">

<input

type="text"

placeholder="Search documents..."

value={search}
onChange={(e) => setSearch(e.target.value)}

/>

<select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>

<option value="all">All</option>

<option value="active">Active</option>

<option value="expired">Expired</option>

</select>

</div>

<table className="dashboard-table">

<thead>

<tr>

<th>Name</th>

<th>Expiry</th>

<th>Status</th>

<th>Visible</th>

<th>Action</th>

</tr>

</thead>

<tbody>

{filteredDocs.map(doc => (

<tr key={doc.id}>

<td>{doc.name}</td>

<td>{doc.expiry_date}</td>

<td>

<span className={`status-tag ${doc.status}`}>{doc.status}</span>

</td>

<td>{doc.visible ? 'Yes' : 'No'}</td>


<td>

<button onClick={() => toggleVisibility(doc.id)}>

Toggle

</button>

</td>

</tr>

))}

</tbody>

</table>

</div>

);

};

export default AdminDashboard;

Document_track/src/components/Dashboard/UserDashboard.jsx

import React, { useEffect, useState } from 'react';

import './Dashboard.css';

const UserDashboard = () => {

const [docs, setDocs] = useState([]);

const [search, setSearch] = useState('');

const [statusFilter, setStatusFilter] = useState('all');

useEffect(() => {

fetch('http://localhost:5000/documents', {
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },

})

.then(res => res.json())

.then(setDocs);

}, []);

const filteredDocs = docs.filter(doc => {

const matchesSearch = doc.name.toLowerCase().includes(search.toLowerCase());

const matchesStatus = statusFilter === 'all' || doc.status === statusFilter;

return matchesSearch && matchesStatus;

});

const total = docs.length;

const activeCount = docs.filter(d => d.status === 'active').length;

const expiredCount = docs.filter(d => d.status === 'expired').length;

return (

<div className="dashboard-container">

<h2>My Documents</h2>

<div className="summary-cards">

<div className="card total">Total: {total}</div>

<div className="card active">Active: {activeCount}</div>

<div className="card expired">Expired: {expiredCount}</div>

</div>

<div className="filter-section">

<input
type="text"

placeholder="Search documents..."

value={search}

onChange={(e) => setSearch(e.target.value)}

/>

<select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>

<option value="all">All</option>

<option value="active">Active</option>

<option value="expired">Expired</option>

</select>

</div>

<table className="dashboard-table">

<thead>

<tr>

<th>Name</th>

<th>Expiry</th>

<th>Status</th>

</tr>

</thead>

<tbody>

{filteredDocs.map(doc => (

<tr key={doc.id}>

<td>{doc.name}</td>

<td>{doc.expiry_date}</td>

<td>

<span className={`status-tag ${doc.status}`}>{doc.status}</span>

</td>
</tr>

))}

</tbody>

</table>

</div>

);

};

export default UserDashboard;

Document_track/src/components/Login/Login.jsx

import React, { useState } from 'react';

import { useNavigate } from 'react-router-dom';

import './Login.css';

const Login = () => {

const [username, setUsername] = useState('');

const [password, setPassword] = useState('');

const [error, setError] = useState('');

const [success, setSuccess] = useState('');

const [attemptsLeft, setAttemptsLeft] = useState(5);

const [showChangePassword, setShowChangePassword] = useState(false);

const [currentPassword, setCurrentPassword] = useState('');

const [newPassword, setNewPassword] = useState('');

const [confirmPassword, setConfirmPassword] = useState('');

const navigate = useNavigate();

const handleLogin = async (e) => {


e.preventDefault();

setError('');

setSuccess('');

try {

const response = await fetch('http://localhost:5000/login', {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify({ username, password }),

});

const data = await response.json();

if (response.ok) {

localStorage.setItem('token', data.token);

navigate('/dashboard'); // Adjust route if needed

} else {

setError(data.message || 'Login failed');

if (data.attemptsLeft !== undefined) {

setAttemptsLeft(data.attemptsLeft);

} catch (err) {

setError('Network error. Please try again.');

};

const handleChangePassword = async (e) => {


e.preventDefault();

setError('');

setSuccess('');

if (newPassword !== confirmPassword) {

setError("New passwords don't match");

return;

try {

const response = await fetch('http://localhost:5000/change-password', {

method: 'POST',

headers: {

'Content-Type': 'application/json',

'Authorization': `Bearer ${localStorage.getItem('token')}`,

},

body: JSON.stringify({ currentPassword, newPassword }),

});

const data = await response.json();

if (response.ok) {

setSuccess('Password changed successfully!');

setShowChangePassword(false);

setCurrentPassword('');

setNewPassword('');

setConfirmPassword('');

} else {
setError(data.message || 'Password change failed');

} catch (err) {

setError('Network error. Please try again.');

};

return (

<div className="login-container">

<h2>Login to Document Tracker</h2>

{error && <div className="error-message">{error}</div>}

{success && <div className="success-message">{success}</div>}

{attemptsLeft < 5 && (

<div className="attempts-warning">

Attempts left today: {attemptsLeft}

</div>

)}

{!showChangePassword ? (

<form onSubmit={handleLogin}>

<div className="form-group">

<label>Employee ID:</label>

<input

type="text"

value={username}

onChange={(e) => setUsername(e.target.value)}

required

/>
</div>

<div className="form-group">

<label>Password:</label>

<input

type="password"

value={password}

onChange={(e) => setPassword(e.target.value)}

required

/>

</div>

<button type="submit" className="login-button">

Login

</button>

<button

type="button"

className="change-password-button"

onClick={() => setShowChangePassword(true)}

>

Change Password

</button>

</form>

):(

<form onSubmit={handleChangePassword}>

<h3>Change Password</h3>

<div className="form-group">

<label>Current Password:</label>

<input

type="password"
value={currentPassword}

onChange={(e) => setCurrentPassword(e.target.value)}

required

/>

</div>

<div className="form-group">

<label>New Password:</label>

<input

type="password"

value={newPassword}

onChange={(e) => setNewPassword(e.target.value)}

required

/>

</div>

<div className="form-group">

<label>Confirm New Password:</label>

<input

type="password"

value={confirmPassword}

onChange={(e) => setConfirmPassword(e.target.value)}

required

/>

</div>

<button type="submit" className="login-button">

Submit

</button>

<button

type="button"
className="cancel-button"

onClick={() => {

setShowChangePassword(false);

setError('');

setSuccess('');

}}

>

Cancel

</button>

</form>

)}

</div>

);

};

export default Login;

Document_track/src/pages/LoginPage.jsx

import React from 'react';

import Login from '../components/Login/Login';

const LoginPage = () => {

return (

<div className="login-page-container">

<Login />

</div>

);

};
export default LoginPage;

Document_track/backend/auth.py

from flask import Flask, request, jsonify

from flask_cors import CORS

from werkzeug.security import check_password_hash

import os

from werkzeug.utils import secure_filename

from database import create_connection, verify_token

from datetime import date, timedelta

from database import (

create_connection, get_user_by_username, get_user_by_id, create_user,


update_user_password,

record_login_attempt, get_login_attempts_count, generate_token,

verify_token

app = Flask(__name__)

CORS(app) # Enable Cross-Origin requests

MAX_ATTEMPTS_PER_DAY = 5

@app.route('/login', methods=['POST'])

def login():

data = request.get_json()
username = data.get('username')

password = data.get('password')

ip_address = request.remote_addr

user = get_user_by_username(username)

if not user:

return jsonify({'message': 'Invalid username or password', 'attemptsLeft':


MAX_ATTEMPTS_PER_DAY}), 401

# SECURE: Compare hashed password

if not check_password_hash(user['password_hash'], password):

record_login_attempt(user['id'], ip_address, False)

attempts = get_login_attempts_count(username, ip_address)

return jsonify({

'message': 'Invalid username or password',

'attemptsLeft': max(0, MAX_ATTEMPTS_PER_DAY - attempts)

}), 401

record_login_attempt(user['id'], ip_address, True)

token = generate_token(user['id'])

return jsonify({

'message': 'Login successful',

'token': token

})

@app.route('/change-password', methods=['POST'])

def change_password():

auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):

return jsonify({'message': 'Missing or invalid token'}), 401

token = auth_header.split(' ')[1]

user_id = verify_token(token)

if not user_id:

return jsonify({'message': 'Invalid or expired token'}), 401

data = request.get_json()

current_password = data.get('currentPassword')

new_password = data.get('newPassword')

user = get_user_by_id(user_id)

if not user:

return jsonify({'message': 'User not found'}), 404

if not check_password_hash(user['password_hash'], current_password):

return jsonify({'message': 'Current password is incorrect'}), 401

# Hash the new password before storing (secure)

from werkzeug.security import generate_password_hash

new_password_hash = generate_password_hash(new_password)

if update_user_password(user['id'], new_password_hash):

return jsonify({'message': 'Password changed successfully'})

else:

return jsonify({'message': 'Failed to change password'}), 500


@app.route('/documents', methods=['GET'])

def get_documents():

token = request.headers.get('Authorization', '').split(' ')[-1]

user_id = verify_token(token)

if not user_id:

return jsonify({'message': 'Unauthorized'}), 401

today = date.today()

conn = create_connection()

cursor = conn.cursor(dictionary=True)

# Update status in DB before fetching

cursor.execute("UPDATE documents SET status = 'expired' WHERE expiry_date < %s",


(today,))

cursor.execute("UPDATE documents SET status = 'active' WHERE expiry_date >= %s",


(today,))

conn.commit()

user = get_user_by_id(user_id)

if user['role'] == 'admin':

cursor.execute("""

SELECT id, name, expiry_date, visible, status

FROM documents

""")

else:
cursor.execute("""

SELECT id, name, expiry_date, status

FROM documents

WHERE visible = TRUE

""")

documents = cursor.fetchall()

cursor.close()

conn.close()

return jsonify(documents)

@app.route('/documents/<int:doc_id>/toggle-visibility', methods=['PATCH'])

def toggle_visibility(doc_id):

token = request.headers.get('Authorization', '').split(' ')[-1]

user_id = verify_token(token)

if not user_id:

return jsonify({'message': 'Unauthorized'}), 401

user = get_user_by_id(user_id)

if user['role'] != 'admin':

return jsonify({'message': 'Forbidden'}), 403

conn = create_connection()

cursor = conn.cursor()

cursor.execute("UPDATE documents SET visible = NOT visible WHERE id = %s", (doc_id,))

conn.commit()
cursor.close()

conn.close()

return jsonify({'message': 'Visibility toggled'})

UPLOAD_FOLDER = 'uploads'

ALLOWED_EXTENSIONS = {'pdf', 'docx', 'jpg', 'png'}

if not os.path.exists(UPLOAD_FOLDER):

os.makedirs(UPLOAD_FOLDER)

def allowed_file(filename):

return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/upload', methods=['POST'])

def upload_document():

token = request.headers.get('Authorization', '').replace('Bearer ', '')

user_id = verify_token(token)

if not user_id:

return jsonify({'message': 'Unauthorized'}), 401

file = request.files.get('file')

name = request.form.get('name')

description = request.form.get('description')

expiry_date = request.form.get('expiry_date')

if not file or not allowed_file(file.filename):

return jsonify({'message': 'Invalid or missing file'}), 400

filename = secure_filename(file.filename)
file_path = os.path.join(UPLOAD_FOLDER, filename)

file.save(file_path)

conn = create_connection()

cursor = conn.cursor()

cursor.execute("""

INSERT INTO documents (user_id, name, description, expiry_date)

VALUES (%s, %s, %s, %s)

""", (user_id, name, description, expiry_date))

conn.commit()

cursor.close()

conn.close()

return jsonify({'message': 'Document uploaded successfully'}), 200

@app.route('/alerts', methods=['GET'])

def get_expiring_documents():

token = request.headers.get('Authorization', '').split(' ')[-1]

user_id = verify_token(token)

if not user_id:

return jsonify({'message': 'Unauthorized'}), 401

today = date.today()

next_week = today + timedelta(days=7)

user = get_user_by_id(user_id)
conn = create_connection()

cursor = conn.cursor(dictionary=True)

if user['role'] == 'admin':

cursor.execute("""

SELECT id, name, expiry_date

FROM documents

WHERE expiry_date BETWEEN %s AND %s

""", (today, next_week))

else:

cursor.execute("""

SELECT id, name, expiry_date

FROM documents

WHERE visible = TRUE AND expiry_date BETWEEN %s AND %s

""", (today, next_week))

results = cursor.fetchall()

cursor.close()

conn.close()

return jsonify(results)

if __name__ == '__main__':

app.run(debug=True, port=5000)

You might also like