updated widgets
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
// 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;
|
||||
|
||||
interface PageProps {
|
||||
export interface PageProps {
|
||||
params: {
|
||||
slug: string;
|
||||
};
|
||||
@@ -28,20 +29,6 @@ export default async function PostPage({ params }: PageProps) {
|
||||
const post = await res.json();
|
||||
|
||||
return (
|
||||
<div className="container mx-auto py-8 max-w-5xl">
|
||||
<article className="prose lg:prose-xl max-w-none">
|
||||
<h1>{post.post_title}</h1>
|
||||
|
||||
{post.post_type === 'post' && (
|
||||
<div className="text-gray-600 mb-6">
|
||||
<time>
|
||||
{new Date(post.post_date).toLocaleString('ru-RU')}
|
||||
</time>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{renderPostContent(post.post_content)}
|
||||
</article>
|
||||
</div>
|
||||
<Post post={post} />
|
||||
);
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export default async function PostPage({ params }: PageProps) {
|
||||
{pageData.post_type === 'post' && (
|
||||
<div className="text-gray-600 mb-6">
|
||||
<time>
|
||||
{new Date(pageData.post_date).toLocaleString('ru-RU')}
|
||||
{pageData.post_date}
|
||||
</time>
|
||||
</div>
|
||||
)}
|
||||
39
src/app/(client)/layout.tsx
Normal file
39
src/app/(client)/layout.tsx
Normal 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
250
src/app/(client)/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
17
src/app/(editor)/[slug]/edit/page.tsx
Normal file
17
src/app/(editor)/[slug]/edit/page.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { PageProps } from "@/app/(client)/[slug]/page";
|
||||
import PostPage from "@/app/(client)/[slug]/page";
|
||||
import ClientLayout from "@/app/(client)/layout";
|
||||
import Post from "@/components/Post/Post";
|
||||
import Tiptap from "@/components/Tiptap/Tiptap";
|
||||
|
||||
export default async function EditorPage({ params }: PageProps) {
|
||||
const { slug } = await params;
|
||||
|
||||
const pageData = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/pages/${slug}`, {
|
||||
//next: { revalidate: 60 },
|
||||
}).then(res => res.json());
|
||||
|
||||
return (
|
||||
<Tiptap slug={slug} data={pageData} />
|
||||
)
|
||||
}
|
||||
91
src/app/(editor)/admin/page.tsx
Normal file
91
src/app/(editor)/admin/page.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
'use client'
|
||||
|
||||
import { WebVitalsCard } from "@/components/WebVitalsCard"
|
||||
import { useWebVitals } from "@/hooks/useWebVitals"
|
||||
|
||||
const AdminPage = () => {
|
||||
const metrics = useWebVitals()
|
||||
|
||||
const vitalsConfig = [
|
||||
{
|
||||
title: 'Time to First Byte (TTFB)',
|
||||
value: metrics.TTFB,
|
||||
unit: 'ms',
|
||||
description: 'Time between request and first byte of response',
|
||||
thresholds: { good: 200, needsImprovement: 500 }
|
||||
},
|
||||
{
|
||||
title: 'First Contentful Paint (FCP)',
|
||||
value: metrics.FCP,
|
||||
unit: 'ms',
|
||||
description: 'Time until first content is rendered',
|
||||
thresholds: { good: 1800, needsImprovement: 3000 }
|
||||
},
|
||||
{
|
||||
title: 'Largest Contentful Paint (LCP)',
|
||||
value: metrics.LCP,
|
||||
unit: 'ms',
|
||||
description: 'Time until largest content element is rendered',
|
||||
thresholds: { good: 2500, needsImprovement: 4000 }
|
||||
},
|
||||
{
|
||||
title: 'First Input Delay (FID)',
|
||||
value: metrics.FID,
|
||||
unit: 'ms',
|
||||
description: 'Time from first user interaction to browser response',
|
||||
thresholds: { good: 100, needsImprovement: 300 }
|
||||
},
|
||||
{
|
||||
title: 'Cumulative Layout Shift (CLS)',
|
||||
value: metrics.CLS,
|
||||
unit: '',
|
||||
description: 'Measures visual stability (lower is better)',
|
||||
thresholds: { good: 0.1, needsImprovement: 0.25 }
|
||||
},
|
||||
{
|
||||
title: 'Interaction to Next Paint (INP)',
|
||||
value: metrics.INP,
|
||||
unit: 'ms',
|
||||
description: 'Measures responsiveness to user interactions',
|
||||
thresholds: { good: 200, needsImprovement: 500 }
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 py-8">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900">Web Vitals Dashboard</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
Real-time Core Web Vitals metrics for your Next.js application
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{vitalsConfig.map((vital) => (
|
||||
<WebVitalsCard
|
||||
key={vital.title}
|
||||
title={vital.title}
|
||||
value={vital.value}
|
||||
unit={vital.unit}
|
||||
description={vital.description}
|
||||
thresholds={vital.thresholds}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
||||
<h2 className="text-lg font-semibold text-blue-900 mb-2">About Core Web Vitals</h2>
|
||||
<p className="text-sm text-blue-800">
|
||||
Core Web Vitals are a set of real-world, user-centered metrics that quantify key aspects
|
||||
of user experience. They measure dimensions of web usability such as load time,
|
||||
interactivity, and the stability of content as it loads.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default AdminPage
|
||||
354
src/app/(editor)/admin/posts/page.tsx
Normal file
354
src/app/(editor)/admin/posts/page.tsx
Normal file
@@ -0,0 +1,354 @@
|
||||
import React from 'react'
|
||||
|
||||
const PostsPage = () => {
|
||||
return (
|
||||
<div className='p-4'>
|
||||
<div className="">
|
||||
<table className="table table-pin-rows table-pin-cols">
|
||||
{/* head */}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<th>Name</th>
|
||||
<th>Job</th>
|
||||
<th>Favorite Color</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/* row 1 */}
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/2@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Hart Hagerty</div>
|
||||
<div className="text-sm opacity-50">United States</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Zemlak, Daniel and Leannon
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Desktop Support Technician</span>
|
||||
</td>
|
||||
<td>Purple</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
{/* row 2 */}
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/3@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Brice Swyre</div>
|
||||
<div className="text-sm opacity-50">China</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Carroll Group
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Tax Accountant</span>
|
||||
</td>
|
||||
<td>Red</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
{/* row 3 */}
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/4@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Marjy Ferencz</div>
|
||||
<div className="text-sm opacity-50">Russia</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Rowe-Schoen
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Office Assistant I</span>
|
||||
</td>
|
||||
<td>Crimson</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
{/* row 4 */}
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/5@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Yancy Tear</div>
|
||||
<div className="text-sm opacity-50">Brazil</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Wyman-Ledner
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Community Outreach Specialist</span>
|
||||
</td>
|
||||
<td>Indigo</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/5@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Yancy Tear</div>
|
||||
<div className="text-sm opacity-50">Brazil</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Wyman-Ledner
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Community Outreach Specialist</span>
|
||||
</td>
|
||||
<td>Indigo</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/5@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Yancy Tear</div>
|
||||
<div className="text-sm opacity-50">Brazil</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Wyman-Ledner
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Community Outreach Specialist</span>
|
||||
</td>
|
||||
<td>Indigo</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/5@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Yancy Tear</div>
|
||||
<div className="text-sm opacity-50">Brazil</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Wyman-Ledner
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Community Outreach Specialist</span>
|
||||
</td>
|
||||
<td>Indigo</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/5@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Yancy Tear</div>
|
||||
<div className="text-sm opacity-50">Brazil</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Wyman-Ledner
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Community Outreach Specialist</span>
|
||||
</td>
|
||||
<td>Indigo</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/5@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Yancy Tear</div>
|
||||
<div className="text-sm opacity-50">Brazil</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Wyman-Ledner
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Community Outreach Specialist</span>
|
||||
</td>
|
||||
<td>Indigo</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" className="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="avatar">
|
||||
<div className="mask mask-squircle h-12 w-12">
|
||||
<img
|
||||
src="https://img.daisyui.com/images/profile/demo/5@94.webp"
|
||||
alt="Avatar Tailwind CSS Component" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">Yancy Tear</div>
|
||||
<div className="text-sm opacity-50">Brazil</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
Wyman-Ledner
|
||||
<br />
|
||||
<span className="badge badge-ghost badge-sm">Community Outreach Specialist</span>
|
||||
</td>
|
||||
<td>Indigo</td>
|
||||
<th>
|
||||
<button className="btn btn-ghost btn-xs">details</button>
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
{/* foot */}
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>Job</th>
|
||||
<th>Favorite Color</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PostsPage
|
||||
70
src/app/(editor)/layout.tsx
Normal file
70
src/app/(editor)/layout.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { IconBook2, IconHome, IconSettings } from "@tabler/icons-react";
|
||||
|
||||
export default function EditLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="drawer lg:drawer-open">
|
||||
<input id="my-drawer-4" type="checkbox" className="drawer-toggle" />
|
||||
<div className="drawer-content h-screen grid grid-rows-[min-content_1fr]">
|
||||
{/* Navbar */}
|
||||
<nav className="navbar w-full bg-base-300">
|
||||
<label htmlFor="my-drawer-4" aria-label="open sidebar" className="btn btn-square btn-ghost">
|
||||
{/* Sidebar toggle icon */}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" strokeLinejoin="round" strokeLinecap="round" strokeWidth="2" fill="none" stroke="currentColor" className="my-1.5 inline-block size-4"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg>
|
||||
</label>
|
||||
<div id="editor-navbar-title" className="px-4">
|
||||
{/* Editor Navbar title goes here */}
|
||||
</div>
|
||||
<div id="editor-navbar" className="ml-auto flex space-x-2">
|
||||
{/* Editor Navbar items go here */}
|
||||
</div>
|
||||
</nav>
|
||||
{/* Page content here */}
|
||||
<div className="overflow-auto max-h-full">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="drawer-side is-drawer-close:overflow-visible">
|
||||
<label htmlFor="my-drawer-4" aria-label="close sidebar" className="drawer-overlay"></label>
|
||||
<div className="flex min-h-full flex-col items-start bg-base-200 is-drawer-close:w-14 is-drawer-open:w-64">
|
||||
{/* Sidebar content here */}
|
||||
<ul className="menu w-full grow border-r border-r-gray-300">
|
||||
{/* List item */}
|
||||
<li>
|
||||
<a
|
||||
role="button"
|
||||
href="/admin"
|
||||
className="is-drawer-close:tooltip is-drawer-close:tooltip-right" data-tip="Главная">
|
||||
{/* Home icon */}
|
||||
<IconHome className="my-1.5 inline-block size-4" size={16} />
|
||||
<span className="is-drawer-close:hidden">Главная</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a
|
||||
role="button"
|
||||
href="/admin/posts"
|
||||
className="is-drawer-close:tooltip is-drawer-close:tooltip-right" data-tip="Записи">
|
||||
<IconBook2 className="my-1.5 inline-block size-4" size={16} />
|
||||
<span className="is-drawer-close:hidden">Записи</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{/* List item */}
|
||||
<li>
|
||||
<button className="is-drawer-close:tooltip is-drawer-close:tooltip-right" data-tip="Настройки">
|
||||
<IconSettings className="my-1.5 inline-block size-4" size={16} />
|
||||
<span className="is-drawer-close:hidden">Настройки</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import Header from "@/components/Blocks/Header/Header";
|
||||
import Footer from "@/components/Footer/Footer";
|
||||
import "@/styles/globals.css";
|
||||
import { Montserrat, Roboto, Roboto_Condensed } from 'next/font/google'
|
||||
|
||||
@@ -25,13 +23,16 @@ export default function GlobalLayout({
|
||||
return (
|
||||
<html lang="en" className={mainFont.className}>
|
||||
<body className="relative min-h-screen flex flex-col overflow-auto">
|
||||
<Header />
|
||||
{/* <Header />
|
||||
|
||||
<main className="flex-1 px-4 sm:px-0 overflow-x-auto">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
|
||||
<CookieNotice /> */}
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// app/page.tsx
|
||||
import { renderPostContent } from '@/components/WPRenderer/WPRenderer';
|
||||
import { 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();
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col items-center">
|
||||
{posts.map(post => (
|
||||
<div key={post.ID} className="max-w-5xl">
|
||||
{renderPostContent(post.post_content)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
13
src/app/test/page.tsx
Normal file
13
src/app/test/page.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
// app/page.tsx
|
||||
|
||||
import Areas from "@/components/WP/Areas/Areas";
|
||||
|
||||
export const revalidate = 10;
|
||||
|
||||
export default async function TestPage() {
|
||||
return (
|
||||
<div className="w-full flex flex-col items-center">
|
||||
<Areas />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user