diff options
author | Parker <contact@pkrm.dev> | 2024-11-11 20:29:56 -0600 |
---|---|---|
committer | Parker <contact@pkrm.dev> | 2024-11-11 20:29:56 -0600 |
commit | 918a04076fa582200cc5b5b542f6d7b47c0fa5e0 (patch) | |
tree | 67ab9243785b549225f273f8961694c76157060a /app/src | |
parent | c0b5500f007b0ec7a116a5dd20083e66cba7409e (diff) |
Fix formatting - mainly spacing
Diffstat (limited to 'app/src')
-rw-r--r-- | app/src/App.tsx | 26 | ||||
-rw-r--r-- | app/src/components/Dashboard.tsx | 233 | ||||
-rw-r--r-- | app/src/components/Login.tsx | 151 | ||||
-rw-r--r-- | app/src/components/Signup.tsx | 189 | ||||
-rw-r--r-- | app/src/helpers/api.js | 27 | ||||
-rw-r--r-- | app/src/main.tsx | 10 |
6 files changed, 317 insertions, 319 deletions
diff --git a/app/src/App.tsx b/app/src/App.tsx index 14fa571..858ad44 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1,18 +1,18 @@ import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; -import Login from './components/Login' -import Signup from './components/Signup' -import Dashboard from './components/Dashboard' +import Login from './components/Login'; +import Signup from './components/Signup'; +import Dashboard from './components/Dashboard'; function App() { - return ( - <Router> - <Routes> - <Route path="/login" element={<Login />} /> - <Route path="/signup" element={<Signup />} /> - <Route path="/dashboard" element={<Dashboard />} /> - </Routes> - </Router> - ) + return ( + <Router> + <Routes> + <Route path="/login" element={<Login />} /> + <Route path="/signup" element={<Signup />} /> + <Route path="/dashboard" element={<Dashboard />} /> + </Routes> + </Router> + ); } -export default App +export default App; diff --git a/app/src/components/Dashboard.tsx b/app/src/components/Dashboard.tsx index ccbfd99..851308e 100644 --- a/app/src/components/Dashboard.tsx +++ b/app/src/components/Dashboard.tsx @@ -6,124 +6,139 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrash } from '@fortawesome/free-solid-svg-icons'; function Dashboard() { - document.title = 'LinkLogger | Dashboard' + document.title = 'LinkLogger | Dashboard'; - interface Log { - id: number; - link: string; - timestamp: string; - ip: string; - location: string; - browser: string; - os: string; - userAgent: string; - isp: string; - } + interface Log { + id: number; + link: string; + timestamp: string; + ip: string; + location: string; + browser: string; + os: string; + userAgent: string; + isp: string; + } - interface Link { - link: string; - owner: number; - redirect_link: string; - expire_date: string; - } + interface Link { + link: string; + owner: number; + redirect_link: string; + expire_date: string; + } - const [links, setLinks] = useState<Link[]>([]); - const [logs, setLogs] = useState<Log[]>([]); - const [visibleLog, setVisibleLog] = useState<string | null>(null); - const navigate = useNavigate(); + const [links, setLinks] = useState<Link[]>([]); + const [logs, setLogs] = useState<Log[]>([]); + const [visibleLog, setVisibleLog] = useState<string | null>(null); + const navigate = useNavigate(); - // Fetch links from API - useEffect(() => { - Axios.get('/api/links').then((res) => { - if (res.status === 200) { - setLinks(res.data); - } else { - navigate('/login'); - } - }).catch(() => { - navigate('/login'); - }); - }, []); + // Fetch links from API + useEffect(() => { + Axios.get('/api/links') + .then((res) => { + if (res.status === 200) { + setLinks(res.data); + } else { + navigate('/login'); + } + }) + .catch(() => { + navigate('/login'); + }); + }, []); - // Fetch logs from API - useEffect(() => { - Axios.get('/api/logs').then((res) => { - if (res.status === 200) { - setLogs(res.data); - } else { - navigate('/login'); - } - }).catch(() => { - navigate('/login'); - }); - }, []); + // Fetch logs from API + useEffect(() => { + Axios.get('/api/logs') + .then((res) => { + if (res.status === 200) { + setLogs(res.data); + } else { + navigate('/login'); + } + }) + .catch(() => { + navigate('/login'); + }); + }, []); + const toggleLogRow = (link: string) => { + setVisibleLog(visibleLog === link ? null : link); + }; - const toggleLogRow = (link: string) => { - setVisibleLog(visibleLog === link ? null : link); - }; + 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> - 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}> + {/* 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> - <button onClick={() => toggleLogRow(link.link)} className={styles.linkButton}>{link.link}</button> + <FontAwesomeIcon + icon={faTrash} + className={styles.trashBin} + /> </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}/></td> - </tr> - ))} - </tbody> - </table> - </td> - </tr> - )} - </React.Fragment> - ))} - </tbody> - </table> - ) + </tr> + ))} + </tbody> + </table> + </td> + </tr> + )} + </React.Fragment> + ))} + </tbody> + </table> + ); } -export default Dashboard;
\ No newline at end of file +export default Dashboard; diff --git a/app/src/components/Login.tsx b/app/src/components/Login.tsx index a3e5cf9..2a81295 100644 --- a/app/src/components/Login.tsx +++ b/app/src/components/Login.tsx @@ -5,82 +5,87 @@ import { useNavigate } from 'react-router-dom'; import axios from 'axios'; function Login() { - document.title = 'LinkLogger | Login' + document.title = 'LinkLogger | Login'; - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [isSubmitting, setIsSubmitting] = useState(false); - const [error, setError] = useState<string | null>(null); - const navigate = useNavigate(); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState<string | null>(null); + const navigate = useNavigate(); - const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { - e.preventDefault(); - setIsSubmitting(true); - try { - const res = await axios.post( - '/api/auth/token', - new URLSearchParams({ - username: username, - password: password, - }), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - } - ); - - if (res.status === 200) { - navigate('/dashboard'); - } - } catch (error: unknown) { - if (axios.isAxiosError(error)) { - const customErrorMessage = error.response?.data?.detail || null; - setPassword(''); - setError(customErrorMessage || 'An error occurred. Please try again.'); - } else { - setPassword(''); - setError('Unknown error. Please try again.'); - } - } finally { - setIsSubmitting(false); + const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { + e.preventDefault(); + setIsSubmitting(true); + try { + const res = await axios.post( + '/api/auth/token', + new URLSearchParams({ + username: username, + password: password, + }), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, } - }; + ); + + if (res.status === 200) { + navigate('/dashboard'); + } + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + const customErrorMessage = error.response?.data?.detail || null; + setPassword(''); + setError(customErrorMessage || 'An error occurred. Please try again.'); + } else { + setPassword(''); + setError('Unknown error. Please try again.'); + } + } finally { + setIsSubmitting(false); + } + }; - 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> - </div> - </div> - ); + 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> + </div> + </div> + ); } -export default Login;
\ No newline at end of file +export default Login; diff --git a/app/src/components/Signup.tsx b/app/src/components/Signup.tsx index 547fa9e..388396c 100644 --- a/app/src/components/Signup.tsx +++ b/app/src/components/Signup.tsx @@ -5,104 +5,109 @@ import { useNavigate } from 'react-router-dom'; import axios from 'axios'; function Signup() { - document.title = 'LinkLogger | Signup' + document.title = 'LinkLogger | Signup'; - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [passwordConfirm, setPasswordConfirm] = useState(''); - const [isSubmitting, setIsSubmitting] = useState(false); - const [error, setError] = useState<string | null>(null); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [passwordConfirm, setPasswordConfirm] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState<string | null>(null); - const navigate = useNavigate(); + const navigate = useNavigate(); - const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { - e.preventDefault(); - setIsSubmitting(true); + const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { + e.preventDefault(); + setIsSubmitting(true); - if (password !== passwordConfirm) { - setPassword(''); - setPasswordConfirm(''); - return setError('Passwords do not match.'); - } - - try { - const res = await axios.post( - '/api/users/register', - new URLSearchParams({ - username: username, - password: password, - }), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - } - ); + if (password !== passwordConfirm) { + setPassword(''); + setPasswordConfirm(''); + return setError('Passwords do not match.'); + } - if (res.status === 200) { - navigate('/login'); - } - } catch (error: unknown) { - if (axios.isAxiosError(error)) { - const customErrorMessage = error.response?.data?.detail || null; - setUsername(''); - setPassword(''); - setPasswordConfirm(''); - setError(customErrorMessage || 'An error occurred. Please try again.'); - } else { - setUsername(''); - setPassword(''); - setPasswordConfirm(''); - setError('Unknown error. Please try again.'); - } - } finally { - setIsSubmitting(false); + try { + const res = await axios.post( + '/api/users/register', + new URLSearchParams({ + username: username, + password: password, + }), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, } - }; + ); + + if (res.status === 200) { + navigate('/login'); + } + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + const customErrorMessage = error.response?.data?.detail || null; + setUsername(''); + setPassword(''); + setPasswordConfirm(''); + setError(customErrorMessage || 'An error occurred. Please try again.'); + } else { + setUsername(''); + setPassword(''); + setPasswordConfirm(''); + setError('Unknown error. Please try again.'); + } + } finally { + setIsSubmitting(false); + } + }; - return ( - <div id={styles.container}> - <p id={styles.signupText}>Sign up</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} - 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> - ); + return ( + <div id={styles.container}> + <p id={styles.signupText}>Sign up</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} + 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> + ); } -export default Signup;
\ No newline at end of file +export default Signup; diff --git a/app/src/helpers/api.js b/app/src/helpers/api.js deleted file mode 100644 index 0381f18..0000000 --- a/app/src/helpers/api.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Accept an API endpoint, method, and body to send to the API. - * - If successful, return the response - * - If not, return false - * @param {*} endpoint API endpoint - * @param {*} method String (GET, POST, PUT, DELETE) - * @param {*} body Data to send to the API - * @returns response.json or false - */ -async function accessAPI(endpoint, method, body) { - let response = await fetch(`http://127.0.0.1:5252/api${endpoint}`, { - method: method, - credentials: 'include', - body: body, - }); - - if (response.ok) { - let data = await response.json(); - data = await data; - return data; - - } - - return false; -} - -export { accessAPI };
\ No newline at end of file diff --git a/app/src/main.tsx b/app/src/main.tsx index 4aff025..3a8bd35 100644 --- a/app/src/main.tsx +++ b/app/src/main.tsx @@ -1,9 +1,9 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import App from './App.tsx' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App.tsx'; createRoot(document.getElementById('root')!).render( <StrictMode> <App /> - </StrictMode>, -) + </StrictMode> +); |