refactor: improve code quality
All checks were successful
Backend Check Build (NixCN CMS) TeamCity build finished
Client CMS Check Build (NixCN CMS) TeamCity build finished

Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
2026-02-13 11:56:25 +08:00
parent 545facba22
commit d230d7474e
5 changed files with 89 additions and 67 deletions

View File

@@ -1,3 +1,4 @@
import { useMemo } from 'react';
import { useEvents } from '@/hooks/data/useEvents'; import { useEvents } from '@/hooks/data/useEvents';
import { Button } from '../../ui/button'; import { Button } from '../../ui/button';
import { DialogTrigger } from '../../ui/dialog'; import { DialogTrigger } from '../../ui/dialog';
@@ -10,22 +11,28 @@ import { EventGridView } from './event-grid.view';
export function EventGridContainer() { export function EventGridContainer() {
const { data, isLoading } = useEvents(); const { data, isLoading } = useEvents();
const events = useMemo(() => {
return data?.pages.flatMap(page => page.data ?? []).map(toEventInfo) ?? [];
}, [data]);
return ( return (
isLoading isLoading
? <EventGridSkeleton /> ? <EventGridSkeleton />
: ( : (
<EventGridView <EventGridView
events={data.pages.flatMap(page => page.data ?? []).map(toEventInfo)} events={events}
footer={(eventInfo) => { footer={(eventInfo) => {
const Container = eventInfo.requireKyc ? KycDialogContainer : EventJoinDialogContainer; const Container = eventInfo.requireKyc ? KycDialogContainer : EventJoinDialogContainer;
return ( return (
<Container event={eventInfo}> <Container event={eventInfo}>
{eventInfo.isJoined ? ( {eventInfo.isJoined
? (
<Button className="w-full" disabled> <Button className="w-full" disabled>
</Button> </Button>
) : ( )
: (
<DialogTrigger asChild> <DialogTrigger asChild>
<Button className="w-full"></Button> <Button className="w-full"></Button>
</DialogTrigger> </DialogTrigger>

View File

@@ -1,14 +1,17 @@
import type { TurnstileInstance } from '@marsidev/react-turnstile'; import type { TurnstileInstance } from '@marsidev/react-turnstile';
import type { AuthorizeSearchParams } from '@/routes/authorize'; import type { AuthorizeSearchParams } from '@/routes/authorize';
import { Turnstile } from '@marsidev/react-turnstile'; import { Turnstile } from '@marsidev/react-turnstile';
import { useForm } from '@tanstack/react-form';
import { useNavigate } from '@tanstack/react-router'; import { useNavigate } from '@tanstack/react-router';
import { Loader2 } from 'lucide-react'; import { Loader2 } from 'lucide-react';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import z from 'zod';
import NixOSLogo from '@/assets/nixos.svg?react'; import NixOSLogo from '@/assets/nixos.svg?react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Field, Field,
FieldError,
FieldGroup, FieldGroup,
FieldLabel, FieldLabel,
} from '@/components/ui/field'; } from '@/components/ui/field';
@@ -23,29 +26,43 @@ export function LoginForm({
}: React.ComponentProps<'div'> & { }: React.ComponentProps<'div'> & {
oauthParams: AuthorizeSearchParams; oauthParams: AuthorizeSearchParams;
}) { }) {
const formRef = useRef<HTMLFormElement>(null);
const turnstileRef = useRef<TurnstileInstance>(null); const turnstileRef = useRef<TurnstileInstance>(null);
const [token, setToken] = useState<string | null>(import.meta.env.DEV ? 'turnstile_token' : null); const [token, setToken] = useState<string | null>(import.meta.env.DEV ? 'turnstile_token' : null);
const { mutateAsync, isPending } = useGetMagicLink(); const { mutateAsync, isPending } = useGetMagicLink();
const navigate = useNavigate(); const navigate = useNavigate();
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { const form = useForm({
event.preventDefault(); defaultValues: {
const formData = new FormData(formRef.current!); email: '',
const email = formData.get('email')! as string; },
mutateAsync({ body: { email, turnstile_token: token!, ...oauthParams } }).then(() => { validators: {
void navigate({ to: '/magicLinkSent', search: { email } }); onSubmit: z.object({
}).catch((error) => { email: z.string().email('请输入有效的邮箱地址'),
}),
},
onSubmit: async ({ value }) => {
try {
await mutateAsync({ body: { email: value.email, turnstile_token: token!, ...oauthParams } });
await navigate({ to: '/magicLinkSent', search: { email: value.email } });
}
catch (error) {
console.error(error); console.error(error);
toast.error('请求登录链接失败'); toast.error('请求登录链接失败');
turnstileRef.current?.reset(); turnstileRef.current?.reset();
}
},
}); });
};
const isLoading = isPending || token === null; const isLoading = isPending || token === null;
return ( return (
<div className={cn('flex flex-col gap-6', className)} {...props}> <div className={cn('flex flex-col gap-6', className)} {...props}>
<form ref={formRef} onSubmit={handleSubmit}> <form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
void form.handleSubmit();
}}
>
<FieldGroup> <FieldGroup>
<div className="flex flex-col items-center gap-2 text-center"> <div className="flex flex-col items-center gap-2 text-center">
<div className="flex size-8 items-center justify-center rounded-md"> <div className="flex size-8 items-center justify-center rounded-md">
@@ -54,6 +71,8 @@ export function LoginForm({
<span className="sr-only">Nix CN CMS</span> <span className="sr-only">Nix CN CMS</span>
<h1 className="text-xl font-bold"> Nix CN CMS</h1> <h1 className="text-xl font-bold"> Nix CN CMS</h1>
</div> </div>
<form.Field name="email">
{field => (
<Field> <Field>
<FieldLabel htmlFor="email">Email</FieldLabel> <FieldLabel htmlFor="email">Email</FieldLabel>
<Input <Input
@@ -62,8 +81,14 @@ export function LoginForm({
type="email" type="email"
placeholder="edolstra@gmail.com" placeholder="edolstra@gmail.com"
required required
value={field.state.value}
onBlur={field.handleBlur}
onChange={e => field.handleChange(e.target.value)}
/> />
<FieldError errors={field.state.meta.errors} />
</Field> </Field>
)}
</form.Field>
<Field> <Field>
<Button type="submit" disabled={isLoading}> <Button type="submit" disabled={isLoading}>
{isLoading && <Loader2 className="animate-spin" />} {isLoading && <Loader2 className="animate-spin" />}

View File

@@ -1,4 +1,3 @@
import type { Query } from '@tanstack/react-query';
import type { ClassValue } from 'clsx'; import type { ClassValue } from 'clsx';
// eslint-disable-next-line unicorn/prefer-node-protocol // eslint-disable-next-line unicorn/prefer-node-protocol
import { Buffer } from 'buffer'; import { Buffer } from 'buffer';
@@ -19,15 +18,6 @@ export function utf8ToBase64(utf8: string): string {
return Buffer.from(utf8, 'utf-8').toString('base64'); return Buffer.from(utf8, 'utf-8').toString('base64');
} }
export function invalidateBlurry(id: string) {
return {
predicate: (query: Query<unknown, Error, unknown, readonly unknown[]>) => {
const key = query.queryKey[0] as { _id: string };
return key?._id === id;
},
};
}
export function isInDateRange(start: Date, end: Date, target: Date = new Date()): boolean { export function isInDateRange(start: Date, end: Date, target: Date = new Date()): boolean {
const time = target.getTime(); const time = target.getTime();
return time >= start.getTime() && time <= end.getTime(); return time >= start.getTime() && time <= end.getTime();