Update UI
This commit is contained in:
parent
9ce608b637
commit
dbc53a555e
@ -47,10 +47,7 @@ function Dashboard() {
|
|||||||
|
|
||||||
.catch((error: unknown) => {
|
.catch((error: unknown) => {
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
if (error.response?.status === 404) {
|
if (error.response?.status != 404) {
|
||||||
// Create a message alerting the user there are no links
|
|
||||||
navigate('/login');
|
|
||||||
} else {
|
|
||||||
navigate('/login');
|
navigate('/login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,11 +65,12 @@ function Dashboard() {
|
|||||||
navigate('/login');
|
navigate('/login');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch((error: unknown) => {
|
||||||
// Catch 404 error = user has no logs
|
if (axios.isAxiosError(error)) {
|
||||||
|
if (error.response?.status != 404) {
|
||||||
.catch(() => {
|
navigate('/login');
|
||||||
navigate('/login');
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -117,7 +115,7 @@ function Dashboard() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<table id={styles.mainTable}>
|
<table className={styles.mainTable}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr style={{ border: '2px solid #ccc' }}>
|
<tr style={{ border: '2px solid #ccc' }}>
|
||||||
<th>Link</th>
|
<th>Link</th>
|
||||||
@ -127,6 +125,17 @@ function Dashboard() {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{/* If there are no links, put a special message */}
|
||||||
|
{links.length === 0 && (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={4}>
|
||||||
|
<div className={styles.noLinks}>
|
||||||
|
You do not have any shortened links - try creating one.
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* For every link and its logs */}
|
{/* For every link and its logs */}
|
||||||
{links.map((link) => (
|
{links.map((link) => (
|
||||||
<React.Fragment key={link.link}>
|
<React.Fragment key={link.link}>
|
||||||
@ -161,7 +170,7 @@ function Dashboard() {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{/* Render logs only if visibleLog matches the link */}
|
{/* Render all logs for the link */}
|
||||||
{logs
|
{logs
|
||||||
.filter((log) => log.link === link.link)
|
.filter((log) => log.link === link.link)
|
||||||
.map((log, index, filteredLogs) => (
|
.map((log, index, filteredLogs) => (
|
||||||
@ -181,6 +190,17 @@ function Dashboard() {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
{/* If the link has no logs, put a special message */}
|
||||||
|
{logs.filter((log) => log.link === link.link).length ===
|
||||||
|
0 && (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={6}>
|
||||||
|
<div className={styles.noLogs}>
|
||||||
|
No logs for this link
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, FormEvent } from 'react';
|
import { useState, FormEvent } from 'react';
|
||||||
import styles from '../styles/Login.module.css';
|
import styles from '../styles/Auth.module.css';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
@ -51,42 +51,36 @@ function Login() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<div id={styles.container}>
|
<div className={styles.container}>
|
||||||
<p id={styles.loginText}>Log In</p>
|
<h1>Log In</h1>
|
||||||
<p id={styles.error} className={error ? 'visible' : 'hidden'}>
|
<h2 className={error ? 'errorVisible' : 'errorHidden'}>{error}</h2>
|
||||||
{error}
|
<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 className={styles.footnote}>
|
||||||
|
Don't have an account?{' '}
|
||||||
|
<Link to="/signup" className={styles.footnoteLink}>
|
||||||
|
Create one now
|
||||||
|
</Link>
|
||||||
</p>
|
</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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,21 +1,63 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
import styles from '../styles/Navbar.module.css';
|
import styles from '../styles/Navbar.module.css';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { faCircleUp, faCircleDown } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
function Navbar() {
|
function Navbar() {
|
||||||
|
const [isOnline, setIsOnline] = useState<boolean | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkAPIStatus = async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/ping');
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
setIsOnline(true);
|
||||||
|
} else {
|
||||||
|
setIsOnline(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setIsOnline(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkAPIStatus();
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.navbar}>
|
<div className={styles.navbar}>
|
||||||
<div className={styles.navbarLeft}>
|
<div className={styles.left}>
|
||||||
<Link to={'/login'}>
|
<Link to={'/login'}>
|
||||||
<a className={styles.navbarLink}>Login</a>
|
<a className={styles.link}>Login</a>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={'/signup'}>
|
<Link to={'/signup'}>
|
||||||
<a className={styles.navbarLink}>Signup</a>
|
<a className={styles.link}>Signup</a>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.navbarRight}>
|
<div className={styles.right}>
|
||||||
<Link to={'/status'}>
|
<a
|
||||||
<a className={styles.navbarLink}>API Status</a>
|
className={styles.link}
|
||||||
</Link>
|
title={
|
||||||
|
isOnline === null
|
||||||
|
? 'Loading...'
|
||||||
|
: isOnline
|
||||||
|
? 'API is online'
|
||||||
|
: 'API is offline'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
API Status:{' '}
|
||||||
|
{isOnline === null ? (
|
||||||
|
'Loading...'
|
||||||
|
) : isOnline ? (
|
||||||
|
<FontAwesomeIcon icon={faCircleUp} className={styles.circleUp} />
|
||||||
|
) : (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faCircleDown}
|
||||||
|
className={styles.circleDown}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, FormEvent } from 'react';
|
import { useState, FormEvent } from 'react';
|
||||||
import styles from '../styles/Login.module.css';
|
import styles from '../styles/Auth.module.css';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
@ -64,51 +64,45 @@ function Signup() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<div id={styles.container}>
|
<div className={styles.container}>
|
||||||
<p id={styles.signupText}>Sign up</p>
|
<h1>Sign up</h1>
|
||||||
<p id={styles.error} className={error ? 'visible' : 'hidden'}>
|
<h2 className={error ? 'errorVisible' : 'errorHidden'}>{error}</h2>
|
||||||
{error}
|
<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}
|
||||||
|
minLength={8}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="confirm password"
|
||||||
|
value={passwordConfirm}
|
||||||
|
minLength={8}
|
||||||
|
onChange={(e) => setPasswordConfirm(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<button type="submit" disabled={isSubmitting}>
|
||||||
|
{isSubmitting ? 'Submitting...' : 'Submit'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<hr></hr>
|
||||||
|
<p className={styles.footnote}>
|
||||||
|
Already have an account?{' '}
|
||||||
|
<Link to="/login" className={styles.footnoteLink}>
|
||||||
|
Log in here.
|
||||||
|
</Link>
|
||||||
</p>
|
</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}
|
|
||||||
minLength={8}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
placeholder="confirm password"
|
|
||||||
value={passwordConfirm}
|
|
||||||
minLength={8}
|
|
||||||
onChange={(e) => setPasswordConfirm(e.target.value)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<button type="submit" disabled={isSubmitting}>
|
|
||||||
{isSubmitting ? 'Submitting...' : 'Submit'}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<hr></hr>
|
|
||||||
<p id={styles.bottomText}>
|
|
||||||
Already have an account?{' '}
|
|
||||||
<Link to="/login" className={styles.link}>
|
|
||||||
Log in here.
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,7 @@ body {
|
|||||||
background-color: #2c3338;
|
background-color: #2c3338;
|
||||||
}
|
}
|
||||||
|
|
||||||
#container {
|
.container {
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@ -14,8 +14,7 @@ body {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loginText,
|
h1 {
|
||||||
#signupText {
|
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -59,29 +58,26 @@ button:active {
|
|||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
#error {
|
.errorVisible {
|
||||||
|
visibility: visible;
|
||||||
color: #ee6161;
|
color: #ee6161;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.errorHidden {
|
||||||
text-decoration: underline;
|
visibility: hidden;
|
||||||
color: #ccc;
|
color: #ee6161;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.link:hover {
|
.footnoteLink {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnoteLink:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #415eac;
|
color: #415eac;
|
||||||
}
|
}
|
||||||
|
|
||||||
#bottomText {
|
.footnote {
|
||||||
color: #606468;
|
color: #606468;
|
||||||
}
|
|
||||||
|
|
||||||
.visible {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
}
|
@ -5,13 +5,6 @@ body {
|
|||||||
background-color: #2c3338;
|
background-color: #2c3338;
|
||||||
}
|
}
|
||||||
|
|
||||||
#mainTable {
|
|
||||||
position: absolute;
|
|
||||||
top: 100px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
table {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -22,42 +15,31 @@ table {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Center all sub tables */
|
|
||||||
.logTableRow table {
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logTableRow table {
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th {
|
table th {
|
||||||
background-color: #415eac;
|
background-color: #415eac;
|
||||||
border: 2px solid #ccc;
|
border: 2px solid #ccc;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.linkTableRow {
|
|
||||||
border: 2px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td {
|
table td {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mainTable {
|
||||||
|
position: absolute;
|
||||||
|
top: 100px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.linkTableRow {
|
||||||
|
border: 2px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
.linkTableRow td {
|
.linkTableRow td {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logTableRow table td {
|
|
||||||
background-color: #3b4148;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logTableRow table tr {
|
|
||||||
border: 2px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.linkButton {
|
.linkButton {
|
||||||
background-color: #3b4148;
|
background-color: #3b4148;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
@ -68,6 +50,20 @@ table td {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logTableRow table {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logTableRow table td {
|
||||||
|
background-color: #3b4148;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logTableRow table tr {
|
||||||
|
border: 2px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
.trashBin:hover {
|
.trashBin:hover {
|
||||||
color: rgb(238, 86, 86);
|
color: rgb(238, 86, 86);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* Create the navbar and set the colors */
|
|
||||||
.navbar {
|
.navbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -7,7 +6,7 @@
|
|||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbarLink {
|
.link {
|
||||||
margin: 0 20px;
|
margin: 0 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -17,7 +16,7 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbarLink::after {
|
.link::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -28,14 +27,22 @@
|
|||||||
transition: width 0.3s ease;
|
transition: width 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbarLink:hover::after {
|
.link:hover::after {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbarLeft {
|
.circleUp {
|
||||||
|
color: rgb(122, 224, 122);
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleDown {
|
||||||
|
color: rgb(218, 112, 112);
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
margin-left: 50px;
|
margin-left: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbarRight {
|
.right {
|
||||||
margin-right: 50px;
|
margin-right: 50px;
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user