From 0b374c558a7f1cc71e8a1fb490f5a7f4b00b0a14 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Thu, 4 Apr 2024 10:04:07 +0800 Subject: [PATCH] Create legal pages and refactor navigation New pages for Cookie Policy, Terms of Service, and Privacy Policy were added. The navigation system was restructured using a mapped array of links instead of hard coded components. The 'Not Found' page's metadata handling was improved and its translation was updated. Certain components that aid this refactoring were created and some pre-existing components or functions were moved to more appropriate locations or renamed for clarity. The Site Navigation, Footer, and Page header components were updated in layout and content. Various pages including Blog and Documentation were updated or removed. --- .../(legal)/cookie-policy/page.tsx | 17 ++++ .../(legal)/privacy-policy/page.tsx | 14 +++ .../(legal)/terms-of-service/page.tsx | 14 +++ .../(marketing)/_components/site-footer.tsx | 66 +++++++------ .../_components/site-navigation-item.tsx | 33 +++++++ .../_components/site-navigation.tsx | 55 ++++------- .../_components/site-page-header.tsx | 4 +- apps/web/app/(marketing)/about/page.tsx | 93 ------------------- .../app/(marketing)/docs/[...slug]/page.tsx | 6 +- .../docs/_components/docs-navigation.tsx | 10 +- ...ation-page-link.tsx => docs-page-link.tsx} | 2 +- apps/web/app/(marketing)/docs/layout.tsx | 8 +- apps/web/app/(marketing)/docs/page.tsx | 16 ++-- apps/web/app/not-found.tsx | 11 ++- apps/web/public/locales/en/common.json | 1 + apps/web/public/locales/en/marketing.json | 9 +- packages/ui/src/utils/index.ts | 1 + .../{makerkit => utils}/is-route-active.ts | 4 +- 18 files changed, 178 insertions(+), 186 deletions(-) create mode 100644 apps/web/app/(marketing)/(legal)/cookie-policy/page.tsx create mode 100644 apps/web/app/(marketing)/(legal)/privacy-policy/page.tsx create mode 100644 apps/web/app/(marketing)/(legal)/terms-of-service/page.tsx create mode 100644 apps/web/app/(marketing)/_components/site-navigation-item.tsx delete mode 100644 apps/web/app/(marketing)/about/page.tsx rename apps/web/app/(marketing)/docs/_components/{documentation-page-link.tsx => docs-page-link.tsx} (96%) rename packages/ui/src/{makerkit => utils}/is-route-active.ts (97%) 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] ?? '';