diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..25fa621 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/app/api/ApolloClientForRSC.tsx b/app/api/ApolloClientForRSC.tsx deleted file mode 100644 index 5d8d8cf..0000000 --- a/app/api/ApolloClientForRSC.tsx +++ /dev/null @@ -1,27 +0,0 @@ -// import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client"; -// import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc"; -// import { cookies } from "next/headers"; - -// import { COOKIE_NAME } from "@/utils/supabase/client"; - -// const { getClient } = registerApolloClient(() => { -// const cookieValue = cookies().get(COOKIE_NAME)?.value ?? ""; -// const headers = cookieValue -// ? { -// headers: { -// cookie: `${COOKIE_NAME}=${cookieValue}`, -// }, -// } -// : {}; -// return new ApolloClient({ -// cache: new InMemoryCache(), -// link: new HttpLink({ -// // this needs to be an absolute url, as relative urls cannot be used in SSR -// uri: VITE_JSCL_API_URL, -// // fetch, -// ...headers, -// }), -// }); -// }); - -// export const getApolloClientForRSC = getClient; diff --git a/app/api/ApolloWrapper.tsx b/app/api/ApolloWrapper.tsx index 9f97ad9..56dc7b1 100644 --- a/app/api/ApolloWrapper.tsx +++ b/app/api/ApolloWrapper.tsx @@ -10,8 +10,8 @@ import { setContext } from "@apollo/client/link/context"; import { onError } from "@apollo/client/link/error"; import { RetryLink } from "@apollo/client/link/retry"; -import { useSetTokenRef, useTokenRef } from "../utils/supabase/AuthProvider"; -import { supabaseClient } from "../utils/supabase/client"; +import { useTokenRef } from "../utils/supabase/AuthProvider"; +import { useRefreshToken } from "../utils/supabase/client"; const retryLink = new RetryLink(); @@ -62,38 +62,21 @@ const useAuthLink = () => { // Este link se encarga de manejar errores de autenticación // Si el servidor responde con un code UNAUTHENTICATED, intenta refrescar el token. const useErrorLink = () => { - const setToken = useSetTokenRef(); + const refreshToken = useRefreshToken(); return onError(({ graphQLErrors, networkError, operation, forward }) => { if (graphQLErrors) { for (const err of graphQLErrors) { - if (err.extensions.code === "UNAUTHENTICATED") { - supabaseClient.auth - .getSession() - .then(({ data, error }) => { - if (error) { - // eslint-disable-next-line no-console - console.error("Error refreshing access token", error); - - return; - } - - const newToken = data.session?.access_token; - - if (!newToken) { - // eslint-disable-next-line no-console - console.error("No access token found in session data"); - } else { - setToken(newToken); - } - + if (err.extensions.type === "UNAUTHENTICATED") { + refreshToken( + () => { forward(operation); - }) - .catch((error: unknown) => { + }, + () => { // eslint-disable-next-line no-console - console.error("Error refreshing access token", error); - }); - // } + console.error("Error refreshing access token"); + }, + ); } } } else if (networkError) { diff --git a/app/api/gql/gql.ts b/app/api/gql/gql.ts index 264fbad..eb4693d 100644 --- a/app/api/gql/gql.ts +++ b/app/api/gql/gql.ts @@ -15,6 +15,8 @@ import type { TypedDocumentNode as DocumentNode } from "@graphql-typed-document- const documents = { "query FetchExampleEvents {\n events {\n id\n description\n community {\n id\n name\n }\n tags {\n id\n name\n description\n }\n }\n}": types.FetchExampleEventsDocument, + "query myTickets($input: PaginatedInputMyTicketsSearchValues!) {\n myTickets(input: $input) {\n data {\n approvalStatus\n id\n paymentStatus\n redemptionStatus\n }\n pagination {\n currentPage\n pageSize\n totalPages\n totalRecords\n }\n }\n}": + types.MyTicketsDocument, "mutation createPurchaseOrder($input: TicketClaimInput!) {\n claimUserTicket(input: $input) {\n __typename\n ... on PurchaseOrder {\n __typename\n id\n currency {\n id\n }\n finalPrice\n paymentLink\n status\n tickets {\n id\n approvalStatus\n redemptionStatus\n paymentStatus\n }\n }\n ... on RedeemUserTicketError {\n __typename\n error\n errorMessage\n }\n }\n}": types.CreatePurchaseOrderDocument, "fragment EventTicketFragment on Ticket {\n id\n name\n description\n quantity\n isFree\n startDateTime\n status\n isUnlimited\n prices {\n id\n amount\n currency {\n currency\n id\n }\n }\n}": @@ -43,6 +45,12 @@ export function graphql(source: string): unknown; export function graphql( source: "query FetchExampleEvents {\n events {\n id\n description\n community {\n id\n name\n }\n tags {\n id\n name\n description\n }\n }\n}", ): (typeof documents)["query FetchExampleEvents {\n events {\n id\n description\n community {\n id\n name\n }\n tags {\n id\n name\n description\n }\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql( + source: "query myTickets($input: PaginatedInputMyTicketsSearchValues!) {\n myTickets(input: $input) {\n data {\n approvalStatus\n id\n paymentStatus\n redemptionStatus\n }\n pagination {\n currentPage\n pageSize\n totalPages\n totalRecords\n }\n }\n}", +): (typeof documents)["query myTickets($input: PaginatedInputMyTicketsSearchValues!) {\n myTickets(input: $input) {\n data {\n approvalStatus\n id\n paymentStatus\n redemptionStatus\n }\n pagination {\n currentPage\n pageSize\n totalPages\n totalRecords\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/app/api/gql/graphql.ts b/app/api/gql/graphql.ts index ebac85a..e9fbae5 100644 --- a/app/api/gql/graphql.ts +++ b/app/api/gql/graphql.ts @@ -375,13 +375,38 @@ export type MutationValidateWorkEmailArgs = { confirmationToken: Scalars["String"]["input"]; }; -export type MyTicketsSearchInput = { +export type MyTicketsSearchValues = { approvalStatus: InputMaybe; eventId: InputMaybe; paymentStatus: InputMaybe; redemptionStatus: InputMaybe; }; +export type PaginatedInputMyTicketsSearchValues = { + pagination: PaginationSearchInputParams; + search: InputMaybe; +}; + +/** Type used for querying the paginated leaves and it's paginated meta data */ +export type PaginatedUserTicket = { + data: Array; + pagination: Pagination; +}; + +/** Pagination meta data */ +export type Pagination = { + currentPage: Scalars["Int"]["output"]; + pageSize: Scalars["Int"]["output"]; + totalPages: Scalars["Int"]["output"]; + totalRecords: Scalars["Int"]["output"]; +}; + +export type PaginationSearchInputParams = { + /** Page number, starts at 0 */ + page: Scalars["Int"]["input"]; + pageSize: Scalars["Int"]["input"]; +}; + export type PayForPurchaseOrderInput = { currencyID: Scalars["String"]["input"]; purchaseOrderId: Scalars["String"]["input"]; @@ -449,7 +474,7 @@ export type Query = { /** Get the current user */ me: User; /** Get a list of tickets for the current user */ - myTickets: Array; + myTickets: PaginatedUserTicket; /** Get a list of salaries associated to the user */ salaries: Array; /** Search a consolidated payment logs, by date, aggregated by platform and currency_id */ @@ -504,7 +529,7 @@ export type QueryEventsArgs = { }; export type QueryMyTicketsArgs = { - input: InputMaybe; + input: PaginatedInputMyTicketsSearchValues; }; export type QuerySearchConsolidatedPaymentLogsArgs = { @@ -749,6 +774,7 @@ export type UserTicket = { id: Scalars["ID"]["output"]; paymentStatus: TicketPaymentStatus; redemptionStatus: TicketRedemptionStatus; + ticketTemplate: Ticket; }; export enum ValidPaymentMethods { @@ -830,6 +856,27 @@ export type FetchExampleEventsQuery = { }>; }; +export type MyTicketsQueryVariables = Exact<{ + input: PaginatedInputMyTicketsSearchValues; +}>; + +export type MyTicketsQuery = { + myTickets: { + data: Array<{ + approvalStatus: TicketApprovalStatus; + id: string; + paymentStatus: TicketPaymentStatus; + redemptionStatus: TicketRedemptionStatus; + }>; + pagination: { + currentPage: number; + pageSize: number; + totalPages: number; + totalRecords: number; + }; + }; +}; + export type CreatePurchaseOrderMutationVariables = Exact<{ input: TicketClaimInput; }>; @@ -1003,6 +1050,106 @@ export const FetchExampleEventsDocument = { FetchExampleEventsQuery, FetchExampleEventsQueryVariables >; +export const MyTicketsDocument = { + kind: "Document", + definitions: [ + { + kind: "OperationDefinition", + operation: "query", + name: { kind: "Name", value: "myTickets" }, + variableDefinitions: [ + { + kind: "VariableDefinition", + variable: { + kind: "Variable", + name: { kind: "Name", value: "input" }, + }, + type: { + kind: "NonNullType", + type: { + kind: "NamedType", + name: { + kind: "Name", + value: "PaginatedInputMyTicketsSearchValues", + }, + }, + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "myTickets" }, + arguments: [ + { + kind: "Argument", + name: { kind: "Name", value: "input" }, + value: { + kind: "Variable", + name: { kind: "Name", value: "input" }, + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "data" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "approvalStatus" }, + }, + { kind: "Field", name: { kind: "Name", value: "id" } }, + { + kind: "Field", + name: { kind: "Name", value: "paymentStatus" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "redemptionStatus" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "pagination" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "currentPage" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "pageSize" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "totalPages" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "totalRecords" }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode; export const CreatePurchaseOrderDocument = { kind: "Document", definitions: [ diff --git a/app/api/gql/schema.gql b/app/api/gql/schema.gql index e65d8b1..256d8d8 100644 --- a/app/api/gql/schema.gql +++ b/app/api/gql/schema.gql @@ -343,13 +343,44 @@ type Mutation { validateWorkEmail(confirmationToken: String!): WorkEmail! } -input MyTicketsSearchInput { +input MyTicketsSearchValues { approvalStatus: TicketApprovalStatus eventId: String paymentStatus: TicketPaymentStatus redemptionStatus: TicketRedemptionStatus } +input PaginatedInputMyTicketsSearchValues { + pagination: PaginationSearchInputParams! = { page: 0, pageSize: 20 } + search: MyTicketsSearchValues +} + +""" +Type used for querying the paginated leaves and it's paginated meta data +""" +type PaginatedUserTicket { + data: [UserTicket!]! + pagination: Pagination! +} + +""" +Pagination meta data +""" +type Pagination { + currentPage: Int! + pageSize: Int! + totalPages: Int! + totalRecords: Int! +} + +input PaginationSearchInputParams { + """ + Page number, starts at 0 + """ + page: Int! + pageSize: Int! +} + input PayForPurchaseOrderInput { currencyID: String! purchaseOrderId: String! @@ -452,7 +483,7 @@ type Query { """ Get a list of tickets for the current user """ - myTickets(input: MyTicketsSearchInput): [UserTicket!]! + myTickets(input: PaginatedInputMyTicketsSearchValues!): PaginatedUserTicket! """ Get a list of salaries associated to the user @@ -757,6 +788,7 @@ type UserTicket { id: ID! paymentStatus: TicketPaymentStatus! redemptionStatus: TicketRedemptionStatus! + ticketTemplate: Ticket! } enum ValidPaymentMethods { diff --git a/app/components/MyTickets/graphql/myTickets.generated.tsx b/app/components/MyTickets/graphql/myTickets.generated.tsx new file mode 100644 index 0000000..8db11bf --- /dev/null +++ b/app/components/MyTickets/graphql/myTickets.generated.tsx @@ -0,0 +1,121 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +// @ts-nocheck +/* eslint-disable */ +/* prettier-ignore */ +/* This file is automatically generated. Please do not modify it manually. */ +import * as Types from '../../../api/gql/graphql'; + +import { gql } from "graphql-tag"; +import * as Apollo from "@apollo/client"; +const defaultOptions = {} as const; +export type MyTicketsQueryVariables = Types.Exact<{ + input: Types.PaginatedInputMyTicketsSearchValues; +}>; + +export type MyTicketsQuery = { + __typename?: "Query"; + myTickets: { + __typename?: "PaginatedUserTicket"; + data: Array<{ + __typename?: "UserTicket"; + approvalStatus: Types.TicketApprovalStatus; + id: string; + paymentStatus: Types.TicketPaymentStatus; + redemptionStatus: Types.TicketRedemptionStatus; + }>; + pagination: { + __typename?: "Pagination"; + currentPage: number; + pageSize: number; + totalPages: number; + totalRecords: number; + }; + }; +}; + +export const MyTicketsDocument = gql` + query myTickets($input: PaginatedInputMyTicketsSearchValues!) { + myTickets(input: $input) { + data { + approvalStatus + id + paymentStatus + redemptionStatus + } + pagination { + currentPage + pageSize + totalPages + totalRecords + } + } + } +`; + +/** + * __useMyTicketsQuery__ + * + * To run a query within a React component, call `useMyTicketsQuery` and pass it any options that fit your needs. + * When your component renders, `useMyTicketsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMyTicketsQuery({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useMyTicketsQuery( + baseOptions: Apollo.QueryHookOptions< + MyTicketsQuery, + MyTicketsQueryVariables + > & + ( + | { variables: MyTicketsQueryVariables; skip?: boolean } + | { skip: boolean } + ), +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + MyTicketsDocument, + options, + ); +} +export function useMyTicketsLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + MyTicketsQuery, + MyTicketsQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + MyTicketsDocument, + options, + ); +} +export function useMyTicketsSuspenseQuery( + baseOptions?: Apollo.SuspenseQueryHookOptions< + MyTicketsQuery, + MyTicketsQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useSuspenseQuery( + MyTicketsDocument, + options, + ); +} +export type MyTicketsQueryHookResult = ReturnType; +export type MyTicketsLazyQueryHookResult = ReturnType< + typeof useMyTicketsLazyQuery +>; +export type MyTicketsSuspenseQueryHookResult = ReturnType< + typeof useMyTicketsSuspenseQuery +>; +export type MyTicketsQueryResult = Apollo.QueryResult< + MyTicketsQuery, + MyTicketsQueryVariables +>; diff --git a/app/components/MyTickets/graphql/myTickets.gql b/app/components/MyTickets/graphql/myTickets.gql new file mode 100644 index 0000000..f7b178c --- /dev/null +++ b/app/components/MyTickets/graphql/myTickets.gql @@ -0,0 +1,16 @@ +query myTickets($input: PaginatedInputMyTicketsSearchValues!) { + myTickets(input: $input) { + data { + approvalStatus + id + paymentStatus + redemptionStatus + } + pagination { + currentPage + pageSize + totalPages + totalRecords + } + } +} diff --git a/app/components/Navbar/MainNav.tsx b/app/components/Navbar/MainNav.tsx index a18e839..87cdb02 100644 --- a/app/components/Navbar/MainNav.tsx +++ b/app/components/Navbar/MainNav.tsx @@ -11,9 +11,11 @@ export function MainNav({ items }: NavBarProps) { return (