diff --git a/apps/web/app/(marketing)/(legal)/cookie-policy/page.tsx b/apps/web/app/(marketing)/(legal)/cookie-policy/page.tsx new file mode 100644 index 000000000..e3c981561 --- /dev/null +++ b/apps/web/app/(marketing)/(legal)/cookie-policy/page.tsx @@ -0,0 +1,17 @@ +import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; +import { withI18n } from '~/lib/i18n/with-i18n'; + +function CookiePolicyPage() { + return ( +
+
+ +
+
+ ); +} + +export default withI18n(CookiePolicyPage); diff --git a/apps/web/app/(marketing)/(legal)/privacy-policy/page.tsx b/apps/web/app/(marketing)/(legal)/privacy-policy/page.tsx new file mode 100644 index 000000000..d8f3d94f5 --- /dev/null +++ b/apps/web/app/(marketing)/(legal)/privacy-policy/page.tsx @@ -0,0 +1,14 @@ +import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; +import { withI18n } from '~/lib/i18n/with-i18n'; + +function PrivacyPolicyPage() { + return ( +
+
+ +
+
+ ); +} + +export default withI18n(PrivacyPolicyPage); diff --git a/apps/web/app/(marketing)/(legal)/terms-of-service/page.tsx b/apps/web/app/(marketing)/(legal)/terms-of-service/page.tsx new file mode 100644 index 000000000..4993eaede --- /dev/null +++ b/apps/web/app/(marketing)/(legal)/terms-of-service/page.tsx @@ -0,0 +1,14 @@ +import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; +import { withI18n } from '~/lib/i18n/with-i18n'; + +function TermsOfServicePage() { + return ( +
+
+ +
+
+ ); +} + +export default withI18n(TermsOfServicePage); diff --git a/apps/web/app/(marketing)/_components/site-footer.tsx b/apps/web/app/(marketing)/_components/site-footer.tsx index 99bb86119..132c7044d 100644 --- a/apps/web/app/(marketing)/_components/site-footer.tsx +++ b/apps/web/app/(marketing)/_components/site-footer.tsx @@ -1,5 +1,7 @@ import Link from 'next/link'; +import { Trans } from '@kit/ui/trans'; + import { AppLogo } from '~/components/app-logo'; import appConfig from '~/config/app.config'; @@ -22,12 +24,12 @@ export function SiteFooter() {
-

+

Add a short tagline about your product

-
+

© Copyright {YEAR} {appConfig.name}. All Rights Reserved.

@@ -43,54 +45,63 @@ export function SiteFooter() { } >
-
- About +
+ + + - Who we are + + + - Blog - - - Contact + + +
-
- Product +
+ + + - Documentation - - - Help Center - - - Changelog + + +
-
- Legal +
+ + + - Terms of Service + + + - Privacy Policy + + + - Cookie Policy + + +
@@ -111,19 +122,14 @@ function FooterSectionHeading(props: React.PropsWithChildren) { } function FooterSectionList(props: React.PropsWithChildren) { - return ( -
    - {props.children} -
- ); + return
    {props.children}
; } function FooterLink(props: React.PropsWithChildren) { return (
  • a]:transition-colors [&>a]:hover:text-gray-800' + - ' dark:[&>a]:hover:text-white' + 'text-sm text-muted-foreground hover:underline [&>a]:transition-colors' } > {props.children} diff --git a/apps/web/app/(marketing)/_components/site-navigation-item.tsx b/apps/web/app/(marketing)/_components/site-navigation-item.tsx new file mode 100644 index 000000000..39f81e495 --- /dev/null +++ b/apps/web/app/(marketing)/_components/site-navigation-item.tsx @@ -0,0 +1,33 @@ +'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(`text-sm transition-all px-4 py-2 rounded-full font-medium`, { + 'bg-muted': isActive, + 'hover:bg-muted active:bg-muted/50': !isActive, + }); +}; + +export function SiteNavigationItem({ + path, + children, +}: React.PropsWithChildren<{ + path: string; +}>) { + const currentPathName = usePathname(); + + return ( + + + {children} + + + ); +} diff --git a/apps/web/app/(marketing)/_components/site-navigation.tsx b/apps/web/app/(marketing)/_components/site-navigation.tsx index 20c6d5608..de98afd1a 100644 --- a/apps/web/app/(marketing)/_components/site-navigation.tsx +++ b/apps/web/app/(marketing)/_components/site-navigation.tsx @@ -8,64 +8,47 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from '@kit/ui/dropdown-menu'; -import { - NavigationMenu, - NavigationMenuItem, - NavigationMenuList, -} from '@kit/ui/navigation-menu'; +import { NavigationMenu, NavigationMenuList } from '@kit/ui/navigation-menu'; +import { Trans } from '@kit/ui/trans'; + +import { SiteNavigationItem } from '~/(marketing)/_components/site-navigation-item'; const links = { - SignIn: { - label: 'Sign In', - path: '/auth/sign-in', - }, Blog: { - label: 'Blog', + label: 'marketing:blog', path: '/blog', }, Docs: { - label: 'Documentation', + label: 'marketing:documentation', path: '/docs', }, Pricing: { - label: 'Pricing', + label: 'marketing:pricing', path: '/pricing', }, FAQ: { - label: 'FAQ', + label: 'marketing:faq', path: '/faq', }, }; export function SiteNavigation() { - const className = `hover:underline text-sm`; + const NavItems = Object.values(links).map((item) => { + return ( + + + + ); + }); return ( <>
    - - - - {links.Blog.label} - - - - - {links.Docs.label} - - - - - {links.Pricing.label} - - - - - - {links.FAQ.label} - - + + {NavItems}
    diff --git a/apps/web/app/(marketing)/_components/site-page-header.tsx b/apps/web/app/(marketing)/_components/site-page-header.tsx index b3b4a6d85..c07e4dff3 100644 --- a/apps/web/app/(marketing)/_components/site-page-header.tsx +++ b/apps/web/app/(marketing)/_components/site-page-header.tsx @@ -12,9 +12,9 @@ export function SitePageHeader(props: { > {props.title} - +

    {props.subtitle} - +

  • ); } diff --git a/apps/web/app/(marketing)/about/page.tsx b/apps/web/app/(marketing)/about/page.tsx deleted file mode 100644 index 703fc95c0..000000000 --- a/apps/web/app/(marketing)/about/page.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Heading } from '@kit/ui/heading'; - -export const metadata = { - title: 'About', -}; - -const AboutPage = () => { - return ( -
    -
    -
    -
    - About us - - - We are a team of passionate developers and designers who love to - build great products. - -
    - -
    -
    - We are a team of visionaries, dreamers, and doers who are on a - mission to change the world for the better -
    - -
    - With a passion for innovation and a commitment to excellence, we - are dedicated to creating products and services that will improve - people's lives and make a positive impact on society. -
    - -
    - It all started with a simple idea: to use technology to solve some - of the biggest challenges facing humanity. We realized that with - the right team and the right approach, we could make a difference - and leave a lasting legacy. And so, with a lot of hard work and - determination, we set out on a journey to turn our vision into - reality. -
    - -
    - Today, we are proud to be a leader in our field, and our products - and services are used by millions of people all over the world. - But we're not done yet. We still have big dreams and even - bigger plans, and we're always looking for ways to push the - boundaries of what's possible. -
    - -
    - Our Values: At the heart of everything we do is a set of core - values that guide us in all that we do. These values are what make - us who we are, and they are what set us apart from the rest. -
    - -
    -
      -
    • - Innovation: We are always looking for new and better ways to - do things. -
    • - -
    • - Excellence: We strive for excellence in all that we do, and we - never settle for less. -
    • - -
    • - Responsibility: We take our responsibilities seriously, and we - always act with integrity. -
    • - -
    • - Collaboration: We believe that by working together, we can - achieve more than we can on our own. -
    • -
    -
    - -
    Yes, this was generated with ChatGPT
    -
    -
    -
    -
    - ); -}; - -export default AboutPage; diff --git a/apps/web/app/(marketing)/docs/[...slug]/page.tsx b/apps/web/app/(marketing)/docs/[...slug]/page.tsx index b4581df1e..2ef7bb7db 100644 --- a/apps/web/app/(marketing)/docs/[...slug]/page.tsx +++ b/apps/web/app/(marketing)/docs/[...slug]/page.tsx @@ -49,7 +49,11 @@ async function DocumentationPage({ params }: PageParams) { return (
    -
    +
    = ({ label, url, level, activePath }) => { - const isCurrent = url == activePath; + const isCurrent = isRouteActive(url, activePath, 0); const isFirstLevel = level === 0; return ( @@ -70,7 +70,7 @@ function Tree({ activePath: string; }) { return ( -
    0 ? 'border-l' : '')}> +
    {pages.map((treeNode, index) => ( @@ -109,7 +109,7 @@ export function DocsNavigation({ pages }: { pages: Cms.ContentItem[] }) { function getNavLinkClassName(isCurrent: boolean, isFirstLevel: boolean) { return cn( - 'group flex h-8 items-center justify-between space-x-2 whitespace-nowrap rounded-md px-3 text-sm leading-none transition-colors', + 'group flex min-h-8 items-center justify-between space-x-2 whitespace-nowrap rounded-md px-3 text-sm transition-colors', { [`bg-muted`]: isCurrent, [`hover:bg-muted`]: !isCurrent, diff --git a/apps/web/app/(marketing)/docs/_components/documentation-page-link.tsx b/apps/web/app/(marketing)/docs/_components/docs-page-link.tsx similarity index 96% rename from apps/web/app/(marketing)/docs/_components/documentation-page-link.tsx rename to apps/web/app/(marketing)/docs/_components/docs-page-link.tsx index 9dcfcc2a6..ac06ed56d 100644 --- a/apps/web/app/(marketing)/docs/_components/documentation-page-link.tsx +++ b/apps/web/app/(marketing)/docs/_components/docs-page-link.tsx @@ -3,7 +3,7 @@ import Link from 'next/link'; import { If } from '@kit/ui/if'; import { cn } from '@kit/ui/utils'; -export function DocumentationPageLink({ +export function DocsPageLink({ page, before, after, diff --git a/apps/web/app/(marketing)/docs/layout.tsx b/apps/web/app/(marketing)/docs/layout.tsx index 1cdfc1e60..badfe1822 100644 --- a/apps/web/app/(marketing)/docs/layout.tsx +++ b/apps/web/app/(marketing)/docs/layout.tsx @@ -10,12 +10,10 @@ async function DocsLayout({ children }: React.PropsWithChildren) { }); return ( -
    -
    - +
    + -
    {children}
    -
    + {children}
    ); } diff --git a/apps/web/app/(marketing)/docs/page.tsx b/apps/web/app/(marketing)/docs/page.tsx index aaf4f01ff..a9630cecf 100644 --- a/apps/web/app/(marketing)/docs/page.tsx +++ b/apps/web/app/(marketing)/docs/page.tsx @@ -26,14 +26,16 @@ async function DocsPage() { const cards = docs.filter((item) => !item.parentId); return ( -
    - +
    + +
    + - - + +
    ); diff --git a/apps/web/app/not-found.tsx b/apps/web/app/not-found.tsx index 5f29d906d..ab379f2e4 100644 --- a/apps/web/app/not-found.tsx +++ b/apps/web/app/not-found.tsx @@ -8,11 +8,16 @@ import { Heading } from '@kit/ui/heading'; import { Trans } from '@kit/ui/trans'; import { SiteHeader } from '~/(marketing)/_components/site-header'; -import appConfig from '~/config/app.config'; +import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; -export const metadata = { - title: `Page not found - ${appConfig.name}`, +export const generateMetadata = async () => { + const i18n = await createI18nServerInstance(); + const title = i18n.t('common:notFound'); + + return { + title, + }; }; const NotFoundPage = async () => { diff --git a/apps/web/public/locales/en/common.json b/apps/web/public/locales/en/common.json index af9599120..0cf5c16e8 100644 --- a/apps/web/public/locales/en/common.json +++ b/apps/web/public/locales/en/common.json @@ -19,6 +19,7 @@ "imageInputLabel": "Click here to upload an image", "cancel": "Cancel", "clear": "Clear", + "notFound": "Not Found", "backToHomePage": "Back to Home Page", "genericServerError": "Sorry, something went wrong.", "genericServerErrorHeading": "Sorry, something went wrong while processing your request. Please contact us if the issue persists.", diff --git a/apps/web/public/locales/en/marketing.json b/apps/web/public/locales/en/marketing.json index 3d69925c0..ec5fe0acc 100644 --- a/apps/web/public/locales/en/marketing.json +++ b/apps/web/public/locales/en/marketing.json @@ -6,5 +6,12 @@ "faq": "FAQ", "faqSubtitle": "Frequently asked questions about the platform", "pricing": "Pricing", - "pricingSubtitle": "Pricing plans and payment options" + "pricingSubtitle": "Pricing plans and payment options", + "contact": "Contact", + "about": "About", + "product": "Product", + "legal": "Legal", + "tos": "Terms of Service", + "cookiePolicy": "Cookie Policy", + "privacyPolicy": "Privacy Policy" } diff --git a/packages/ui/src/utils/index.ts b/packages/ui/src/utils/index.ts index 9ae36a532..6d5d4c266 100644 --- a/packages/ui/src/utils/index.ts +++ b/packages/ui/src/utils/index.ts @@ -1 +1,2 @@ export * from './cn'; +export * from './is-route-active'; diff --git a/packages/ui/src/makerkit/is-route-active.ts b/packages/ui/src/utils/is-route-active.ts similarity index 97% rename from packages/ui/src/makerkit/is-route-active.ts rename to packages/ui/src/utils/is-route-active.ts index 98898e618..06fb745bb 100644 --- a/packages/ui/src/makerkit/is-route-active.ts +++ b/packages/ui/src/utils/is-route-active.ts @@ -8,10 +8,10 @@ const ROOT_PATH = '/'; * @param currentRoute - the current route * @param depth - how far down should segments be matched? */ -export default function isRouteActive( +export function isRouteActive( targetLink: string, currentRoute: string, - depth: number, + depth = 1, ) { // we remove any eventual query param from the route's URL const currentRoutePath = currentRoute.split('?')[0] ?? '';