slug website
This commit is contained in:
@@ -153,3 +153,142 @@ tbody tr {
|
|||||||
.animate-slide-up {
|
.animate-slide-up {
|
||||||
animation: slideUp 1s ease-out forwards;
|
animation: slideUp 1s ease-out forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pattern-fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.05;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0.05;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes logo-float {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.9);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes logo-glow {
|
||||||
|
0% {
|
||||||
|
filter: drop-shadow(0 0 0 rgba(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
filter: drop-shadow(0 0 10px rgba(0, 0, 0, 0.1));
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
filter: drop-shadow(0 0 0 rgba(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progress-bar {
|
||||||
|
0% {
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in {
|
||||||
|
animation: fade-in 0.8s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-pattern-fade-in {
|
||||||
|
animation: pattern-fade-in 1.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-logo-float {
|
||||||
|
animation: logo-float 0.8s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-logo-glow {
|
||||||
|
animation: logo-glow 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-progress-bar {
|
||||||
|
animation: progress-bar 2.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes circle-pulse {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1.2);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes circle-pulse-delayed {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: scale(0.9);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1.3);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wave {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0) translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateX(-25%) translateY(-10px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-circle-pulse {
|
||||||
|
animation: circle-pulse 3s ease-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-circle-pulse-delayed {
|
||||||
|
animation: circle-pulse-delayed 3s ease-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-wave {
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent,
|
||||||
|
rgba(0, 0, 0, 0.05),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
animation: wave 10s linear infinite;
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,4 +41,5 @@ export interface DanakSuggestService {
|
|||||||
icon: string;
|
icon: string;
|
||||||
deletedAt: string | null;
|
deletedAt: string | null;
|
||||||
category: ServiceCategoryType;
|
category: ServiceCategoryType;
|
||||||
|
slug: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
|||||||
|
|
||||||
import QueryProvider from "@/config/QueryProvider";
|
import QueryProvider from "@/config/QueryProvider";
|
||||||
import ToastContainer from "@/components/Toast";
|
import ToastContainer from "@/components/Toast";
|
||||||
|
// import SplashChatgpt from "@/components/SplashChatgpt";
|
||||||
// import Splash from "@/components/Splash";
|
// import Splash from "@/components/Splash";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
@@ -53,6 +54,7 @@ export default function RootLayout({
|
|||||||
{/* <ToastContainer /> */}
|
{/* <ToastContainer /> */}
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
{/* <Splash /> */}
|
{/* <Splash /> */}
|
||||||
|
{/* <SplashChatgpt /> */}
|
||||||
</QueryProvider>
|
</QueryProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { FC, Fragment } from 'react'
|
import { FC, Fragment } from 'react'
|
||||||
import { useGetDetailService } from './hooks/useProductData'
|
import { useGetProductBySlug } from './hooks/useProductData'
|
||||||
import ServiceHeader from './components/ServiceHeader'
|
import ServiceHeader from './components/ServiceHeader'
|
||||||
import moment from 'moment-jalaali'
|
import moment from 'moment-jalaali'
|
||||||
import ServiceImages from './components/ServiceImages'
|
import ServiceImages from './components/ServiceImages'
|
||||||
@@ -18,7 +18,7 @@ const DetailService: FC<{ id: string }> = ({ id }) => {
|
|||||||
// const { t } = useTranslation('global')
|
// const { t } = useTranslation('global')
|
||||||
// const getAdsLeft = useGetAds(AdsDisplayLocation.SINGLE_SERVICE_PAGE)
|
// const getAdsLeft = useGetAds(AdsDisplayLocation.SINGLE_SERVICE_PAGE)
|
||||||
|
|
||||||
const getDetailService = useGetDetailService(id)
|
const getDetailService = useGetProductBySlug(id)
|
||||||
|
|
||||||
// Show the skeleton loading component while data is loading
|
// Show the skeleton loading component while data is loading
|
||||||
if (getDetailService.isLoading) {
|
if (getDetailService.isLoading) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const Products: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<SwiperSlide key={item.id} className='xl:max-w-[45%] max-w-[90%]'>
|
<SwiperSlide key={item.id} className='xl:max-w-[45%] max-w-[90%]'>
|
||||||
<div className='rounded-4xl overflow-hidden relative'>
|
<div className='rounded-4xl overflow-hidden relative'>
|
||||||
<Link href={`/products/${item.id}`}>
|
<Link href={`/products/${item.slug}`}>
|
||||||
<Image
|
<Image
|
||||||
src={item.coverUrl}
|
src={item.coverUrl}
|
||||||
alt='product'
|
alt='product'
|
||||||
@@ -70,7 +70,7 @@ const Products: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Link href={`/products/${item.id}`}>
|
<Link href={`/products/${item.slug}`}>
|
||||||
<div className='flex gap-1.5 text-sm items-center'>
|
<div className='flex gap-1.5 text-sm items-center'>
|
||||||
<div>جزییات</div>
|
<div>جزییات</div>
|
||||||
<ArrowLeft
|
<ArrowLeft
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Suspense } from 'react'
|
import { Suspense } from 'react'
|
||||||
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
|
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
|
||||||
import { detailServiceQueryOptions } from '../hooks/useProductData'
|
import { getProductBySlugQueryOptions } from '../hooks/useProductData'
|
||||||
import { Metadata, NextPage } from 'next'
|
import { Metadata, NextPage } from 'next'
|
||||||
import DetailService from '../DetailService'
|
import DetailService from '../DetailService'
|
||||||
import Loading from './loading'
|
import Loading from './loading'
|
||||||
@@ -19,8 +19,9 @@ export async function generateMetadata(
|
|||||||
const { id } = await params
|
const { id } = await params
|
||||||
const queryClient = new QueryClient()
|
const queryClient = new QueryClient()
|
||||||
|
|
||||||
await queryClient.prefetchQuery(detailServiceQueryOptions(id))
|
await queryClient.prefetchQuery(getProductBySlugQueryOptions(id))
|
||||||
const service = queryClient.getQueryData(detailServiceQueryOptions(id).queryKey) as ServiceDetailResponse
|
const service = queryClient.getQueryData(getProductBySlugQueryOptions(id).queryKey) as ServiceDetailResponse
|
||||||
|
console.log('service', service);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: 'محصولات داناک' + ' | ' + service.data.danakService.name,
|
title: 'محصولات داناک' + ' | ' + service.data.danakService.name,
|
||||||
@@ -43,7 +44,7 @@ async function SingleServicePage({ id }: { id: string }) {
|
|||||||
const queryClient = new QueryClient()
|
const queryClient = new QueryClient()
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
queryClient.prefetchQuery(detailServiceQueryOptions(id)),
|
queryClient.prefetchQuery(getProductBySlugQueryOptions(id)),
|
||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -51,7 +51,18 @@ export const useGetDetailService = (serviceId: string) =>
|
|||||||
queryFn: () => api.getDetailService(serviceId),
|
queryFn: () => api.getDetailService(serviceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const useGetProductBySlug = (slug: string) =>
|
||||||
|
useQuery({
|
||||||
|
queryKey: ["product-detail", slug],
|
||||||
|
queryFn: () => api.getProductBySlug(slug),
|
||||||
|
});
|
||||||
|
|
||||||
export const detailServiceQueryOptions = (serviceId: string) => ({
|
export const detailServiceQueryOptions = (serviceId: string) => ({
|
||||||
queryKey: ["service-detail", serviceId],
|
queryKey: ["service-detail", serviceId],
|
||||||
queryFn: () => api.getDetailService(serviceId),
|
queryFn: () => api.getDetailService(serviceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const getProductBySlugQueryOptions = (slug: string) => ({
|
||||||
|
queryKey: ["product-detail", slug],
|
||||||
|
queryFn: () => api.getProductBySlug(slug),
|
||||||
|
});
|
||||||
|
|||||||
@@ -45,3 +45,10 @@ export const getSliderProducts = async () => {
|
|||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getProductBySlug = async (slug: string) => {
|
||||||
|
const { data } = await axios.get<ServiceDetailResponse>(
|
||||||
|
`/danak-services/slug/${slug}/public`
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export interface DanakService {
|
|||||||
link: string;
|
link: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
coverUrl: string;
|
coverUrl: string;
|
||||||
|
slug: string;
|
||||||
category: {
|
category: {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ type Props = {
|
|||||||
|
|
||||||
const ServiceItem: FC<Props> = (props) => {
|
const ServiceItem: FC<Props> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Link href={`/products/${props.item.id}`} className='flex-1 bg-white rounded-4xl xl:p-7 p-4 flex flex-col'>
|
<Link href={`/products/${props.item.slug}`} className='flex-1 bg-white rounded-4xl xl:p-7 p-4 flex flex-col'>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<div className='xl:size-[65px] size-[50px] rounded-2xl overflow-hidden'>
|
<div className='xl:size-[65px] size-[50px] rounded-2xl overflow-hidden'>
|
||||||
<Image src={props.item.icon} className='size-full object-cover' alt={props.item.name} width={65} height={65} />
|
<Image src={props.item.icon} className='size-full object-cover' alt={props.item.name} width={65} height={65} />
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
'use client';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
const SplashChatgpt = () => {
|
||||||
|
const [fadeOut, setFadeOut] = useState(false);
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setFadeOut(true);
|
||||||
|
}, 3000);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (fadeOut) {
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
}, [fadeOut]);
|
||||||
|
|
||||||
|
if (!mounted) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`fixed inset-0 bg-white top-0 h-screen flex items-center justify-center z-50 transition-all duration-700 ${fadeOut ? 'opacity-0 pointer-events-none' : 'opacity-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 bg-[url('/images/grid-pattern.svg')] opacity-5 animate-pattern-fade-in" />
|
||||||
|
|
||||||
|
{/* Decorative circles */}
|
||||||
|
<div className="absolute w-[200px] h-[200px] rounded-full bg-primary/5 animate-circle-pulse" />
|
||||||
|
<div className="absolute w-[180px] h-[180px] rounded-full bg-primary/10 animate-circle-pulse-delayed" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className="relative z-10">
|
||||||
|
<div className="w-[140px] h-[140px] relative animate-logo-float animate-logo-glow">
|
||||||
|
<Image
|
||||||
|
src="/images/logo.svg"
|
||||||
|
alt="Danak Logo"
|
||||||
|
fill
|
||||||
|
className="object-contain"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SplashChatgpt;
|
||||||
@@ -58,17 +58,17 @@ const Footer: FC = () => {
|
|||||||
</h6>
|
</h6>
|
||||||
<ul className='xl:text-sm text-xs mt-4 flex flex-col gap-4 text-right'>
|
<ul className='xl:text-sm text-xs mt-4 flex flex-col gap-4 text-right'>
|
||||||
<li>
|
<li>
|
||||||
<Link href='/products/63462bf5-2a34-4466-819f-43d0593e999a'>
|
<Link href='/products/Dmenu'>
|
||||||
دی منو
|
دی منو
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href='/products/e51afdc3-ea0b-49cf-8f49-2a6f131b024e'>
|
<Link href='/products/Dmail'>
|
||||||
دی میل
|
دی میل
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href='/products/d9fd428b-72a5-4ecf-a701-f1c0fb332f63'>
|
<Link href='/products/Dpage'>
|
||||||
دی پیج
|
دی پیج
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
Reference in New Issue
Block a user