Revert "Unify workspace dropdowns; Update layouts (#458)"
This reverts commit 4bc8448a1d.
This commit is contained in:
58
apps/web/app/(marketing)/_components/site-footer.tsx
Normal file
58
apps/web/app/(marketing)/_components/site-footer.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Footer } from '@kit/ui/marketing';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
import appConfig from '~/config/app.config';
|
||||
|
||||
export function SiteFooter() {
|
||||
return (
|
||||
<Footer
|
||||
logo={<AppLogo className="w-[85px] md:w-[95px]" />}
|
||||
description={<Trans i18nKey="marketing:footerDescription" />}
|
||||
copyright={
|
||||
<Trans
|
||||
i18nKey="marketing:copyright"
|
||||
values={{
|
||||
product: appConfig.name,
|
||||
year: new Date().getFullYear(),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
sections={[
|
||||
{
|
||||
heading: <Trans i18nKey="marketing:about" />,
|
||||
links: [
|
||||
{ href: '/blog', label: <Trans i18nKey="marketing:blog" /> },
|
||||
{ href: '/contact', label: <Trans i18nKey="marketing:contact" /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: <Trans i18nKey="marketing:product" />,
|
||||
links: [
|
||||
{
|
||||
href: '/docs',
|
||||
label: <Trans i18nKey="marketing:documentation" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: <Trans i18nKey="marketing:legal" />,
|
||||
links: [
|
||||
{
|
||||
href: '/terms-of-service',
|
||||
label: <Trans i18nKey="marketing:termsOfService" />,
|
||||
},
|
||||
{
|
||||
href: '/privacy-policy',
|
||||
label: <Trans i18nKey="marketing:privacyPolicy" />,
|
||||
},
|
||||
{
|
||||
href: '/cookie-policy',
|
||||
label: <Trans i18nKey="marketing:cookiePolicy" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
'use client';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { PersonalAccountDropdown } from '@kit/accounts/personal-account-dropdown';
|
||||
import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
|
||||
import { JWTUserData } from '@kit/supabase/types';
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import featuresFlagConfig from '~/config/feature-flags.config';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
|
||||
const ModeToggle = dynamic(
|
||||
() =>
|
||||
import('@kit/ui/mode-toggle').then((mod) => ({
|
||||
default: mod.ModeToggle,
|
||||
})),
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
const MobileModeToggle = dynamic(
|
||||
() =>
|
||||
import('@kit/ui/mobile-mode-toggle').then((mod) => ({
|
||||
default: mod.MobileModeToggle,
|
||||
})),
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
const paths = {
|
||||
home: pathsConfig.app.home,
|
||||
};
|
||||
|
||||
const features = {
|
||||
enableThemeToggle: featuresFlagConfig.enableThemeToggle,
|
||||
};
|
||||
|
||||
export function SiteHeaderAccountSection({
|
||||
user,
|
||||
}: {
|
||||
user: JWTUserData | null;
|
||||
}) {
|
||||
const signOut = useSignOut();
|
||||
|
||||
if (user) {
|
||||
return (
|
||||
<PersonalAccountDropdown
|
||||
showProfileName={false}
|
||||
paths={paths}
|
||||
features={features}
|
||||
user={user}
|
||||
signOutRequested={() => signOut.mutateAsync()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <AuthButtons />;
|
||||
}
|
||||
|
||||
function AuthButtons() {
|
||||
return (
|
||||
<div
|
||||
className={'animate-in fade-in flex items-center gap-x-2 duration-500'}
|
||||
>
|
||||
<div className={'hidden md:flex'}>
|
||||
<If condition={features.enableThemeToggle}>
|
||||
<ModeToggle />
|
||||
</If>
|
||||
</div>
|
||||
|
||||
<div className={'md:hidden'}>
|
||||
<If condition={features.enableThemeToggle}>
|
||||
<MobileModeToggle />
|
||||
</If>
|
||||
</div>
|
||||
|
||||
<div className={'flex items-center gap-x-2'}>
|
||||
<Button
|
||||
className={'hidden md:flex md:text-sm'}
|
||||
asChild
|
||||
variant={'outline'}
|
||||
size={'sm'}
|
||||
>
|
||||
<Link href={pathsConfig.auth.signIn}>
|
||||
<Trans i18nKey={'auth:signIn'} />
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
asChild
|
||||
className="text-xs md:text-sm"
|
||||
variant={'default'}
|
||||
size={'sm'}
|
||||
>
|
||||
<Link href={pathsConfig.auth.signUp}>
|
||||
<Trans i18nKey={'auth:signUp'} />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
17
apps/web/app/(marketing)/_components/site-header.tsx
Normal file
17
apps/web/app/(marketing)/_components/site-header.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { JWTUserData } from '@kit/supabase/types';
|
||||
import { Header } from '@kit/ui/marketing';
|
||||
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
|
||||
import { SiteHeaderAccountSection } from './site-header-account-section';
|
||||
import { SiteNavigation } from './site-navigation';
|
||||
|
||||
export function SiteHeader(props: { user?: JWTUserData | null }) {
|
||||
return (
|
||||
<Header
|
||||
logo={<AppLogo />}
|
||||
navigation={<SiteNavigation />}
|
||||
actions={<SiteHeaderAccountSection user={props.user ?? null} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { NavigationMenuItem } from '@kit/ui/navigation-menu';
|
||||
import { cn, isRouteActive } from '@kit/ui/utils';
|
||||
|
||||
const getClassName = (path: string, currentPathName: string) => {
|
||||
const isActive = isRouteActive(path, currentPathName);
|
||||
|
||||
return cn(
|
||||
`inline-flex w-max text-sm font-medium transition-colors duration-300`,
|
||||
{
|
||||
'dark:text-gray-300 dark:hover:text-white': !isActive,
|
||||
'text-current dark:text-white': isActive,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export function SiteNavigationItem({
|
||||
path,
|
||||
children,
|
||||
}: React.PropsWithChildren<{
|
||||
path: string;
|
||||
}>) {
|
||||
const currentPathName = usePathname();
|
||||
const className = getClassName(path, currentPathName);
|
||||
|
||||
return (
|
||||
<NavigationMenuItem key={path}>
|
||||
<Link className={className} href={path} as={path} prefetch={true}>
|
||||
{children}
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
);
|
||||
}
|
||||
87
apps/web/app/(marketing)/_components/site-navigation.tsx
Normal file
87
apps/web/app/(marketing)/_components/site-navigation.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Menu } from 'lucide-react';
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@kit/ui/dropdown-menu';
|
||||
import { NavigationMenu, NavigationMenuList } from '@kit/ui/navigation-menu';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { SiteNavigationItem } from './site-navigation-item';
|
||||
|
||||
const links = {
|
||||
Blog: {
|
||||
label: 'marketing:blog',
|
||||
path: '/blog',
|
||||
},
|
||||
Changelog: {
|
||||
label: 'marketing:changelog',
|
||||
path: '/changelog',
|
||||
},
|
||||
Docs: {
|
||||
label: 'marketing:documentation',
|
||||
path: '/docs',
|
||||
},
|
||||
Pricing: {
|
||||
label: 'marketing:pricing',
|
||||
path: '/pricing',
|
||||
},
|
||||
FAQ: {
|
||||
label: 'marketing:faq',
|
||||
path: '/faq',
|
||||
},
|
||||
};
|
||||
|
||||
export function SiteNavigation() {
|
||||
const NavItems = Object.values(links).map((item) => {
|
||||
return (
|
||||
<SiteNavigationItem key={item.path} path={item.path}>
|
||||
<Trans i18nKey={item.label} />
|
||||
</SiteNavigationItem>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'hidden items-center justify-center md:flex'}>
|
||||
<NavigationMenu>
|
||||
<NavigationMenuList className={'gap-x-2.5'}>
|
||||
{NavItems}
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
</div>
|
||||
|
||||
<div className={'flex justify-start sm:items-center md:hidden'}>
|
||||
<MobileDropdown />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileDropdown() {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger aria-label={'Open Menu'}>
|
||||
<Menu className={'h-8 w-8'} />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent className={'w-full'}>
|
||||
{Object.values(links).map((item) => {
|
||||
const className = 'flex w-full h-full items-center';
|
||||
|
||||
return (
|
||||
<DropdownMenuItem key={item.path} asChild>
|
||||
<Link className={className} href={item.path}>
|
||||
<Trans i18nKey={item.label} />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
47
apps/web/app/(marketing)/_components/site-page-header.tsx
Normal file
47
apps/web/app/(marketing)/_components/site-page-header.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
export function SitePageHeader({
|
||||
title,
|
||||
subtitle,
|
||||
container = true,
|
||||
className = '',
|
||||
}: {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
container?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
const containerClass = container ? 'container' : '';
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'border-border/40 border-b py-6 xl:py-8 2xl:py-10',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col items-center gap-y-2 lg:gap-y-3',
|
||||
containerClass,
|
||||
)}
|
||||
>
|
||||
<h1
|
||||
className={
|
||||
'font-heading text-3xl tracking-tighter xl:text-5xl dark:text-white'
|
||||
}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
<h2
|
||||
className={
|
||||
'text-muted-foreground text-lg tracking-tight 2xl:text-2xl'
|
||||
}
|
||||
>
|
||||
{subtitle}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user