Compare commits

...

4 Commits

Author SHA1 Message Date
c43c37a127 feat(client): add loading skeleton and global error handling components
All checks were successful
Client CMS Check Build (NixCN CMS) TeamCity build finished
Backend Check Build (NixCN CMS) TeamCity build finished
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-02-06 21:36:58 +08:00
5cf00407b4 refactor(client): remove excess api version header
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-02-06 20:58:23 +08:00
f4a5b37892 refactor: migrate error handling to TanStack Router and add RawError type
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-02-06 20:55:44 +08:00
3c4e078bdd refactor(profile): update error display to use Empty component
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-02-06 20:47:35 +08:00
29 changed files with 424 additions and 259 deletions

View File

@@ -3,8 +3,27 @@
import { type InfiniteData, infiniteQueryOptions, queryOptions, type UseMutationOptions } from '@tanstack/react-query';
import { client } from '../client.gen';
import { getAuthRedirect, getEventAttendance, getEventCheckin, getEventCheckinQuery, getEventInfo, getEventList, getUserInfo, getUserInfoByUserId, getUserList, type Options, patchUserUpdate, postAuthExchange, postAuthMagic, postAuthRefresh, postAuthToken, postEventCheckinSubmit, postEventJoin, postKycQuery, postKycSession } from '../sdk.gen';
import type { GetAuthRedirectData, GetAuthRedirectError, GetEventAttendanceData, GetEventAttendanceError, GetEventAttendanceResponse, GetEventCheckinData, GetEventCheckinError, GetEventCheckinQueryData, GetEventCheckinQueryError, GetEventCheckinQueryResponse, GetEventCheckinResponse, GetEventInfoData, GetEventInfoError, GetEventInfoResponse, GetEventListData, GetEventListError, GetEventListResponse, GetUserInfoByUserIdData, GetUserInfoByUserIdError, GetUserInfoByUserIdResponse, GetUserInfoData, GetUserInfoError, GetUserInfoResponse, GetUserListData, GetUserListError, GetUserListResponse, PatchUserUpdateData, PatchUserUpdateError, PatchUserUpdateResponse, PostAuthExchangeData, PostAuthExchangeError, PostAuthExchangeResponse, PostAuthMagicData, PostAuthMagicError, PostAuthMagicResponse, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshResponse, PostAuthTokenData, PostAuthTokenError, PostAuthTokenResponse, PostEventCheckinSubmitData, PostEventCheckinSubmitError, PostEventCheckinSubmitResponse, PostEventJoinData, PostEventJoinError, PostEventJoinResponse, PostKycQueryData, PostKycQueryError, PostKycQueryResponse, PostKycSessionData, PostKycSessionError, PostKycSessionResponse } from '../types.gen';
import { getAuthRedirect, getEventAttendance, getEventCheckin, getEventCheckinQuery, getEventInfo, getEventJoined, getEventList, getUserInfo, getUserInfoByUserId, getUserList, type Options, patchUserUpdate, postAgendaSubmit, postAuthExchange, postAuthMagic, postAuthRefresh, postAuthToken, postEventCheckinSubmit, postEventJoin, postKycQuery, postKycSession } from '../sdk.gen';
import type { GetAuthRedirectData, GetAuthRedirectError, GetEventAttendanceData, GetEventAttendanceError, GetEventAttendanceResponse, GetEventCheckinData, GetEventCheckinError, GetEventCheckinQueryData, GetEventCheckinQueryError, GetEventCheckinQueryResponse, GetEventCheckinResponse, GetEventInfoData, GetEventInfoError, GetEventInfoResponse, GetEventJoinedData, GetEventJoinedError, GetEventJoinedResponse, GetEventListData, GetEventListError, GetEventListResponse, GetUserInfoByUserIdData, GetUserInfoByUserIdError, GetUserInfoByUserIdResponse, GetUserInfoData, GetUserInfoError, GetUserInfoResponse, GetUserListData, GetUserListError, GetUserListResponse, PatchUserUpdateData, PatchUserUpdateError, PatchUserUpdateResponse, PostAgendaSubmitData, PostAgendaSubmitError, PostAgendaSubmitResponse, PostAuthExchangeData, PostAuthExchangeError, PostAuthExchangeResponse, PostAuthMagicData, PostAuthMagicError, PostAuthMagicResponse, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshResponse, PostAuthTokenData, PostAuthTokenError, PostAuthTokenResponse, PostEventCheckinSubmitData, PostEventCheckinSubmitError, PostEventCheckinSubmitResponse, PostEventJoinData, PostEventJoinError, PostEventJoinResponse, PostKycQueryData, PostKycQueryError, PostKycQueryResponse, PostKycSessionData, PostKycSessionError, PostKycSessionResponse } from '../types.gen';
/**
* Submit Agenda
*
* Creates a new agenda item for a specific attendance record.
*/
export const postAgendaSubmitMutation = (options?: Partial<Options<PostAgendaSubmitData>>): UseMutationOptions<PostAgendaSubmitResponse, PostAgendaSubmitError, Options<PostAgendaSubmitData>> => {
const mutationOptions: UseMutationOptions<PostAgendaSubmitResponse, PostAgendaSubmitError, Options<PostAgendaSubmitData>> = {
mutationFn: async (fnOptions) => {
const { data } = await postAgendaSubmit({
...options,
...fnOptions,
throwOnError: true
});
return data;
}
};
return mutationOptions;
};
/**
* Exchange Auth Code
@@ -253,16 +272,16 @@ export const postEventJoinMutation = (options?: Partial<Options<PostEventJoinDat
return mutationOptions;
};
export const getEventListQueryKey = (options: Options<GetEventListData>) => createQueryKey('getEventList', options);
export const getEventJoinedQueryKey = (options?: Options<GetEventJoinedData>) => createQueryKey('getEventJoined', options);
/**
* List Events
* Get Joined Events
*
* Fetches a list of events with support for pagination via limit and offset. Data is retrieved directly from the database for consistency.
* Fetches a list of events where the authenticated user is a participant. Supports pagination.
*/
export const getEventListOptions = (options: Options<GetEventListData>) => queryOptions<GetEventListResponse, GetEventListError, GetEventListResponse, ReturnType<typeof getEventListQueryKey>>({
export const getEventJoinedOptions = (options?: Options<GetEventJoinedData>) => queryOptions<GetEventJoinedResponse, GetEventJoinedError, GetEventJoinedResponse, ReturnType<typeof getEventJoinedQueryKey>>({
queryFn: async ({ queryKey, signal }) => {
const { data } = await getEventList({
const { data } = await getEventJoined({
...options,
...queryKey[0],
signal,
@@ -270,7 +289,7 @@ export const getEventListOptions = (options: Options<GetEventListData>) => query
});
return data;
},
queryKey: getEventListQueryKey(options)
queryKey: getEventJoinedQueryKey(options)
});
const createInfiniteParams = <K extends Pick<QueryKey<Options>[0], 'body' | 'headers' | 'path' | 'query'>>(queryKey: QueryKey<Options>, page: K) => {
@@ -302,14 +321,63 @@ const createInfiniteParams = <K extends Pick<QueryKey<Options>[0], 'body' | 'hea
return params as unknown as typeof page;
};
export const getEventListInfiniteQueryKey = (options: Options<GetEventListData>): QueryKey<Options<GetEventListData>> => createQueryKey('getEventList', options, true);
export const getEventJoinedInfiniteQueryKey = (options?: Options<GetEventJoinedData>): QueryKey<Options<GetEventJoinedData>> => createQueryKey('getEventJoined', options, true);
/**
* Get Joined Events
*
* Fetches a list of events where the authenticated user is a participant. Supports pagination.
*/
export const getEventJoinedInfiniteOptions = (options?: Options<GetEventJoinedData>) => infiniteQueryOptions<GetEventJoinedResponse, GetEventJoinedError, InfiniteData<GetEventJoinedResponse>, QueryKey<Options<GetEventJoinedData>>, number | Pick<QueryKey<Options<GetEventJoinedData>>[0], 'body' | 'headers' | 'path' | 'query'>>(
// @ts-ignore
{
queryFn: async ({ pageParam, queryKey, signal }) => {
// @ts-ignore
const page: Pick<QueryKey<Options<GetEventJoinedData>>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : {
query: {
offset: pageParam
}
};
const params = createInfiniteParams(queryKey, page);
const { data } = await getEventJoined({
...options,
...params,
signal,
throwOnError: true
});
return data;
},
queryKey: getEventJoinedInfiniteQueryKey(options)
});
export const getEventListQueryKey = (options?: Options<GetEventListData>) => createQueryKey('getEventList', options);
/**
* List Events
*
* Fetches a list of events with support for pagination via limit and offset. Data is retrieved directly from the database for consistency.
*/
export const getEventListInfiniteOptions = (options: Options<GetEventListData>) => infiniteQueryOptions<GetEventListResponse, GetEventListError, InfiniteData<GetEventListResponse>, QueryKey<Options<GetEventListData>>, number | Pick<QueryKey<Options<GetEventListData>>[0], 'body' | 'headers' | 'path' | 'query'>>(
export const getEventListOptions = (options?: Options<GetEventListData>) => queryOptions<GetEventListResponse, GetEventListError, GetEventListResponse, ReturnType<typeof getEventListQueryKey>>({
queryFn: async ({ queryKey, signal }) => {
const { data } = await getEventList({
...options,
...queryKey[0],
signal,
throwOnError: true
});
return data;
},
queryKey: getEventListQueryKey(options)
});
export const getEventListInfiniteQueryKey = (options?: Options<GetEventListData>): QueryKey<Options<GetEventListData>> => createQueryKey('getEventList', options, true);
/**
* List Events
*
* Fetches a list of events with support for pagination via limit and offset. Data is retrieved directly from the database for consistency.
*/
export const getEventListInfiniteOptions = (options?: Options<GetEventListData>) => infiniteQueryOptions<GetEventListResponse, GetEventListError, InfiniteData<GetEventListResponse>, QueryKey<Options<GetEventListData>>, number | Pick<QueryKey<Options<GetEventListData>>[0], 'body' | 'headers' | 'path' | 'query'>>(
// @ts-ignore
{
queryFn: async ({ pageParam, queryKey, signal }) => {
@@ -369,14 +437,14 @@ export const postKycSessionMutation = (options?: Partial<Options<PostKycSessionD
return mutationOptions;
};
export const getUserInfoQueryKey = (options: Options<GetUserInfoData>) => createQueryKey('getUserInfo', options);
export const getUserInfoQueryKey = (options?: Options<GetUserInfoData>) => createQueryKey('getUserInfo', options);
/**
* Get My User Information
*
* Fetches the complete profile data for the user associated with the provided session/token.
*/
export const getUserInfoOptions = (options: Options<GetUserInfoData>) => queryOptions<GetUserInfoResponse, GetUserInfoError, GetUserInfoResponse, ReturnType<typeof getUserInfoQueryKey>>({
export const getUserInfoOptions = (options?: Options<GetUserInfoData>) => queryOptions<GetUserInfoResponse, GetUserInfoError, GetUserInfoResponse, ReturnType<typeof getUserInfoQueryKey>>({
queryFn: async ({ queryKey, signal }) => {
const { data } = await getUserInfo({
...options,

View File

@@ -13,4 +13,4 @@ import type { ClientOptions as ClientOptions2 } from './types.gen';
*/
export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T>;
export const client = createClient(createConfig<ClientOptions2>({ baseUrl: 'http://localhost:8000/api/v1' }));
export const client = createClient(createConfig<ClientOptions2>({ baseUrl: 'http://localhost:8000/app/api/v1' }));

View File

@@ -1,4 +1,4 @@
// This file is auto-generated by @hey-api/openapi-ts
export { getAuthRedirect, getEventAttendance, getEventCheckin, getEventCheckinQuery, getEventInfo, getEventList, getUserInfo, getUserInfoByUserId, getUserList, type Options, patchUserUpdate, postAuthExchange, postAuthMagic, postAuthRefresh, postAuthToken, postEventCheckinSubmit, postEventJoin, postKycQuery, postKycSession } from './sdk.gen';
export type { ClientOptions, DataEventIndexDoc, DataUserIndexDoc, GetAuthRedirectData, GetAuthRedirectError, GetAuthRedirectErrors, GetEventAttendanceData, GetEventAttendanceError, GetEventAttendanceErrors, GetEventAttendanceResponse, GetEventAttendanceResponses, GetEventCheckinData, GetEventCheckinError, GetEventCheckinErrors, GetEventCheckinQueryData, GetEventCheckinQueryError, GetEventCheckinQueryErrors, GetEventCheckinQueryResponse, GetEventCheckinQueryResponses, GetEventCheckinResponse, GetEventCheckinResponses, GetEventInfoData, GetEventInfoError, GetEventInfoErrors, GetEventInfoResponse, GetEventInfoResponses, GetEventListData, GetEventListError, GetEventListErrors, GetEventListResponse, GetEventListResponses, GetUserInfoByUserIdData, GetUserInfoByUserIdError, GetUserInfoByUserIdErrors, GetUserInfoByUserIdResponse, GetUserInfoByUserIdResponses, GetUserInfoData, GetUserInfoError, GetUserInfoErrors, GetUserInfoResponse, GetUserInfoResponses, GetUserListData, GetUserListError, GetUserListErrors, GetUserListResponse, GetUserListResponses, PatchUserUpdateData, PatchUserUpdateError, PatchUserUpdateErrors, PatchUserUpdateResponse, PatchUserUpdateResponses, PostAuthExchangeData, PostAuthExchangeError, PostAuthExchangeErrors, PostAuthExchangeResponse, PostAuthExchangeResponses, PostAuthMagicData, PostAuthMagicError, PostAuthMagicErrors, PostAuthMagicResponse, PostAuthMagicResponses, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshErrors, PostAuthRefreshResponse, PostAuthRefreshResponses, PostAuthTokenData, PostAuthTokenError, PostAuthTokenErrors, PostAuthTokenResponse, PostAuthTokenResponses, PostEventCheckinSubmitData, PostEventCheckinSubmitError, PostEventCheckinSubmitErrors, PostEventCheckinSubmitResponse, PostEventCheckinSubmitResponses, PostEventJoinData, PostEventJoinError, PostEventJoinErrors, PostEventJoinResponse, PostEventJoinResponses, PostKycQueryData, PostKycQueryError, PostKycQueryErrors, PostKycQueryResponse, PostKycQueryResponses, PostKycSessionData, PostKycSessionError, PostKycSessionErrors, PostKycSessionResponse, PostKycSessionResponses, ServiceAuthExchangeData, ServiceAuthExchangeResponse, ServiceAuthMagicData, ServiceAuthMagicResponse, ServiceAuthRefreshData, ServiceAuthTokenData, ServiceAuthTokenResponse, ServiceEventAttendanceListResponse, ServiceEventCheckinQueryResponse, ServiceEventCheckinResponse, ServiceEventCheckinSubmitData, ServiceEventEventJoinData, ServiceKycKycQueryData, ServiceKycKycQueryResponse, ServiceKycKycSessionData, ServiceKycKycSessionResponse, ServiceUserUserInfoData, UtilsRespStatus } from './types.gen';
export { getAuthRedirect, getEventAttendance, getEventCheckin, getEventCheckinQuery, getEventInfo, getEventJoined, getEventList, getUserInfo, getUserInfoByUserId, getUserList, type Options, patchUserUpdate, postAgendaSubmit, postAuthExchange, postAuthMagic, postAuthRefresh, postAuthToken, postEventCheckinSubmit, postEventJoin, postKycQuery, postKycSession } from './sdk.gen';
export type { ClientOptions, DataEventIndexDoc, DataUserIndexDoc, GetAuthRedirectData, GetAuthRedirectError, GetAuthRedirectErrors, GetEventAttendanceData, GetEventAttendanceError, GetEventAttendanceErrors, GetEventAttendanceResponse, GetEventAttendanceResponses, GetEventCheckinData, GetEventCheckinError, GetEventCheckinErrors, GetEventCheckinQueryData, GetEventCheckinQueryError, GetEventCheckinQueryErrors, GetEventCheckinQueryResponse, GetEventCheckinQueryResponses, GetEventCheckinResponse, GetEventCheckinResponses, GetEventInfoData, GetEventInfoError, GetEventInfoErrors, GetEventInfoResponse, GetEventInfoResponses, GetEventJoinedData, GetEventJoinedError, GetEventJoinedErrors, GetEventJoinedResponse, GetEventJoinedResponses, GetEventListData, GetEventListError, GetEventListErrors, GetEventListResponse, GetEventListResponses, GetUserInfoByUserIdData, GetUserInfoByUserIdError, GetUserInfoByUserIdErrors, GetUserInfoByUserIdResponse, GetUserInfoByUserIdResponses, GetUserInfoData, GetUserInfoError, GetUserInfoErrors, GetUserInfoResponse, GetUserInfoResponses, GetUserListData, GetUserListError, GetUserListErrors, GetUserListResponse, GetUserListResponses, PatchUserUpdateData, PatchUserUpdateError, PatchUserUpdateErrors, PatchUserUpdateResponse, PatchUserUpdateResponses, PostAgendaSubmitData, PostAgendaSubmitError, PostAgendaSubmitErrors, PostAgendaSubmitResponse, PostAgendaSubmitResponses, PostAuthExchangeData, PostAuthExchangeError, PostAuthExchangeErrors, PostAuthExchangeResponse, PostAuthExchangeResponses, PostAuthMagicData, PostAuthMagicError, PostAuthMagicErrors, PostAuthMagicResponse, PostAuthMagicResponses, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshErrors, PostAuthRefreshResponse, PostAuthRefreshResponses, PostAuthTokenData, PostAuthTokenError, PostAuthTokenErrors, PostAuthTokenResponse, PostAuthTokenResponses, PostEventCheckinSubmitData, PostEventCheckinSubmitError, PostEventCheckinSubmitErrors, PostEventCheckinSubmitResponse, PostEventCheckinSubmitResponses, PostEventJoinData, PostEventJoinError, PostEventJoinErrors, PostEventJoinResponse, PostEventJoinResponses, PostKycQueryData, PostKycQueryError, PostKycQueryErrors, PostKycQueryResponse, PostKycQueryResponses, PostKycSessionData, PostKycSessionError, PostKycSessionErrors, PostKycSessionResponse, PostKycSessionResponses, ServiceAgendaSubmitData, ServiceAgendaSubmitResponse, ServiceAuthExchangeData, ServiceAuthExchangeResponse, ServiceAuthMagicData, ServiceAuthMagicResponse, ServiceAuthRefreshData, ServiceAuthTokenData, ServiceAuthTokenResponse, ServiceEventAttendanceListResponse, ServiceEventCheckinQueryResponse, ServiceEventCheckinResponse, ServiceEventCheckinSubmitData, ServiceEventEventJoinData, ServiceEventEventJoinResponse, ServiceKycKycQueryData, ServiceKycKycQueryResponse, ServiceKycKycSessionData, ServiceKycKycSessionResponse, ServiceUserUserInfoData, UtilsRespStatus } from './types.gen';

View File

@@ -2,7 +2,7 @@
import type { Client, Options as Options2, TDataShape } from './client';
import { client } from './client.gen';
import type { GetAuthRedirectData, GetAuthRedirectErrors, GetEventAttendanceData, GetEventAttendanceErrors, GetEventAttendanceResponses, GetEventCheckinData, GetEventCheckinErrors, GetEventCheckinQueryData, GetEventCheckinQueryErrors, GetEventCheckinQueryResponses, GetEventCheckinResponses, GetEventInfoData, GetEventInfoErrors, GetEventInfoResponses, GetEventListData, GetEventListErrors, GetEventListResponses, GetUserInfoByUserIdData, GetUserInfoByUserIdErrors, GetUserInfoByUserIdResponses, GetUserInfoData, GetUserInfoErrors, GetUserInfoResponses, GetUserListData, GetUserListErrors, GetUserListResponses, PatchUserUpdateData, PatchUserUpdateErrors, PatchUserUpdateResponses, PostAuthExchangeData, PostAuthExchangeErrors, PostAuthExchangeResponses, PostAuthMagicData, PostAuthMagicErrors, PostAuthMagicResponses, PostAuthRefreshData, PostAuthRefreshErrors, PostAuthRefreshResponses, PostAuthTokenData, PostAuthTokenErrors, PostAuthTokenResponses, PostEventCheckinSubmitData, PostEventCheckinSubmitErrors, PostEventCheckinSubmitResponses, PostEventJoinData, PostEventJoinErrors, PostEventJoinResponses, PostKycQueryData, PostKycQueryErrors, PostKycQueryResponses, PostKycSessionData, PostKycSessionErrors, PostKycSessionResponses } from './types.gen';
import type { GetAuthRedirectData, GetAuthRedirectErrors, GetEventAttendanceData, GetEventAttendanceErrors, GetEventAttendanceResponses, GetEventCheckinData, GetEventCheckinErrors, GetEventCheckinQueryData, GetEventCheckinQueryErrors, GetEventCheckinQueryResponses, GetEventCheckinResponses, GetEventInfoData, GetEventInfoErrors, GetEventInfoResponses, GetEventJoinedData, GetEventJoinedErrors, GetEventJoinedResponses, GetEventListData, GetEventListErrors, GetEventListResponses, GetUserInfoByUserIdData, GetUserInfoByUserIdErrors, GetUserInfoByUserIdResponses, GetUserInfoData, GetUserInfoErrors, GetUserInfoResponses, GetUserListData, GetUserListErrors, GetUserListResponses, PatchUserUpdateData, PatchUserUpdateErrors, PatchUserUpdateResponses, PostAgendaSubmitData, PostAgendaSubmitErrors, PostAgendaSubmitResponses, PostAuthExchangeData, PostAuthExchangeErrors, PostAuthExchangeResponses, PostAuthMagicData, PostAuthMagicErrors, PostAuthMagicResponses, PostAuthRefreshData, PostAuthRefreshErrors, PostAuthRefreshResponses, PostAuthTokenData, PostAuthTokenErrors, PostAuthTokenResponses, PostEventCheckinSubmitData, PostEventCheckinSubmitErrors, PostEventCheckinSubmitResponses, PostEventJoinData, PostEventJoinErrors, PostEventJoinResponses, PostKycQueryData, PostKycQueryErrors, PostKycQueryResponses, PostKycSessionData, PostKycSessionErrors, PostKycSessionResponses } from './types.gen';
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<TData, ThrowOnError> & {
/**
@@ -18,6 +18,20 @@ export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends
meta?: Record<string, unknown>;
};
/**
* Submit Agenda
*
* Creates a new agenda item for a specific attendance record.
*/
export const postAgendaSubmit = <ThrowOnError extends boolean = false>(options: Options<PostAgendaSubmitData, ThrowOnError>) => (options.client ?? client).post<PostAgendaSubmitResponses, PostAgendaSubmitErrors, ThrowOnError>({
url: '/agenda/submit',
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
});
/**
* Exchange Auth Code
*
@@ -137,12 +151,19 @@ export const postEventJoin = <ThrowOnError extends boolean = false>(options: Opt
}
});
/**
* Get Joined Events
*
* Fetches a list of events where the authenticated user is a participant. Supports pagination.
*/
export const getEventJoined = <ThrowOnError extends boolean = false>(options?: Options<GetEventJoinedData, ThrowOnError>) => (options?.client ?? client).get<GetEventJoinedResponses, GetEventJoinedErrors, ThrowOnError>({ url: '/event/joined', ...options });
/**
* List Events
*
* Fetches a list of events with support for pagination via limit and offset. Data is retrieved directly from the database for consistency.
*/
export const getEventList = <ThrowOnError extends boolean = false>(options: Options<GetEventListData, ThrowOnError>) => (options.client ?? client).get<GetEventListResponses, GetEventListErrors, ThrowOnError>({ url: '/event/list', ...options });
export const getEventList = <ThrowOnError extends boolean = false>(options?: Options<GetEventListData, ThrowOnError>) => (options?.client ?? client).get<GetEventListResponses, GetEventListErrors, ThrowOnError>({ url: '/event/list', ...options });
/**
* Query KYC Status
@@ -177,7 +198,7 @@ export const postKycSession = <ThrowOnError extends boolean = false>(options: Op
*
* Fetches the complete profile data for the user associated with the provided session/token.
*/
export const getUserInfo = <ThrowOnError extends boolean = false>(options: Options<GetUserInfoData, ThrowOnError>) => (options.client ?? client).get<GetUserInfoResponses, GetUserInfoErrors, ThrowOnError>({ url: '/user/info', ...options });
export const getUserInfo = <ThrowOnError extends boolean = false>(options?: Options<GetUserInfoData, ThrowOnError>) => (options?.client ?? client).get<GetUserInfoResponses, GetUserInfoErrors, ThrowOnError>({ url: '/user/info', ...options });
/**
* Get Other User Information

View File

@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
export type ClientOptions = {
baseUrl: 'http://localhost:8000/api/v1' | 'https://localhost:8000/api/v1' | (string & {});
baseUrl: 'http://localhost:8000/app/api/v1' | 'https://localhost:8000/app/api/v1' | (string & {});
};
export type DataEventIndexDoc = {
@@ -28,6 +28,16 @@ export type DataUserIndexDoc = {
username?: string;
};
export type ServiceAgendaSubmitData = {
description?: string;
event_id?: string;
name?: string;
};
export type ServiceAgendaSubmitResponse = {
agenda_id?: string;
};
export type ServiceAuthExchangeData = {
client_id?: string;
redirect_uri?: string;
@@ -88,6 +98,10 @@ export type ServiceEventEventJoinData = {
kyc_id?: string;
};
export type ServiceEventEventJoinResponse = {
attendance_id?: string;
};
export type ServiceKycKycQueryData = {
kyc_id?: string;
};
@@ -138,17 +152,53 @@ export type UtilsRespStatus = {
status?: string;
};
export type PostAgendaSubmitData = {
/**
* Agenda Submission Data
*/
body: ServiceAgendaSubmitData;
path?: never;
query?: never;
url: '/agenda/submit';
};
export type PostAgendaSubmitErrors = {
/**
* Invalid Input
*/
400: UtilsRespStatus & {
data?: {
[key: string]: unknown;
};
};
/**
* Internal Server Error
*/
500: UtilsRespStatus & {
data?: {
[key: string]: unknown;
};
};
};
export type PostAgendaSubmitError = PostAgendaSubmitErrors[keyof PostAgendaSubmitErrors];
export type PostAgendaSubmitResponses = {
/**
* OK
*/
200: UtilsRespStatus & {
data?: ServiceAgendaSubmitResponse;
};
};
export type PostAgendaSubmitResponse = PostAgendaSubmitResponses[keyof PostAgendaSubmitResponses];
export type PostAuthExchangeData = {
/**
* Exchange Request Credentials
*/
body: ServiceAuthExchangeData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: never;
url: '/auth/exchange';
@@ -199,12 +249,6 @@ export type PostAuthMagicData = {
* Magic Link Request Data
*/
body: ServiceAuthMagicData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: never;
url: '/auth/magic';
@@ -308,12 +352,6 @@ export type PostAuthRefreshData = {
* Refresh Token Body
*/
body: ServiceAuthRefreshData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: never;
url: '/auth/refresh';
@@ -364,12 +402,6 @@ export type PostAuthTokenData = {
* Token Request Body
*/
body: ServiceAuthTokenData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: never;
url: '/auth/token';
@@ -417,12 +449,6 @@ export type PostAuthTokenResponse = PostAuthTokenResponses[keyof PostAuthTokenRe
export type GetEventAttendanceData = {
body?: never;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query: {
/**
@@ -480,6 +506,10 @@ export type GetEventCheckinData = {
* latest
*/
'X-Api-Version': string;
/**
* Bearer token
*/
Authorization: string;
};
path?: never;
query: {
@@ -580,6 +610,16 @@ export type PostEventCheckinSubmitData = {
* Checkin Code Data
*/
body: ServiceEventCheckinSubmitData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
/**
* Bearer token
*/
Authorization: string;
};
path?: never;
query?: never;
url: '/event/checkin/submit';
@@ -613,12 +653,6 @@ export type PostEventCheckinSubmitResponse = PostEventCheckinSubmitResponses[key
export type GetEventInfoData = {
body?: never;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query: {
/**
@@ -682,12 +716,6 @@ export type PostEventJoinData = {
* Event Join Details (UserId and EventId are required)
*/
body: ServiceEventEventJoinData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: never;
url: '/event/join';
@@ -735,22 +763,70 @@ export type PostEventJoinResponses = {
* Successfully joined the event
*/
200: UtilsRespStatus & {
data?: ServiceEventEventJoinResponse;
};
};
export type PostEventJoinResponse = PostEventJoinResponses[keyof PostEventJoinResponses];
export type GetEventJoinedData = {
body?: never;
path?: never;
query?: {
/**
* Maximum number of events to return (default 20)
*/
limit?: number;
/**
* Number of events to skip
*/
offset?: number;
};
url: '/event/joined';
};
export type GetEventJoinedErrors = {
/**
* Invalid Input
*/
400: UtilsRespStatus & {
data?: {
[key: string]: unknown;
};
};
/**
* Unauthorized
*/
401: UtilsRespStatus & {
data?: {
[key: string]: unknown;
};
};
/**
* Internal Server Error
*/
500: UtilsRespStatus & {
data?: {
[key: string]: unknown;
};
};
};
export type PostEventJoinResponse = PostEventJoinResponses[keyof PostEventJoinResponses];
export type GetEventJoinedError = GetEventJoinedErrors[keyof GetEventJoinedErrors];
export type GetEventJoinedResponses = {
/**
* Successful retrieval of joined events
*/
200: UtilsRespStatus & {
data?: Array<DataEventIndexDoc>;
};
};
export type GetEventJoinedResponse = GetEventJoinedResponses[keyof GetEventJoinedResponses];
export type GetEventListData = {
body?: never;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: {
/**
@@ -810,12 +886,6 @@ export type PostKycQueryData = {
* KYC query data (KycId)
*/
body: ServiceKycKycQueryData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: never;
url: '/kyc/query';
@@ -866,12 +936,6 @@ export type PostKycSessionData = {
* KYC session data (Type and Base64 Identity)
*/
body: ServiceKycKycSessionData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: never;
url: '/kyc/session';
@@ -919,12 +983,6 @@ export type PostKycSessionResponse = PostKycSessionResponses[keyof PostKycSessio
export type GetUserInfoData = {
body?: never;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: never;
url: '/user/info';
@@ -972,12 +1030,6 @@ export type GetUserInfoResponse = GetUserInfoResponses[keyof GetUserInfoResponse
export type GetUserInfoByUserIdData = {
body?: never;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path: {
/**
* Other user id
@@ -1038,12 +1090,6 @@ export type GetUserInfoByUserIdResponse = GetUserInfoByUserIdResponses[keyof Get
export type GetUserListData = {
body?: never;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query: {
/**
@@ -1103,12 +1149,6 @@ export type PatchUserUpdateData = {
* Updated User Profile Data
*/
body: ServiceUserUserInfoData;
headers: {
/**
* latest
*/
'X-Api-Version': string;
};
path?: never;
query?: never;
url: '/user/update';

View File

@@ -26,6 +26,16 @@ export const zDataUserIndexDoc = z.object({
username: z.string().optional()
});
export const zServiceAgendaSubmitData = z.object({
description: z.string().optional(),
event_id: z.string().optional(),
name: z.string().optional()
});
export const zServiceAgendaSubmitResponse = z.object({
agenda_id: z.string().optional()
});
export const zServiceAuthExchangeData = z.object({
client_id: z.string().optional(),
redirect_uri: z.string().optional(),
@@ -79,6 +89,10 @@ export const zServiceEventEventJoinData = z.object({
kyc_id: z.string().optional()
});
export const zServiceEventEventJoinResponse = z.object({
attendance_id: z.string().optional()
});
export const zServiceKycKycQueryData = z.object({
kyc_id: z.string().optional()
});
@@ -124,13 +138,23 @@ export const zUtilsRespStatus = z.object({
status: z.string().optional()
});
export const zPostAgendaSubmitData = z.object({
body: zServiceAgendaSubmitData,
path: z.never().optional(),
query: z.never().optional()
});
/**
* OK
*/
export const zPostAgendaSubmitResponse = zUtilsRespStatus.and(z.object({
data: zServiceAgendaSubmitResponse.optional()
}));
export const zPostAuthExchangeData = z.object({
body: zServiceAuthExchangeData,
path: z.never().optional(),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**
@@ -143,10 +167,7 @@ export const zPostAuthExchangeResponse = zUtilsRespStatus.and(z.object({
export const zPostAuthMagicData = z.object({
body: zServiceAuthMagicData,
path: z.never().optional(),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**
@@ -170,10 +191,7 @@ export const zGetAuthRedirectData = z.object({
export const zPostAuthRefreshData = z.object({
body: zServiceAuthRefreshData,
path: z.never().optional(),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**
@@ -186,10 +204,7 @@ export const zPostAuthRefreshResponse = zUtilsRespStatus.and(z.object({
export const zPostAuthTokenData = z.object({
body: zServiceAuthTokenData,
path: z.never().optional(),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**
@@ -204,9 +219,6 @@ export const zGetEventAttendanceData = z.object({
path: z.never().optional(),
query: z.object({
event_id: z.string()
}),
headers: z.object({
'X-Api-Version': z.string()
})
});
@@ -224,7 +236,8 @@ export const zGetEventCheckinData = z.object({
event_id: z.string()
}),
headers: z.object({
'X-Api-Version': z.string()
'X-Api-Version': z.string(),
Authorization: z.string()
})
});
@@ -253,7 +266,11 @@ export const zGetEventCheckinQueryResponse = zUtilsRespStatus.and(z.object({
export const zPostEventCheckinSubmitData = z.object({
body: zServiceEventCheckinSubmitData,
path: z.never().optional(),
query: z.never().optional()
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string(),
Authorization: z.string()
})
});
/**
@@ -268,9 +285,6 @@ export const zGetEventInfoData = z.object({
path: z.never().optional(),
query: z.object({
event_id: z.string()
}),
headers: z.object({
'X-Api-Version': z.string()
})
});
@@ -284,17 +298,30 @@ export const zGetEventInfoResponse = zUtilsRespStatus.and(z.object({
export const zPostEventJoinData = z.object({
body: zServiceEventEventJoinData,
path: z.never().optional(),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**
* Successfully joined the event
*/
export const zPostEventJoinResponse = zUtilsRespStatus.and(z.object({
data: z.record(z.unknown()).optional()
data: zServiceEventEventJoinResponse.optional()
}));
export const zGetEventJoinedData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.object({
limit: z.number().int().optional(),
offset: z.number().int().optional()
}).optional()
});
/**
* Successful retrieval of joined events
*/
export const zGetEventJoinedResponse = zUtilsRespStatus.and(z.object({
data: z.array(zDataEventIndexDoc).optional()
}));
export const zGetEventListData = z.object({
@@ -303,10 +330,7 @@ export const zGetEventListData = z.object({
query: z.object({
limit: z.number().int().optional(),
offset: z.number().int().optional()
}).optional(),
headers: z.object({
'X-Api-Version': z.string()
})
}).optional()
});
/**
@@ -319,10 +343,7 @@ export const zGetEventListResponse = zUtilsRespStatus.and(z.object({
export const zPostKycQueryData = z.object({
body: zServiceKycKycQueryData,
path: z.never().optional(),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**
@@ -335,10 +356,7 @@ export const zPostKycQueryResponse = zUtilsRespStatus.and(z.object({
export const zPostKycSessionData = z.object({
body: zServiceKycKycSessionData,
path: z.never().optional(),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**
@@ -351,10 +369,7 @@ export const zPostKycSessionResponse = zUtilsRespStatus.and(z.object({
export const zGetUserInfoData = z.object({
body: z.never().optional(),
path: z.never().optional(),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**
@@ -369,10 +384,7 @@ export const zGetUserInfoByUserIdData = z.object({
path: z.object({
user_id: z.string()
}),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**
@@ -388,9 +400,6 @@ export const zGetUserListData = z.object({
query: z.object({
limit: z.string().optional(),
offset: z.string()
}),
headers: z.object({
'X-Api-Version': z.string()
})
});
@@ -404,10 +413,7 @@ export const zGetUserListResponse = zUtilsRespStatus.and(z.object({
export const zPatchUserUpdateData = z.object({
body: zServiceUserUserInfoData,
path: z.never().optional(),
query: z.never().optional(),
headers: z.object({
'X-Api-Version': z.string()
})
query: z.never().optional()
});
/**

View File

@@ -3,14 +3,19 @@ import PlaceholderImage from '@/assets/event-placeholder.png';
import { useGetEvents } from '@/hooks/data/useGetEvents';
import { Button } from '../ui/button';
import { DialogTrigger } from '../ui/dialog';
import { EventGridSkeleton } from './event-grid.skeleton';
import { EventGridView } from './event-grid.view';
import { KycDialogContainer } from './kyc/kyc.dialog.container';
export function EventGridContainer() {
const { data, isLoading } = useGetEvents();
const allEvents: EventInfo[] = isLoading
? []
: data.pages.flatMap(page => page.data ?? []).map(it => ({
return (
isLoading
? <EventGridSkeleton />
: (
<EventGridView
events={data.pages.flatMap(page => page.data ?? []).map(it => ({
type: it.type! as EventInfo['type'],
eventId: it.event_id!,
isJoined: it.is_joined!,
@@ -20,11 +25,7 @@ export function EventGridContainer() {
description: it.description!,
startTime: new Date(it.start_time!),
endTime: new Date(it.end_time!),
} satisfies EventInfo));
return (
<EventGridView
events={allEvents}
} satisfies EventInfo))}
footer={eventInfo => (eventInfo.isJoined
? <Button className="w-full" disabled></Button>
: (
@@ -36,5 +37,6 @@ export function EventGridContainer() {
)
)}
/>
)
);
}

View File

@@ -0,0 +1,16 @@
import { FileExclamationPoint } from 'lucide-react';
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '../ui/empty';
export function EventGridError() {
return (
<Empty className="h-full">
<EmptyHeader>
<EmptyMedia variant="icon">
<FileExclamationPoint />
</EmptyMedia>
<EmptyTitle></EmptyTitle>
<EmptyDescription> </EmptyDescription>
</EmptyHeader>
</Empty>
);
}

View File

@@ -3,7 +3,7 @@ import { EventCardSkeleton } from './event-card.skeleton';
export function EventGridSkeleton() {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{Array.from({ length: 8 }).map((_, i) => (
{Array.from({ length: 4 }).map((_, i) => (
// eslint-disable-next-line react/no-array-index-key
<EventCardSkeleton key={i} />
))}

View File

@@ -6,7 +6,7 @@ export function EventGridView({ events, footer }: { events: EventInfo[]; footer:
return (
<>
{events.length > 0 && (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4 h-full">
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
{events.map(event => (
<EventCardView key={event.eventId} eventInfo={event} actionFooter={footer(event)} />
))}

View File

@@ -6,7 +6,6 @@ import { useStore } from 'zustand';
import { postEventJoin, postKycQuery } from '@/client';
import { getEventListInfiniteQueryKey } from '@/client/@tanstack/react-query.gen';
import { useCreateKycSession } from '@/hooks/data/useCreateKycSession';
import { ver } from '@/lib/apiVersion';
import { KycFailedDialogView } from './kyc-failed.dialog.view';
import { KycMethodSelectionDialogView } from './kyc-method-selection.dialog.view';
import { KycPendingDialogView } from './kyc-pending.dialog.view';
@@ -30,7 +29,6 @@ export function KycDialogContainer({ eventIdToJoin, children }: { eventIdToJoin:
await postEventJoin({
signal: abortSignal,
body: { event_id: eventId, kyc_id: kycId },
headers: ver('20260205'),
});
setStage('success');
}
@@ -71,7 +69,6 @@ export function KycDialogContainer({ eventIdToJoin, children }: { eventIdToJoin:
const { data } = await postKycQuery({
signal: controller.signal,
body: { kyc_id: store.getState().kycId! },
headers: ver('20260205'),
});
const status = data?.data?.status;
@@ -112,7 +109,7 @@ export function KycDialogContainer({ eventIdToJoin, children }: { eventIdToJoin:
onOpenChange={(open) => {
if (!open) {
void queryClient.invalidateQueries({
queryKey: getEventListInfiniteQueryKey({ query: {}, headers: ver('20260205') }),
queryKey: getEventListInfiniteQueryKey({ query: {} }),
});
}
setIsDialogOpen(open);

View File

@@ -0,0 +1,18 @@
import type { RawError } from '@/lib/types';
import { TriangleAlert } from 'lucide-react';
import { isRawError } from '@/lib/types';
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from './ui/empty';
export function GlobalError({ error }: { error: Error | RawError }) {
return (
<Empty className="h-screen w-full">
<EmptyHeader>
<EmptyMedia variant="icon">
<TriangleAlert />
</EmptyMedia>
<EmptyTitle></EmptyTitle>
<EmptyDescription>{isRawError(error) ? error.error_id : error.message}</EmptyDescription>
</EmptyHeader>
</Empty>
);
}

View File

@@ -13,7 +13,6 @@ import {
} from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import { useGetMagicLink } from '@/hooks/data/useGetMagicLink';
import { ver } from '@/lib/apiVersion';
import { cn } from '@/lib/utils';
export function LoginForm({
@@ -33,7 +32,7 @@ export function LoginForm({
event.preventDefault();
const formData = new FormData(formRef.current!);
const email = formData.get('email')! as string;
mutateAsync({ body: { email, turnstile_token: token!, ...oauthParams }, headers: ver('20260205') }).then(() => {
mutateAsync({ body: { email, turnstile_token: token!, ...oauthParams } }).then(() => {
void navigate({ to: '/magicLinkSent', search: { email } });
}).catch((error) => {
console.error(error);

View File

@@ -1,6 +1,5 @@
import type { ServiceUserUserInfoData } from '@/client';
import { useUpdateUser } from '@/hooks/data/useUpdateUser';
import { ver } from '@/lib/apiVersion';
import { EditProfileDialogView } from './edit-profile.dialog.view';
export function EditProfileDialogContainer({ data }: { data: ServiceUserUserInfoData }) {
@@ -9,7 +8,7 @@ export function EditProfileDialogContainer({ data }: { data: ServiceUserUserInfo
<EditProfileDialogView
user={data}
updateProfile={async (data) => {
await mutateAsync({ body: data, headers: ver('20260205') });
await mutateAsync({ body: data });
}}
/>
);

View File

@@ -1,6 +1,5 @@
import { useUpdateUser } from '@/hooks/data/useUpdateUser';
import { useOtherUserInfo } from '@/hooks/data/useUserInfo';
import { ver } from '@/lib/apiVersion';
import { utf8ToBase64 } from '@/lib/utils';
import { ProfileView } from './profile.view';
@@ -11,7 +10,7 @@ export function ProfileContainer({ userId }: { userId: string }) {
<ProfileView
user={data.data!}
onSaveBio={async (bio) => {
await mutateAsync({ body: { bio: utf8ToBase64(bio) }, headers: ver('20260205') });
await mutateAsync({ body: { bio: utf8ToBase64(bio) } });
}}
/>
);

View File

@@ -1,30 +1,16 @@
import { Mail } from 'lucide-react';
import { Skeleton } from '../ui/skeleton';
import { UserLock } from 'lucide-react';
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '../ui/empty';
export function ProfileError({ reason }: { reason: string }) {
return (
<div className="flex flex-col justify-center w-full lg:w-auto h-full lg:h-auto lg:flex-row lg:gap-8">
<div className="flex w-full flex-row mt-2 lg:mt-0 lg:flex-col lg:w-max">
<div className="flex flex-col w-full gap-3">
<div className="flex flex-col w-full gap-3">
<div className="flex flex-row gap-3 w-full lg:flex-col">
<Skeleton className="size-16 rounded-full border-2 border-muted lg:size-64" />
<div className="flex flex-1 flex-col justify-center lg:mt-3 gap-2">
<Skeleton className="w-32 h-8" />
<Skeleton className="w-20 h-6" />
</div>
</div>
<div className="flex flex-row gap-2 items-center text-sm px-1 lg:px-0">
<Mail className="h-4 w-4 stroke-muted-foreground" />
<Skeleton className="w-32 h-4" />
</div>
</div>
<Skeleton className="w-64 h-[40px]" />
</div>
</div>
<Skeleton className="relative rounded-md border border-muted w-full flex-1 lg:flex-auto min-h-72 lg:h-full mt-4 lg:mt-0 prose dark:prose-invert max-w-[1012px] self-center flex items-center justify-center">
{reason}
</Skeleton>
</div>
<Empty className="h-full">
<EmptyHeader>
<EmptyMedia variant="icon">
<UserLock />
</EmptyMedia>
<EmptyTitle></EmptyTitle>
<EmptyDescription>{reason}</EmptyDescription>
</EmptyHeader>
</Empty>
);
}

View File

@@ -1,7 +1,6 @@
import type { KycSubmission } from '@/components/events/kyc/kyc.types';
import { useMutation } from '@tanstack/react-query';
import { postKycSessionMutation } from '@/client/@tanstack/react-query.gen';
import { ver } from '@/lib/apiVersion';
import { utf8ToBase64 } from '@/lib/utils';
type CreateKycSessionBase64Payload = {
@@ -37,7 +36,6 @@ export function useCreateKycSession() {
identity: payload,
type: data.method,
},
headers: ver('20260205'),
});
return response;
},

View File

@@ -1,13 +1,11 @@
import { useInfiniteQuery } from '@tanstack/react-query';
import { isNil } from 'lodash-es';
import { getEventListInfiniteOptions } from '@/client/@tanstack/react-query.gen';
import { ver } from '@/lib/apiVersion';
export function useGetEvents() {
return useInfiniteQuery({
...getEventListInfiniteOptions({
query: {},
headers: ver('20260205'),
}),
initialPageParam: 0,
getNextPageParam: (lastPage, allPages) => {

View File

@@ -1,17 +1,16 @@
import type { ServiceUserUserInfoData } from '@/client';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { getUserInfoByUserIdQueryKey, getUserInfoQueryKey, patchUserUpdateMutation } from '@/client/@tanstack/react-query.gen';
import { ver } from '@/lib/apiVersion';
export function useUpdateUser() {
const queryClient = useQueryClient();
const data: { data: ServiceUserUserInfoData | undefined } | undefined = queryClient.getQueryData(getUserInfoQueryKey({ headers: ver('20260205') }));
const data: { data: ServiceUserUserInfoData | undefined } | undefined = queryClient.getQueryData(getUserInfoQueryKey());
return useMutation({
...patchUserUpdateMutation(),
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: getUserInfoQueryKey({ headers: ver('20260205') }) });
await queryClient.invalidateQueries({ queryKey: getUserInfoQueryKey() });
if ((data?.data?.user_id) != null) {
await queryClient.invalidateQueries({ queryKey: getUserInfoByUserIdQueryKey({ path: { user_id: data.data.user_id }, headers: ver('20260205') }) });
await queryClient.invalidateQueries({ queryKey: getUserInfoByUserIdQueryKey({ path: { user_id: data.data.user_id } }) });
}
},
});

View File

@@ -3,18 +3,17 @@ import {
getUserInfoByUserIdOptions,
getUserInfoOptions,
} from '@/client/@tanstack/react-query.gen';
import { ver } from '@/lib/apiVersion';
export function useUserInfo() {
return useSuspenseQuery({
...getUserInfoOptions({ headers: ver('20260205') }),
...getUserInfoOptions(),
staleTime: 10 * 60 * 1000,
});
}
export function useOtherUserInfo(userId: string) {
return useSuspenseQuery({
...getUserInfoByUserIdOptions({ path: { user_id: userId }, headers: ver('20260205') }),
...getUserInfoByUserIdOptions({ path: { user_id: userId } }),
staleTime: 10 * 60 * 1000,
retry: (_failureCount, error) => error.code !== 403,
});

View File

@@ -1,7 +1,6 @@
import type { ServiceAuthTokenResponse } from '@/client';
import { toast } from 'sonner';
import { postAuthRefresh } from '@/client';
import { ver } from './apiVersion';
import { router } from './router';
const ACCESS_TOKEN_LOCALSTORAGE_KEY = 'token';
@@ -41,7 +40,6 @@ export async function doRefreshToken(refreshToken: string): Promise<ServiceAuthT
body: {
refresh_token: refreshToken,
},
headers: ver('20260205'),
});
return data?.data;
}

View File

@@ -0,0 +1,16 @@
export interface RawError {
code: number;
status: string;
error_id: string;
data: null;
}
export function isRawError(obj: any): obj is RawError {
return (
typeof obj === 'object'
&& obj !== null
&& 'code' in obj
&& 'status' in obj
&& 'error_id' in obj
);
}

View File

@@ -1,5 +1,6 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createRootRoute, Outlet } from '@tanstack/react-router';
import { GlobalError } from '@/components/global.error';
import { ThemeProvider } from '@/components/theme-provider';
import { Toaster } from '@/components/ui/sonner';
import '@/index.css';
@@ -34,4 +35,4 @@ function RootLayout() {
);
}
export const Route = createRootRoute({ component: RootLayout });
export const Route = createRootRoute({ component: RootLayout, errorComponent: ({ error }) => <GlobalError error={error} /> });

View File

@@ -1,28 +1,26 @@
import type { RawError } from '@/lib/types';
import { createFileRoute } from '@tanstack/react-router';
import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { ProfileContainer } from '@/components/profile/profile.container';
import { ProfileError } from '@/components/profile/profile.error';
import { ProfileSkeleton } from '@/components/profile/profile.skeleton';
export const Route = createFileRoute('/_workbenchLayout/profile/$userId')({
component: RouteComponent,
errorComponent: ({ error }) => {
if ((error as unknown as RawError).code === 403)
return <ProfileError reason="用户个人资料未公开" />;
else return <ProfileError reason="获取用户个人资料失败" />;
},
});
function RouteComponent() {
const { userId } = Route.useParams();
return (
<div className="flex h-full flex-col gap-6 px-4 py-6">
<ErrorBoundary fallbackRender={(error) => {
if ((error.error as { code: number }).code === 403)
return <ProfileError reason="用户个人资料未公开" />;
else return <ProfileError reason="获取用户个人资料失败" />;
}}
>
<Suspense fallback={<ProfileSkeleton />}>
<ProfileContainer userId={userId} />
</Suspense>
</ErrorBoundary>
</div>
);
}

View File

@@ -1,21 +1,21 @@
import { createFileRoute, Navigate } from '@tanstack/react-router';
import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { ProfileError } from '@/components/profile/profile.error';
import { ProfileSkeleton } from '@/components/profile/profile.skeleton';
import { useUserInfo } from '@/hooks/data/useUserInfo';
export const Route = createFileRoute('/_workbenchLayout/profile/')({
component: RouteComponent,
errorComponent: () => {
return <ProfileError reason="获取用户个人资料失败" />;
},
});
function RouteComponent() {
const { data } = useUserInfo();
return (
<ErrorBoundary fallback={<ProfileError reason="获取用户个人资料失败" />}>
<Suspense fallback={<ProfileSkeleton />}>
<Navigate to="/profile/$userId" params={{ userId: data.data!.user_id! }} />
</Suspense>
</ErrorBoundary>
);
}

View File

@@ -5,7 +5,6 @@ import { useEffect } from 'react';
import z from 'zod';
import { LoginForm } from '@/components/login-form';
import { useExchangeToken } from '@/hooks/data/useExchangeToken';
import { ver } from '@/lib/apiVersion';
import { generateOAuthState } from '@/lib/random';
import { getAccessToken } from '@/lib/token';
@@ -40,7 +39,6 @@ function RouteComponent() {
redirect_uri: oauthParams.redirect_uri,
state: oauthParams.state,
},
headers: ver('20260205'),
});
}
}, [token, mutation.isIdle, mutation, oauthParams.client_id, oauthParams.redirect_uri, oauthParams.state]);

View File

@@ -6,7 +6,6 @@ import {
} from 'react';
import z from 'zod';
import { postAuthTokenMutation } from '@/client/@tanstack/react-query.gen';
import { ver } from '@/lib/apiVersion';
import { setAccessToken, setRefreshToken } from '@/lib/token';
const tokenCodeSchema = z.object({
@@ -37,7 +36,7 @@ function RouteComponent() {
useEffect(() => {
if (mutation.isIdle) {
mutation.mutate({ body: { code }, headers: ver('20260205') });
mutation.mutate({ body: { code } });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

View File

@@ -65,7 +65,7 @@ export const Primary: Story = {
};
export const Empty: Story = {
decorators: [Story => <div className="h-screen">{Story()}</div>],
decorators: [Story => <div className="h-screen"><Story /></div>],
args: {
events: [],
footer: () => <Button className="w-full"></Button>,

View File

@@ -39,10 +39,20 @@ export const Loading: Story = {
export const Error: Story = {
render: () => <ProfileError reason="用户个人资料未公开" />,
decorators: [
Story => (
<div className="h-screen">
<Story />
</div>
),
],
args: {
user: {
allow_public: false,
},
onSaveBio: async () => Promise.resolve(),
},
parameters: {
layout: 'fullscreen',
},
};