diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 12e467f27..3a776a090 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,6 +1,7 @@ import { headers } from 'next/headers'; import { Toaster } from '@kit/ui/sonner'; +import { cn } from '@kit/ui/utils'; import { RootProviders } from '~/components/root-providers'; import { getFontsClassName } from '~/lib/fonts'; @@ -19,13 +20,17 @@ export default async function RootLayout({ }: { children: React.ReactNode; }) { - const { language } = await createI18nServerInstance(); - const theme = await getRootTheme(); - const className = getFontsClassName(theme); - const nonce = await getCspNonce(); + const [theme, nonce, i18n] = await Promise.all([ + getRootTheme(), + getCspNonce(), + createI18nServerInstance(), + ]); + + const className = getRootClassName(theme); + const language = i18n.language; return ( - + {children} @@ -37,6 +42,15 @@ export default async function RootLayout({ ); } +function getRootClassName(theme: string) { + const fontsClassName = getFontsClassName(theme); + + return cn( + 'bg-background min-h-screen overscroll-y-none antialiased', + fontsClassName, + ); +} + async function getCspNonce() { const headersStore = await headers(); diff --git a/apps/web/lib/fonts.ts b/apps/web/lib/fonts.ts index bdf7195d0..6570c79a4 100644 --- a/apps/web/lib/fonts.ts +++ b/apps/web/lib/fonts.ts @@ -42,7 +42,7 @@ export function getFontsClassName(theme?: string) { [], ); - return cn('bg-background min-h-screen antialiased', ...font, { + return cn(...font, { dark, light, }); diff --git a/package.json b/package.json index 068dc14a5..a231f182b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-supabase-saas-kit-turbo", - "version": "2.20.0", + "version": "2.20.1", "private": true, "sideEffects": false, "engines": { diff --git a/packages/cms/core/src/content-renderer.tsx b/packages/cms/core/src/content-renderer.tsx index e8dfdbe38..01ef2be1c 100644 --- a/packages/cms/core/src/content-renderer.tsx +++ b/packages/cms/core/src/content-renderer.tsx @@ -1,4 +1,5 @@ -import type { CmsType } from '@kit/cms-types'; +import { CmsType } from '@kit/cms-types'; +import { createRegistry } from '@kit/shared/registry'; const CMS_CLIENT = process.env.CMS_CLIENT as CmsType; @@ -7,42 +8,34 @@ interface ContentRendererProps { type?: CmsType; } +// Create a registry for CMS client implementations +const cmsContentRendererRegistry = createRegistry< + React.ComponentType, + CmsType +>(); + export async function ContentRenderer({ content, type = CMS_CLIENT, }: ContentRendererProps) { - const Renderer = await getContentRenderer(type); + const Renderer = await cmsContentRendererRegistry.get(type); - return Renderer ? : null; -} - -/** - * Gets the content renderer for the specified CMS client. - * - * @param {CmsType} type - The type of CMS client. - */ -async function getContentRenderer(type: CmsType) { - switch (type) { - case 'keystatic': { - const { KeystaticContentRenderer } = await import( - '@kit/keystatic/renderer' - ); - - return KeystaticContentRenderer; - } - - case 'wordpress': { - const { WordpressContentRenderer } = await import( - '@kit/wordpress/renderer' - ); - - return WordpressContentRenderer; - } - - default: { - console.error(`Unknown CMS client: ${type as string}`); - - return null; - } + if (Renderer) { + return ; } + + // fallback to the raw content if no renderer is found + return content as React.ReactNode; } + +cmsContentRendererRegistry.register('keystatic', async () => { + const { KeystaticContentRenderer } = await import('@kit/keystatic/renderer'); + + return KeystaticContentRenderer; +}); + +cmsContentRendererRegistry.register('wordpress', async () => { + const { WordpressContentRenderer } = await import('@kit/wordpress/renderer'); + + return WordpressContentRenderer; +}); diff --git a/packages/features/auth/src/components/auth-layout.tsx b/packages/features/auth/src/components/auth-layout.tsx index 173fa5d15..3325d8b6d 100644 --- a/packages/features/auth/src/components/auth-layout.tsx +++ b/packages/features/auth/src/components/auth-layout.tsx @@ -21,7 +21,7 @@ export function AuthLayoutShell({
diff --git a/packages/features/auth/src/components/email-input.tsx b/packages/features/auth/src/components/email-input.tsx new file mode 100644 index 000000000..7440dd33b --- /dev/null +++ b/packages/features/auth/src/components/email-input.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { Mail } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +import { + InputGroup, + InputGroupAddon, + InputGroupInput, +} from '@kit/ui/input-group'; + +export function EmailInput(props: React.ComponentProps<'input'>) { + const { t } = useTranslation('auth'); + + return ( + + + + + + + + ); +} diff --git a/packages/features/auth/src/components/existing-account-hint.tsx b/packages/features/auth/src/components/existing-account-hint.tsx index 65aaa5a95..24672be78 100644 --- a/packages/features/auth/src/components/existing-account-hint.tsx +++ b/packages/features/auth/src/components/existing-account-hint.tsx @@ -70,11 +70,7 @@ export function ExistingAccountHintImpl({ return ( - + @@ -84,10 +80,7 @@ export function ExistingAccountHintImpl({ components={{ method: , signInLink: ( - + ), }} /> diff --git a/packages/features/auth/src/components/magic-link-auth-container.tsx b/packages/features/auth/src/components/magic-link-auth-container.tsx index d8f81eef3..c0d613588 100644 --- a/packages/features/auth/src/components/magic-link-auth-container.tsx +++ b/packages/features/auth/src/components/magic-link-auth-container.tsx @@ -19,12 +19,12 @@ import { FormMessage, } from '@kit/ui/form'; import { If } from '@kit/ui/if'; -import { Input } from '@kit/ui/input'; import { toast } from '@kit/ui/sonner'; import { Trans } from '@kit/ui/trans'; import { useCaptcha } from '../captcha/client'; import { useLastAuthMethod } from '../hooks/use-last-auth-method'; +import { EmailInput } from './email-input'; import { TermsAndConditionsFormField } from './terms-and-conditions-form-field'; export function MagicLinkAuthContainer({ @@ -118,13 +118,7 @@ export function MagicLinkAuthContainer({ - + diff --git a/packages/features/auth/src/components/otp-sign-in-container.tsx b/packages/features/auth/src/components/otp-sign-in-container.tsx index 3403cfed7..1e747d34d 100644 --- a/packages/features/auth/src/components/otp-sign-in-container.tsx +++ b/packages/features/auth/src/components/otp-sign-in-container.tsx @@ -17,7 +17,6 @@ import { FormItem, FormMessage, } from '@kit/ui/form'; -import { Input } from '@kit/ui/input'; import { InputOTP, InputOTPGroup, @@ -30,6 +29,7 @@ import { Trans } from '@kit/ui/trans'; import { useCaptcha } from '../captcha/client'; import { useLastAuthMethod } from '../hooks/use-last-auth-method'; import { AuthErrorAlert } from './auth-error-alert'; +import { EmailInput } from './email-input'; const EmailSchema = z.object({ email: z.string().email() }); const OtpSchema = z.object({ token: z.string().min(6).max(6) }); @@ -216,13 +216,7 @@ function OtpEmailForm({ render={({ field }) => ( - + diff --git a/packages/features/auth/src/components/password-input.tsx b/packages/features/auth/src/components/password-input.tsx new file mode 100644 index 000000000..dedf56c35 --- /dev/null +++ b/packages/features/auth/src/components/password-input.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { useState } from 'react'; + +import { Eye, EyeOff, Lock } from 'lucide-react'; + +import { Button } from '@kit/ui/button'; +import { + InputGroup, + InputGroupAddon, + InputGroupInput, +} from '@kit/ui/input-group'; + +export function PasswordInput(props: React.ComponentProps<'input'>) { + const [showPassword, setShowPassword] = useState(false); + + return ( + + + + + + + + + + + + ); +} diff --git a/packages/features/auth/src/components/password-sign-in-form.tsx b/packages/features/auth/src/components/password-sign-in-form.tsx index 51c933e73..4276f58c5 100644 --- a/packages/features/auth/src/components/password-sign-in-form.tsx +++ b/packages/features/auth/src/components/password-sign-in-form.tsx @@ -3,7 +3,7 @@ import Link from 'next/link'; import { zodResolver } from '@hookform/resolvers/zod'; -import { ArrowRight } from 'lucide-react'; +import { ArrowRight, Mail } from 'lucide-react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import type { z } from 'zod'; @@ -14,14 +14,18 @@ import { FormControl, FormField, FormItem, - FormLabel, FormMessage, } from '@kit/ui/form'; import { If } from '@kit/ui/if'; -import { Input } from '@kit/ui/input'; +import { + InputGroup, + InputGroupAddon, + InputGroupInput, +} from '@kit/ui/input-group'; import { Trans } from '@kit/ui/trans'; import { PasswordSignInSchema } from '../schemas/password-sign-in.schema'; +import { PasswordInput } from './password-input'; export function PasswordSignInForm({ onSubmit, @@ -48,67 +52,61 @@ export function PasswordSignInForm({ className={'flex w-full flex-col gap-y-4'} onSubmit={form.handleSubmit(onSubmit)} > - ( - - - - +
+ ( + + + + + + - - - + + + - - - )} - /> + + + )} + /> - ( - - - - + ( + + + + - - - + - - -
- -
-
- )} - /> +
+ +
+
+ )} + /> +