fix: avoid duplicate billing portal link (#330)
* fix: avoid duplicate billing portal link * fix: improve DataTable API
This commit is contained in:
committed by
GitHub
parent
ad427365c9
commit
f9ebe2f927
File diff suppressed because it is too large
Load Diff
@@ -8,12 +8,12 @@
|
|||||||
"format": "prettier --check --write \"**/*.{js,cjs,mjs,ts,tsx,md,json}\""
|
"format": "prettier --check --write \"**/*.{js,cjs,mjs,ts,tsx,md,json}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/openai": "^2.0.19",
|
"@ai-sdk/openai": "^2.0.20",
|
||||||
"@faker-js/faker": "^9.9.0",
|
"@faker-js/faker": "^9.9.0",
|
||||||
"@hookform/resolvers": "^5.2.1",
|
"@hookform/resolvers": "^5.2.1",
|
||||||
"@tanstack/react-query": "5.85.5",
|
"@tanstack/react-query": "5.85.5",
|
||||||
"ai": "5.0.21",
|
"ai": "5.0.23",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.541.0",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"nodemailer": "^7.0.5",
|
"nodemailer": "^7.0.5",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"@tailwindcss/postcss": "^4.1.12",
|
"@tailwindcss/postcss": "^4.1.12",
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.0",
|
||||||
"@types/nodemailer": "7.0.1",
|
"@types/nodemailer": "7.0.1",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.7",
|
||||||
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
||||||
"pino-pretty": "13.0.0",
|
"pino-pretty": "13.0.0",
|
||||||
|
|||||||
@@ -62,13 +62,13 @@ export default defineConfig({
|
|||||||
|
|
||||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
navigationTimeout: 15_000,
|
navigationTimeout: 15 * 1000,
|
||||||
},
|
},
|
||||||
// test timeout set to 2 minutes
|
// test timeout set to 2 minutes
|
||||||
timeout: 120 * 1000,
|
timeout: 120 * 1000,
|
||||||
expect: {
|
expect: {
|
||||||
// expect timeout set to 5 seconds
|
// expect timeout set to 10 seconds
|
||||||
timeout: 5 * 1000,
|
timeout: 10 * 1000,
|
||||||
},
|
},
|
||||||
/* Configure projects for major browsers */
|
/* Configure projects for major browsers */
|
||||||
projects: [
|
projects: [
|
||||||
|
|||||||
@@ -388,11 +388,15 @@ async function filterAccounts(page: Page, email: string) {
|
|||||||
.fill(email);
|
.fill(email);
|
||||||
|
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
await page.waitForTimeout(250);
|
await page.waitForTimeout(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectAccount(page: Page, email: string) {
|
async function selectAccount(page: Page, email: string) {
|
||||||
await page.getByRole('link', { name: email.split('@')[0] }).click();
|
await page
|
||||||
|
.locator('tr', { hasText: email.split('@')[0] })
|
||||||
|
.locator('a')
|
||||||
|
.click();
|
||||||
|
|
||||||
await page.waitForURL(new RegExp(`/admin/accounts/[a-z0-9-]+`));
|
await page.waitForURL(new RegExp(`/admin/accounts/[a-z0-9-]+`));
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,15 +60,14 @@ async function PersonalAccountBillingPage() {
|
|||||||
|
|
||||||
<PageBody>
|
<PageBody>
|
||||||
<div className={'flex flex-col space-y-4'}>
|
<div className={'flex flex-col space-y-4'}>
|
||||||
<If condition={!hasBillingData}>
|
<If
|
||||||
<PersonalAccountCheckoutForm customerId={customerId} />
|
condition={hasBillingData}
|
||||||
|
fallback={
|
||||||
<If condition={customerId}>
|
<>
|
||||||
<CustomerBillingPortalForm />
|
<PersonalAccountCheckoutForm customerId={customerId} />
|
||||||
</If>
|
</>
|
||||||
</If>
|
}
|
||||||
|
>
|
||||||
<If condition={hasBillingData}>
|
|
||||||
<div className={'flex w-full max-w-2xl flex-col space-y-6'}>
|
<div className={'flex w-full max-w-2xl flex-col space-y-6'}>
|
||||||
<If condition={subscription}>
|
<If condition={subscription}>
|
||||||
{(subscription) => {
|
{(subscription) => {
|
||||||
|
|||||||
@@ -60,14 +60,14 @@
|
|||||||
"@tanstack/react-query": "5.85.5",
|
"@tanstack/react-query": "5.85.5",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.541.0",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"next-sitemap": "^4.2.3",
|
"next-sitemap": "^4.2.3",
|
||||||
"next-themes": "0.4.6",
|
"next-themes": "0.4.6",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"react-dom": "19.1.1",
|
"react-dom": "19.1.1",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-i18next": "^15.7.1",
|
"react-i18next": "^15.7.2",
|
||||||
"recharts": "2.15.3",
|
"recharts": "2.15.3",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"zod": "^3.25.74"
|
"zod": "^3.25.74"
|
||||||
@@ -79,10 +79,10 @@
|
|||||||
"@next/bundle-analyzer": "15.5.0",
|
"@next/bundle-analyzer": "15.5.0",
|
||||||
"@tailwindcss/postcss": "^4.1.12",
|
"@tailwindcss/postcss": "^4.1.12",
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.0",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.7",
|
||||||
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
||||||
"cssnano": "^7.1.0",
|
"cssnano": "^7.1.1",
|
||||||
"pino-pretty": "13.0.0",
|
"pino-pretty": "13.0.0",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"supabase": "2.34.0",
|
"supabase": "2.34.0",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
"skip": "Skip",
|
"skip": "Skip",
|
||||||
"signedInAs": "Signed in as",
|
"signedInAs": "Signed in as",
|
||||||
"pageOfPages": "Page {{page}} of {{total}}",
|
"pageOfPages": "Page {{page}} of {{total}}",
|
||||||
|
"showingRecordCount": "Showing {{pageSize}} of {{totalCount}} rows",
|
||||||
"noData": "No data available",
|
"noData": "No data available",
|
||||||
"pageNotFoundHeading": "Ouch! :|",
|
"pageNotFoundHeading": "Ouch! :|",
|
||||||
"errorPageHeading": "Ouch! :|",
|
"errorPageHeading": "Ouch! :|",
|
||||||
|
|||||||
@@ -27,13 +27,13 @@
|
|||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@supabase/supabase-js": "2.55.0",
|
"@supabase/supabase-js": "2.55.0",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.541.0",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-i18next": "^15.7.1",
|
"react-i18next": "^15.7.2",
|
||||||
"zod": "^3.25.74"
|
"zod": "^3.25.74"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"@kit/supabase": "workspace:*",
|
"@kit/supabase": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"zod": "^3.25.74"
|
"zod": "^3.25.74"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"@kit/supabase": "workspace:*",
|
"@kit/supabase": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.0",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"zod": "^3.25.74"
|
"zod": "^3.25.74"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.0",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"wp-types": "^4.68.1"
|
"wp-types": "^4.68.1"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
|
|||||||
@@ -36,15 +36,15 @@
|
|||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@supabase/supabase-js": "2.55.0",
|
"@supabase/supabase-js": "2.55.0",
|
||||||
"@tanstack/react-query": "5.85.5",
|
"@tanstack/react-query": "5.85.5",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.7",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.541.0",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"next-themes": "0.4.6",
|
"next-themes": "0.4.6",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"react-dom": "19.1.1",
|
"react-dom": "19.1.1",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-i18next": "^15.7.1",
|
"react-i18next": "^15.7.2",
|
||||||
"zod": "^3.25.74"
|
"zod": "^3.25.74"
|
||||||
},
|
},
|
||||||
"prettier": "@kit/prettier-config",
|
"prettier": "@kit/prettier-config",
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
"@supabase/supabase-js": "2.55.0",
|
"@supabase/supabase-js": "2.55.0",
|
||||||
"@tanstack/react-query": "5.85.5",
|
"@tanstack/react-query": "5.85.5",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.541.0",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"react-dom": "19.1.1",
|
"react-dom": "19.1.1",
|
||||||
|
|||||||
@@ -31,11 +31,11 @@
|
|||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@supabase/supabase-js": "2.55.0",
|
"@supabase/supabase-js": "2.55.0",
|
||||||
"@tanstack/react-query": "5.85.5",
|
"@tanstack/react-query": "5.85.5",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.541.0",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-i18next": "^15.7.1",
|
"react-i18next": "^15.7.2",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"zod": "^3.25.74"
|
"zod": "^3.25.74"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@supabase/supabase-js": "2.55.0",
|
"@supabase/supabase-js": "2.55.0",
|
||||||
"@tanstack/react-query": "5.85.5",
|
"@tanstack/react-query": "5.85.5",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.541.0",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"react-dom": "19.1.1",
|
"react-dom": "19.1.1",
|
||||||
"react-i18next": "^15.7.1"
|
"react-i18next": "^15.7.2"
|
||||||
},
|
},
|
||||||
"prettier": "@kit/prettier-config",
|
"prettier": "@kit/prettier-config",
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
|
|||||||
@@ -35,16 +35,16 @@
|
|||||||
"@supabase/supabase-js": "2.55.0",
|
"@supabase/supabase-js": "2.55.0",
|
||||||
"@tanstack/react-query": "5.85.5",
|
"@tanstack/react-query": "5.85.5",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.7",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.541.0",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"react-dom": "19.1.1",
|
"react-dom": "19.1.1",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-i18next": "^15.7.1",
|
"react-i18next": "^15.7.2",
|
||||||
"zod": "^3.25.74"
|
"zod": "^3.25.74"
|
||||||
},
|
},
|
||||||
"prettier": "@kit/prettier-config",
|
"prettier": "@kit/prettier-config",
|
||||||
|
|||||||
@@ -24,10 +24,10 @@
|
|||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"react-dom": "19.1.1",
|
"react-dom": "19.1.1",
|
||||||
"react-i18next": "^15.7.1"
|
"react-i18next": "^15.7.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"i18next": "25.4.0",
|
"i18next": "25.4.2",
|
||||||
"i18next-browser-languagedetector": "8.2.0",
|
"i18next-browser-languagedetector": "8.2.0",
|
||||||
"i18next-resources-to-backend": "^1.2.1"
|
"i18next-resources-to-backend": "^1.2.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"@kit/sentry": "workspace:*",
|
"@kit/sentry": "workspace:*",
|
||||||
"@kit/shared": "workspace:*",
|
"@kit/shared": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"zod": "^3.25.74"
|
"zod": "^3.25.74"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"@kit/eslint-config": "workspace:*",
|
"@kit/eslint-config": "workspace:*",
|
||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"zod": "^3.25.74"
|
"zod": "^3.25.74"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"@kit/eslint-config": "workspace:*",
|
"@kit/eslint-config": "workspace:*",
|
||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"react": "19.1.1"
|
"react": "19.1.1"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"@kit/monitoring-core": "workspace:*",
|
"@kit/monitoring-core": "workspace:*",
|
||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"react": "19.1.1"
|
"react": "19.1.1"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@supabase/supabase-js": "2.55.0",
|
"@supabase/supabase-js": "2.55.0",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.7",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"react-dom": "19.1.1",
|
"react-dom": "19.1.1",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
"@kit/eslint-config": "workspace:*",
|
"@kit/eslint-config": "workspace:*",
|
||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@types/react": "19.1.10"
|
"@types/react": "19.1.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pino": "^9.8.0"
|
"pino": "^9.8.0"
|
||||||
|
|||||||
@@ -25,10 +25,10 @@
|
|||||||
"@kit/eslint-config": "workspace:*",
|
"@kit/eslint-config": "workspace:*",
|
||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@supabase/ssr": "^0.6.1",
|
"@supabase/ssr": "^0.7.0",
|
||||||
"@supabase/supabase-js": "2.55.0",
|
"@supabase/supabase-js": "2.55.0",
|
||||||
"@tanstack/react-query": "5.85.5",
|
"@tanstack/react-query": "5.85.5",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
"server-only": "^0.0.1",
|
"server-only": "^0.0.1",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "1.1.1",
|
"cmdk": "1.1.1",
|
||||||
"input-otp": "1.4.2",
|
"input-otp": "1.4.2",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.541.0",
|
||||||
"radix-ui": "1.4.3",
|
"radix-ui": "1.4.3",
|
||||||
"react-dropzone": "^14.3.8",
|
"react-dropzone": "^14.3.8",
|
||||||
"react-top-loading-bar": "3.0.2",
|
"react-top-loading-bar": "3.0.2",
|
||||||
@@ -28,17 +28,17 @@
|
|||||||
"@supabase/supabase-js": "2.55.0",
|
"@supabase/supabase-js": "2.55.0",
|
||||||
"@tanstack/react-query": "5.85.5",
|
"@tanstack/react-query": "5.85.5",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.7",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"eslint": "^9.33.0",
|
"eslint": "^9.34.0",
|
||||||
"next": "15.5.0",
|
"next": "15.5.0",
|
||||||
"next-themes": "0.4.6",
|
"next-themes": "0.4.6",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"react-day-picker": "^9.9.0",
|
"react-day-picker": "^9.9.0",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-i18next": "^15.7.1",
|
"react-i18next": "^15.7.2",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwindcss": "4.1.12",
|
"tailwindcss": "4.1.12",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { Fragment, useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
Cell,
|
||||||
flexRender,
|
flexRender,
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
@@ -49,6 +50,7 @@ export {
|
|||||||
Row,
|
Row,
|
||||||
SortingState,
|
SortingState,
|
||||||
VisibilityState,
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ReactTableProps<T extends DataItem> {
|
interface ReactTableProps<T extends DataItem> {
|
||||||
@@ -57,6 +59,8 @@ interface ReactTableProps<T extends DataItem> {
|
|||||||
renderSubComponent?: (props: { row: Row<T> }) => React.ReactElement;
|
renderSubComponent?: (props: { row: Row<T> }) => React.ReactElement;
|
||||||
pageIndex?: number;
|
pageIndex?: number;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
headerClassName?: string;
|
||||||
|
footerClassName?: string;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
pageCount?: number;
|
pageCount?: number;
|
||||||
sorting?: SortingState;
|
sorting?: SortingState;
|
||||||
@@ -69,14 +73,17 @@ interface ReactTableProps<T extends DataItem> {
|
|||||||
onColumnVisibilityChange?: (visibility: VisibilityState) => void;
|
onColumnVisibilityChange?: (visibility: VisibilityState) => void;
|
||||||
onColumnPinningChange?: (pinning: ColumnPinningState) => void;
|
onColumnPinningChange?: (pinning: ColumnPinningState) => void;
|
||||||
onRowSelectionChange?: (selection: Record<string, boolean>) => void;
|
onRowSelectionChange?: (selection: Record<string, boolean>) => void;
|
||||||
onClick?: (row: Row<T>) => void;
|
onClick?: (props: { row: Row<T>; cell: Cell<T, unknown> }) => void;
|
||||||
tableProps?: React.ComponentProps<typeof Table> &
|
tableProps?: React.ComponentProps<typeof Table> &
|
||||||
Record<`data-${string}`, string>;
|
Record<`data-${string}`, string>;
|
||||||
sticky?: boolean;
|
sticky?: boolean;
|
||||||
|
renderCell?: (props: {
|
||||||
|
cell: Cell<T, unknown>;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
className?: string;
|
||||||
|
}) => (props: React.PropsWithChildren<object>) => React.ReactNode;
|
||||||
renderRow?: (props: {
|
renderRow?: (props: {
|
||||||
row: Row<T>;
|
row: Row<T>;
|
||||||
onClick?: (row: Row<T>) => void;
|
|
||||||
className?: string;
|
|
||||||
}) => (props: React.PropsWithChildren<object>) => React.ReactNode;
|
}) => (props: React.PropsWithChildren<object>) => React.ReactNode;
|
||||||
noResultsMessage?: React.ReactNode;
|
noResultsMessage?: React.ReactNode;
|
||||||
forcePagination?: boolean; // Force pagination to show even when pageCount <= 1
|
forcePagination?: boolean; // Force pagination to show even when pageCount <= 1
|
||||||
@@ -97,7 +104,10 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
onClick,
|
onClick,
|
||||||
tableProps,
|
tableProps,
|
||||||
className,
|
className,
|
||||||
|
headerClassName,
|
||||||
|
footerClassName,
|
||||||
renderRow,
|
renderRow,
|
||||||
|
renderCell,
|
||||||
noResultsMessage,
|
noResultsMessage,
|
||||||
sorting: controlledSorting,
|
sorting: controlledSorting,
|
||||||
columnVisibility: controlledColumnVisibility,
|
columnVisibility: controlledColumnVisibility,
|
||||||
@@ -106,6 +116,9 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
sticky = false,
|
sticky = false,
|
||||||
forcePagination = false,
|
forcePagination = false,
|
||||||
}: ReactTableProps<RecordData>) {
|
}: ReactTableProps<RecordData>) {
|
||||||
|
// TODO: remove when https://github.com/TanStack/table/issues/5567 gets fixed
|
||||||
|
'use no memo';
|
||||||
|
|
||||||
const [pagination, setPagination] = useState<PaginationState>({
|
const [pagination, setPagination] = useState<PaginationState>({
|
||||||
pageIndex: pageIndex ?? 0,
|
pageIndex: pageIndex ?? 0,
|
||||||
pageSize: pageSize ?? 15,
|
pageSize: pageSize ?? 15,
|
||||||
@@ -127,19 +140,7 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
controlledRowSelection ?? {},
|
controlledRowSelection ?? {},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Use props if provided (controlled mode), otherwise use internal state (uncontrolled mode)
|
// Computed values for table state - computed inline in callbacks for fresh values
|
||||||
const columnVisibility =
|
|
||||||
controlledColumnVisibility ?? internalColumnVisibility;
|
|
||||||
|
|
||||||
const columnPinning = controlledColumnPinning ?? internalColumnPinning;
|
|
||||||
const rowSelection = controlledRowSelection ?? internalRowSelection;
|
|
||||||
|
|
||||||
if (pagination.pageIndex !== pageIndex && pageIndex !== undefined) {
|
|
||||||
setPagination({
|
|
||||||
pageIndex,
|
|
||||||
pageSize: pagination.pageSize,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const navigateToPage = useNavigateToNewPage();
|
const navigateToPage = useNavigateToNewPage();
|
||||||
|
|
||||||
@@ -155,7 +156,9 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
onColumnFiltersChange: setColumnFilters,
|
onColumnFiltersChange: setColumnFilters,
|
||||||
onColumnVisibilityChange: (updater) => {
|
onColumnVisibilityChange: (updater) => {
|
||||||
if (typeof updater === 'function') {
|
if (typeof updater === 'function') {
|
||||||
const nextState = updater(columnVisibility);
|
const currentVisibility =
|
||||||
|
controlledColumnVisibility ?? internalColumnVisibility;
|
||||||
|
const nextState = updater(currentVisibility);
|
||||||
|
|
||||||
// If controlled mode (callback provided), call it
|
// If controlled mode (callback provided), call it
|
||||||
if (onColumnVisibilityChange) {
|
if (onColumnVisibilityChange) {
|
||||||
@@ -176,7 +179,8 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
},
|
},
|
||||||
onColumnPinningChange: (updater) => {
|
onColumnPinningChange: (updater) => {
|
||||||
if (typeof updater === 'function') {
|
if (typeof updater === 'function') {
|
||||||
const nextState = updater(columnPinning);
|
const currentPinning = controlledColumnPinning ?? internalColumnPinning;
|
||||||
|
const nextState = updater(currentPinning);
|
||||||
|
|
||||||
// If controlled mode (callback provided), call it
|
// If controlled mode (callback provided), call it
|
||||||
if (onColumnPinningChange) {
|
if (onColumnPinningChange) {
|
||||||
@@ -197,7 +201,8 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
},
|
},
|
||||||
onRowSelectionChange: (updater) => {
|
onRowSelectionChange: (updater) => {
|
||||||
if (typeof updater === 'function') {
|
if (typeof updater === 'function') {
|
||||||
const nextState = updater(rowSelection);
|
const currentSelection = controlledRowSelection ?? internalRowSelection;
|
||||||
|
const nextState = updater(currentSelection);
|
||||||
|
|
||||||
// If controlled mode (callback provided), call it
|
// If controlled mode (callback provided), call it
|
||||||
if (onRowSelectionChange) {
|
if (onRowSelectionChange) {
|
||||||
@@ -221,9 +226,9 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
pagination,
|
pagination,
|
||||||
sorting,
|
sorting,
|
||||||
columnFilters,
|
columnFilters,
|
||||||
columnVisibility,
|
columnVisibility: controlledColumnVisibility ?? internalColumnVisibility,
|
||||||
columnPinning,
|
columnPinning: controlledColumnPinning ?? internalColumnPinning,
|
||||||
rowSelection,
|
rowSelection: controlledRowSelection ?? internalRowSelection,
|
||||||
},
|
},
|
||||||
onSortingChange: (updater) => {
|
onSortingChange: (updater) => {
|
||||||
if (typeof updater === 'function') {
|
if (typeof updater === 'function') {
|
||||||
@@ -269,27 +274,12 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Force table to update column pinning when controlled prop changes
|
if (pagination.pageIndex !== pageIndex && pageIndex !== undefined) {
|
||||||
useEffect(() => {
|
setPagination({
|
||||||
if (controlledColumnPinning) {
|
pageIndex,
|
||||||
// Use the table's setColumnPinning method to force an update
|
pageSize: pagination.pageSize,
|
||||||
table.setColumnPinning(controlledColumnPinning);
|
});
|
||||||
}
|
}
|
||||||
}, [controlledColumnPinning, table]);
|
|
||||||
|
|
||||||
// Force table to update column visibility when controlled prop changes
|
|
||||||
useEffect(() => {
|
|
||||||
if (controlledColumnVisibility) {
|
|
||||||
table.setColumnVisibility(controlledColumnVisibility);
|
|
||||||
}
|
|
||||||
}, [controlledColumnVisibility, table]);
|
|
||||||
|
|
||||||
// Force table to update row selection when controlled prop changes
|
|
||||||
useEffect(() => {
|
|
||||||
if (controlledRowSelection) {
|
|
||||||
table.setRowSelection(controlledRowSelection);
|
|
||||||
}
|
|
||||||
}, [controlledRowSelection, table]);
|
|
||||||
|
|
||||||
const rows = table.getRowModel().rows;
|
const rows = table.getRowModel().rows;
|
||||||
|
|
||||||
@@ -302,7 +292,7 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
data-testid="data-table"
|
data-testid="data-table"
|
||||||
{...tableProps}
|
{...tableProps}
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-background border-separate border-spacing-0',
|
'bg-background border-collapse border-spacing-0',
|
||||||
className,
|
className,
|
||||||
{
|
{
|
||||||
'h-full': data.length === 0,
|
'h-full': data.length === 0,
|
||||||
@@ -310,8 +300,8 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<TableHeader
|
<TableHeader
|
||||||
className={cn('', {
|
className={cn(headerClassName, {
|
||||||
['bg-background/20 outline-border sticky top-[0px] z-10 outline backdrop-blur-sm transition-all duration-300']:
|
['bg-background/20 outline-border sticky top-[0px] z-10 outline backdrop-blur-sm']:
|
||||||
sticky,
|
sticky,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
@@ -344,10 +334,10 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
className={cn(
|
className={cn(
|
||||||
'text-muted-foreground bg-background/80 border-transparent font-sans font-medium',
|
'text-muted-foreground bg-background/80 border-transparent font-sans font-medium',
|
||||||
{
|
{
|
||||||
['border-r-background sticky top-0 z-10 border-r opacity-95 backdrop-blur-sm']:
|
['border-r-background border-r']: isPinned === 'left',
|
||||||
isPinned === 'left',
|
['border-l-background border-l']: isPinned === 'right',
|
||||||
['border-l-background sticky top-0 z-10 border-l opacity-95 backdrop-blur-sm']:
|
['sticky top-0 z-10 opacity-95 backdrop-blur-sm']:
|
||||||
isPinned === 'right',
|
isPinned,
|
||||||
['relative z-0']: !isPinned,
|
['relative z-0']: !isPinned,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
@@ -375,9 +365,7 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
|
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{rows.map((row) => {
|
{rows.map((row) => {
|
||||||
const RowWrapper = renderRow
|
const RowWrapper = renderRow ? renderRow({ row }) : TableRow;
|
||||||
? renderRow({ row, onClick })
|
|
||||||
: TableRow;
|
|
||||||
|
|
||||||
const children = row.getVisibleCells().map((cell, index) => {
|
const children = row.getVisibleCells().map((cell, index) => {
|
||||||
const isPinned = cell.column.getIsPinned();
|
const isPinned = cell.column.getIsPinned();
|
||||||
@@ -417,16 +405,23 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
const style = {
|
||||||
|
width: `${size}px`,
|
||||||
|
minWidth: `${size}px`,
|
||||||
|
left: left !== undefined ? `${left}px` : undefined,
|
||||||
|
right: right !== undefined ? `${right}px` : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return renderCell ? (
|
||||||
|
<Fragment key={cell.id}>
|
||||||
|
{renderCell({ cell, style, className })({})}
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
<TableCell
|
<TableCell
|
||||||
style={{
|
|
||||||
left: left !== undefined ? `${left}px` : undefined,
|
|
||||||
right: right !== undefined ? `${right}px` : undefined,
|
|
||||||
width: `${size}px`,
|
|
||||||
minWidth: `${size}px`,
|
|
||||||
}}
|
|
||||||
key={cell.id}
|
key={cell.id}
|
||||||
|
style={style}
|
||||||
className={className}
|
className={className}
|
||||||
|
onClick={onClick ? () => onClick({ row, cell }) : undefined}
|
||||||
>
|
>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -435,11 +430,12 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RowWrapper
|
<RowWrapper
|
||||||
className={cn('active:bg-accent bg-background/80', {
|
|
||||||
['hover:bg-accent/60 cursor-pointer']: !row.getIsSelected(),
|
|
||||||
})}
|
|
||||||
onClick={() => onClick && onClick(row)}
|
|
||||||
key={row.id}
|
key={row.id}
|
||||||
|
className={cn('bg-background/80', {
|
||||||
|
'hover:bg-accent/60': !row.getIsSelected(),
|
||||||
|
'active:bg-accent': !!onClick,
|
||||||
|
'cursor-pointer': !!onClick && !row.getIsSelected(),
|
||||||
|
})}
|
||||||
data-state={row.getIsSelected() && 'selected'}
|
data-state={row.getIsSelected() && 'selected'}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -460,15 +456,22 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
<If condition={displayPagination}>
|
<If condition={displayPagination}>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-background/80 outline-border sticky bottom-0 z-10 border-b outline backdrop-blur-sm',
|
'bg-background/80 sticky bottom-0 z-10 border-t backdrop-blur-sm',
|
||||||
{
|
{
|
||||||
['sticky bottom-0 z-10 max-w-full rounded-none']: sticky,
|
['sticky bottom-0 z-10 max-w-full rounded-none']: sticky,
|
||||||
},
|
},
|
||||||
|
footerClassName,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className={'px-2.5 py-1.5'}>
|
<div className={'px-2.5 py-1.5'}>
|
||||||
<Pagination table={table} />
|
<Pagination
|
||||||
|
table={table}
|
||||||
|
pageSize={pageSize}
|
||||||
|
totalCount={
|
||||||
|
pageCount && pageSize ? pageCount * pageSize : undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -479,8 +482,12 @@ export function DataTable<RecordData extends DataItem>({
|
|||||||
|
|
||||||
function Pagination<T>({
|
function Pagination<T>({
|
||||||
table,
|
table,
|
||||||
|
totalCount,
|
||||||
|
pageSize,
|
||||||
}: React.PropsWithChildren<{
|
}: React.PropsWithChildren<{
|
||||||
table: ReactTable<T>;
|
table: ReactTable<T>;
|
||||||
|
totalCount?: number;
|
||||||
|
pageSize?: number;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
@@ -539,6 +546,15 @@ function Pagination<T>({
|
|||||||
<ChevronsRight className={'h-4'} />
|
<ChevronsRight className={'h-4'} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<If condition={totalCount}>
|
||||||
|
<span className="text-muted-foreground flex items-center text-xs">
|
||||||
|
<Trans
|
||||||
|
i18nKey={'common:showingRecordCount'}
|
||||||
|
values={{ totalCount, pageSize }}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</If>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function DataTable<TData, TValue>({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-md border">
|
<div className="rounded-md">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
|||||||
1488
pnpm-lock.yaml
generated
1488
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ export default tsEsLint.config(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
rules: {
|
rules: {
|
||||||
|
'@typescript-eslint/triple-slash-reference': 'off',
|
||||||
'react/react-in-jsx-scope': 'off',
|
'react/react-in-jsx-scope': 'off',
|
||||||
'import/no-anonymous-default-export': 'off',
|
'import/no-anonymous-default-export': 'off',
|
||||||
'import/named': 'off',
|
'import/named': 'off',
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"eslint": "^9.33.0",
|
"eslint": "^9.34.0",
|
||||||
"typescript": "^5.9.2"
|
"typescript": "^5.9.2"
|
||||||
},
|
},
|
||||||
"prettier": "@kit/prettier-config"
|
"prettier": "@kit/prettier-config"
|
||||||
|
|||||||
Reference in New Issue
Block a user