blog
This commit is contained in:
@@ -1,7 +1,14 @@
|
||||
import { clx } from '@/helpers/utils'
|
||||
import { FC } from 'react'
|
||||
|
||||
import { useGetBlogCategories } from '../hooks/useBlogsData'
|
||||
import Image from 'next/image'
|
||||
import { useBlogStore } from '../store/BlogStore'
|
||||
const Category: FC = () => {
|
||||
const { data } = useGetBlogCategories()
|
||||
const { selectedCategory, setSelectedCategory } = useBlogStore()
|
||||
|
||||
console.log(data);
|
||||
|
||||
return (
|
||||
<div className='w-full rounded-4xl bg-white p-4'>
|
||||
<h4>
|
||||
@@ -11,15 +18,28 @@ const Category: FC = () => {
|
||||
<div className='flex flex-col gap-4 mt-7 text-sm'>
|
||||
<div className={clx(
|
||||
'h-10 bg-[#ECEFF6] rounded-xl p-2.5 flex items-center gap-2 ',
|
||||
'bg-black text-white'
|
||||
)}>
|
||||
selectedCategory === "" ? 'bg-black text-white' : ''
|
||||
)} onClick={() => setSelectedCategory("")}>
|
||||
<div className='size-5 bg-white rounded-full'></div>
|
||||
<h6>همه</h6>
|
||||
</div>
|
||||
<div className='h-10 bg-[#ECEFF6] rounded-xl p-2.5 flex items-center gap-2'>
|
||||
<div className='size-5 bg-black rounded-full'></div>
|
||||
<h6>تکنولوژی</h6>
|
||||
{
|
||||
data?.data?.categories?.map((item) => {
|
||||
return (
|
||||
<div key={item.id} className={clx(
|
||||
'h-10 bg-[#ECEFF6] rounded-xl p-2.5 flex items-center gap-2 cursor-pointer',
|
||||
selectedCategory === item.id ? 'bg-black text-white' : ''
|
||||
)} onClick={() => setSelectedCategory(item.id)}>
|
||||
<div className='size-5 rounded-full overflow-hidden'>
|
||||
<Image src={item.iconUrl} alt={item.title} width={20} height={20} />
|
||||
</div>
|
||||
<h6>{item.title}</h6>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import BlogItem from '@/components/BlogItem'
|
||||
import Image from 'next/image'
|
||||
import { FC } from 'react'
|
||||
import { useGetBlogCombined } from '../hooks/useBlogsData'
|
||||
|
||||
const HeroSection: FC = () => {
|
||||
|
||||
const { data } = useGetBlogCombined()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='relative rounded-4xl overflow-hidden'>
|
||||
{
|
||||
data?.data?.pinnedBlogs?.map((item, index: number) => {
|
||||
if (index === 0) {
|
||||
return (
|
||||
<div key={item.id} className='relative rounded-4xl overflow-hidden'>
|
||||
<Image
|
||||
src={'/images/banner.webp'}
|
||||
alt='banner'
|
||||
src={item.imageUrl}
|
||||
alt={item.title}
|
||||
width={1920}
|
||||
height={1080}
|
||||
className='w-full rounded-4xl object-cover xl:mt-14 mt-8 xl:min-h-[210px] min-h-[350px]'
|
||||
@@ -16,19 +24,30 @@ const HeroSection: FC = () => {
|
||||
|
||||
<div className='absolute bottom-0 h-[140px] modalGlass3 w-full p-6 text-white'>
|
||||
<h4 className='font-bold xl:text-base text-sm'>
|
||||
سفارش نرمافزار اختصاصی: سرمایهای برای آینده یا فقط هزینهای برای امروز؟
|
||||
{item.title}
|
||||
</h4>
|
||||
|
||||
<p className="text-xs xl:text-sm mt-4 leading-7 truncate xl:text-clip xl:whitespace-normal">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون بلکه...لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون بلکه...
|
||||
{item.previewContent}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
<div className='flex xl:flex-row flex-col gap-8 mt-10'>
|
||||
<BlogItem />
|
||||
<BlogItem />
|
||||
<BlogItem />
|
||||
{
|
||||
data?.data?.mostVisitedBlogs?.map((item, index: number) => {
|
||||
if (index !== 0) {
|
||||
return (
|
||||
<BlogItem key={item.id} item={item} />
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import Image from 'next/image'
|
||||
import { FC } from 'react'
|
||||
|
||||
const MiniBlogItem: FC = () => {
|
||||
import { Blog } from '../types/BlogTypes'
|
||||
const MiniBlogItem: FC<{ item: Blog }> = ({ item }) => {
|
||||
return (
|
||||
<div className='flex items-center gap-2'>
|
||||
<Image
|
||||
src='https://picsum.photos/seed/picsum/200/300'
|
||||
alt='blog'
|
||||
src={item.imageUrl}
|
||||
alt={item.title}
|
||||
width={72}
|
||||
height={72}
|
||||
className='rounded-xl size-[72px] min-w-[72px] object-cover'
|
||||
/>
|
||||
<h6 className='text-xs leading-5 font-bold'>
|
||||
سفارش نرمافزار اختصاصی: سرمایهای برای آینده یا فقط هزینهای برای امروز؟
|
||||
{item.title}
|
||||
</h6>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { FC } from 'react'
|
||||
import MiniBlogItem from './MiniBlogItem'
|
||||
import { useGetBlogCombined } from '../hooks/useBlogsData'
|
||||
|
||||
const MostVisited: FC = () => {
|
||||
const { data } = useGetBlogCombined()
|
||||
return (
|
||||
<div className='w-full rounded-4xl bg-white p-4'>
|
||||
<h4>
|
||||
@@ -9,9 +11,11 @@ const MostVisited: FC = () => {
|
||||
</h4>
|
||||
|
||||
<div className='flex flex-col gap-6 mt-7 text-sm'>
|
||||
<MiniBlogItem />
|
||||
<MiniBlogItem />
|
||||
<MiniBlogItem />
|
||||
{
|
||||
data?.data?.mostVisitedBlogs?.map((item) => {
|
||||
return <MiniBlogItem key={item.id} item={item} />
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,15 +1,48 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getBlogs } from "../service/BlogService";
|
||||
import * as api from "../service/BlogService";
|
||||
|
||||
// برای client
|
||||
export const useGetBlogs = () =>
|
||||
useQuery({
|
||||
queryKey: ["blogs"],
|
||||
queryFn: getBlogs,
|
||||
queryFn: api.getBlogs,
|
||||
});
|
||||
|
||||
export const useGetBlogsPinned = () =>
|
||||
useQuery({
|
||||
queryKey: ["blogs-pinned"],
|
||||
queryFn: api.getBlogsPinned,
|
||||
});
|
||||
|
||||
export const useGetBlogCategories = () =>
|
||||
useQuery({
|
||||
queryKey: ["blog-categories"],
|
||||
queryFn: api.getBlogCategories,
|
||||
});
|
||||
|
||||
export const useGetBlogCombined = () =>
|
||||
useQuery({
|
||||
queryKey: ["blog-combined"],
|
||||
queryFn: api.getCombinedBlogs,
|
||||
});
|
||||
|
||||
// برای server prefetch
|
||||
export const blogsQueryOptions = {
|
||||
queryKey: ["blogs"],
|
||||
queryFn: getBlogs,
|
||||
queryFn: api.getBlogs,
|
||||
};
|
||||
|
||||
export const blogsPinnedQueryOptions = {
|
||||
queryKey: ["blogs-pinned"],
|
||||
queryFn: api.getBlogsPinned,
|
||||
};
|
||||
|
||||
export const blogCategoriesQueryOptions = {
|
||||
queryKey: ["blog-categories"],
|
||||
queryFn: api.getBlogCategories,
|
||||
};
|
||||
|
||||
export const blogCombinedQueryOptions = {
|
||||
queryKey: ["blog-combined"],
|
||||
queryFn: api.getCombinedBlogs,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { HydrationBoundary, dehydrate } from '@tanstack/react-query'
|
||||
import { QueryClient } from '@tanstack/react-query'
|
||||
import BlogList from './components/BlogList'
|
||||
import { blogsQueryOptions } from './hooks/useBlogsData'
|
||||
import { blogCategoriesQueryOptions, blogsQueryOptions, blogCombinedQueryOptions } from './hooks/useBlogsData'
|
||||
|
||||
export const metadata = {
|
||||
title: 'مجله داناک | مقالات و آموزشها',
|
||||
@@ -9,8 +9,11 @@ export const metadata = {
|
||||
}
|
||||
|
||||
export default async function BlogsPage() {
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
await queryClient.prefetchQuery(blogsQueryOptions)
|
||||
await queryClient.prefetchQuery(blogCategoriesQueryOptions)
|
||||
await queryClient.prefetchQuery(blogCombinedQueryOptions)
|
||||
|
||||
return (
|
||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import axios from "@/config/axios";
|
||||
import { BlogResponse } from "../types/BlogTypes";
|
||||
import {
|
||||
BlogCategoryResponse,
|
||||
BlogCombinedResponse,
|
||||
BlogResponse,
|
||||
} from "../types/BlogTypes";
|
||||
|
||||
export const getBlogs = async () => {
|
||||
const { data } = await axios.get<BlogResponse>(`/blogs`);
|
||||
@@ -7,6 +11,18 @@ export const getBlogs = async () => {
|
||||
};
|
||||
|
||||
export const getBlogCategories = async () => {
|
||||
const { data } = await axios.get<BlogResponse>(`/blogs/categories/public`);
|
||||
const { data } = await axios.get<BlogCategoryResponse>(
|
||||
`/blogs/categories/public`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getBlogsPinned = async () => {
|
||||
const { data } = await axios.get<BlogResponse>(`/blogs/pinned`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getCombinedBlogs = async () => {
|
||||
const { data } = await axios.get<BlogCombinedResponse>(`/blogs/combined`);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { create } from "zustand";
|
||||
import { BlogStoreType } from "../types/BlogTypes";
|
||||
|
||||
export const useBlogStore = create<BlogStoreType>((set) => ({
|
||||
selectedCategory: "",
|
||||
setSelectedCategory(value) {
|
||||
set({ selectedCategory: value });
|
||||
},
|
||||
}));
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ApiResponse } from "@/shared/types/SharedTypes";
|
||||
|
||||
export interface Author {
|
||||
id: string;
|
||||
firstName: string;
|
||||
@@ -33,11 +35,25 @@ export interface Pager {
|
||||
nextPage: boolean;
|
||||
}
|
||||
|
||||
export interface BlogResponse {
|
||||
statusCode: number;
|
||||
success: boolean;
|
||||
data: {
|
||||
export type BlogResponse = ApiResponse<{
|
||||
pager: Pager;
|
||||
blogs: Blog[];
|
||||
}>;
|
||||
|
||||
export type BlogPinnedResponse = ApiResponse<{
|
||||
blogs: Blog[];
|
||||
}>;
|
||||
|
||||
export type BlogCategoryResponse = ApiResponse<{
|
||||
categories: Category[];
|
||||
}>;
|
||||
|
||||
export type BlogCombinedResponse = ApiResponse<{
|
||||
pinnedBlogs: Blog[];
|
||||
mostVisitedBlogs: Blog[];
|
||||
}>;
|
||||
|
||||
export type BlogStoreType = {
|
||||
selectedCategory: string;
|
||||
setSelectedCategory: (category: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,3 +2,9 @@ export type SharedStoreType = {
|
||||
openSidebar: boolean;
|
||||
setOpenSidebar: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export interface ApiResponse<T> {
|
||||
statusCode: number;
|
||||
success: boolean;
|
||||
data: T;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user