Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"test:coverage": "vitest run --coverage",
"precommit": "npx lint-staged",
"prepare": "husky",
"postbuild": "pagefind --force-language ru --site .next/server/app/docs/ru --output-path build/_next/static/pagefind/ru && pagefind --force-language en --site .next/server/app/docs/en --output-path build/_next/static/pagefind/en",
"postbuild": "pagefind --force-language ru --site .next/server/app/ru/docs --output-path build/_next/static/pagefind/ru && pagefind --force-language en --site .next/server/app/docs --output-path build/_next/static/pagefind/en",
"contentful:prepare": "dotenv -e .env -- bash -c 'cf-content-types-generator -X -r -d -s $CONTENTFUL_SPACE_ID -t $CONTENTFUL_MANAGEMENT_TOKEN -o src/shared/types/contentful' && npx eslint src/shared/types/contentful/*.ts --fix && prettier --write src/shared/types/contentful/**/*.ts"
},
"engines": {
Expand Down
20 changes: 20 additions & 0 deletions src/app/[lang]/community/og.png/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PAGE_TYPE, pageStore, resolvePageLocale } from '@/entities/page';
import { generateLangParams } from '@/entities/page/helpers/generate-lang-params';
import { PagePropsOg } from '@/entities/page/types';
import { createPageTree } from '@/shared/og/view/pages-tree/generate-pages-tree';

export { DYNAMIC as dynamic } from '@/shared/constants';

export const generateStaticParams = generateLangParams;

export async function GET(_request: Request, { params }: PagePropsOg) {
const { lang } = await params;
const locale = resolvePageLocale(lang);
const { seoOgImageTitle: title, seoOgImageDescription: description } =
await pageStore.loadPage(PAGE_TYPE.COMMUNITY, locale);

return createPageTree({
title,
description,
});
}
37 changes: 37 additions & 0 deletions src/app/[lang]/community/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Metadata } from 'next';

import { resolvePageLocale } from '@/entities/page';
import { PAGE_TYPE } from '@/entities/page/constants';
import { pageStore } from '@/entities/page/model/store';
import { PageProps } from '@/entities/page/types';
import { communityMetadata } from '@/metadata/community';
import { generatePageMetadata } from '@/shared/helpers/generate-page-metadata';
import Community from '@/views/community';

export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { lang } = await params;
const locale = resolvePageLocale(lang);
const { title, seoDescription: description, seoKeywords: keywords } =
await pageStore.loadPage(PAGE_TYPE.COMMUNITY, locale);

const { canonical, robots } = communityMetadata;

const metadata = generatePageMetadata({
title,
description,
imagePath: `/${lang}/community/og.png`,
keywords,
alternates: { canonical },
robots,
});

return metadata;
}

export default async function CommunityRoute({ params }: PageProps) {
const { lang } = await params;
const locale = resolvePageLocale(lang);
const { sections } = await pageStore.loadPage(PAGE_TYPE.COMMUNITY, locale);

return <Community sections={sections} />;
}
59 changes: 59 additions & 0 deletions src/app/[lang]/courses/[slug]/og.png/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { courseStore } from '@/entities/course';
import { resolvePageLocale } from '@/entities/page';
import { PAGE_TYPE } from '@/entities/page/constants';
import { pageStore } from '@/entities/page/model/store';
import { PageProps } from '@/entities/page/types';
import { fetchAndConvertToDataUri } from '@/shared/og/utils/fetch-and-convert-to-data-uri';
import { loadImageAsDataUri } from '@/shared/og/utils/load-image-as-data-uri';
import { createCourseTree } from '@/shared/og/view/courses-tree/generate-courses-tree';

export const preferredRegion = 'auto';
const fallbackPath = 'src/shared/assets/svg/rss-logo.svg';
const logoFallbackSize = 250;

export async function generateStaticParams({ params: { lang } }: { params: Awaited<PageProps['params']> }) {
const locale = resolvePageLocale(lang);
const pages = await pageStore.loadPagesMetadata(PAGE_TYPE.COURSE, locale);

return pages.map(({ slug }) => ({
slug,
lang: 'ru',
}));
}

export async function GET(_request: Request, { params }: { params: PageProps['params'] }) {
const { slug, lang } = await params;
const locale = resolvePageLocale(lang);

const { title: courseName, courseId } = await pageStore.loadPage(PAGE_TYPE.COURSE, locale, slug);

const course = await courseStore.loadCourse(courseId);

if (!course) {
throw new Error(`Course metadata not found for id="${courseId}"`);
}

const logoWidth = course.iconSrc.width ?? logoFallbackSize;
const logoHeight = course.iconSrc.height ?? logoFallbackSize;
const logoCache = new Map<string, string>();

let logoDataUri: string | undefined = logoCache.get(course.iconSrc.src);

try {
logoDataUri = await fetchAndConvertToDataUri(course.iconSrc.src);
logoCache.set(course.iconSrc.src, logoDataUri);
} catch (err) {
console.warn('Failed to load remote logo, using fallback', err);
logoDataUri = await loadImageAsDataUri(fallbackPath);
}

return createCourseTree({
name: courseName,
logo: {
src: logoDataUri,
width: logoWidth,
height: logoHeight,
},
startDate: course.startDate,
});
}
50 changes: 50 additions & 0 deletions src/app/[lang]/courses/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Metadata } from 'next';
import path from 'path';

import { resolvePageLocale } from '@/entities/page';
import { PAGE_TYPE } from '@/entities/page/constants';
import { pageStore } from '@/entities/page/model/store';
import { PageProps } from '@/entities/page/types';
import { generatePageMetadata } from '@/shared/helpers/generate-page-metadata';
import { Course } from '@/views/course';

export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { slug, lang } = await params;
const locale = resolvePageLocale(lang);

const {
courseUrl,
title: courseName,
seoDescription: description,
seoKeywords: keywords,
} = await pageStore.loadPage(PAGE_TYPE.COURSE, locale, slug);

const title = `${courseName} Β· The Rolling Scopes School`;
const robots = {
index: true,
follow: true,
};

return generatePageMetadata({
title,
description,
imagePath: path.join(lang, 'courses', slug, 'og.png'),
keywords,
alternates: { canonical: courseUrl },
robots,
});
}

export async function generateStaticParams({ params: { lang } }: { params: Awaited<PageProps['params']> }) {
const locale = resolvePageLocale(lang);

return await pageStore.loadPagesMetadata(PAGE_TYPE.COURSE, locale);
}

export default async function CourseRoute({ params }: PageProps) {
const { slug, lang } = await params;
const locale = resolvePageLocale(lang);
const { sections, courseId } = await pageStore.loadPage(PAGE_TYPE.COURSE, locale, slug);

return <Course id={courseId} sections={sections} locale={locale} />;
}
20 changes: 20 additions & 0 deletions src/app/[lang]/courses/og.png/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PAGE_TYPE, pageStore, resolvePageLocale } from '@/entities/page';
import { generateLangParams } from '@/entities/page/helpers/generate-lang-params';
import { PagePropsOg } from '@/entities/page/types';
import { createPageTree } from '@/shared/og/view/pages-tree/generate-pages-tree';

export { DYNAMIC as dynamic } from '@/shared/constants';

export const generateStaticParams = generateLangParams;

export async function GET(_request: Request, { params }: PagePropsOg) {
const { lang } = await params;
const locale = resolvePageLocale(lang);
const { seoOgImageTitle: title, seoOgImageDescription: description } =
await pageStore.loadPage(PAGE_TYPE.COURSES, locale);

return createPageTree({
title,
description,
});
}
41 changes: 41 additions & 0 deletions src/app/[lang]/courses/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Metadata } from 'next';

import { resolvePageLocale } from '@/entities/page';
import { PAGE_TYPE } from '@/entities/page/constants';
import { pageStore } from '@/entities/page/model/store';
import { PageProps } from '@/entities/page/types';
import { coursesMetadata } from '@/metadata/courses';
import { OG_SITE_NAME } from '@/shared/constants';
import { generatePageMetadata } from '@/shared/helpers/generate-page-metadata';
import { Courses } from '@/views/courses';

export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { lang } = await params;
const locale = resolvePageLocale(lang);
const {
title: coursesTitle,
seoDescription: description,
seoKeywords: keywords,
} = await pageStore.loadPage(PAGE_TYPE.COURSES, locale);
const title = `${coursesTitle} Β· ${OG_SITE_NAME}`;
const { canonical, robots } = coursesMetadata;

const metadata = generatePageMetadata({
title,
description,
imagePath: `/${lang}/courses/og.png`,
keywords,
alternates: { canonical },
robots,
});

return metadata;
}

export default async function CoursesRoute({ params }: PageProps) {
const { lang } = await params;
const locale = resolvePageLocale(lang);
const { sections } = await pageStore.loadPage(PAGE_TYPE.COURSES, locale);

return <Courses sections={sections} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,17 @@ import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import path from 'path';

import { DocsContent } from '../../components/docs-content/docs-content';
import { TITLE_POSTFIX } from '../../constants';
import { Menu } from '../../types';
import { fetchMarkdownContent } from '../../utils/fetch-markdown-content';
import { fetchMenu } from '../../utils/fetch-menu';
import { DocsContent } from '@/app/docs/components/docs-content/docs-content';
import { TITLE_POSTFIX } from '@/app/docs/constants';
import { Menu } from '@/app/docs/types';
import { fetchMarkdownContent } from '@/app/docs/utils/fetch-markdown-content';
import { fetchMenu } from '@/app/docs/utils/fetch-menu';
import { PagePropsDocs } from '@/entities/page/types';
import { generateDocsMetadata } from '@/metadata/docs';
import { generatePageMetadata } from '@/shared/helpers/generate-page-metadata';
import { Language } from '@/shared/types';

type RouteParams = { lang: Language;
slug: string[]; };

export async function generateMetadata({
params,
}: {
params: Promise<RouteParams>;
}): Promise<Metadata> {
export async function generateMetadata({ params }: PagePropsDocs): Promise<Metadata> {
const { lang, slug } = await params;
const docsMenu = await fetchMenu(lang);

Expand Down Expand Up @@ -53,7 +47,7 @@ export async function generateMetadata({
const metadata = generatePageMetadata({
title: `${title} ${TITLE_POSTFIX}`,
description,
imagePath: path.join('docs', lang, 'og.png'),
imagePath: path.join(lang, 'docs', 'og.png'),
keywords,
alternates: { canonical },
robots,
Expand All @@ -62,8 +56,8 @@ export async function generateMetadata({
return metadata;
}

export async function generateStaticParams(): Promise<RouteParams[]> {
const supportedLanguages: Language[] = ['en', 'ru'];
export async function generateStaticParams() {
const supportedLanguages: Language[] = ['ru'];
const allSlugs = [];

const collectSlugs = (items: Menu, lang: Language) => {
Expand Down Expand Up @@ -104,7 +98,7 @@ export async function generateStaticParams(): Promise<RouteParams[]> {
return allSlugs;
}

export default async function DocPage({ params }: { params: Promise<RouteParams> }) {
export default async function DocPage({ params }: PagePropsDocs) {
const { lang, slug } = await params;

try {
Expand Down
17 changes: 17 additions & 0 deletions src/app/[lang]/docs/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PropsWithChildren } from 'react';

import { DocsLayout } from '@/app/docs/components/docs-layout/docs-layout';
import { fetchMenu } from '@/app/docs/utils/fetch-menu';
import { PageProps } from '@/entities/page/types';
import { Language } from '@/shared/types';

export default async function RootLayout({ children, params }: { params: Promise<Pick<Awaited<PageProps['params']>, 'lang'>> } & PropsWithChildren) {
const { lang } = (await params) as { lang: Language };
const menu = await fetchMenu(lang);

return (
<DocsLayout menu={menu} lang={lang}>
{children}
</DocsLayout>
);
}
20 changes: 20 additions & 0 deletions src/app/[lang]/docs/og.png/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PAGE_TYPE, pageStore, resolvePageLocale } from '@/entities/page';
import { generateLangParams } from '@/entities/page/helpers/generate-lang-params';
import { PagePropsOg } from '@/entities/page/types';
import { createPageTree } from '@/shared/og/view/pages-tree/generate-pages-tree';

export { DYNAMIC as dynamic } from '@/shared/constants';

export const generateStaticParams = generateLangParams;

export async function GET(_request: Request, { params }: PagePropsOg) {
const { lang } = await params;
const locale = resolvePageLocale(lang);
const { seoOgImageTitle: title, seoOgImageDescription: description } =
await pageStore.loadPage(PAGE_TYPE.DOCS, locale);

return createPageTree({
title,
description,
});
}
33 changes: 33 additions & 0 deletions src/app/[lang]/docs/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { DocsContent } from '@/app/docs/components/docs-content/docs-content';
import { fetchMarkdownContent } from '@/app/docs/utils/fetch-markdown-content';
import { PAGE_TYPE, pageStore, resolvePageLocale } from '@/entities/page';
import { PageProps } from '@/entities/page/types';
import { docsLangMetadata } from '@/metadata/docs';
import { generatePageMetadata } from '@/shared/helpers/generate-page-metadata';

export async function generateMetadata({ params }: PageProps) {
const { lang } = await params;
const locale = resolvePageLocale(lang);
const { title, seoDescription: description, seoKeywords: keywords } =
await pageStore.loadPage(PAGE_TYPE.DOCS, locale);
const { canonical, robots } = docsLangMetadata;

const metadata = generatePageMetadata({
title,
description,
imagePath: `/${lang}/docs/og.png`,
keywords,
alternates: { canonical },
robots,
});

return metadata;
}

export default async function DocsIndex({ params }: PageProps) {
const { lang } = await params;

const indexContent = await fetchMarkdownContent(lang);

return <DocsContent markdownContent={indexContent} lang={lang} />;
}
9 changes: 9 additions & 0 deletions src/app/[lang]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { PropsWithChildren } from 'react';

import { generateLangParams } from '@/entities/page/helpers/generate-lang-params';

export const generateStaticParams = generateLangParams;

export default function Layout({ children }: PropsWithChildren) {
return children;
}
Loading
Loading