TASKS-1823 WebManager module

This commit is contained in:
Igor Martyniuk
2026-08-28 08:35:58 +00:00
parent fdb2cf3837
commit c93d8c025b
654 changed files with 26232 additions and 6075 deletions
@@ -0,0 +1,47 @@
import type { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
import { notFound } from 'next/navigation';
import { resolvePageRenderData } from '#web-runtime/api/render-data';
import { pageMetadata } from '#web-runtime/lib/page-metadata';
import PageRenderer from '#web-runtime/renderer';
import { getPathname } from '#i18n/navigation';
import { routing } from '#i18n/routing';
interface PageRouteProps {
params: Promise<{
locale: string;
path?: string[];
}>;
searchParams: Promise<Record<string, string | string[] | undefined>>;
}
function routePath(path?: string[]): string {
return path?.join('/') ?? '';
}
function publicPath(locale: string, path: string): string {
return getPathname({ locale, href: path ? `/${path}` : '/' });
}
export async function generateMetadata({ params }: PageRouteProps): Promise<Metadata> {
const { locale, path } = await params;
const data = await resolvePageRenderData(locale, routePath(path));
if (!data) {
const t = await getTranslations({ locale, namespace: 'Pages.NotFoundPage' });
return { title: t('metaTitle') };
}
return pageMetadata(data, routing.defaultLocale, publicPath);
}
export default async function PublicPage({ params, searchParams }: PageRouteProps) {
const { locale, path } = await params;
const data = await resolvePageRenderData(locale, routePath(path));
if (!data) {
notFound();
}
return <PageRenderer data={data} searchParams={await searchParams} />;
}
+49
View File
@@ -0,0 +1,49 @@
import type { Metadata } from 'next';
import { hasLocale, NextIntlClientProvider } from 'next-intl';
import { getMessages, getTranslations } from 'next-intl/server';
import { notFound } from 'next/navigation';
import { siteUrl } from '#web-runtime/lib/site-url';
import { routing } from '#i18n/routing';
import '../globals.css';
export async function generateMetadata({
params
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Metadata' });
return {
metadataBase: siteUrl(),
title: {
default: t('title'),
template: t('template')
},
description: t('description')
};
}
export default async function LocaleLayout({
children,
params
}: Readonly<{
children: React.ReactNode;
params: Promise<{ locale: string }>;
}>) {
const { locale } = await params;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
const messages = await getMessages();
return (
<html lang={locale} className="scroll-smooth bg-[#f4f1e9] motion-reduce:scroll-auto">
<body className="min-h-dvh bg-[#f4f1e9] font-sans text-[#172019] antialiased">
<NextIntlClientProvider messages={messages}>{children}</NextIntlClientProvider>
</body>
</html>
);
}
+24
View File
@@ -0,0 +1,24 @@
import { useTranslations } from 'next-intl';
import { Link } from '#i18n/navigation';
import { Button } from '#shared/ui/Button';
export default function NotFound() {
const t = useTranslations('Pages.NotFoundPage');
return (
<main className="grid min-h-dvh place-items-center p-8">
<div className="max-w-176">
<p className="mb-5 font-mono text-xs font-bold tracking-[0.12em] text-[#517000] uppercase">
404
</p>
<h1 className="max-w-[15ch] text-[clamp(3rem,8vw,6rem)] leading-[0.98] font-semibold tracking-[-0.065em]">
{t('title')}
</h1>
<p className="mt-6 mb-8 text-lg text-[#5d675f]">{t('description')}</p>
<Button asChild>
<Link href="/">{t('cta')}</Link>
</Button>
</div>
</main>
);
}
+38
View File
@@ -0,0 +1,38 @@
import type { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
import { resolvePreviewRenderData } from '#web-runtime/api/render-data';
import PageRenderer from '#web-runtime/renderer';
export const metadata: Metadata = {
title: 'Preview',
robots: { index: false, follow: false }
};
export const dynamic = 'force-dynamic';
interface PreviewRouteProps {
searchParams: Promise<{ token?: string }>;
}
async function PreviewUnavailable() {
const t = await getTranslations('Pages.PreviewPage');
return (
<main className="flex min-h-dvh flex-col items-center justify-center gap-3 px-6 text-center">
<h1 className="text-2xl font-semibold">{t('title')}</h1>
<p className="max-w-md text-sm opacity-70">{t('description')}</p>
</main>
);
}
export default async function PreviewPage({ searchParams }: PreviewRouteProps) {
const { token } = await searchParams;
const data = token ? await resolvePreviewRenderData(token) : null;
if (!data) {
return <PreviewUnavailable />;
}
return <PageRenderer data={data} />;
}