import { useState, type FormEventHandler } from "react"; import Icon, { ICONS } from "./Icon"; import Modal from "./Modal"; import { useLocation, useNavigate } from "react-router"; import { API } from "~/api/api"; import { getApiErrorMessage, useAuth } from "~/context/AuthContext"; export default function Header() { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const { user, login, logout, isLoading } = useAuth(); const navigate = useNavigate(); const onBack = () => { navigate("/") }; const onUploadClick= () => setIsUploadModalOpen(true) const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); const [isRegisterModalOpen, setIsRegisterModalOpen] = useState(false); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); const onTagsClick = () => { navigate("/tags") } const onLoginClick = () => { setIsLoginModalOpen(true); } const onRegisterClick = () => { setIsRegisterModalOpen(true); } const onLogout = () => { logout(); if(isMobileMenuOpen) setIsMobileMenuOpen(false); } type NavButtonProps = { onClick: () => void; icon: string; children: React.ReactNode; className?: string; }; const NavButton = ({ onClick, icon, children, className = '' }: NavButtonProps) => ( {children} ); type MobileNavLinkProps = { onClick: () => void; icon: string; children: React.ReactNode; className?: string; }; const MobileNavLink = ({ onClick, icon, children, className = '' }: MobileNavLinkProps) => ( { e.preventDefault(); onClick(); setIsMobileMenuOpen(false); }} className={`flex items-center space-x-3 px-3 py-2 text-base font-medium rounded-md ${className}`}> {children} ); const UploadModal = ({ isOpen, onClose }: { isOpen: boolean; onClose: () => void; }) => { const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const [tags, setTags] = useState(''); const [imageFile, setImageFile] = useState(null); const [error, setError] = useState(''); const handleSubmit: FormEventHandler = async (e) => { e.preventDefault(); setError(''); if (!user) return; const enteredTags = tags.split(' ').filter(t => t); if (!title || !imageFile || !description) { setError('Please fill in all fields and select an image.'); return; } try { const response = await API.createPostRequest( title, description, enteredTags, imageFile.name, imageFile.type, imageFile.size, user ); const data = await response.json(); if (!response.ok) { setError(getApiErrorMessage(data)); return; } const uploadUrl = data.fileUrl; if (!uploadUrl) { setError('Upload URL not provided by server.'); return; } const uploadResponse = await API.patchFileUploadUrl(uploadUrl, imageFile, user); if (!uploadResponse.ok) { const uploadError = await uploadResponse.json(); setError(getApiErrorMessage(uploadError)); return; } navigate(`/posts/${data.id}`); onClose(); } catch (err: any) { setError(getApiErrorMessage(err)); } }; return ( Upload a Post Title setTitle(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="e.g., My Awesome Picture"/> Description setDescription(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="A short description of your image..."/> Tags (space-separated) setTags(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="e.g., high-res"/> Image { const files = e.target.files if (files && files.length > 0) setImageFile(files[0]) }} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"/> {error && {error}} Upload ); }; const RegisterModal = ({ isOpen, onClose }: { isOpen: boolean; onClose: () => void; }) => { const [username, setUsername] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [error, setError] = useState(''); const handleSubmit: FormEventHandler = async (e) => { e.preventDefault(); setError(''); if (password !== confirmPassword) { setError("Passwords do not match."); return; } try { const response = await API.register(username, email, password); if (!response.ok) { const data = await response.json(); setError(getApiErrorMessage(data)); return; } // Automatically log in the user after successful registration const loginResponse = await API.login(email, password); if (loginResponse.ok) { const loginData = await loginResponse.json(); login(loginData.accessToken, loginData.refreshToken); } onClose(); setUsername(''); setEmail(''); setPassword(''); setConfirmPassword(''); } catch (err: any) { setError(getApiErrorMessage(err)); } }; return ( Register Username setUsername(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="Enter username" required/> Email setEmail(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="Enter email" required/> Password setPassword(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="Enter password" required/> Confirm Password setConfirmPassword(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" placeholder="Confirm password" required/> {error && {error}} Register ); }; const LoginModal = ({ isOpen, onClose }: { isOpen: boolean; onClose: () => void; }) => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const handleSubmit: FormEventHandler = async (e) => { e.preventDefault(); setError(''); try { const response = await API.login(username, password); if (!response.ok) { const err = await response.json(); setError(getApiErrorMessage(err)); return; } const data = await response.json(); login(data.accessToken, data.refreshToken); onClose(); setUsername(''); setPassword(''); } catch (err: any) { setError(getApiErrorMessage(err)); } }; return ( Login Email setUsername(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="Enter email"/> Password setPassword(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" placeholder="Enter password"/> {error && {error}} Sign In { e.preventDefault(); onClose(); onRegisterClick(); }} className="inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800"> Create an Account ); }; return ( <> ImgBoard {/* Desktop Nav */} Tags {isLoading ? ( Loading... ) : user ? ( <> Upload Logout > ) : ( <> Login Register > )} {/* Mobile Menu Button */} setIsMobileMenuOpen(!isMobileMenuOpen)} className="p-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-100"> {/* Mobile Menu */} {isMobileMenuOpen && ( Tags {isLoading ? ( Loading... ) : user ? ( <> Upload Image Logout > ) : ( <> Login Register > )} )} setIsLoginModalOpen(false)}/> setIsRegisterModalOpen(false)}/> setIsUploadModalOpen(false)}/> > ); }
{error}