feat/static-pages #21

Merged
palid merged 5 commits from feat/static-pages into main 2025-02-15 00:22:14 +00:00
14 changed files with 2031 additions and 130 deletions

View file

@ -1,7 +1,14 @@
import createMDX from "@next/mdx";
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
pageExtensions: ["js", "jsx", "md", "mdx", "ts", "tsx"],
};
export default nextConfig;
const withMDX = createMDX({
// Add markdown plugins here, as desired
});
// Merge MDX config with Next.js config
export default withMDX(nextConfig);

1924
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -14,10 +14,15 @@
"@lingui/core": "^5.1.2",
"@lingui/macro": "^5.1.2",
"@lingui/react": "^5.1.2",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@next/mdx": "^15.1.7",
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-dialog": "^1.1.5",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@tailwindcss/typography": "^0.5.16",
"@types/mdx": "^2.0.13",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"form-data": "^4.0.1",

View file

@ -0,0 +1,5 @@
import { notFound } from "next/navigation"
export default function NotFoundDummy() {
notFound()
}

View file

@ -0,0 +1,38 @@
import { NavContainer } from "@/components/nav-container";
import { getLocale, Lang } from "@/i18n/locales";
import { translations } from "@/i18n/translations";
export default async function MdxLayout({ children, params }: { children: React.ReactNode, params: Promise<{ locale: Lang }> }) {
const { locale } = await (params);
const currentLang = getLocale(locale);
const t = translations[currentLang];
return (
<div>
<NavContainer title={t.nav.title} >{null}</NavContainer>
<section className="container mx-auto px-4 py-10">
<div className="container mx-auto px-4 py-8">
<article
className="prose prose-invert max-w-none
prose-h1:font-press-start prose-h1:text-4xl md:prose-h1:text-5xl prose-h1:mb-8 prose-h1:text-center prose-h1:text-foreground
prose-h2:font-press-start prose-h2:text-2xl md:prose-h2:text-3xl prose-h2:text-foreground/80
prose-h3:font-press-start prose-h3:text-xl md:prose-h3:text-2xl prose-h3:text-foreground/60
prose-h4:font-press-start prose-h4:text-l md:prose-h4:text-xl prose-h4:text-foreground/40
prose-p:text-muted-foreground prose-p:leading-relaxed
prose-a:text-foreground prose-a:no-underline hover:prose-a:text-foreground/80 prose-a:transition-colors
prose-strong:text-foreground prose-strong:font-bold
prose-code:text-foreground prose-code:bg-muted/20 prose-code:px-1 prose-code:rounded
prose-pre:bg-muted/20 prose-pre:border prose-pre:border-muted
prose-img:rounded-lg prose-img:border prose-img:border-muted
prose-blockquote:border-primary prose-blockquote:text-muted-foreground
prose-ul:text-muted-foreground prose-ol:text-muted-foreground
prose-li:marker:text-foreground"
>
{children}
</article>
</div>
</section>
</div>
)
}

View file

@ -0,0 +1,34 @@
import { getLocale, Lang } from "@/i18n/locales"
import { notFound } from "next/navigation"
export default async function Page({
params,
}: {
params: Promise<{ slug: string, locale: Lang }>
}) {
const { slug, locale } = await params
const currentLocale = getLocale(locale)
const isReallyProperSlug = /^[a-zA-Z0-9_-]+$/.test(slug)
if (!isReallyProperSlug) {
notFound()
}
try {
const path = `@/pages/${currentLocale}/${slug}.mdx`
const pagemodule = await import(path)
const Post = pagemodule.default
return <Post />
} catch (error) {
console.log(error)
notFound()
}
}
export function generateStaticParams() {
return [{ slug: 'privacy' }]
}
export const dynamicParams = false

View file

@ -4,11 +4,11 @@ import dynamic from 'next/dynamic';
import { Nav } from "@/components/nav";
import { jgs7 } from '@/fonts';
import { Translations } from "@/i18n/translations";
import { cn } from "@/lib/utils";
import { ReactElement, useEffect, useRef } from "react";
import { MainpageNav } from './nav';
import { NewsletterPopup } from './newsletter-form';
import { useTheme } from "./providers";
import { Skeleton } from './ui/skeleton';
@ -150,7 +150,7 @@ export default function LandingPage(
return (
<div>
<Nav t={t} />
<MainpageNav t={t} />
<main className="flex flex-col min-h-screen grid-gap-10 gap-10 pb-12">
<section id="hero" className="h-screen relative overflow-hidden dark:bg-black light:bg-white ">

View file

@ -0,0 +1,40 @@
'use client';
import { Button } from "@/components/ui/button";
import { MoonIcon, SunIcon } from "lucide-react";
import { useTheme } from "./providers";
import { LanguageSelector } from "./ui/language-selector";
export function NavContainer({ children, title, }: { children: React.ReactNode, title: string }) {
const { theme, setTheme } = useTheme();
return (
<nav className="fixed top-0 left-0 right-0 backdrop-blur-xs bg-background/40 border-b z-[10000]">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
<div className="flex gap-4">
<LanguageSelector />
<a href="#" className="text-xl font-bold tracking-tighter hover:text-primary transition-colors">
<h1>{title}</h1>
</a>
</div>
<div className="flex items-center">
{children}
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="ml-4"
>
{theme === "dark" ? (
<SunIcon className="h-5 w-5" />
) : (
<MoonIcon className="h-5 w-5" />
)}
</Button>
</div>
</div>
</div>
</nav>
);
}

View file

@ -1,13 +1,10 @@
"use client"
import { Button } from "@/components/ui/button"
import { useColorSections } from "@/hooks/color-sections"
import { type translations } from "@/i18n/translations"
import { cn } from "@/lib/utils"
import { MoonIcon, SunIcon } from "lucide-react"
import { useRef } from "react"
import { MobileNav } from "./mobile-nav"
import { useTheme } from "./providers"
import { LanguageSelector } from "./ui/language-selector"
import { NavContainer } from "./nav-container"
const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
"hero",
@ -20,61 +17,36 @@ const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
"contact",
]
export function Nav({
export function MainpageNav({
t,
}: {
t: typeof translations.pl
}) {
const { theme, setTheme } = useTheme()
const parent = useRef<HTMLDivElement>(null);
useColorSections(parent);
return (
<nav className="fixed top-0 left-0 right-0 backdrop-blur-xs bg-background/40 border-b z-[10000]">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
<div className="flex gap-4">
<LanguageSelector />
<a href="#" className="text-xl font-bold tracking-tighter hover:text-primary transition-colors">
<h1>{t.nav.title}</h1>
</a>
</div>
<div className="flex items-center">
{/* Desktop Navigation */}
<div className="hidden md:flex md:items-center md:gap-4 lg:gap-8" ref={parent}>
<NavContainer title={t.nav.title}>
<>
<div className="hidden md:flex md:items-center md:gap-4 lg:gap-8" ref={parent}>
{linksOrder.map((value) => (
<a
key={value}
href={`#${value}`}
className="text-sm md:text-md hover:text-primary transition-colors relative group will-change-[color]"
>
{t.nav[value]}
<span data-sub={value} className={cn("absolute inset-x-0 -bottom-1 h-0.5 bg-primary transform scale-x-0 group-hover:scale-x-100 transition-transform will-change-transform", {
})} />
</a>
))}
</div>
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="ml-4"
{linksOrder.map((value) => (
<a
key={value}
href={`#${value}`}
className="text-sm md:text-md hover:text-primary transition-colors relative group will-change-[color]"
>
{theme === "dark" ? <SunIcon className="h-5 w-5" /> : <MoonIcon className="h-5 w-5" />}
</Button>
{/* Mobile Navigation */}
<div className="md:hidden ml-2">
<MobileNav t={t} linksOrder={linksOrder} />
</div>
</div>
{t.nav[value]}
<span data-sub={value} className={cn("absolute inset-x-0 -bottom-1 h-0.5 bg-primary transform scale-x-0 group-hover:scale-x-100 transition-transform will-change-transform", {
})} />
</a>
))}
</div>
</div>
</nav>
<div className="md:hidden ml-2">
<MobileNav t={t} linksOrder={linksOrder} />
</div>
</>
</NavContainer>
)
}

View file

@ -2,17 +2,23 @@
import { Lang } from "@/i18n/locales";
import Link from "next/link";
import { useParams } from "next/navigation";
import { useParams, usePathname } from "next/navigation";
export const LanguageSelector = () => {
const params = useParams<{ locale: Lang }>();
const pathname = usePathname()
const replacements = {
'pl': 'en',
'en': 'pl',
}
const lang = params?.locale || 'pl';
const hash = globalThis?.window?.location?.hash || '';
const changedLang = pathname.replace(`/${lang}/`, `/${replacements[lang]}/`)
if (lang === 'pl') return (<>
<Link suppressHydrationWarning className="pt-1" href={`/en${hash}`}>🇬🇧</Link></>);
<Link suppressHydrationWarning className="pt-1" href={changedLang}>🇬🇧</Link></>);
if (lang === 'en') return (<>
<Link suppressHydrationWarning className="pt-1" href={`/pl${hash}`}>🇵🇱</Link></>);
<Link suppressHydrationWarning className="pt-1" href={changedLang}>🇵🇱</Link></>);
};

View file

@ -1,4 +1,5 @@
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin 'tailwindcss-animate';

7
src/mdx-components.ts Normal file
View file

@ -0,0 +1,7 @@
import type { MDXComponents } from "mdx/types";
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...components,
};
}

6
src/pages/en/privacy.mdx Normal file
View file

@ -0,0 +1,6 @@
# Privacy policy
You will see a privacy policy soon.
It's just a placeholder for now.
Return to the [homepage](/en/)

6
src/pages/pl/privacy.mdx Normal file
View file

@ -0,0 +1,6 @@
# Polityka prywatności
Tu w przyszłości pojawi się polityka prywatności na temat wydarzenia.
W tym momencie po prostu trzymamy sobie stronę.
Wróć do [strony głównej](/pl/)