Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion src/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import Heading from '@/components/Heading/Heading'
import AboutPage from '@/components/AboutPage/AboutPage'

export const metadata: Metadata = {
title: 'О нас | Иконописная Артель',
title: 'О нас',
description:
'Наши мастера создают рукописные иконы в древней технологии яичной темперой, следуя каноническим образцам.',
openGraph: {
title: 'О нас | Иконописная Артель',
description:
'Наши мастера создают рукописные иконы в древней технологии яичной темперой, следуя каноническим образцам.',
},
alternates: {
canonical: '/about',
},
}

const breadcrumbsList: BreadcrumbItem[] = [
Expand Down
8 changes: 4 additions & 4 deletions src/app/categories/[categories-detail]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>

if (!category) {
return {
title: 'Категория не найдена | Иконописная Артель',
title: 'Категория не найдена',
}
}

return {
title: `${category.title} | Иконописная Артель`,
title: category.title,
description: category.description
? category.description.replace(/<[^>]*>/g, '').slice(0, 160)
: '',
Expand All @@ -47,12 +47,12 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
: [],
},
alternates: {
canonical: `${process.env.SITE_URL || process.env.NEXT_PUBLIC_SITE_URL}/categories/${category.slug || category._id}`,
canonical: `/categories/${category.slug || category._id}`,
},
}
} catch {
return {
title: 'Категория не найдена | Иконописная Артель',
title: 'Категория не найдена',
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/app/categories/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import { fetchCollection, fetchCollectionCount, getImageUrl } from '@/lib/api-cl
import { ITEMS_PER_PAGE } from '@/const/const'

export const metadata: Metadata = {
title: 'Категории икон | Иконописная Артель',
title: 'Категории икон',
description: 'Все категории икон, создаваемых в нашей иконописной мастерской',
openGraph: {
title: 'Категории икон | Иконописная Артель',
description: 'Все категории икон, создаваемых в нашей иконописной мастерской',
},
alternates: {
canonical: '/categories',
},
}

const breadcrumbsList: BreadcrumbItem[] = [
Expand Down
5 changes: 4 additions & 1 deletion src/app/contacts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import Heading from '@/components/Heading/Heading'
import ContactsPage from '@/components/ContactsPage/ContactsPage'

export const metadata: Metadata = {
title: 'Контакты | Иконописная Артель',
title: 'Контакты',
description:
'Контактная информация Иконописной Артели. Адрес мастерской, телефон, email для заказа икон. Свяжитесь с нами для консультации.',
openGraph: {
title: 'Контакты | Иконописная Артель',
description:
'Контактная информация Иконописной Артели. Адрес мастерской, телефон, email для заказа икон.',
},
alternates: {
canonical: '/contacts',
},
}

const breadcrumbsList: BreadcrumbItem[] = [
Expand Down
25 changes: 24 additions & 1 deletion src/app/gallery/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { notFound } from 'next/navigation'
import { BreadcrumbItem, GalleryTreeItem } from '@/types/types'
import Heading from '@/components/Heading/Heading'
import GalleryPageClient from '@/components/GalleryPage/GalleryPageClient'
import { fetchTree } from '@/lib/api-client'
import { fetchTree, getImageUrl } from '@/lib/api-client'
import {
prepareGalleryItems,
findGalleryItemBySlug,
Expand All @@ -29,12 +29,35 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
}

const currentItem = findGalleryItemBySlug(galleryData, lastSlug)
const canonicalPath = `/gallery/${slug.join('/')}`

let ogImage: string | undefined
if (currentItem) {
const firstImageChild = currentItem._children?.find(
(child) => child.image && child.image._id,
)
if (firstImageChild?.image?._id) {
ogImage = getImageUrl(firstImageChild.image._id, 1200, 630, { mime: 'jpeg' })
}
}

return {
title: currentItem ? `${currentItem.title} | Галерея` : 'Галерея | Иконописная Артель',
description: currentItem?.title
? `Фотогалерея: ${currentItem.title}. Иконописная Артель.`
: 'Фотогалерея Иконописной Артели. Работы мастеров и события из жизни артели.',
openGraph: {
title: currentItem ? `${currentItem.title} | Галерея` : 'Галерея | Иконописная Артель',
description: currentItem?.title
? `Фотогалерея: ${currentItem.title}. Иконописная Артель.`
: 'Фотогалерея Иконописной Артели. Работы мастеров и события из жизни артели.',
...(ogImage && {
images: [{ url: ogImage, width: 1200, height: 630, alt: currentItem?.title || 'Галерея' }],
}),
},
alternates: {
canonical: canonicalPath,
},
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/app/gallery/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import { fetchTree } from '@/lib/api-client'
import { prepareGalleryItems } from '@/functions/gallery'

export const metadata: Metadata = {
title: 'Галерея | Иконописная Артель',
title: 'Галерея',
description:
'Фотогалерея Иконописной Артели. Фотографии работ наших мастеров, процесса создания икон, мастерской и событий из жизни артели.',
openGraph: {
title: 'Галерея | Иконописная Артель',
description:
'Фотогалерея Иконописной Артели. Фотографии работ наших мастеров, процесса создания икон, мастерской и событий.',
},
alternates: {
canonical: '/gallery',
},
}

const breadcrumbsList: BreadcrumbItem[] = [
Expand Down
48 changes: 17 additions & 31 deletions src/app/in-stock/[in-stock-detail]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BreadcrumbItem, MasterFromServer, SlideItem, WorkFromServer } from '@/t
import Heading from '@/components/Heading/Heading'
import Detail from '@/components/Detail/Detail'
import Master from '@/components/Master/Master'
import ProductJsonLd from '@/components/JsonLd/ProductJsonLd'
import { fetchCollectionItem, getImageUrl } from '@/lib/api-client'

type PageProps = {
Expand All @@ -21,14 +22,14 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>

if (!work) {
return {
title: 'Работа не найдена | Иконописная Артель',
title: 'Работа не найдена',
}
}

const description = work.description ? work.description.replace(/<[^>]*>/g, '').slice(0, 160) : ''

return {
title: `${work.title} | Иконописная Артель`,
title: work.title,
description,
openGraph: {
title: work.title,
Expand All @@ -38,7 +39,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
: [],
},
alternates: {
canonical: `${process.env.SITE_URL || process.env.NEXT_PUBLIC_SITE_URL}/in-stock/${work.slug || work._id}`,
canonical: `/in-stock/${work.slug || work._id}`,
},
}
}
Expand Down Expand Up @@ -83,35 +84,20 @@ export default async function Page({ params }: PageProps): Promise<JSX.Element>
? await fetchCollectionItem<MasterFromServer>('masters', work.master?._id)
: null

const productSchema = {
'@context': 'https://schema.org',
'@type': 'Product',
name: work.title,
description: work.description
? work.description.replace(/<[^>]*>/g, '').slice(0, 160)
: work.title,
image: work.image
? getImageUrl(work.image._id, 1200, 630, { mode: 'thumbnail', mime: 'jpeg' })
: undefined,
brand: {
'@type': 'Organization',
name: 'Иконописная Артель',
},
offers: {
'@type': 'Offer',
availability: 'https://schema.org/InStock',
url: `${process.env.SITE_URL || process.env.NEXT_PUBLIC_SITE_URL}/in-stock/${work.slug || work._id}`,
...(work.price && !isNaN(parseFloat(work.price))
? { price: parseFloat(work.price), priceCurrency: 'RUB' }
: {}),
},
}

return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(productSchema) }}
<ProductJsonLd
name={work.title}
description={work.description || work.title}
slug={work.slug || work._id}
image={
work.image
? getImageUrl(work.image._id, 1200, 630, { mode: 'thumbnail', mime: 'jpeg' })
: undefined
}
price={work.price}
basePath="/in-stock"
inStock={true}
/>

<Heading breadcrumbsList={breadcrumbsList} />
Expand All @@ -128,4 +114,4 @@ export default async function Page({ params }: PageProps): Promise<JSX.Element>
{MasterInfo && <Master master={MasterInfo} />}
</>
)
}
}
5 changes: 4 additions & 1 deletion src/app/in-stock/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ import { fetchCollection, fetchCollectionCount, getImageUrl } from '@/lib/api-cl
import { ITEMS_PER_PAGE } from '@/const/const'

export const metadata: Metadata = {
title: 'Рукописные иконы в наличии | Иконописная Артель',
title: 'Рукописные иконы в наличии',
description:
'Готовые рукописные иконы в наличии. Каноничные образы, написанные яичной темперой на деревянной основе. Возможность приобретения без ожидания.',
openGraph: {
title: 'Рукописные иконы в наличии | Иконописная Артель',
description:
'Готовые рукописные иконы в наличии. Каноничные образы, написанные яичной темперой на деревянной основе.',
},
alternates: {
canonical: '/in-stock',
},
}

const breadcrumbsList: BreadcrumbItem[] = [
Expand Down
17 changes: 3 additions & 14 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Script from 'next/script'
import { cookies } from 'next/headers'
import Header from '@/components/Header/Header'
import Footer from '@/components/Footer/Footer'
import OrganizationJsonLd from '@/components/JsonLd/OrganizationJsonLd'
import ScrollButton from '@/components/ScrollButton/ScrollButton'
import AnimationObserver from '@/components/AnimationObserver/AnimationObserver'
import PageTransition from '@/components/PageTransition/PageTransition'
Expand Down Expand Up @@ -47,6 +48,7 @@ export const metadata: Metadata = {
card: 'summary_large_image',
title: 'Иконописная Артель',
description: 'Рукописные канонические иконы',
images: [{ url: '/og-image.png', width: 1200, height: 630, alt: 'Иконописная Артель' }],
},
icons: {
icon: '/favicon.ico',
Expand Down Expand Up @@ -96,20 +98,7 @@ export default async function RootLayout({ children }: LayoutProps): Promise<JSX
strategy="beforeInteractive"
/>

<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Иконописная Артель',
url: process.env.SITE_URL || process.env.NEXT_PUBLIC_SITE_URL,
logo: (process.env.SITE_URL || process.env.NEXT_PUBLIC_SITE_URL) + '/logo.png',
description:
'Рукописные канонические иконы — храмы, семейные иконы и реставрация. Мастера-иконописцы.',
}),
}}
/>
<OrganizationJsonLd />

<Header />
<main>
Expand Down
43 changes: 13 additions & 30 deletions src/app/news/[news-detail]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { notFound, redirect } from 'next/navigation'
import { BreadcrumbItem, NewsFromServer, SlideItem } from '@/types/types'
import Heading from '@/components/Heading/Heading'
import Detail from '@/components/Detail/Detail'
import ArticleJsonLd from '@/components/JsonLd/ArticleJsonLd'
import { fetchCollectionItem, getImageUrl } from '@/lib/api-client'

type PageProps = {
Expand All @@ -20,14 +21,14 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>

if (!news) {
return {
title: 'Новость не найдена | Иконописная Артель',
title: 'Новость не найдена',
}
}

const description = (news.content || news.description || '').replace(/<[^>]*>/g, '').slice(0, 160)

return {
title: `${news.title} | Иконописная Артель`,
title: news.title,
description,
openGraph: {
title: news.title,
Expand All @@ -37,7 +38,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
: [],
},
alternates: {
canonical: `${process.env.SITE_URL || process.env.NEXT_PUBLIC_SITE_URL}/news/${news.slug || news._id}`,
canonical: `/news/${news.slug || news._id}`,
},
}
}
Expand All @@ -52,7 +53,6 @@ export default async function Page({ params }: PageProps): Promise<JSX.Element>
notFound()
}

// If route used an id but the item has a slug, redirect to canonical slug URL for SEO
if (news.slug && news.slug !== slug) {
redirect(`/news/${news.slug}`)
}
Expand All @@ -79,33 +79,16 @@ export default async function Page({ params }: PageProps): Promise<JSX.Element>
alt: image.title || news.title,
})) || []

const articleSchema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: news.title,
description: (news.content || news.description || '').replace(/<[^>]*>/g, '').slice(0, 160),
image: news.image ? getImageUrl(news.image._id, 1200, 630, { mime: 'jpeg' }) : undefined,
datePublished: news._created ? new Date(news._created * 1000).toISOString() : undefined,
dateModified: news._modified ? new Date(news._modified * 1000).toISOString() : undefined,
author: {
'@type': 'Organization',
name: 'Иконописная Артель',
},
publisher: {
'@type': 'Organization',
name: 'Иконописная Артель',
logo: {
'@type': 'ImageObject',
url: `${process.env.SITE_URL || process.env.NEXT_PUBLIC_SITE_URL}/logo.png`,
},
},
}

return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }}
<ArticleJsonLd
title={news.title}
description={news.content || news.description || ''}
slug={news.slug || news._id}
image={news.image ? getImageUrl(news.image._id, 1200, 630, { mime: 'jpeg' }) : undefined}
datePublished={news._created ? new Date(news._created * 1000).toISOString() : undefined}
dateModified={news._modified ? new Date(news._modified * 1000).toISOString() : undefined}
basePath="/news"
/>

<Heading breadcrumbsList={breadcrumbsList} />
Expand All @@ -118,4 +101,4 @@ export default async function Page({ params }: PageProps): Promise<JSX.Element>
/>
</>
)
}
}
Loading