Compare commits
1 Commits
main
...
feature/ne
Author | SHA1 | Date |
---|---|---|
RaviAnand Mohabir | bae8182970 | 5 months ago |
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"general": {
|
||||||
|
"home": "Home",
|
||||||
|
"getStarted": "Get started by editing"
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,11 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
import createNextIntlPlugin from "next-intl/plugin";
|
||||||
import { withPayload } from "@payloadcms/next/withPayload";
|
import { withPayload } from "@payloadcms/next/withPayload";
|
||||||
|
|
||||||
|
const withNextIntl = createNextIntlPlugin("./src/i18n/config.ts");
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
/* config options here */
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withPayload(nextConfig);
|
export default withNextIntl(withPayload(nextConfig));
|
||||||
|
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.
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,98 @@
|
|||||||
import { Box } from "styled-system/jsx";
|
import Image from "next/image";
|
||||||
import { getI18n } from "@/i18n/server";
|
import styles from "./page.module.css";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export default async function Home() {
|
export default function Home() {
|
||||||
const t = await getI18n();
|
const t = useTranslations("HomePage");
|
||||||
|
|
||||||
return <Box></Box>;
|
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>
|
||||||
|
{t("getStarted")} <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,3 +1,3 @@
|
|||||||
import { Locale } from "@/i18n/locales";
|
import { Locale } from "@/i18n/config";
|
||||||
|
|
||||||
export type Params = { locale: Locale };
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1 @@
|
|||||||
|
export const importMap = {};
|
||||||
|
|
||||||
export const importMap = {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -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,40 +1,51 @@
|
|||||||
import { forwardRef } from 'react'
|
import { forwardRef } from "react";
|
||||||
import { Center, styled } from 'styled-system/jsx'
|
import { Center, styled } from "styled-system/jsx";
|
||||||
import { Spinner } from './spinner'
|
import { Spinner } from "./spinner";
|
||||||
import { Button as StyledButton, type ButtonProps as StyledButtonProps } from './styled/button'
|
import {
|
||||||
|
Button as StyledButton,
|
||||||
|
type ButtonProps as StyledButtonProps,
|
||||||
|
} from "./styled/button";
|
||||||
|
|
||||||
interface ButtonLoadingProps {
|
interface ButtonLoadingProps {
|
||||||
loading?: boolean
|
loading?: boolean;
|
||||||
loadingText?: React.ReactNode
|
loadingText?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ButtonProps extends StyledButtonProps, ButtonLoadingProps {}
|
export interface ButtonProps extends StyledButtonProps, ButtonLoadingProps {}
|
||||||
|
|
||||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
const { loading, disabled, loadingText, children, ...rest } = props
|
(props, ref) => {
|
||||||
|
const { loading, disabled, loadingText, children, ...rest } = props;
|
||||||
|
|
||||||
const trulyDisabled = loading || disabled
|
const trulyDisabled = loading || disabled;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledButton disabled={trulyDisabled} ref={ref} {...rest}>
|
<StyledButton disabled={trulyDisabled} ref={ref} {...rest}>
|
||||||
{loading && !loadingText ? (
|
{loading && !loadingText ? (
|
||||||
<>
|
<>
|
||||||
<ButtonSpinner />
|
<ButtonSpinner />
|
||||||
<styled.span opacity={0}>{children}</styled.span>
|
<styled.span opacity={0}>{children}</styled.span>
|
||||||
</>
|
</>
|
||||||
) : loadingText ? (
|
) : loadingText ? (
|
||||||
loadingText
|
loadingText
|
||||||
) : (
|
) : (
|
||||||
children
|
children
|
||||||
)}
|
)}
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
)
|
);
|
||||||
})
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Button.displayName = 'Button'
|
Button.displayName = "Button";
|
||||||
|
|
||||||
const ButtonSpinner = () => (
|
const ButtonSpinner = () => (
|
||||||
<Center inline position="absolute" transform="translate(-50%, -50%)" top="50%" insetStart="50%">
|
<Center
|
||||||
|
inline
|
||||||
|
position="absolute"
|
||||||
|
transform="translate(-50%, -50%)"
|
||||||
|
top="50%"
|
||||||
|
insetStart="50%"
|
||||||
|
>
|
||||||
<Spinner colorPalette="gray" />
|
<Spinner colorPalette="gray" />
|
||||||
</Center>
|
</Center>
|
||||||
)
|
);
|
||||||
|
@ -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,7 +1,7 @@
|
|||||||
import { ark } from '@ark-ui/react/factory'
|
import { ark } from "@ark-ui/react/factory";
|
||||||
import { styled } from 'styled-system/jsx'
|
import { styled } from "styled-system/jsx";
|
||||||
import { button } from 'styled-system/recipes'
|
import { button } from "styled-system/recipes";
|
||||||
import type { ComponentProps } from 'styled-system/types'
|
import type { ComponentProps } from "styled-system/types";
|
||||||
|
|
||||||
export type ButtonProps = ComponentProps<typeof Button>
|
export type ButtonProps = ComponentProps<typeof Button>;
|
||||||
export const Button = styled(ark.button, button)
|
export const Button = styled(ark.button, button);
|
||||||
|
@ -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);
|
|
@ -0,0 +1,13 @@
|
|||||||
|
import { Locale } from "@/i18n/locales";
|
||||||
|
import { getRequestConfig } from "next-intl/server";
|
||||||
|
import { notFound } from "next/navigation";
|
||||||
|
import { routing } from "@/i18n/routing";
|
||||||
|
|
||||||
|
export default getRequestConfig(async ({ locale }) => {
|
||||||
|
// Validate that the incoming `locale` parameter is valid
|
||||||
|
if (!routing.locales.includes(locale as Locale)) notFound();
|
||||||
|
|
||||||
|
return {
|
||||||
|
messages: (await import(`../messages/${locale}.json`)).default,
|
||||||
|
};
|
||||||
|
});
|
@ -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;
|
|
@ -0,0 +1,8 @@
|
|||||||
|
// Lightweight wrappers around Next.js' navigation APIs
|
||||||
|
|
||||||
|
import { createLocalizedPathnamesNavigation } from "next-intl/navigation";
|
||||||
|
import { routing } from "@/i18n/routing";
|
||||||
|
|
||||||
|
// that will consider the routing configuration
|
||||||
|
export const { Link, redirect, usePathname, useRouter } =
|
||||||
|
createLocalizedPathnamesNavigation(routing);
|
@ -0,0 +1,20 @@
|
|||||||
|
import { defaultLocale, locales } from "./locales";
|
||||||
|
|
||||||
|
import { defineRouting } from "next-intl/routing";
|
||||||
|
|
||||||
|
export const routing = defineRouting({
|
||||||
|
// A list of all locales that are supported
|
||||||
|
locales: locales.map(({ code }) => code),
|
||||||
|
|
||||||
|
// Used when no locale matches
|
||||||
|
defaultLocale,
|
||||||
|
|
||||||
|
localePrefix: "as-needed",
|
||||||
|
|
||||||
|
pathnames: {
|
||||||
|
"/about": {
|
||||||
|
en: "/about",
|
||||||
|
de: "/ueber-uns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -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;
|
|
||||||
};
|
|
Loading…
Reference in New Issue