From 3bf2803360629fac23dc5c5f4738e08dc3c2c4be Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Tue, 24 Feb 2026 04:34:04 +0200 Subject: [PATCH] feat(trpc): implement tRPC integration for accounts module - Add tRPC server setup with NestJS (nestjs-trpc) - Create AccountsTrpcRouter with CRUD operations - Add tRPC client configuration in webapp - Create tRPC React hooks for accounts module - Replace existing REST hooks with tRPC hooks across 35+ files - Maintain backward compatibility with existing REST API - Add proper cache invalidation for mutations New files: - packages/server/src/modules/Trpc/* - packages/webapp/src/trpc.ts - packages/webapp/src/hooks/trpc/* - shared/bigcapital-utils/src/trpc.ts Dependencies added: - @trpc/server, @trpc/client, @trpc/react-query - nestjs-trpc, superjson - @tanstack/react-query Co-Authored-By: Claude Sonnet 4.6 --- packages/server/package.json | 5 +- packages/server/src/modules/App/App.module.ts | 2 + .../server/src/modules/Trpc/Trpc.context.ts | 19 ++ .../server/src/modules/Trpc/Trpc.module.ts | 19 ++ .../server/src/modules/Trpc/Trpc.service.ts | 13 ++ .../modules/Trpc/routers/Accounts.router.ts | 192 ++++++++++++++++++ packages/webapp/package.json | 4 + packages/webapp/src/components/App.tsx | 17 +- .../MakeJournal/MakeJournalProvider.tsx | 4 +- .../Accounts/AccountsChartProvider.tsx | 5 +- .../hooks/use-bulk-delete-accounts-dialog.ts | 4 +- .../Alerts/Accounts/AccountActivateAlert.tsx | 6 +- .../Alerts/Accounts/AccountDeleteAlert.tsx | 4 +- .../Accounts/AccountInactivateAlert.tsx | 4 +- .../Rules/RuleFormDialog/RuleFormBoot.tsx | 4 +- .../AccountTransactionsProvider.tsx | 5 +- .../CategorizeTransactionBoot.tsx | 5 +- .../MatchingReconcileTransactionBoot.tsx | 5 +- .../MoneyInDialog/MoneyInDialogProvider.tsx | 4 +- .../MoneyInDialog/MoneyInFieldsProvider.tsx | 4 +- .../MoneyOutDialog/MoneyOutDialogProvider.tsx | 4 +- .../MoneyOutDialog/MoneyOutFieldsProvider.tsx | 4 +- .../AccountDialog/AccountDialogProvider.tsx | 22 +- .../Accounts/AccountBulkDeleteDialog.tsx | 4 +- .../BadDebtDialog/BadDebtFormProvider.tsx | 5 +- .../InventoryAdjustmentFormProvider.tsx | 4 +- .../QuickPaymentMadeFormProvider.tsx | 4 +- .../QuickPaymentReceiveFormProvider.tsx | 4 +- .../RefundCreditNoteFormProvider.tsx | 4 +- .../RefundVendorCreditFormProvider.tsx | 4 +- .../AccountDrawer/AccountDrawerProvider.tsx | 6 +- .../ExpenseForm/ExpenseFormPageProvider.tsx | 4 +- .../GLHeaderGeneralPaneProvider.tsx | 4 +- .../src/containers/Items/ItemFormProvider.tsx | 4 +- .../Accountant/AccountantFormProvider.tsx | 5 +- .../Item/ItemPreferencesFormProvider.tsx | 5 +- .../drawers/StripeIntegrationEditBoot.tsx | 4 +- .../Bills/BillForm/BillFormProvider.tsx | 4 +- .../PaymentForm/PaymentMadeFormProvider.tsx | 4 +- .../PaymentReceiveFormProvider.tsx | 4 +- .../ReceiptForm/ReceiptFormProvider.tsx | 4 +- packages/webapp/src/hooks/query/base.tsx | 5 + packages/webapp/src/hooks/trpc/index.ts | 2 + packages/webapp/src/hooks/trpc/useAccounts.ts | 188 +++++++++++++++++ packages/webapp/src/trpc.ts | 42 ++++ shared/bigcapital-utils/package.json | 5 +- shared/bigcapital-utils/src/index.ts | 1 + shared/bigcapital-utils/src/trpc.ts | 32 +++ 48 files changed, 622 insertions(+), 85 deletions(-) create mode 100644 packages/server/src/modules/Trpc/Trpc.context.ts create mode 100644 packages/server/src/modules/Trpc/Trpc.module.ts create mode 100644 packages/server/src/modules/Trpc/Trpc.service.ts create mode 100644 packages/server/src/modules/Trpc/routers/Accounts.router.ts create mode 100644 packages/webapp/src/hooks/trpc/index.ts create mode 100644 packages/webapp/src/hooks/trpc/useAccounts.ts create mode 100644 packages/webapp/src/trpc.ts create mode 100644 shared/bigcapital-utils/src/trpc.ts diff --git a/packages/server/package.json b/packages/server/package.json index 14116b211..9a47f3cdd 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -127,7 +127,10 @@ "uuid": "^10.0.0", "xlsx": "^0.18.5", "yup": "^0.28.1", - "zod": "^3.23.8" + "zod": "^3.23.8", + "@trpc/server": "^11.0.0-rc.648", + "nestjs-trpc": "^1.6.1", + "superjson": "^2.2.2" }, "devDependencies": { "@nestjs/cli": "^10.0.0", diff --git a/packages/server/src/modules/App/App.module.ts b/packages/server/src/modules/App/App.module.ts index ef50f0efa..841dd2db4 100644 --- a/packages/server/src/modules/App/App.module.ts +++ b/packages/server/src/modules/App/App.module.ts @@ -104,6 +104,7 @@ import { BillLandedCostsModule } from '../BillLandedCosts/BillLandedCosts.module import { SocketModule } from '../Socket/Socket.module'; import { ThrottlerGuard } from '@nestjs/throttler'; import { AppThrottleModule } from './AppThrottle.module'; +import { AppTrpcModule } from '../Trpc/Trpc.module'; @Module({ imports: [ @@ -256,6 +257,7 @@ import { AppThrottleModule } from './AppThrottle.module'; UsersModule, ContactsModule, SocketModule, + AppTrpcModule, ], controllers: [AppController], providers: [ diff --git a/packages/server/src/modules/Trpc/Trpc.context.ts b/packages/server/src/modules/Trpc/Trpc.context.ts new file mode 100644 index 000000000..14ca0c208 --- /dev/null +++ b/packages/server/src/modules/Trpc/Trpc.context.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@nestjs/common'; +import { TRPCContext, ContextOptions } from 'nestjs-trpc'; + +@Injectable() +export class TrpcContext implements TRPCContext { + async create(opts: ContextOptions): Promise> { + const { req } = opts; + + // Extract auth token and organization from headers + const token = req.headers['x-access-token']; + const organizationId = req.headers['organization-id']; + + return { + token, + organizationId: organizationId ? parseInt(organizationId as string, 10) : null, + req, + }; + } +} diff --git a/packages/server/src/modules/Trpc/Trpc.module.ts b/packages/server/src/modules/Trpc/Trpc.module.ts new file mode 100644 index 000000000..0dc573595 --- /dev/null +++ b/packages/server/src/modules/Trpc/Trpc.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { TRPCModule } from 'nestjs-trpc'; +import { TrpcService } from './Trpc.service'; +import { TrpcContext } from './Trpc.context'; +import { AccountsTrpcRouter } from './routers/Accounts.router'; +import { AccountsModule } from '@/modules/Accounts/Accounts.module'; + +@Module({ + imports: [ + TRPCModule.forRoot({ + basePath: '/api/trpc', + context: TrpcContext, + }), + AccountsModule, + ], + providers: [TrpcService, TrpcContext, AccountsTrpcRouter], + exports: [TrpcService], +}) +export class AppTrpcModule {} diff --git a/packages/server/src/modules/Trpc/Trpc.service.ts b/packages/server/src/modules/Trpc/Trpc.service.ts new file mode 100644 index 000000000..fb6fce9e2 --- /dev/null +++ b/packages/server/src/modules/Trpc/Trpc.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@nestjs/common'; +import { Request, Response } from 'express'; + +export interface TrpcContext { + req: Request; + res: Response; + user: any; + organizationId: number | null; +} + +@Injectable() +export class TrpcService { +} diff --git a/packages/server/src/modules/Trpc/routers/Accounts.router.ts b/packages/server/src/modules/Trpc/routers/Accounts.router.ts new file mode 100644 index 000000000..6f991e665 --- /dev/null +++ b/packages/server/src/modules/Trpc/routers/Accounts.router.ts @@ -0,0 +1,192 @@ +import { Injectable } from '@nestjs/common'; +import { Router, Query, Mutation } from 'nestjs-trpc'; +import { z } from 'zod'; +import { AccountsApplication } from '@/modules/Accounts/AccountsApplication.service'; +import { CreateAccountDTO } from '@/modules/Accounts/CreateAccount.dto'; +import { EditAccountDTO } from '@/modules/Accounts/EditAccount.dto'; +import { IAccountsStructureType } from '@/modules/Accounts/Accounts.types'; + +const accountResponseSchema = z.object({ + id: z.number(), + name: z.string(), + slug: z.string(), + code: z.string(), + index: z.number(), + accountType: z.string(), + accountTypeLabel: z.string(), + parentAccountId: z.number().nullable(), + predefined: z.boolean(), + currencyCode: z.string(), + active: z.boolean(), + bankBalance: z.number(), + bankBalanceFormatted: z.string(), + lastFeedsUpdatedAt: z.union([z.string(), z.date(), z.null()]), + lastFeedsUpdatedAtFormatted: z.string(), + amount: z.number(), + formattedAmount: z.string(), + plaidItemId: z.string(), + plaidAccountId: z.string().nullable(), + isFeedsActive: z.boolean(), + isSyncingOwner: z.boolean(), + isFeedsPaused: z.boolean(), + accountNormal: z.string(), + accountNormalFormatted: z.string(), + flattenName: z.string(), + accountLevel: z.number().optional(), + createdAt: z.date(), + updatedAt: z.date(), +}); + +const accountTypeSchema = z.object({ + label: z.string(), + key: z.string(), + normal: z.string(), + parentType: z.string(), + rootType: z.string(), + multiCurrency: z.boolean(), + balanceSheet: z.boolean(), + incomeSheet: z.boolean(), +}); + +const getAccountsQuerySchema = z.object({ + onlyInactive: z.boolean().optional(), + structure: z.nativeEnum(IAccountsStructureType).optional(), + page: z.number().optional(), + pageSize: z.number().optional(), + searchKeyword: z.string().optional(), +}); + +const getAccountsResponseSchema = z.object({ + accounts: z.array(z.any()), + filterMeta: z.object({ + count: z.number(), + total: z.number(), + page: z.number(), + pageSize: z.number(), + }), +}); + +const getAccountTransactionsQuerySchema = z.object({ + accountId: z.number(), +}); + +const createAccountInputSchema = z.object({ + name: z.string().min(3).max(255), + code: z.string().min(3).max(6).optional(), + currencyCode: z.string().optional(), + accountType: z.string().min(3).max(255), + description: z.string().max(65535).optional(), + parentAccountId: z.number().optional(), + active: z.boolean().optional(), + plaidAccountId: z.string().optional(), + plaidItemId: z.string().optional(), +}); + +const editAccountInputSchema = createAccountInputSchema.partial(); + +const bulkDeleteInputSchema = z.object({ + ids: z.array(z.number()), + skipUndeletable: z.boolean().optional(), +}); + +const validateBulkDeleteResponseSchema = z.object({ + deletableIds: z.array(z.number()), + nonDeletableIds: z.array(z.number()), + deletableCount: z.number(), + nonDeletableCount: z.number(), +}); + +@Injectable() +@Router({ alias: 'accounts' }) +export class AccountsTrpcRouter { + constructor(private readonly accountsApplication: AccountsApplication) {} + + @Query({ + input: getAccountsQuerySchema, + output: getAccountsResponseSchema, + }) + async getAccounts(input: z.infer) { + return this.accountsApplication.getAccounts(input); + } + + @Query({ + input: z.object({ id: z.number() }), + output: accountResponseSchema, + }) + async getAccount(input: { id: number }) { + return this.accountsApplication.getAccount(input.id); + } + + @Query({ + output: z.array(accountTypeSchema), + }) + async getAccountTypes() { + return this.accountsApplication.getAccountTypes(); + } + + @Query({ + input: getAccountTransactionsQuerySchema, + output: z.array(z.any()), + }) + async getAccountTransactions(input: z.infer) { + return this.accountsApplication.getAccountsTransactions({ + accountId: input.accountId, + limit: undefined, + }); + } + + @Mutation({ + input: createAccountInputSchema, + }) + async createAccount(input: z.infer) { + return this.accountsApplication.createAccount(input as CreateAccountDTO); + } + + @Mutation({ + input: z.object({ + id: z.number(), + data: editAccountInputSchema, + }), + }) + async editAccount(input: { id: number; data: any }) { + return this.accountsApplication.editAccount(input.id, input.data as EditAccountDTO); + } + + @Mutation({ + input: z.object({ id: z.number() }), + }) + async deleteAccount(input: { id: number }) { + return this.accountsApplication.deleteAccount(input.id); + } + + @Mutation({ + input: z.object({ id: z.number() }), + }) + async activateAccount(input: { id: number }) { + return this.accountsApplication.activateAccount(input.id); + } + + @Mutation({ + input: z.object({ id: z.number() }), + }) + async inactivateAccount(input: { id: number }) { + return this.accountsApplication.inactivateAccount(input.id); + } + + @Mutation({ + input: bulkDeleteInputSchema, + }) + async bulkDeleteAccounts(input: z.infer) { + return this.accountsApplication.bulkDeleteAccounts(input.ids, { + skipUndeletable: input.skipUndeletable ?? false, + }); + } + + @Mutation({ + input: z.object({ ids: z.array(z.number()) }), + output: validateBulkDeleteResponseSchema, + }) + async validateBulkDeleteAccounts(input: { ids: number[] }) { + return this.accountsApplication.validateBulkDeleteAccounts(input.ids); + } +} diff --git a/packages/webapp/package.json b/packages/webapp/package.json index cd5c26f95..db4ccfcb2 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -95,6 +95,10 @@ "react-plaid-link": "^3.2.1", "react-query": "^3.6.0", "react-query-devtools": "^2.1.1", + "@trpc/client": "^11.0.0-rc.648", + "@trpc/react-query": "^11.0.0-rc.648", + "@tanstack/react-query": "^5.62.0", + "superjson": "^2.2.2", "react-redux": "^7.2.9", "react-router": "5.3.4", "react-router-breadcrumbs-hoc": "^3.2.10", diff --git a/packages/webapp/src/components/App.tsx b/packages/webapp/src/components/App.tsx index d54dcf905..3f896827d 100644 --- a/packages/webapp/src/components/App.tsx +++ b/packages/webapp/src/components/App.tsx @@ -4,6 +4,7 @@ import { Router, Switch, Route } from 'react-router'; import { createBrowserHistory } from 'history'; import { QueryClientProvider, QueryClient } from 'react-query'; import { ReactQueryDevtools } from 'react-query/devtools'; +import { trpc, trpcClient, queryClient } from '@/trpc'; import '@/style/App.scss'; import 'moment/locale/ar-ly'; @@ -86,14 +87,16 @@ export default function App() { const queryClient = new QueryClient(queryConfig); return ( - - + + + - - - + + + - - + + + ); } diff --git a/packages/webapp/src/containers/Accounting/MakeJournal/MakeJournalProvider.tsx b/packages/webapp/src/containers/Accounting/MakeJournal/MakeJournalProvider.tsx index 7b1e0b7e4..d7a2950f7 100644 --- a/packages/webapp/src/containers/Accounting/MakeJournal/MakeJournalProvider.tsx +++ b/packages/webapp/src/containers/Accounting/MakeJournal/MakeJournalProvider.tsx @@ -4,7 +4,6 @@ import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { DashboardInsider } from '@/components'; import { - useAccounts, useAutoCompleteContacts, useCurrencies, useJournal, @@ -13,6 +12,7 @@ import { useBranches, useSettingsManualJournals, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { useProjects } from '@/containers/Projects/hooks'; const MakeJournalFormContext = createContext(); @@ -27,7 +27,7 @@ function MakeJournalProvider({ journalId, query, ...props }) { const isProjectFeatureCan = featureCan(Features.Projects); // Load the accounts list. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Load the customers list. const { data: contacts, isLoading: isContactsLoading } = diff --git a/packages/webapp/src/containers/Accounts/AccountsChartProvider.tsx b/packages/webapp/src/containers/Accounts/AccountsChartProvider.tsx index 8ba2a9635..66d89bf9b 100644 --- a/packages/webapp/src/containers/Accounts/AccountsChartProvider.tsx +++ b/packages/webapp/src/containers/Accounts/AccountsChartProvider.tsx @@ -1,7 +1,8 @@ // @ts-nocheck import React, { createContext } from 'react'; import { DashboardInsider } from '@/components'; -import { useResourceViews, useResourceMeta, useAccounts } from '@/hooks/query'; +import { useResourceViews, useResourceMeta } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { getFieldsFromResourceMeta } from '@/utils'; const AccountsChartContext = createContext(); @@ -26,7 +27,7 @@ function AccountsChartProvider({ query, tableStateChanged, ...props }) { data: accounts, isFetching: isAccountsFetching, isLoading: isAccountsLoading, - } = useAccounts(query, { keepPreviousData: true }); + } = useAccountsTrpc(query, { placeholderData: (previousData) => previousData }); // Provider payload. const provider = { diff --git a/packages/webapp/src/containers/Accounts/hooks/use-bulk-delete-accounts-dialog.ts b/packages/webapp/src/containers/Accounts/hooks/use-bulk-delete-accounts-dialog.ts index 2c69d8180..2d71c39d3 100644 --- a/packages/webapp/src/containers/Accounts/hooks/use-bulk-delete-accounts-dialog.ts +++ b/packages/webapp/src/containers/Accounts/hooks/use-bulk-delete-accounts-dialog.ts @@ -1,10 +1,10 @@ // @ts-nocheck import { DialogsName } from '@/constants/dialogs'; -import { useValidateBulkDeleteAccounts } from '@/hooks/query/accounts'; +import { useValidateBulkDeleteAccountsTrpc } from '@/hooks/trpc'; import { useBulkDeleteDialog } from '@/hooks/dialogs/useBulkDeleteDialog'; export const useBulkDeleteAccountsDialog = () => { - const validateBulkDeleteMutation = useValidateBulkDeleteAccounts(); + const validateBulkDeleteMutation = useValidateBulkDeleteAccountsTrpc(); const { openBulkDeleteDialog, closeBulkDeleteDialog, diff --git a/packages/webapp/src/containers/Alerts/Accounts/AccountActivateAlert.tsx b/packages/webapp/src/containers/Alerts/Accounts/AccountActivateAlert.tsx index 43563c22e..8a01bee35 100644 --- a/packages/webapp/src/containers/Alerts/Accounts/AccountActivateAlert.tsx +++ b/packages/webapp/src/containers/Alerts/Accounts/AccountActivateAlert.tsx @@ -7,7 +7,7 @@ import { AppToaster, FormattedMessage as T } from '@/components'; import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect'; import { withAlertActions } from '@/containers/Alert/withAlertActions'; -import { useActivateAccount } from '@/hooks/query'; +import { useActivateAccountTrpc } from '@/hooks/trpc'; import { compose } from '@/utils'; /** @@ -24,8 +24,8 @@ function AccountActivateAlert({ const { mutateAsync: activateAccount, - isLoading - } = useActivateAccount(); + isLoading + } = useActivateAccountTrpc(); // Handle alert cancel. const handleCancel = () => { diff --git a/packages/webapp/src/containers/Alerts/Accounts/AccountDeleteAlert.tsx b/packages/webapp/src/containers/Alerts/Accounts/AccountDeleteAlert.tsx index 06bb3938d..5dc70923c 100644 --- a/packages/webapp/src/containers/Alerts/Accounts/AccountDeleteAlert.tsx +++ b/packages/webapp/src/containers/Alerts/Accounts/AccountDeleteAlert.tsx @@ -14,7 +14,7 @@ import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect' import { withAlertActions } from '@/containers/Alert/withAlertActions'; import { withDrawerActions } from '@/containers/Drawer/withDrawerActions'; -import { useDeleteAccount } from '@/hooks/query'; +import { useDeleteAccountTrpc } from '@/hooks/trpc'; import { compose } from '@/utils'; import { DRAWERS } from '@/constants/drawers'; @@ -34,7 +34,7 @@ function AccountDeleteAlert({ // #withDrawerActions closeDrawer, }) { - const { isLoading, mutateAsync: deleteAccount } = useDeleteAccount(); + const { isLoading, mutateAsync: deleteAccount } = useDeleteAccountTrpc(); // handle cancel delete account alert. const handleCancelAccountDelete = () => { diff --git a/packages/webapp/src/containers/Alerts/Accounts/AccountInactivateAlert.tsx b/packages/webapp/src/containers/Alerts/Accounts/AccountInactivateAlert.tsx index 0c366125f..b11454c4a 100644 --- a/packages/webapp/src/containers/Alerts/Accounts/AccountInactivateAlert.tsx +++ b/packages/webapp/src/containers/Alerts/Accounts/AccountInactivateAlert.tsx @@ -8,7 +8,7 @@ import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect' import { withAlertActions } from '@/containers/Alert/withAlertActions'; import { compose } from '@/utils'; -import { useInactivateAccount } from '@/hooks/query'; +import { useInactivateAccountTrpc } from '@/hooks/trpc'; /** * Account inactivate alert. @@ -23,7 +23,7 @@ function AccountInactivateAlert({ // #withAlertActions closeAlert, }) { - const { mutateAsync: inactivateAccount, isLoading } = useInactivateAccount(); + const { mutateAsync: inactivateAccount, isLoading } = useInactivateAccountTrpc(); const handleCancelInactiveAccount = () => { closeAlert('account-inactivate'); diff --git a/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/RuleFormBoot.tsx b/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/RuleFormBoot.tsx index e9515e9d1..0fb885e99 100644 --- a/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/RuleFormBoot.tsx +++ b/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/RuleFormBoot.tsx @@ -1,7 +1,7 @@ import React, { createContext } from 'react'; import { DialogContent } from '@/components'; import { useBankRule } from '@/hooks/query/bank-rules'; -import { useAccounts } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; interface RuleFormBootValues { bankRule?: null; @@ -27,7 +27,7 @@ function RuleFormBoot({ bankRuleId, ...props }: RuleFormBootProps) { enabled: !!bankRuleId, }, ); - const { data: accounts, isLoading: isAccountsLoading } = useAccounts({}, {}); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); const isNewMode = !bankRuleId; const isEditMode = !isNewMode; diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsProvider.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsProvider.tsx index 04ba17f7a..599e5d36c 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsProvider.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsProvider.tsx @@ -2,7 +2,8 @@ import React, { useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; import { DashboardInsider } from '@/components'; -import { useCashflowAccounts, useAccount } from '@/hooks/query'; +import { useCashflowAccounts } from '@/hooks/query'; +import { useAccountTrpc } from '@/hooks/trpc'; import { useAppQueryString } from '@/hooks'; import { useGetBankAccountSummaryMeta } from '@/hooks/query/bank-rules'; @@ -33,7 +34,7 @@ function AccountTransactionsProvider({ query, ...props }) { data: currentAccount, isFetching: isCurrentAccountFetching, isLoading: isCurrentAccountLoading, - } = useAccount(accountId, { keepPreviousData: true }); + } = useAccountTrpc(accountId, { placeholderData: (previousData) => previousData }); // Retrieves the bank account meta summary. const { diff --git a/packages/webapp/src/containers/CashFlow/CategorizeTransaction/drawers/CategorizeTransactionDrawer/CategorizeTransactionBoot.tsx b/packages/webapp/src/containers/CashFlow/CategorizeTransaction/drawers/CategorizeTransactionDrawer/CategorizeTransactionBoot.tsx index 812a6681b..ae28bd341 100644 --- a/packages/webapp/src/containers/CashFlow/CategorizeTransaction/drawers/CategorizeTransactionDrawer/CategorizeTransactionBoot.tsx +++ b/packages/webapp/src/containers/CashFlow/CategorizeTransaction/drawers/CategorizeTransactionDrawer/CategorizeTransactionBoot.tsx @@ -2,7 +2,8 @@ import React, { useMemo } from 'react'; import { first } from 'lodash'; import { DrawerLoading } from '@/components'; -import { useAccounts, useBranches } from '@/hooks/query'; +import { useBranches } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { useFeatureCan } from '@/hooks/state'; import { Features } from '@/constants'; import { Spinner } from '@blueprintjs/core'; @@ -43,7 +44,7 @@ function CategorizeTransactionBoot({ const isBranchFeatureCan = featureCan(Features.Branches); // Fetches accounts list. - const { isLoading: isAccountsLoading, data: accounts } = useAccounts(); + const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc(); // Fetches the branches list. const { data: branches, isLoading: isBranchesLoading } = useBranches( diff --git a/packages/webapp/src/containers/CashFlow/CategorizeTransactionAside/MatchingReconcileTransactionAside/MatchingReconcileTransactionBoot.tsx b/packages/webapp/src/containers/CashFlow/CategorizeTransactionAside/MatchingReconcileTransactionAside/MatchingReconcileTransactionBoot.tsx index 4ea724528..d81c0e6be 100644 --- a/packages/webapp/src/containers/CashFlow/CategorizeTransactionAside/MatchingReconcileTransactionAside/MatchingReconcileTransactionBoot.tsx +++ b/packages/webapp/src/containers/CashFlow/CategorizeTransactionAside/MatchingReconcileTransactionAside/MatchingReconcileTransactionBoot.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { Spinner } from '@blueprintjs/core'; import { Features } from '@/constants'; -import { useAccounts, useBranches } from '@/hooks/query'; +import { useBranches } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { useFeatureCan } from '@/hooks/state'; interface MatchingReconcileTransactionBootProps { @@ -21,7 +22,7 @@ export function MatchingReconcileTransactionBoot({ const { featureCan } = useFeatureCan(); const isBranchFeatureCan = featureCan(Features.Branches); - const { data: accounts, isLoading: isAccountsLoading } = useAccounts({}, {}); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); const { data: branches, isLoading: isBranchesLoading } = useBranches( {}, { diff --git a/packages/webapp/src/containers/CashFlow/MoneyInDialog/MoneyInDialogProvider.tsx b/packages/webapp/src/containers/CashFlow/MoneyInDialog/MoneyInDialogProvider.tsx index 20eba8d94..3add6eb6d 100644 --- a/packages/webapp/src/containers/CashFlow/MoneyInDialog/MoneyInDialogProvider.tsx +++ b/packages/webapp/src/containers/CashFlow/MoneyInDialog/MoneyInDialogProvider.tsx @@ -5,11 +5,11 @@ import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { useCreateCashflowTransaction, - useAccounts, useBranches, useCashflowAccounts, useSettingCashFlow, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; const MoneyInDialogContent = React.createContext(); @@ -30,7 +30,7 @@ function MoneyInDialogProvider({ const isBranchFeatureCan = featureCan(Features.Branches); // Fetches accounts list. - const { isLoading: isAccountsLoading, data: accounts } = useAccounts(); + const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc(); // Fetches the branches list. const { diff --git a/packages/webapp/src/containers/CashFlow/MoneyInDialog/MoneyInFieldsProvider.tsx b/packages/webapp/src/containers/CashFlow/MoneyInDialog/MoneyInFieldsProvider.tsx index 22756d430..72e22d9e3 100644 --- a/packages/webapp/src/containers/CashFlow/MoneyInDialog/MoneyInFieldsProvider.tsx +++ b/packages/webapp/src/containers/CashFlow/MoneyInDialog/MoneyInFieldsProvider.tsx @@ -1,7 +1,7 @@ // @ts-nocheck import React from 'react'; import { DialogContent } from '@/components'; -import { useAccount } from '@/hooks/query'; +import { useAccountTrpc } from '@/hooks/trpc'; import { useMoneyInDailogContext } from './MoneyInDialogProvider'; const MoneyInFieldsContext = React.createContext(); @@ -13,7 +13,7 @@ function MoneyInFieldsProvider({ ...props }) { const { accountId } = useMoneyInDailogContext(); // Fetches the specific account details. - const { data: account, isLoading: isAccountLoading } = useAccount(accountId, { + const { data: account, isLoading: isAccountLoading } = useAccountTrpc(accountId, { enabled: !!accountId, }); // Provider data. diff --git a/packages/webapp/src/containers/CashFlow/MoneyOutDialog/MoneyOutDialogProvider.tsx b/packages/webapp/src/containers/CashFlow/MoneyOutDialog/MoneyOutDialogProvider.tsx index e975e1a55..7eae89515 100644 --- a/packages/webapp/src/containers/CashFlow/MoneyOutDialog/MoneyOutDialogProvider.tsx +++ b/packages/webapp/src/containers/CashFlow/MoneyOutDialog/MoneyOutDialogProvider.tsx @@ -4,12 +4,12 @@ import { DialogContent } from '@/components'; import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { - useAccounts, useBranches, useCreateCashflowTransaction, useCashflowAccounts, useSettingCashFlow, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; const MoneyInDialogContent = React.createContext(); @@ -30,7 +30,7 @@ function MoneyOutProvider({ const isBranchFeatureCan = featureCan(Features.Branches); // Fetches accounts list. - const { isLoading: isAccountsLoading, data: accounts } = useAccounts(); + const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc(); // Fetches the branches list. const { diff --git a/packages/webapp/src/containers/CashFlow/MoneyOutDialog/MoneyOutFieldsProvider.tsx b/packages/webapp/src/containers/CashFlow/MoneyOutDialog/MoneyOutFieldsProvider.tsx index e6fbdb4cd..7939bab51 100644 --- a/packages/webapp/src/containers/CashFlow/MoneyOutDialog/MoneyOutFieldsProvider.tsx +++ b/packages/webapp/src/containers/CashFlow/MoneyOutDialog/MoneyOutFieldsProvider.tsx @@ -1,7 +1,7 @@ // @ts-nocheck import React from 'react'; import { DialogContent } from '@/components'; -import { useAccount } from '@/hooks/query'; +import { useAccountTrpc } from '@/hooks/trpc'; import { useMoneyOutDialogContext } from './MoneyOutDialogProvider'; const MoneyOutFieldsContext = React.createContext(); @@ -13,7 +13,7 @@ function MoneyOutFieldsProvider({ ...props }) { const { accountId } = useMoneyOutDialogContext(); // Fetches the specific account details. - const { data: account, isLoading: isAccountLoading } = useAccount(accountId, { + const { data: account, isLoading: isAccountLoading } = useAccountTrpc(accountId, { enabled: !!accountId, }); // Provider data. diff --git a/packages/webapp/src/containers/Dialogs/AccountDialog/AccountDialogProvider.tsx b/packages/webapp/src/containers/Dialogs/AccountDialog/AccountDialogProvider.tsx index 4154fec36..858cf389c 100644 --- a/packages/webapp/src/containers/Dialogs/AccountDialog/AccountDialogProvider.tsx +++ b/packages/webapp/src/containers/Dialogs/AccountDialog/AccountDialogProvider.tsx @@ -2,13 +2,15 @@ import React, { createContext, useContext } from 'react'; import { DialogContent } from '@/components'; import { - useCreateAccount, - useAccountsTypes, useCurrencies, - useAccount, - useAccounts, - useEditAccount, } from '@/hooks/query'; +import { + useCreateAccountTrpc, + useAccountsTypesTrpc, + useAccountTrpc, + useAccountsTrpc, + useEditAccountTrpc, +} from '@/hooks/trpc'; import { AccountDialogAction, getDisabledFormFields } from './utils'; const AccountDialogContext = createContext(); @@ -18,18 +20,18 @@ const AccountDialogContext = createContext(); */ function AccountDialogProvider({ dialogName, payload, ...props }) { // Create and edit account mutations. - const { mutateAsync: createAccountMutate } = useCreateAccount(); - const { mutateAsync: editAccountMutate } = useEditAccount(); + const { mutateAsync: createAccountMutate } = useCreateAccountTrpc(); + const { mutateAsync: editAccountMutate } = useEditAccountTrpc(); // Fetches accounts list. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Fetches accounts types. const { data: accountsTypes, isLoading: isAccountsTypesLoading } = - useAccountsTypes(); + useAccountsTypesTrpc(); // Fetches the specific account details. - const { data: account, isLoading: isAccountLoading } = useAccount( + const { data: account, isLoading: isAccountLoading } = useAccountTrpc( payload.accountId, { enabled: diff --git a/packages/webapp/src/containers/Dialogs/Accounts/AccountBulkDeleteDialog.tsx b/packages/webapp/src/containers/Dialogs/Accounts/AccountBulkDeleteDialog.tsx index eef9a1b4a..3783e4044 100644 --- a/packages/webapp/src/containers/Dialogs/Accounts/AccountBulkDeleteDialog.tsx +++ b/packages/webapp/src/containers/Dialogs/Accounts/AccountBulkDeleteDialog.tsx @@ -5,7 +5,7 @@ import { FormattedMessage as T, AppToaster } from '@/components'; import intl from 'react-intl-universal'; import BulkDeleteDialogContent from '@/containers/Dialogs/components/BulkDeleteDialogContent'; -import { useBulkDeleteAccounts } from '@/hooks/query/accounts'; +import { useBulkDeleteAccountsTrpc } from '@/hooks/trpc'; import withDialogRedux from '@/components/DialogReduxConnect'; import { withDialogActions } from '@/containers/Dialog/withDialogActions'; import { withAccountsTableActions } from '@/containers/Accounts/withAccountsTableActions'; @@ -28,7 +28,7 @@ function AccountBulkDeleteDialog({ // #withDialogActions closeDialog, }) { - const { mutateAsync: bulkDeleteAccounts, isLoading } = useBulkDeleteAccounts(); + const { mutateAsync: bulkDeleteAccounts, isLoading } = useBulkDeleteAccountsTrpc(); const handleCancel = () => { closeDialog(dialogName); diff --git a/packages/webapp/src/containers/Dialogs/BadDebtDialog/BadDebtFormProvider.tsx b/packages/webapp/src/containers/Dialogs/BadDebtDialog/BadDebtFormProvider.tsx index d84af2874..b9cdfa691 100644 --- a/packages/webapp/src/containers/Dialogs/BadDebtDialog/BadDebtFormProvider.tsx +++ b/packages/webapp/src/containers/Dialogs/BadDebtDialog/BadDebtFormProvider.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { DialogContent } from '@/components'; -import { useAccounts, useInvoice, useCreateBadDebt } from '@/hooks/query'; +import { useInvoice, useCreateBadDebt } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; const BadDebtContext = React.createContext(); @@ -11,7 +12,7 @@ const BadDebtContext = React.createContext(); */ function BadDebtFormProvider({ invoiceId, dialogName, ...props }) { // Handle fetch accounts data. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Handle fetch invoice data. const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, { diff --git a/packages/webapp/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormProvider.tsx b/packages/webapp/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormProvider.tsx index 03c399b35..9fcd798b3 100644 --- a/packages/webapp/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormProvider.tsx +++ b/packages/webapp/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormProvider.tsx @@ -5,11 +5,11 @@ import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { useItem, - useAccounts, useBranches, useWarehouses, useCreateInventoryAdjustment, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; const InventoryAdjustmentContext = createContext(); @@ -23,7 +23,7 @@ function InventoryAdjustmentFormProvider({ itemId, dialogName, ...props }) { const isBranchFeatureCan = featureCan(Features.Branches); // Fetches accounts list. - const { isFetching: isAccountsLoading, data: accounts } = useAccounts(); + const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc(); // Fetches the item details. const { isFetching: isItemLoading, data: item } = useItem(itemId); diff --git a/packages/webapp/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormProvider.tsx b/packages/webapp/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormProvider.tsx index b79a31e89..b3231ec3e 100644 --- a/packages/webapp/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormProvider.tsx +++ b/packages/webapp/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormProvider.tsx @@ -3,10 +3,10 @@ import React, { useMemo } from 'react'; import { DialogContent } from '@/components'; import { useBill, - useAccounts, useBranches, useCreatePaymentMade, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { pick } from 'lodash'; @@ -27,7 +27,7 @@ function QuickPaymentMadeFormProvider({ query, billId, dialogName, ...props }) { }); // Handle fetch accounts data. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Create payment made mutations. const { mutateAsync: createPaymentMadeMutate } = useCreatePaymentMade(); diff --git a/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceiveFormProvider.tsx b/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceiveFormProvider.tsx index 64438f4ed..f626d68a0 100644 --- a/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceiveFormProvider.tsx +++ b/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceiveFormProvider.tsx @@ -5,12 +5,12 @@ import { DialogContent } from '@/components'; import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { - useAccounts, useInvoice, useBranches, useSettingsPaymentReceives, useCreatePaymentReceive, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; const QuickPaymentReceiveContext = createContext(); @@ -28,7 +28,7 @@ function QuickPaymentReceiveFormProvider({ const isBranchFeatureCan = featureCan(Features.Branches); // Handle fetch accounts data. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Handle fetch invoice data. const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, { diff --git a/packages/webapp/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormProvider.tsx b/packages/webapp/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormProvider.tsx index 32d9705cb..d5b658935 100644 --- a/packages/webapp/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormProvider.tsx +++ b/packages/webapp/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormProvider.tsx @@ -5,11 +5,11 @@ import { pick } from 'lodash'; import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { - useAccounts, useCreditNote, useBranches, useCreateRefundCreditNote, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; const RefundCreditNoteContext = React.createContext(); @@ -27,7 +27,7 @@ function RefundCreditNoteFormProvider({ const isBranchFeatureCan = featureCan(Features.Branches); // Handle fetch accounts data. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Handle fetch credit note data. const { data: creditNote, isLoading: isCreditNoteLoading } = useCreditNote( diff --git a/packages/webapp/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormProvider.tsx b/packages/webapp/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormProvider.tsx index 3aa09e323..8b1b2795d 100644 --- a/packages/webapp/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormProvider.tsx +++ b/packages/webapp/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormProvider.tsx @@ -5,11 +5,11 @@ import { pick } from 'lodash'; import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { - useAccounts, useVendorCredit, useBranches, useCreateRefundVendorCredit, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; const RefundVendorCreditContext = React.createContext(); @@ -24,7 +24,7 @@ function RefundVendorCreditFormProvider({ const isBranchFeatureCan = featureCan(Features.Branches); // Handle fetch accounts data. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Fetches the branches list. const { diff --git a/packages/webapp/src/containers/Drawers/AccountDrawer/AccountDrawerProvider.tsx b/packages/webapp/src/containers/Drawers/AccountDrawer/AccountDrawerProvider.tsx index 607a59bd4..1117b00ed 100644 --- a/packages/webapp/src/containers/Drawers/AccountDrawer/AccountDrawerProvider.tsx +++ b/packages/webapp/src/containers/Drawers/AccountDrawer/AccountDrawerProvider.tsx @@ -1,6 +1,6 @@ // @ts-nocheck import React from 'react'; -import { useAccount, useAccountTransactions } from '@/hooks/query'; +import { useAccountTrpc, useAccountTransactionsTrpc } from '@/hooks/trpc'; import { DrawerHeaderContent, DrawerLoading } from '@/components'; import { DRAWERS } from '@/constants/drawers'; @@ -11,13 +11,13 @@ const AccountDrawerContext = React.createContext(); */ function AccountDrawerProvider({ accountId, name, ...props }) { // Fetches the specific account details. - const { data: account, isLoading: isAccountLoading } = useAccount(accountId, { + const { data: account, isLoading: isAccountLoading } = useAccountTrpc(accountId, { enabled: !!accountId, }); // Load the specific account transactions. const { data: accounts, isLoading: isAccountsLoading } = - useAccountTransactions(accountId, { + useAccountTransactionsTrpc(accountId, {}, { enabled: !!accountId, }); diff --git a/packages/webapp/src/containers/Expenses/ExpenseForm/ExpenseFormPageProvider.tsx b/packages/webapp/src/containers/Expenses/ExpenseForm/ExpenseFormPageProvider.tsx index ef3abffb9..df9c9a4ec 100644 --- a/packages/webapp/src/containers/Expenses/ExpenseForm/ExpenseFormPageProvider.tsx +++ b/packages/webapp/src/containers/Expenses/ExpenseForm/ExpenseFormPageProvider.tsx @@ -8,11 +8,11 @@ import { useCurrencies, useCustomers, useExpense, - useAccounts, useBranches, useCreateExpense, useEditExpense, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { useProjects } from '@/containers/Projects/hooks'; const ExpenseFormPageContext = createContext(); @@ -47,7 +47,7 @@ function ExpenseFormPageProvider({ query, expenseId, ...props }) { } = useBranches(query, { enabled: isBranchFeatureCan }); // Fetch accounts list. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Fetch the projects list. const { diff --git a/packages/webapp/src/containers/FinancialStatements/GeneralLedger/GLHeaderGeneralPaneProvider.tsx b/packages/webapp/src/containers/FinancialStatements/GeneralLedger/GLHeaderGeneralPaneProvider.tsx index 44a7939af..a3f780c90 100644 --- a/packages/webapp/src/containers/FinancialStatements/GeneralLedger/GLHeaderGeneralPaneProvider.tsx +++ b/packages/webapp/src/containers/FinancialStatements/GeneralLedger/GLHeaderGeneralPaneProvider.tsx @@ -1,7 +1,7 @@ // @ts-nocheck import React, { createContext, useContext } from 'react'; -import { useAccounts } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { FinancialHeaderLoadingSkeleton } from '../FinancialHeaderLoadingSkeleton'; const GLHeaderGeneralPanelContext = createContext(); @@ -11,7 +11,7 @@ const GLHeaderGeneralPanelContext = createContext(); */ function GLHeaderGeneralPanelProvider({ ...props }) { // Accounts list. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Provider const provider = { diff --git a/packages/webapp/src/containers/Items/ItemFormProvider.tsx b/packages/webapp/src/containers/Items/ItemFormProvider.tsx index 9c4df7485..7452327bc 100644 --- a/packages/webapp/src/containers/Items/ItemFormProvider.tsx +++ b/packages/webapp/src/containers/Items/ItemFormProvider.tsx @@ -7,8 +7,8 @@ import { useItemsCategories, useCreateItem, useEditItem, - useAccounts, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { useWatchItemError } from './utils'; import { useTaxRates } from '@/hooks/query/taxRates'; @@ -23,7 +23,7 @@ function ItemFormProvider({ itemId, ...props }) { const duplicateId = state?.action; // Fetches the accounts list. - const { isLoading: isAccountsLoading, data: accounts } = useAccounts(); + const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc(); // Fetches the items categories list. const { diff --git a/packages/webapp/src/containers/Preferences/Accountant/AccountantFormProvider.tsx b/packages/webapp/src/containers/Preferences/Accountant/AccountantFormProvider.tsx index c9a08f117..4b4eadd5c 100644 --- a/packages/webapp/src/containers/Preferences/Accountant/AccountantFormProvider.tsx +++ b/packages/webapp/src/containers/Preferences/Accountant/AccountantFormProvider.tsx @@ -5,7 +5,8 @@ import styled from 'styled-components'; import { Card } from '@/components'; import { CLASSES } from '@/constants/classes'; -import { useAccounts, useSaveSettings, useSettings } from '@/hooks/query'; +import { useSaveSettings, useSettings } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import PreferencesPageLoader from '../PreferencesPageLoader'; const AccountantFormContext = React.createContext(); @@ -15,7 +16,7 @@ const AccountantFormContext = React.createContext(); */ function AccountantFormProvider({ ...props }) { // Fetches the accounts list. - const { isLoading: isAccountsLoading, data: accounts } = useAccounts(); + const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc(); // Fetches Organization Settings. const { isLoading: isSettingsLoading } = useSettings(); diff --git a/packages/webapp/src/containers/Preferences/Item/ItemPreferencesFormProvider.tsx b/packages/webapp/src/containers/Preferences/Item/ItemPreferencesFormProvider.tsx index 692d0d285..f5db36600 100644 --- a/packages/webapp/src/containers/Preferences/Item/ItemPreferencesFormProvider.tsx +++ b/packages/webapp/src/containers/Preferences/Item/ItemPreferencesFormProvider.tsx @@ -5,7 +5,8 @@ import styled from 'styled-components'; import { CLASSES } from '@/constants/classes'; import { Card } from '@/components'; -import { useSettingsItems, useAccounts, useSaveSettings } from '@/hooks/query'; +import { useSettingsItems, useSaveSettings } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import PreferencesPageLoader from '../PreferencesPageLoader'; const ItemFormContext = createContext(); @@ -16,7 +17,7 @@ const ItemFormContext = createContext(); function ItemPreferencesFormProvider({ ...props }) { // Fetches the accounts list. - const { isLoading: isAccountsLoading, data: accounts } = useAccounts(); + const { isLoading: isAccountsLoading, data: accounts } = useAccountsTrpc(); const { isLoading: isItemsSettingsLoading, diff --git a/packages/webapp/src/containers/Preferences/PaymentMethods/drawers/StripeIntegrationEditBoot.tsx b/packages/webapp/src/containers/Preferences/PaymentMethods/drawers/StripeIntegrationEditBoot.tsx index 2c1d2ff94..9c0902e1e 100644 --- a/packages/webapp/src/containers/Preferences/PaymentMethods/drawers/StripeIntegrationEditBoot.tsx +++ b/packages/webapp/src/containers/Preferences/PaymentMethods/drawers/StripeIntegrationEditBoot.tsx @@ -1,6 +1,6 @@ import React, { createContext, useContext } from 'react'; import { Spinner } from '@blueprintjs/core'; -import { useAccounts } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { useGetPaymentMethod } from '@/hooks/query/payment-services'; import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; @@ -40,7 +40,7 @@ export const StripeIntegrationEditBoot: React.FC< payload: { stripePaymentMethodId }, } = useDrawerContext(); - const { data: accounts, isLoading: isAccountsLoading } = useAccounts({}, {}); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); const { data: paymentMethod, isLoading: isPaymentMethodLoading } = useGetPaymentMethod(stripePaymentMethodId, { enabled: !!stripePaymentMethodId, diff --git a/packages/webapp/src/containers/Purchases/Bills/BillForm/BillFormProvider.tsx b/packages/webapp/src/containers/Purchases/Bills/BillForm/BillFormProvider.tsx index d960891f0..71143e573 100644 --- a/packages/webapp/src/containers/Purchases/Bills/BillForm/BillFormProvider.tsx +++ b/packages/webapp/src/containers/Purchases/Bills/BillForm/BillFormProvider.tsx @@ -5,7 +5,6 @@ import { useFeatureCan } from '@/hooks/state'; import { DashboardInsider } from '@/components/Dashboard'; import { useProjects } from '@/containers/Projects/hooks'; import { - useAccounts, useVendors, useItems, useBill, @@ -15,6 +14,7 @@ import { useCreateBill, useEditBill, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { useTaxRates } from '@/hooks/query/taxRates'; const BillFormContext = createContext(); @@ -48,7 +48,7 @@ function BillFormProvider({ billId, ...props }) { const isProjectsFeatureCan = featureCan(Features.Projects); // Handle fetch accounts. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Handle fetch vendors data table const { diff --git a/packages/webapp/src/containers/Purchases/PaymentsMade/PaymentForm/PaymentMadeFormProvider.tsx b/packages/webapp/src/containers/Purchases/PaymentsMade/PaymentForm/PaymentMadeFormProvider.tsx index 7a7e215f4..3cfd7c170 100644 --- a/packages/webapp/src/containers/Purchases/PaymentsMade/PaymentForm/PaymentMadeFormProvider.tsx +++ b/packages/webapp/src/containers/Purchases/PaymentsMade/PaymentForm/PaymentMadeFormProvider.tsx @@ -3,7 +3,6 @@ import React, { createContext, useContext, useState } from 'react'; import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { - useAccounts, useVendors, useItems, useBranches, @@ -12,6 +11,7 @@ import { useCreatePaymentMade, useEditPaymentMade, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { DashboardInsider } from '@/components'; // Payment made form context. @@ -29,7 +29,7 @@ function PaymentMadeFormProvider({ query, paymentMadeId, ...props }) { const isBranchFeatureCan = featureCan(Features.Branches); // Handle fetch accounts data. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Handle fetch Items data table or list. const { diff --git a/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveFormProvider.tsx b/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveFormProvider.tsx index 1a9f6fe12..57f1154b3 100644 --- a/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveFormProvider.tsx +++ b/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveFormProvider.tsx @@ -7,7 +7,6 @@ import { useProjects } from '@/containers/Projects/hooks'; import { useSettingsPaymentReceives, usePaymentReceiveEditPage, - useAccounts, useCustomers, useBranches, useCreatePaymentReceive, @@ -15,6 +14,7 @@ import { usePaymentReceivedState, PaymentReceivedStateResponse, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { useGetPdfTemplates } from '@/hooks/query/pdf-templates'; interface PaymentReceivedFormContextValue { @@ -52,7 +52,7 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) { const paymentEntriesEditPage = paymentReceivedEditData?.entries // Handle fetch accounts data. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Fetch payment made settings. const fetchSettings = useSettingsPaymentReceives(); diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormProvider.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormProvider.tsx index 8395084fa..72caa2157 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormProvider.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormProvider.tsx @@ -5,7 +5,6 @@ import { useFeatureCan } from '@/hooks/state'; import { DashboardInsider } from '@/components/Dashboard'; import { useReceipt, - useAccounts, useSettingsReceipts, useCustomers, useWarehouses, @@ -16,6 +15,7 @@ import { useGetReceiptState, IGetReceiptStateResponse, } from '@/hooks/query'; +import { useAccountsTrpc } from '@/hooks/trpc'; import { useProjects } from '@/containers/Projects/hooks'; import { useGetPdfTemplates } from '@/hooks/query/pdf-templates'; @@ -43,7 +43,7 @@ function ReceiptFormProvider({ receiptId, ...props }) { enabled: !!receiptId, }); // Fetch accounts list. - const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); + const { data: accounts, isLoading: isAccountsLoading } = useAccountsTrpc(); // Fetch customers list. const { diff --git a/packages/webapp/src/hooks/query/base.tsx b/packages/webapp/src/hooks/query/base.tsx index a3a2ccc66..b4d7382d4 100644 --- a/packages/webapp/src/hooks/query/base.tsx +++ b/packages/webapp/src/hooks/query/base.tsx @@ -1,4 +1,6 @@ // @ts-nocheck +import { QueryClient } from '@tanstack/react-query'; + // Query client config. export const queryConfig = { defaultOptions: { @@ -8,3 +10,6 @@ export const queryConfig = { }, }, }; + +// Create a new QueryClient instance for tRPC +export const tanstackQueryClient = new QueryClient(queryConfig); diff --git a/packages/webapp/src/hooks/trpc/index.ts b/packages/webapp/src/hooks/trpc/index.ts new file mode 100644 index 000000000..111ab42da --- /dev/null +++ b/packages/webapp/src/hooks/trpc/index.ts @@ -0,0 +1,2 @@ +// tRPC hooks for accounts module +export * from './useAccounts'; diff --git a/packages/webapp/src/hooks/trpc/useAccounts.ts b/packages/webapp/src/hooks/trpc/useAccounts.ts new file mode 100644 index 000000000..edac37796 --- /dev/null +++ b/packages/webapp/src/hooks/trpc/useAccounts.ts @@ -0,0 +1,188 @@ +import { trpc } from '@/trpc'; +import { useQueryClient } from '@tanstack/react-query'; + +// Query keys for cache invalidation +const accountQueryKeys = { + accounts: ['accounts'], + account: (id: number) => ['account', id], + accountTypes: ['accountTypes'], + accountTransactions: (id: number) => ['accountTransactions', id], + cashFlowAccounts: ['cashFlowAccounts'], + financialReport: ['financialReport'], +}; + +/** + * Retrieve accounts list using tRPC. + */ +export function useAccountsTrpc(query?: Record, options = {}) { + return trpc.accounts.getAccounts.useQuery(query || {}, { + select: (res) => res.accounts, + ...options, + }); +} + +/** + * Retrieve the given account details using tRPC. + */ +export function useAccountTrpc(id: number, options = {}) { + return trpc.accounts.getAccount.useQuery( + { id }, + { + enabled: !!id, + ...options, + } + ); +} + +/** + * Retrieve accounts types list using tRPC. + */ +export function useAccountsTypesTrpc(options = {}) { + return trpc.accounts.getAccountTypes.useQuery(undefined, options); +} + +/** + * Retrieve account transactions using tRPC. + */ +export function useAccountTransactionsTrpc( + accountId: number, + filters?: { fromDate?: string; toDate?: string }, + options = {} +) { + return trpc.accounts.getAccountTransactions.useQuery( + { + accountId, + fromDate: filters?.fromDate, + toDate: filters?.toDate, + }, + { + enabled: !!accountId, + ...options, + } + ); +} + +/** + * Creates account using tRPC. + */ +export function useCreateAccountTrpc(options = {}) { + const queryClient = useQueryClient(); + + return trpc.accounts.createAccount.useMutation({ + onSuccess: () => { + // Invalidate related queries + queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport }); + }, + ...options, + }); +} + +/** + * Edits the given account using tRPC. + */ +export function useEditAccountTrpc(options = {}) { + const queryClient = useQueryClient(); + + return trpc.accounts.editAccount.useMutation({ + onSuccess: (_, variables) => { + // Invalidate related queries + queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts }); + queryClient.invalidateQueries({ + queryKey: accountQueryKeys.account(variables.id), + }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport }); + }, + ...options, + }); +} + +/** + * Deletes the given account using tRPC. + */ +export function useDeleteAccountTrpc(options = {}) { + const queryClient = useQueryClient(); + + return trpc.accounts.deleteAccount.useMutation({ + onSuccess: () => { + // Invalidate related queries + queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport }); + }, + ...options, + }); +} + +/** + * Activates the given account using tRPC. + */ +export function useActivateAccountTrpc(options = {}) { + const queryClient = useQueryClient(); + + return trpc.accounts.activateAccount.useMutation({ + onSuccess: () => { + // Invalidate related queries + queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport }); + }, + ...options, + }); +} + +/** + * Inactivates the given account using tRPC. + */ +export function useInactivateAccountTrpc(options = {}) { + const queryClient = useQueryClient(); + + return trpc.accounts.inactivateAccount.useMutation({ + onSuccess: () => { + // Invalidate related queries + queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport }); + }, + ...options, + }); +} + +/** + * Validates which accounts can be deleted in bulk using tRPC. + */ +export function useValidateBulkDeleteAccountsTrpc(options = {}) { + return trpc.accounts.validateBulkDeleteAccounts.useMutation(options); +} + +/** + * Deletes multiple accounts in bulk using tRPC. + */ +export function useBulkDeleteAccountsTrpc(options = {}) { + const queryClient = useQueryClient(); + + return trpc.accounts.bulkDeleteAccounts.useMutation({ + onSuccess: () => { + // Invalidate related queries + queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.cashFlowAccounts }); + queryClient.invalidateQueries({ queryKey: accountQueryKeys.financialReport }); + }, + ...options, + }); +} + +/** + * Hook to refresh accounts list. + */ +export function useRefreshAccountsTrpc() { + const queryClient = useQueryClient(); + + return { + refresh: () => { + queryClient.invalidateQueries({ queryKey: accountQueryKeys.accounts }); + }, + }; +} diff --git a/packages/webapp/src/trpc.ts b/packages/webapp/src/trpc.ts new file mode 100644 index 000000000..e9e935295 --- /dev/null +++ b/packages/webapp/src/trpc.ts @@ -0,0 +1,42 @@ +import { createTRPCReact } from '@trpc/react-query'; +import { httpBatchLink } from '@trpc/client'; +import superjson from 'superjson'; +import { store } from '@/store/createStore'; +import { tanstackQueryClient } from '@/hooks/query/base'; + +// Define the AppRouter type - this will be imported from the server package +// For now, we use any until the server exports the proper types +export type AppRouter = any; + +export const trpc = createTRPCReact(); + +export function getAuthHeaders() { + const state = store.getState(); + const { token, organizationId } = state.authentication; + const headers: Record = {}; + + if (token) { + headers['x-access-token'] = token; + } + if (organizationId) { + headers['organization-id'] = organizationId.toString(); + } + headers['Accept-Language'] = 'en'; + + return headers; +} + +export const trpcClient = trpc.createClient({ + transformer: superjson, + links: [ + httpBatchLink({ + url: '/api/trpc', + headers() { + return getAuthHeaders(); + }, + }), + ], +}); + +// Export the QueryClient for use in the TRPCProvider +export const queryClient = tanstackQueryClient; diff --git a/shared/bigcapital-utils/package.json b/shared/bigcapital-utils/package.json index 4e7df9315..71195c5b1 100644 --- a/shared/bigcapital-utils/package.json +++ b/shared/bigcapital-utils/package.json @@ -19,5 +19,8 @@ "dev": "npm run build -- --watch" }, "author": "", - "license": "ISC" + "license": "ISC", + "dependencies": { + "@trpc/server": "^11.10.0" + } } diff --git a/shared/bigcapital-utils/src/index.ts b/shared/bigcapital-utils/src/index.ts index e8c2e449f..60c94dfbd 100644 --- a/shared/bigcapital-utils/src/index.ts +++ b/shared/bigcapital-utils/src/index.ts @@ -1,3 +1,4 @@ export * from './countries'; +export * from './trpc'; export const test = () => {}; \ No newline at end of file diff --git a/shared/bigcapital-utils/src/trpc.ts b/shared/bigcapital-utils/src/trpc.ts new file mode 100644 index 000000000..c98a3a8b3 --- /dev/null +++ b/shared/bigcapital-utils/src/trpc.ts @@ -0,0 +1,32 @@ +/** + * tRPC Router Types + * This file exports the types for the tRPC router that are shared between + * the server and the webapp (frontend). + * + * Note: This file only contains TYPE definitions. It does not import any + * runtime code from the server to avoid bundle bloat in the webapp. + */ + +// We define the router type structure here to avoid importing the actual router +// from the server package, which would cause issues with bundling. + +import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'; + +// This is a placeholder type that will be replaced by the actual router type +// when the server builds and exports it. +// The webapp will import the actual type from the server package during development +// but will use the built type declaration during production. + +export type AppRouter = any; + +/** + * Inference helpers for input types + * @example type MyInput = RouterInputs['accounts']['getAccount'] + */ +export type RouterInputs = inferRouterInputs; + +/** + * Inference helpers for output types + * @example type MyOutput = RouterOutputs['accounts']['getAccount'] + */ +export type RouterOutputs = inferRouterOutputs;