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:
committed by
GitHub
parent
b3acbbe801
commit
9fae142f2d
@@ -1248,6 +1248,16 @@ export function DataTableStory() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-6">
|
||||
{/* Note about pagination examples */}
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50 p-3">
|
||||
<p className="text-sm text-amber-800">
|
||||
<strong>Note:</strong> These examples show only the first page
|
||||
of data for demonstration. In a real application, pagination
|
||||
would fetch different pages from your backend based on the
|
||||
current page index.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Small dataset with pagination */}
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-semibold">
|
||||
@@ -1267,7 +1277,7 @@ export function DataTableStory() {
|
||||
),
|
||||
},
|
||||
]}
|
||||
data={data.slice(0, 15)}
|
||||
data={data.slice(0, 5)}
|
||||
pageSize={5}
|
||||
pageCount={3}
|
||||
getRowId={(row) => row.id}
|
||||
@@ -1303,7 +1313,7 @@ export function DataTableStory() {
|
||||
},
|
||||
},
|
||||
]}
|
||||
data={data.slice(0, 30)}
|
||||
data={data.slice(0, 10)}
|
||||
pageSize={10}
|
||||
pageCount={3}
|
||||
getRowId={(row) => row.id}
|
||||
@@ -1416,7 +1426,7 @@ export function DataTableStory() {
|
||||
size: 80,
|
||||
},
|
||||
]}
|
||||
data={data.slice(0, 50)}
|
||||
data={data.slice(0, 15)}
|
||||
pageSize={15}
|
||||
pageCount={Math.ceil(50 / 15)}
|
||||
getRowId={(row) => row.id}
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
"format": "prettier --check --write \"**/*.{js,cjs,mjs,ts,tsx,md,json}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^2.0.23",
|
||||
"@ai-sdk/openai": "^2.0.24",
|
||||
"@faker-js/faker": "^10.0.0",
|
||||
"@hookform/resolvers": "^5.2.1",
|
||||
"@tanstack/react-query": "5.85.5",
|
||||
"ai": "5.0.28",
|
||||
"@tanstack/react-query": "5.87.1",
|
||||
"ai": "5.0.33",
|
||||
"lucide-react": "^0.542.0",
|
||||
"next": "15.5.2",
|
||||
"nodemailer": "^7.0.5",
|
||||
"nodemailer": "^7.0.6",
|
||||
"react": "19.1.1",
|
||||
"react-dom": "19.1.1",
|
||||
"rxjs": "^7.8.2"
|
||||
@@ -27,8 +27,8 @@
|
||||
"@kit/shared": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@tailwindcss/postcss": "^4.1.12",
|
||||
"@types/node": "^24.3.0",
|
||||
"@tailwindcss/postcss": "^4.1.13",
|
||||
"@types/node": "^24.3.1",
|
||||
"@types/nodemailer": "7.0.1",
|
||||
"@types/react": "19.1.12",
|
||||
"@types/react-dom": "19.1.9",
|
||||
@@ -36,7 +36,7 @@
|
||||
"pino-pretty": "13.0.0",
|
||||
"react-hook-form": "^7.62.0",
|
||||
"recharts": "2.15.3",
|
||||
"tailwindcss": "4.1.12",
|
||||
"tailwindcss": "4.1.13",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "^5.9.2",
|
||||
"zod": "^3.25.74"
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.55.0",
|
||||
"@types/node": "^24.3.0",
|
||||
"dotenv": "17.2.1",
|
||||
"@types/node": "^24.3.1",
|
||||
"dotenv": "17.2.2",
|
||||
"node-html-parser": "^7.0.1",
|
||||
"totp-generator": "^1.0.0"
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ test.describe('Admin', () => {
|
||||
page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/admin/accounts') &&
|
||||
response.status() === 200,
|
||||
response.request().method() === 'POST',
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -172,10 +172,12 @@ test.describe('Admin', () => {
|
||||
page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/admin/accounts') &&
|
||||
response.status() === 200,
|
||||
response.request().method() === 'POST',
|
||||
),
|
||||
]);
|
||||
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
// Verify ban badge is removed
|
||||
await expect(page.getByText('Banned')).not.toBeVisible();
|
||||
|
||||
@@ -388,14 +390,17 @@ async function filterAccounts(page: Page, email: string) {
|
||||
.fill(email);
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForTimeout(250);
|
||||
}
|
||||
|
||||
async function selectAccount(page: Page, email: string) {
|
||||
await page
|
||||
const link = page
|
||||
.locator('tr', { hasText: email.split('@')[0] })
|
||||
.locator('a')
|
||||
.click();
|
||||
.locator('a');
|
||||
|
||||
await expect(link).toBeVisible();
|
||||
|
||||
await link.click();
|
||||
|
||||
await page.waitForURL(new RegExp(`/admin/accounts/[a-z0-9-]+`));
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
@@ -46,9 +46,12 @@ const config = {
|
||||
resolveExtensions: ['.ts', '.tsx', '.js', '.jsx'],
|
||||
resolveAlias: getModulesAliases(),
|
||||
},
|
||||
devIndicators: {
|
||||
position: 'bottom-right',
|
||||
},
|
||||
devIndicators:
|
||||
process.env.NEXT_PUBLIC_CI === 'true'
|
||||
? false
|
||||
: {
|
||||
position: 'bottom-right',
|
||||
},
|
||||
experimental: {
|
||||
mdxRs: true,
|
||||
reactCompiler: ENABLE_REACT_COMPILER,
|
||||
@@ -131,7 +134,6 @@ function getModulesAliases() {
|
||||
|
||||
// exclude the modules that are not needed
|
||||
const excludeSentry = monitoringProvider !== 'sentry';
|
||||
const excludeBaselime = monitoringProvider !== 'baselime';
|
||||
const excludeStripe = billingProvider !== 'stripe';
|
||||
const excludeNodemailer = mailerProvider !== 'nodemailer';
|
||||
const excludeTurnstile = !captchaProvider;
|
||||
@@ -146,10 +148,6 @@ function getModulesAliases() {
|
||||
aliases['@sentry/nextjs'] = noopPath;
|
||||
}
|
||||
|
||||
if (excludeBaselime) {
|
||||
aliases['@baselime/react-rum'] = noopPath;
|
||||
}
|
||||
|
||||
if (excludeStripe) {
|
||||
aliases['stripe'] = noopPath;
|
||||
aliases['@stripe/stripe-js'] = noopPath;
|
||||
|
||||
@@ -54,10 +54,10 @@
|
||||
"@makerkit/data-loader-supabase-core": "^0.0.10",
|
||||
"@makerkit/data-loader-supabase-nextjs": "^1.2.5",
|
||||
"@marsidev/react-turnstile": "^1.3.0",
|
||||
"@nosecone/next": "1.0.0-beta.10",
|
||||
"@nosecone/next": "1.0.0-beta.11",
|
||||
"@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",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"lucide-react": "^0.542.0",
|
||||
@@ -77,8 +77,8 @@
|
||||
"@kit/prettier-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@next/bundle-analyzer": "15.5.2",
|
||||
"@tailwindcss/postcss": "^4.1.12",
|
||||
"@types/node": "^24.3.0",
|
||||
"@tailwindcss/postcss": "^4.1.13",
|
||||
"@types/node": "^24.3.1",
|
||||
"@types/react": "19.1.12",
|
||||
"@types/react-dom": "19.1.9",
|
||||
"babel-plugin-react-compiler": "19.1.0-rc.3",
|
||||
@@ -86,7 +86,7 @@
|
||||
"pino-pretty": "13.0.0",
|
||||
"prettier": "^3.6.2",
|
||||
"supabase": "2.39.2",
|
||||
"tailwindcss": "4.1.12",
|
||||
"tailwindcss": "4.1.13",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
|
||||
@@ -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": {
|
||||
"*": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@kit/shared": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/wordpress": "workspace:*",
|
||||
"@types/node": "^24.3.0"
|
||||
"@types/node": "^24.3.1"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"nodemailer": "^7.0.5"
|
||||
"nodemailer": "^7.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/eslint-config": "workspace:*",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@types/react": "19.1.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"pino": "^9.8.0"
|
||||
"pino": "^9.9.4"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
4037
pnpm-lock.yaml
generated
4037
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -17,12 +17,12 @@
|
||||
"@types/eslint": "9.6.1",
|
||||
"eslint-config-next": "15.5.2",
|
||||
"eslint-config-turbo": "^2.5.6",
|
||||
"typescript-eslint": "8.41.0"
|
||||
"typescript-eslint": "8.42.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/prettier-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"eslint": "^9.34.0",
|
||||
"eslint": "^9.35.0",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"prettier": "@kit/prettier-config"
|
||||
|
||||
Reference in New Issue
Block a user