Новое измнений в код
This commit is contained in:
parent
690a2ab472
commit
735146fbfb
3 changed files with 152 additions and 88 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
7
frontend/src/types/global.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export {};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
showToast: (message: string, type?: 'success' | 'error' | 'info', duration?: number) => void;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue