-
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added user accounts, not perfect, share links are broken by this commit
- Loading branch information
1 parent
b924de5
commit 8191392
Showing
24 changed files
with
1,015 additions
and
371 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import React, { useState } from 'react'; | ||
import { Link } from 'react-router-dom'; | ||
import { useAuth } from '../../hooks/useAuth'; | ||
import { register } from '../../utils/api/auth'; | ||
import { PageContainer } from '../common/layout/PageContainer'; | ||
|
||
export const RegisterPage: React.FC = () => { | ||
const [username, setUsername] = useState(''); | ||
const [password, setPassword] = useState(''); | ||
const [confirmPassword, setConfirmPassword] = useState(''); | ||
const [error, setError] = useState(''); | ||
const [isLoading, setIsLoading] = useState(false); | ||
const { login, authConfig } = useAuth(); | ||
|
||
if (!authConfig?.allowNewAccounts) { | ||
return ( | ||
<PageContainer className="flex items-center justify-center min-h-screen"> | ||
<div className="text-center"> | ||
<h2 className="text-2xl font-bold mb-4">Registration Disabled</h2> | ||
<p className="text-gray-400 mb-4">New account registration is currently disabled.</p> | ||
<Link | ||
to="/login" | ||
className="text-blue-400 hover:text-blue-300 underline" | ||
> | ||
Return to Login | ||
</Link> | ||
</div> | ||
</PageContainer> | ||
); | ||
} | ||
|
||
const handleSubmit = async (e: React.FormEvent) => { | ||
e.preventDefault(); | ||
setError(''); | ||
setIsLoading(true); | ||
|
||
if (password !== confirmPassword) { | ||
setError('Passwords do not match'); | ||
setIsLoading(false); | ||
return; | ||
} | ||
|
||
try { | ||
const response = await register(username, password); | ||
if (response.token && response.user) { | ||
login(response.token, response.user); | ||
} | ||
} catch (err: any) { | ||
const errorMessage = err.message || 'Failed to register'; | ||
setError(errorMessage); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
return ( | ||
<PageContainer className="flex items-center justify-center min-h-screen"> | ||
<div className="max-w-md w-full space-y-8"> | ||
<div> | ||
<h2 className="mt-6 text-center text-3xl font-bold text-white"> | ||
Create Account | ||
</h2> | ||
<p className="mt-2 text-center text-sm text-gray-400"> | ||
Or{' '} | ||
<Link to="/login" className="text-blue-400 hover:text-blue-300"> | ||
sign in to your account | ||
</Link> | ||
</p> | ||
</div> | ||
|
||
<form className="mt-8 space-y-6" onSubmit={handleSubmit}> | ||
{error && ( | ||
<div className="text-red-500 text-sm text-center bg-red-500/10 py-2 px-4 rounded-md border border-red-500/20"> | ||
{error} | ||
</div> | ||
)} | ||
|
||
<div className="rounded-md shadow-sm -space-y-px"> | ||
<div> | ||
<label htmlFor="username" className="sr-only">Username</label> | ||
<input | ||
id="username" | ||
type="text" | ||
required | ||
className="appearance-none rounded-none relative block w-full px-3 py-2 border | ||
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-t-md | ||
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm" | ||
placeholder="Username" | ||
value={username} | ||
onChange={(e) => setUsername(e.target.value)} | ||
disabled={isLoading} | ||
/> | ||
</div> | ||
<div> | ||
<label htmlFor="password" className="sr-only">Password</label> | ||
<input | ||
id="password" | ||
type="password" | ||
required | ||
className="appearance-none rounded-none relative block w-full px-3 py-2 border | ||
border-gray-700 placeholder-gray-500 text-white bg-gray-800 | ||
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm" | ||
placeholder="Password" | ||
value={password} | ||
onChange={(e) => setPassword(e.target.value)} | ||
disabled={isLoading} | ||
/> | ||
</div> | ||
<div> | ||
<label htmlFor="confirm-password" className="sr-only">Confirm Password</label> | ||
<input | ||
id="confirm-password" | ||
type="password" | ||
required | ||
className="appearance-none rounded-none relative block w-full px-3 py-2 border | ||
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-b-md | ||
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm" | ||
placeholder="Confirm Password" | ||
value={confirmPassword} | ||
onChange={(e) => setConfirmPassword(e.target.value)} | ||
disabled={isLoading} | ||
/> | ||
</div> | ||
</div> | ||
|
||
<div> | ||
<button | ||
type="submit" | ||
className="group relative w-full flex justify-center py-2 px-4 border border-transparent | ||
text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 | ||
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 | ||
disabled:opacity-50 disabled:cursor-not-allowed" | ||
disabled={isLoading} | ||
> | ||
{isLoading ? ( | ||
<span className="flex items-center"> | ||
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> | ||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle> | ||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> | ||
</svg> | ||
Creating Account... | ||
</span> | ||
) : ( | ||
'Create Account' | ||
)} | ||
</button> | ||
</div> | ||
</form> | ||
</div> | ||
</PageContainer> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React, { useRef, useState } from 'react'; | ||
import { LogOut, User } from 'lucide-react'; | ||
import { useAuth } from '../../hooks/useAuth'; | ||
import { useOutsideClick } from '../../hooks/useOutsideClick'; | ||
|
||
export const UserDropdown: React.FC = () => { | ||
const [isOpen, setIsOpen] = useState(false); | ||
const dropdownRef = useRef<HTMLDivElement>(null); | ||
const { user, logout } = useAuth(); | ||
|
||
useOutsideClick(dropdownRef, () => setIsOpen(false)); | ||
|
||
return ( | ||
<div ref={dropdownRef} className="relative"> | ||
<button | ||
onClick={() => setIsOpen(!isOpen)} | ||
className="flex items-center gap-2 px-3 py-1.5 bg-gray-800 hover:bg-gray-700 | ||
rounded-md transition-colors text-sm" | ||
> | ||
<User size={16} /> | ||
<span>{user?.username}</span> | ||
</button> | ||
|
||
{isOpen && ( | ||
<div | ||
className="absolute right-0 mt-1 w-48 bg-gray-800 rounded-md shadow-lg | ||
border border-gray-700 py-1 z-50" | ||
> | ||
<button | ||
onClick={() => { | ||
setIsOpen(false); | ||
logout(); | ||
}} | ||
className="w-full px-4 py-2 text-sm text-left text-gray-300 hover:bg-gray-700 | ||
flex items-center gap-2" | ||
> | ||
<LogOut size={16} /> | ||
<span>Sign out</span> | ||
</button> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.