chore: bump version to 2.23.13 and update dependencies (#450)
* chore: bump version to 2.23.13 and update dependencies - Updated application version from 2.23.12 to 2.23.13 in package.json. - Upgraded several dependencies including @marsidev/react-turnstile to 1.4.2, @next/bundle-analyzer to 16.1.6, @next/eslint-plugin-next to 16.1.6, and others for improved functionality and security. - Adjusted package versions in pnpm-lock.yaml and pnpm-workspace.yaml for consistency across the project. - Removed unused AI translation functionality from translations-comparison component to streamline the codebase. * refactor: clean up code formatting and update Stripe API version - Removed unnecessary blank lines in LineItemDetails component for improved readability. - Enhanced formatting in PricingItem component for better clarity. - Updated Stripe API version from '2025-12-15.clover' to '2026-01-28.clover' for compatibility with the latest features. - Adjusted i18n initialization in email templates for consistency. - Reformatted props in AdminReactivateUserDialog for better structure. - Cleaned up type imports in ImageUploadInput component.
This commit is contained in:
committed by
GitHub
parent
58f08c5f39
commit
68276fda8a
@@ -1,8 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ChevronDownIcon, Loader2Icon } from 'lucide-react';
|
import { ChevronDownIcon } from 'lucide-react';
|
||||||
import { Subject, debounceTime } from 'rxjs';
|
import { Subject, debounceTime } from 'rxjs';
|
||||||
|
|
||||||
import { Button } from '@kit/ui/button';
|
import { Button } from '@kit/ui/button';
|
||||||
@@ -32,10 +32,7 @@ import {
|
|||||||
} from '@kit/ui/table';
|
} from '@kit/ui/table';
|
||||||
import { cn } from '@kit/ui/utils';
|
import { cn } from '@kit/ui/utils';
|
||||||
|
|
||||||
import {
|
import { updateTranslationAction } from '../lib/server-actions';
|
||||||
translateWithAIAction,
|
|
||||||
updateTranslationAction,
|
|
||||||
} from '../lib/server-actions';
|
|
||||||
import type { TranslationData, Translations } from '../lib/translations-loader';
|
import type { TranslationData, Translations } from '../lib/translations-loader';
|
||||||
|
|
||||||
function flattenTranslations(
|
function flattenTranslations(
|
||||||
@@ -64,7 +61,6 @@ export function TranslationsComparison({
|
|||||||
translations: Translations;
|
translations: Translations;
|
||||||
}) {
|
}) {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [isTranslating, setIsTranslating] = useState(false);
|
|
||||||
|
|
||||||
// Create RxJS Subject for handling translation updates
|
// Create RxJS Subject for handling translation updates
|
||||||
const subject$ = useMemo(
|
const subject$ = useMemo(
|
||||||
@@ -132,60 +128,6 @@ export function TranslationsComparison({
|
|||||||
setSelectedLocales(newSelectedLocales);
|
setSelectedLocales(newSelectedLocales);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTranslateWithAI = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
setIsTranslating(true);
|
|
||||||
|
|
||||||
// Get missing translations for the selected namespace
|
|
||||||
const missingTranslations: Record<string, string> = {};
|
|
||||||
const baseTranslations = flattenedTranslations[baseLocale] ?? {};
|
|
||||||
|
|
||||||
for (const locale of visibleLocales) {
|
|
||||||
if (locale === baseLocale) continue;
|
|
||||||
|
|
||||||
const localeTranslations = flattenedTranslations[locale] ?? {};
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(baseTranslations)) {
|
|
||||||
if (!localeTranslations[key]) {
|
|
||||||
missingTranslations[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(missingTranslations).length > 0) {
|
|
||||||
await translateWithAIAction({
|
|
||||||
sourceLocale: baseLocale,
|
|
||||||
targetLocale: locale,
|
|
||||||
namespace: selectedNamespace,
|
|
||||||
translations: missingTranslations,
|
|
||||||
});
|
|
||||||
|
|
||||||
toast.success(`Translated missing strings to ${locale}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
toast.error('Failed to translate: ' + (error as Error).message);
|
|
||||||
} finally {
|
|
||||||
setIsTranslating(false);
|
|
||||||
}
|
|
||||||
}, [flattenedTranslations, baseLocale, visibleLocales, selectedNamespace]);
|
|
||||||
|
|
||||||
// Calculate if there are any missing translations
|
|
||||||
const hasMissingTranslations = useMemo(() => {
|
|
||||||
if (!flattenedTranslations || !baseLocale || !visibleLocales) return false;
|
|
||||||
|
|
||||||
const baseTranslations = flattenedTranslations[baseLocale] ?? {};
|
|
||||||
|
|
||||||
return visibleLocales.some((locale) => {
|
|
||||||
if (locale === baseLocale) return false;
|
|
||||||
|
|
||||||
const localeTranslations = flattenedTranslations[locale] ?? {};
|
|
||||||
|
|
||||||
return Object.keys(baseTranslations).some(
|
|
||||||
(key) => !localeTranslations[key],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}, [flattenedTranslations, baseLocale, visibleLocales]);
|
|
||||||
|
|
||||||
// Set up subscription to handle debounced updates
|
// Set up subscription to handle debounced updates
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = subject$.pipe(debounceTime(500)).subscribe((props) => {
|
const subscription = subject$.pipe(debounceTime(500)).subscribe((props) => {
|
||||||
@@ -262,22 +204,6 @@ export function TranslationsComparison({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
onClick={handleTranslateWithAI}
|
|
||||||
disabled={isTranslating || !hasMissingTranslations}
|
|
||||||
>
|
|
||||||
{isTranslating ? (
|
|
||||||
<>
|
|
||||||
<Loader2Icon className="mr-2 h-4 w-4 animate-spin" />
|
|
||||||
Translating...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
'Translate missing with AI'
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,10 @@
|
|||||||
|
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
|
|
||||||
import { openai } from '@ai-sdk/openai';
|
import { readFileSync, writeFileSync } from 'node:fs';
|
||||||
import { generateText } from 'ai';
|
|
||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
||||||
import { resolve } from 'node:url';
|
import { resolve } from 'node:url';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { getLogger } from '@kit/shared/logger';
|
|
||||||
|
|
||||||
const Schema = z.object({
|
const Schema = z.object({
|
||||||
locale: z.string().min(1),
|
locale: z.string().min(1),
|
||||||
namespace: z.string().min(1),
|
namespace: z.string().min(1),
|
||||||
@@ -17,13 +13,6 @@ const Schema = z.object({
|
|||||||
value: z.string(),
|
value: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const TranslateSchema = z.object({
|
|
||||||
sourceLocale: z.string(),
|
|
||||||
targetLocale: z.string(),
|
|
||||||
namespace: z.string(),
|
|
||||||
translations: z.record(z.string(), z.string()),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a translation value in the specified locale and namespace.
|
* Update a translation value in the specified locale and namespace.
|
||||||
* @param props
|
* @param props
|
||||||
@@ -70,92 +59,3 @@ export async function updateTranslationAction(props: z.infer<typeof Schema>) {
|
|||||||
throw new Error('Failed to update translation');
|
throw new Error('Failed to update translation');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const translateWithAIAction = async (
|
|
||||||
data: z.infer<typeof TranslateSchema>,
|
|
||||||
) => {
|
|
||||||
const logger = await getLogger();
|
|
||||||
|
|
||||||
z.string().min(1).parse(process.env.OPENAI_API_KEY);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { sourceLocale, targetLocale, namespace, translations } =
|
|
||||||
TranslateSchema.parse(data);
|
|
||||||
|
|
||||||
// if the path does not exist, create it using an empty object
|
|
||||||
const root = resolve(process.cwd(), '..');
|
|
||||||
const folderPath = `${root}apps/web/public/locales/${targetLocale}`;
|
|
||||||
|
|
||||||
if (!existsSync(folderPath)) {
|
|
||||||
// create the directory if it doesn't exist
|
|
||||||
mkdirSync(folderPath, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const filePath = `${folderPath}/${namespace}.json`;
|
|
||||||
|
|
||||||
if (!existsSync(filePath)) {
|
|
||||||
// create the file if it doesn't exist
|
|
||||||
writeFileSync(filePath, JSON.stringify({}, null, 2), 'utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
const results: Record<string, string> = {};
|
|
||||||
|
|
||||||
// Process translations in batches of 5 for efficiency
|
|
||||||
const entries = Object.entries(translations);
|
|
||||||
const batches = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < entries.length; i += 5) {
|
|
||||||
batches.push(entries.slice(i, i + 5));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const batch of batches) {
|
|
||||||
const batchPromises = batch.map(async ([key, value]) => {
|
|
||||||
const prompt = `Translate the following text from ${sourceLocale} to ${targetLocale}. Maintain any placeholders (like {name} or %{count}) and HTML tags. Only return the translated text, nothing else.
|
|
||||||
|
|
||||||
Original text: ${value}`;
|
|
||||||
|
|
||||||
const MODEL_NAME = process.env.LLM_MODEL_NAME ?? 'gpt-4o-mini';
|
|
||||||
const model = openai(MODEL_NAME);
|
|
||||||
|
|
||||||
const { text } = await generateText({
|
|
||||||
model,
|
|
||||||
prompt,
|
|
||||||
temperature: 0.3,
|
|
||||||
maxTokens: 200,
|
|
||||||
});
|
|
||||||
|
|
||||||
return [key, text.trim()] as [string, string];
|
|
||||||
});
|
|
||||||
|
|
||||||
const batchResults = await Promise.all(batchPromises);
|
|
||||||
|
|
||||||
for (const [key, translation] of batchResults) {
|
|
||||||
results[key] = translation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update each translation
|
|
||||||
for (const [key, translation] of Object.entries(results)) {
|
|
||||||
await updateTranslationAction({
|
|
||||||
locale: targetLocale,
|
|
||||||
namespace,
|
|
||||||
key,
|
|
||||||
value: translation,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('AI translation completed', {
|
|
||||||
sourceLocale,
|
|
||||||
targetLocale,
|
|
||||||
namespace,
|
|
||||||
count: Object.keys(results).length,
|
|
||||||
});
|
|
||||||
|
|
||||||
revalidatePath('/translations');
|
|
||||||
|
|
||||||
return { success: true, translations: results };
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('AI translation failed', { error });
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -8,11 +8,9 @@
|
|||||||
"format": "prettier --check --write \"**/*.{ts,tsx}\" --ignore-path=\"../../.prettierignore\""
|
"format": "prettier --check --write \"**/*.{ts,tsx}\" --ignore-path=\"../../.prettierignore\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/openai": "^2.0.88",
|
|
||||||
"@faker-js/faker": "^10.2.0",
|
"@faker-js/faker": "^10.2.0",
|
||||||
"@hookform/resolvers": "^5.2.2",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
"@tanstack/react-query": "catalog:",
|
"@tanstack/react-query": "catalog:",
|
||||||
"ai": "5.0.116",
|
|
||||||
"lucide-react": "catalog:",
|
"lucide-react": "catalog:",
|
||||||
"next": "catalog:",
|
"next": "catalog:",
|
||||||
"nodemailer": "catalog:",
|
"nodemailer": "catalog:",
|
||||||
|
|||||||
@@ -11,10 +11,10 @@
|
|||||||
},
|
},
|
||||||
"author": "Makerkit",
|
"author": "Makerkit",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.58.0",
|
"@playwright/test": "^1.58.1",
|
||||||
"@supabase/supabase-js": "catalog:",
|
"@supabase/supabase-js": "catalog:",
|
||||||
"@types/node": "catalog:",
|
"@types/node": "catalog:",
|
||||||
"dotenv": "17.2.3",
|
"dotenv": "17.2.4",
|
||||||
"node-html-parser": "^7.0.2",
|
"node-html-parser": "^7.0.2",
|
||||||
"totp-generator": "^2.0.1"
|
"totp-generator": "^2.0.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,14 +152,12 @@ test.describe('Admin', () => {
|
|||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// TODO: find out why we need to reload the page only in CI
|
|
||||||
await page.reload();
|
|
||||||
|
|
||||||
// Verify ban badge is removed
|
// Verify ban badge is removed
|
||||||
await expect(page.getByText('Banned')).not.toBeVisible();
|
await expect(page.getByText('Banned')).not.toBeVisible();
|
||||||
|
|
||||||
// Log out
|
// Log out
|
||||||
await page.context().clearCookies();
|
await page.context().clearCookies();
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
// Verify user can log in again
|
// Verify user can log in again
|
||||||
await page.goto('/auth/sign-in');
|
await page.goto('/auth/sign-in');
|
||||||
|
|||||||
@@ -53,8 +53,8 @@
|
|||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@makerkit/data-loader-supabase-core": "^0.0.10",
|
"@makerkit/data-loader-supabase-core": "^0.0.10",
|
||||||
"@makerkit/data-loader-supabase-nextjs": "^1.2.5",
|
"@makerkit/data-loader-supabase-nextjs": "^1.2.5",
|
||||||
"@marsidev/react-turnstile": "^1.4.1",
|
"@marsidev/react-turnstile": "^1.4.2",
|
||||||
"@nosecone/next": "1.0.0",
|
"@nosecone/next": "1.1.0",
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@supabase/supabase-js": "catalog:",
|
"@supabase/supabase-js": "catalog:",
|
||||||
"@tanstack/react-query": "catalog:",
|
"@tanstack/react-query": "catalog:",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "next-supabase-saas-kit-turbo",
|
"name": "next-supabase-saas-kit-turbo",
|
||||||
"version": "2.23.12",
|
"version": "2.23.13",
|
||||||
"private": true,
|
"private": true,
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -183,7 +183,6 @@ export function LineItemDetails(
|
|||||||
</If>
|
</If>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
||||||
<If condition={!item.tiers?.length}>
|
<If condition={!item.tiers?.length}>
|
||||||
<span>-</span>
|
<span>-</span>
|
||||||
|
|
||||||
|
|||||||
@@ -162,10 +162,12 @@ function PricingItem(
|
|||||||
const i18nKey = `billing:units.${lineItem.unit}`;
|
const i18nKey = `billing:units.${lineItem.unit}`;
|
||||||
|
|
||||||
const unitLabel = lineItem?.unit
|
const unitLabel = lineItem?.unit
|
||||||
? i18n.exists(i18nKey) ? t(i18nKey, {
|
? i18n.exists(i18nKey)
|
||||||
count: 1,
|
? t(i18nKey, {
|
||||||
defaultValue: lineItem.unit,
|
count: 1,
|
||||||
}) : lineItem.unit
|
defaultValue: lineItem.unit,
|
||||||
|
})
|
||||||
|
: lineItem.unit
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const isDefaultSeatUnit = lineItem?.unit === 'member';
|
const isDefaultSeatUnit = lineItem?.unit === 'member';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'server-only';
|
|||||||
|
|
||||||
import { StripeServerEnvSchema } from '../schema/stripe-server-env.schema';
|
import { StripeServerEnvSchema } from '../schema/stripe-server-env.schema';
|
||||||
|
|
||||||
const STRIPE_API_VERSION = '2025-12-15.clover';
|
const STRIPE_API_VERSION = '2026-01-28.clover';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns a Stripe instance
|
* @description returns a Stripe instance
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { initializeServerI18n } from '@kit/i18n/server';
|
|
||||||
import { createI18nSettings } from '@kit/i18n';
|
import { createI18nSettings } from '@kit/i18n';
|
||||||
|
import { initializeServerI18n } from '@kit/i18n/server';
|
||||||
|
|
||||||
export function initializeEmailI18n(params: {
|
export function initializeEmailI18n(params: {
|
||||||
language: string | undefined;
|
language: string | undefined;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
}) {
|
}) {
|
||||||
const language = params.language ?? process.env.NEXT_PUBLIC_DEFAULT_LOCALE ?? 'en';
|
const language =
|
||||||
|
params.language ?? process.env.NEXT_PUBLIC_DEFAULT_LOCALE ?? 'en';
|
||||||
|
|
||||||
return initializeServerI18n(
|
return initializeServerI18n(
|
||||||
createI18nSettings({
|
createI18nSettings({
|
||||||
|
|||||||
@@ -52,7 +52,10 @@ export function AdminReactivateUserDialog(
|
|||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
|
|
||||||
<ReactivateUserForm userId={props.userId} onSuccess={() => setOpen(false)} />
|
<ReactivateUserForm
|
||||||
|
userId={props.userId}
|
||||||
|
onSuccess={() => setOpen(false)}
|
||||||
|
/>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@modelcontextprotocol/sdk": "1.25.3",
|
"@modelcontextprotocol/sdk": "1.26.0",
|
||||||
"@types/node": "catalog:",
|
"@types/node": "catalog:",
|
||||||
"postgres": "3.4.8",
|
"postgres": "3.4.8",
|
||||||
"zod": "catalog:"
|
"zod": "catalog:"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"input-otp": "1.4.2",
|
"input-otp": "1.4.2",
|
||||||
"lucide-react": "catalog:",
|
"lucide-react": "catalog:",
|
||||||
"radix-ui": "1.4.3",
|
"radix-ui": "1.4.3",
|
||||||
"react-dropzone": "^14.3.8",
|
"react-dropzone": "^14.4.0",
|
||||||
"react-top-loading-bar": "3.0.2",
|
"react-top-loading-bar": "3.0.2",
|
||||||
"recharts": "2.15.3",
|
"recharts": "2.15.3",
|
||||||
"tailwind-merge": "^3.4.0"
|
"tailwind-merge": "^3.4.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import type { FormEvent, MouseEventHandler } from 'react';
|
import type { MouseEventHandler } from 'react';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
@@ -39,40 +39,41 @@ export const ImageUploadInput: React.FC<Props> =
|
|||||||
fileName: '',
|
fileName: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const onInputChange = useCallback(
|
const onInputChange: React.InputEventHandler<HTMLInputElement> =
|
||||||
(e: FormEvent<HTMLInputElement>) => {
|
useCallback(
|
||||||
e.preventDefault();
|
(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
const files = e.currentTarget.files;
|
const files = e.currentTarget.files;
|
||||||
|
|
||||||
if (files?.length) {
|
if (files?.length) {
|
||||||
const file = files[0];
|
const file = files[0];
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = URL.createObjectURL(file);
|
const data = URL.createObjectURL(file);
|
||||||
|
|
||||||
setState({
|
setState({
|
||||||
image: data,
|
|
||||||
fileName: file.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (onValueChange) {
|
|
||||||
onValueChange({
|
|
||||||
image: data,
|
image: data,
|
||||||
file,
|
fileName: file.name,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onInput) {
|
if (onValueChange) {
|
||||||
onInput(e);
|
onValueChange({
|
||||||
}
|
image: data,
|
||||||
},
|
file,
|
||||||
[onInput, onValueChange],
|
});
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onInput) {
|
||||||
|
onInput(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onInput, onValueChange],
|
||||||
|
);
|
||||||
|
|
||||||
const onRemove = useCallback(() => {
|
const onRemove = useCallback(() => {
|
||||||
setState({
|
setState({
|
||||||
|
|||||||
2461
pnpm-lock.yaml
generated
2461
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -4,36 +4,36 @@ packages:
|
|||||||
- tooling/*
|
- tooling/*
|
||||||
|
|
||||||
catalog:
|
catalog:
|
||||||
'@marsidev/react-turnstile': 1.4.1
|
'@marsidev/react-turnstile': 1.4.2
|
||||||
'@next/bundle-analyzer': 16.1.5
|
'@next/bundle-analyzer': 16.1.6
|
||||||
'@next/eslint-plugin-next': 16.1.5
|
'@next/eslint-plugin-next': 16.1.6
|
||||||
'@react-email/components': 1.0.6
|
'@react-email/components': 1.0.7
|
||||||
'@sentry/nextjs': 10.37.0
|
'@sentry/nextjs': 10.38.0
|
||||||
'@stripe/react-stripe-js': 5.5.0
|
'@stripe/react-stripe-js': 5.6.0
|
||||||
'@stripe/stripe-js': 8.6.4
|
'@stripe/stripe-js': 8.7.0
|
||||||
'@supabase/supabase-js': 2.93.1
|
'@supabase/supabase-js': 2.95.3
|
||||||
'@tailwindcss/postcss': 4.1.18
|
'@tailwindcss/postcss': 4.1.18
|
||||||
'@tanstack/react-query': 5.90.20
|
'@tanstack/react-query': 5.90.20
|
||||||
'@types/eslint': 9.6.1
|
'@types/eslint': 9.6.1
|
||||||
'@types/node': 25.0.10
|
'@types/node': 25.2.1
|
||||||
'@types/nodemailer': 7.0.9
|
'@types/nodemailer': 7.0.9
|
||||||
'@types/react': 19.2.9
|
'@types/react': 19.2.13
|
||||||
'@types/react-dom': 19.2.3
|
'@types/react-dom': 19.2.3
|
||||||
eslint: 9.39.2
|
eslint: 9.39.2
|
||||||
eslint-config-next: 16.1.5
|
eslint-config-next: 16.1.6
|
||||||
eslint-config-turbo: 2.7.6
|
eslint-config-turbo: 2.8.3
|
||||||
i18next: 25.8.0
|
i18next: 25.8.4
|
||||||
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
|
||||||
lucide-react: 0.563.0
|
lucide-react: 0.563.0
|
||||||
next: 16.1.5
|
next: 16.1.6
|
||||||
nodemailer: 7.0.12
|
nodemailer: 8.0.0
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
react-dom: 19.2.4
|
react-dom: 19.2.4
|
||||||
react-hook-form: 7.71.1
|
react-hook-form: 7.71.1
|
||||||
react-i18next: 16.5.3
|
react-i18next: 16.5.4
|
||||||
stripe: 20.2.0
|
stripe: 20.3.1
|
||||||
supabase: 2.72.8
|
supabase: 2.75.5
|
||||||
tailwindcss: 4.1.18
|
tailwindcss: 4.1.18
|
||||||
tw-animate-css: 1.4.0
|
tw-animate-css: 1.4.0
|
||||||
zod: 3.25.76
|
zod: 3.25.76
|
||||||
|
|||||||
Reference in New Issue
Block a user