redirect url + accordion + blog + ...

This commit is contained in:
hamid zarghami
2025-04-21 12:09:01 +03:30
parent 486bf6a0a7
commit dbee916ad3
26 changed files with 452 additions and 103 deletions
+10 -42
View File
@@ -1,7 +1,8 @@
import InfoBox from '@/components/InfoBox'
import { AddCircle, TickCircle } from 'iconsax-react'
import { TickCircle } from 'iconsax-react'
import { NextPage } from 'next'
import React from 'react'
import Accordion from '@/components/Accordion'
import WhyDanak from '../home/WhyDanak'
const About: NextPage = () => {
@@ -63,19 +64,19 @@ const About: NextPage = () => {
صادرکنندگان نرم افزار
</div>
<div className='flex-1 xl:min-w-[30%] min-w-[90%] bg-[#ECEFF6] h-10 rounded-xl font-bold flex justify-center items-center'>
صادرکنندگان نرم افزار
مشاوره استقرار ISMS
</div>
<div className='flex-1 xl:min-w-[30%] min-w-[90%] bg-[#ECEFF6] h-10 rounded-xl font-bold flex justify-center items-center'>
صادرکنندگان نرم افزار
نظام صنفی
</div>
<div className='flex-1 xl:min-w-[30%] min-w-[90%] bg-[#ECEFF6] h-10 rounded-xl font-bold flex justify-center items-center'>
صادرکنندگان نرم افزار
GDPR
</div>
<div className='flex-1 xl:min-w-[30%] min-w-[90%] bg-[#ECEFF6] h-10 rounded-xl font-bold flex justify-center items-center'>
صادرکنندگان نرم افزار
ISO:27001
</div>
<div className='flex-1 min-w-[30%] bg-[#ECEFF6] h-10 rounded-xl font-bold flex justify-center items-center'>
صادرکنندگان نرم افزار
SSAE18
</div>
</div>
</div>
@@ -93,42 +94,9 @@ const About: NextPage = () => {
</div>
<div className='flex-1 flex flex-col gap-4'>
<div className='bg-white rounded-xl p-3 flex-1 h-fit'>
<div className='flex justify-between'>
<div className='font-bold'>
تعهد
</div>
<AddCircle
size={24}
color='black'
/>
</div>
</div>
<div className='bg-white rounded-xl p-3 flex-1 h-fit'>
<div className='flex justify-between'>
<div className='font-bold'>
تعهد
</div>
<AddCircle
size={24}
color='black'
/>
</div>
</div>
<div className='bg-white rounded-xl p-3 flex-1 h-fit'>
<div className='flex justify-between'>
<div className='font-bold'>
تعهد
</div>
<AddCircle
size={24}
color='black'
/>
</div>
</div>
<Accordion title='تعهد' content='ما در داناک متعهد هستیم که بهترین خدمات و محصولات را با کیفیت بالا به مشتریان خود ارائه دهیم و همواره به دنبال بهبود و ارتقای سطح خدمات خود هستیم.' />
<Accordion title='تعهد' content='ما در داناک متعهد هستیم که بهترین خدمات و محصولات را با کیفیت بالا به مشتریان خود ارائه دهیم و همواره به دنبال بهبود و ارتقای سطح خدمات خود هستیم.' />
<Accordion title='تعهد' content='ما در داناک متعهد هستیم که بهترین خدمات و محصولات را با کیفیت بالا به مشتریان خود ارائه دهیم و همواره به دنبال بهبود و ارتقای سطح خدمات خود هستیم.' />
</div>
</div>
-2
View File
@@ -14,7 +14,6 @@ interface PageProps {
const SingleBlog: NextPage<PageProps> = async ({ params }) => {
const { id } = await params
console.log("loading", id);
return (
<Suspense fallback={<Loading />}>
@@ -24,7 +23,6 @@ const SingleBlog: NextPage<PageProps> = async ({ params }) => {
}
async function BlogContent({ id }: { id: string }) {
console.log(id)
const queryClient = new QueryClient()
await Promise.all([
+112
View File
@@ -0,0 +1,112 @@
import { FC } from 'react'
import Input from '../../../components/Input'
import Textarea from '../../../components/Textarea'
import Button from '../../../components/Button'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { ErrorType } from '../../../helpers/types'
import { toast } from 'react-toastify'
import { useCreateBlogComment } from '../hooks/useBlogsData'
import { CreateBlogCommentType } from '../types/BlogTypes'
import { useSharedStore } from '@/shared/store/sharedStore'
import Link from 'next/link'
import { LOGIN_URL } from '@/config/const'
type Props = {
refetch: () => void
id: string
}
const CreateReview: FC<Props> = ({ refetch, id }) => {
const { isLogin } = useSharedStore()
const createReview = useCreateBlogComment()
const formik = useFormik<CreateBlogCommentType>({
initialValues: {
title: '',
content: ''
},
validationSchema: Yup.object({
title: Yup.string().required('این فیلد الزامی است'),
content: Yup.string().required('این فیلد الزامی است'),
}),
onSubmit: (values) => {
createReview.mutate({ id: id, params: values }, {
onSuccess: () => {
formik.resetForm()
refetch()
},
onError: (error: ErrorType) => {
if (error.response && 'status' in error.response && error.response.status === 401) {
window.location.href = LOGIN_URL + '?redirect=' + window.location.href
} else {
toast.error(error.response?.data?.error?.message?.[0] || 'An error occurred')
}
}
})
},
})
return (
<div className='bg-white p-6 rounded-3xl xl:w-[360px] w-full'>
<div>
ارسال نظر
</div>
{
!isLogin ? (
<div className='mt-6 text-description text-sm'>
<div>
برای ارسال نظر لطفا وارد حساب کاربری خود شوید
</div>
<div className='mt-6 flex justify-center'>
<Link href={LOGIN_URL + '?redirect=' + window.location.href}>
<Button
label={'ورود | ثبت نام'}
className='w-fit px-7'
/>
</Link>
</div>
</div>
) : (
<div className='mt-6 text-description text-sm'>
<div className='mt-6'>
<Input
label={'عنوان'}
placeholder={'عنوان'}
{...formik.getFieldProps('title')}
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
/>
</div>
<div className='mt-6'>
<Textarea
label={'نظر'}
placeholder={'نظر'}
{...formik.getFieldProps('content')}
error_text={formik.touched.content && formik.errors.content ? formik.errors.content : ''}
/>
</div>
<div className='mt-6'>
<Button
label={'ارسال نظر'}
onClick={() => formik.handleSubmit()}
isLoading={createReview.isPending}
/>
</div>
</div>
)
}
</div>
)
}
export default CreateReview
+64 -3
View File
@@ -1,20 +1,21 @@
'use client'
import { Profile, Calendar } from 'iconsax-react'
import { Profile, Calendar, Tag } from 'iconsax-react'
import { FC } from 'react'
import Image from 'next/image'
// import MostVisited from '../components/MostVisited'
import ContactUs from '../components/ContactUs'
import { useGetBlogSingle } from '../hooks/useBlogsData'
import { toJalaliDate } from '@/config/func'
import { timeAgo, toJalaliDate } from '@/config/func'
import MostVisited from './MostVisited'
import { TelegramShareButton, WhatsappShareButton } from 'react-share'
import CreateReview from './CreateReview'
type Props = {
id: string
}
const SingleBlogPage: FC<Props> = ({ id }) => {
const { data } = useGetBlogSingle(id)
const { data, refetch } = useGetBlogSingle(id)
if (!data) return null
@@ -64,6 +65,20 @@ const SingleBlogPage: FC<Props> = ({ id }) => {
</p>
<div className='mt-6 flex items-center gap-2 text-description text-sm'>
<Tag size={20} color='#7D818C' />
<div>برچسب ها :</div>
{
data?.data?.blog?.tags.map((tag) => {
return (
<div key={tag} className='text-xs flex items-center h-6 rounded-md bg-[#EAEDF5] text-[#999999] px-4'>
{tag}
</div>
)
})
}
</div>
<div className='mt-6 flex gap-4 border-y border-[#E5E5E5] py-4'>
<div className='text-sm'>اشتراک گذاری</div>
@@ -84,6 +99,8 @@ const SingleBlogPage: FC<Props> = ({ id }) => {
/>
</TelegramShareButton>
</div>
</div>
<div className='xl:w-[261px]'>
@@ -96,6 +113,50 @@ const SingleBlogPage: FC<Props> = ({ id }) => {
</div>
</div>
</div>
<div className='mt-8 flex xl:flex-row flex-col gap-8 items-start'>
<CreateReview id={data?.data?.blog?.id} refetch={refetch} />
<div className='flex-1 w-full max-w-full bg-white p-6 rounded-3xl'>
<div className='text-sm'>
نظرات کاربران
</div>
{
data?.data?.comments?.length === 0 ?
<div className='flex justify-center items-center h-full mt-7'>
<div className='text-description text-sm'>
هیچ نظری ثبت نشده است
</div>
</div>
:
data?.data?.comments?.map((comment) => {
return (
<div key={comment.id} className='mt-5 border border-border rounded-3xl p-6'>
<div className='flex gap-2 items-center'>
<Image alt='avatar' width={24} height={24} src={comment.user?.profilePic || '/images/default-avatar.png'} className='size-6 rounded-full object-cover' />
<div className='text-description text-xs'>
{comment.user?.firstName + ' ' + comment.user?.lastName}
</div>
</div>
<div className='mt-4 flex justify-between'>
<div className='text-sm max-w-[70%] truncate'>
{comment.title}
</div>
<div className='text-xs text-description'>
{timeAgo(comment.createdAt)}
</div>
</div>
<div className='mt-4 text-[11px] leading-6'>
{comment.content}
</div>
</div>
)
})
}
</div>
</div>
</div>
)
}
+8 -1
View File
@@ -1,5 +1,6 @@
import { useQuery } from "@tanstack/react-query";
import { useQuery, useMutation } from "@tanstack/react-query";
import * as api from "../service/BlogService";
import { CreateBlogCommentType } from "../types/BlogTypes";
// برای client
export const useGetBlogs = (categoryId: string) =>
@@ -38,6 +39,12 @@ export const useGetBlogSingle = (id: string) =>
queryFn: () => api.getBlogSingle(id),
});
export const useCreateBlogComment = () =>
useMutation({
mutationFn: (variables: { id: string; params: CreateBlogCommentType }) =>
api.createBlogComment(variables.id, variables.params),
});
// برای server prefetch
export const blogsQueryOptions = {
queryKey: ["blogs", ""],
+12
View File
@@ -4,6 +4,7 @@ import {
BlogCombinedResponse,
BlogResponse,
BlogSingleResponse,
CreateBlogCommentType,
} from "../types/BlogTypes";
export const getBlogs = async (categoryId: string) => {
@@ -41,3 +42,14 @@ export const getBlogSingle = async (slug: string) => {
const { data } = await axios.get<BlogSingleResponse>(`/blogs/slug/${slug}`);
return data;
};
export const createBlogComment = async (
id: string,
params: CreateBlogCommentType
) => {
const { data } = await axios.post<BlogSingleResponse>(
`/blogs/${id}/comments`,
params
);
return data;
};
+23
View File
@@ -25,6 +25,7 @@ export interface Blog {
author: Author;
category: Category;
content: string;
tags: string[];
}
export interface Pager {
@@ -59,6 +60,28 @@ export type BlogStoreType = {
setSelectedCategory: (category: string) => void;
};
export type Comment = {
id: string;
createdAt: string;
title: string;
content: string;
status: string;
user: User;
};
export type User = {
id: string;
firstName: string;
lastName: string;
profilePic: string;
};
export type BlogSingleResponse = ApiResponse<{
blog: Blog;
comments: Comment[];
}>;
export type CreateBlogCommentType = {
title: string;
content: string;
};
+2 -2
View File
@@ -59,7 +59,7 @@ const Contact: NextPage = () => {
color='black'
/>
<div className='text-sm'>
۰۲۱-۲۱۲۳۲۱۲۳ | ۰۸۶-۲۱۰۰۲۰۰۱
۰۲۱-۹۱۶۹۳۵۳۳
</div>
</div>
@@ -85,7 +85,7 @@ const Contact: NextPage = () => {
color='black'
/>
<div className='text-sm'>
یوسف آباد، فتجی شقایق، پلاک ۵ ، واحد ۱۳
تهران - یوسف آباد - فتحی شقاقی - پلاک ۵ - واحد ۱۳
</div>
</div>
</div>
@@ -12,7 +12,7 @@ const DanakServicesContent: FC = () => {
</h1>
<div className='max-w-[300px] text-sm leading-6'>
ما در داناک با ترکیب خلاقیت، تخصص و تکنولوژیهای روز، راهکارهای نرمافزاری منحصربهفردی ارائه میدهیم که کسبوکار شما را به سطحی بالاتر میبرد.
داناک از ابتدای مسیر انتخاب تا استفاده از راهکارهای نرمافزاری در کنار شما خواهد بود.
</div>
</div>
+1 -1
View File
@@ -12,7 +12,7 @@ const Developers: NextPage = () => {
</h1>
<div className='flex xl:flex-row flex-col xl:gap-24 gap-10 mt-14'>
<div className='flex-[2] font-bold'>
<div className='flex-[2] leading-6 font-bold'>
چه در قالب فرد و یا شرکت ایده منحصر به فرد و خلاقانه ای دارید که نیاز به مشتری و گسترش بازار دارید؟
</div>
<div className='flex-[3] text-sm'>
+40 -33
View File
@@ -110,40 +110,47 @@ const DetailService: FC<{ id: string }> = ({ id }) => {
</div>
{
getDetailService.data?.data.danakService?.reviews?.map((item: ReviewItem) => {
return (
<div key={item.id} className='mt-5 border border-border rounded-3xl p-6'>
<div className='flex gap-2 items-center'>
<Image alt='avatar' width={24} height={24} src={item.user?.profilePic || defaultAvatar} className='size-6 rounded-full object-cover' />
<div className='text-description text-xs'>
{item.user?.firstName + ' ' + item.user?.lastName}
</div>
</div>
<div className='mt-4 flex justify-between'>
<div className='text-sm max-w-[70%] truncate'>
{item.title}
</div>
<div className='text-xs text-description'>
{moment(item.createdAt).fromNow()}
</div>
</div>
<div className='mt-2'>
<Rate
disabled
value={item.rating}
count={5}
style={{ fontSize: 14 }}
/>
</div>
<div className='mt-4 text-[11px] leading-6'>
{item.comment}
</div>
getDetailService.data?.data.danakService?.reviews?.length === 0 ?
<div className='flex justify-center items-center h-full mt-7'>
<div className='text-description text-sm'>
هیچ نظری ثبت نشده است
</div>
)
})
</div>
:
getDetailService.data?.data.danakService?.reviews?.map((item: ReviewItem) => {
return (
<div key={item.id} className='mt-5 border border-border rounded-3xl p-6'>
<div className='flex gap-2 items-center'>
<Image alt='avatar' width={24} height={24} src={item.user?.profilePic || defaultAvatar} className='size-6 rounded-full object-cover' />
<div className='text-description text-xs'>
{item.user?.firstName + ' ' + item.user?.lastName}
</div>
</div>
<div className='mt-4 flex justify-between'>
<div className='text-sm max-w-[70%] truncate'>
{item.title}
</div>
<div className='text-xs text-description'>
{moment(item.createdAt).fromNow()}
</div>
</div>
<div className='mt-2'>
<Rate
disabled
value={item.rating}
count={5}
style={{ fontSize: 14 }}
/>
</div>
<div className='mt-4 text-[11px] leading-6'>
{item.comment}
</div>
</div>
)
})
}
</div>
</div>
@@ -8,7 +8,7 @@ import Link from 'next/link'
import { DanakService } from '../types/ProductTypes'
import RadioGroup from '@/components/RadioGroup'
import Image from 'next/image'
import { CONSOLE_URL } from '@/config/const'
type Props = {
data: DanakService,
}
@@ -25,8 +25,8 @@ const ServiceHeader: FC<Props> = (props: Props) => {
const plan: any = data?.subscriptionPlans.find((item) => item.id === planSelected)
setFinalPrice(plan.finalPrice)
setPrice(plan?.price)
setFinalPrice(plan.price)
setPrice(plan?.originalPrice)
}, [data?.subscriptionPlans, planSelected])
@@ -82,11 +82,11 @@ const ServiceHeader: FC<Props> = (props: Props) => {
{NumberFormat(Number(price))}
</div>
}
<div>{NumberFormat(Number(price))}</div>
<div>{NumberFormat(Number(finalPrice))}</div>
</div>
<span>تومان</span>
</div>
<Link target='_blank' href={`https://console.danakcorp.com/services/detail/${data?.id}`}>
<Link target='_blank' href={`${CONSOLE_URL}/other-service/buy/${data?.id}/${planSelected}`}>
<Button
// onClick={handleBuy}
// isLoading={buyService.isPending}
+47
View File
@@ -0,0 +1,47 @@
'use client'
import { AddCircle } from 'iconsax-react'
import React, { FC, useState, useRef, useEffect } from 'react'
type Props = {
title: string
content: string
}
const Accordion: FC<Props> = ({ title, content }) => {
const [isOpen, setIsOpen] = useState(false)
const contentRef = useRef<HTMLDivElement>(null)
const [contentHeight, setContentHeight] = useState(0)
useEffect(() => {
if (contentRef.current) {
setContentHeight(isOpen ? contentRef.current.scrollHeight : 0)
}
}, [isOpen])
return (
<div className='bg-white rounded-xl p-3 flex-1 h-fit'>
<div className='flex justify-between items-center cursor-pointer accordion-trigger' onClick={() => setIsOpen(!isOpen)}>
<div className='font-bold'>
{title}
</div>
<AddCircle
size={24}
color='black'
className={`transition-transform duration-300 accordion-icon ${isOpen ? 'rotate-45' : 'rotate-0'}`}
/>
</div>
<div
ref={contentRef}
className='overflow-hidden transition-all duration-300 accordion-content'
style={{ maxHeight: `${contentHeight}px` }}
>
<p className='text-sm leading-6 py-2 text-[#7D818C]'>
{content}
</p>
</div>
</div>
)
}
export default Accordion
+2 -2
View File
@@ -33,7 +33,7 @@ const Input: FC<Props> = (props: Props) => {
const [search, setSearch] = useState<string>('')
const inputClass = clx(
'w-full h-10 text-black block px-4 text-xs rounded-xl border border-border',
'w-full h-10 text-black block px-4 text-xs rounded-xl border border-[#D0D0D0]',
props.readOnly && 'bg-gray-100 border-0 text-description',
props.variant === 'search' && 'bg-[#EEF0F7] border-0 ps-10',
props.className
@@ -80,7 +80,7 @@ const Input: FC<Props> = (props: Props) => {
return (
<div className='w-full'>
<label className='text-sm'>
<label className='text-sm text-black'>
{props.label}
</label>
+2 -2
View File
@@ -12,7 +12,7 @@ const Textarea: FC<Props> = (props: Props) => {
<div className='w-full relative'>
{
props.label &&
<div className='text-sm' >
<div className='text-sm text-black' >
{props.label}
</div>
}
@@ -20,7 +20,7 @@ const Textarea: FC<Props> = (props: Props) => {
<textarea
{...props}
className={clx(
'border border-border leading-7 w-full rounded-xl px-4 py-3 min-h-[100px] mt-1 text-xs',
'border border-[#D0D0D0] leading-7 w-full rounded-xl px-4 py-3 min-h-[100px] mt-1 text-xs',
props.className
)}
>
+13 -1
View File
@@ -1,10 +1,22 @@
'use client'
import { QueryClient, QueryClientProvider, HydrationBoundary } from '@tanstack/react-query'
import { IApiErrorRepsonse } from '@/types/error.types'
import { QueryClient, QueryClientProvider, HydrationBoundary, QueryCache } from '@tanstack/react-query'
import { ReactNode, useState } from 'react'
import { removeToken } from './func'
import { useSharedStore } from '@/shared/store/sharedStore'
export default function QueryProvider({ children }: { children: ReactNode }) {
const { setIsLogin } = useSharedStore()
const [client] = useState(() => new QueryClient({
queryCache: new QueryCache({
onError: async (error: IApiErrorRepsonse) => {
if (error.response?.status === 401) {
removeToken()
setIsLogin(false)
}
}
}),
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // داده‌ها برای 5 دقیقه معتبر هستند
+2 -1
View File
@@ -1,10 +1,11 @@
import axios from "axios";
import { getToken } from "./func";
import { BASE_URL } from "./const";
// import { Pages } from "./Pages";
const axiosInstance = axios.create({
// baseURL: process.env.NEXT_PUBLIC_BASE_URL,
baseURL: "https://api.danakcorp.com",
baseURL: BASE_URL,
});
axiosInstance.interceptors.response.use(
+5
View File
@@ -0,0 +1,5 @@
export const BASE_URL = "https://api.danakcorp.com";
export const REFRESH_TOKEN_NAME = "dsc_refresh_token";
export const TOKEN_NAME = "dsc_token";
export const LOGIN_URL = "https://console.danakcorp.com/auth/login";
export const CONSOLE_URL = "https://console.danakcorp.com";
+5 -4
View File
@@ -1,4 +1,5 @@
import Cookie from "js-cookie";
import { TOKEN_NAME, REFRESH_TOKEN_NAME } from "./const";
export function isEmail(input: string): boolean {
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
@@ -31,12 +32,12 @@ export const timeAgo = (date: string | Date): string => {
return "همین الان";
};
export const getToken = () => {
return Cookie.get(process.env.NEXT_PUBLIC_BASE_URL || "");
export const getToken = async () => {
return Cookie.get(TOKEN_NAME);
};
export const getRefreshToken = () => {
return Cookie.get(process.env.VITE_REFRESH_TOKEN_NAME || "");
return Cookie.get(REFRESH_TOKEN_NAME);
};
const isProduction = process.env.NODE_ENV === "production";
@@ -55,7 +56,7 @@ export const setRefreshToken = (refreshToken: string) => {
};
export const removeToken = () => {
Cookie.remove(process.env.NEXT_PUBLIC_TOKEN_NAME || "", {
Cookie.remove(TOKEN_NAME, {
domain,
path: "/",
});
+28 -3
View File
@@ -1,13 +1,34 @@
'use client'
import { FC, Fragment } from 'react'
import { FC, Fragment, useEffect } from 'react'
import Image from 'next/image'
import Link from 'next/link'
import { HambergerMenu, Profile, SearchNormal } from 'iconsax-react'
import Button from '@/components/Button'
import { useSharedStore } from '@/shared/store/sharedStore'
import { getToken } from '@/config/func'
import { useGetMe } from './hooks/useShareData'
import { CONSOLE_URL, LOGIN_URL } from '@/config/const'
const Header: FC = () => {
const { openSidebar, setOpenSidebar } = useSharedStore()
const { setIsLogin, isLogin } = useSharedStore()
const { data } = useGetMe()
useEffect(() => {
handleCheckLogin()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const handleCheckLogin = async () => {
const token = await getToken()
if (!token) {
setIsLogin(false)
} else {
setIsLogin(true)
}
}
return (
<Fragment>
<div className='w-full h-[100px] bg-white/26 rounded-4xl border-[3px] border-white px-10 xl:block hidden'>
@@ -56,12 +77,16 @@ const Header: FC = () => {
/>
</div>
<a href='https://console.danakcorp.com/auth/login' target='_blank' className='border-l-2 whitespace-nowrap border-[#D8DCE4] pl-4 flex gap-2'>
<a href={isLogin ? CONSOLE_URL + '/profile' : `${LOGIN_URL}?redirect=${typeof window !== 'undefined' ? window.location.href : ''}`} target='_blank' className='border-l-2 whitespace-nowrap border-[#D8DCE4] pl-4 flex gap-2'>
<Profile
size={20}
color='black'
/>
<div>ورود | ثبت نام</div>
{isLogin ? (
<div>{`${data?.data?.user?.firstName || ''} ${data?.data?.user?.lastName || ''}`}</div>
) : (
<div>ورود | ثبت نام</div>
)}
</a>
<Link href='/contact'>
+13
View File
@@ -0,0 +1,13 @@
import * as api from "../service/SharedService";
import { useQuery } from "@tanstack/react-query";
import { useSharedStore } from "../store/sharedStore";
export const useGetMe = () => {
const { isLogin } = useSharedStore();
return useQuery({
queryKey: ["me"],
queryFn: api.getMe,
enabled: isLogin,
retry: false,
});
};
+7
View File
@@ -0,0 +1,7 @@
import axios from "@/config/axios";
import { UserMeResponseType } from "../types/SharedTypes";
export const getMe = async () => {
const { data } = await axios.get<UserMeResponseType>("/users/me");
return data;
};
+2
View File
@@ -4,4 +4,6 @@ import { SharedStoreType } from "../types/SharedTypes";
export const useSharedStore = create<SharedStoreType>((set) => ({
openSidebar: false,
setOpenSidebar: (value) => set({ openSidebar: value }),
isLogin: false,
setIsLogin: (value) => set({ isLogin: value }),
}));
+27
View File
@@ -1,6 +1,9 @@
export type SharedStoreType = {
openSidebar: boolean;
setOpenSidebar: (value: boolean) => void;
isLogin: boolean;
setIsLogin: (value: boolean) => void;
};
export interface ApiResponse<T> {
@@ -8,3 +11,27 @@ export interface ApiResponse<T> {
success: boolean;
data: T;
}
export type UserMeResponseType = ApiResponse<{
user: {
id: string;
createdAt: string;
updatedAt: string;
firstName: string;
lastName: string;
email: string;
emailVerified: boolean;
phone: string;
birthDate: string;
nationalCode: string;
financialType: string;
profilePic: string | null;
userName: string | null;
roles: Array<{
id: string;
createdAt: string;
name: string;
// Add other role properties as needed
}>;
};
}>;
+14
View File
@@ -0,0 +1,14 @@
import { DefaultError } from "@tanstack/react-query";
import { IBaseResponse } from "./response.types";
interface IError {
message: string[];
}
export interface IErrorResponse extends IBaseResponse {
error: IError;
}
export interface IApiErrorRepsonse extends DefaultError {
response?: IErrorResponse;
}
+7
View File
@@ -0,0 +1,7 @@
export interface IBaseResponse {
status: number;
success: boolean;
}
export interface IResponse<T> extends IBaseResponse {
data: T;
}