Compare commits
No commits in common. 'a3246fc97231edb4b4b87ee58a07ef4cd59f2393' and 'c68f2934da5655b03994e6c6878c38d31e00e72f' have entirely different histories.
a3246fc972
...
c68f2934da
File diff suppressed because it is too large
Load Diff
@ -1,127 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* This file was automatically generated by Payload.
|
|
||||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
|
||||||
* and re-run `payload generate:types` to regenerate this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface Config {
|
|
||||||
auth: {
|
|
||||||
users: UserAuthOperations;
|
|
||||||
};
|
|
||||||
collections: {
|
|
||||||
users: User;
|
|
||||||
media: Media;
|
|
||||||
'payload-preferences': PayloadPreference;
|
|
||||||
'payload-migrations': PayloadMigration;
|
|
||||||
};
|
|
||||||
db: {
|
|
||||||
defaultIDType: string;
|
|
||||||
};
|
|
||||||
globals: {};
|
|
||||||
locale: null;
|
|
||||||
user: User & {
|
|
||||||
collection: 'users';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export interface UserAuthOperations {
|
|
||||||
forgotPassword: {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
login: {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
registerFirstUser: {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
unlock: {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
|
||||||
* via the `definition` "users".
|
|
||||||
*/
|
|
||||||
export interface User {
|
|
||||||
id: string;
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
email: string;
|
|
||||||
resetPasswordToken?: string | null;
|
|
||||||
resetPasswordExpiration?: string | null;
|
|
||||||
salt?: string | null;
|
|
||||||
hash?: string | null;
|
|
||||||
loginAttempts?: number | null;
|
|
||||||
lockUntil?: string | null;
|
|
||||||
password?: string | null;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
|
||||||
* via the `definition` "media".
|
|
||||||
*/
|
|
||||||
export interface Media {
|
|
||||||
id: string;
|
|
||||||
alt?: string | null;
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
url?: string | null;
|
|
||||||
thumbnailURL?: string | null;
|
|
||||||
filename?: string | null;
|
|
||||||
mimeType?: string | null;
|
|
||||||
filesize?: number | null;
|
|
||||||
width?: number | null;
|
|
||||||
height?: number | null;
|
|
||||||
focalX?: number | null;
|
|
||||||
focalY?: number | null;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
|
||||||
* via the `definition` "payload-preferences".
|
|
||||||
*/
|
|
||||||
export interface PayloadPreference {
|
|
||||||
id: string;
|
|
||||||
user: {
|
|
||||||
relationTo: 'users';
|
|
||||||
value: string | User;
|
|
||||||
};
|
|
||||||
key?: string | null;
|
|
||||||
value?:
|
|
||||||
| {
|
|
||||||
[k: string]: unknown;
|
|
||||||
}
|
|
||||||
| unknown[]
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| null;
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
|
||||||
* via the `definition` "payload-migrations".
|
|
||||||
*/
|
|
||||||
export interface PayloadMigration {
|
|
||||||
id: string;
|
|
||||||
name?: string | null;
|
|
||||||
batch?: number | null;
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
|
||||||
* via the `definition` "auth".
|
|
||||||
*/
|
|
||||||
export interface Auth {
|
|
||||||
[k: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
declare module 'payload' {
|
|
||||||
export interface GeneratedTypes extends Config {}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,39 +0,0 @@
|
|||||||
import "../globals.css";
|
|
||||||
|
|
||||||
import Footer from "@/components/layout/footer";
|
|
||||||
import { I18nProviderClient } from "@/i18n/client";
|
|
||||||
import type { Metadata } from "next";
|
|
||||||
import Navbar from "@/components/layout/navbar";
|
|
||||||
import { Params } from "./params";
|
|
||||||
import localFont from "next/font/local";
|
|
||||||
import { styled } from "@styled-system/jsx";
|
|
||||||
|
|
||||||
const moderustic = localFont({
|
|
||||||
src: "../Moderustic-VariableFont_wght.ttf",
|
|
||||||
display: "swap",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Create Next App",
|
|
||||||
description: "Generated by create next app",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function RootLayout({
|
|
||||||
params: { locale },
|
|
||||||
children,
|
|
||||||
}: Readonly<{
|
|
||||||
children: React.ReactNode;
|
|
||||||
params: Params;
|
|
||||||
}>) {
|
|
||||||
return (
|
|
||||||
<html lang="en">
|
|
||||||
<I18nProviderClient locale={locale}>
|
|
||||||
<styled.body className={moderustic.className}>
|
|
||||||
<Navbar />
|
|
||||||
<styled.main pb={20}>{children}</styled.main>
|
|
||||||
<Footer />
|
|
||||||
</styled.body>
|
|
||||||
</I18nProviderClient>
|
|
||||||
</html>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import { Box } from "styled-system/jsx";
|
|
||||||
import { getI18n } from "@/i18n/server";
|
|
||||||
|
|
||||||
export default async function Home() {
|
|
||||||
const t = await getI18n();
|
|
||||||
|
|
||||||
return <Box></Box>;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
import { Locale } from "@/i18n/locales";
|
|
||||||
|
|
||||||
export type Params = { locale: Locale };
|
|
@ -1 +1,44 @@
|
|||||||
@layer reset, base, tokens, recipes, utilities;
|
@layer reset, base, tokens, recipes, utilities;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background: #ffffff;
|
||||||
|
--foreground: #171717;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--background: #0a0a0a;
|
||||||
|
--foreground: #ededed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--foreground);
|
||||||
|
background: var(--background);
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
html {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import localFont from "next/font/local";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
const geistSans = localFont({
|
||||||
|
src: "./fonts/GeistVF.woff",
|
||||||
|
variable: "--font-geist-sans",
|
||||||
|
weight: "100 900",
|
||||||
|
});
|
||||||
|
const geistMono = localFont({
|
||||||
|
src: "./fonts/GeistMonoVF.woff",
|
||||||
|
variable: "--font-geist-mono",
|
||||||
|
weight: "100 900",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Create Next App",
|
||||||
|
description: "Generated by create next app",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body className={`${geistSans.variable} ${geistMono.variable}`}>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
.page {
|
||||||
|
--gray-rgb: 0, 0, 0;
|
||||||
|
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
|
||||||
|
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
|
||||||
|
|
||||||
|
--button-primary-hover: #383838;
|
||||||
|
--button-secondary-hover: #f2f2f2;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 20px 1fr 20px;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
min-height: 100svh;
|
||||||
|
padding: 80px;
|
||||||
|
gap: 64px;
|
||||||
|
font-family: var(--font-geist-sans);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.page {
|
||||||
|
--gray-rgb: 255, 255, 255;
|
||||||
|
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
|
||||||
|
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
|
||||||
|
|
||||||
|
--button-primary-hover: #ccc;
|
||||||
|
--button-secondary-hover: #1a1a1a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 32px;
|
||||||
|
grid-row-start: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main ol {
|
||||||
|
font-family: var(--font-geist-mono);
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main li:not(:last-of-type) {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main code {
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--gray-alpha-100);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctas {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctas a {
|
||||||
|
appearance: none;
|
||||||
|
border-radius: 128px;
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 20px;
|
||||||
|
border: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
transition:
|
||||||
|
background 0.2s,
|
||||||
|
color 0.2s,
|
||||||
|
border-color 0.2s;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.primary {
|
||||||
|
background: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.secondary {
|
||||||
|
border-color: var(--gray-alpha-200);
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
grid-row-start: 3;
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer img {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable hover only on non-touch devices */
|
||||||
|
@media (hover: hover) and (pointer: fine) {
|
||||||
|
a.primary:hover {
|
||||||
|
background: var(--button-primary-hover);
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.secondary:hover {
|
||||||
|
background: var(--button-secondary-hover);
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.page {
|
||||||
|
padding: 32px;
|
||||||
|
padding-bottom: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main ol {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctas {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctas a {
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.secondary {
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.logo {
|
||||||
|
filter: invert();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import styles from "./page.module.css";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className={styles.page}>
|
||||||
|
<main className={styles.main}>
|
||||||
|
<Image
|
||||||
|
className={styles.logo}
|
||||||
|
src="https://nextjs.org/icons/next.svg"
|
||||||
|
alt="Next.js logo"
|
||||||
|
width={180}
|
||||||
|
height={38}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
Get started by editing <code>src/app/page.tsx</code>.
|
||||||
|
</li>
|
||||||
|
<li>Save and see your changes instantly.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<div className={styles.ctas}>
|
||||||
|
<a
|
||||||
|
className={styles.primary}
|
||||||
|
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
className={styles.logo}
|
||||||
|
src="https://nextjs.org/icons/vercel.svg"
|
||||||
|
alt="Vercel logomark"
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
Deploy now
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={styles.secondary}
|
||||||
|
>
|
||||||
|
Read our docs
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer className={styles.footer}>
|
||||||
|
<a
|
||||||
|
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
aria-hidden
|
||||||
|
src="https://nextjs.org/icons/file.svg"
|
||||||
|
alt="File icon"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
Learn
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
aria-hidden
|
||||||
|
src="https://nextjs.org/icons/window.svg"
|
||||||
|
alt="Window icon"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
Examples
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
aria-hidden
|
||||||
|
src="https://nextjs.org/icons/globe.svg"
|
||||||
|
alt="Globe icon"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
Go to nextjs.org →
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
import { Text } from "@/components/ui/text";
|
|
||||||
import { stack } from "@styled-system/patterns";
|
|
||||||
import { styled } from "@styled-system/jsx";
|
|
||||||
|
|
||||||
export default async function Footer() {
|
|
||||||
return (
|
|
||||||
<styled.footer
|
|
||||||
minH={60}
|
|
||||||
p={8}
|
|
||||||
className={stack({ gap: 4, justify: "end" })}
|
|
||||||
>
|
|
||||||
<Text textAlign="center">Powered by Jenyus</Text>
|
|
||||||
</styled.footer>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { defaultLocale, locales } from "@/i18n/locales";
|
|
||||||
import { useChangeLocale, useCurrentLocale, useI18n } from "@/i18n/client";
|
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Menu } from "@/components/ui/menu";
|
|
||||||
|
|
||||||
export default function LanguagePicker(props: Menu.RootProps) {
|
|
||||||
const changeLocale = useChangeLocale();
|
|
||||||
const locale = useCurrentLocale();
|
|
||||||
const t = useI18n();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu.Root {...props} positioning={{ offset: { crossAxis: -35 } }}>
|
|
||||||
<Menu.Trigger asChild>
|
|
||||||
<Button variant="outline" size={props.size}>
|
|
||||||
{t(`general.${locale}`)}
|
|
||||||
</Button>
|
|
||||||
</Menu.Trigger>
|
|
||||||
<Menu.Positioner>
|
|
||||||
<Menu.Content right={0}>
|
|
||||||
<Menu.ItemGroup>
|
|
||||||
{locales.map((locale) => (
|
|
||||||
<Menu.Item
|
|
||||||
asChild
|
|
||||||
key={locale.code}
|
|
||||||
value={locale.code}
|
|
||||||
onClick={() => changeLocale(locale.code)}
|
|
||||||
>
|
|
||||||
<Button>
|
|
||||||
{locale.label[locale.code] ?? locale.label[defaultLocale]}
|
|
||||||
</Button>
|
|
||||||
</Menu.Item>
|
|
||||||
))}
|
|
||||||
</Menu.ItemGroup>
|
|
||||||
</Menu.Content>
|
|
||||||
</Menu.Positioner>
|
|
||||||
</Menu.Root>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { Menu, X } from "lucide-react";
|
|
||||||
|
|
||||||
import { IconButton } from "@/components/ui/icon-button";
|
|
||||||
import Image from "next/image";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { Media } from "@/payload-types";
|
|
||||||
import NavLink from "@/components/layout/nav-link";
|
|
||||||
import { css } from "@styled-system/css";
|
|
||||||
import { styled } from "@styled-system/jsx";
|
|
||||||
import { useI18n } from "@/i18n/client";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
export default function MobileNav() {
|
|
||||||
const [show, setShow] = useState(false);
|
|
||||||
const t = useI18n();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<styled.div className={css({ display: "block", sm: { display: "none" } })}>
|
|
||||||
<IconButton onClick={() => setShow((show) => !show)}>
|
|
||||||
<Menu />
|
|
||||||
</IconButton>
|
|
||||||
<styled.div
|
|
||||||
display={show ? "flex" : "none"}
|
|
||||||
zIndex={600}
|
|
||||||
position="fixed"
|
|
||||||
top={0}
|
|
||||||
left={0}
|
|
||||||
right={0}
|
|
||||||
bottom={0}
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
flexDir="column"
|
|
||||||
gap={4}
|
|
||||||
p={12}
|
|
||||||
bg="white"
|
|
||||||
alignItems="center"
|
|
||||||
onClick={() => setShow(false)}
|
|
||||||
>
|
|
||||||
<styled.div alignSelf="stretch" display="flex" justifyContent="end">
|
|
||||||
<IconButton variant="ghost" onClick={() => setShow(false)}>
|
|
||||||
<X />
|
|
||||||
</IconButton>
|
|
||||||
</styled.div>
|
|
||||||
|
|
||||||
<Link href="/" className={css({ fontSize: 24 })}>
|
|
||||||
Localbites
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<NavLink href="/">{t("general.home")}</NavLink>
|
|
||||||
<NavLink href="/about">{t("general.about")}</NavLink>
|
|
||||||
|
|
||||||
<NavLink
|
|
||||||
href="/contact"
|
|
||||||
type="button"
|
|
||||||
className={css({
|
|
||||||
alignSelf: "stretch",
|
|
||||||
textAlign: "center",
|
|
||||||
marginTop: "auto",
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{t("general.contact")}
|
|
||||||
</NavLink>
|
|
||||||
</styled.div>
|
|
||||||
</styled.div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
import Link, { LinkProps } from "next/link";
|
|
||||||
import { RecipeVariantProps, cva, cx } from "@styled-system/css";
|
|
||||||
|
|
||||||
import { AnchorHTMLAttributes } from "react";
|
|
||||||
|
|
||||||
const variants = cva({
|
|
||||||
base: { _hover: { color: "gray.800" } },
|
|
||||||
variants: {
|
|
||||||
type: {
|
|
||||||
button: {
|
|
||||||
background: "accent.9",
|
|
||||||
color: "white",
|
|
||||||
p: 2,
|
|
||||||
borderRadius: "md",
|
|
||||||
_hover: {
|
|
||||||
background: "accent.11",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function NavLink({
|
|
||||||
className,
|
|
||||||
children,
|
|
||||||
type,
|
|
||||||
...props
|
|
||||||
}: RecipeVariantProps<typeof variants> &
|
|
||||||
Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps> &
|
|
||||||
LinkProps) {
|
|
||||||
return (
|
|
||||||
<Link className={cx(variants({ type }), className)} {...props}>
|
|
||||||
{children}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
import { Box, styled } from "@styled-system/jsx";
|
|
||||||
|
|
||||||
import LanguagePicker from "./language-picker";
|
|
||||||
import Link from "next/link";
|
|
||||||
import MobileNav from "./mobile-nav";
|
|
||||||
import NavLink from "./nav-link";
|
|
||||||
import { css } from "@styled-system/css";
|
|
||||||
import { flex } from "@styled-system/patterns";
|
|
||||||
import { getI18n } from "@/i18n/server";
|
|
||||||
|
|
||||||
export default async function Navbar() {
|
|
||||||
const t = await getI18n();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<styled.nav
|
|
||||||
className={flex({ gap: 4 })}
|
|
||||||
bg="white"
|
|
||||||
h={20}
|
|
||||||
px={4}
|
|
||||||
boxShadow="lg"
|
|
||||||
alignItems="center"
|
|
||||||
position="fixed"
|
|
||||||
zIndex={500}
|
|
||||||
top={0}
|
|
||||||
left={0}
|
|
||||||
right={0}
|
|
||||||
width="100%"
|
|
||||||
>
|
|
||||||
<Link href="/" className={css({ fontSize: 24 })}>
|
|
||||||
Localbites
|
|
||||||
</Link>
|
|
||||||
<styled.div flexGrow={1} />
|
|
||||||
|
|
||||||
<NavLink
|
|
||||||
href="/about"
|
|
||||||
className={css({ display: "none", sm: { display: "block" } })}
|
|
||||||
>
|
|
||||||
{t("general.about")}
|
|
||||||
</NavLink>
|
|
||||||
|
|
||||||
<NavLink
|
|
||||||
href="/contact"
|
|
||||||
type="button"
|
|
||||||
className={css({ display: "none", sm: { display: "block" } })}
|
|
||||||
>
|
|
||||||
{t("general.contact")}
|
|
||||||
</NavLink>
|
|
||||||
|
|
||||||
<LanguagePicker />
|
|
||||||
|
|
||||||
<MobileNav />
|
|
||||||
</styled.nav>
|
|
||||||
<Box h={20} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export { Heading, type HeadingProps } from './styled/heading'
|
|
@ -1 +0,0 @@
|
|||||||
export { IconButton, type IconButtonProps } from './styled/icon-button'
|
|
@ -1 +0,0 @@
|
|||||||
export * as Menu from './styled/menu'
|
|
@ -1,29 +0,0 @@
|
|||||||
import { forwardRef } from 'react'
|
|
||||||
import { styled } from 'styled-system/jsx'
|
|
||||||
import { Spinner as StyledSpinner, type SpinnerProps as StyledSpinnerProps } from './styled/spinner'
|
|
||||||
|
|
||||||
export interface SpinnerProps extends StyledSpinnerProps {
|
|
||||||
/**
|
|
||||||
* For accessibility, it is important to add a fallback loading text.
|
|
||||||
* This text will be visible to screen readers.
|
|
||||||
* @default "Loading..."
|
|
||||||
*/
|
|
||||||
label?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Spinner = forwardRef<HTMLDivElement, SpinnerProps>((props, ref) => {
|
|
||||||
const { label = 'Loading...', ...rest } = props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledSpinner
|
|
||||||
ref={ref}
|
|
||||||
borderBottomColor="transparent"
|
|
||||||
borderLeftColor="transparent"
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
{label && <styled.span srOnly>{label}</styled.span>}
|
|
||||||
</StyledSpinner>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
Spinner.displayName = 'Spinner'
|
|
@ -1,10 +0,0 @@
|
|||||||
import { styled } from 'styled-system/jsx'
|
|
||||||
import { type TextVariantProps, text } from 'styled-system/recipes'
|
|
||||||
import type { ComponentProps, StyledComponent } from 'styled-system/types'
|
|
||||||
|
|
||||||
type TextProps = TextVariantProps & { as?: React.ElementType }
|
|
||||||
|
|
||||||
export type HeadingProps = ComponentProps<typeof Heading>
|
|
||||||
export const Heading = styled('h2', text, {
|
|
||||||
defaultProps: { variant: 'heading' },
|
|
||||||
}) as StyledComponent<'h2', TextProps>
|
|
@ -1,9 +0,0 @@
|
|||||||
import { ark } from '@ark-ui/react/factory'
|
|
||||||
import { styled } from 'styled-system/jsx'
|
|
||||||
import { type ButtonVariantProps, button } from 'styled-system/recipes'
|
|
||||||
import type { ComponentProps } from 'styled-system/types'
|
|
||||||
|
|
||||||
export type IconButtonProps = ComponentProps<typeof IconButton>
|
|
||||||
export const IconButton = styled(ark.button, button, {
|
|
||||||
defaultProps: { px: '0' } as ButtonVariantProps,
|
|
||||||
})
|
|
@ -1,103 +0,0 @@
|
|||||||
'use client'
|
|
||||||
import type { Assign } from '@ark-ui/react'
|
|
||||||
import { Menu } from '@ark-ui/react/menu'
|
|
||||||
import { type MenuVariantProps, menu } from 'styled-system/recipes'
|
|
||||||
import type { ComponentProps, HTMLStyledProps } from 'styled-system/types'
|
|
||||||
import { createStyleContext } from './utils/create-style-context'
|
|
||||||
|
|
||||||
const { withRootProvider, withContext } = createStyleContext(menu)
|
|
||||||
|
|
||||||
export type RootProviderProps = ComponentProps<typeof RootProvider>
|
|
||||||
export const RootProvider = withRootProvider<Assign<Menu.RootProviderProps, MenuVariantProps>>(
|
|
||||||
Menu.RootProvider,
|
|
||||||
)
|
|
||||||
|
|
||||||
export type RootProps = ComponentProps<typeof Root>
|
|
||||||
export const Root = withRootProvider<Assign<Menu.RootProps, MenuVariantProps>>(Menu.Root)
|
|
||||||
|
|
||||||
export const Arrow = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.ArrowBaseProps>
|
|
||||||
>(Menu.Arrow, 'arrow')
|
|
||||||
|
|
||||||
export const ArrowTip = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.ArrowTipBaseProps>
|
|
||||||
>(Menu.ArrowTip, 'arrowTip')
|
|
||||||
|
|
||||||
export const CheckboxItem = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.CheckboxItemBaseProps>
|
|
||||||
>(Menu.CheckboxItem, 'item')
|
|
||||||
|
|
||||||
export const Content = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.ContentBaseProps>
|
|
||||||
>(Menu.Content, 'content')
|
|
||||||
|
|
||||||
export const ContextTrigger = withContext<
|
|
||||||
HTMLButtonElement,
|
|
||||||
Assign<HTMLStyledProps<'button'>, Menu.ContextTriggerBaseProps>
|
|
||||||
>(Menu.ContextTrigger, 'contextTrigger')
|
|
||||||
|
|
||||||
export const Indicator = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.IndicatorBaseProps>
|
|
||||||
>(Menu.Indicator, 'indicator')
|
|
||||||
|
|
||||||
export const ItemGroupLabel = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.ItemGroupLabelBaseProps>
|
|
||||||
>(Menu.ItemGroupLabel, 'itemGroupLabel')
|
|
||||||
|
|
||||||
export const ItemGroup = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.ItemGroupBaseProps>
|
|
||||||
>(Menu.ItemGroup, 'itemGroup')
|
|
||||||
|
|
||||||
export const ItemIndicator = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.ItemIndicatorBaseProps>
|
|
||||||
>(Menu.ItemIndicator, 'itemIndicator')
|
|
||||||
|
|
||||||
export const Item = withContext<HTMLDivElement, Assign<HTMLStyledProps<'div'>, Menu.ItemBaseProps>>(
|
|
||||||
Menu.Item,
|
|
||||||
'item',
|
|
||||||
)
|
|
||||||
|
|
||||||
export const ItemText = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.ItemTextBaseProps>
|
|
||||||
>(Menu.ItemText, 'itemText')
|
|
||||||
|
|
||||||
export const Positioner = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.PositionerBaseProps>
|
|
||||||
>(Menu.Positioner, 'positioner')
|
|
||||||
|
|
||||||
export const RadioItemGroup = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.RadioItemGroupBaseProps>
|
|
||||||
>(Menu.RadioItemGroup, 'itemGroup')
|
|
||||||
|
|
||||||
export const RadioItem = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.RadioItemBaseProps>
|
|
||||||
>(Menu.RadioItem, 'item')
|
|
||||||
|
|
||||||
export const Separator = withContext<
|
|
||||||
HTMLHRElement,
|
|
||||||
Assign<HTMLStyledProps<'hr'>, Menu.SeparatorBaseProps>
|
|
||||||
>(Menu.Separator, 'separator')
|
|
||||||
|
|
||||||
export const TriggerItem = withContext<
|
|
||||||
HTMLDivElement,
|
|
||||||
Assign<HTMLStyledProps<'div'>, Menu.TriggerItemBaseProps>
|
|
||||||
>(Menu.TriggerItem, 'triggerItem')
|
|
||||||
|
|
||||||
export const Trigger = withContext<
|
|
||||||
HTMLButtonElement,
|
|
||||||
Assign<HTMLStyledProps<'button'>, Menu.TriggerBaseProps>
|
|
||||||
>(Menu.Trigger, 'trigger')
|
|
||||||
|
|
||||||
export { MenuContext as Context } from '@ark-ui/react/menu'
|
|
@ -1,7 +0,0 @@
|
|||||||
import { ark } from '@ark-ui/react/factory'
|
|
||||||
import { styled } from 'styled-system/jsx'
|
|
||||||
import { spinner } from 'styled-system/recipes'
|
|
||||||
import type { ComponentProps } from 'styled-system/types'
|
|
||||||
|
|
||||||
export type SpinnerProps = ComponentProps<typeof Spinner>
|
|
||||||
export const Spinner = styled(ark.div, spinner)
|
|
@ -1,8 +0,0 @@
|
|||||||
import { styled } from 'styled-system/jsx'
|
|
||||||
import { type TextVariantProps, text } from 'styled-system/recipes'
|
|
||||||
import type { ComponentProps, StyledComponent } from 'styled-system/types'
|
|
||||||
|
|
||||||
type ParagraphProps = TextVariantProps & { as?: React.ElementType }
|
|
||||||
|
|
||||||
export type TextProps = ComponentProps<typeof Text>
|
|
||||||
export const Text = styled('p', text) as StyledComponent<'p', ParagraphProps>
|
|
@ -1 +0,0 @@
|
|||||||
export { Text, type TextProps } from './styled/text'
|
|
@ -1,12 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { createI18nClient } from "next-international/client";
|
|
||||||
import { importedLocales } from "./locales";
|
|
||||||
|
|
||||||
export const {
|
|
||||||
useI18n,
|
|
||||||
useScopedI18n,
|
|
||||||
I18nProviderClient,
|
|
||||||
useChangeLocale,
|
|
||||||
useCurrentLocale,
|
|
||||||
} = createI18nClient(importedLocales);
|
|
@ -1,6 +0,0 @@
|
|||||||
export default {
|
|
||||||
general: {
|
|
||||||
home: "Startseite",
|
|
||||||
getStarted: "Um zu starten bearbeiten Sie",
|
|
||||||
},
|
|
||||||
} as const;
|
|
@ -1,6 +0,0 @@
|
|||||||
export default {
|
|
||||||
general: {
|
|
||||||
home: "Home",
|
|
||||||
getStarted: "Get started by editing",
|
|
||||||
},
|
|
||||||
} as const;
|
|
@ -1,29 +0,0 @@
|
|||||||
export const locales = [
|
|
||||||
{
|
|
||||||
label: {
|
|
||||||
de: "Deutsch",
|
|
||||||
en: "German",
|
|
||||||
fr: "Allemand",
|
|
||||||
it: "Tedesco",
|
|
||||||
},
|
|
||||||
code: "de",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: {
|
|
||||||
de: "Englisch",
|
|
||||||
en: "English",
|
|
||||||
it: "Inglese",
|
|
||||||
fr: "Anglais",
|
|
||||||
},
|
|
||||||
code: "en",
|
|
||||||
},
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export type Locale = (typeof locales)[number]["code"];
|
|
||||||
|
|
||||||
export const defaultLocale: Locale = "de";
|
|
||||||
|
|
||||||
export const importedLocales = {
|
|
||||||
de: () => import("./de"),
|
|
||||||
en: () => import("./en"),
|
|
||||||
} as const;
|
|
@ -1,16 +0,0 @@
|
|||||||
import { Locale, importedLocales } from "./locales";
|
|
||||||
import {
|
|
||||||
createI18nServer,
|
|
||||||
setStaticParamsLocale,
|
|
||||||
} from "next-international/server";
|
|
||||||
|
|
||||||
export const { getI18n, getScopedI18n, getStaticParams, getCurrentLocale } =
|
|
||||||
createI18nServer(importedLocales);
|
|
||||||
|
|
||||||
export const getLocalizedI18n = async (locale: Locale) => {
|
|
||||||
const prevLocale = getCurrentLocale();
|
|
||||||
|
|
||||||
setStaticParamsLocale(locale);
|
|
||||||
|
|
||||||
return [await getI18n(), () => setStaticParamsLocale(prevLocale)] as const;
|
|
||||||
};
|
|
@ -1,18 +0,0 @@
|
|||||||
import { defaultLocale, locales } from "@/i18n/locales";
|
|
||||||
|
|
||||||
import { NextRequest } from "next/server";
|
|
||||||
import { createI18nMiddleware } from "next-international/middleware";
|
|
||||||
|
|
||||||
const I18nMiddleware = createI18nMiddleware({
|
|
||||||
locales: locales.map(({ code }) => code),
|
|
||||||
defaultLocale,
|
|
||||||
urlMappingStrategy: "rewriteDefault",
|
|
||||||
});
|
|
||||||
|
|
||||||
export function middleware(request: NextRequest) {
|
|
||||||
return I18nMiddleware(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
matcher: ["/((?!api|admin|static|.*\\..*|_next|favicon.ico|robots.txt).*)"],
|
|
||||||
};
|
|
Loading…
Reference in New Issue