Compare commits
5 commits
7986886012
...
baba60aa3f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
baba60aa3f | ||
|
|
b3e7f118f2 | ||
|
|
62964757f7 | ||
|
|
b9efdc7d5a | ||
|
|
9a6d46a826 |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
public/videos/** filter=lfs diff=lfs merge=lfs -text
|
||||||
|
source-videos/** filter=lfs diff=lfs merge=lfs -text
|
||||||
49
.vscode/launch.json
vendored
Normal file
49
.vscode/launch.json
vendored
Normal 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}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
56
Dockerfile
Normal file
56
Dockerfile
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
FROM node:23-alpine3.20 AS base
|
||||||
|
|
||||||
|
# Install dependencies only when needed
|
||||||
|
FROM base AS deps
|
||||||
|
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies based on the preferred package manager
|
||||||
|
COPY . .
|
||||||
|
RUN npm ci --ignore-scripts
|
||||||
|
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# If using npm comment out above and use below instead
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production image, copy all the files and run next
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
|
||||||
|
# Set the correct permission for prerender cache
|
||||||
|
RUN mkdir .next
|
||||||
|
RUN chown nextjs:nodejs .next
|
||||||
|
|
||||||
|
# Automatically leverage output traces to reduce image size
|
||||||
|
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT 3000
|
||||||
|
# set hostname to localhost
|
||||||
|
ENV HOSTNAME "0.0.0.0"
|
||||||
|
|
||||||
|
# server.js is created by next build from the standalone output
|
||||||
|
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
|
||||||
|
|
||||||
|
ARG VERSION="local"
|
||||||
|
ENV VERSION=$VERSION
|
||||||
|
|
||||||
|
CMD echo "Version: $VERSION" && node server.js
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
output: "standalone",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "MyWebSite",
|
"name": "CebulaCamp",
|
||||||
"short_name": "MySite",
|
"short_name": "Cebula",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/web-app-manifest-192x192.png",
|
"src": "/web-app-manifest-192x192.png",
|
||||||
|
|
|
||||||
BIN
public/videos/ceboola_gradient-white_full.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_full.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_full.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_full.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_full.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_full.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_hd.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_hd.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_hd.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_hd.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_hd.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_hd.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_mobile.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_mobile.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_mobile.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_mobile.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_mobile.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_mobile.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_tablet.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_tablet.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_tablet.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_tablet.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_tablet.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_tablet.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_twok.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_twok.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_twok.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_twok.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_twok.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_twok.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_uhd.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_uhd.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_uhd.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_uhd.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_uhd.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_uhd.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_full.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_full.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_full.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_full.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_full.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_full.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_hd.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_hd.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_hd.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_hd.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_hd.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_hd.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_mobile.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_mobile.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_mobile.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_mobile.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_mobile.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_mobile.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_tablet.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_tablet.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_tablet.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_tablet.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_tablet.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_tablet.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_twok.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_twok.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_twok.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_twok.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_twok.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_twok.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_uhd.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_uhd.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_uhd.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_uhd.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_uhd.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_uhd.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
94
scripts/reencode-videos.sh
Executable file
94
scripts/reencode-videos.sh
Executable file
|
|
@ -0,0 +1,94 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Define directories
|
||||||
|
SOURCE_DIR="./source-videos"
|
||||||
|
DEST_DIR="./public/videos"
|
||||||
|
|
||||||
|
# Define sizes and names
|
||||||
|
SIZES=(480 720 1080 1440 2160)
|
||||||
|
NAMES=("mobile" "tablet" "hd" "twok" "uhd")
|
||||||
|
|
||||||
|
# Create destination directory if it doesn't exist
|
||||||
|
mkdir -p "$DEST_DIR"
|
||||||
|
|
||||||
|
for video in "$SOURCE_DIR"/*.mkv; do
|
||||||
|
if [ -f "$video" ]; then
|
||||||
|
filename=$(basename "$video" .mkv)
|
||||||
|
echo "Processing: $filename"
|
||||||
|
|
||||||
|
# Get video dimensions
|
||||||
|
width=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=p=0 "$video")
|
||||||
|
height=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "$video")
|
||||||
|
|
||||||
|
# Process each size
|
||||||
|
for i in "${!SIZES[@]}"; do
|
||||||
|
size="${SIZES[$i]}"
|
||||||
|
name="${NAMES[$i]}"
|
||||||
|
|
||||||
|
echo "Debug: Processing $name with height=$size"
|
||||||
|
|
||||||
|
if [ "$size" -le "$height" ]; then
|
||||||
|
echo "Processing size $name (${size}p)"
|
||||||
|
# MP4
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libx264 \
|
||||||
|
-vf "scale=-1:${size}" \
|
||||||
|
-preset slow \
|
||||||
|
-crf 23 \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_${name}.mp4"
|
||||||
|
|
||||||
|
# WebM
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libvpx-vp9 \
|
||||||
|
-deadline good \
|
||||||
|
-cpu-used 2 \
|
||||||
|
-row-mt 1 \
|
||||||
|
-threads 8 \
|
||||||
|
-vf "scale=-1:${size}" \
|
||||||
|
-quality good \
|
||||||
|
-an \
|
||||||
|
-crf 20 \
|
||||||
|
"${DEST_DIR}/${filename}_${name}.webm"
|
||||||
|
|
||||||
|
# Ogg
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libtheora \
|
||||||
|
-q:v 5 \
|
||||||
|
-vf "scale=-1:${size}" \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_${name}.ogv"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create original resolution version
|
||||||
|
echo "Creating original resolution version"
|
||||||
|
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libx264 \
|
||||||
|
-preset slow \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_full.mp4"
|
||||||
|
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libvpx-vp9 \
|
||||||
|
-deadline good \
|
||||||
|
-cpu-used 2 \
|
||||||
|
-row-mt 1 \
|
||||||
|
-threads 8 \
|
||||||
|
-quality good \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_full.webm"
|
||||||
|
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libtheora \
|
||||||
|
-q:v 5 \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_full.ogv"
|
||||||
|
|
||||||
|
echo "Completed processing: $filename"
|
||||||
|
echo "----------------------------"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "All videos have been processed."
|
||||||
BIN
source-videos/ceboola_gradient-white.mkv
Executable file
BIN
source-videos/ceboola_gradient-white.mkv
Executable file
Binary file not shown.
BIN
source-videos/ceboola_gradient.mkv
Executable file
BIN
source-videos/ceboola_gradient.mkv
Executable file
Binary file not shown.
|
|
@ -2,10 +2,12 @@ import "../../globals.css";
|
||||||
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { Lang, locales } from "@/i18n/locales";
|
import { getLocale, Lang } from "@/i18n/locales";
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
|
|
||||||
|
import { ThemeProvider } from "@/components/providers";
|
||||||
import { Oxanium } from "next/font/google";
|
import { Oxanium } from "next/font/google";
|
||||||
|
import { headers } from "next/headers";
|
||||||
const oxanium = Oxanium({ subsets: ["latin-ext"] })
|
const oxanium = Oxanium({ subsets: ["latin-ext"] })
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -16,20 +18,34 @@ export default async function RootLayout({
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
params: Promise<{ locale: Lang }>
|
params: Promise<{ locale: Lang }>
|
||||||
}) {
|
}) {
|
||||||
const { locale } = await params
|
const [{ locale }, head] = await Promise.all([
|
||||||
const currentLang = locales.includes(locale) ? locale : "en"
|
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 (
|
return (
|
||||||
<html lang={currentLang} className={`${oxanium.className} dark`}>
|
<html lang={currentLang} className={`${oxanium.className} ${defaultTheme}`}>
|
||||||
<Head>
|
<Head>
|
||||||
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<link rel="shortcut icon" href="/favicon.ico" />
|
<link rel="shortcut icon" href="/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
<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" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
</Head>
|
</Head>
|
||||||
<body className="bg-background text:foreground antialiased">
|
<body className="bg-background text:foreground antialiased">
|
||||||
|
<ThemeProvider>
|
||||||
{children}
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
4
src/app/[locale]/loading.tsx
Normal file
4
src/app/[locale]/loading.tsx
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
export default function Loading() {
|
||||||
|
return <div>Loading...</div>
|
||||||
|
}
|
||||||
5
src/app/[locale]/not-found.tsx
Normal file
5
src/app/[locale]/not-found.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
|
return <div>Not Found</div>
|
||||||
|
}
|
||||||
|
|
@ -1,130 +1,17 @@
|
||||||
"use client"
|
import LandingPage from "@/components/landing-page";
|
||||||
|
import { getLocale, Lang } from "@/i18n/locales";
|
||||||
import { Nav } from "@/components/nav"
|
import { translations } from "@/i18n/translations";
|
||||||
import { translations } from "@/i18n/translations"
|
|
||||||
import { useEffect, useRef } from "react"
|
|
||||||
|
|
||||||
|
|
||||||
export default function Home() {
|
export default async function Home(
|
||||||
const videoRef = useRef<HTMLVideoElement>(null)
|
{ params }
|
||||||
const t = translations.pl // For now using Polish, could be made dynamic
|
:
|
||||||
|
{ params: Promise<{ locale: Lang }> }
|
||||||
|
) {
|
||||||
|
const { locale } = await params
|
||||||
|
const currentLocale = getLocale(locale)
|
||||||
|
const t = translations[currentLocale];
|
||||||
|
|
||||||
|
return <LandingPage t={t} />
|
||||||
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>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
173
src/components/landing-page.tsx
Normal file
173
src/components/landing-page.tsx
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
"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 getSource({
|
||||||
|
src,
|
||||||
|
type
|
||||||
|
}: {
|
||||||
|
src: string;
|
||||||
|
type: 'mp4' | 'webm' | 'ogv'
|
||||||
|
}) {
|
||||||
|
const sourceType = `video/${type}`
|
||||||
|
return [
|
||||||
|
<source
|
||||||
|
key={`mobile-${type}`}
|
||||||
|
media="(max-width: 640px)"
|
||||||
|
src={src.replace('.mp4', `_mobile.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`tablet-${type}`}
|
||||||
|
media="(max-width: 1024px)"
|
||||||
|
src={src.replace('.mp4', `_tablet.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`hd-${type}`}
|
||||||
|
media="(max-width: 1920px)"
|
||||||
|
src={src.replace('.mp4', `_hd.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`twok-${type}`}
|
||||||
|
media="(max-width: 2560px)"
|
||||||
|
src={src.replace('.mp4', `_2k.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`uhd-${type}`}
|
||||||
|
media="(max-width: 3840px)"
|
||||||
|
src={src.replace('.mp4', `_uhd.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`original-${type}`}
|
||||||
|
media="(min-width: 3841px)"
|
||||||
|
src={src.replace('.mp4', `_original.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function Video({ sourceBase, hidden }: {
|
||||||
|
sourceBase: string;
|
||||||
|
hidden: boolean;
|
||||||
|
}) {
|
||||||
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
if (!videoRef.current || hidden) return;
|
||||||
|
|
||||||
|
videoRef.current.play();
|
||||||
|
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]);
|
||||||
|
|
||||||
|
const sources = [...getSource({ src: sourceBase, type: 'mp4' }), ...getSource({ src: sourceBase, type: 'ogv' }), ...getSource({ src: sourceBase, type: 'webm' })]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<video
|
||||||
|
ref={videoRef}
|
||||||
|
preload="auto"
|
||||||
|
autoPlay
|
||||||
|
muted
|
||||||
|
loop
|
||||||
|
playsInline
|
||||||
|
webkit-playsinline="true"
|
||||||
|
x5-playsinline="true"
|
||||||
|
className={cn("w-full h-full object-contain parallax-video", {
|
||||||
|
hidden,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{sources.map(x => x)}
|
||||||
|
</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 id="hero" className="h-screen relative overflow-hidden dark:bg-black light:bg-white ">
|
||||||
|
<div className="absolute inset-0 opacity-80">
|
||||||
|
<Video sourceBase="/videos/ceboola_gradient.mp4" hidden={theme === 'light'} />
|
||||||
|
<Video sourceBase="/videos/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 light:text-background">{t.hero.title}</h1>
|
||||||
|
<p className="mt-2 text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl-text:7xl 2xl:text-8xl text-primary">{t.hero.subtitle}</p>
|
||||||
|
<p className="mt-2 text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl-text:7xl 2xl:text-8xl text-primary ">{t.when.date}</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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -3,10 +3,12 @@ import { Button } from "@/components/ui/button"
|
||||||
import { Sections, type translations } from "@/i18n/translations"
|
import { Sections, type translations } from "@/i18n/translations"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { MoonIcon, SunIcon } from "lucide-react"
|
import { MoonIcon, SunIcon } from "lucide-react"
|
||||||
import { useCallback, useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { MobileNav } from "./mobile-nav"
|
import { MobileNav } from "./mobile-nav"
|
||||||
|
import { useTheme } from "./providers"
|
||||||
|
|
||||||
const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
|
const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
|
||||||
|
"hero",
|
||||||
"about",
|
"about",
|
||||||
"where",
|
"where",
|
||||||
"when",
|
"when",
|
||||||
|
|
@ -16,41 +18,12 @@ const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
|
||||||
"contact",
|
"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({
|
export function Nav({
|
||||||
t,
|
t,
|
||||||
}: {
|
}: {
|
||||||
t: typeof translations.pl
|
t: typeof translations.pl
|
||||||
}) {
|
}) {
|
||||||
const [theme, setTheme] = useTheme()
|
const { theme, setTheme } = useTheme()
|
||||||
const [activeSection, setActiveSection] = useState<Sections>("about")
|
const [activeSection, setActiveSection] = useState<Sections>("about")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -90,7 +63,7 @@ export function Nav({
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="fixed top-0 left-0 right-0 z-50 backdrop-blur-xs border-b">
|
<nav className="fixed top-0 left-0 right-0 z-50 dark:backdrop-blur-xs light:backdrop-blur-sm bg-background/80 border-b">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="flex items-center justify-between h-16">
|
<div className="flex items-center justify-between h-16">
|
||||||
<a href="#" className="text-xl font-bold tracking-tighter hover:text-primary transition-colors">
|
<a href="#" className="text-xl font-bold tracking-tighter hover:text-primary transition-colors">
|
||||||
|
|
|
||||||
43
src/components/providers.tsx
Normal file
43
src/components/providers.tsx
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ const SheetOverlay = React.forwardRef<
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<SheetPrimitive.Overlay
|
<SheetPrimitive.Overlay
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,9 @@
|
||||||
export const locales = ["en", "pl"] as const;
|
export const locales = ["en", "pl"] as const;
|
||||||
|
|
||||||
export type Lang = (typeof locales)[number];
|
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];
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
const common = {
|
const common = {
|
||||||
jgs7: "JGS7",
|
jgs7: "JGS font Jgs font by Adel Faure. Distributed by velvetyne.fr",
|
||||||
oxanium: "Oxanium",
|
oxanium: "Oxanium",
|
||||||
};
|
};
|
||||||
|
|
||||||
const pl = {
|
const pl = {
|
||||||
siteTitle: "CEBULACAMP",
|
siteTitle: "CEBULACAMP",
|
||||||
nav: {
|
nav: {
|
||||||
|
hero: "Cebula",
|
||||||
about: "O nas",
|
about: "O nas",
|
||||||
when: "Kiedy",
|
when: "Kiedy",
|
||||||
where: "Gdzie",
|
where: "Gdzie",
|
||||||
|
|
@ -15,7 +16,7 @@ const pl = {
|
||||||
accommodation: "Nocleg",
|
accommodation: "Nocleg",
|
||||||
},
|
},
|
||||||
mobileNav: {
|
mobileNav: {
|
||||||
toggleMenu: "Aktywnuj menu",
|
toggleMenu: "Aktywuj menu",
|
||||||
menu: "Menu",
|
menu: "Menu",
|
||||||
},
|
},
|
||||||
hero: {
|
hero: {
|
||||||
|
|
@ -35,7 +36,7 @@ const pl = {
|
||||||
title: "Kiedy",
|
title: "Kiedy",
|
||||||
date: "28-31.08.2025",
|
date: "28-31.08.2025",
|
||||||
extra:
|
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: {
|
tickets: {
|
||||||
title: "Bilety",
|
title: "Bilety",
|
||||||
|
|
@ -60,19 +61,79 @@ const pl = {
|
||||||
usedFonts: "Użyte fonty:",
|
usedFonts: "Użyte fonty:",
|
||||||
oxanium: common.oxanium,
|
oxanium: common.oxanium,
|
||||||
jgs7: common.jgs7,
|
jgs7: common.jgs7,
|
||||||
jgs7RemovedGlyphs: "z usuniętymi znakami",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const en = {};
|
const en = {
|
||||||
|
siteTitle: "CEBULACAMP",
|
||||||
|
nav: {
|
||||||
|
hero: "Onion",
|
||||||
|
about: "About us",
|
||||||
|
when: "When",
|
||||||
|
where: "Where",
|
||||||
|
food: "Food",
|
||||||
|
contact: "Contact",
|
||||||
|
tickets: "Tickets",
|
||||||
|
accommodation: "Accomodation",
|
||||||
|
},
|
||||||
|
mobileNav: {
|
||||||
|
toggleMenu: "Toggle menu",
|
||||||
|
menu: "Menu",
|
||||||
|
},
|
||||||
|
hero: {
|
||||||
|
title: "CEBULACAMP 2025",
|
||||||
|
subtitle: "REACTIVATED",
|
||||||
|
},
|
||||||
|
about: {
|
||||||
|
title: "About us",
|
||||||
|
description:
|
||||||
|
"A gathering of hackers, open source enthusiasts, and free spirits. Organized by hackers for hackers. There will be mate, there will be utopia, there will be chill vibes.\n\nExpect interesting presentations, weird art installations, and lots of discussions. You can talk about your project, demonstrate your constructed toys, or create something together during the event.",
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
title: "Where",
|
||||||
|
location:
|
||||||
|
"Łącznik club, Tramwajowa 1-3, Wrocław, next to Hackerspace Wrocław",
|
||||||
|
},
|
||||||
|
when: {
|
||||||
|
title: "When",
|
||||||
|
date: "28-31.08.2025",
|
||||||
|
extra:
|
||||||
|
"those willing to help with preparations are welcome to join on Day 0, August 27",
|
||||||
|
},
|
||||||
|
tickets: {
|
||||||
|
title: "Tickets",
|
||||||
|
status: "soon",
|
||||||
|
},
|
||||||
|
accommodation: {
|
||||||
|
title: "Accommodation",
|
||||||
|
description:
|
||||||
|
"in the true camp spirit, we're preparing a fenced area for pitching tents with toilets and shower facilities. The number of tent spots is limited, first-come-first-served basis. There's also the possibility of arranging your own accommodation in a nearby hotel or student dormitories.",
|
||||||
|
},
|
||||||
|
food: {
|
||||||
|
title: "Food",
|
||||||
|
description:
|
||||||
|
"self-catering, there are restaurants with delivery in the area, and we plan to enhance the evenings with communal barbecues.",
|
||||||
|
},
|
||||||
|
contact: {
|
||||||
|
title: "Contact",
|
||||||
|
email: "contact@cebula.camp",
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
title: "Credits",
|
||||||
|
usedFonts: "Used fonts:",
|
||||||
|
oxanium: common.oxanium,
|
||||||
|
jgs7: common.jgs7,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const translations: {
|
export const translations: {
|
||||||
en: typeof pl;
|
en: typeof pl;
|
||||||
pl: typeof pl;
|
pl: typeof pl;
|
||||||
} = {
|
} = {
|
||||||
pl: pl,
|
pl: pl,
|
||||||
// @ts-expect-error This should fail so far, remove me when translations are properly added to "en" object.
|
|
||||||
en: en,
|
en: en,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Translations = typeof pl;
|
||||||
|
|
||||||
export type Sections = keyof (typeof pl)["nav"];
|
export type Sections = keyof (typeof pl)["nav"];
|
||||||
|
|
|
||||||
|
|
@ -27,5 +27,7 @@ export function middleware(request: NextRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
matcher: "/((?!api|static|.*\\..*|_next).*)",
|
||||||
|
|
||||||
|
// matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
||||||
};
|
};
|
||||||
|
|
@ -22,6 +22,6 @@
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "tailwind.config.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue