155 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'; // Превращаем компонент в клиентский
import Link from "next/link";
import React, { useEffect, useState } from "react";
import { allProjects } from "contentlayer/generated";
import { Navigation } from "../components/nav";
import { Card } from "../components/card";
import { Article } from "./article";
import { Eye } from "lucide-react";
export const revalidate = 60;
type ViewsData = Record<string, number>;
export default function ProjectsPage() {
const [views, setViews] = useState<ViewsData>({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Загружаем данные через клиентский запрос
useEffect(() => {
const fetchViews = async () => {
try {
const response = await fetch('/api/views');
if (!response.ok) throw new Error('Failed to fetch views');
const data = await response.json();
setViews(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};
fetchViews();
}, []);
const featured = allProjects.find((project) => project.slug === "cbg")!;
const top2 = allProjects.find((project) => project.slug === "blog")!;
const top3 = allProjects.find((project) => project.slug === "bimkaspace")!;
const sorted = allProjects
.filter((p) => p.published)
.filter(
(project) =>
project.slug !== featured.slug &&
project.slug !== top2.slug &&
project.slug !== top3.slug,
)
.sort(
(a, b) =>
new Date(b.date ?? Number.POSITIVE_INFINITY).getTime() -
new Date(a.date ?? Number.POSITIVE_INFINITY).getTime(),
);
return (
<div className="relative pb-16">
<Navigation />
<div className="px-6 pt-20 mx-auto space-y-8 max-w-7xl lg:px-8 md:space-y-16 md:pt-24 lg:pt-32">
<div className="max-w-2xl mx-auto lg:mx-0">
<h2 className="text-3xl font-bold tracking-tight text-zinc-100 sm:text-4xl">
Проекты
</h2>
<p className="mt-4 text-zinc-400">
Часть проектов делал из академического интереса, часть мои проекты для себя.
</p>
</div>
<div className="w-full h-px bg-zinc-800" />
<div className="grid grid-cols-1 gap-8 mx-auto lg:grid-cols-2 ">
<Card>
<Link href={`/projects/${featured.slug}`}>
<article className="relative w-full h-full p-4 md:p-8">
<div className="flex items-center justify-between gap-2">
<div className="text-xs text-zinc-100">
{featured.date ? (
<time dateTime={new Date(featured.date).toISOString()}>
{Intl.DateTimeFormat(undefined, {
dateStyle: "medium",
}).format(new Date(featured.date))}
</time>
) : (
<span>SOON</span>
)}
</div>
<span className="flex items-center gap-1 text-xs text-zinc-500">
<Eye className="w-4 h-4" />{" "}
{Intl.NumberFormat("en-US", { notation: "compact" }).format(
views[featured.slug] ?? 0,
)}
</span>
</div>
<h2
id="featured-post"
className="mt-4 text-3xl font-bold text-zinc-100 group-hover:text-white sm:text-4xl font-display"
>
{featured.title}
</h2>
<p className="mt-4 leading-8 duration-150 text-zinc-400 group-hover:text-zinc-300">
{featured.description}
</p>
<div className="absolute bottom-4 md:bottom-8">
<p className="hidden text-zinc-200 hover:text-zinc-50 lg:block">
Подробнее <span aria-hidden="true">&rarr;</span>
</p>
</div>
</article>
</Link>
</Card>
<div className="flex flex-col w-full gap-8 mx-auto border-t border-gray-900/10 lg:mx-0 lg:border-t-0 ">
{[top2, top3].map((project) => (
<Card key={project.slug}>
<Article project={project} views={views[project.slug] ?? 0} />
</Card>
))}
</div>
</div>
<div className="hidden w-full h-px md:block bg-zinc-800" />
<div className="grid grid-cols-1 gap-4 mx-auto lg:mx-0 md:grid-cols-3">
<div className="grid grid-cols-1 gap-4">
{sorted
.filter((_, i) => i % 3 === 0)
.map((project) => (
<Card key={project.slug}>
<Article project={project} views={views[project.slug] ?? 0} />
</Card>
))}
</div>
<div className="grid grid-cols-1 gap-4">
{sorted
.filter((_, i) => i % 3 === 1)
.map((project) => (
<Card key={project.slug}>
<Article project={project} views={views[project.slug] ?? 0} />
</Card>
))}
</div>
<div className="grid grid-cols-1 gap-4">
{sorted
.filter((_, i) => i % 3 === 2)
.map((project) => (
<Card key={project.slug}>
<Article project={project} views={views[project.slug] ?? 0} />
</Card>
))}
</div>
</div>
</div>
</div>
);
}