fix: исправить типизацию, добавить SEO метатеги, исправить API Rules PB
- Добавлены типы PostVotes, VoteStats, Comment, Consultation, PostResponse - Заменены все any на конкретные типы - Исправлены catch (error: any) -> catch (error: unknown) - Добавлены og: и twitter: метатеги в Layout - Исправлены API Rules в PocketBase (posts, reviews, post_votes)
This commit is contained in:
parent
2f57bf91ef
commit
b5d2174fdf
20 changed files with 110 additions and 41 deletions
|
|
@ -64,9 +64,52 @@ export interface DocumentItem {
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NavLink {
|
export interface PostVotes {
|
||||||
|
id: string;
|
||||||
|
post_id: string;
|
||||||
|
user_id: string;
|
||||||
|
vote_type: 'like' | 'dislike';
|
||||||
|
created: string;
|
||||||
|
updated: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VoteStats {
|
||||||
|
likes: number;
|
||||||
|
dislikes: number;
|
||||||
|
userVote: 'like' | 'dislike' | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Comment {
|
||||||
|
id: string;
|
||||||
|
post_id: string;
|
||||||
|
user_id: string;
|
||||||
|
author_name: string;
|
||||||
|
content: string;
|
||||||
|
status: 'pending' | 'published';
|
||||||
|
created: string;
|
||||||
|
updated: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Consultation {
|
||||||
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
phone: string;
|
||||||
|
question: string;
|
||||||
|
status: 'new' | 'in_progress' | 'completed';
|
||||||
|
created: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostResponse {
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
author: string;
|
||||||
|
category: string;
|
||||||
|
categoryColor: string;
|
||||||
|
date: string;
|
||||||
|
readTime: string;
|
||||||
|
image: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompanyInfo {
|
export interface CompanyInfo {
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,10 @@ export interface Props {
|
||||||
description: string;
|
description: string;
|
||||||
canonicalLink?: string;
|
canonicalLink?: string;
|
||||||
breadcrumbs?: Array<{ label: string; href?: string }>;
|
breadcrumbs?: Array<{ label: string; href?: string }>;
|
||||||
|
ogImage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, description, canonicalLink, breadcrumbs } = Astro.props;
|
const { title, description, canonicalLink, breadcrumbs, ogImage } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
|
@ -32,6 +33,17 @@ const { title, description, canonicalLink, breadcrumbs } = Astro.props;
|
||||||
<title>{title} {SITE_TITLE_SUFFIX}</title>
|
<title>{title} {SITE_TITLE_SUFFIX}</title>
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
{canonicalLink && <link rel="canonical" href={canonicalLink} />}
|
{canonicalLink && <link rel="canonical" href={canonicalLink} />}
|
||||||
|
<!-- Open Graph -->
|
||||||
|
<meta property="og:title" content={title} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
{canonicalLink && <meta property="og:url" content={canonicalLink} />}
|
||||||
|
{ogImage && <meta property="og:image" content={ogImage} />}
|
||||||
|
<!-- Twitter Card -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content={title} />
|
||||||
|
<meta name="twitter:description" content={description} />
|
||||||
|
{ogImage && <meta name="twitter:image" content={ogImage} />}
|
||||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
<!-- Yandex верификация -->
|
<!-- Yandex верификация -->
|
||||||
<meta name="yandex-verification" content="be3edfd138348e43" />
|
<meta name="yandex-verification" content="be3edfd138348e43" />
|
||||||
|
|
|
||||||
|
|
@ -60,13 +60,12 @@ export async function getPostVotes(postId: string): Promise<VoteStats> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPostVotesStats(postId: string): Promise<{ likes: number; dislikes: number }> {
|
export async function getPostVotesStats(postId: string): Promise<{ likes: number; dislikes: number }> {
|
||||||
// Получаем данные из коллекции голосов
|
|
||||||
const votes = await pb.collection('post_votes').getList(1, 1000, {
|
const votes = await pb.collection('post_votes').getList(1, 1000, {
|
||||||
filter: `post="${postId}"`,
|
filter: `post="${postId}"`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const likes = votes.items.filter((v: any) => v.vote_type === 'like').length;
|
const likes = votes.items.filter((v) => v.vote_type === 'like').length;
|
||||||
const dislikes = votes.items.filter((v: any) => v.vote_type === 'dislike').length;
|
const dislikes = votes.items.filter((v) => v.vote_type === 'dislike').length;
|
||||||
|
|
||||||
return { likes, dislikes };
|
return { likes, dislikes };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ export const POST: APIRoute = async ({ request }) => {
|
||||||
message: 'Email подтверждён'
|
message: 'Email подтверждён'
|
||||||
}), { status: 200 });
|
}), { status: 200 });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Confirm error:', error);
|
console.error('Confirm error:', error);
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ export const POST: APIRoute = async ({ request }) => {
|
||||||
message: 'Ссылка для сброса пароля отправлена'
|
message: 'Ссылка для сброса пароля отправлена'
|
||||||
}), { status: 200 });
|
}), { status: 200 });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Forgot password error:', error);
|
console.error('Forgot password error:', error);
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ export const POST: APIRoute = async ({ request }) => {
|
||||||
});
|
});
|
||||||
const collectionData = await collectionResponse.json();
|
const collectionData = await collectionResponse.json();
|
||||||
|
|
||||||
const fieldExists = collectionData.fields?.some((f: any) => f.name === 'reset_token');
|
const fieldExists = collectionData.fields?.some((f: { name: string }) => f.name === 'reset_token');
|
||||||
|
|
||||||
if (!fieldExists) {
|
if (!fieldExists) {
|
||||||
// Добавляем поля в коллекцию
|
// Добавляем поля в коллекцию
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ export const POST: APIRoute = async ({ request }) => {
|
||||||
message: 'Пароль успешно изменён'
|
message: 'Пароль успешно изменён'
|
||||||
}), { status: 200 });
|
}), { status: 200 });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Reset password error:', error);
|
console.error('Reset password error:', error);
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
|
|
|
||||||
|
|
@ -53,12 +53,12 @@ export const POST: APIRoute = async ({ request, cookies, url }) => {
|
||||||
redirect: redirectUrl
|
redirect: redirectUrl
|
||||||
}), { status: 200 });
|
}), { status: 200 });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Sign in error:', error);
|
console.error('Sign in error:', error);
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message || 'Неверный email или пароль'
|
error: error instanceof Error ? error.message : 'Неверный email или пароль'
|
||||||
}), { status: 401 });
|
}), { status: 401 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -80,19 +80,20 @@ export const POST: APIRoute = async ({ request, redirect }) => {
|
||||||
email
|
email
|
||||||
}), { status: 201 });
|
}), { status: 201 });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Sign up error:', error);
|
console.error('Sign up error:', error);
|
||||||
|
|
||||||
let errorMessage = 'Ошибка при регистрации';
|
let errorMessage = 'Ошибка при регистрации';
|
||||||
|
|
||||||
if (error.response?.data) {
|
if (error && typeof error === 'object' && 'response' in error) {
|
||||||
const data = error.response.data;
|
const errWithResponse = error as { response?: { data?: Record<string, unknown> } };
|
||||||
if (data.email) {
|
const data = errWithResponse.response?.data as Record<string, unknown> | undefined;
|
||||||
errorMessage = `Email: ${data.email.message || 'уже используется'}`;
|
if (data?.email) {
|
||||||
} else if (data.password) {
|
errorMessage = `Email: ${(data.email as { message?: string }).message || 'уже используется'}`;
|
||||||
errorMessage = `Пароль: ${data.password.message || 'некорректный'}`;
|
} else if (data?.password) {
|
||||||
|
errorMessage = `Пароль: ${(data.password as { message?: string }).message || 'некорректный'}`;
|
||||||
}
|
}
|
||||||
} else if (error.message) {
|
} else if (error instanceof Error) {
|
||||||
errorMessage = error.message;
|
errorMessage = error.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146,12 +146,13 @@ export const POST: APIRoute = async ({ request }) => {
|
||||||
id: result.id
|
id: result.id
|
||||||
}), { status: 201, headers: { 'Content-Type': 'application/json' } });
|
}), { status: 201, headers: { 'Content-Type': 'application/json' } });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Consultation error:', error);
|
console.error('Consultation error:', error);
|
||||||
|
|
||||||
|
const message = error instanceof Error ? error.message : 'Ошибка при отправке заявки';
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message || 'Ошибка при отправке заявки'
|
error: message
|
||||||
}), { status: 400, headers: { 'Content-Type': 'application/json' } });
|
}), { status: 400, headers: { 'Content-Type': 'application/json' } });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -38,11 +38,13 @@ export const GET: APIRoute = async ({ params }) => {
|
||||||
content: post.content,
|
content: post.content,
|
||||||
}), { status: 200 });
|
}), { status: 200 });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('API post error:', error);
|
console.error('API post error:', error);
|
||||||
|
|
||||||
|
const message = error instanceof Error ? error.message : 'Ошибка при получении поста';
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
error: error.message || 'Ошибка при получении поста'
|
error: message
|
||||||
}), { status: 500 });
|
}), { status: 500 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro';
|
||||||
import PocketBase from 'pocketbase';
|
import PocketBase from 'pocketbase';
|
||||||
|
import type { PostResponse } from '../../globalInterfaces';
|
||||||
|
|
||||||
const PB_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
|
const PB_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
|
||||||
|
|
||||||
|
|
@ -27,7 +28,7 @@ export const GET: APIRoute = async ({ url }) => {
|
||||||
sort: '-date',
|
sort: '-date',
|
||||||
});
|
});
|
||||||
|
|
||||||
const getImageUrl = (post: any) => {
|
const getImageUrl = (post: PostResponse) => {
|
||||||
if (!post.image) return null;
|
if (!post.image) return null;
|
||||||
const fileUrl = pb.files.getUrl(post, post.image);
|
const fileUrl = pb.files.getUrl(post, post.image);
|
||||||
if (fileUrl.startsWith('/')) {
|
if (fileUrl.startsWith('/')) {
|
||||||
|
|
@ -37,7 +38,7 @@ export const GET: APIRoute = async ({ url }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
posts: result.items.map(post => ({
|
posts: result.items.map((post) => ({
|
||||||
id: post.id,
|
id: post.id,
|
||||||
slug: post.slug,
|
slug: post.slug,
|
||||||
title: post.title,
|
title: post.title,
|
||||||
|
|
@ -55,11 +56,13 @@ export const GET: APIRoute = async ({ url }) => {
|
||||||
totalPages: result.totalPages,
|
totalPages: result.totalPages,
|
||||||
}), { status: 200 });
|
}), { status: 200 });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('API posts error:', error);
|
console.error('API posts error:', error);
|
||||||
|
|
||||||
|
const message = error instanceof Error ? error.message : 'Ошибка при получении постов';
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
error: error.message || 'Ошибка при получении постов'
|
error: message
|
||||||
}), { status: 500 });
|
}), { status: 500 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -125,8 +125,8 @@ export const POST: APIRoute = async ({ request, cookies }) => {
|
||||||
if (votesRes.ok) {
|
if (votesRes.ok) {
|
||||||
const votesData = await votesRes.json();
|
const votesData = await votesRes.json();
|
||||||
console.log('[ReviewVote API] Votes data:', JSON.stringify(votesData));
|
console.log('[ReviewVote API] Votes data:', JSON.stringify(votesData));
|
||||||
likes = votesData.items?.filter((v: any) => v.vote_type === 'likes').length || 0;
|
likes = votesData.items?.filter((v: { vote_type: string }) => v.vote_type === 'likes').length || 0;
|
||||||
dislikes = votesData.items?.filter((v: any) => v.vote_type === 'dislikes').length || 0;
|
dislikes = votesData.items?.filter((v: { vote_type: string }) => v.vote_type === 'dislikes').length || 0;
|
||||||
} else {
|
} else {
|
||||||
const errorText = await votesRes.text();
|
const errorText = await votesRes.text();
|
||||||
console.error('[ReviewVote API] Votes error:', errorText);
|
console.error('[ReviewVote API] Votes error:', errorText);
|
||||||
|
|
@ -179,8 +179,8 @@ export const GET: APIRoute = async ({ url, cookies }) => {
|
||||||
if (votesRes.ok) {
|
if (votesRes.ok) {
|
||||||
const votesData = await votesRes.json();
|
const votesData = await votesRes.json();
|
||||||
console.log('[ReviewVote API GET] Votes data:', JSON.stringify(votesData));
|
console.log('[ReviewVote API GET] Votes data:', JSON.stringify(votesData));
|
||||||
likes = votesData.items?.filter((v: any) => v.vote_type === 'likes').length || 0;
|
likes = votesData.items?.filter((v: { vote_type: string }) => v.vote_type === 'likes').length || 0;
|
||||||
dislikes = votesData.items?.filter((v: any) => v.vote_type === 'dislikes').length || 0;
|
dislikes = votesData.items?.filter((v: { vote_type: string }) => v.vote_type === 'dislikes').length || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let userVote: 'likes' | 'dislikes' | null = null;
|
let userVote: 'likes' | 'dislikes' | null = null;
|
||||||
|
|
|
||||||
|
|
@ -167,8 +167,8 @@ export const POST: APIRoute = async ({ request, cookies }) => {
|
||||||
|
|
||||||
if (votesRes.ok) {
|
if (votesRes.ok) {
|
||||||
const votesData = await votesRes.json();
|
const votesData = await votesRes.json();
|
||||||
likes = votesData.items.filter((v: any) => v.vote_type === 'like').length;
|
likes = votesData.items.filter((v: { vote_type: string }) => v.vote_type === 'like').length;
|
||||||
dislikes = votesData.items.filter((v: any) => v.vote_type === 'dislike').length;
|
dislikes = votesData.items.filter((v: { vote_type: string }) => v.vote_type === 'dislike').length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляем счетчики в посте только если есть токен
|
// Обновляем счетчики в посте только если есть токен
|
||||||
|
|
@ -243,8 +243,8 @@ export const GET: APIRoute = async ({ url, cookies }) => {
|
||||||
|
|
||||||
if (votesCountRes.ok) {
|
if (votesCountRes.ok) {
|
||||||
const votesData = await votesCountRes.json();
|
const votesData = await votesCountRes.json();
|
||||||
likes = votesData.items.filter((v: any) => v.vote_type === 'like').length;
|
likes = votesData.items.filter((v: { vote_type: string }) => v.vote_type === 'like').length;
|
||||||
dislikes = votesData.items.filter((v: any) => v.vote_type === 'dislike').length;
|
dislikes = votesData.items.filter((v: { vote_type: string }) => v.vote_type === 'dislike').length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Определяем голос текущего пользователя
|
// Определяем голос текущего пользователя
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import BlogCard from '@components/blog/BlogCard.astro';
|
||||||
import Pagination from '@components/base/Pagination.astro';
|
import Pagination from '@components/base/Pagination.astro';
|
||||||
import SearchModal from '@components/base/SearchModal.astro';
|
import SearchModal from '@components/base/SearchModal.astro';
|
||||||
import { getPosts, getAllCategories, getPostImageUrl } from '@lib/pb';
|
import { getPosts, getAllCategories, getPostImageUrl } from '@lib/pb';
|
||||||
|
import type { Post } from '@globalInterfaces';
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
|
|
@ -65,7 +66,7 @@ const formatDate = (date: string) => {
|
||||||
<div class="site-container">
|
<div class="site-container">
|
||||||
<div class="blog-grid" id="blog-grid">
|
<div class="blog-grid" id="blog-grid">
|
||||||
{posts.length > 0 ? (
|
{posts.length > 0 ? (
|
||||||
posts.map((post: any) => (
|
posts.map((post: Post) => (
|
||||||
<article class="blog-card-wrapper" data-category={post.category}>
|
<article class="blog-card-wrapper" data-category={post.category}>
|
||||||
<BlogCard
|
<BlogCard
|
||||||
title={post.title}
|
title={post.title}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import BlogCard from '@components/blog/BlogCard.astro';
|
||||||
import Pagination from '@components/base/Pagination.astro';
|
import Pagination from '@components/base/Pagination.astro';
|
||||||
import SearchModal from '@components/base/SearchModal.astro';
|
import SearchModal from '@components/base/SearchModal.astro';
|
||||||
import { getPosts, getAllCategories, getPostImageUrl } from '@lib/pb';
|
import { getPosts, getAllCategories, getPostImageUrl } from '@lib/pb';
|
||||||
|
import type { Post } from '@globalInterfaces';
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
|
|
@ -66,7 +67,7 @@ const formatDate = (date: string) => {
|
||||||
<div class="site-container">
|
<div class="site-container">
|
||||||
<div class="blog-grid" id="blog-grid">
|
<div class="blog-grid" id="blog-grid">
|
||||||
{posts.length > 0 ? (
|
{posts.length > 0 ? (
|
||||||
posts.map((post: any) => (
|
posts.map((post: Post) => (
|
||||||
<article class="blog-card-wrapper" data-category={post.category}>
|
<article class="blog-card-wrapper" data-category={post.category}>
|
||||||
<BlogCard
|
<BlogCard
|
||||||
title={post.title}
|
title={post.title}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import BlogCard from '@components/blog/BlogCard.astro';
|
||||||
import Pagination from '@components/base/Pagination.astro';
|
import Pagination from '@components/base/Pagination.astro';
|
||||||
import SearchModal from '@components/base/SearchModal.astro';
|
import SearchModal from '@components/base/SearchModal.astro';
|
||||||
import { getPosts, getAllCategories, getPostImageUrl } from '@lib/pb';
|
import { getPosts, getAllCategories, getPostImageUrl } from '@lib/pb';
|
||||||
|
import type { Post } from '@globalInterfaces';
|
||||||
|
|
||||||
const POSTS_PER_PAGE = 6;
|
const POSTS_PER_PAGE = 6;
|
||||||
const currentPage = 1;
|
const currentPage = 1;
|
||||||
|
|
@ -56,7 +57,7 @@ const postsCountText = String(total);
|
||||||
<section class="blog-grid-section">
|
<section class="blog-grid-section">
|
||||||
<div class="site-container">
|
<div class="site-container">
|
||||||
<div class="blog-grid" id="blog-grid">
|
<div class="blog-grid" id="blog-grid">
|
||||||
{posts.map((post: any) => (
|
{posts.map((post: Post) => (
|
||||||
<article class="blog-card-wrapper" data-category={post.category}>
|
<article class="blog-card-wrapper" data-category={post.category}>
|
||||||
<BlogCard
|
<BlogCard
|
||||||
title={post.title}
|
title={post.title}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import BlogCard from '@components/blog/BlogCard.astro';
|
||||||
import Pagination from '@components/base/Pagination.astro';
|
import Pagination from '@components/base/Pagination.astro';
|
||||||
import SearchModal from '@components/base/SearchModal.astro';
|
import SearchModal from '@components/base/SearchModal.astro';
|
||||||
import { getPosts, getAllCategories, getPostImageUrl } from '@lib/pb';
|
import { getPosts, getAllCategories, getPostImageUrl } from '@lib/pb';
|
||||||
|
import type { Post } from '@globalInterfaces';
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
|
|
@ -59,7 +60,7 @@ const formatDate = (date: string) => {
|
||||||
<section class="blog-grid-section">
|
<section class="blog-grid-section">
|
||||||
<div class="site-container">
|
<div class="site-container">
|
||||||
<div class="blog-grid" id="blog-grid">
|
<div class="blog-grid" id="blog-grid">
|
||||||
{posts.map((post: any) => (
|
{posts.map((post: Post) => (
|
||||||
<article class="blog-card-wrapper" data-category={post.category}>
|
<article class="blog-card-wrapper" data-category={post.category}>
|
||||||
<BlogCard
|
<BlogCard
|
||||||
title={post.title}
|
title={post.title}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { SITE_URL } from '@constants';
|
||||||
import BlogCard from '@components/blog/BlogCard.astro';
|
import BlogCard from '@components/blog/BlogCard.astro';
|
||||||
import SearchModal from '@components/base/SearchModal.astro';
|
import SearchModal from '@components/base/SearchModal.astro';
|
||||||
import { getPosts, getPostImageUrl } from '@lib/pb';
|
import { getPosts, getPostImageUrl } from '@lib/pb';
|
||||||
|
import type { Post } from '@globalInterfaces';
|
||||||
|
|
||||||
const url = new URL(Astro.request.url);
|
const url = new URL(Astro.request.url);
|
||||||
const searchQuery = url.searchParams.get('q') || '';
|
const searchQuery = url.searchParams.get('q') || '';
|
||||||
|
|
@ -70,7 +71,7 @@ const formatDate = (date: string) => {
|
||||||
<section class="results-section">
|
<section class="results-section">
|
||||||
<div class="site-container">
|
<div class="site-container">
|
||||||
<div class="results-grid">
|
<div class="results-grid">
|
||||||
{searchResults.map((post: any) => (
|
{searchResults.map((post: Post) => (
|
||||||
<BlogCard
|
<BlogCard
|
||||||
title={post.title}
|
title={post.title}
|
||||||
description={post.description}
|
description={post.description}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@ import WhyUs from "@components/home/WhyUs.astro";
|
||||||
import Reviews from "@components/home/Reviews.astro";
|
import Reviews from "@components/home/Reviews.astro";
|
||||||
import Faq from "@components/home/Faq.astro";
|
import Faq from "@components/home/Faq.astro";
|
||||||
import { SITE_URL, EXPERIENCE_YEARS } from '@constants';
|
import { SITE_URL, EXPERIENCE_YEARS } from '@constants';
|
||||||
|
|
||||||
|
const ogImage = `${SITE_URL}/images/home/avtourist-surgut.avif`;
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout
|
<Layout
|
||||||
title="Автоюрист в Сургуте — юридическая помощь автовладельцам"
|
title="Автоюрист в Сургуте — юридическая помощь автовладельцам"
|
||||||
description="Профессиональная юридическая помощь автовладельцам в Сургуте. Споры со страховыми, возврат прав, ДТП, споры с автосалонами."
|
description="Профессиональная юридическая помощь автовладельцам в Сургуте. Споры со страховыми, возврат прав, ДТП, споры с автосалонами."
|
||||||
canonicalLink={SITE_URL}
|
canonicalLink={SITE_URL}
|
||||||
|
ogImage={ogImage}
|
||||||
>
|
>
|
||||||
<Hero
|
<Hero
|
||||||
badgeText="ЗАЩИТА ВОДИТЕЛЕЙ В СУРГУТЕ"
|
badgeText="ЗАЩИТА ВОДИТЕЛЕЙ В СУРГУТЕ"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue