Deps Update + Table improvements (#351)

* fix: enhance DataTable pagination examples and improve display logic

- Added a note in the DataTableStory component to clarify that examples show only the first page of data for demonstration purposes.
- Adjusted pagination examples to reflect smaller datasets, changing the displayed data slices for better clarity and testing.
- Updated the Pagination component to calculate and display the current record range more accurately based on the current page index and size.

* chore(dependencies): update package versions for improved compatibility

- Upgraded `@supabase/supabase-js` from `2.55.0` to `2.57.0` for enhanced functionality and performance.
- Bumped `@tanstack/react-query` from `5.85.5` to `5.85.9` to incorporate the latest improvements.
- Updated `ai` from `5.0.28` to `5.0.30` for better performance.
- Incremented `nodemailer` from `7.0.5` to `7.0.6` for stability.
- Updated `typescript-eslint` from `8.41.0` to `8.42.0` for improved type definitions and linting capabilities.
- Adjusted various package dependencies across multiple components to ensure compatibility and stability.

* chore(dependencies): update package versions for improved compatibility

- Upgraded `@ai-sdk/openai` from `2.0.23` to `2.0.24` for enhanced functionality.
- Bumped `@tanstack/react-query` from `5.85.9` to `5.86.0` to incorporate the latest improvements.
- Updated `ai` from `5.0.30` to `5.0.33` for better performance.
- Incremented `@types/node` from `24.3.0` to `24.3.1` for type safety enhancements.
- Updated `dotenv` from `17.2.1` to `17.2.2` for stability.
- Adjusted `tailwindcss` and related packages to `4.1.13` for improved styling capabilities.
- Updated `react-i18next` from `15.7.3` to `15.7.3` to include the latest localization fixes.
- Incremented `@sentry/nextjs` from `10.8.0` to `10.10.0` for enhanced monitoring features.
- Updated various package dependencies across multiple components to ensure compatibility and stability.

* fix(config): conditionally disable `devIndicators` in CI environment

* feat(settings): encapsulate danger zone actions in a styled card component

- Introduced a new `DangerZoneCard` component to enhance the visual presentation of danger zone actions in the team account settings.
- Updated `TeamAccountDangerZone` to wrap deletion and leave actions within the `DangerZoneCard` for improved user experience.
- Removed redundant card structure from `TeamAccountSettingsContainer` to streamline the component hierarchy.

* fix(e2e): improve admin account tests for response handling and visibility checks

- Enhanced the admin test suite by adding a check for the POST request method when waiting for the response from the `/admin/accounts` endpoint.
- Reduced wait times in the `filterAccounts` function for improved test performance.
- Updated the `selectAccount` function to ensure the account link is visible before clicking, enhancing reliability in the test flow.

* chore(dependencies): update package versions for improved compatibility

- Upgraded `@supabase/supabase-js` from `2.57.0` to `2.57.2` for enhanced functionality and performance.
- Bumped `@tanstack/react-query` from `5.86.0` to `5.87.1` to incorporate the latest improvements.
- Updated `i18next` from `25.5.1` to `25.5.2` for better localization support.
- Incremented `eslint` from `9.34.0` to `9.35.0` for improved linting capabilities.
- Adjusted various package dependencies across multiple components to ensure compatibility and stability.

* feat(admin): enhance user ban and reactivation actions with success handling

- Updated `AdminBanUserDialog` and `AdminReactivateUserDialog` components to handle success states based on the results of the respective actions.
- Modified `banUserAction` and `reactivateUserAction` to return success status and log errors if the actions fail.
- Introduced `revalidatePage` function to refresh the user account page after banning or reactivating a user.
- Improved error handling in the dialogs to provide better feedback to the admin user.

* feat(admin): refactor user ban and reactivation dialogs for improved structure and error handling

- Introduced `BanUserForm` and `ReactivateUserForm` components to encapsulate form logic within the respective dialogs, enhancing readability and maintainability.
- Updated the `AdminBanUserDialog` and `AdminReactivateUserDialog` components to utilize the new form components, streamlining the user interface.
- Enhanced error handling to provide clearer feedback to the admin user during ban and reactivation actions.
- Removed unnecessary revalidation calls in the server actions to optimize performance and maintain clarity in the action flow.
- Added `@types/react-dom` dependency for improved type definitions.

* refactor(admin): streamline user dialogs and server actions for improved clarity

- Removed unnecessary `useRouter` imports from `AdminBanUserDialog` and `AdminReactivateUserDialog` components to simplify the code.
- Updated `revalidateAdmin` function calls to use `revalidatePath` with specific paths, enhancing clarity in the server actions.
- Ensured that the user dialogs maintain a clean structure while focusing on form logic and error handling.
This commit is contained in:
Giancarlo Buomprisco
2025-09-06 17:30:09 +07:00
committed by GitHub
parent b3acbbe801
commit 9fae142f2d
37 changed files with 2293 additions and 2313 deletions

View File

@@ -17,7 +17,7 @@
"@kit/eslint-config": "workspace:*",
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/node": "^24.3.0"
"@types/node": "^24.3.1"
},
"typesVersions": {
"*": {

View File

@@ -26,7 +26,7 @@
"@kit/supabase": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@supabase/supabase-js": "2.55.0",
"@supabase/supabase-js": "2.57.2",
"@types/react": "19.1.12",
"date-fns": "^4.1.0",
"lucide-react": "^0.542.0",

View File

@@ -15,7 +15,7 @@
"./components": "./src/components/index.ts"
},
"dependencies": {
"@stripe/react-stripe-js": "^3.9.2",
"@stripe/react-stripe-js": "^4.0.0",
"@stripe/stripe-js": "^7.9.0",
"stripe": "^18.5.0"
},

View File

@@ -20,7 +20,7 @@
"@kit/shared": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/wordpress": "workspace:*",
"@types/node": "^24.3.0"
"@types/node": "^24.3.1"
},
"typesVersions": {
"*": {

View File

@@ -26,7 +26,7 @@
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@types/node": "^24.3.0",
"@types/node": "^24.3.1",
"@types/react": "19.1.12",
"react": "19.1.1",
"zod": "^3.25.74"

View File

@@ -20,7 +20,7 @@
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@types/node": "^24.3.0",
"@types/node": "^24.3.1",
"@types/react": "19.1.12",
"wp-types": "^4.68.1"
},

View File

@@ -22,7 +22,7 @@
"@kit/supabase": "workspace:*",
"@kit/team-accounts": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@supabase/supabase-js": "2.55.0",
"@supabase/supabase-js": "2.57.2",
"zod": "^3.25.74"
},
"typesVersions": {

View File

@@ -34,8 +34,8 @@
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@radix-ui/react-icons": "^1.3.2",
"@supabase/supabase-js": "2.55.0",
"@tanstack/react-query": "5.85.5",
"@supabase/supabase-js": "2.57.2",
"@tanstack/react-query": "5.87.1",
"@types/react": "19.1.12",
"@types/react-dom": "19.1.9",
"lucide-react": "^0.542.0",

View File

@@ -20,8 +20,8 @@
"@kit/ui": "workspace:*",
"@makerkit/data-loader-supabase-core": "^0.0.10",
"@makerkit/data-loader-supabase-nextjs": "^1.2.5",
"@supabase/supabase-js": "2.55.0",
"@tanstack/react-query": "5.85.5",
"@supabase/supabase-js": "2.57.2",
"@tanstack/react-query": "5.87.1",
"@tanstack/react-table": "^8.21.3",
"@types/react": "19.1.12",
"lucide-react": "^0.542.0",

View File

@@ -37,17 +37,6 @@ export function AdminBanUserDialog(
userId: string;
}>,
) {
const [pending, startTransition] = useTransition();
const [error, setError] = useState<boolean>(false);
const form = useForm({
resolver: zodResolver(BanUserSchema),
defaultValues: {
userId: props.userId,
confirmation: '',
},
});
return (
<AlertDialog>
<AlertDialogTrigger asChild>{props.children}</AlertDialogTrigger>
@@ -62,72 +51,86 @@ export function AdminBanUserDialog(
</AlertDialogDescription>
</AlertDialogHeader>
<Form {...form}>
<form
data-test={'admin-ban-user-form'}
className={'flex flex-col space-y-8'}
onSubmit={form.handleSubmit((data) => {
startTransition(async () => {
try {
await banUserAction(data);
setError(false);
} catch {
setError(true);
}
});
})}
>
<If condition={error}>
<Alert variant={'destructive'}>
<AlertTitle>Error</AlertTitle>
<AlertDescription>
There was an error banning the user. Please check the server
logs to see what went wrong.
</AlertDescription>
</Alert>
</If>
<FormField
name={'confirmation'}
render={({ field }) => (
<FormItem>
<FormLabel>
Type <b>CONFIRM</b> to confirm
</FormLabel>
<FormControl>
<Input
required
pattern={'CONFIRM'}
placeholder={'Type CONFIRM to confirm'}
{...field}
/>
</FormControl>
<FormDescription>
Are you sure you want to do this?
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<Button
disabled={pending}
type={'submit'}
variant={'destructive'}
>
Ban User
</Button>
</AlertDialogFooter>
</form>
</Form>
<BanUserForm userId={props.userId} />
</AlertDialogContent>
</AlertDialog>
);
}
function BanUserForm(props: { userId: string }) {
const [pending, startTransition] = useTransition();
const [error, setError] = useState<boolean>(false);
const form = useForm({
resolver: zodResolver(BanUserSchema),
defaultValues: {
userId: props.userId,
confirmation: '',
},
});
return (
<Form {...form}>
<form
data-test={'admin-ban-user-form'}
className={'flex flex-col space-y-8'}
onSubmit={form.handleSubmit((data) => {
startTransition(async () => {
try {
const result = await banUserAction(data);
setError(!result.success);
} catch {
setError(true);
}
});
})}
>
<If condition={error}>
<Alert variant={'destructive'}>
<AlertTitle>Error</AlertTitle>
<AlertDescription>
There was an error banning the user. Please check the server logs
to see what went wrong.
</AlertDescription>
</Alert>
</If>
<FormField
name={'confirmation'}
render={({ field }) => (
<FormItem>
<FormLabel>
Type <b>CONFIRM</b> to confirm
</FormLabel>
<FormControl>
<Input
required
pattern={'CONFIRM'}
placeholder={'Type CONFIRM to confirm'}
{...field}
/>
</FormControl>
<FormDescription>
Are you sure you want to do this?
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<AlertDialogFooter>
<AlertDialogCancel disabled={pending}>Cancel</AlertDialogCancel>
<Button disabled={pending} type={'submit'} variant={'destructive'}>
{pending ? 'Banning...' : 'Ban User'}
</Button>
</AlertDialogFooter>
</form>
</Form>
);
}

View File

@@ -37,17 +37,6 @@ export function AdminReactivateUserDialog(
userId: string;
}>,
) {
const [pending, startTransition] = useTransition();
const [error, setError] = useState<boolean>(false);
const form = useForm({
resolver: zodResolver(ReactivateUserSchema),
defaultValues: {
userId: props.userId,
confirmation: '',
},
});
return (
<AlertDialog>
<AlertDialogTrigger asChild>{props.children}</AlertDialogTrigger>
@@ -61,68 +50,86 @@ export function AdminReactivateUserDialog(
</AlertDialogDescription>
</AlertDialogHeader>
<Form {...form}>
<form
data-test={'admin-reactivate-user-form'}
className={'flex flex-col space-y-8'}
onSubmit={form.handleSubmit((data) => {
startTransition(async () => {
try {
await reactivateUserAction(data);
setError(false);
} catch {
setError(true);
}
});
})}
>
<If condition={error}>
<Alert variant={'destructive'}>
<AlertTitle>Error</AlertTitle>
<AlertDescription>
There was an error reactivating the user. Please check the
server logs to see what went wrong.
</AlertDescription>
</Alert>
</If>
<FormField
name={'confirmation'}
render={({ field }) => (
<FormItem>
<FormLabel>
Type <b>CONFIRM</b> to confirm
</FormLabel>
<FormControl>
<Input
required
pattern={'CONFIRM'}
placeholder={'Type CONFIRM to confirm'}
{...field}
/>
</FormControl>
<FormDescription>
Are you sure you want to do this?
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<Button disabled={pending} type={'submit'}>
{pending ? 'Reactivating...' : 'Reactivate User'}
</Button>
</AlertDialogFooter>
</form>
</Form>
<ReactivateUserForm userId={props.userId} />
</AlertDialogContent>
</AlertDialog>
);
}
function ReactivateUserForm(props: { userId: string }) {
const [pending, startTransition] = useTransition();
const [error, setError] = useState<boolean>(false);
const form = useForm({
resolver: zodResolver(ReactivateUserSchema),
defaultValues: {
userId: props.userId,
confirmation: '',
},
});
return (
<Form {...form}>
<form
data-test={'admin-reactivate-user-form'}
className={'flex flex-col space-y-8'}
onSubmit={form.handleSubmit((data) => {
startTransition(async () => {
try {
const result = await reactivateUserAction(data);
setError(!result.success);
} catch {
setError(true);
}
});
})}
>
<If condition={error}>
<Alert variant={'destructive'}>
<AlertTitle>Error</AlertTitle>
<AlertDescription>
There was an error reactivating the user. Please check the server
logs to see what went wrong.
</AlertDescription>
</Alert>
</If>
<FormField
name={'confirmation'}
render={({ field }) => (
<FormItem>
<FormLabel>
Type <b>CONFIRM</b> to confirm
</FormLabel>
<FormControl>
<Input
required
pattern={'CONFIRM'}
placeholder={'Type CONFIRM to confirm'}
{...field}
/>
</FormControl>
<FormDescription>
Are you sure you want to do this?
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<AlertDialogFooter>
<AlertDialogCancel disabled={pending}>Cancel</AlertDialogCancel>
<Button disabled={pending} type={'submit'}>
{pending ? 'Reactivating...' : 'Reactivate User'}
</Button>
</AlertDialogFooter>
</form>
</Form>
);
}

View File

@@ -33,7 +33,15 @@ export const banUserAction = adminAction(
logger.info({ userId }, `Super Admin is banning user...`);
await service.banUser(userId);
const { error } = await service.banUser(userId);
if (error) {
logger.error({ error }, `Error banning user`);
return {
success: false,
};
}
logger.info({ userId }, `Super Admin has successfully banned user`);
@@ -61,12 +69,20 @@ export const reactivateUserAction = adminAction(
logger.info({ userId }, `Super Admin is reactivating user...`);
await service.reactivateUser(userId);
const { error } = await service.reactivateUser(userId);
logger.info({ userId }, `Super Admin has successfully reactivated user`);
if (error) {
logger.error({ error }, `Error reactivating user`);
return {
success: false,
};
}
revalidateAdmin();
logger.info({ userId }, `Super Admin has successfully reactivated user`);
return {
success: true,
};
@@ -113,8 +129,6 @@ export const deleteUserAction = adminAction(
logger.info({ userId }, `Super Admin has successfully deleted user`);
revalidateAdmin();
return redirect('/admin/accounts');
},
{
@@ -137,13 +151,13 @@ export const deleteAccountAction = adminAction(
await service.deleteAccount(accountId);
revalidateAdmin();
logger.info(
{ accountId },
`Super Admin has successfully deleted account`,
);
revalidateAdmin();
return redirect('/admin/accounts');
},
{
@@ -180,7 +194,7 @@ export const createUserAction = adminAction(
`Super Admin has successfully created a new user`,
);
revalidateAdmin();
revalidatePath(`/admin/accounts`);
return {
success: true,
@@ -212,8 +226,6 @@ export const resetPasswordAction = adminAction(
`Super Admin has successfully sent password reset email`,
);
revalidateAdmin();
return result;
},
{
@@ -223,7 +235,7 @@ export const resetPasswordAction = adminAction(
);
function revalidateAdmin() {
revalidatePath('/admin', 'layout');
revalidatePath(`/admin/accounts/[id]`, 'page');
}
function getAdminAuthService() {

View File

@@ -153,7 +153,7 @@ class AdminAuthUserService {
}
private async setBanDuration(userId: string, banDuration: string) {
await this.adminClient.auth.admin.updateUserById(userId, {
return this.adminClient.auth.admin.updateUserById(userId, {
ban_duration: banDuration,
});
}

View File

@@ -29,8 +29,8 @@
"@kit/ui": "workspace:*",
"@marsidev/react-turnstile": "^1.3.0",
"@radix-ui/react-icons": "^1.3.2",
"@supabase/supabase-js": "2.55.0",
"@tanstack/react-query": "5.85.5",
"@supabase/supabase-js": "2.57.2",
"@tanstack/react-query": "5.87.1",
"@types/react": "19.1.12",
"lucide-react": "^0.542.0",
"next": "15.5.2",

View File

@@ -19,8 +19,8 @@
"@kit/supabase": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@supabase/supabase-js": "2.55.0",
"@tanstack/react-query": "5.85.5",
"@supabase/supabase-js": "2.57.2",
"@tanstack/react-query": "5.87.1",
"@types/react": "19.1.12",
"lucide-react": "^0.542.0",
"react": "19.1.1",

View File

@@ -32,8 +32,8 @@
"@kit/supabase": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@supabase/supabase-js": "2.55.0",
"@tanstack/react-query": "5.85.5",
"@supabase/supabase-js": "2.57.2",
"@tanstack/react-query": "5.87.1",
"@tanstack/react-table": "^8.21.3",
"@types/react": "19.1.12",
"@types/react-dom": "19.1.9",

View File

@@ -21,6 +21,13 @@ import {
AlertDialogTrigger,
} from '@kit/ui/alert-dialog';
import { Button } from '@kit/ui/button';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@kit/ui/card';
import {
Form,
FormControl,
@@ -64,7 +71,11 @@ export function TeamAccountDangerZone({
if (userIsPrimaryOwner) {
if (features.enableTeamDeletion) {
return <DeleteTeamContainer account={account} />;
return (
<DangerZoneCard>
<DeleteTeamContainer account={account} />
</DangerZoneCard>
);
}
return;
@@ -72,7 +83,11 @@ export function TeamAccountDangerZone({
// A primary owner can't leave the team account
// but other members can
return <LeaveTeamContainer account={account} />;
return (
<DangerZoneCard>
<LeaveTeamContainer account={account} />
</DangerZoneCard>
);
}
function DeleteTeamContainer(props: {
@@ -411,3 +426,21 @@ function DeleteTeamErrorAlert() {
</div>
);
}
function DangerZoneCard({ children }: React.PropsWithChildren) {
return (
<Card className={'border-destructive border'}>
<CardHeader>
<CardTitle>
<Trans i18nKey={'teams:settings.dangerZone'} />
</CardTitle>
<CardDescription>
<Trans i18nKey={'teams:settings.dangerZoneDescription'} />
</CardDescription>
</CardHeader>
<CardContent>{children}</CardContent>
</Card>
);
}

View File

@@ -67,25 +67,11 @@ export function TeamAccountSettingsContainer(props: {
</CardContent>
</Card>
<Card className={'border-destructive border'}>
<CardHeader>
<CardTitle>
<Trans i18nKey={'teams:settings.dangerZone'} />
</CardTitle>
<CardDescription>
<Trans i18nKey={'teams:settings.dangerZoneDescription'} />
</CardDescription>
</CardHeader>
<CardContent>
<TeamAccountDangerZone
primaryOwnerUserId={props.account.primaryOwnerUserId}
account={props.account}
features={props.features}
/>
</CardContent>
</Card>
<TeamAccountDangerZone
primaryOwnerUserId={props.account.primaryOwnerUserId}
account={props.account}
features={props.features}
/>
</div>
);
}

View File

@@ -20,14 +20,14 @@
"@kit/prettier-config": "workspace:*",
"@kit/shared": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@tanstack/react-query": "5.85.5",
"@tanstack/react-query": "5.87.1",
"next": "15.5.2",
"react": "19.1.1",
"react-dom": "19.1.1",
"react-i18next": "^15.7.3"
},
"dependencies": {
"i18next": "25.4.2",
"i18next": "25.5.2",
"i18next-browser-languagedetector": "8.2.0",
"i18next-resources-to-backend": "^1.2.1"
},

View File

@@ -20,7 +20,7 @@
"@kit/resend": "workspace:*",
"@kit/shared": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/node": "^24.3.0",
"@types/node": "^24.3.1",
"zod": "^3.25.74"
},
"typesVersions": {

View File

@@ -13,7 +13,7 @@
".": "./src/index.ts"
},
"dependencies": {
"nodemailer": "^7.0.5"
"nodemailer": "^7.0.6"
},
"devDependencies": {
"@kit/eslint-config": "workspace:*",

View File

@@ -17,7 +17,7 @@
"@kit/mailers-shared": "workspace:*",
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/node": "^24.3.0",
"@types/node": "^24.3.1",
"zod": "^3.25.74"
},
"typesVersions": {

View File

@@ -16,7 +16,7 @@
"./config/server": "./src/sentry.client.server.ts"
},
"dependencies": {
"@sentry/nextjs": "^10.8.0",
"@sentry/nextjs": "^10.10.0",
"import-in-the-middle": "1.14.2"
},
"devDependencies": {

View File

@@ -20,7 +20,7 @@
"@kit/prettier-config": "workspace:*",
"@kit/supabase": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@supabase/supabase-js": "2.55.0",
"@supabase/supabase-js": "2.57.2",
"next": "15.5.2",
"zod": "^3.25.74"
},

View File

@@ -25,7 +25,7 @@
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@radix-ui/react-icons": "^1.3.2",
"@supabase/supabase-js": "2.55.0",
"@supabase/supabase-js": "2.57.2",
"@types/react": "19.1.12",
"@types/react-dom": "19.1.9",
"react": "19.1.1",

View File

@@ -23,7 +23,7 @@
"@types/react": "19.1.12"
},
"dependencies": {
"pino": "^9.8.0"
"pino": "^9.9.4"
},
"typesVersions": {
"*": {

View File

@@ -26,8 +26,8 @@
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@supabase/ssr": "^0.7.0",
"@supabase/supabase-js": "2.55.0",
"@tanstack/react-query": "5.85.5",
"@supabase/supabase-js": "2.57.2",
"@tanstack/react-query": "5.87.1",
"@types/react": "19.1.12",
"next": "15.5.2",
"react": "19.1.1",

View File

@@ -25,14 +25,14 @@
"@kit/eslint-config": "workspace:*",
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@supabase/supabase-js": "2.55.0",
"@tanstack/react-query": "5.85.5",
"@supabase/supabase-js": "2.57.2",
"@tanstack/react-query": "5.87.1",
"@tanstack/react-table": "^8.21.3",
"@types/react": "19.1.12",
"@types/react-dom": "19.1.9",
"class-variance-authority": "^0.7.1",
"date-fns": "^4.1.0",
"eslint": "^9.34.0",
"eslint": "^9.35.0",
"next": "15.5.2",
"next-themes": "0.4.6",
"prettier": "^3.6.2",
@@ -40,7 +40,7 @@
"react-hook-form": "^7.62.0",
"react-i18next": "^15.7.3",
"sonner": "^2.0.7",
"tailwindcss": "4.1.12",
"tailwindcss": "4.1.13",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.9.2",
"zod": "^3.25.74"

View File

@@ -364,12 +364,12 @@ export function DataTable<RecordData extends DataItem>({
? 'hover:bg-accent/50 -mx-3 cursor-pointer rounded px-3 py-1 select-none'
: '',
)}
onClick={header.column.getToggleSortingHandler()}
>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
{header.column.getCanSort() && (
<div className="flex flex-col">
<ChevronUp
@@ -503,7 +503,6 @@ export function DataTable<RecordData extends DataItem>({
<div className={'px-2.5 py-1.5'}>
<Pagination
table={table}
pageSize={pageSize}
totalCount={
pageCount && pageSize ? pageCount * pageSize : undefined
}
@@ -519,19 +518,26 @@ export function DataTable<RecordData extends DataItem>({
function Pagination<T>({
table,
totalCount,
pageSize,
}: React.PropsWithChildren<{
table: ReactTable<T>;
totalCount?: number;
pageSize?: number;
}>) {
const currentPageIndex = table.getState().pagination.pageIndex;
const currentPageSize = table.getState().pagination.pageSize;
const rows = table.getRowModel().rows;
// Calculate what records are being shown on this page
const startRecord = currentPageIndex * currentPageSize + 1;
const endRecord = startRecord + rows.length - 1;
return (
<div className="flex items-center space-x-4">
<span className="text-muted-foreground flex items-center text-xs">
<Trans
i18nKey={'common:pageOfPages'}
values={{
page: table.getState().pagination.pageIndex + 1,
page: currentPageIndex + 1,
total: table.getPageCount(),
}}
/>
@@ -583,12 +589,9 @@ function Pagination<T>({
</Button>
</div>
<If condition={totalCount}>
<If condition={totalCount && rows.length > 0}>
<span className="text-muted-foreground flex items-center text-xs">
<Trans
i18nKey={'common:showingRecordCount'}
values={{ totalCount, pageSize }}
/>
Showing {startRecord} to {endRecord} of {totalCount} rows
</span>
</If>
</div>