diff options
author | Parker <contact@pkrm.dev> | 2024-12-19 01:27:15 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-19 01:27:15 +0000 |
commit | 880280d67f8ef7593d7caad8e1bbc5dfa7c14bed (patch) | |
tree | fc931891daef93de651290f4cfe5536a3e40c528 | |
parent | 3b57e4a4487f0c40e8e2b8c4d613e5cd588f5027 (diff) | |
parent | 1ae686480c084f3af897de138fc2af97f0c262d2 (diff) |
Merge pull request #4 from PacketParker/dev
Dev
-rw-r--r-- | api/util/authentication.py | 1 | ||||
-rw-r--r-- | app/package.json | 2 | ||||
-rw-r--r-- | app/src/App.tsx | 2 | ||||
-rw-r--r-- | app/src/components/CreateLink.tsx | 100 | ||||
-rw-r--r-- | app/src/components/Dashboard.tsx | 71 | ||||
-rw-r--r-- | app/src/components/Signup.tsx | 4 | ||||
-rw-r--r-- | app/src/styles/Auth.module.css | 7 | ||||
-rw-r--r-- | app/src/styles/Create.module.css | 11 | ||||
-rw-r--r-- | app/src/styles/Dashboard.module.css | 56 | ||||
-rw-r--r-- | app/yarn.lock | 8 |
10 files changed, 137 insertions, 125 deletions
diff --git a/api/util/authentication.py b/api/util/authentication.py index 43b147a..021399a 100644 --- a/api/util/authentication.py +++ b/api/util/authentication.py @@ -45,7 +45,6 @@ def authenticate_user(db, username: str, password: str): if not user: return False if not verify_password(password, user.hashed_password): - print("WHY") return False return user diff --git a/app/package.json b/app/package.json index d7b658b..2d2f2ed 100644 --- a/app/package.json +++ b/app/package.json @@ -29,7 +29,7 @@ "globals": "^15.11.0", "typescript": "~5.6.2", "typescript-eslint": "^8.11.0", - "vite": "^6.0.2" + "vite": "^6.0.3" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/app/src/App.tsx b/app/src/App.tsx index 06ead3b..54a500d 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -7,7 +7,6 @@ import { import Login from './components/Login'; import Signup from './components/Signup'; import Dashboard from './components/Dashboard'; -import CreateLink from './components/CreateLink'; function App() { return ( @@ -17,7 +16,6 @@ function App() { <Route path="/login" element={<Login />} /> <Route path="/signup" element={<Signup />} /> <Route path="/dashboard" element={<Dashboard />} /> - <Route path="/create" element={<CreateLink />} /> </Routes> </Router> ); diff --git a/app/src/components/CreateLink.tsx b/app/src/components/CreateLink.tsx deleted file mode 100644 index a6456f1..0000000 --- a/app/src/components/CreateLink.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { useState, FormEvent, useEffect } from 'react'; -import createStyles from '../styles/Create.module.css'; -import styles from '../styles/Auth.module.css'; -import { Link } from 'react-router-dom'; -import axios from 'axios'; -import Navbar from './Navbar'; -import { useNavigate } from 'react-router-dom'; - -function CreateLink() { - document.title = 'LinkLogger | Create Short Link'; - - const [link, setLink] = useState(''); - const [url, setURL] = useState(''); - const [isSubmitting, setIsSubmitting] = useState(false); - const [isCopied, setIsCopied] = useState(false); - const [error, setError] = useState<string | null>(null); - const navigate = useNavigate(); - - // Get /api/users/me to make sure the user is logged in, and - // to get the username for rendering on screen - useEffect(() => { - axios - .get('/api/users/me') - .then((res) => { - if (res.status != 200) { - navigate('/login'); - } - }) - .catch(() => { - navigate('/login'); - }); - }, []); - - const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { - e.preventDefault(); - setIsSubmitting(true); - try { - const res = await axios.post('/api/links', { url }); - if (res.status === 200) { - setLink(res.data.link); - } - } catch (error) { - setError('STRANGE'); - } - }; - - const copyLink = () => { - navigator.clipboard.writeText(`${window.location.origin}/c/${link}`); - setIsCopied(true); - // Wait 5 seconds, then set isCopied back to false - setTimeout(() => { - setIsCopied(false); - }, 5000); - }; - - return ( - <> - <Navbar /> - <div className={styles.container}> - <h1>Create a new short link by entering the long URL below</h1> - <p className={error ? styles.errorVisible : styles.errorHidden}> - {error} - </p> - <hr></hr> - <form onSubmit={handleSubmit}> - <input - className={createStyles.createInput} - type="text" - placeholder="Full URL" - value={url} - onChange={(e) => setURL(e.target.value)} - required - /> - {link.length === 0 ? ( - <button type="submit" disabled={isSubmitting}> - {isSubmitting ? 'Creating...' : 'Create'} - </button> - ) : ( - <button type="button" onClick={copyLink}> - {isCopied ? ( - <em>Copied!</em> - ) : ( - `Click to copy: ${window.location.origin}/c/${link}` - )} - </button> - )} - </form> - <hr></hr> - <p className={styles.footnote}> - <Link to="/dashboard" className={styles.footnoteLink}> - Click here - </Link>{' '} - to visit your dashboard. - </p> - </div> - </> - ); -} - -export default CreateLink; diff --git a/app/src/components/Dashboard.tsx b/app/src/components/Dashboard.tsx index d699efb..913b566 100644 --- a/app/src/components/Dashboard.tsx +++ b/app/src/components/Dashboard.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, FormEvent } from 'react'; import axios from 'axios'; import styles from '../styles/Dashboard.module.css'; import { useNavigate, Link } from 'react-router-dom'; @@ -28,6 +28,14 @@ function Dashboard() { expire_date: string; } + // Link creation states + const [link, setLink] = useState(''); + const [url, setURL] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isCopied, setIsCopied] = useState(false); + const [error, setError] = useState<string | null>(null); + + // Log and link states const [links, setLinks] = useState<Link[]>([]); const [logs, setLogs] = useState<Log[]>([]); const [visibleLog, setVisibleLog] = useState<string | null>(null); @@ -35,6 +43,37 @@ function Dashboard() { const [loadingLogs, setLoadingLogs] = useState<boolean>(true); // Track loading state for logs const navigate = useNavigate(); + // Handle form submission to create a new link + const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { + e.preventDefault(); + setIsSubmitting(true); + try { + const res = await axios.post('/api/links', { url }); + if (res.status === 200) { + setLink(res.data.link); + } + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + const customErrorMessage = error.response?.data?.error || null; + setError(customErrorMessage || 'An error occurred. Please try again.'); + setIsSubmitting(false); + } else { + setError('Unknown error. Please try again.'); + setIsSubmitting(false); + } + } + }; + + // Copy the link to the clipboard + const copyLink = () => { + navigator.clipboard.writeText(`${window.location.origin}/c/${link}`); + setIsCopied(true); + // Wait 5 seconds, then set isCopied back to false + setTimeout(() => { + setIsCopied(false); + }, 5000); + }; + // Fetch links from API useEffect(() => { axios @@ -130,6 +169,36 @@ function Dashboard() { return ( <> <Navbar /> + + <div className={styles.createContainer}> + <h1>Create a new short link by entering the long URL below</h1> + <form onSubmit={handleSubmit}> + <input + type="text" + placeholder="Full URL" + value={url} + onChange={(e) => setURL(e.target.value)} + required + /> + {link.length === 0 ? ( + <button type="submit" disabled={isSubmitting}> + {isSubmitting ? 'Creating...' : 'Create'} + </button> + ) : ( + <button type="button" onClick={copyLink}> + {isCopied ? ( + <em>Copied!</em> + ) : ( + `Click to copy: ${window.location.origin}/c/${link}` + )} + </button> + )} + </form> + <p className={error ? styles.errorVisible : styles.errorHidden}> + {error} + </p> + </div> + {/* Show loading spinner if either links or logs are still loading */} {loadingLinks || loadingLogs ? ( <LoadingSpinner /> diff --git a/app/src/components/Signup.tsx b/app/src/components/Signup.tsx index 0822c46..300f189 100644 --- a/app/src/components/Signup.tsx +++ b/app/src/components/Signup.tsx @@ -66,9 +66,9 @@ function Signup() { <Navbar /> <div className={styles.container}> <h1>Create Account</h1> - <h2 className={error ? styles.errorVisible : styles.errorHidden}> + <p className={error ? styles.errorVisible : styles.errorHidden}> {error} - </h2> + </p> <hr></hr> <form onSubmit={handleSubmit}> <input diff --git a/app/src/styles/Auth.module.css b/app/src/styles/Auth.module.css index 841665a..bd8059c 100644 --- a/app/src/styles/Auth.module.css +++ b/app/src/styles/Auth.module.css @@ -8,7 +8,7 @@ body { .container { font-size: 17px; position: absolute; - max-width: 500px; + width: 400px; top: 50%; left: 50%; transform: translate(-50%, -50%); @@ -27,7 +27,8 @@ h1 { .authInput { display: block; margin: 10px auto; - width: 300px; + width: 100%; + box-sizing: border-box; border-radius: 5px; padding: 15px; font-size: 17px; @@ -62,6 +63,8 @@ button:active { visibility: visible; color: #ee6161; font-weight: 600; + word-wrap: break-word; + width: 100%; } .errorHidden { diff --git a/app/src/styles/Create.module.css b/app/src/styles/Create.module.css deleted file mode 100644 index d8dbcb7..0000000 --- a/app/src/styles/Create.module.css +++ /dev/null @@ -1,11 +0,0 @@ -.createInput { - display: block; - margin: 10px auto; - width: 470px; - border-radius: 5px; - padding: 15px; - color: #ccc; - background-color: #3b4148; - border: none; - font-size: 17px; -}
\ No newline at end of file diff --git a/app/src/styles/Dashboard.module.css b/app/src/styles/Dashboard.module.css index fbc1d6d..e87c414 100644 --- a/app/src/styles/Dashboard.module.css +++ b/app/src/styles/Dashboard.module.css @@ -5,6 +5,61 @@ body { background-color: #2c3338; } +.createContainer { + margin: 25px auto 25px auto; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 1000px; +} + +.createContainer h1 { + font-size: 25px; + text-align: center; + width: 100%; + box-sizing: border-box; +} + +.createContainer form { + display: flex; + gap: 10px; + width: 100%; +} + +.createContainer form input { + display: block; + margin: 10px auto; + width: 60%; + box-sizing: border-box; + border-radius: 5px; + padding: 15px; + font-size: 17px; + color: #ccc; + background-color: #3b4148; + border: none; +} + +.createContainer form button { + width: 40%; +} + +.errorVisible { + visibility: visible; + color: #ee6161; + font-weight: 600; + word-wrap: break-word; + width: 100%; + text-align: center; + font-size: 17px; + margin: 0 auto; +} + +.errorHidden { + visibility: hidden; + color: #ee6161; +} + .loadingSpinner { display: flex; flex-direction: column; @@ -56,7 +111,6 @@ table td { .mainTable { position: absolute; - top: 150px; left: 50%; transform: translateX(-50%); } diff --git a/app/yarn.lock b/app/yarn.lock index 7182cab..d9b4d31 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -1593,10 +1593,10 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -vite@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.2.tgz#7a22630c73c7b663335ddcdb2390971ffbc14993" - integrity sha512-XdQ+VsY2tJpBsKGs0wf3U/+azx8BBpYRHFAyKm5VeEZNOJZRB63q7Sc8Iup3k0TrN3KO6QgyzFf+opSbfY1y0g== +vite@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.3.tgz#cc01f403e326a9fc1e064235df8a6de084c8a491" + integrity sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw== dependencies: esbuild "^0.24.0" postcss "^8.4.49" |