feat(client): checkin

Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
2026-02-08 17:16:18 +08:00
parent 79ccd0036e
commit a315eea087
16 changed files with 211 additions and 186 deletions

View File

@@ -10,6 +10,7 @@ export type DataEventIndexDoc = {
enable_kyc?: boolean; enable_kyc?: boolean;
end_time?: string; end_time?: string;
event_id?: string; event_id?: string;
is_checked_in?: boolean;
is_joined?: boolean; is_joined?: boolean;
join_count?: number; join_count?: number;
name?: string; name?: string;
@@ -501,16 +502,6 @@ export type GetEventAttendanceResponse = GetEventAttendanceResponses[keyof GetEv
export type GetEventCheckinData = { export type GetEventCheckinData = {
body?: never; body?: never;
headers: {
/**
* latest
*/
'X-Api-Version': string;
/**
* Bearer token
*/
Authorization: string;
};
path?: never; path?: never;
query: { query: {
/** /**
@@ -610,16 +601,6 @@ export type PostEventCheckinSubmitData = {
* Checkin Code Data * Checkin Code Data
*/ */
body: ServiceEventCheckinSubmitData; body: ServiceEventCheckinSubmitData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
/**
* Bearer token
*/
Authorization: string;
};
path?: never; path?: never;
query?: never; query?: never;
url: '/event/checkin/submit'; url: '/event/checkin/submit';

View File

@@ -8,6 +8,7 @@ export const zDataEventIndexDoc = z.object({
enable_kyc: z.boolean().optional(), enable_kyc: z.boolean().optional(),
end_time: z.string().optional(), end_time: z.string().optional(),
event_id: z.string().optional(), event_id: z.string().optional(),
is_checked_in: z.boolean().optional(),
is_joined: z.boolean().optional(), is_joined: z.boolean().optional(),
join_count: z.number().int().optional(), join_count: z.number().int().optional(),
name: z.string().optional(), name: z.string().optional(),
@@ -234,10 +235,6 @@ export const zGetEventCheckinData = z.object({
path: z.never().optional(), path: z.never().optional(),
query: z.object({ query: z.object({
event_id: z.string() event_id: z.string()
}),
headers: z.object({
'X-Api-Version': z.string(),
Authorization: z.string()
}) })
}); });
@@ -266,11 +263,7 @@ export const zGetEventCheckinQueryResponse = zUtilsRespStatus.and(z.object({
export const zPostEventCheckinSubmitData = z.object({ export const zPostEventCheckinSubmitData = z.object({
body: zServiceEventCheckinSubmitData, body: zServiceEventCheckinSubmitData,
path: z.never().optional(), path: z.never().optional(),
query: z.never().optional(), query: z.never().optional()
headers: z.object({
'X-Api-Version': z.string(),
Authorization: z.string()
})
}); });
/** /**

View File

@@ -0,0 +1,28 @@
import type { EventInfo } from '../events/types';
import { isNil } from 'lodash-es';
import { useState } from 'react';
import { useCheckinCode } from '@/hooks/data/useCheckinCode';
import { Dialog } from '../ui/dialog';
import { CheckinQrDialogError } from './checkin-qr.dialog.error';
import { CheckinQrDialogSkeleton } from './checkin-qr.dialog.skeleton';
import { CheckinQrDialogView } from './checkin-qr.dialog.view';
export function CheckinQrDialogContainer({ event, children }: { event: EventInfo; children: React.ReactNode }) {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const { data, isLoading, isError } = useCheckinCode(event.eventId, isDialogOpen);
return (
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
{children}
{isLoading && (
<CheckinQrDialogSkeleton />
)}
{isError && (
<CheckinQrDialogError />
)}
{!isLoading && !isError && !isNil(data) && (
<CheckinQrDialogView checkinCode={String(data.data!.checkin_code)} />
)}
</Dialog>
);
}

View File

@@ -0,0 +1,18 @@
import { TicketX } from 'lucide-react';
import { DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog';
export function CheckinQrDialogError() {
return (
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
<div className="flex justify-center my-12">
<TicketX size={100} className="stroke-[1.5]" />
</div>
</DialogDescription>
</DialogHeader>
</DialogContent>
);
}

View File

@@ -0,0 +1,23 @@
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../ui/dialog';
import { QRCode } from '../ui/shadcn-io/qr-code';
export function CheckinQrDialogSkeleton() {
return (
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
</DialogDescription>
</DialogHeader>
<div className="border-2 px-4 py-8 border-muted rounded-xl flex flex-col items-center justify-center p-4">
<QRCode data="welcome to join the conference" className="size-60 blur-sm" />
</div>
<DialogFooter>
<div className="flex flex-1 items-center ml-2 text-2xl text-primary/80 justify-center">
...
</div>
</DialogFooter>
</DialogContent>
);
}

View File

@@ -0,0 +1,23 @@
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../ui/dialog';
import { QRCode } from '../ui/shadcn-io/qr-code';
export function CheckinQrDialogView({ checkinCode }: { checkinCode: string }) {
return (
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
</DialogDescription>
</DialogHeader>
<div className="border-2 px-4 py-8 border-muted rounded-xl flex flex-col items-center justify-center p-4">
<QRCode data={checkinCode} className="size-60" />
</div>
<DialogFooter>
<div className="flex flex-1 items-center ml-2 font-mono text-2xl tracking-widest text-primary/80 justify-center">
{checkinCode}
</div>
</DialogFooter>
</DialogContent>
);
}

View File

@@ -1,70 +0,0 @@
import { useState } from 'react';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import { QRCode } from '@/components/ui/shadcn-io/qr-code';
import { Button } from '../ui/button';
export function QrDialog(
{ eventId }: { eventId: string },
) {
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button className="w-20"></Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>QR Code</DialogTitle>
<DialogDescription>
</DialogDescription>
</DialogHeader>
<QrSection eventId={eventId} enabled={open} />
</DialogContent>
</Dialog>
);
}
function QrSection({ eventId, enabled }: { eventId: string; enabled: boolean }) {
// const { data } = useCheckinCode(eventId, enabled);
const data = { data: { checkin_code: `dummy${eventId}${enabled}` } };
return data
? (
<>
<div className="border-2 px-4 py-8 border-muted rounded-xl flex flex-col items-center justify-center p-4">
<QRCode data={data.data.checkin_code} className="size-60" />
</div>
<DialogFooter>
<div className="flex flex-1 items-center ml-2 font-mono text-3xl tracking-widest text-primary/80 justify-center">
{data.data.checkin_code}
</div>
</DialogFooter>
</>
)
: (
<QrSectionSkeleton />
);
}
function QrSectionSkeleton() {
return (
<>
<div className="border-2 px-4 py-8 border-muted rounded-xl flex flex-col items-center justify-center p-4">
<QRCode data="114514" className="size-60 blur-xs" />
</div>
<DialogFooter>
<div className="flex flex-1 items-center ml-2 font-mono text-3xl tracking-widest text-primary/80 justify-center">
Loading...
</div>
</DialogFooter>
</>
);
}

View File

@@ -7,12 +7,6 @@ import { toEventInfo } from '../types';
import { EventGridSkeleton } from './event-grid.skeleton'; import { EventGridSkeleton } from './event-grid.skeleton';
import { EventGridView } from './event-grid.view'; import { EventGridView } from './event-grid.view';
function JoinButton() {
return (
<Button className="w-full"></Button>
);
}
export function EventGridContainer() { export function EventGridContainer() {
const { data, isLoading } = useEvents(); const { data, isLoading } = useEvents();
@@ -29,14 +23,14 @@ export function EventGridContainer() {
? ( ? (
<KycDialogContainer event={eventInfo}> <KycDialogContainer event={eventInfo}>
<DialogTrigger asChild> <DialogTrigger asChild>
<JoinButton /> <Button className="w-full"></Button>
</DialogTrigger> </DialogTrigger>
</KycDialogContainer> </KycDialogContainer>
) )
: ( : (
<EventJoinDialogContainer event={eventInfo}> <EventJoinDialogContainer event={eventInfo}>
<DialogTrigger asChild> <DialogTrigger asChild>
<JoinButton /> <Button className="w-full"></Button>
</DialogTrigger> </DialogTrigger>
</EventJoinDialogContainer> </EventJoinDialogContainer>
) )

View File

@@ -1,16 +1,31 @@
import type { EventInfo } from './types'; import type { EventInfo } from './types';
import { useNavigate } from '@tanstack/react-router';
import { useJoinedEvents } from '@/hooks/data/useJoinedEvents'; import { useJoinedEvents } from '@/hooks/data/useJoinedEvents';
import { isInDateRange } from '@/lib/utils'; import { isInDateRange } from '@/lib/utils';
import { CheckinQrDialogContainer } from '../checkin/checkin-qr.dialog.container';
import { Button } from '../ui/button'; import { Button } from '../ui/button';
import { DialogTrigger } from '../ui/dialog';
import { EventGridSkeleton } from './event-grid/event-grid.skeleton'; import { EventGridSkeleton } from './event-grid/event-grid.skeleton';
import { EventGridView } from './event-grid/event-grid.view'; import { EventGridView } from './event-grid/event-grid.view';
import { toEventInfo } from './types'; import { toEventInfo } from './types';
export function JoinedEventGridFooter({ event }: { event: EventInfo }) { export function JoinedEventGridFooter({ event }: { event: EventInfo }) {
const isOutOfDateRange = !isInDateRange(event.startTime, event.endTime);
const isCheckedIn = event.isCheckedIn;
const canCheckIn = !isOutOfDateRange && !isCheckedIn;
const navigate = useNavigate();
return ( return (
<div className="flex flex-row justify-between w-full gap-4"> <div className="flex flex-row justify-between w-full gap-4">
<Button className="flex-1" disabled={!isInDateRange(event.startTime, event.endTime)}></Button> <CheckinQrDialogContainer event={event}>
<Button className="flex-1"></Button> <DialogTrigger asChild>
<Button className="flex-1" disabled={isOutOfDateRange}>
{isOutOfDateRange && '未到签到时间'}
{isCheckedIn && '已签到'}
{canCheckIn && '签到'}
</Button>
</DialogTrigger>
</CheckinQrDialogContainer>
<Button className="flex-1" onClick={() => void navigate({ to: `/events/$eventId`, params: { eventId: event.eventId } })}></Button>
</div> </div>
); );
} }

View File

@@ -1,12 +1,11 @@
import type { EventInfo } from '../types'; import type { EventInfo } from '../types';
import type { KycSubmission } from './kyc.types'; import type { KycSubmission } from './kyc.types';
import { Dialog } from '@radix-ui/react-dialog'; import { Dialog } from '@radix-ui/react-dialog';
import { useQueryClient } from '@tanstack/react-query';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { postEventJoin, postKycQuery } from '@/client'; import { postKycQuery } from '@/client';
import { getEventListInfiniteQueryKey } from '@/client/@tanstack/react-query.gen';
import { useCreateKycSession } from '@/hooks/data/useCreateKycSession'; import { useCreateKycSession } from '@/hooks/data/useCreateKycSession';
import { useJoinEvent } from '@/hooks/data/useJoinEvent';
import { KycFailedDialogView } from './kyc-failed.dialog.view'; import { KycFailedDialogView } from './kyc-failed.dialog.view';
import { KycMethodSelectionDialogView } from './kyc-method-selection.dialog.view'; import { KycMethodSelectionDialogView } from './kyc-method-selection.dialog.view';
import { KycPendingDialogView } from './kyc-pending.dialog.view'; import { KycPendingDialogView } from './kyc-pending.dialog.view';
@@ -22,12 +21,12 @@ export function KycDialogContainer({ event, children }: { event: EventInfo; chil
const setStage = useStore(store, s => s.setStage); const setStage = useStore(store, s => s.setStage);
const setKycId = useStore(store, s => s.setKycId); const setKycId = useStore(store, s => s.setKycId);
const { mutateAsync } = useCreateKycSession(); const { mutateAsync: createKycSessionAsync } = useCreateKycSession();
const queryClient = useQueryClient(); const { mutateAsync: joinEventAsync } = useJoinEvent();
const joinEvent = useCallback(async (eventId: string, kycId: string, abortSignal?: AbortSignal) => { const joinEvent = useCallback(async (eventId: string, kycId: string, abortSignal?: AbortSignal) => {
try { try {
await postEventJoin({ await joinEventAsync({
signal: abortSignal, signal: abortSignal,
body: { event_id: eventId, kyc_id: kycId }, body: { event_id: eventId, kyc_id: kycId },
}); });
@@ -37,11 +36,11 @@ export function KycDialogContainer({ event, children }: { event: EventInfo; chil
console.error('Error joining event:', e); console.error('Error joining event:', e);
setStage('failed'); setStage('failed');
} }
}, [setStage]); }, [joinEventAsync, setStage]);
const onKycSessionCreate = useCallback(async (submission: KycSubmission) => { const onKycSessionCreate = useCallback(async (submission: KycSubmission) => {
try { try {
const { data } = await mutateAsync(submission); const { data } = await createKycSessionAsync(submission);
setKycId(data!.kyc_id!); setKycId(data!.kyc_id!);
if (data!.status === 'success') { if (data!.status === 'success') {
await joinEvent(event.eventId, data!.kyc_id!, undefined); await joinEvent(event.eventId, data!.kyc_id!, undefined);
@@ -55,7 +54,7 @@ export function KycDialogContainer({ event, children }: { event: EventInfo; chil
console.error(e); console.error(e);
setStage('failed'); setStage('failed');
} }
}, [event.eventId, joinEvent, mutateAsync, setKycId, setStage]); }, [event.eventId, joinEvent, createKycSessionAsync, setKycId, setStage]);
useEffect(() => { useEffect(() => {
if (stage !== 'pending' || !isDialogOpen) { if (stage !== 'pending' || !isDialogOpen) {
@@ -107,14 +106,7 @@ export function KycDialogContainer({ event, children }: { event: EventInfo; chil
return ( return (
<Dialog <Dialog
open={isDialogOpen} open={isDialogOpen}
onOpenChange={(open) => { onOpenChange={setIsDialogOpen}
if (!open) {
void queryClient.invalidateQueries({
queryKey: getEventListInfiniteQueryKey({ query: {} }),
});
}
setIsDialogOpen(open);
}}
> >
{children} {children}
{stage === 'prompt' && <KycPromptDialogView next={() => setStage('methodSelection')} />} {stage === 'prompt' && <KycPromptDialogView next={() => setStage('methodSelection')} />}

View File

@@ -5,6 +5,7 @@ export interface EventInfo {
type: 'official' | 'party'; type: 'official' | 'party';
eventId: string; eventId: string;
isJoined: boolean; isJoined: boolean;
isCheckedIn: boolean;
requireKyc: boolean; requireKyc: boolean;
coverImage: string; coverImage: string;
eventName: string; eventName: string;
@@ -19,6 +20,7 @@ export function toEventInfo(raw: DataEventIndexDoc): EventInfo {
eventId: raw.event_id!, eventId: raw.event_id!,
isJoined: raw.is_joined!, isJoined: raw.is_joined!,
requireKyc: raw.enable_kyc!, requireKyc: raw.enable_kyc!,
isCheckedIn: raw.is_checked_in ?? false,
coverImage: raw.thumbnail! || PlaceholderImage, coverImage: raw.thumbnail! || PlaceholderImage,
eventName: raw.name!, eventName: raw.name!,
description: raw.description!, description: raw.description!,

View File

@@ -0,0 +1,9 @@
import { useQuery } from '@tanstack/react-query';
import { getEventCheckinOptions } from '@/client/@tanstack/react-query.gen';
export function useCheckinCode(eventId: string, enabled: boolean) {
return useQuery({
...getEventCheckinOptions({ query: { event_id: eventId } }),
enabled,
});
}

View File

@@ -1,8 +1,21 @@
import { useMutation } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { postEventJoinMutation } from '@/client/@tanstack/react-query.gen'; import {
getEventJoinedInfiniteQueryKey,
getEventListInfiniteQueryKey,
postEventJoinMutation,
} from '@/client/@tanstack/react-query.gen';
export function useJoinEvent() { export function useJoinEvent() {
const queryClient = useQueryClient();
return useMutation({ return useMutation({
...postEventJoinMutation(), ...postEventJoinMutation(),
onSuccess: () => {
void queryClient.invalidateQueries({
queryKey: getEventListInfiniteQueryKey(),
});
void queryClient.invalidateQueries({
queryKey: getEventJoinedInfiniteQueryKey(),
});
},
}); });
} }

View File

@@ -15,7 +15,6 @@ import { Route as AuthorizeRouteImport } from './routes/authorize'
import { Route as WorkbenchLayoutRouteImport } from './routes/_workbenchLayout' import { Route as WorkbenchLayoutRouteImport } from './routes/_workbenchLayout'
import { Route as WorkbenchLayoutIndexRouteImport } from './routes/_workbenchLayout/index' import { Route as WorkbenchLayoutIndexRouteImport } from './routes/_workbenchLayout/index'
import { Route as WorkbenchLayoutJoinedEventsRouteImport } from './routes/_workbenchLayout/joined-events' import { Route as WorkbenchLayoutJoinedEventsRouteImport } from './routes/_workbenchLayout/joined-events'
import { Route as WorkbenchLayoutEventsRouteImport } from './routes/_workbenchLayout/events'
import { Route as WorkbenchLayoutProfileIndexRouteImport } from './routes/_workbenchLayout/profile/index' import { Route as WorkbenchLayoutProfileIndexRouteImport } from './routes/_workbenchLayout/profile/index'
import { Route as WorkbenchLayoutEventsIndexRouteImport } from './routes/_workbenchLayout/events/index' import { Route as WorkbenchLayoutEventsIndexRouteImport } from './routes/_workbenchLayout/events/index'
import { Route as WorkbenchLayoutProfileUserIdRouteImport } from './routes/_workbenchLayout/profile/$userId' import { Route as WorkbenchLayoutProfileUserIdRouteImport } from './routes/_workbenchLayout/profile/$userId'
@@ -51,11 +50,6 @@ const WorkbenchLayoutJoinedEventsRoute =
path: '/joined-events', path: '/joined-events',
getParentRoute: () => WorkbenchLayoutRoute, getParentRoute: () => WorkbenchLayoutRoute,
} as any) } as any)
const WorkbenchLayoutEventsRoute = WorkbenchLayoutEventsRouteImport.update({
id: '/events',
path: '/events',
getParentRoute: () => WorkbenchLayoutRoute,
} as any)
const WorkbenchLayoutProfileIndexRoute = const WorkbenchLayoutProfileIndexRoute =
WorkbenchLayoutProfileIndexRouteImport.update({ WorkbenchLayoutProfileIndexRouteImport.update({
id: '/profile/', id: '/profile/',
@@ -64,9 +58,9 @@ const WorkbenchLayoutProfileIndexRoute =
} as any) } as any)
const WorkbenchLayoutEventsIndexRoute = const WorkbenchLayoutEventsIndexRoute =
WorkbenchLayoutEventsIndexRouteImport.update({ WorkbenchLayoutEventsIndexRouteImport.update({
id: '/', id: '/events/',
path: '/', path: '/events/',
getParentRoute: () => WorkbenchLayoutEventsRoute, getParentRoute: () => WorkbenchLayoutRoute,
} as any) } as any)
const WorkbenchLayoutProfileUserIdRoute = const WorkbenchLayoutProfileUserIdRoute =
WorkbenchLayoutProfileUserIdRouteImport.update({ WorkbenchLayoutProfileUserIdRouteImport.update({
@@ -76,9 +70,9 @@ const WorkbenchLayoutProfileUserIdRoute =
} as any) } as any)
const WorkbenchLayoutEventsEventIdRoute = const WorkbenchLayoutEventsEventIdRoute =
WorkbenchLayoutEventsEventIdRouteImport.update({ WorkbenchLayoutEventsEventIdRouteImport.update({
id: '/$eventId', id: '/events/$eventId',
path: '/$eventId', path: '/events/$eventId',
getParentRoute: () => WorkbenchLayoutEventsRoute, getParentRoute: () => WorkbenchLayoutRoute,
} as any) } as any)
export interface FileRoutesByFullPath { export interface FileRoutesByFullPath {
@@ -86,7 +80,6 @@ export interface FileRoutesByFullPath {
'/authorize': typeof AuthorizeRoute '/authorize': typeof AuthorizeRoute
'/magicLinkSent': typeof MagicLinkSentRoute '/magicLinkSent': typeof MagicLinkSentRoute
'/token': typeof TokenRoute '/token': typeof TokenRoute
'/events': typeof WorkbenchLayoutEventsRouteWithChildren
'/joined-events': typeof WorkbenchLayoutJoinedEventsRoute '/joined-events': typeof WorkbenchLayoutJoinedEventsRoute
'/events/$eventId': typeof WorkbenchLayoutEventsEventIdRoute '/events/$eventId': typeof WorkbenchLayoutEventsEventIdRoute
'/profile/$userId': typeof WorkbenchLayoutProfileUserIdRoute '/profile/$userId': typeof WorkbenchLayoutProfileUserIdRoute
@@ -110,7 +103,6 @@ export interface FileRoutesById {
'/authorize': typeof AuthorizeRoute '/authorize': typeof AuthorizeRoute
'/magicLinkSent': typeof MagicLinkSentRoute '/magicLinkSent': typeof MagicLinkSentRoute
'/token': typeof TokenRoute '/token': typeof TokenRoute
'/_workbenchLayout/events': typeof WorkbenchLayoutEventsRouteWithChildren
'/_workbenchLayout/joined-events': typeof WorkbenchLayoutJoinedEventsRoute '/_workbenchLayout/joined-events': typeof WorkbenchLayoutJoinedEventsRoute
'/_workbenchLayout/': typeof WorkbenchLayoutIndexRoute '/_workbenchLayout/': typeof WorkbenchLayoutIndexRoute
'/_workbenchLayout/events/$eventId': typeof WorkbenchLayoutEventsEventIdRoute '/_workbenchLayout/events/$eventId': typeof WorkbenchLayoutEventsEventIdRoute
@@ -125,7 +117,6 @@ export interface FileRouteTypes {
| '/authorize' | '/authorize'
| '/magicLinkSent' | '/magicLinkSent'
| '/token' | '/token'
| '/events'
| '/joined-events' | '/joined-events'
| '/events/$eventId' | '/events/$eventId'
| '/profile/$userId' | '/profile/$userId'
@@ -148,7 +139,6 @@ export interface FileRouteTypes {
| '/authorize' | '/authorize'
| '/magicLinkSent' | '/magicLinkSent'
| '/token' | '/token'
| '/_workbenchLayout/events'
| '/_workbenchLayout/joined-events' | '/_workbenchLayout/joined-events'
| '/_workbenchLayout/' | '/_workbenchLayout/'
| '/_workbenchLayout/events/$eventId' | '/_workbenchLayout/events/$eventId'
@@ -208,13 +198,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof WorkbenchLayoutJoinedEventsRouteImport preLoaderRoute: typeof WorkbenchLayoutJoinedEventsRouteImport
parentRoute: typeof WorkbenchLayoutRoute parentRoute: typeof WorkbenchLayoutRoute
} }
'/_workbenchLayout/events': {
id: '/_workbenchLayout/events'
path: '/events'
fullPath: '/events'
preLoaderRoute: typeof WorkbenchLayoutEventsRouteImport
parentRoute: typeof WorkbenchLayoutRoute
}
'/_workbenchLayout/profile/': { '/_workbenchLayout/profile/': {
id: '/_workbenchLayout/profile/' id: '/_workbenchLayout/profile/'
path: '/profile' path: '/profile'
@@ -224,10 +207,10 @@ declare module '@tanstack/react-router' {
} }
'/_workbenchLayout/events/': { '/_workbenchLayout/events/': {
id: '/_workbenchLayout/events/' id: '/_workbenchLayout/events/'
path: '/' path: '/events'
fullPath: '/events/' fullPath: '/events/'
preLoaderRoute: typeof WorkbenchLayoutEventsIndexRouteImport preLoaderRoute: typeof WorkbenchLayoutEventsIndexRouteImport
parentRoute: typeof WorkbenchLayoutEventsRoute parentRoute: typeof WorkbenchLayoutRoute
} }
'/_workbenchLayout/profile/$userId': { '/_workbenchLayout/profile/$userId': {
id: '/_workbenchLayout/profile/$userId' id: '/_workbenchLayout/profile/$userId'
@@ -238,42 +221,29 @@ declare module '@tanstack/react-router' {
} }
'/_workbenchLayout/events/$eventId': { '/_workbenchLayout/events/$eventId': {
id: '/_workbenchLayout/events/$eventId' id: '/_workbenchLayout/events/$eventId'
path: '/$eventId' path: '/events/$eventId'
fullPath: '/events/$eventId' fullPath: '/events/$eventId'
preLoaderRoute: typeof WorkbenchLayoutEventsEventIdRouteImport preLoaderRoute: typeof WorkbenchLayoutEventsEventIdRouteImport
parentRoute: typeof WorkbenchLayoutEventsRoute parentRoute: typeof WorkbenchLayoutRoute
} }
} }
} }
interface WorkbenchLayoutEventsRouteChildren {
WorkbenchLayoutEventsEventIdRoute: typeof WorkbenchLayoutEventsEventIdRoute
WorkbenchLayoutEventsIndexRoute: typeof WorkbenchLayoutEventsIndexRoute
}
const WorkbenchLayoutEventsRouteChildren: WorkbenchLayoutEventsRouteChildren = {
WorkbenchLayoutEventsEventIdRoute: WorkbenchLayoutEventsEventIdRoute,
WorkbenchLayoutEventsIndexRoute: WorkbenchLayoutEventsIndexRoute,
}
const WorkbenchLayoutEventsRouteWithChildren =
WorkbenchLayoutEventsRoute._addFileChildren(
WorkbenchLayoutEventsRouteChildren,
)
interface WorkbenchLayoutRouteChildren { interface WorkbenchLayoutRouteChildren {
WorkbenchLayoutEventsRoute: typeof WorkbenchLayoutEventsRouteWithChildren
WorkbenchLayoutJoinedEventsRoute: typeof WorkbenchLayoutJoinedEventsRoute WorkbenchLayoutJoinedEventsRoute: typeof WorkbenchLayoutJoinedEventsRoute
WorkbenchLayoutIndexRoute: typeof WorkbenchLayoutIndexRoute WorkbenchLayoutIndexRoute: typeof WorkbenchLayoutIndexRoute
WorkbenchLayoutEventsEventIdRoute: typeof WorkbenchLayoutEventsEventIdRoute
WorkbenchLayoutProfileUserIdRoute: typeof WorkbenchLayoutProfileUserIdRoute WorkbenchLayoutProfileUserIdRoute: typeof WorkbenchLayoutProfileUserIdRoute
WorkbenchLayoutEventsIndexRoute: typeof WorkbenchLayoutEventsIndexRoute
WorkbenchLayoutProfileIndexRoute: typeof WorkbenchLayoutProfileIndexRoute WorkbenchLayoutProfileIndexRoute: typeof WorkbenchLayoutProfileIndexRoute
} }
const WorkbenchLayoutRouteChildren: WorkbenchLayoutRouteChildren = { const WorkbenchLayoutRouteChildren: WorkbenchLayoutRouteChildren = {
WorkbenchLayoutEventsRoute: WorkbenchLayoutEventsRouteWithChildren,
WorkbenchLayoutJoinedEventsRoute: WorkbenchLayoutJoinedEventsRoute, WorkbenchLayoutJoinedEventsRoute: WorkbenchLayoutJoinedEventsRoute,
WorkbenchLayoutIndexRoute: WorkbenchLayoutIndexRoute, WorkbenchLayoutIndexRoute: WorkbenchLayoutIndexRoute,
WorkbenchLayoutEventsEventIdRoute: WorkbenchLayoutEventsEventIdRoute,
WorkbenchLayoutProfileUserIdRoute: WorkbenchLayoutProfileUserIdRoute, WorkbenchLayoutProfileUserIdRoute: WorkbenchLayoutProfileUserIdRoute,
WorkbenchLayoutEventsIndexRoute: WorkbenchLayoutEventsIndexRoute,
WorkbenchLayoutProfileIndexRoute: WorkbenchLayoutProfileIndexRoute, WorkbenchLayoutProfileIndexRoute: WorkbenchLayoutProfileIndexRoute,
} }

View File

@@ -1,14 +0,0 @@
import { createFileRoute } from '@tanstack/react-router';
import { EventGridContainer } from '@/components/events/event-grid/event-grid.container';
export const Route = createFileRoute('/_workbenchLayout/events')({
component: RouteComponent,
});
function RouteComponent() {
return (
<div className="py-4 px-6 md:gap-6 md:py-6 h-full">
<EventGridContainer />
</div>
);
}

View File

@@ -0,0 +1,48 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { CheckinQrDialogError } from '@/components/checkin/checkin-qr.dialog.error';
import { CheckinQrDialogSkeleton } from '@/components/checkin/checkin-qr.dialog.skeleton';
import { CheckinQrDialogView } from '@/components/checkin/checkin-qr.dialog.view';
import { Dialog } from '@/components/ui/dialog';
const meta = {
title: 'Events/CheckinQRDialog',
component: CheckinQrDialogView,
decorators: [
Story => (
<Dialog open={true}>
<Story />
</Dialog>
),
],
} satisfies Meta<typeof CheckinQrDialogView>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Prompt: Story = {
args: {
checkinCode: '114514',
},
};
export const Skeleton: Story = {
render: () => (
<Dialog open={true}>
<CheckinQrDialogSkeleton />
</Dialog>
),
args: {
checkinCode: '',
},
};
export const Error: Story = {
render: () => (
<Dialog open={true}>
<CheckinQrDialogError />
</Dialog>
),
args: {
checkinCode: '',
},
};