Begin navbar
This commit is contained in:
parent
41a21ee0b5
commit
3b2258877f
@ -4,6 +4,7 @@ import styles from '../styles/Dashboard.module.css';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||
import Navbar from './Navbar';
|
||||
|
||||
function Dashboard() {
|
||||
document.title = 'LinkLogger | Dashboard';
|
||||
@ -44,10 +45,15 @@ function Dashboard() {
|
||||
}
|
||||
})
|
||||
|
||||
// Catch 404 error = user has no links
|
||||
|
||||
.catch(() => {
|
||||
navigate('/login');
|
||||
.catch((error: unknown) => {
|
||||
if (axios.isAxiosError(error)) {
|
||||
if (error.response?.status === 404) {
|
||||
// Create a message alerting the user there are no links
|
||||
navigate('/login');
|
||||
} else {
|
||||
navigate('/login');
|
||||
}
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
@ -109,79 +115,82 @@ function Dashboard() {
|
||||
};
|
||||
|
||||
return (
|
||||
<table id={styles.mainTable}>
|
||||
<thead>
|
||||
<tr style={{ border: '2px solid #ccc' }}>
|
||||
<th>Link</th>
|
||||
<th>Visits</th>
|
||||
<th>Redirect</th>
|
||||
<th>Expire Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/* For every link and its logs */}
|
||||
{links.map((link) => (
|
||||
<React.Fragment key={link.link}>
|
||||
<tr className={styles.linkTableRow}>
|
||||
<td>
|
||||
<button
|
||||
onClick={() => toggleLogRow(link.link)}
|
||||
className={styles.linkButton}
|
||||
>
|
||||
{link.link}
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
{logs.filter((log) => log.link === link.link).length || 0}
|
||||
</td>
|
||||
<td>{link.redirect_link}</td>
|
||||
<td>{link.expire_date}</td>
|
||||
</tr>
|
||||
|
||||
{/* Conditionally render logs for this link */}
|
||||
{visibleLog === link.link && (
|
||||
<tr className={styles.logTableRow}>
|
||||
<td colSpan={6}>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Timestamp</th>
|
||||
<th>IP</th>
|
||||
<th>Location</th>
|
||||
<th colSpan={2}>ISP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/* Render logs only if visibleLog matches the link */}
|
||||
{logs
|
||||
.filter((log) => log.link === link.link)
|
||||
.map((log, index, filteredLogs) => (
|
||||
<tr key={log.id}>
|
||||
<td>{filteredLogs.length - index}</td>
|
||||
<td>{log.timestamp}</td>
|
||||
<td>{log.ip}</td>
|
||||
<td>{log.location}</td>
|
||||
<td>{log.isp}</td>
|
||||
<td>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrash}
|
||||
className={styles.trashBin}
|
||||
id={log.id.toString()}
|
||||
onClick={deleteLog}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<>
|
||||
<Navbar />
|
||||
<table id={styles.mainTable}>
|
||||
<thead>
|
||||
<tr style={{ border: '2px solid #ccc' }}>
|
||||
<th>Link</th>
|
||||
<th>Visits</th>
|
||||
<th>Redirect</th>
|
||||
<th>Expire Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/* For every link and its logs */}
|
||||
{links.map((link) => (
|
||||
<React.Fragment key={link.link}>
|
||||
<tr className={styles.linkTableRow}>
|
||||
<td>
|
||||
<button
|
||||
onClick={() => toggleLogRow(link.link)}
|
||||
className={styles.linkButton}
|
||||
>
|
||||
{link.link}
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
{logs.filter((log) => log.link === link.link).length || 0}
|
||||
</td>
|
||||
<td>{link.redirect_link}</td>
|
||||
<td>{link.expire_date}</td>
|
||||
</tr>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{/* Conditionally render logs for this link */}
|
||||
{visibleLog === link.link && (
|
||||
<tr className={styles.logTableRow}>
|
||||
<td colSpan={6}>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Timestamp</th>
|
||||
<th>IP</th>
|
||||
<th>Location</th>
|
||||
<th colSpan={2}>ISP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/* Render logs only if visibleLog matches the link */}
|
||||
{logs
|
||||
.filter((log) => log.link === link.link)
|
||||
.map((log, index, filteredLogs) => (
|
||||
<tr key={log.id}>
|
||||
<td>{filteredLogs.length - index}</td>
|
||||
<td>{log.timestamp}</td>
|
||||
<td>{log.ip}</td>
|
||||
<td>{log.location}</td>
|
||||
<td>{log.isp}</td>
|
||||
<td>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrash}
|
||||
className={styles.trashBin}
|
||||
id={log.id.toString()}
|
||||
onClick={deleteLog}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import styles from '../styles/Login.module.css';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import Navbar from './Navbar';
|
||||
|
||||
function Login() {
|
||||
document.title = 'LinkLogger | Login';
|
||||
@ -48,43 +49,46 @@ function Login() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div id={styles.container}>
|
||||
<p id={styles.loginText}>Log In</p>
|
||||
<p id={styles.error} className={error ? 'visible' : 'hidden'}>
|
||||
{error}
|
||||
</p>
|
||||
<div>
|
||||
<header>
|
||||
<hr></hr>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button type="submit" disabled={isSubmitting}>
|
||||
{isSubmitting ? 'Submitting...' : 'Submit'}
|
||||
</button>
|
||||
</form>
|
||||
<hr></hr>
|
||||
<p id={styles.bottomText}>
|
||||
Don't have an account?{' '}
|
||||
<Link to="/signup" className={styles.link}>
|
||||
Create one now
|
||||
</Link>
|
||||
</p>
|
||||
</header>
|
||||
<>
|
||||
<Navbar />
|
||||
<div id={styles.container}>
|
||||
<p id={styles.loginText}>Log In</p>
|
||||
<p id={styles.error} className={error ? 'visible' : 'hidden'}>
|
||||
{error}
|
||||
</p>
|
||||
<div>
|
||||
<header>
|
||||
<hr></hr>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button type="submit" disabled={isSubmitting}>
|
||||
{isSubmitting ? 'Submitting...' : 'Submit'}
|
||||
</button>
|
||||
</form>
|
||||
<hr></hr>
|
||||
<p id={styles.bottomText}>
|
||||
Don't have an account?{' '}
|
||||
<Link to="/signup" className={styles.link}>
|
||||
Create one now
|
||||
</Link>
|
||||
</p>
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
11
app/src/components/Navbar.tsx
Normal file
11
app/src/components/Navbar.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import styles from '../styles/Navbar.module.css';
|
||||
|
||||
function Navbar() {
|
||||
return (
|
||||
<nav className={styles.navbar}>
|
||||
<span>LinkLogger</span>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
export default Navbar;
|
18
app/src/styles/Navbar.module.css
Normal file
18
app/src/styles/Navbar.module.css
Normal file
@ -0,0 +1,18 @@
|
||||
/* Create the nav and center the span */
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
/* Create the nav links */
|
||||
|
||||
span {
|
||||
font-size: 35px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
margin-right: 10px;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user