aboutsummaryrefslogtreecommitdiff
path: root/app/src/components
diff options
context:
space:
mode:
authorParker <contact@pkrm.dev>2024-11-10 16:36:16 -0600
committerParker <contact@pkrm.dev>2024-11-10 16:36:16 -0600
commit691aa744a0398f185b3ca98a36fbd83806c7786c (patch)
tree7840f31c30bb6eda903abd6bbf4dbfb2ac590966 /app/src/components
parent8941213c8d94f3ad84e07e467e78105dc7fed734 (diff)
TOO MUCH STUFF
Diffstat (limited to 'app/src/components')
-rw-r--r--app/src/components/Dashboard.tsx47
-rw-r--r--app/src/components/Login.tsx84
-rw-r--r--app/src/components/Signup.tsx106
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