diff options
author | Parker <contact@pkrm.dev> | 2024-11-10 16:36:16 -0600 |
---|---|---|
committer | Parker <contact@pkrm.dev> | 2024-11-10 16:36:16 -0600 |
commit | 691aa744a0398f185b3ca98a36fbd83806c7786c (patch) | |
tree | 7840f31c30bb6eda903abd6bbf4dbfb2ac590966 /app/src/components | |
parent | 8941213c8d94f3ad84e07e467e78105dc7fed734 (diff) |
TOO MUCH STUFF
Diffstat (limited to 'app/src/components')
-rw-r--r-- | app/src/components/Dashboard.tsx | 47 | ||||
-rw-r--r-- | app/src/components/Login.tsx | 84 | ||||
-rw-r--r-- | app/src/components/Signup.tsx | 106 |
3 files changed, 237 insertions, 0 deletions
diff --git a/app/src/components/Dashboard.tsx b/app/src/components/Dashboard.tsx new file mode 100644 index 0000000..bcab092 --- /dev/null +++ b/app/src/components/Dashboard.tsx @@ -0,0 +1,47 @@ +import { useState, useEffect } from 'react'; +import Axios from 'axios'; +import styles from '../styles/Dashboard.module.css'; +// import { accessAPI } from '../helpers/api'; + + +function Dashboard() { + // Get the links from the API + const [links, setLinks] = useState([]); + useEffect(() => { + Axios.get('/api/links') + .then((res) => { + setLinks(res.data); + }) + .catch((err) => { + console.log(err); + }); + }, []); + + + return ( + <div id={styles.container}> + <table> + <thead> + <tr style={{ border: '2px solid #ccc' }}> + <th>Link</th> + <th>Visits</th> + <th>Redirect</th> + <th>Expire Date</th> + </tr> + </thead> + <tbody> + {/* {links.map((link: any) => ( + <tr key={link.id}> + <td>{link.url}</td> + <td>{link.visits}</td> + <td>{link.redirect}</td> + <td>{link.expire_date}</td> + </tr> + ))} */} + </tbody> + </table> + </div> + ) +} + +export default Dashboard;
\ No newline at end of file diff --git a/app/src/components/Login.tsx b/app/src/components/Login.tsx new file mode 100644 index 0000000..8235b65 --- /dev/null +++ b/app/src/components/Login.tsx @@ -0,0 +1,84 @@ +import { useState, FormEvent } from 'react'; +import styles from '../styles/Login.module.css'; +import { Link } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; +import axios from 'axios'; + +function 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 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> + ); +} + +export default Login;
\ No newline at end of file diff --git a/app/src/components/Signup.tsx b/app/src/components/Signup.tsx new file mode 100644 index 0000000..293b51a --- /dev/null +++ b/app/src/components/Signup.tsx @@ -0,0 +1,106 @@ +import { useState, FormEvent } from 'react'; +import styles from '../styles/Login.module.css'; +import { Link } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; +import axios from 'axios'; + +function 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 navigate = useNavigate(); + + 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 (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> + ); +} + +export default Signup;
\ No newline at end of file |