Новое измнений в код

This commit is contained in:
Web-serfer 2026-03-31 18:30:25 +05:00
parent 690a2ab472
commit 735146fbfb
3 changed files with 152 additions and 88 deletions

View file

@ -8,8 +8,10 @@ import SidebarCategories from '@components/blog/SidebarCategories.astro';
import TagCloud from '@components/blog/TagCloud.astro';
import { MONTHS } from '@lib/constants';
// Конфигурация
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || "http://localhost:8090";
// Типы
interface PostRecord {
id: string;
title: string;
@ -29,22 +31,7 @@ interface CategoryItem {
count: number;
}
// Получаем параметр категории из URL
const { category } = Astro.params;
// Декодируем категорию из URL (например, "ugolovnoe-pravo" -> "Уголовное право")
function decodeCategory(slug: string | undefined): string {
if (!slug) return "";
// Заменяем дефисы на пробелы и приводим к правильному регистру
return slug
.replace(/-/g, ' ')
.replace(/\b\w/g, (char) => char.toUpperCase());
}
const categoryName = decodeCategory(category);
// Загружаем посты из PocketBase с фильтром по категории
let posts: Array<{
interface Post {
id: string;
image: string;
category: string;
@ -52,43 +39,134 @@ let posts: Array<{
excerpt: string;
date: string;
slug: string;
}> = [];
}
interface PopularPost {
image: string;
title: string;
views: string;
}
// Получаем параметр из URL
const { category } = Astro.params;
// Декодируем slug в название категории
function decodeCategory(slug: string | undefined): string {
if (!slug) return "";
return slug
.replace(/-/g, ' ')
.replace(/\b\w/g, (char) => char.toUpperCase());
}
const categoryName = decodeCategory(category);
// Форматирование даты
function formatDate(dateString: string): string {
const date = new Date(dateString);
return `${date.getDate()} ${MONTHS[date.getMonth()]} ${date.getFullYear()} года`;
}
// Получение URL изображения
function getImageUrl(post: PostRecord): string {
if (post.image) {
return `${POCKETBASE_URL}/api/files/posts/${post.id}/${post.image}`;
}
return "https://images.unsplash.com/photo-1556761175-5973dc0f32e7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80";
}
// Получение категории поста
function getPostCategory(post: PostRecord): string {
return post.category?.trim() || "НОВОСТИ";
}
// Загрузка постов по категории
async function fetchPostsByCategory(category: string): Promise<Post[]> {
if (!category) return [];
try {
// Фильтруем по полю category
const filter = encodeURIComponent(`category="${categoryName}"`);
const response = await fetch(`${POCKETBASE_URL}/api/collections/posts/records?filter=${filter}&perPage=20&sort=-created`);
const filter = encodeURIComponent(`category="${category}"`);
const url = `${POCKETBASE_URL}/api/collections/posts/records?filter=${filter}&perPage=20&sort=-created`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: PocketBaseResponse = await response.json();
if (data.items) {
posts = data.items.map((post) => {
const date = new Date(post.created);
const formattedDate = `${date.getDate()} ${MONTHS[date.getMonth()]} ${date.getFullYear()} года`;
if (!data.items) return [];
// Формируем URL изображения
const imageUrl = post.image
? `${POCKETBASE_URL}/api/files/posts/${post.id}/${post.image}`
: "https://images.unsplash.com/photo-1556761175-5973dc0f32e7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80";
// Берём категорию из поля category
const postCategory = post.category && post.category.trim() !== "" ? post.category : "НОВОСТИ";
return {
return data.items.map((post) => ({
id: post.id,
image: imageUrl,
category: postCategory,
image: getImageUrl(post),
category: getPostCategory(post),
title: post.title,
excerpt: post.excerpt,
date: formattedDate,
date: formatDate(post.created),
slug: `/blog/${post.slug}`
};
});
}
}));
} catch (error) {
console.error("Error fetching posts from PocketBase:", error);
console.error("[CategoryPage] Error fetching posts:", error);
return [];
}
}
const popularPosts = [
// Загрузка всех категорий
async function fetchCategories(): Promise<CategoryItem[]> {
try {
const response = await fetch(`${POCKETBASE_URL}/api/collections/posts/records?perPage=500`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: PocketBaseResponse = await response.json();
if (!data.items) return [];
const categoryMap = new Map<string, number>();
data.items.forEach((post) => {
const cat = getPostCategory(post);
categoryMap.set(cat, (categoryMap.get(cat) || 0) + 1);
});
return Array.from(categoryMap.entries())
.map(([name, count]) => ({ name, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 6);
} catch (error) {
console.error("[CategoryPage] Error fetching categories:", error);
return [];
}
}
// Склонение слова "статья"
function getArticlesCountText(count: number): string {
const lastDigit = count % 10;
const lastTwoDigits = count % 100;
if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
return `${count} статей`;
}
if (lastDigit === 1) {
return `${count} статья`;
}
if (lastDigit >= 2 && lastDigit <= 4) {
return `${count} статьи`;
}
return `${count} статей`;
}
// Данные страницы
const posts = await fetchPostsByCategory(categoryName);
const categories = await fetchCategories();
const popularPosts: PopularPost[] = [
{
image: "https://images.unsplash.com/photo-1556761175-5973dc0f32e7?w=200&h=200&fit=crop&q=80",
title: "Как обжаловать решение суда первой инстанции",
@ -106,35 +184,11 @@ const popularPosts = [
}
];
// Загружаем категории для сайдбара
let categories: CategoryItem[] = [];
try {
const allPostsResponse = await fetch(`${POCKETBASE_URL}/api/collections/posts/records?perPage=500`);
const allPostsData: PocketBaseResponse = await allPostsResponse.json();
if (allPostsData.items) {
const categoryMap = new Map<string, number>();
allPostsData.items.forEach((post) => {
const cat = post.category && post.category.trim() !== "" ? post.category : "НОВОСТИ";
const currentCount = categoryMap.get(cat) || 0;
categoryMap.set(cat, currentCount + 1);
});
categories = Array.from(categoryMap.entries())
.map(([name, count]) => ({ name, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 6);
}
} catch (error) {
console.error("Error fetching categories from PocketBase:", error);
categories = [];
}
// SEO данные
const pageTitle = `${categoryName} — Статьи и публикации`;
const pageDescription = `Все статьи категории "${categoryName}" на сайте адвоката`;
// SEO
const pageTitle = categoryName ? `${categoryName} — Статьи и публикации` : "Категории — Блог";
const pageDescription = categoryName
? `Все статьи категории "${categoryName}" на сайте адвоката`
: "Все категории статей на сайте адвоката";
---
<Layout

View file

@ -27,17 +27,20 @@ import Toast from "@components/base/Toast.astro";
<script>
document.addEventListener("DOMContentLoaded", () => {
const anchors = document.querySelectorAll('a[href^="#"]');
const anchors = document.querySelectorAll<HTMLAnchorElement>('a[href^="#"]');
anchors.forEach((anchor) => {
anchor.addEventListener("click", function (e) {
anchor.addEventListener("click", function (this: HTMLAnchorElement, e: Event) {
e.preventDefault();
const targetId = this.getAttribute("href");
const targetElement = document.querySelector(targetId);
if (!targetId) return;
const targetElement = document.querySelector<HTMLElement>(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 80, // отступ сверху для компенсации фиксированного хедера
top: targetElement.offsetTop - 80,
behavior: "smooth",
});
}

7
frontend/src/types/global.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
export {};
declare global {
interface Window {
showToast: (message: string, type?: 'success' | 'error' | 'info', duration?: number) => void;
}
}