Some changes ported from the work on the makerkit.dev website related… (#89)
* Some changes ported from the work on the makerkit.dev website related to the marketing sections of the kit, such as documentation * Added slight background hue to make darker theme better looking * Support more complex configurations for documentation navigations. * Do not fetch content from Keystatic when non-needed * Add cursor pointers in dropdown * Updated packages
This commit is contained in:
committed by
GitHub
parent
a682b991f3
commit
079a8f857a
@@ -3,15 +3,15 @@
|
||||
}
|
||||
|
||||
.HTML h1 {
|
||||
@apply mt-14 text-4xl font-semibold font-heading tracking-tight ;
|
||||
@apply mt-14 text-4xl font-semibold font-heading tracking-tight;
|
||||
}
|
||||
|
||||
.HTML h2 {
|
||||
@apply mb-4 mt-12 font-semibold text-2xl font-heading tracking-tight;
|
||||
@apply mb-6 mt-12 font-semibold text-2xl font-heading tracking-tight;
|
||||
}
|
||||
|
||||
.HTML h3 {
|
||||
@apply mt-10 text-xl font-semibold font-heading tracking-tight;
|
||||
@apply mt-12 text-xl font-semibold font-heading tracking-tight;
|
||||
}
|
||||
|
||||
.HTML h4 {
|
||||
@@ -41,11 +41,11 @@ For more info: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomm
|
||||
}
|
||||
|
||||
.HTML p {
|
||||
@apply mb-4 mt-2 text-base leading-7;
|
||||
@apply mb-6 mt-4 text-base leading-7 text-muted-foreground;
|
||||
}
|
||||
|
||||
.HTML li {
|
||||
@apply relative my-1.5 text-base leading-7;
|
||||
@apply relative my-1.5 text-base leading-7 text-muted-foreground;
|
||||
}
|
||||
|
||||
.HTML ul > li:before {
|
||||
@@ -55,7 +55,7 @@ For more info: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomm
|
||||
}
|
||||
|
||||
.HTML ol > li:before {
|
||||
@apply inline-flex font-medium;
|
||||
@apply inline-flex font-medium text-muted-foreground;
|
||||
|
||||
content: counters(counts, '.') '. ';
|
||||
font-feature-settings: 'tnum';
|
||||
@@ -63,7 +63,7 @@ For more info: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomm
|
||||
|
||||
.HTML b,
|
||||
.HTML strong {
|
||||
@apply font-bold;
|
||||
@apply font-semibold text-secondary-foreground;
|
||||
}
|
||||
|
||||
:global(.dark) .HTML b,
|
||||
@@ -92,10 +92,35 @@ For more info: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomm
|
||||
font-feature-settings: 'tnum';
|
||||
}
|
||||
|
||||
.HTML p > code, .HTML li > code {
|
||||
@apply p-0.5 text-sm font-semibold bg-muted/50 border font-mono text-secondary-foreground;
|
||||
}
|
||||
|
||||
.HTML blockquote {
|
||||
@apply my-4 border-l-8 border border-primary px-6 py-4 text-lg font-medium text-muted-foreground;
|
||||
}
|
||||
|
||||
.HTML pre {
|
||||
@apply my-6 text-sm text-current border p-6 rounded-lg overflow-x-scroll;
|
||||
.HTML a {
|
||||
@apply border-b-black border-b hover:border-b-2 pb-0.5 text-secondary-foreground font-semibold;
|
||||
}
|
||||
|
||||
:global(.dark) .HTML a {
|
||||
@apply border-yellow-300;
|
||||
}
|
||||
|
||||
.HTML hr {
|
||||
@apply mt-8 mb-6 border-border;
|
||||
}
|
||||
|
||||
.HTML [role='alert'] {
|
||||
@apply py-4 m-0 my-8;
|
||||
}
|
||||
|
||||
.HTML [role='alert'] * {
|
||||
color: inherit;
|
||||
@apply m-0 p-0 text-sm;
|
||||
}
|
||||
|
||||
.HTML [role='alert'] h5 {
|
||||
color: inherit;
|
||||
}
|
||||
@@ -37,6 +37,7 @@ const getContentItems = cache(
|
||||
limit,
|
||||
offset,
|
||||
language,
|
||||
content: false,
|
||||
sortBy: 'publishedAt',
|
||||
sortDirection: 'desc',
|
||||
});
|
||||
|
||||
@@ -61,13 +61,15 @@ async function DocumentationPage({ params }: DocumentationPageProps) {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={'flex flex-1 flex-col space-y-4'}>
|
||||
<div className={'flex'}>
|
||||
<article className={cn(styles.HTML, 'container space-y-12')}>
|
||||
<div className={'flex flex-1 flex-col space-y-4 overflow-y-hidden'}>
|
||||
<div className={'flex overflow-y-hidden'}>
|
||||
<article
|
||||
className={cn(styles.HTML, 'container space-y-12 overflow-y-auto')}
|
||||
>
|
||||
<section className={'flex flex-col space-y-4 pt-6'}>
|
||||
<h1 className={'!my-0'}>{page.title}</h1>
|
||||
|
||||
<h2 className={'!mb-0 !font-normal text-muted-foreground'}>
|
||||
<h2 className={'!mb-0 !font-normal !text-muted-foreground'}>
|
||||
{description}
|
||||
</h2>
|
||||
</section>
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import { useRef } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { SidebarMenuButton, SidebarMenuItem } from '@kit/ui/shadcn-sidebar';
|
||||
import { cn, isRouteActive } from '@kit/ui/utils';
|
||||
|
||||
export function DocsNavLink({ label, url }: { label: string; url: string }) {
|
||||
export function DocsNavLink({
|
||||
label,
|
||||
url,
|
||||
children,
|
||||
}: React.PropsWithChildren<{ label: string; url: string }>) {
|
||||
const currentPath = usePathname();
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
const isCurrent = isRouteActive(url, currentPath, true);
|
||||
|
||||
return (
|
||||
@@ -15,10 +22,16 @@ export function DocsNavLink({ label, url }: { label: string; url: string }) {
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
isActive={isCurrent}
|
||||
className={cn('border-l-3 transition-background !font-normal')}
|
||||
className={cn('border-l-3 transition-background !font-normal', {
|
||||
'font-bold text-secondary-foreground': isCurrent,
|
||||
})}
|
||||
>
|
||||
<Link href={url}>
|
||||
<span className="block max-w-full truncate">{label}</span>
|
||||
<span ref={ref} className="block max-w-full truncate">
|
||||
{label}
|
||||
</span>
|
||||
|
||||
{children}
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
'use client';
|
||||
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { Cms } from '@kit/cms';
|
||||
import { Collapsible } from '@kit/ui/collapsible';
|
||||
import { isRouteActive } from '@kit/ui/utils';
|
||||
|
||||
export function DocsNavigationCollapsible(
|
||||
props: React.PropsWithChildren<{
|
||||
node: Cms.ContentItem;
|
||||
prefix: string;
|
||||
}>,
|
||||
) {
|
||||
const currentPath = usePathname();
|
||||
const prefix = props.prefix;
|
||||
|
||||
const isChildActive = props.node.children.some((child) =>
|
||||
isRouteActive(prefix + '/' + child.url, currentPath, false),
|
||||
);
|
||||
|
||||
return (
|
||||
<Collapsible
|
||||
className={'group/collapsible'}
|
||||
defaultOpen={isChildActive ? true : !props.node.collapsed}
|
||||
>
|
||||
{props.children}
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
@@ -1,66 +1,137 @@
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
|
||||
import { Cms } from '@kit/cms';
|
||||
import { CollapsibleContent, CollapsibleTrigger } from '@kit/ui/collapsible';
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuSub,
|
||||
} from '@kit/ui/shadcn-sidebar';
|
||||
|
||||
import { DocsNavLink } from '~/(marketing)/docs/_components/docs-nav-link';
|
||||
import { DocsNavigationCollapsible } from '~/(marketing)/docs/_components/docs-navigation-collapsible';
|
||||
|
||||
import { FloatingDocumentationNavigation } from './floating-docs-navigation';
|
||||
|
||||
function Node({ node, level }: { node: Cms.ContentItem; level: number }) {
|
||||
const pathPrefix = `/docs`;
|
||||
const url = `${pathPrefix}/${node.slug}`;
|
||||
function Node({
|
||||
node,
|
||||
level,
|
||||
prefix,
|
||||
}: {
|
||||
node: Cms.ContentItem;
|
||||
level: number;
|
||||
prefix: string;
|
||||
}) {
|
||||
const url = `${prefix}/${node.slug}`;
|
||||
const label = node.label ? node.label : node.title;
|
||||
|
||||
const Container = (props: React.PropsWithChildren) => {
|
||||
if (node.collapsible) {
|
||||
return (
|
||||
<DocsNavigationCollapsible node={node} prefix={prefix}>
|
||||
{props.children}
|
||||
</DocsNavigationCollapsible>
|
||||
);
|
||||
}
|
||||
|
||||
return props.children;
|
||||
};
|
||||
|
||||
const ContentContainer = (props: React.PropsWithChildren) => {
|
||||
if (node.collapsible) {
|
||||
return <CollapsibleContent>{props.children}</CollapsibleContent>;
|
||||
}
|
||||
|
||||
return props.children;
|
||||
};
|
||||
|
||||
const Trigger = () => {
|
||||
if (node.collapsible) {
|
||||
return (
|
||||
<CollapsibleTrigger asChild>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton>
|
||||
{label}
|
||||
<ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</CollapsibleTrigger>
|
||||
);
|
||||
}
|
||||
|
||||
return <DocsNavLink label={label} url={url} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocsNavLink label={node.title} url={url} />
|
||||
<Container>
|
||||
<Trigger />
|
||||
|
||||
{(node.children ?? []).length > 0 && (
|
||||
<Tree pages={node.children ?? []} level={level + 1} />
|
||||
)}
|
||||
</>
|
||||
<ContentContainer>
|
||||
<Tree pages={node.children ?? []} level={level + 1} prefix={prefix} />
|
||||
</ContentContainer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
function Tree({ pages, level }: { pages: Cms.ContentItem[]; level: number }) {
|
||||
function Tree({
|
||||
pages,
|
||||
level,
|
||||
prefix,
|
||||
}: {
|
||||
pages: Cms.ContentItem[];
|
||||
level: number;
|
||||
prefix: string;
|
||||
}) {
|
||||
if (level === 0) {
|
||||
return pages.map((treeNode, index) => (
|
||||
<SidebarGroup key={index}>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
<Node key={index} node={treeNode} level={level} />
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
<Node key={index} node={treeNode} level={level} prefix={prefix} />
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarMenuSub>
|
||||
{pages.map((treeNode, index) => (
|
||||
<Node key={index} node={treeNode} level={level} />
|
||||
<Node key={index} node={treeNode} level={level} prefix={prefix} />
|
||||
))}
|
||||
</SidebarMenuSub>
|
||||
);
|
||||
}
|
||||
|
||||
export function DocsNavigation({ pages }: { pages: Cms.ContentItem[] }) {
|
||||
export function DocsNavigation({
|
||||
pages,
|
||||
prefix = '/docs',
|
||||
}: {
|
||||
pages: Cms.ContentItem[];
|
||||
prefix?: string;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Sidebar
|
||||
variant={'ghost'}
|
||||
className={'z-1 sticky max-h-full overflow-y-auto'}
|
||||
>
|
||||
<Tree pages={pages} level={0} />
|
||||
<SidebarGroup>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
<Tree pages={pages} level={0} prefix={prefix} />
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
</Sidebar>
|
||||
|
||||
<div className={'lg:hidden'}>
|
||||
<FloatingDocumentationNavigation>
|
||||
<Tree pages={pages} level={0} />
|
||||
<SidebarGroup>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
<Tree pages={pages} level={0} prefix={prefix} />
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
</FloatingDocumentationNavigation>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -18,7 +18,8 @@ async function docsLoader(language: string | undefined) {
|
||||
const data = await cms.getContentItems({
|
||||
collection: 'documentation',
|
||||
language,
|
||||
limit: 500,
|
||||
limit: Infinity,
|
||||
content: false,
|
||||
});
|
||||
|
||||
return data.items;
|
||||
|
||||
@@ -15,7 +15,7 @@ async function DocsLayout({ children }: React.PropsWithChildren) {
|
||||
return (
|
||||
<SidebarProvider
|
||||
style={{ '--sidebar-width': '20em' } as React.CSSProperties}
|
||||
className={'lg:container'}
|
||||
className={'h-[calc(100vh-72px)] overflow-y-hidden lg:container'}
|
||||
>
|
||||
<DocsNavigation pages={tree} />
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
|
||||
import { SiteFooter } from '~/(marketing)/_components/site-footer';
|
||||
import { SiteHeader } from '~/(marketing)/_components/site-header';
|
||||
import { BackgroundHue } from '~/components/background-hue';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
async function SiteLayout(props: React.PropsWithChildren) {
|
||||
@@ -17,6 +18,7 @@ async function SiteLayout(props: React.PropsWithChildren) {
|
||||
|
||||
{props.children}
|
||||
|
||||
<BackgroundHue />
|
||||
<SiteFooter />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
FeatureGrid,
|
||||
FeatureShowcase,
|
||||
FeatureShowcaseIconContainer,
|
||||
GradientSecondaryText,
|
||||
Hero,
|
||||
Pill,
|
||||
SecondaryHero,
|
||||
@@ -24,38 +23,40 @@ import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
function Home() {
|
||||
return (
|
||||
<div className={'mt-4 flex flex-col space-y-24 py-14'}>
|
||||
<Hero
|
||||
pill={
|
||||
<Pill label={'New'}>
|
||||
<span>The leading SaaS Starter Kit for ambitious developers</span>
|
||||
</Pill>
|
||||
}
|
||||
title={
|
||||
<>
|
||||
<span>The ultimate SaaS Starter</span>
|
||||
<span>for your next project</span>
|
||||
</>
|
||||
}
|
||||
subtitle={
|
||||
<span>
|
||||
Build and Ship a SaaS faster than ever before with the next-gen SaaS
|
||||
Starter Kit. Ship your SaaS in days, not months.
|
||||
</span>
|
||||
}
|
||||
cta={<MainCallToActionButton />}
|
||||
image={
|
||||
<Image
|
||||
priority
|
||||
className={
|
||||
'rounded-2xl border border-gray-200 dark:border-primary/10'
|
||||
}
|
||||
width={3558}
|
||||
height={2222}
|
||||
src={`/images/dashboard.webp`}
|
||||
alt={`App Image`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<div className={'container mx-auto'}>
|
||||
<Hero
|
||||
pill={
|
||||
<Pill label={'New'}>
|
||||
<span>The leading SaaS Starter Kit for ambitious developers</span>
|
||||
</Pill>
|
||||
}
|
||||
title={
|
||||
<>
|
||||
<span>The ultimate SaaS Starter</span>
|
||||
<span>for your next project</span>
|
||||
</>
|
||||
}
|
||||
subtitle={
|
||||
<span>
|
||||
Build and Ship a SaaS faster than ever before with the next-gen
|
||||
SaaS Starter Kit. Ship your SaaS in days, not months.
|
||||
</span>
|
||||
}
|
||||
cta={<MainCallToActionButton />}
|
||||
image={
|
||||
<Image
|
||||
priority
|
||||
className={
|
||||
'rounded-2xl border border-gray-200 dark:border-primary/10'
|
||||
}
|
||||
width={3558}
|
||||
height={2222}
|
||||
src={`/images/dashboard.webp`}
|
||||
alt={`App Image`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={'container mx-auto'}>
|
||||
<div
|
||||
@@ -68,10 +69,10 @@ function Home() {
|
||||
The ultimate SaaS Starter Kit
|
||||
</b>
|
||||
.{' '}
|
||||
<GradientSecondaryText>
|
||||
<span className="font-normal text-muted-foreground">
|
||||
Unleash your creativity and build your SaaS faster than ever
|
||||
with Makerkit.
|
||||
</GradientSecondaryText>
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
icon={
|
||||
@@ -83,9 +84,7 @@ function Home() {
|
||||
>
|
||||
<FeatureGrid>
|
||||
<FeatureCard
|
||||
className={
|
||||
'relative col-span-2 overflow-hidden bg-violet-500 text-white lg:h-96'
|
||||
}
|
||||
className={'relative col-span-2 overflow-hidden lg:h-96'}
|
||||
label={'Beautiful Dashboard'}
|
||||
description={`Makerkit provides a beautiful dashboard to manage your SaaS business.`}
|
||||
>
|
||||
@@ -155,7 +154,7 @@ function Home() {
|
||||
}
|
||||
>
|
||||
<SecondaryHero
|
||||
pill={<Pill>Get started for free. No credit card required.</Pill>}
|
||||
pill={<Pill label="Start for free">No credit card required.</Pill>}
|
||||
heading="Fair pricing for all types of businesses"
|
||||
subheading="Get started on our free plan and upgrade when you are ready."
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { AuthLayoutShell } from '@kit/auth/shared';
|
||||
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
import { BackgroundHue } from '~/components/background-hue';
|
||||
|
||||
function AuthLayout({ children }: React.PropsWithChildren) {
|
||||
return <AuthLayoutShell Logo={AppLogo}>{children}</AuthLayoutShell>;
|
||||
return (
|
||||
<AuthLayoutShell Logo={AppLogo}>
|
||||
{children}
|
||||
|
||||
<BackgroundHue />
|
||||
</AuthLayoutShell>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthLayout;
|
||||
|
||||
@@ -54,6 +54,7 @@ async function getContentItems() {
|
||||
const posts = client
|
||||
.getContentItems({
|
||||
collection: 'posts',
|
||||
content: false,
|
||||
limit,
|
||||
})
|
||||
.then((response) => response.items)
|
||||
@@ -69,6 +70,7 @@ async function getContentItems() {
|
||||
const docs = client
|
||||
.getContentItems({
|
||||
collection: 'documentation',
|
||||
content: false,
|
||||
limit,
|
||||
})
|
||||
.then((response) => response.items)
|
||||
|
||||
@@ -31,10 +31,14 @@ export function AppLogo({
|
||||
label,
|
||||
className,
|
||||
}: {
|
||||
href?: string;
|
||||
href?: string | null;
|
||||
className?: string;
|
||||
label?: string;
|
||||
}) {
|
||||
if (href === null) {
|
||||
return <LogoImage className={className} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link aria-label={label ?? 'Home Page'} href={href ?? '/'}>
|
||||
<LogoImage className={className} />
|
||||
|
||||
52
apps/web/components/background-hue.tsx
Normal file
52
apps/web/components/background-hue.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
const DEFAULT_COLORS_SCALE = {
|
||||
0: 'hsl(var(--primary))',
|
||||
1: 'hsl(var(--primary))',
|
||||
2: 'hsl(var(--primary))',
|
||||
};
|
||||
|
||||
export function BackgroundHue({
|
||||
className,
|
||||
opacity = 0.03,
|
||||
colorsScale = DEFAULT_COLORS_SCALE,
|
||||
}: {
|
||||
className?: string;
|
||||
opacity?: number;
|
||||
colorsScale?: Record<number, string>;
|
||||
}) {
|
||||
const colors = Object.values(colorsScale).map((color, index, array) => {
|
||||
const offset = array.length > 1 ? index / (array.length - 1) : 0;
|
||||
const stopOpacity = 1 - index / (array.length - 1);
|
||||
|
||||
return (
|
||||
<stop
|
||||
offset={offset}
|
||||
key={index}
|
||||
style={{ stopColor: color, stopOpacity }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<svg
|
||||
className={`pointer-events-none fixed left-0 top-0 !m-0 hidden h-full w-full dark:block ${className}`}
|
||||
style={{ opacity }}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 100 100"
|
||||
preserveAspectRatio="none"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="purpleGradient"
|
||||
x1="10%"
|
||||
y1="70%"
|
||||
x2="50%"
|
||||
y2="20%"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
{colors}
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="100" height="100" fill="url(#purpleGradient)" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -55,8 +55,8 @@
|
||||
"@makerkit/data-loader-supabase-nextjs": "^1.2.3",
|
||||
"@marsidev/react-turnstile": "^1.1.0",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"date-fns": "^4.1.0",
|
||||
"lucide-react": "^0.468.0",
|
||||
@@ -65,10 +65,10 @@
|
||||
"next-themes": "0.4.4",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hook-form": "^7.54.0",
|
||||
"react-i18next": "^15.1.3",
|
||||
"recharts": "2.14.1",
|
||||
"sonner": "^1.7.0",
|
||||
"sonner": "^1.7.1",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user