2025-12-11 01:12:45 +03:00
|
|
|
|
import { fetchGraphQL } from './graphql-client.js';
|
|
|
|
|
|
import type { ProfileArticle } from '../types/graphql.js';
|
|
|
|
|
|
|
2025-12-13 23:29:25 +03:00
|
|
|
|
export interface AnewsPost {
|
|
|
|
|
|
title: string;
|
|
|
|
|
|
uri: string;
|
|
|
|
|
|
date: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//последние статьи
|
2025-12-11 01:12:45 +03:00
|
|
|
|
export async function getLatestPosts(first = 12, after = null) {
|
|
|
|
|
|
const query = `
|
|
|
|
|
|
query GetLatestProfileArticles($first: Int!, $after: String) {
|
|
|
|
|
|
profileArticles(
|
|
|
|
|
|
first: $first,
|
|
|
|
|
|
after: $after,
|
|
|
|
|
|
where: {orderby: { field: DATE, order: DESC }}
|
|
|
|
|
|
) {
|
|
|
|
|
|
pageInfo {
|
|
|
|
|
|
hasNextPage
|
|
|
|
|
|
endCursor
|
|
|
|
|
|
}
|
|
|
|
|
|
edges {
|
|
|
|
|
|
cursor
|
|
|
|
|
|
node {
|
|
|
|
|
|
id
|
|
|
|
|
|
databaseId
|
|
|
|
|
|
title
|
|
|
|
|
|
uri
|
|
|
|
|
|
date
|
|
|
|
|
|
featuredImage {
|
|
|
|
|
|
node {
|
|
|
|
|
|
sourceUrl(size: LARGE)
|
|
|
|
|
|
altText
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
author {
|
|
|
|
|
|
node {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
firstName
|
|
|
|
|
|
lastName
|
|
|
|
|
|
avatar {
|
|
|
|
|
|
url
|
|
|
|
|
|
}
|
|
|
|
|
|
uri
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
categories {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
uri
|
|
|
|
|
|
databaseId
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
tags {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
uri
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const data = await fetchGraphQL(query, { first, after });
|
|
|
|
|
|
|
|
|
|
|
|
// Преобразуем edges в nodes
|
|
|
|
|
|
const posts = data.profileArticles?.edges?.map(edge => edge.node) || [];
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
posts,
|
|
|
|
|
|
pageInfo: data.profileArticles?.pageInfo || { hasNextPage: false, endCursor: null }
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-13 23:29:25 +03:00
|
|
|
|
//последние новости
|
2025-12-11 01:12:45 +03:00
|
|
|
|
export async function getLatestAnews(count = 12): Promise<AnewsPost[]> {
|
|
|
|
|
|
const query = `
|
|
|
|
|
|
query GetAnews($count: Int!) {
|
|
|
|
|
|
aNews(first: $count, where: {orderby: {field: DATE, order: DESC}}) {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
title
|
|
|
|
|
|
uri
|
|
|
|
|
|
date
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const data = await fetchGraphQL(query, { count });
|
|
|
|
|
|
return data.aNews?.nodes || []; // Исправлено: aNews вместо anews
|
2025-12-13 23:29:25 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Получить ProfileArticle по databaseId
|
|
|
|
|
|
export async function getProfileArticleById(databaseId) {
|
|
|
|
|
|
const query = `
|
|
|
|
|
|
query GetProfileArticleById($id: ID!) {
|
|
|
|
|
|
profileArticle(id: $id, idType: DATABASE_ID) {
|
|
|
|
|
|
id
|
|
|
|
|
|
databaseId
|
|
|
|
|
|
title
|
|
|
|
|
|
content
|
|
|
|
|
|
excerpt
|
|
|
|
|
|
uri
|
|
|
|
|
|
slug
|
|
|
|
|
|
date
|
|
|
|
|
|
modified
|
|
|
|
|
|
status
|
|
|
|
|
|
featuredImage {
|
|
|
|
|
|
node {
|
|
|
|
|
|
id
|
|
|
|
|
|
sourceUrl
|
|
|
|
|
|
altText
|
|
|
|
|
|
caption
|
|
|
|
|
|
mediaDetails {
|
|
|
|
|
|
width
|
|
|
|
|
|
height
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
author {
|
|
|
|
|
|
node {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
firstName
|
|
|
|
|
|
lastName
|
|
|
|
|
|
avatar {
|
|
|
|
|
|
url
|
|
|
|
|
|
}
|
|
|
|
|
|
description
|
|
|
|
|
|
uri
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
categories {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
uri
|
|
|
|
|
|
description
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
tags {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
uri
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const data = await fetchGraphQL(query, { id: databaseId });
|
|
|
|
|
|
return data?.profileArticle || null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Получить тег по slug
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function getTagBySlug(slug) {
|
|
|
|
|
|
const query = `
|
|
|
|
|
|
query GetTagBySlug($slug: ID!) {
|
|
|
|
|
|
tag(id: $slug, idType: SLUG) {
|
|
|
|
|
|
id
|
|
|
|
|
|
databaseId
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
description
|
|
|
|
|
|
count
|
|
|
|
|
|
seo {
|
|
|
|
|
|
title
|
|
|
|
|
|
metaDesc
|
|
|
|
|
|
canonical
|
|
|
|
|
|
opengraphTitle
|
|
|
|
|
|
opengraphDescription
|
|
|
|
|
|
opengraphImage {
|
|
|
|
|
|
sourceUrl
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const data = await fetchGraphQL(query, { slug });
|
|
|
|
|
|
return data?.tag || null;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Error fetching tag:', error);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Получить посты тега с пагинацией (offset-based)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function getTagPostsPaginated(tagSlug, perPage = 12, page = 1) {
|
|
|
|
|
|
const offset = (page - 1) * perPage;
|
|
|
|
|
|
|
|
|
|
|
|
const query = `
|
|
|
|
|
|
query GetTagPostsPaginated($slug: ID!, $first: Int!, $offset: Int!) {
|
|
|
|
|
|
tag(id: $slug, idType: SLUG) {
|
|
|
|
|
|
id
|
|
|
|
|
|
databaseId
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
description
|
|
|
|
|
|
count
|
|
|
|
|
|
seo {
|
|
|
|
|
|
title
|
|
|
|
|
|
metaDesc
|
|
|
|
|
|
canonical
|
|
|
|
|
|
}
|
|
|
|
|
|
posts(
|
|
|
|
|
|
first: $first
|
|
|
|
|
|
offset: $offset
|
|
|
|
|
|
where: {
|
|
|
|
|
|
orderby: { field: DATE, order: DESC }
|
|
|
|
|
|
}
|
|
|
|
|
|
) {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
id
|
|
|
|
|
|
databaseId
|
|
|
|
|
|
title
|
|
|
|
|
|
excerpt
|
|
|
|
|
|
uri
|
|
|
|
|
|
slug
|
|
|
|
|
|
date
|
|
|
|
|
|
modified
|
|
|
|
|
|
featuredImage {
|
|
|
|
|
|
node {
|
|
|
|
|
|
sourceUrl(size: MEDIUM_LARGE)
|
|
|
|
|
|
altText
|
|
|
|
|
|
caption
|
|
|
|
|
|
mediaDetails {
|
|
|
|
|
|
width
|
|
|
|
|
|
height
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
author {
|
|
|
|
|
|
node {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
firstName
|
|
|
|
|
|
lastName
|
|
|
|
|
|
avatar {
|
|
|
|
|
|
url
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
categories {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
uri
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
tags {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
seo {
|
|
|
|
|
|
title
|
|
|
|
|
|
metaDesc
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
pageInfo {
|
|
|
|
|
|
offsetPagination {
|
|
|
|
|
|
total
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const data = await fetchGraphQL(query, {
|
|
|
|
|
|
slug: tagSlug,
|
|
|
|
|
|
first: perPage,
|
|
|
|
|
|
offset
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const tag = data?.tag;
|
|
|
|
|
|
|
|
|
|
|
|
if (!tag) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
tag: null,
|
|
|
|
|
|
posts: [],
|
|
|
|
|
|
total: 0,
|
|
|
|
|
|
totalPages: 0,
|
|
|
|
|
|
currentPage: page,
|
|
|
|
|
|
hasNext: false,
|
|
|
|
|
|
hasPrevious: false
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const posts = tag.posts?.nodes || [];
|
|
|
|
|
|
const total = tag.posts?.pageInfo?.offsetPagination?.total || 0;
|
|
|
|
|
|
const totalPages = Math.ceil(total / perPage);
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
tag,
|
|
|
|
|
|
posts,
|
|
|
|
|
|
total,
|
|
|
|
|
|
totalPages,
|
|
|
|
|
|
currentPage: page,
|
|
|
|
|
|
hasNext: page < totalPages,
|
|
|
|
|
|
hasPrevious: page > 1,
|
|
|
|
|
|
perPage
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Error fetching tag posts:', error);
|
|
|
|
|
|
return {
|
|
|
|
|
|
tag: null,
|
|
|
|
|
|
posts: [],
|
|
|
|
|
|
total: 0,
|
|
|
|
|
|
totalPages: 0,
|
|
|
|
|
|
currentPage: page,
|
|
|
|
|
|
hasNext: false,
|
|
|
|
|
|
hasPrevious: false
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Получить общее количество постов для всех тегов
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function getAllTagsWithCount(first = 100) {
|
|
|
|
|
|
const query = `
|
|
|
|
|
|
query GetAllTagsWithCount($first: Int!) {
|
|
|
|
|
|
tags(first: $first, where: { hideEmpty: true }) {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
id
|
|
|
|
|
|
databaseId
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
count
|
|
|
|
|
|
posts(first: 1) {
|
|
|
|
|
|
pageInfo {
|
|
|
|
|
|
offsetPagination {
|
|
|
|
|
|
total
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const data = await fetchGraphQL(query, { first });
|
|
|
|
|
|
return data?.tags?.nodes || [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|