From ed157f6f8a57b3cffd81ef7d6bfd94ab0f97e923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Tue, 11 Aug 2026 15:26:10 +0200 Subject: [PATCH] refactor(frontend): call backend routes through Wayfinder instead of literal URLs (#772) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the Inertia v3 upgrade (#769). While auditing whether the frontend used Wayfinder's current API, I found **32 HTTP call sites across 16 files that built their URL by hand** — every one of which already had a generated Wayfinder action sitting unused. ## Why this matters Wayfinder exists so a route rename fails at **compile** time. A hand-written `'/api/transactions/bulk'` fails at **runtime**, in production, on a path a unit test can't catch because the tests mock `axios`. The worst offender was `services/transaction-sync.ts` — the offline sync service — which had five of them. I verified each URL against `php artisan route:list` before changing it; there were no missing routes, only unused generated ones. ## What changed | File | Sites | Now uses | | --- | --- | --- | | `services/transaction-sync.ts` | 6 | `TransactionController` + `Sync/TransactionSyncController` | | `components/transactions/saved-filters.tsx` | 4 | `Api/SavedFilterController` | | `hooks/use-cashflow-data.ts` | 5 | `Api/CashflowAnalyticsController` | | `components/transactions/import-transactions-drawer.tsx` | 3 | `TransactionController@categorize` | | `components/accounts/account-balance-chart.tsx` | 2 | `Api/DashboardAnalyticsController` | | `hooks/use-decrypt-account-names.ts` | 2 | `Api/AccountController` | | `components/dashboard/net-worth-chart.tsx` | 2 | the two net-worth preference controllers | | `pages/transactions/index.tsx` | 2 | `TransactionController@bulkUpdate` | | `lib/import-config-storage.ts` | 2 | `Api/AccountImportConfigController` | | `app.tsx`, `use-decrypt-transactions.ts`, `encryption-key-context.tsx`, `import-step-preview.tsx`, `import-transactions-button.tsx`, `category-analysis-drawer.tsx`, `settings/appearance.tsx` | 1 each | respective controllers | ## Query strings got simpler The endpoints with parameters were assembling `URLSearchParams` by hand. The generated `.url({ query })` helper does it, so that scaffolding is gone: ```diff -const periodParams = new URLSearchParams({ from: fromStr, to: toStr }); -const periodQuery = `?${periodParams.toString()}`; -fetch(`/api/cashflow/breakdown${periodQuery}&type=income`) +const periodQuery = { from: fromStr, to: toStr }; +fetch(cashflowBreakdown.url({ query: { ...periodQuery, type: 'income' } })) ``` `lib/import-config-storage.ts` also loses its local `configUrl()` helper, which only existed to interpolate an account id. ## Two aliases, on purpose `transaction-sync.ts` and `import-transactions-button.tsx` import with `as` aliases because the plain names collide with a method (`update`, `store`, `destroy`) and a `useState` variable (`importData`) already in those files. Named imports are kept everywhere so tree-shaking still works. ## Verification - `bun run test` — **356/356, with zero test changes.** That is the useful signal here: `transaction-sync.test.ts` asserts `axios.delete` was called with the literal `'/transactions/txn-1'`, and it still passes, so the generated URLs are byte-identical to the strings they replaced. - `bun run types` — no new errors. (`transaction-sync.ts:45` and the `.test.tsx` matcher errors are pre-existing; the former just shifted line number as imports were added.) - `bun run build`, `bun run lint`, `bun run format`, `bun run dry` — green. ### Browser check Tests mock `axios`, so a wrong URL would still pass them. I exercised the rewritten endpoints in a real browser and captured the actual network traffic — all **200**, query strings identical to what the manual code produced: ``` 200 /api/cashflow/summary?from=2026-08-01&to=2026-08-31 200 /api/cashflow/trend?months=12&to=2026-08-31 200 /api/cashflow/breakdown?from=2026-08-01&to=2026-08-31&type=income 200 /api/cashflow/breakdown?from=2026-08-01&to=2026-08-31&type=expense 200 /api/dashboard/account/{id}/balance-evolution?from=2025-08-11&to=2026-08-11 200 /api/saved-filters ``` 0 failed requests, 0 console errors across cashflow, transactions, accounts, account detail and appearance. ## Out of scope 11 hardcoded **navigation** URLs remain (`href="/register"`, `href="/privacy"`, `router.visit('/dashboard')`), mostly on the marketing pages. They are static routes with a much lower rename risk, so I left them for a separate pass rather than widen this diff. --- resources/js/app.tsx | 3 +- .../accounts/account-balance-chart.tsx | 14 +++++--- .../categories/category-analysis-drawer.tsx | 3 +- .../components/dashboard/net-worth-chart.tsx | 6 ++-- .../transactions/import-step-preview.tsx | 9 +++-- .../import-transactions-button.tsx | 3 +- .../import-transactions-drawer.tsx | 10 +++--- .../components/transactions/saved-filters.tsx | 14 +++++--- .../js/contexts/encryption-key-context.tsx | 3 +- resources/js/hooks/use-cashflow-data.ts | 34 ++++++++++++------- .../js/hooks/use-decrypt-account-names.ts | 11 ++++-- .../js/hooks/use-decrypt-transactions.ts | 3 +- resources/js/lib/import-config-storage.ts | 13 ++++--- resources/js/pages/settings/appearance.tsx | 3 +- resources/js/pages/transactions/index.tsx | 5 +-- resources/js/services/transaction-sync.ts | 20 +++++++---- 16 files changed, 98 insertions(+), 56 deletions(-) diff --git a/resources/js/app.tsx b/resources/js/app.tsx index 5b9bf481..fb2b4049 100644 --- a/resources/js/app.tsx +++ b/resources/js/app.tsx @@ -18,6 +18,7 @@ import { import { StrictMode, useEffect, useState } from 'react'; import { createRoot } from 'react-dom/client'; import { toast, Toaster } from 'sonner'; +import { update as updateTimezone } from './actions/App/Http/Controllers/Settings/TimezoneController'; import { AppErrorBoundary } from './components/app-error-boundary'; import { EncryptionKeyProvider } from './contexts/encryption-key-context'; import { PrivacyModeProvider } from './contexts/privacy-mode-context'; @@ -214,7 +215,7 @@ createInertiaApp({ hasAttemptedTimezoneBackfill = true; try { - await axios.patch('/settings/timezone', { + await axios.patch(updateTimezone.url(), { timezone: detectedTimezone, }); } catch { diff --git a/resources/js/components/accounts/account-balance-chart.tsx b/resources/js/components/accounts/account-balance-chart.tsx index 7d582c44..6a9bc8f9 100644 --- a/resources/js/components/accounts/account-balance-chart.tsx +++ b/resources/js/components/accounts/account-balance-chart.tsx @@ -1,3 +1,7 @@ +import { + accountBalanceEvolution, + accountDailyBalanceEvolution, +} from '@/actions/App/Http/Controllers/Api/DashboardAnalyticsController'; import { AccountName } from '@/components/accounts/account-name'; import { type ChartCurrencyMode, @@ -378,9 +382,10 @@ export function AccountBalanceChart({ if (currentGranularity === 'daily') { // Fetch DAILY_DAYS + 1 days (extra day for DoD baseline) const from = format(subDays(now, DAILY_DAYS), 'yyyy-MM-dd'); - const params = new URLSearchParams({ from, to }); const response = await fetch( - `/api/dashboard/account/${account.id}/daily-balance-evolution?${params.toString()}`, + accountDailyBalanceEvolution.url(account.id, { + query: { from, to }, + }), ); const data: AccountDailyBalanceData = await response.json(); // Normalize daily data so the rest of the component works uniformly @@ -391,9 +396,10 @@ export function AccountBalanceChart({ }); } else { const from = format(subMonths(now, 12), 'yyyy-MM-dd'); - const params = new URLSearchParams({ from, to }); const response = await fetch( - `/api/dashboard/account/${account.id}/balance-evolution?${params.toString()}`, + accountBalanceEvolution.url(account.id, { + query: { from, to }, + }), ); const data = await response.json(); setBalanceData(data); diff --git a/resources/js/components/categories/category-analysis-drawer.tsx b/resources/js/components/categories/category-analysis-drawer.tsx index 503b6a24..dbc1656f 100644 --- a/resources/js/components/categories/category-analysis-drawer.tsx +++ b/resources/js/components/categories/category-analysis-drawer.tsx @@ -1,3 +1,4 @@ +import monthlyBreakdown from '@/actions/App/Http/Controllers/Api/CategoryMonthlyBreakdownController'; import { CategoryCombobox } from '@/components/shared/category-combobox'; import { AmountDisplay } from '@/components/ui/amount-display'; import { Card, CardContent } from '@/components/ui/card'; @@ -130,7 +131,7 @@ export function CategoryAnalysisDrawer({ setIsLoading(true); setError(null); - fetch(`/api/categories/${categoryId}/monthly-breakdown`, { + fetch(monthlyBreakdown.url(categoryId), { headers: { Accept: 'application/json' }, }) .then((response) => { diff --git a/resources/js/components/dashboard/net-worth-chart.tsx b/resources/js/components/dashboard/net-worth-chart.tsx index ab4c25e1..1352ddc3 100644 --- a/resources/js/components/dashboard/net-worth-chart.tsx +++ b/resources/js/components/dashboard/net-worth-chart.tsx @@ -1,3 +1,5 @@ +import { update as updateLoanPreference } from '@/actions/App/Http/Controllers/Settings/NetWorthChartLoanPreferenceController'; +import { update as updateRealEstatePreference } from '@/actions/App/Http/Controllers/Settings/NetWorthChartRealEstatePreferenceController'; import { AccountName } from '@/components/accounts/account-name'; import { type ChartGranularity, @@ -439,7 +441,7 @@ export function NetWorthChart({ const handleIncludeLoansChange = useCallback((includeLoans: boolean) => { router.patch( - '/settings/net-worth-chart-loan-preference', + updateLoanPreference.url(), { include_loans_in_net_worth_chart: includeLoans, }, @@ -454,7 +456,7 @@ export function NetWorthChart({ const handleIncludeRealEstateChange = useCallback( (includeRealEstate: boolean) => { router.patch( - '/settings/net-worth-chart-real-estate-preference', + updateRealEstatePreference.url(), { include_real_estate_in_net_worth_chart: includeRealEstate, }, diff --git a/resources/js/components/transactions/import-step-preview.tsx b/resources/js/components/transactions/import-step-preview.tsx index 818b0b2f..8ba32c97 100644 --- a/resources/js/components/transactions/import-step-preview.tsx +++ b/resources/js/components/transactions/import-step-preview.tsx @@ -1,3 +1,4 @@ +import { index as transactionsIndex } from '@/actions/App/Http/Controllers/Api/TransactionController'; import { TransactionDescription } from '@/components/transactions/transaction-description'; import { AmountDisplay } from '@/components/ui/amount-display'; import { Badge } from '@/components/ui/badge'; @@ -58,9 +59,11 @@ export function ImportStepPreview({ } axios - .get('/api/transactions', { - params: { account_id: accountId, per_page: 10 }, - }) + .get( + transactionsIndex.url({ + query: { account_id: accountId, per_page: 10 }, + }), + ) .then((response) => { setExistingTransactions(response.data.data ?? []); }) diff --git a/resources/js/components/transactions/import-transactions-button.tsx b/resources/js/components/transactions/import-transactions-button.tsx index baa399bd..bebb013e 100644 --- a/resources/js/components/transactions/import-transactions-button.tsx +++ b/resources/js/components/transactions/import-transactions-button.tsx @@ -1,3 +1,4 @@ +import { index as importDataRoute } from '@/actions/App/Http/Controllers/Api/ImportDataController'; import { Button } from '@/components/ui/button'; import { Tooltip, @@ -30,7 +31,7 @@ export function ImportTransactionsButton() { // Fetch data on-demand when drawer opens setLoading(true); try { - const response = await fetch('/api/import/data'); + const response = await fetch(importDataRoute.url()); if (!response.ok) { throw new Error('Failed to load import data'); } diff --git a/resources/js/components/transactions/import-transactions-drawer.tsx b/resources/js/components/transactions/import-transactions-drawer.tsx index 18ceae2f..4713d3b7 100644 --- a/resources/js/components/transactions/import-transactions-drawer.tsx +++ b/resources/js/components/transactions/import-transactions-drawer.tsx @@ -2,6 +2,7 @@ import { index as indexBalances, store as storeBalance, } from '@/actions/App/Http/Controllers/AccountBalanceController'; +import { categorize } from '@/actions/App/Http/Controllers/TransactionController'; import AlertError from '@/components/alert-error'; import { Drawer, @@ -687,8 +688,7 @@ export function ImportTransactionsDrawer({ uncategorizedCount > 0 ? { label: 'Categorize', - onClick: () => - router.visit('/transactions/categorize'), + onClick: () => router.visit(categorize.url()), } : undefined, }); @@ -703,8 +703,7 @@ export function ImportTransactionsDrawer({ uncategorizedCount > 0 ? { label: 'Categorize', - onClick: () => - router.visit('/transactions/categorize'), + onClick: () => router.visit(categorize.url()), } : undefined, }); @@ -718,8 +717,7 @@ export function ImportTransactionsDrawer({ uncategorizedCount > 0 ? { label: 'Categorize', - onClick: () => - router.visit('/transactions/categorize'), + onClick: () => router.visit(categorize.url()), } : undefined, }); diff --git a/resources/js/components/transactions/saved-filters.tsx b/resources/js/components/transactions/saved-filters.tsx index e73833ba..ff73d16a 100644 --- a/resources/js/components/transactions/saved-filters.tsx +++ b/resources/js/components/transactions/saved-filters.tsx @@ -1,3 +1,9 @@ +import { + destroy as destroySavedFilter, + index as savedFiltersIndex, + store as storeSavedFilter, + update as updateSavedFilter, +} from '@/actions/App/Http/Controllers/Api/SavedFilterController'; import { AlertDialog, AlertDialogAction, @@ -113,7 +119,7 @@ export function SavedFilters({ filters, onLoad }: SavedFiltersProps) { let active = true; axios - .get<{ data: SavedFilter[] }>('/api/saved-filters') + .get<{ data: SavedFilter[] }>(savedFiltersIndex.url()) .then((response) => { if (active) { setSavedFilters(response.data.data); @@ -143,7 +149,7 @@ export function SavedFilters({ filters, onLoad }: SavedFiltersProps) { } try { - await axios.delete(`/api/saved-filters/${savedFilter.id}`); + await axios.delete(destroySavedFilter.url(savedFilter.id)); } catch (error) { console.error('Failed to delete saved filter:', error); setSavedFilters(previous); @@ -154,7 +160,7 @@ export function SavedFilters({ filters, onLoad }: SavedFiltersProps) { async function handleUpdate(savedFilter: SavedFilter) { try { const response = await axios.patch<{ data: SavedFilter }>( - `/api/saved-filters/${savedFilter.id}`, + updateSavedFilter.url(savedFilter.id), { filters: serializeFilters(filters) }, ); @@ -180,7 +186,7 @@ export function SavedFilters({ filters, onLoad }: SavedFiltersProps) { setIsSaving(true); try { const response = await axios.post<{ data: SavedFilter }>( - '/api/saved-filters', + storeSavedFilter.url(), { name: trimmedName, filters: serializeFilters(filters), diff --git a/resources/js/contexts/encryption-key-context.tsx b/resources/js/contexts/encryption-key-context.tsx index fdd430d9..126f9a5e 100644 --- a/resources/js/contexts/encryption-key-context.tsx +++ b/resources/js/contexts/encryption-key-context.tsx @@ -1,3 +1,4 @@ +import { getMessage } from '@/actions/App/Http/Controllers/EncryptionController'; import { clearKey, getStoredKey } from '@/lib/key-storage'; import axios from 'axios'; import { @@ -56,7 +57,7 @@ export function EncryptionKeyProvider({ try { const response = await axios.get( - '/api/encryption/message', + getMessage.url(), ); setEncryptedMessageData(response.data); } catch (err) { diff --git a/resources/js/hooks/use-cashflow-data.ts b/resources/js/hooks/use-cashflow-data.ts index 69fcb745..0955809d 100644 --- a/resources/js/hooks/use-cashflow-data.ts +++ b/resources/js/hooks/use-cashflow-data.ts @@ -1,3 +1,9 @@ +import { + breakdown as cashflowBreakdown, + sankey as cashflowSankey, + summary as cashflowSummary, + trend as cashflowTrend, +} from '@/actions/App/Http/Controllers/Api/CashflowAnalyticsController'; import { Category } from '@/types/category'; import { endOfMonth, format, startOfMonth } from 'date-fns'; import { useCallback, useEffect, useState } from 'react'; @@ -109,30 +115,32 @@ export function useCashflowData({ const fromStr = format(from, 'yyyy-MM-dd'); const toStr = format(to, 'yyyy-MM-dd'); - const periodParams = new URLSearchParams({ - from: fromStr, - to: toStr, - }); - const periodQuery = `?${periodParams.toString()}`; + const periodQuery = { from: fromStr, to: toStr }; const trendQuery = - periodType === 'month' ? `?months=12&to=${toStr}` : periodQuery; + periodType === 'month' + ? { months: 12, to: toStr } + : periodQuery; const [summary, sankey, trend, incomeBreakdown, expenseBreakdown] = await Promise.all([ - fetch(`/api/cashflow/summary${periodQuery}`).then((r) => - r.json(), + fetch(cashflowSummary.url({ query: periodQuery })).then( + (r) => r.json(), ), - fetch(`/api/cashflow/sankey${periodQuery}`).then((r) => - r.json(), + fetch(cashflowSankey.url({ query: periodQuery })).then( + (r) => r.json(), ), - fetch(`/api/cashflow/trend${trendQuery}`).then((r) => + fetch(cashflowTrend.url({ query: trendQuery })).then((r) => r.json(), ), fetch( - `/api/cashflow/breakdown${periodQuery}&type=income`, + cashflowBreakdown.url({ + query: { ...periodQuery, type: 'income' }, + }), ).then((r) => r.json()), fetch( - `/api/cashflow/breakdown${periodQuery}&type=expense`, + cashflowBreakdown.url({ + query: { ...periodQuery, type: 'expense' }, + }), ).then((r) => r.json()), ]); diff --git a/resources/js/hooks/use-decrypt-account-names.ts b/resources/js/hooks/use-decrypt-account-names.ts index fe837712..7884bdb3 100644 --- a/resources/js/hooks/use-decrypt-account-names.ts +++ b/resources/js/hooks/use-decrypt-account-names.ts @@ -1,3 +1,7 @@ +import { + index as accountsIndex, + update as updateAccount, +} from '@/actions/App/Http/Controllers/Api/AccountController'; import { useEncryptionKey } from '@/contexts/encryption-key-context'; import { decrypt, importKey } from '@/lib/crypto'; import { getStoredKey } from '@/lib/key-storage'; @@ -32,8 +36,9 @@ export function useDecryptAccountNames() { return; } - const { data: accounts } = - await axios.get('/api/accounts'); + const { data: accounts } = await axios.get( + accountsIndex.url(), + ); const encryptedAccounts = accounts.filter( (a) => a.encrypted && a.name_iv, @@ -53,7 +58,7 @@ export function useDecryptAccountNames() { account.name_iv!, ); - await axios.put(`/api/accounts/${account.id}`, { + await axios.put(updateAccount.url(account.id), { name: decryptedName, encrypted: false, }); diff --git a/resources/js/hooks/use-decrypt-transactions.ts b/resources/js/hooks/use-decrypt-transactions.ts index c6b5809c..07ee4504 100644 --- a/resources/js/hooks/use-decrypt-transactions.ts +++ b/resources/js/hooks/use-decrypt-transactions.ts @@ -1,3 +1,4 @@ +import { bulkUpdate } from '@/actions/App/Http/Controllers/Api/TransactionController'; import { useEncryptionKey } from '@/contexts/encryption-key-context'; import { decrypt, importKey } from '@/lib/crypto'; import { getStoredKey } from '@/lib/key-storage'; @@ -127,7 +128,7 @@ export function useDecryptTransactions() { for (let i = 0; i < batch.length; i += 50) { const chunk = batch.slice(i, i + 50); await withRetry(() => - axios.patch('/api/transactions/bulk', { + axios.patch(bulkUpdate.url(), { transactions: chunk, }), ); diff --git a/resources/js/lib/import-config-storage.ts b/resources/js/lib/import-config-storage.ts index 57848a5d..8f52fde6 100644 --- a/resources/js/lib/import-config-storage.ts +++ b/resources/js/lib/import-config-storage.ts @@ -1,3 +1,7 @@ +import { + show as showImportConfig, + update as updateImportConfig, +} from '@/actions/App/Http/Controllers/Api/AccountImportConfigController'; import type { BalanceColumnMapping } from '@/types/balance-import'; import { type ColumnMapping, DateFormat } from '@/types/import'; import { type UUID } from '@/types/uuid'; @@ -15,17 +19,13 @@ interface BalanceImportConfig { type ImportConfigType = 'transaction' | 'balance'; -function configUrl(accountId: UUID): string { - return `/api/accounts/${accountId}/import-config`; -} - async function saveConfig( accountId: UUID, type: ImportConfigType, config: ImportConfig | BalanceImportConfig, ): Promise { try { - await axios.put(configUrl(accountId), { type, config }); + await axios.put(updateImportConfig.url(accountId), { type, config }); } catch (error) { console.error(`Failed to save ${type} import configuration:`, error); } @@ -37,8 +37,7 @@ async function loadConfig( ): Promise { try { const { data } = await axios.get<{ data: T | null }>( - configUrl(accountId), - { params: { type } }, + showImportConfig.url(accountId, { query: { type } }), ); const config = data.data; diff --git a/resources/js/pages/settings/appearance.tsx b/resources/js/pages/settings/appearance.tsx index 18d9f78e..c6a7e292 100644 --- a/resources/js/pages/settings/appearance.tsx +++ b/resources/js/pages/settings/appearance.tsx @@ -1,3 +1,4 @@ +import { update as updateChartColorScheme } from '@/actions/App/Http/Controllers/Settings/ChartColorSchemeController'; import { __ } from '@/utils/i18n'; import { Head, router } from '@inertiajs/react'; @@ -38,7 +39,7 @@ export default function Appearance() { updateScheme(newScheme); router.patch( - '/settings/chart-color-scheme', + updateChartColorScheme.url(), { chart_color_scheme: newScheme }, { preserveScroll: true }, ); diff --git a/resources/js/pages/transactions/index.tsx b/resources/js/pages/transactions/index.tsx index 069266f7..584f6f8d 100644 --- a/resources/js/pages/transactions/index.tsx +++ b/resources/js/pages/transactions/index.tsx @@ -1,3 +1,4 @@ +import { bulkUpdate as bulkUpdateTransactions } from '@/actions/App/Http/Controllers/TransactionController'; import { useLocale } from '@/hooks/use-locale'; import { usePollJobStatus } from '@/hooks/use-poll-job-status'; import { __ } from '@/utils/i18n'; @@ -1121,7 +1122,7 @@ export default function Transactions({ if (isSelectingAll) { const toastId = toast.loading(__('Updating transactions...')); const response = await axios.patch<{ count: number }>( - '/transactions/bulk', + bulkUpdateTransactions.url(), { filters: clientFiltersToBackendFilters(filters), category_id: categoryId, @@ -1262,7 +1263,7 @@ export default function Transactions({ if (isSelectingAll) { const toastId = toast.loading(__('Updating transactions...')); const response = await axios.patch<{ count: number }>( - '/transactions/bulk', + bulkUpdateTransactions.url(), { filters: clientFiltersToBackendFilters(filters), label_ids: labelIds, diff --git a/resources/js/services/transaction-sync.ts b/resources/js/services/transaction-sync.ts index 3d6b2653..cd82ea54 100644 --- a/resources/js/services/transaction-sync.ts +++ b/resources/js/services/transaction-sync.ts @@ -1,3 +1,11 @@ +// Aliased because this service's own methods share these names. +import { index as syncTransactions } from '@/actions/App/Http/Controllers/Sync/TransactionSyncController'; +import { + bulkUpdate as bulkUpdateRoute, + destroy as destroyRoute, + store as storeRoute, + update as updateRoute, +} from '@/actions/App/Http/Controllers/TransactionController'; import { db, withDb } from '@/lib/dexie-db'; import { TransactionSyncManager } from '@/lib/sync-manager'; import type { LearnedRuleNotice } from '@/types/automation-rule'; @@ -32,7 +40,7 @@ class TransactionSyncService { constructor() { this.syncManager = new TransactionSyncManager({ - endpoint: '/api/sync/transactions', + endpoint: syncTransactions.url(), transformFromServer: (data) => { const label_ids = data.labels?.map((l: { id: string }) => l.id); // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -69,7 +77,7 @@ class TransactionSyncService { data: Omit, options?: { updateBalance?: boolean }, ): Promise { - const response = await axios.post('/transactions', { + const response = await axios.post(storeRoute.url(), { ...data, ...(options?.updateBalance ? { update_balance: true } : {}), }); @@ -106,7 +114,7 @@ class TransactionSyncService { ): Promise { const { label_ids, ...transactionData } = data; - const response = await axios.patch(`/transactions/${id}`, { + const response = await axios.patch(updateRoute.url(id), { ...transactionData, label_ids, ...(options?.updateBalance ? { update_balance: true } : {}), @@ -134,7 +142,7 @@ class TransactionSyncService { ): Promise { const { label_ids, ...transactionData } = data; - await axios.patch('/transactions/bulk', { + await axios.patch(bulkUpdateRoute.url(), { transaction_ids: ids, label_ids: label_ids, ...transactionData, @@ -178,7 +186,7 @@ class TransactionSyncService { requestFilters.debtor_name = filters.debtorName; } - const response = await axios.patch('/transactions/bulk', { + const response = await axios.patch(bulkUpdateRoute.url(), { filters: requestFilters, label_ids: label_ids, ...transactionData, @@ -191,7 +199,7 @@ class TransactionSyncService { id: string, options?: { updateBalance?: boolean }, ): Promise { - await axios.delete(`/transactions/${id}`, { + await axios.delete(destroyRoute.url(id), { data: options?.updateBalance ? { update_balance: true } : undefined, }); // The API delete above is authoritative; the local cache eviction is