Next.js 16, React 19.2, Identities page, Invitations identities step, PNPM Catalogs (#381)
* Upgraded to Next.js 16 * Refactored code to comply with React 19.2 ESLint rules * Refactored some useEffect usages with the new useEffectEvent * Added Identities page and added second step to set up an identity after accepting an invitation * Updated all dependencies * Introduced PNPM catalogs for some frequently updated dependencies * Bugs fixing and improvements
This commit is contained in:
committed by
GitHub
parent
ea0c1dde80
commit
2c0d0bf7a1
@@ -26,14 +26,14 @@
|
||||
"@kit/supabase": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@supabase/supabase-js": "2.58.0",
|
||||
"@types/react": "19.1.16",
|
||||
"@supabase/supabase-js": "2.76.1",
|
||||
"@types/react": "catalog:",
|
||||
"date-fns": "^4.1.0",
|
||||
"lucide-react": "^0.544.0",
|
||||
"next": "15.5.5",
|
||||
"react": "19.1.1",
|
||||
"react-hook-form": "^7.63.0",
|
||||
"react-i18next": "^16.0.0",
|
||||
"lucide-react": "^0.546.0",
|
||||
"next": "16.0.0",
|
||||
"react": "19.2.0",
|
||||
"react-hook-form": "^7.65.0",
|
||||
"react-i18next": "^16.1.4",
|
||||
"zod": "^3.25.74"
|
||||
},
|
||||
"typesVersions": {
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
import { Suspense, forwardRef, lazy, memo, useMemo } from 'react';
|
||||
import { Suspense, lazy } from 'react';
|
||||
|
||||
import { Enums } from '@kit/supabase/database';
|
||||
import { LoadingOverlay } from '@kit/ui/loading-overlay';
|
||||
|
||||
type BillingProvider = Enums<'billing_provider'>;
|
||||
|
||||
const Fallback = <LoadingOverlay fullPage={false} />;
|
||||
// Create lazy components at module level (not during render)
|
||||
const StripeCheckoutLazy = lazy(async () => {
|
||||
const { StripeCheckout } = await import('@kit/stripe/components');
|
||||
return { default: StripeCheckout };
|
||||
});
|
||||
|
||||
const LemonSqueezyCheckoutLazy = lazy(async () => {
|
||||
const { LemonSqueezyEmbeddedCheckout } = await import(
|
||||
'@kit/lemon-squeezy/components'
|
||||
);
|
||||
return { default: LemonSqueezyEmbeddedCheckout };
|
||||
});
|
||||
|
||||
type CheckoutProps = {
|
||||
onClose: (() => unknown) | undefined;
|
||||
checkoutToken: string;
|
||||
};
|
||||
|
||||
export function EmbeddedCheckout(
|
||||
props: React.PropsWithChildren<{
|
||||
@@ -14,100 +30,54 @@ export function EmbeddedCheckout(
|
||||
onClose?: () => void;
|
||||
}>,
|
||||
) {
|
||||
const CheckoutComponent = useMemo(
|
||||
() => loadCheckoutComponent(props.provider),
|
||||
[props.provider],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CheckoutComponent
|
||||
onClose={props.onClose}
|
||||
checkoutToken={props.checkoutToken}
|
||||
/>
|
||||
<Suspense fallback={<LoadingOverlay fullPage={false} />}>
|
||||
<CheckoutSelector
|
||||
provider={props.provider}
|
||||
onClose={props.onClose}
|
||||
checkoutToken={props.checkoutToken}
|
||||
/>
|
||||
</Suspense>
|
||||
|
||||
<BlurryBackdrop />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function loadCheckoutComponent(provider: BillingProvider) {
|
||||
switch (provider) {
|
||||
case 'stripe': {
|
||||
return buildLazyComponent(() => {
|
||||
return import('@kit/stripe/components').then(({ StripeCheckout }) => {
|
||||
return {
|
||||
default: StripeCheckout,
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
case 'lemon-squeezy': {
|
||||
return buildLazyComponent(() => {
|
||||
return import('@kit/lemon-squeezy/components').then(
|
||||
({ LemonSqueezyEmbeddedCheckout }) => {
|
||||
return {
|
||||
default: LemonSqueezyEmbeddedCheckout,
|
||||
};
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
case 'paddle': {
|
||||
throw new Error('Paddle is not yet supported');
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported provider: ${provider as string}`);
|
||||
}
|
||||
}
|
||||
|
||||
function buildLazyComponent<
|
||||
Component extends React.ComponentType<{
|
||||
onClose: (() => unknown) | undefined;
|
||||
checkoutToken: string;
|
||||
}>,
|
||||
>(
|
||||
load: () => Promise<{
|
||||
default: Component;
|
||||
}>,
|
||||
fallback = Fallback,
|
||||
function CheckoutSelector(
|
||||
props: CheckoutProps & { provider: BillingProvider },
|
||||
) {
|
||||
let LoadedComponent: ReturnType<typeof lazy<Component>> | null = null;
|
||||
|
||||
const LazyComponent = forwardRef<
|
||||
React.ElementRef<'div'>,
|
||||
{
|
||||
onClose: (() => unknown) | undefined;
|
||||
checkoutToken: string;
|
||||
}
|
||||
>(function LazyDynamicComponent(props, ref) {
|
||||
if (!LoadedComponent) {
|
||||
LoadedComponent = lazy(load);
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={fallback}>
|
||||
{/* @ts-expect-error: weird TS */}
|
||||
<LoadedComponent
|
||||
switch (props.provider) {
|
||||
case 'stripe':
|
||||
return (
|
||||
<StripeCheckoutLazy
|
||||
onClose={props.onClose}
|
||||
checkoutToken={props.checkoutToken}
|
||||
ref={ref}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
});
|
||||
);
|
||||
|
||||
return memo(LazyComponent);
|
||||
case 'lemon-squeezy':
|
||||
return (
|
||||
<LemonSqueezyCheckoutLazy
|
||||
onClose={props.onClose}
|
||||
checkoutToken={props.checkoutToken}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'paddle':
|
||||
throw new Error('Paddle is not yet supported');
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported provider: ${props.provider as string}`);
|
||||
}
|
||||
}
|
||||
|
||||
function BlurryBackdrop() {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'bg-background/30 fixed left-0 top-0 w-full backdrop-blur-sm' +
|
||||
'bg-background/30 fixed top-0 left-0 w-full backdrop-blur-sm' +
|
||||
' !m-0 h-full'
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -316,7 +316,7 @@ export function PlanPicker(
|
||||
|
||||
<div
|
||||
className={
|
||||
'flex flex-col gap-y-3 lg:flex-row lg:items-center lg:space-x-4 lg:space-y-0 lg:text-right'
|
||||
'flex flex-col gap-y-3 lg:flex-row lg:items-center lg:space-y-0 lg:space-x-4 lg:text-right'
|
||||
}
|
||||
>
|
||||
<div>
|
||||
@@ -415,6 +415,7 @@ function PlanDetails({
|
||||
const isRecurring = selectedPlan.paymentType === 'recurring';
|
||||
|
||||
// trick to force animation on re-render
|
||||
// eslint-disable-next-line react-hooks/purity
|
||||
const key = Math.random();
|
||||
|
||||
return (
|
||||
|
||||
@@ -422,7 +422,7 @@ function PlanIntervalSwitcher(
|
||||
const selected = plan === props.interval;
|
||||
|
||||
const className = cn(
|
||||
'animate-in fade-in !outline-hidden rounded-full transition-all focus:!ring-0',
|
||||
'animate-in fade-in rounded-full !outline-hidden transition-all focus:!ring-0',
|
||||
{
|
||||
'border-r-transparent': index === 0,
|
||||
['hover:text-primary text-muted-foreground']: !selected,
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
"@kit/supabase": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@types/react": "19.1.16",
|
||||
"next": "15.5.5",
|
||||
"react": "19.1.1",
|
||||
"@types/react": "catalog:",
|
||||
"next": "16.0.0",
|
||||
"react": "19.2.0",
|
||||
"zod": "^3.25.74"
|
||||
},
|
||||
"typesVersions": {
|
||||
|
||||
@@ -56,8 +56,6 @@ export class LemonSqueezyBillingStrategyService
|
||||
const { data: response, error } = await createLemonSqueezyCheckout(params);
|
||||
|
||||
if (error ?? !response?.data.id) {
|
||||
console.log(error);
|
||||
|
||||
logger.error(
|
||||
{
|
||||
...ctx,
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
"./components": "./src/components/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stripe/react-stripe-js": "^5.0.0",
|
||||
"@stripe/stripe-js": "^8.0.0",
|
||||
"stripe": "^19.0.0"
|
||||
"@stripe/react-stripe-js": "^5.2.0",
|
||||
"@stripe/stripe-js": "^8.1.0",
|
||||
"stripe": "^19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/billing": "workspace:*",
|
||||
@@ -27,10 +27,10 @@
|
||||
"@kit/supabase": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@types/react": "19.1.16",
|
||||
"@types/react": "catalog:",
|
||||
"date-fns": "^4.1.0",
|
||||
"next": "15.5.5",
|
||||
"react": "19.1.1",
|
||||
"next": "16.0.0",
|
||||
"react": "19.2.0",
|
||||
"zod": "^3.25.74"
|
||||
},
|
||||
"typesVersions": {
|
||||
|
||||
Reference in New Issue
Block a user