@@ -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';
|
||||||
|
|||||||
@@ -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()
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
client/cms/src/components/checkin/checkin-qr.dialog.view.tsx
Normal file
23
client/cms/src/components/checkin/checkin-qr.dialog.view.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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')} />}
|
||||||
|
|||||||
@@ -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!,
|
||||||
|
|||||||
9
client/cms/src/hooks/data/useCheckinCode.ts
Normal file
9
client/cms/src/hooks/data/useCheckinCode.ts
Normal 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,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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(),
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
48
client/cms/src/stories/events/checkin-dialog.stories.tsx
Normal file
48
client/cms/src/stories/events/checkin-dialog.stories.tsx
Normal 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: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user