refactor(frontend): call backend routes through Wayfinder instead of literal URLs (#772)
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.
This commit is contained in:
parent
fa6f6e2be3
commit
ed157f6f8a
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 ?? []);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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<EncryptedMessageData>(
|
||||
'/api/encryption/message',
|
||||
getMessage.url(),
|
||||
);
|
||||
setEncryptedMessageData(response.data);
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<EncryptedAccount[]>('/api/accounts');
|
||||
const { data: accounts } = await axios.get<EncryptedAccount[]>(
|
||||
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,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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<T extends ImportConfig | BalanceImportConfig>(
|
|||
): Promise<T | null> {
|
||||
try {
|
||||
const { data } = await axios.get<{ data: T | null }>(
|
||||
configUrl(accountId),
|
||||
{ params: { type } },
|
||||
showImportConfig.url(accountId, { query: { type } }),
|
||||
);
|
||||
|
||||
const config = data.data;
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<Transaction, 'id' | 'created_at' | 'updated_at'>,
|
||||
options?: { updateBalance?: boolean },
|
||||
): Promise<Transaction> {
|
||||
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<UpdatedTransaction> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue