fix: fix URL params when switch to balance from transactions

This commit is contained in:
javayhu 2025-08-28 23:23:52 +08:00
parent 21eee041ab
commit fa2e981c16
2 changed files with 83 additions and 42 deletions

View File

@ -4,34 +4,33 @@ import { CreditTransactionsTable } from '@/components/settings/credits/credit-tr
import { useCreditTransactions } from '@/hooks/use-credits'; import { useCreditTransactions } from '@/hooks/use-credits';
import type { SortingState } from '@tanstack/react-table'; import type { SortingState } from '@tanstack/react-table';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import {
parseAsIndex, interface CreditTransactionsProps {
parseAsInteger, page: number;
parseAsString, pageSize: number;
useQueryStates, search: string;
} from 'nuqs'; sorting: SortingState;
import { useMemo } from 'react'; onPageChange: (pageIndex: number) => void;
onPageSizeChange: (pageSize: number) => void;
onSearch: (search: string) => void;
onSortingChange: (sorting: SortingState) => void;
}
/** /**
* Credit transactions component * Credit transactions component
*/ */
export function CreditTransactions() { export function CreditTransactions({
page,
pageSize,
search,
sorting,
onPageChange,
onPageSizeChange,
onSearch,
onSortingChange,
}: CreditTransactionsProps) {
const t = useTranslations('Dashboard.settings.credits.transactions'); const t = useTranslations('Dashboard.settings.credits.transactions');
const [{ page, pageSize, search, sortId, sortDesc }, setQueryStates] =
useQueryStates({
page: parseAsIndex.withDefault(0), // 0-based internally, 1-based in URL
pageSize: parseAsInteger.withDefault(10),
search: parseAsString.withDefault(''),
sortId: parseAsString.withDefault('createdAt'),
sortDesc: parseAsInteger.withDefault(1),
});
const sorting: SortingState = useMemo(
() => [{ id: sortId, desc: Boolean(sortDesc) }],
[sortId, sortDesc]
);
const { data, isLoading } = useCreditTransactions( const { data, isLoading } = useCreditTransactions(
page, page,
pageSize, pageSize,
@ -48,19 +47,10 @@ export function CreditTransactions() {
search={search} search={search}
sorting={sorting} sorting={sorting}
loading={isLoading} loading={isLoading}
onSearch={(newSearch) => setQueryStates({ search: newSearch, page: 0 })} onSearch={onSearch}
onPageChange={(newPageIndex) => setQueryStates({ page: newPageIndex })} onPageChange={onPageChange}
onPageSizeChange={(newPageSize) => onPageSizeChange={onPageSizeChange}
setQueryStates({ pageSize: newPageSize, page: 0 }) onSortingChange={onSortingChange}
}
onSortingChange={(newSorting) => {
if (newSorting.length > 0) {
setQueryStates({
sortId: newSorting[0].id,
sortDesc: newSorting[0].desc ? 1 : 0,
});
}
}}
/> />
); );
} }

View File

@ -5,7 +5,15 @@ import { CreditTransactions } from '@/components/settings/credits/credit-transac
import CreditsBalanceCard from '@/components/settings/credits/credits-balance-card'; import CreditsBalanceCard from '@/components/settings/credits/credits-balance-card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { parseAsStringLiteral, useQueryState } from 'nuqs'; import {
parseAsIndex,
parseAsInteger,
parseAsString,
parseAsStringLiteral,
useQueryStates,
} from 'nuqs';
import { useMemo } from 'react';
import type { SortingState } from '@tanstack/react-table';
/** /**
* Credits page client, show credit balance and transactions * Credits page client, show credit balance and transactions
@ -13,21 +21,46 @@ import { parseAsStringLiteral, useQueryState } from 'nuqs';
export default function CreditsPageClient() { export default function CreditsPageClient() {
const t = useTranslations('Dashboard.settings.credits'); const t = useTranslations('Dashboard.settings.credits');
const [activeTab, setActiveTab] = useQueryState( // Manage all URL states in the parent component
'tab', const [{ tab, page, pageSize, search, sortId, sortDesc }, setQueryStates] =
parseAsStringLiteral(['balance', 'transactions']).withDefault('balance') useQueryStates({
tab: parseAsStringLiteral(['balance', 'transactions']).withDefault('balance'),
// Transaction-specific parameters
page: parseAsIndex.withDefault(0),
pageSize: parseAsInteger.withDefault(10),
search: parseAsString.withDefault(''),
sortId: parseAsString.withDefault('createdAt'),
sortDesc: parseAsInteger.withDefault(1),
});
const sorting: SortingState = useMemo(
() => [{ id: sortId, desc: Boolean(sortDesc) }],
[sortId, sortDesc]
); );
const handleTabChange = (value: string) => { const handleTabChange = (value: string) => {
if (value === 'balance' || value === 'transactions') { if (value === 'balance' || value === 'transactions') {
setActiveTab(value); if (value === 'balance') {
// When switching to balance tab, clear transaction parameters
setQueryStates({
tab: value,
page: null,
pageSize: null,
search: null,
sortId: null,
sortDesc: null,
});
} else {
// When switching to transactions tab, just set the tab
setQueryStates({ tab: value });
}
} }
}; };
return ( return (
<div className="flex flex-col gap-8"> <div className="flex flex-col gap-8">
<Tabs <Tabs
value={activeTab} value={tab}
onValueChange={handleTabChange} onValueChange={handleTabChange}
className="w-full" className="w-full"
> >
@ -52,7 +85,25 @@ export default function CreditsPageClient() {
<TabsContent value="transactions" className="mt-4"> <TabsContent value="transactions" className="mt-4">
{/* Credit Transactions */} {/* Credit Transactions */}
<CreditTransactions /> <CreditTransactions
page={page}
pageSize={pageSize}
search={search}
sorting={sorting}
onPageChange={(newPageIndex) => setQueryStates({ page: newPageIndex })}
onPageSizeChange={(newPageSize) =>
setQueryStates({ pageSize: newPageSize, page: 0 })
}
onSearch={(newSearch) => setQueryStates({ search: newSearch, page: 0 })}
onSortingChange={(newSorting) => {
if (newSorting.length > 0) {
setQueryStates({
sortId: newSorting[0].id,
sortDesc: newSorting[0].desc ? 1 : 0,
});
}
}}
/>
</TabsContent> </TabsContent>
</Tabs> </Tabs>
</div> </div>