please fix this it bad

This commit is contained in:
Dariusz Niemczyk 2025-02-01 21:13:35 +01:00
parent 892ca8a79a
commit 46c5ddca4e
No known key found for this signature in database
16 changed files with 308 additions and 171 deletions

49
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,49 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug server-side",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev"
},
{
"name": "Next.js: debug client-side",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000"
},
{
"name": "Next.js: debug client-side (Firefox)",
"type": "firefox",
"request": "launch",
"url": "http://localhost:3000",
"reAttach": true,
"pathMappings": [
{
"url": "webpack://_N_E",
"path": "${workspaceFolder}"
}
]
},
{
"name": "Next.js: debug full stack",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/next",
"runtimeArgs": [
"--inspect"
],
"skipFiles": [
"<node_internals>/**"
],
"serverReadyAction": {
"action": "debugWithEdge",
"killOnServerStop": true,
"pattern": "- Local:.+(https?://.+)",
"uriFormat": "%s",
"webRoot": "${workspaceFolder}"
}
}
]
}

BIN
public/ceboola_gradient.mp4 Normal file

Binary file not shown.

Binary file not shown.

View file

@ -1,6 +1,6 @@
{
"name": "MyWebSite",
"short_name": "MySite",
"name": "CebulaCamp",
"short_name": "Cebula",
"icons": [
{
"src": "/web-app-manifest-192x192.png",
@ -18,4 +18,4 @@
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
}

View file

@ -0,0 +1,37 @@
'use client'
import { useEffect } from "react"
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error)
}, [error])
return (
<div className="flex h-full w-full items-center justify-center">
<div className="text-center">
<h1 className="text-6xl font-bold tracking-tight text-black sm:text-8xl">
{error.name}
</h1>
<p className="mt-4 text-lg text-gray-500">{error.message}</p>
<div className="mt-6">
<button
type="button"
className="inline-flex items-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 sm:text-sm"
onClick={reset}
>
Try again
</button>
</div>
</div>
</div>
)
}

View file

@ -2,10 +2,12 @@ import "../../globals.css";
import type React from "react";
import { Lang, locales } from "@/i18n/locales";
import { getLocale, Lang } from "@/i18n/locales";
import Head from 'next/head';
import { ThemeProvider } from "@/components/providers";
import { Oxanium } from "next/font/google";
import { headers } from "next/headers";
const oxanium = Oxanium({ subsets: ["latin-ext"] })
@ -16,20 +18,34 @@ export default async function RootLayout({
children: React.ReactNode
params: Promise<{ locale: Lang }>
}) {
const { locale } = await params
const currentLang = locales.includes(locale) ? locale : "en"
const [{ locale }, head] = await Promise.all([
params,
headers(),
]);
const preferedTheme = head.get("Sec-CH-Prefers-Color-Scheme")?.toLowerCase();
const supportedThemes = ["dark", "light"];
const isDarkOrLight =
preferedTheme && supportedThemes.includes(preferedTheme);
const currentLang = getLocale(locale);
const defaultTheme = isDarkOrLight ? preferedTheme : "dark";
return (
<html lang={currentLang} className={`${oxanium.className} dark`}>
<html lang={currentLang} className={`${oxanium.className} ${defaultTheme}`}>
<Head>
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="MyWebSite" />
<meta name="apple-mobile-web-app-title" content="CebulaCamp" />
<link rel="manifest" href="/site.webmanifest" />
</Head>
<body className="bg-background text:foreground antialiased">
{children}
<ThemeProvider>
{children}
</ThemeProvider>
</body>
</html>
)

View file

@ -0,0 +1,4 @@
export default function Loading() {
return <div>Loading...</div>
}

View file

@ -0,0 +1,5 @@
export default function NotFound() {
return <div>Not Found</div>
}

View file

@ -1,130 +1,17 @@
"use client"
import { Nav } from "@/components/nav"
import { translations } from "@/i18n/translations"
import { useEffect, useRef } from "react"
import LandingPage from "@/components/landing-page";
import { getLocale, Lang } from "@/i18n/locales";
import { translations } from "@/i18n/translations";
export default function Home() {
const videoRef = useRef<HTMLVideoElement>(null)
const t = translations.pl // For now using Polish, could be made dynamic
export default async function Home(
{ params }
:
{ params: Promise<{ locale: Lang }> }
) {
const { locale } = await params
const currentLocale = getLocale(locale)
const t = translations[currentLocale];
useEffect(() => {
const handleScroll = () => {
if (videoRef.current) {
const scrolled = window.scrollY;
videoRef.current.style.willChange = "transform";
videoRef.current.style.transform = `translateY(${scrolled * 0.5}px)`;
videoRef.current.style.willChange = "unset";
}
};
const throttledHandleScroll = () => {
requestAnimationFrame(handleScroll);
};
window.addEventListener('scroll', throttledHandleScroll);
return () => window.removeEventListener('scroll', throttledHandleScroll);
}, []);
return (
<div>
<Nav t={t} />
<main className="flex flex-col min-h-screen">
<section className="h-screen relative overflow-hidden dark:bg-black">
<div className="absolute inset-0 bg-black opacity-80">
<video ref={videoRef} autoPlay muted loop playsInline className="w-full h-full object-cover parallax-video ">
<source src="/cebula.mp4" type="video/mp4" />
</video>
</div>
<div className="relative z-10 container mx-auto px-4 h-full flex items-center justify-center">
<div className="text-center font-[JGS7]">
<h1 className="text-5xl sm:text-6xl md:text-8xl font-bold tracking-tighter mb-6 light:text-background">{t.hero.title}</h1>
<p className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl-text:7xl 2xl:text-8xl text-primary">{t.hero.subtitle}</p>
</div>
</div>
</section>
<section id="about" className="py-24 bg-background">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.about.title}</h2>
<div className="text-lg text-muted-foreground max-w-3xl mx-auto whitespace-pre-line">
{t.about.description}
</div>
</div>
</section>
<section id="where" className="py-24 bg-background/90">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.where.title}</h2>
<div className="text-lg text-muted-foreground">
<p>{t.where.location}</p>
</div>
</div>
</section>
<section id="when" className="py-24 bg-background">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.when.title}</h2>
<div className="text-lg text-muted-foreground">
<p className="text-primary text-3xl font-[JGS7]">{t.when.date}</p>
<p className="mt-4">{t.when.extra}</p>
</div>
</div>
</section>
<section id="tickets" className="py-24 bg-background/90">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.tickets.title}</h2>
<div className="text-lg text-muted-foreground">
<p>{t.tickets.status}</p>
</div>
</div>
</section>
<section id="accommodation" className="py-24 bg-background">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.accommodation.title}</h2>
<div className="text-lg text-muted-foreground max-w-3xl mx-auto">
<p>{t.accommodation.description}</p>
</div>
</div>
</section>
<section id="food" className="py-24 bg-background/90">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.food.title}</h2>
<div className="text-lg text-muted-foreground max-w-3xl mx-auto">
<p>{t.food.description}</p>
</div>
</div>
</section>
<section id="contact" className="py-24 bg-background">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.contact.title}</h2>
<div className="text-lg text-muted-foreground">
<a href={`mailto:${t.contact.email}`} className="text-primary hover:underline">
{t.contact.email}
</a>
</div>
</div>
</section>
<section id="credits" className="py-24 bg-background">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.credits.title}</h2>
<div className="text-lg text-muted-foreground">
<p>{t.credits.usedFonts}</p>
<p><a className="hover:underline" href="https://velvetyne.fr/fonts/jgs-font/">{t.credits.jgs7}</a> {` ${t.credits.jgs7RemovedGlyphs}`}</p>
<p><a className="hover:underline" href="https://fonts.google.com/specimen/Oxanium">{t.credits.oxanium}</a></p>
</div>
</div>
</section>
</main>
</div>
)
return <LandingPage t={t} />
}

View file

@ -0,0 +1,115 @@
"use client"
import { Nav } from "@/components/nav";
import { Translations } from "@/i18n/translations";
import { cn } from "@/lib/utils";
import { ReactElement, useEffect, useRef } from "react";
import { useTheme } from "./providers";
function Section({
id,
title,
paragraphs
}: {
id: string
title: string;
paragraphs: ReactElement;
}) {
return (<section id={id} className="py-24 bg-background">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-8 tracking-tighter max-w-3xl mx-auto">{title}</h2>
<div className="text-lg text-muted-foreground max-w-3xl mx-auto whitespace-pre-line">
{paragraphs}
</div>
</div>
</section>)
}
function Video({ src, hidden }: {
src: string;
hidden: boolean;
}) {
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
const handleScroll = () => {
if (!videoRef.current || hidden) return;
const scrolled = window.scrollY;
videoRef.current.style.willChange = "transform";
videoRef.current.style.transform = `translateY(${scrolled * 0.5}px)`;
videoRef.current.style.willChange = "unset";
};
const throttledHandleScroll = () => {
requestAnimationFrame(handleScroll);
};
window.addEventListener("scroll", throttledHandleScroll);
handleScroll();
return () => window.removeEventListener("scroll", throttledHandleScroll);
}, [hidden]);
return (
<video
ref={videoRef}
preload="auto"
autoPlay
muted
loop
playsInline
className={cn("w-full h-full object-cover parallax-video", {
hidden,
})}
>
<source src={src} type="video/mp4" />
</video>
);
}
export default function LandingPage(
{ t }: { t: Translations }
) {
const { theme } = useTheme()
return (
<div>
<Nav t={t} />
<main className="flex flex-col min-h-screen">
<section className="h-screen relative overflow-hidden dark:bg-black light:bg-white ">
<div className="absolute inset-0 opacity-80">
<Video src="/ceboola_gradient.mp4" hidden={theme === "light"} />
{/* <Video src="/cebula.mp4" hidden={theme === "light"} /> */}
<Video src="/ceboola_gradient_white.mp4" hidden={theme === "dark"} />
</div>
<div className="relative z-10 container mx-auto px-4 h-full flex items-center justify-center">
<div className="text-center font-[JGS7]">
<h1 className="text-5xl sm:text-6xl md:text-8xl font-bold tracking-tighter mb-6 light:text-background">{t.hero.title}</h1>
<p className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl-text:7xl 2xl:text-8xl text-primary">{t.hero.subtitle}</p>
</div>
</div>
</section>
<Section id="about" title={t.about.title} paragraphs={<p>{t.about.description}</p>} />
<Section id="where" title={t.where.title} paragraphs={<p>{t.where.location}</p>} />
<Section id="when" title={t.where.title} paragraphs={<>
<p className="text-primary text-3xl font-[JGS7]">{t.when.date}</p>
<p className="mt-4">{t.when.extra}</p></>}
/>
<Section id="tickets" title={t.tickets.title} paragraphs={<p>{t.tickets.status}</p>} />
<Section id="accommodation" title={t.accommodation.title} paragraphs={<p>{t.accommodation.description}</p>} />
<Section id="food" title={t.food.title} paragraphs={<p>{t.food.description}</p>} />
<Section id="contact" title={t.contact.title} paragraphs={<p>{t.contact.email}</p>} />
<Section id="credits" title={t.credits.title} paragraphs={<>
<p>{t.credits.usedFonts}</p>
<p><a className="hover:underline" href="https://velvetyne.fr/fonts/jgs-font/">{t.credits.jgs7}</a></p>
<p><a className="hover:underline" href="https://fonts.google.com/specimen/Oxanium">{t.credits.oxanium}</a></p>
</>}
/>
</main>
</div>
)
}

View file

@ -3,8 +3,9 @@ import { Button } from "@/components/ui/button"
import { Sections, type translations } from "@/i18n/translations"
import { cn } from "@/lib/utils"
import { MoonIcon, SunIcon } from "lucide-react"
import { useCallback, useEffect, useState } from "react"
import { useEffect, useState } from "react"
import { MobileNav } from "./mobile-nav"
import { useTheme } from "./providers"
const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
"about",
@ -16,41 +17,12 @@ const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
"contact",
]
function useTheme(): [theme: "light" | "dark", setTheme: (theme: "light" | "dark") => void] {
/* eslint-disable react-hooks/rules-of-hooks */
if (typeof window === "undefined") return ["dark", () => { }]
const [theme, setTheme] = useState<"light" | "dark">("dark")
const root = window.document.documentElement
const changeTheme = useCallback((theme: "light" | "dark") => {
root.classList.remove("light", "dark")
root.classList.add(theme)
}, [root])
useEffect(() => {
// Determine the user's preferred color scheme
const preferredTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
setTheme(preferredTheme)
changeTheme(preferredTheme)
}, [changeTheme])
const updateTheme = useCallback((theme: "light" | "dark") => {
setTheme(theme)
changeTheme(theme)
}, [changeTheme, setTheme])
return [theme, updateTheme]
/* eslint-enable react-hooks/rules-of-hooks */
}
export function Nav({
t,
}: {
t: typeof translations.pl
}) {
const [theme, setTheme] = useTheme()
const { theme, setTheme } = useTheme()
const [activeSection, setActiveSection] = useState<Sections>("about")
@ -90,7 +62,7 @@ export function Nav({
}, []);
return (
<nav className="fixed top-0 left-0 right-0 z-50 bg-background/80 backdrop-blur-sm border-b">
<nav className="fixed top-0 left-0 right-0 z-50 backdrop-blur-sm border-b">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
<a href="#" className="text-xl font-bold tracking-tighter hover:text-primary transition-colors">

View file

@ -0,0 +1,43 @@
'use client';
import React, { createContext, useCallback, useContext, useState } from "react";
type Theme = 'dark' | 'light';
interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState<Theme>("dark")
const changeTheme = useCallback((theme: Theme) => {
const root = window.document.documentElement
root.classList.remove("light", "dark")
root.classList.add(theme)
}, [])
const updateTheme = useCallback((theme: Theme) => {
setTheme(theme)
changeTheme(theme)
}, [changeTheme, setTheme])
return (
<ThemeContext.Provider value={{ theme, setTheme: updateTheme }}>
{children}
</ThemeContext.Provider>
);
};
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}

View file

@ -21,7 +21,7 @@ const SheetOverlay = React.forwardRef<
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"fixed inset-0 z-50 bg-background/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}

View file

@ -2,3 +2,9 @@
export const locales = ["en", "pl"] as const;
export type Lang = (typeof locales)[number];
export function getLocale(lang: Lang): Lang {
return locales.includes(lang as Lang) ? lang : "en";
}
export const defaultLocale = locales[0];

View file

@ -1,5 +1,5 @@
const common = {
jgs7: "JGS7",
jgs7: "JGS font Jgs font by Adel Faure. Distributed by velvetyne.fr",
oxanium: "Oxanium",
};
@ -35,7 +35,7 @@ const pl = {
title: "Kiedy",
date: "28-31.08.2025",
extra:
"chętnych do pomocy w przygotowaniach zapraszamy już na *Day 0* 27 sierpnia",
"chętnych do pomocy w przygotowaniach zapraszamy już na Day 0, 27 sierpnia",
},
tickets: {
title: "Bilety",
@ -60,7 +60,6 @@ const pl = {
usedFonts: "Użyte fonty:",
oxanium: common.oxanium,
jgs7: common.jgs7,
jgs7RemovedGlyphs: "z usuniętymi znakami",
},
};
@ -75,4 +74,6 @@ export const translations: {
en: en,
};
export type Translations = typeof pl;
export type Sections = keyof (typeof pl)["nav"];

View file

@ -27,5 +27,7 @@ export function middleware(request: NextRequest) {
}
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
matcher: "/((?!api|static|.*\\..*|_next).*)",
// matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};