updated widgets

This commit is contained in:
2026-02-25 17:35:01 +09:00
parent 787724b09c
commit e4d2966377
43 changed files with 3648 additions and 180 deletions

View File

@@ -0,0 +1,15 @@
'use client';
export default function Error() {
return (
<div className="container mx-auto py-16 max-w-3xl text-center">
<h1 className="text-2xl font-semibold mb-4">
500 Сервер временно недоступен
</h1>
<p className="text-gray-600">
Страница не может быть загружена прямо сейчас.
Пожалуйста, попробуйте позже.
</p>
</div>
);
}

View File

@@ -0,0 +1,34 @@
// app/[slug]/page.tsx
import Post from '@/components/Post/Post';
import { renderPostContent } from '@/components/WPRenderer/WPRenderer';
// ISR: regenerate every 60 seconds
export const revalidate = 60;
export interface PageProps {
params: {
slug: string;
};
}
export default async function PostPage({ params }: PageProps) {
const { slug } = await params;
const baseUrl =
process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
const res = await fetch(`${baseUrl}/api/posts/${slug}`, {
next: { revalidate: 60 },
});
if (!res.ok) {
console.log(`${baseUrl}/api/posts/${slug}`)
// 🚨 THROW — this is critical
throw new Error('Failed to fetch post');
}
const post = await res.json();
return (
<Post post={post} />
);
}

View File

@@ -0,0 +1,71 @@
import PostGrid from "@/components/WP/PostGrid/PostGrid.server";
import { renderPostContent } from "@/components/WPRenderer/WPRenderer";
import parse, { DOMNode } from 'html-react-parser'
interface PageProps {
params: { slug: string; page: string };
}
export function extractPostGridId(content: string): number | null {
let gridId: number | null = null;
parse(content, {
replace: (domNode: DOMNode) => {
// Only check text nodes
if ("data" in domNode && typeof domNode.data === "string") {
const match = domNode.data.match(/\[the-post-grid\s+id="(\d+)"/);
if (match) {
gridId = Number(match[1]);
return; // stop parsing
}
}
},
});
return gridId;
}
export default async function PostPage({ params }: PageProps) {
const { slug, page } = await params;
const pageNum = Number(page) || 1;
// 1. Fetch the WP page content (same as page 1)
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/pages/${slug}`, {
next: { revalidate: 60 },
});
const pageData = await res.json();
if (!pageData) return <p>Page not found</p>;
// 2. Extract grid ID from shortcode
const gridId = extractPostGridId(pageData.post_content);
return (
<div className="container mx-auto py-8 max-w-5xl">
<article className="prose lg:prose-xl max-w-none">
<h1>{pageData.post_title}</h1>
{pageData.post_type === 'post' && (
<div className="text-gray-600 mb-6">
<time>
{pageData.post_date}
</time>
</div>
)}
{renderPostContent(pageData.post_content, { page: pageNum })}
</article>
</div>
)
return (
<article>
<h1>{pageData.title}</h1>
{/* {gridId && <PostGrid id={gridId} page={pageNum} />} */}
{renderPostContent(pageData.post_content, { page: pageNum })}
</article>
);
}

View File

@@ -0,0 +1,39 @@
import Header from "@/components/Blocks/Header/Header";
import CookieNotice from "@/components/CookieNotice/CookieNotice";
import Footer from "@/components/Footer/Footer";
import "@/styles/globals.css";
import { Montserrat, Roboto, Roboto_Condensed } from 'next/font/google'
export const montserratFont = Montserrat({
subsets: ['latin', 'cyrillic'],
})
const mainFont = Roboto({
subsets: ['latin', 'cyrillic'],
})
export const condensedFont = Roboto_Condensed({
subsets: ['latin', 'cyrillic'],
})
export const revalidate = 10
export default function ClientLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<Header />
<main className="flex-1 overflow-x-auto">
{children}
</main>
<Footer />
<CookieNotice />
</>
);
}

250
src/app/(client)/page.tsx Normal file
View File

@@ -0,0 +1,250 @@
// app/page.tsx
import Section from '@/components/Blocks/Section/Section';
import EmblaCarousel from '@/components/UI/EmblaCarousel/EmblaCarousel';
import Areas from '@/components/WP/Areas/Areas';
import { SmartSlider } from '@/components/WPRenderer/SmartSlider';
import { renderPostContent } from '@/components/WPRenderer/WPRenderer';
import { CarouselSlide, PostData } from '@/types/entities';
export const revalidate = 10;
export default async function HomePage() {
const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
const res = await fetch(`${baseUrl}/api/home`, { next: { revalidate: 10 }, });
if (!res.ok) {
// 🚨 REQUIRED for stale reuse
throw new Error('Failed to fetch home posts');
}
const posts: PostData[] = await res.json();
const stats: { title: string, value: string }[] = [
{
title: 'территории Якутии',
value: '74 %'
},
{
title: 'муниципальных районов',
value: '26'
},
{
title: 'теплоснабжающих объектов',
value: '637'
},
{
title: 'Гкал/ч установленная мощность',
value: '2645,4'
},
{
title: 'км. инженерных сетей',
value: '2818,5'
},
{
title: 'работников',
value: '8446'
}
]
const areaSlides: CarouselSlide[] = [
{
id: 0,
title: 'Абыйский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%90%D0%B1%D1%8B%D0%B9%D1%81%D0%BA%D0%B8%D0%B9-pxq84cwhvn5ot9eugai2c8bxn40u3rd9vx6c1fzro0.jpg'
},
{
id: 1,
title: 'Аллаиховский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%90%D0%BB%D0%BB%D0%B0%D0%B8%D1%85%D0%BE%D0%B2%D1%81%D0%BA%D0%B8%D0%B9-pxq84gnumzau3p9duc4km7ds0niayjs78fs9yju6z4.jpg'
},
{
id: 3,
title: 'Амгинский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%90%D0%BC%D0%B3%D0%B8%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq84kf7ebfze53x8dr2w6fme6zrtc74kye7vnoma8.jpg'
},
{
id: 4,
title: 'Анабарский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%90%D0%BD%D0%B0%D0%B1%D0%B0%D1%80%D1%81%D0%BA%D0%B8%D0%B9-pxq84o6k5nl4okygmfdl65hgrqh8o4m1xh05srj1lc.jpg'
},
{
id: 5,
title: 'Булунский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%91%D1%83%D0%BB%D1%83%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq84rxwwzq9z0t00h03g4jb59ypix0z9zm3pvdgwg.jpg'
},
{
id: 6,
title: 'Верхневилюйский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%92%D0%B5%D1%80%D1%85%D0%BD%D0%B5%D0%B2%D0%B8%D0%BB%D1%8E%D0%B9%D1%81%D0%BA%D0%B8%D0%B9-pxq84vp9obvf9gnjeimlq3l5itg6dpfwmi81mz7w7k.jpg'
},
{
id: 7,
title: 'Верхнеколымский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%92%D0%B5%D1%80%D1%85%D0%BD%D0%B5%D0%BA%D0%BE%D0%BB%D1%8B%D0%BC%D1%81%D0%BA%D0%B8%D0%B9-pxq84zgmfo0kjwi2sk9402mzwcxn8hutz0tzk32bio.jpg'
},
{
id: 8,
title: 'Вилюйский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%92%D0%B8%D0%BB%D1%8E%D0%B9%D1%81%D0%BA%D0%B8%D0%B9-pxq857x656c5ge5sf5wr4ii58try5rsf06pcvkpryo.jpg'
},
{
id: 9,
title: 'Верхоянский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%92%D0%B5%D1%80%D1%85%D0%BE%D1%8F%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq8545tdu705yb914a8ujgavaahazdhno3eygvcnk.jpg'
},
{
id: 10,
title: 'Горный',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%93%D0%BE%D1%80%D0%BD%D1%8B%D0%B9-pxq85cmd3cil2fyynpxvyzbg7r4s89b2otys9yit3k.jpg'
},
{
id: 11,
title: 'Жиганский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%96%D0%B8%D0%B3%D0%B0%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq85gdpuonqcvti1rke8ydalam931q01ckq72d8eo.jpg'
},
{
id: 12,
title: 'Заречный Кобяй',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%97%D0%B0%D1%80%D0%B5%D1%87%D0%BD%D1%8B%D0%B9-%D0%9A%D0%BE%D0%B1%D1%8F%D0%B9-pxq85k52m0svnbo1ft6wixf4yu3pxu4xdv6o467nps.jpg'
},
{
id: 13,
title: 'Кобяйский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%9A%D0%BE%D0%B1%D1%8F%D0%B9%D1%81%D0%BA%D0%B8%D0%B9-pxq85nwfdcy0xriktuteswgzcdl6smjuqdsm1a230w.jpg'
},
{
id: 14,
title: 'Мегино-Кангаласский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%9C%D0%B5%D0%B3%D0%B8%D0%BD%D0%BE-%D0%9A%D0%B0%D0%BD%D0%B3%D0%B0%D0%BB%D0%B0%D1%81%D1%81%D0%BA%D0%B8%D0%B9-pxq85rns4p3687d47wfx2vitpx2nneys2wejydwic0.jpg'
},
{
id: 15,
title: 'Момский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%9C%D0%BE%D0%BC%D1%81%D0%BA%D0%B8%D0%B9-pxq85vf4w18bin7nly2fcuko3gk4i7dpff0hvhqxn4.jpg'
},
{
id: 16,
title: 'Нижнеколымский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%9D%D0%B8%D0%B6%D0%BD%D0%B5%D0%BA%D0%BE%D0%BB%D1%8B%D0%BC%D1%81%D0%BA%D0%B8%D0%B9-pxq8604bu7er4p0tui3k7bdz2dwykowd429x9vjys0.jpg'
},
{
id: 17,
title: 'Нюрбинский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%9D%D1%8E%D1%80%D0%B1%D0%B8%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq863voljjwf4vd8jq2haftfxeffhbagkvv6zee34.jpg'
},
{
id: 18,
title: 'Олекминский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%9E%D0%BB%D0%B5%D0%BA%D0%BC%D0%B8%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq867n1cvp1pkpwmlckr9hntgvwa9q7t3ht438te8.jpg'
},
{
id: 19,
title: 'Оленекский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%9E%D0%BB%D0%B5%D0%BD%D0%B5%D0%BA%D1%81%D0%BA%D0%B8%D0%B9-pxq86bee47u700kg0mz318ji70dd52555m3r1738pc.jpg'
},
{
id: 20,
title: 'Среднеколымский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%A1%D1%80%D0%B5%D0%B4%D0%BD%D0%B5%D0%BA%D0%BE%D0%BB%D1%8B%D0%BC%D1%81%D0%BA%D0%B8%D0%B9-pxq86f5qvjzcagezeollb7lckjutzuk2i4poyaxo0g.jpg'
},
{
id: 21,
title: 'Сунтарский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%A1%D1%83%D0%BD%D1%82%D0%B0%D1%80%D1%81%D0%BA%D0%B8%D0%B9-pxq86juxtq5rwi85n8mq5oenjh7o2c2q6rz4coqp5c.jpg'
},
{
id: 22,
title: 'Таттинский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%A2%D0%B0%D1%82%D1%82%D0%B8%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq86nmal2ax6y2p1a98fnghx0p4x4hnjal29sl4gg.jpg'
},
{
id: 23,
title: 'Томпонский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%A2%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq86rdnceg2hdx8fbvqpmicak6lrwwkvt706wfjrk.jpg'
},
{
id: 24,
title: 'Усть-Алданский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%A3%D1%81%D1%82%D1%8C-%D0%90%D0%BB%D0%B4%D0%B0%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq86w2uakmi3fqenvwvk3bn9hjfuef8kggfla8kwg.jpg'
},
{
id: 25,
title: 'Хангаласский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%A5%D0%B0%D0%BD%D0%B3%D0%B0%D0%BB%D0%B0%D1%81%D1%81%D0%BA%D0%B8%D0%B9-pxq86zu71wrndvky1xjdu2dhn10wp6u5wz2die307k.jpg'
},
{
id: 26,
title: 'Чурапчинский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%A7%D1%83%D1%80%D0%B0%D0%BF%D1%87%D0%B8%D0%BD%D1%81%D0%BA%D0%B8%D0%B9-pxq873ljt8wsobfhfz5w41fc0kidjz939hobfhxfio.jpg'
},
{
id: 27,
title: 'Эвено-Бытантайский',
src: 'https://new.jkhsakha.ru/wp-content/uploads/elementor/thumbs/%D0%AD%D0%B2%D0%B5%D0%BD%D0%BE-%D0%91%D1%8B%D1%82%D0%B0%D0%BD%D1%82%D0%B0%D0%B9%D1%81%D0%BA%D0%B8%D0%B9-pxq877cwkl1xyra0u0see0h6e3zuero0m0a9clruts.jpg'
}
]
const slides: CarouselSlide[] = [
{
id: 0,
title: "Тепло сердец - людям!",
src: "/first_slide2.jpg",
},
{
id: 1,
title: "Личный кабинет",
src: "/Личный-кабинет.jpg",
href: "https://lk.jkhsakha.ru",
},
{
id: 2,
title: "Способы оплаты",
src: "/Способ-оплаты.jpg",
href: "/sposoby-oplaty/",
}
]
return (
<div className="w-full flex flex-col items-center sm:gap-8 mb-8">
{/* {posts.map(post => (
<div key={post.ID} className="max-w-5xl">
{renderPostContent(post.post_content)}
</div>
))} */}
<EmblaCarousel title_align='left' title_position='top' show_dots={true} show_title={true} slides={slides} />
<Section title='Новости'>
<div className='grid grid-cols-2 space-y-4 space-x-4'>
<EmblaCarousel rounded title_align='left' shape='square' show_title title_position='bottom' autoplay={true} show_dots={true} slides_per_view={1} slides={slides} dots_position='bottom_inside' />
</div>
</Section>
<Section title='О предприятии'>
<div className={`grid grid-cols-1 lg:grid-cols-2 justify-center items-center`}>
<div>
<Areas />
</div>
<div className="grid grid-cols-2 space-y-4 space-x-4">
{stats.map((stat, index) => (
<div key={index} className="grid grid-rows-[min-content_1fr] space-y-2">
<div className="stat-value text-3xl sm:text-5xl text-[#0063A7]">{stat.value}</div>
<div className="stat-title text-wrap text-lg sm:text-2xl text-neutral">{stat.title}</div>
</div>
))}
</div>
</div>
</Section>
<Section title='Филиалы'>
<EmblaCarousel rounded autoscroll={true} show_dots={false} slides_per_view={4} slides={areaSlides} dots_position='bottom' />
</Section>
<Section title="Контакты">
</Section>
</div>
);
}