diff --git a/src/client/components/UserMenu.tsx b/src/client/components/UserMenu.tsx index 4704650..a27edc4 100644 --- a/src/client/components/UserMenu.tsx +++ b/src/client/components/UserMenu.tsx @@ -43,7 +43,7 @@ export function UserMenu() { type="button" onClick={() => { setOpen(false); - logout.mutate(); + logout(); }} className="flex items-center gap-2 w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors" > diff --git a/src/client/hooks/useAuth.ts b/src/client/hooks/useAuth.ts index 88e6e40..25e6754 100644 --- a/src/client/hooks/useAuth.ts +++ b/src/client/hooks/useAuth.ts @@ -1,9 +1,9 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { apiDelete, apiGet, apiPost, apiPut } from "../lib/api"; +import { apiDelete, apiGet, apiPost } from "../lib/api"; interface AuthState { - user: { id: number } | null; - setupRequired: boolean; + user: { id: string; email?: string } | null; + authenticated: boolean; } export function useAuth() { @@ -14,43 +14,11 @@ export function useAuth() { }); } -export function useLogin() { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (data: { username: string; password: string }) => - apiPost<{ username: string }>("/api/auth/login", data), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["auth"] }); - }, - }); -} - export function useLogout() { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: () => apiPost<{ success: boolean }>("/api/auth/logout", {}), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["auth"] }); - }, - }); -} - -export function useSetup() { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (data: { username: string; password: string }) => - apiPost<{ username: string }>("/api/auth/setup", data), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["auth"] }); - }, - }); -} - -export function useChangePassword() { - return useMutation({ - mutationFn: (data: { currentPassword: string; newPassword: string }) => - apiPut<{ success: boolean }>("/api/auth/password", data), - }); + const logout = () => { + window.location.href = "/logout"; + }; + return { logout }; } interface ApiKeyListItem { diff --git a/src/client/routes/login.tsx b/src/client/routes/login.tsx index 888f3ca..f5a62e3 100644 --- a/src/client/routes/login.tsx +++ b/src/client/routes/login.tsx @@ -1,6 +1,6 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router"; -import { useState } from "react"; -import { useAuth, useLogin, useSetup } from "../hooks/useAuth"; +import { useEffect } from "react"; +import { useAuth } from "../hooks/useAuth"; export const Route = createFileRoute("/login")({ component: LoginPage, @@ -8,96 +8,42 @@ export const Route = createFileRoute("/login")({ function LoginPage() { const navigate = useNavigate(); - const { data: auth } = useAuth(); - const login = useLogin(); - const setup = useSetup(); + const { data: auth, isLoading } = useAuth(); - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - const [error, setError] = useState(""); - - const isSetup = auth?.setupRequired ?? false; - - async function handleSubmit(e: React.FormEvent) { - e.preventDefault(); - setError(""); - - try { - if (isSetup) { - await setup.mutateAsync({ username, password }); - } else { - await login.mutateAsync({ username, password }); - } + useEffect(() => { + if (auth?.authenticated) { navigate({ to: "/" }); - } catch (err) { - setError((err as Error).message); } - } + }, [auth, navigate]); - const isPending = login.isPending || setup.isPending; + if (isLoading) { + return ( +
+

Loading...

+
+ ); + } return (

- {isSetup ? "Create Account" : "Sign In"} + Sign in to GearBox

- -
- {isSetup && ( -

- Create your admin account to manage your gear collection. -

- )} - -
- - setUsername(e.target.value)} - required - className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-200" - /> -
- -
- - setPassword(e.target.value)} - required - minLength={isSetup ? 6 : undefined} - className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-200" - /> -
- - {error &&

{error}

} - +
+

+ You will be redirected to sign in with your account. +

- +
); diff --git a/src/client/routes/settings.tsx b/src/client/routes/settings.tsx index 9d9f79a..f39c699 100644 --- a/src/client/routes/settings.tsx +++ b/src/client/routes/settings.tsx @@ -3,7 +3,6 @@ import { useRef, useState } from "react"; import { useApiKeys, useAuth, - useChangePassword, useCreateApiKey, useDeleteApiKey, } from "../hooks/useAuth"; @@ -16,9 +15,9 @@ import type { Currency, WeightUnit } from "../lib/formatters"; const UNITS: WeightUnit[] = ["g", "oz", "lb", "kg"]; const CURRENCIES: { value: Currency; label: string }[] = [ { value: "USD", label: "$" }, - { value: "EUR", label: "€" }, - { value: "GBP", label: "£" }, - { value: "JPY", label: "¥" }, + { value: "EUR", label: "\u20AC" }, + { value: "GBP", label: "\u00A3" }, + { value: "JPY", label: "\u00A5" }, { value: "CAD", label: "CA$" }, { value: "AUD", label: "A$" }, ]; @@ -27,66 +26,6 @@ export const Route = createFileRoute("/settings")({ component: SettingsPage, }); -function ChangePasswordSection() { - const changePassword = useChangePassword(); - const [currentPassword, setCurrentPassword] = useState(""); - const [newPassword, setNewPassword] = useState(""); - const [message, setMessage] = useState<{ - type: "success" | "error"; - text: string; - } | null>(null); - - async function handleSubmit(e: React.FormEvent) { - e.preventDefault(); - setMessage(null); - try { - await changePassword.mutateAsync({ currentPassword, newPassword }); - setMessage({ type: "success", text: "Password changed" }); - setCurrentPassword(""); - setNewPassword(""); - } catch (err) { - setMessage({ type: "error", text: (err as Error).message }); - } - } - - return ( -
-

Change Password

- setCurrentPassword(e.target.value)} - required - className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-200" - /> - setNewPassword(e.target.value)} - required - minLength={6} - className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-200" - /> - {message && ( -

- {message.text} -

- )} - -
- ); -} - function ApiKeySection() { const { data: keys } = useApiKeys(); const createKey = useCreateApiKey(); @@ -349,10 +288,8 @@ function SettingsPage() { - {auth?.user && ( + {auth?.authenticated && (
- -
)}