From 93e286177ff71e37db10e3b952fe1dd4b868f352 Mon Sep 17 00:00:00 2001 From: Cats Juice Date: Tue, 31 Oct 2023 17:47:59 +0800 Subject: [PATCH] feat(core): billing history pagination (#4787) --- .../server/src/modules/users/resolver.ts | 13 ++++++ packages/backend/server/src/schema.gql | 3 ++ .../general-setting/billing/index.tsx | 45 +++++++++++++------ .../general-setting/billing/style.css.ts | 10 ++++- .../frontend/graphql/src/graphql/index.ts | 13 ++++++ .../graphql/src/graphql/invoices-count.gql | 5 +++ packages/frontend/graphql/src/schema.ts | 12 +++++ 7 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 packages/frontend/graphql/src/graphql/invoices-count.gql diff --git a/packages/backend/server/src/modules/users/resolver.ts b/packages/backend/server/src/modules/users/resolver.ts index cc3a529f44..806fa41cab 100644 --- a/packages/backend/server/src/modules/users/resolver.ts +++ b/packages/backend/server/src/modules/users/resolver.ts @@ -8,10 +8,12 @@ import { Args, Field, ID, + Int, Mutation, ObjectType, Query, registerEnumType, + ResolveField, Resolver, } from '@nestjs/graphql'; import type { User } from '@prisma/client'; @@ -156,6 +158,17 @@ export class UserResolver { return user; } + @Throttle({ default: { limit: 10, ttl: 60 } }) + @ResolveField(() => Int, { + name: 'invoiceCount', + description: 'Get user invoice count', + }) + async invoiceCount(@CurrentUser() user: UserType) { + return this.prisma.userInvoice.count({ + where: { userId: user.id }, + }); + } + @Throttle({ default: { limit: 10, diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index dc68b19d1a..7e735b488b 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -23,6 +23,9 @@ type UserType { """User password has been set""" hasPassword: Boolean token: TokenType! + + """Get user invoice count""" + invoiceCount: Int! subscription: UserSubscription invoices(take: Int = 8, skip: Int): [UserInvoice!]! } diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx index a99e6f7098..c698d77676 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx @@ -1,3 +1,4 @@ +import { Pagination } from '@affine/component/member-components'; import { SettingHeader, SettingRow, @@ -6,6 +7,7 @@ import { import { cancelSubscriptionMutation, createCustomerPortalMutation, + getInvoicesCountQuery, type InvoicesQuery, invoicesQuery, InvoiceStatus, @@ -41,6 +43,8 @@ enum DescriptionI18NKey { Yearly = 'com.affine.payment.billing-setting.current-plan.description.yearly', } +const INVOICE_PAGE_SIZE = 12; + const getMessageKey = ( plan: SubscriptionPlan, recurring: SubscriptionRecurring @@ -334,27 +338,40 @@ const CancelSubscription = ({ loading }: { loading?: boolean }) => { const BillingHistory = () => { const t = useAFFiNEI18N(); + const { data: invoicesCountQueryResult } = useQuery({ + query: getInvoicesCountQuery, + }); + + const [skip, setSkip] = useState(0); + const { data: invoicesQueryResult } = useQuery({ query: invoicesQuery, - variables: { - skip: 0, - take: 12, - }, + variables: { skip, take: INVOICE_PAGE_SIZE }, }); const invoices = invoicesQueryResult.currentUser?.invoices ?? []; + const invoiceCount = invoicesCountQueryResult.currentUser?.invoiceCount ?? 0; return ( -
- {invoices.length === 0 ? ( -

- {t['com.affine.payment.billing-setting.no-invoice']()} -

- ) : ( - // TODO: pagination - invoices.map(invoice => ( - - )) +
+
+ {invoices.length === 0 ? ( +

+ {t['com.affine.payment.billing-setting.no-invoice']()} +

+ ) : ( + invoices.map(invoice => ( + + )) + )} +
+ + {invoiceCount > INVOICE_PAGE_SIZE && ( + setSkip(skip)} + /> )}
); diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/style.css.ts b/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/style.css.ts index f923725250..8790ec1831 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/style.css.ts +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/style.css.ts @@ -2,7 +2,15 @@ import { globalStyle, style } from '@vanilla-extract/css'; export const subscription = style({}); -export const billingHistory = style({}); +export const history = style({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: '24px', +}); +export const historyContent = style({ + width: '100%', +}); export const planCard = style({ display: 'flex', diff --git a/packages/frontend/graphql/src/graphql/index.ts b/packages/frontend/graphql/src/graphql/index.ts index b5e39f3166..5a5c189da0 100644 --- a/packages/frontend/graphql/src/graphql/index.ts +++ b/packages/frontend/graphql/src/graphql/index.ts @@ -359,6 +359,19 @@ query getWorkspaces { }`, }; +export const getInvoicesCountQuery = { + id: 'getInvoicesCountQuery' as const, + operationName: 'getInvoicesCount', + definitionName: 'currentUser', + containsFile: false, + query: ` +query getInvoicesCount { + currentUser { + invoiceCount + } +}`, +}; + export const invoicesQuery = { id: 'invoicesQuery' as const, operationName: 'invoices', diff --git a/packages/frontend/graphql/src/graphql/invoices-count.gql b/packages/frontend/graphql/src/graphql/invoices-count.gql new file mode 100644 index 0000000000..99ecb95125 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/invoices-count.gql @@ -0,0 +1,5 @@ +query getInvoicesCount { + currentUser { + invoiceCount + } +} diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts index 07e606e7f2..1b6c989b7f 100644 --- a/packages/frontend/graphql/src/schema.ts +++ b/packages/frontend/graphql/src/schema.ts @@ -359,6 +359,13 @@ export type GetWorkspacesQuery = { workspaces: Array<{ __typename?: 'WorkspaceType'; id: string }>; }; +export type GetInvoicesCountQueryVariables = Exact<{ [key: string]: never }>; + +export type GetInvoicesCountQuery = { + __typename?: 'Query'; + currentUser: { __typename?: 'UserType'; invoiceCount: number } | null; +}; + export type InvoicesQueryVariables = Exact<{ take: Scalars['Int']['input']; skip: Scalars['Int']['input']; @@ -693,6 +700,11 @@ export type Queries = variables: GetWorkspacesQueryVariables; response: GetWorkspacesQuery; } + | { + name: 'getInvoicesCountQuery'; + variables: GetInvoicesCountQueryVariables; + response: GetInvoicesCountQuery; + } | { name: 'invoicesQuery'; variables: InvoicesQueryVariables;