diff --git a/package.json b/package.json index d186d9a..75242a2 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "build": "next build && rm -rf out/dev" }, "dependencies": { + "@hookform/resolvers": "^3.1.1", "@mui/material": "^5.13.4", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec9e874..5763a43 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,6 +1,9 @@ lockfileVersion: '6.0' dependencies: + '@hookform/resolvers': + specifier: ^3.1.1 + version: 3.1.1(react-hook-form@7.44.3) '@mui/material': specifier: ^5.13.4 version: 5.13.4(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) @@ -2009,6 +2012,14 @@ packages: tslib: 2.5.3 dev: false + /@hookform/resolvers@3.1.1(react-hook-form@7.44.3): + resolution: {integrity: sha512-tS16bAUkqjITNSvbJuO1x7MXbn7Oe8ZziDTJdA9mMvsoYthnOOiznOTGBYwbdlYBgU+tgpI/BtTU3paRbCuSlg==} + peerDependencies: + react-hook-form: ^7.0.0 + dependencies: + react-hook-form: 7.44.3(react@18.2.0) + dev: false + /@humanwhocodes/config-array@0.11.10: resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} engines: {node: '>=10.10.0'} diff --git a/src/app/[locale]/account/signin/page.tsx b/src/app/[locale]/account/signin/page.tsx index 9619b78..27430d3 100644 --- a/src/app/[locale]/account/signin/page.tsx +++ b/src/app/[locale]/account/signin/page.tsx @@ -19,30 +19,63 @@ import { } from '@/components/ui/dropdown-menu'; import { Input } from '@/components/ui/input'; import { cn } from '@/lib/utils'; +import { zodResolver } from '@hookform/resolvers/zod'; import { ChevronsUpDown, Info, Loader2, XCircle } from 'lucide-react'; import { useTranslations } from 'next-intl'; import Link from 'next-intl/link'; -import React, { createRef, useCallback, useState } from 'react'; +import React, { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; type scope = 'player' | 'manager' | 'admin'; +const Schema = z.object({ + identifier: z.string().email({ message: 'Enter a valid email address!' }), +}); + +type Schema = z.infer; + export default function Signin() { + const defaultScope = + (typeof window !== 'undefined' && + (localStorage.getItem('signin.default-scope') as scope)) || + 'player'; + const t = useTranslations('pages.account.signin'); - const [scope, setScope] = useState('player'); + const [scope, setScope] = useState(defaultScope); const [status, setStatus] = useState<{ error?: boolean; message?: string; loading?: boolean; }>({}); - const emailRef = createRef(); - const submitEmail = useCallback(async () => { - const identifier = emailRef.current?.value; - if (!identifier) { - setStatus({ message: 'Please enter your email', error: true }); - return; + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(Schema), + }); + + // Dirty solution, will fix later :) + useEffect(() => { + const set = (message: string) => { + if (!status.error && status.message !== message) { + setStatus({ + error: true, + message, + }); + } + }; + + if (errors.root?.message) { + set(errors.root.message); + } else if (errors.identifier?.message) { + set(errors.identifier.message); } + }, [errors, status, setStatus]); + const handler = handleSubmit(async ({ identifier }) => { setStatus({ message: 'Loading', loading: true }); const raw = await fetch('/api/auth/magic-link', { @@ -66,11 +99,14 @@ export default function Signin() { error: true, }); } - }, [emailRef, scope]); + }); return ( -
+
@@ -111,7 +147,7 @@ export default function Signin() {
{status.message && ( @@ -137,9 +173,7 @@ export default function Signin() {
- + {t('link.create-account')} -
+ ); } diff --git a/src/lib/auth.ts b/src/lib/auth.ts index ae73580..824c1c7 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -9,17 +9,22 @@ import { create } from 'zustand'; type AnyUser = TAdminRecord | TManagerRecord | TPlayerRecord; export type AuthStore = { - user?: AnyUser; + user?: AnyUser & { scope: string }; loading: boolean; - setUser: (user: AnyUser) => void; + setUser: (user: AnyUser & { scope: string }) => void; refreshUser: () => void; }; export const useAuth = create((set) => { function updateAuth() { surreal - .info() - .then((user) => set(() => ({ user, loading: false }))); + .query<[(AnyUser & { scope: string })[]]>( + /* surrealql */ `SELECT *, scope = meta::tb(id) FROM $auth` + ) + .then((res) => { + const user = res?.[0]?.result?.[0]; + if (user) set(() => ({ user: user, loading: false })); + }); } setInterval(updateAuth, 60000);