Skip to content
Merged

Dev #33

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
24 changes: 12 additions & 12 deletions src/app/api/revalidate/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,21 @@ export async function POST(request: NextRequest) {

const modelTagMap: Record<string, string[]> = {
// Collections
news: ['collection-news'],
works: ['collection-works'],
reviews: ['collection-reviews'],
category: ['collection-category'],
faq: ['collection-faq'],
advantages: ['collection-advantages'],
news: ['collection-news'],
works: ['collection-works'],
reviews: ['collection-reviews'],
category: ['collection-category'],
faq: ['collection-faq'],
advantages: ['collection-advantages'],
createprocess: ['collection-createprocess'],
restoration: ['singleton-restoration'],
masters: ['collection-masters'],
mainslider: ['collection-mainslider'],
restoration: ['singleton-restoration'],
masters: ['collection-masters'],
mainslider: ['collection-mainslider'],
// Singletons
maininfo: ['singleton-maininfo'],
order: ['singleton-order'],
maininfo: ['singleton-maininfo'],
order: ['singleton-order'],
// Trees
gallery: ['tree-gallery'],
gallery: ['tree-gallery'],
}

const tagsToRevalidate = modelTagMap[model] || []
Expand Down
11 changes: 8 additions & 3 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ import '../styles/globals.scss'
import '../styles/blocks.scss'
import { Montserrat, CyrillicOld } from './fonts'
import type { Metadata } from 'next'
import { JSX } from 'react'
import { JSX, ReactNode } from 'react'
import clsx from 'clsx'
import Script from 'next/script'
import Header from '@/components/Header/Header'
import Footer from '@/components/Footer/Footer'
import ScrollButton from '@/components/ScrollButton/ScrollButton'
import AnimationObserver from '@/components/AnimationObserver/AnimationObserver'
import PageTransition from '@/components/PageTransition/PageTransition'

export const dynamic = 'force-dynamic'

type LayoutProps = {
children?: React.ReactNode
children?: ReactNode
}

export const metadata: Metadata = {
Expand Down Expand Up @@ -90,9 +92,12 @@ export default function RootLayout({ children }: LayoutProps): JSX.Element {
/>

<Header />
<main>{children}</main>
<main>
<PageTransition>{children}</PageTransition>
</main>
<Footer />
<ScrollButton />
<AnimationObserver />
</body>
</html>
)
Expand Down
10 changes: 7 additions & 3 deletions src/app/order-delivery/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,21 @@ export default function Page(): JSX.Element {

<section className="section">
<div className="container">
<h2 className="section__title">Связаться с нами</h2>
<h2 className="section__title" data-animate="fade-up">
Связаться с нами
</h2>

<FormContacts />
</div>
</section>

<section className="section calculation">
<div className="container">
<h2 className="section__title">Расчёт примерной стоимости</h2>
<h2 className="section__title" data-animate="fade-up">
Расчёт примерной стоимости
</h2>

<p className="section__description">
<p className="section__description" data-animate="fade-up" data-stagger="1">
(выполнение гравировки и ассиста рассчитывается отдельно)
</p>

Expand Down
22 changes: 15 additions & 7 deletions src/components/AboutPage/AboutPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ImageItem, SlideItem } from '@/types/types'
import { createSanitizedHTML } from '@/functions/functions'
import SliderDetail from '@/components/SliderDetail/SliderDetail'
import aboutPageStyles from './AboutPage.module.scss'
import cockpit from '@/lib/CockpitAPI'
import { fetchSingleton, getImageUrl } from '@/lib/api-client'
import Image from 'next/image'
import EmptySection from '@/components/EmptySection/EmptySection'

Expand All @@ -16,7 +16,7 @@ type AboutPageProps = {
}

export default async function AboutPage(): Promise<JSX.Element> {
const about: AboutPageProps | null = await cockpit.getSingleItem('about')
const about: AboutPageProps | null = await fetchSingleton<AboutPageProps>('about')

if (!about) return <EmptySection />

Expand All @@ -25,17 +25,17 @@ export default async function AboutPage(): Promise<JSX.Element> {

const slidesList: SlideItem[] = about.slider?.map((image) => ({
id: image._id,
image: cockpit.getImageUrl(image._id, 800, 800),
image: getImageUrl(image._id, 800, 800),
alt: image.title || about.title,
}))

const imageSrc = about.image ? cockpit.getImageUrl(about.image._id, 800, 500) : ''
const imageSrc = about.image ? getImageUrl(about.image._id, 800, 500) : ''
const alt = about.image?.alt ?? title

return (
<section className={clsx('section', aboutPageStyles['about'])}>
<div className={clsx('container', aboutPageStyles['about__container'])}>
<div className={aboutPageStyles['about__content']}>
<div className={aboutPageStyles['about__content']} data-animate="fade-up">
<h2 className="visually-hidden">{title}</h2>

<div
Expand All @@ -45,13 +45,21 @@ export default async function AboutPage(): Promise<JSX.Element> {
</div>

{slidesList.length > 0 && (
<div className={aboutPageStyles['about__slider']}>
<div
className={aboutPageStyles['about__slider']}
data-animate="scale-in"
data-stagger="1"
>
<SliderDetail items={slidesList} />
</div>
)}

{slidesList.length === 0 && imageSrc && (
<div className={aboutPageStyles['about__image-wrapper']}>
<div
className={aboutPageStyles['about__image-wrapper']}
data-animate="scale-in"
data-stagger="1"
>
<Image
className={aboutPageStyles['about__image']}
src={imageSrc}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Address/Address.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { JSX } from 'react'
import addressStyles from './Address.module.scss'
import { MainInfo } from '@/types/types'
import cockpit from '@/lib/CockpitAPI'
import { fetchSingleton } from '@/lib/api-client'

export default async function Address(): Promise<JSX.Element | null> {
const mainInfo: MainInfo | null = await cockpit.getSingleItem('maininfo')
const mainInfo: MainInfo | null = await fetchSingleton<MainInfo>('maininfo')

if (!mainInfo || !mainInfo.address) {
return null
Expand Down
50 changes: 50 additions & 0 deletions src/components/AnimationObserver/AnimationObserver.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client'

import { useEffect } from 'react'
import { usePathname } from 'next/navigation'

export default function AnimationObserver(): null {
const pathname = usePathname()

useEffect(() => {
let observer: IntersectionObserver | null = null

// rAF ensures DOM has fully updated after Next.js navigation
const raf = requestAnimationFrame(() => {
const elements = document.querySelectorAll<HTMLElement>('[data-animate]:not(.is-visible)')

if (!elements.length) return

observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return

const el = entry.target as HTMLElement
const stagger = el.dataset.stagger

if (stagger) {
el.style.setProperty('--stagger', stagger)
}

el.classList.add('is-visible')
observer?.unobserve(el)
})
},
{
threshold: 0.12,
rootMargin: '0px 0px -40px 0px',
},
)

elements.forEach((el) => observer!.observe(el))
})

return () => {
cancelAnimationFrame(raf)
observer?.disconnect()
}
}, [pathname])

return null
}
15 changes: 10 additions & 5 deletions src/components/Categories/Categories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import clsx from 'clsx'
import { CardItem, CategoryFromServer } from '@/types/types'
import Card from '@/components/Card/Card'
import categoriesStyles from './Categories.module.scss'
import cockpit from '@/lib/CockpitAPI'
import { fetchCollection, getImageUrl } from '@/lib/api-client'

export default async function Categories(): Promise<JSX.Element | null> {
const categoriesData: CategoryFromServer[] = await cockpit.getCollection('category', {
const categoriesData: CategoryFromServer[] = await fetchCollection<CategoryFromServer>('category', {
sort: { sort: 1 },
})

Expand All @@ -19,7 +19,7 @@ export default async function Categories(): Promise<JSX.Element | null> {
title: category.title,
description: category.description,
href: `/categories/${category.slug || category._id}`,
image: cockpit.getImageUrl(category.image._id, 400, 400),
image: getImageUrl(category.image._id, 400, 400),
alt: category.image.title || category.title,
}))

Expand All @@ -29,9 +29,14 @@ export default async function Categories(): Promise<JSX.Element | null> {
<h2 className="visually-hidden">Категории икон</h2>

<ul className={categoriesStyles['categories__list']}>
{categoriesList.map((category) => {
{categoriesList.map((category, index) => {
return (
<li className={categoriesStyles['categories__item']} key={category.id}>
<li
className={categoriesStyles['categories__item']}
key={category.id}
data-animate="fade-up"
data-stagger={String(index % 6)}
>
<Card data={category} />
</li>
)
Expand Down
4 changes: 2 additions & 2 deletions src/components/Contacts/Contacts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { JSX } from 'react'
import contactsStyles from './Contacts.module.scss'
import clsx from 'clsx'
import { MainInfo } from '@/types/types'
import cockpit from '@/lib/CockpitAPI'
import { fetchSingleton } from '@/lib/api-client'
import { createEmailLink, createPhoneLink } from '@/functions/functions'

type ContactsProps = {
addClass: string
}

export default async function Contacts({ addClass }: ContactsProps): Promise<JSX.Element | null> {
const mainInfo: MainInfo | null = await cockpit.getSingleItem('maininfo')
const mainInfo: MainInfo | null = await fetchSingleton<MainInfo>('maininfo')

if (!mainInfo) {
return null
Expand Down
34 changes: 26 additions & 8 deletions src/components/ContactsPage/ContactsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import Social from '@/components/Social/Social'
import FormContacts from '@/components/Forms/FormContacts/FormContacts'
import YandexMap from '@/components/YandexMap/YandexMap'
import { MainInfo } from '@/types/types'
import cockpit from '@/lib/CockpitAPI'
import { fetchSingleton, getImageUrl } from '@/lib/api-client'
import { createEmailLink, createPhoneLink } from '@/functions/functions'

export default async function ContactsPage(): Promise<JSX.Element | null> {
const contactsInfo: MainInfo | null = await cockpit.getSingleItem('maininfo')
const contactsInfo: MainInfo | null = await fetchSingleton<MainInfo>('maininfo')

if (!contactsInfo) {
return null
}

const logo = cockpit.getImageUrl(contactsInfo.logo._id, 60, 60)
const logo = getImageUrl(contactsInfo.logo._id, 60, 60)
const address = contactsInfo.address
const coordinates = contactsInfo.coordinates
const email = contactsInfo.email
Expand All @@ -29,7 +29,11 @@ export default async function ContactsPage(): Promise<JSX.Element | null> {

<ul className={contactsPageStyles['contacts__list']}>
{address && (
<li className={contactsPageStyles['contacts__item']}>
<li
className={contactsPageStyles['contacts__item']}
data-animate="fade-up"
data-stagger="0"
>
<address className={contactsPageStyles['contacts__address']}>
<span className={contactsPageStyles['contacts__link-caption']}>Адрес:</span>

Expand All @@ -39,7 +43,11 @@ export default async function ContactsPage(): Promise<JSX.Element | null> {
)}

{email && (
<li className={contactsPageStyles['contacts__item']}>
<li
className={contactsPageStyles['contacts__item']}
data-animate="fade-up"
data-stagger="1"
>
<a className={contactsPageStyles['contacts__link']} href={createEmailLink(email)}>
<span className={contactsPageStyles['contacts__link-caption']}>Email:</span>

Expand All @@ -49,7 +57,11 @@ export default async function ContactsPage(): Promise<JSX.Element | null> {
)}

{phone && (
<li className={contactsPageStyles['contacts__item']}>
<li
className={contactsPageStyles['contacts__item']}
data-animate="fade-up"
data-stagger="2"
>
<a className={contactsPageStyles['contacts__link']} href={createPhoneLink(phone)}>
<span className={contactsPageStyles['contacts__link-caption']}>Телефон:</span>

Expand All @@ -62,7 +74,11 @@ export default async function ContactsPage(): Promise<JSX.Element | null> {
<Social addClass={contactsPageStyles['contacts__social']} />

{coordinates && (
<div className={contactsPageStyles['contacts__map']}>
<div
className={contactsPageStyles['contacts__map']}
data-animate="fade-in"
data-stagger="3"
>
<YandexMap logo={logo} coordinates={coordinates} />
</div>
)}
Expand All @@ -71,7 +87,9 @@ export default async function ContactsPage(): Promise<JSX.Element | null> {

<section className="section">
<div className="container">
<h2 className="section__title">Связаться с нами</h2>
<h2 className="section__title" data-animate="fade-up">
Связаться с нами
</h2>

<FormContacts />
</div>
Expand Down
10 changes: 7 additions & 3 deletions src/components/Detail/Detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function Detail({
return (
<section className={clsx('section', detailStyles['detail'])}>
<div className={clsx('container', detailStyles['detail__container'])}>
<div className={detailStyles['detail__content']}>
<div className={detailStyles['detail__content']} data-animate="fade-up">
<h2 className="section__title">{title}</h2>

<div
Expand All @@ -36,13 +36,17 @@ export default function Detail({
</div>

{slidesList.length > 0 && (
<div className={detailStyles['detail__slider']}>
<div className={detailStyles['detail__slider']} data-animate="scale-in" data-stagger="1">
<SliderDetail items={slidesList} />
</div>
)}

{slidesList.length === 0 && image && (
<div className={detailStyles['detail__image-wrapper']}>
<div
className={detailStyles['detail__image-wrapper']}
data-animate="scale-in"
data-stagger="1"
>
<Image
className={detailStyles['detail__image']}
src={src}
Expand Down
Loading