Next.js Supabase V3 (#463)

Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
This commit is contained in:
Giancarlo Buomprisco
2026-03-24 13:40:38 +08:00
committed by GitHub
parent 4912e402a3
commit 7ebff31475
840 changed files with 71395 additions and 20095 deletions

View File

@@ -1,8 +1,8 @@
'use client';
import { PlusSquare } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { useLocale, useTranslations } from 'next-intl';
import * as z from 'zod';
import type { LineItemSchema } from '@kit/billing';
import { formatCurrency } from '@kit/shared/utils';
@@ -14,14 +14,14 @@ const className = 'flex text-secondary-foreground items-center text-sm';
export function LineItemDetails(
props: React.PropsWithChildren<{
lineItems: z.infer<typeof LineItemSchema>[];
lineItems: z.output<typeof LineItemSchema>[];
currency: string;
selectedInterval?: string | undefined;
alwaysDisplayMonthlyPrice?: boolean;
}>,
) {
const { t, i18n } = useTranslation();
const locale = i18n.language;
const t = useTranslations('billing');
const locale = useLocale();
const currencyCode = props?.currency.toLowerCase();
const shouldDisplayMonthlyPrice =
@@ -32,16 +32,16 @@ export function LineItemDetails(
return '';
}
const i18nKey = `billing:units.${unit}`;
const i18nKey = `units.${unit}` as never;
if (!i18n.exists(i18nKey)) {
if (!t.has(i18nKey)) {
return unit;
}
return t(i18nKey, {
count,
defaultValue: unit,
});
} as never);
};
const getDisplayCost = (cost: number, hasTiers: boolean) => {
@@ -82,7 +82,7 @@ export function LineItemDetails(
<span>
<Trans
i18nKey={'billing:setupFee'}
i18nKey={'billing.setupFee'}
values={{
setupFee: formatCurrency({
currencyCode,
@@ -111,18 +111,18 @@ export function LineItemDetails(
<PlusSquare className={'w-3'} />
<span>
<Trans i18nKey={'billing:basePlan'} />
<Trans i18nKey={'billing.basePlan'} />
</span>
</span>
<span>
<If
condition={props.selectedInterval}
fallback={<Trans i18nKey={'billing:lifetime'} />}
fallback={<Trans i18nKey={'billing.lifetime'} />}
>
(
<Trans
i18nKey={`billing:billingInterval.${props.selectedInterval}`}
i18nKey={`billing.billingInterval.${props.selectedInterval}`}
/>
)
</If>
@@ -149,7 +149,7 @@ export function LineItemDetails(
<span className={'flex gap-x-2 text-sm'}>
<span>
<Trans
i18nKey={'billing:perUnit'}
i18nKey={'billing.perUnit'}
values={{
unit: getUnitLabel(unit, 1),
}}
@@ -172,10 +172,10 @@ export function LineItemDetails(
<span>
<If
condition={Boolean(unit) && !isDefaultSeatUnit}
fallback={<Trans i18nKey={'billing:perTeamMember'} />}
fallback={<Trans i18nKey={'billing.perTeamMember'} />}
>
<Trans
i18nKey={'billing:perUnitShort'}
i18nKey={'billing.perUnitShort'}
values={{
unit: getUnitLabel(unit, 1),
}}
@@ -215,7 +215,7 @@ export function LineItemDetails(
<span className={'flex space-x-1'}>
<span>
<Trans
i18nKey={'billing:perUnit'}
i18nKey={'billing.perUnit'}
values={{
unit: getUnitLabel(unit, 1),
}}
@@ -268,11 +268,11 @@ function Tiers({
unit,
}: {
currency: string;
item: z.infer<typeof LineItemSchema>;
unit?: string;
item: z.output<typeof LineItemSchema>;
}) {
const { t, i18n } = useTranslation();
const locale = i18n.language;
const t = useTranslations('billing');
const locale = useLocale();
// Helper to safely convert tier values to numbers for pluralization
// Falls back to plural form (2) for 'unlimited' values
@@ -285,10 +285,13 @@ function Tiers({
const getUnitLabel = (count: number) => {
if (!unit) return '';
return t(`billing:units.${unit}`, {
count,
defaultValue: unit,
});
return t(
`units.${unit}` as never,
{
count,
defaultValue: unit,
} as never,
);
};
const tiers = item.tiers?.map((tier, index) => {
@@ -327,7 +330,7 @@ function Tiers({
<If condition={tiersLength > 1}>
<span>
<Trans
i18nKey={'billing:andAbove'}
i18nKey={'billing.andAbove'}
values={{
unit: getUnitLabel(getSafeCount(previousTierFrom) - 1),
previousTier: getSafeCount(previousTierFrom) - 1,
@@ -338,7 +341,7 @@ function Tiers({
<If condition={tiersLength === 1}>
<span>
<Trans
i18nKey={'billing:forEveryUnit'}
i18nKey={'billing.forEveryUnit'}
values={{
unit: getUnitLabel(1),
}}
@@ -350,7 +353,7 @@ function Tiers({
<If condition={isIncluded}>
<span>
<Trans
i18nKey={'billing:includedUpTo'}
i18nKey={'billing.includedUpTo'}
values={{
unit: getUnitLabel(getSafeCount(upTo)),
upTo,
@@ -368,7 +371,7 @@ function Tiers({
</span>{' '}
<span>
<Trans
i18nKey={'billing:fromPreviousTierUpTo'}
i18nKey={'billing.fromPreviousTierUpTo'}
values={{
previousTierFrom,
unit: getUnitLabel(1),