Merge branch 'main' of https://github.com/Byeto-Company/hossein_por_shop
This commit is contained in:
@@ -22,3 +22,4 @@ node_modules
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
/test-results/.last-run.json
|
||||
|
||||
@@ -55,27 +55,27 @@
|
||||
|
||||
/* TYPE PARAGRAPH */
|
||||
@utility typo-p-2xl {
|
||||
@apply text-[24px] leading-[40px] font-light ;
|
||||
@apply text-[24px] leading-[42px] font-light ;
|
||||
}
|
||||
|
||||
@utility typo-p-xl {
|
||||
@apply text-[20px] leading-[32px] font-light ;
|
||||
@apply text-[20px] leading-[34px] font-light ;
|
||||
}
|
||||
|
||||
@utility typo-p-lg {
|
||||
@apply text-[18px] leading-[32px] font-light ;
|
||||
@apply text-[18px] leading-[34px] font-light ;
|
||||
}
|
||||
|
||||
@utility typo-p-md {
|
||||
@apply text-[16px] leading-[28px] font-light ;
|
||||
@apply text-[16px] leading-[30px] font-light ;
|
||||
}
|
||||
|
||||
@utility typo-p-sm {
|
||||
@apply text-[14px] leading-[24px] font-light ;
|
||||
@apply text-[14px] leading-[26px] font-light ;
|
||||
}
|
||||
|
||||
@utility typo-p-xs {
|
||||
@apply text-[12px] leading-[16px] font-light ;
|
||||
@apply text-[12px] leading-[18px] font-light ;
|
||||
}
|
||||
|
||||
/* TYPO LABEL */
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
// import
|
||||
|
||||
import Masonry from "masonry-layout";
|
||||
|
||||
// type
|
||||
|
||||
type Props = {
|
||||
articles: Article[],
|
||||
}
|
||||
|
||||
// props
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const { articles } = toRefs(props);
|
||||
|
||||
// state
|
||||
|
||||
onMounted(() => {
|
||||
new Masonry(".masonry-articles-container", {
|
||||
itemSelector: ".grid-item",
|
||||
columnWidth: ".grid-sizer",
|
||||
percentPosition: true,
|
||||
gutter: ".gutter-sizer"
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="masonry-articles-container w-full">
|
||||
|
||||
<div class="grid-sizer"></div>
|
||||
<div class="gutter-sizer"></div>
|
||||
|
||||
<BlogPost
|
||||
v-for="article in articles"
|
||||
:key="article.id"
|
||||
class="grid-item"
|
||||
:image="article.cover_image"
|
||||
:description="article.summery"
|
||||
:title="article.title"
|
||||
:comments="2"
|
||||
:id="article.id"
|
||||
:date="article.created_at"
|
||||
tag="تگ ندارد"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.grid-sizer,
|
||||
.grid-item {
|
||||
margin-bottom: 24px;
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.gutter-sizer {
|
||||
width: 4%;
|
||||
}
|
||||
</style>
|
||||
@@ -3,12 +3,11 @@
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
id: number;
|
||||
tag: string;
|
||||
date: string;
|
||||
comments: number;
|
||||
title: string;
|
||||
description: string;
|
||||
link: string;
|
||||
variant?: "sm" | "lg";
|
||||
image: string,
|
||||
}
|
||||
@@ -23,96 +22,95 @@ const {} = toRefs(props);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="variant === 'lg' ? 'rounded-150 overflow-hidden' : ''"
|
||||
class="group max-h-[700px] h-[700px] relative"
|
||||
>
|
||||
|
||||
<Tag
|
||||
v-if="variant === 'lg'"
|
||||
class="bg-success-500 absolute left-10 top-10 z-20"
|
||||
>
|
||||
اسپیکر
|
||||
</Tag>
|
||||
|
||||
<NuxtLink :to="`/article/${id}`">
|
||||
<div
|
||||
v-if="variant === 'sm'"
|
||||
class="h-[350px] rounded-150 overflow-hidden relative"
|
||||
:class="variant === 'lg' ? 'h-[600px] rounded-150 overflow-hidden' : 'h-fit'"
|
||||
class="group w-full relative"
|
||||
>
|
||||
|
||||
<Tag
|
||||
class="bg-success-500 absolute z-20 left-6 top-6"
|
||||
v-if="variant === 'lg'"
|
||||
class="bg-success-500 absolute left-10 top-10 z-20"
|
||||
>
|
||||
اسپیکر
|
||||
</Tag>
|
||||
|
||||
<div
|
||||
v-if="variant === 'sm'"
|
||||
class="h-[350px] rounded-150 overflow-hidden relative"
|
||||
>
|
||||
<Tag
|
||||
class="bg-success-500 absolute z-20 left-6 top-6"
|
||||
>
|
||||
اسپیکر
|
||||
</Tag>
|
||||
|
||||
<img
|
||||
:src="image"
|
||||
class="group-hover:scale-105 transition-transform duration-200 absolute size-full object-cover z-10"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:class="variant === 'lg' ? 'absolute px-10' : 'invert mt-8'"
|
||||
class="bottom-10 flex flex-col gap-6 z-20"
|
||||
>
|
||||
|
||||
<div class="flex items-center gap-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon
|
||||
name="ci:comment"
|
||||
size="18"
|
||||
class="**:stroke-white"
|
||||
/>
|
||||
<span class="typo-p-sm text-white">
|
||||
۰ نظر
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon
|
||||
name="ci:calendar"
|
||||
size="18"
|
||||
class="**:stroke-white"
|
||||
/>
|
||||
<span class="typo-p-sm text-white">
|
||||
۳۱ مهر ۱۴۰۳
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 flex-col">
|
||||
<span
|
||||
:class="variant === 'lg' ? 'typo-h-5' : 'typo-h-6'"
|
||||
class="text-white"
|
||||
>
|
||||
{{ title }}
|
||||
</span>
|
||||
<p
|
||||
:class="variant === 'lg' ? 'typo-h-4' : 'typo-h-6 text-slate-500'"
|
||||
class="typo-p-md text-white text-justify"
|
||||
v-html="description"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<NuxtLink :to="`/article/${id}`" class="underline text-white typo-p-md">
|
||||
بیشتر بخوانید...
|
||||
</NuxtLink>
|
||||
|
||||
</div>
|
||||
|
||||
<img
|
||||
v-if="variant === 'lg'"
|
||||
:src="image"
|
||||
class="group-hover:scale-105 transition-transform duration-200 absolute size-full object-cover z-10"
|
||||
alt=""
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="variant === 'lg'"
|
||||
class="w-full h-full bg-linear-to-t from-black to-transparent absolute inset-0 z-15"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:class="variant === 'lg' ? 'absolute px-10' : 'invert mt-8'"
|
||||
class="bottom-10 flex flex-col gap-6 z-20"
|
||||
>
|
||||
|
||||
<div class="flex items-center gap-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon
|
||||
name="ci:comment"
|
||||
size="18"
|
||||
class="**:stroke-white"
|
||||
/>
|
||||
<span class="typo-p-sm text-white">
|
||||
۰ نظر
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon
|
||||
name="ci:calendar"
|
||||
size="18"
|
||||
class="**:stroke-white"
|
||||
/>
|
||||
<span class="typo-p-sm text-white">
|
||||
۳۱ مهر ۱۴۰۳
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 flex-col">
|
||||
<span
|
||||
:class="variant === 'lg' ? 'typo-h-4' : 'typo-h-6'"
|
||||
class="text-white"
|
||||
>
|
||||
برسی آیفون ۱۶ پرومکس
|
||||
</span>
|
||||
<p
|
||||
:class="variant === 'lg' ? 'typo-h-4' : 'typo-h-6 text-slate-500'"
|
||||
class="typo-p-md text-white text-justify"
|
||||
>
|
||||
نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد.
|
||||
نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد.
|
||||
کتابهای زیادی در شصت و سه درصد گذشته.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<span class="underline text-white typo-p-md">
|
||||
بیشتر بخوانید...
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<img
|
||||
v-if="variant === 'lg'"
|
||||
:src="image"
|
||||
class="group-hover:scale-105 transition-transform duration-200 absolute size-full object-cover z-10"
|
||||
alt=""
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="variant === 'lg'"
|
||||
class="w-full h-full bg-linear-to-t from-black to-transparent absolute inset-0 z-15"
|
||||
/>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
@@ -44,9 +44,7 @@ const filters = computed(() => {
|
||||
|
||||
const { data: categories, suspense } = useGetCategories();
|
||||
|
||||
await useAsyncData(async () => {
|
||||
await suspense();
|
||||
});
|
||||
await suspense();
|
||||
|
||||
const { isPending: productsIsPending } = useGetProducts(filters);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import useHomeData from "~/composables/api/home/useHomeData";
|
||||
|
||||
const { data: homeData } = useHomeData();
|
||||
const swiper_instance = ref<SwiperClass | null>(null);
|
||||
const isMuted = ref(true);
|
||||
|
||||
// methods
|
||||
|
||||
@@ -37,18 +38,29 @@ const onChange = (swiper: SwiperClass) => {
|
||||
@slide-change="onChange"
|
||||
>
|
||||
<SwiperSlide
|
||||
v-for="slide in homeData!.sliders"
|
||||
v-for="(slide, index) in homeData!.sliders"
|
||||
:key="slide.id"
|
||||
>
|
||||
<div class="relative w-full rounded-200 h-[80svh] overflow-hidden">
|
||||
<video
|
||||
v-if="!!slide.video"
|
||||
muted
|
||||
autoplay
|
||||
loop
|
||||
class="absolute inset-0 size-full object-cover"
|
||||
:src="slide.video"
|
||||
/>
|
||||
<template v-if="!!slide.video">
|
||||
<button
|
||||
@click="isMuted = !isMuted"
|
||||
class="transition-all hover:invert cursor-pointer flex-center hover:scale-110 size-[50px] border border-white hover:border-transparent rounded-full absolute z-20 top-10 right-20 bg-black"
|
||||
>
|
||||
<Icon
|
||||
:name="isMuted ? 'bi:volume-mute-fill' : 'bi:volume-up-fill'"
|
||||
class="text-white"
|
||||
size="24px"
|
||||
/>
|
||||
</button>
|
||||
<video
|
||||
:muted="swiper_instance?.realIndex !== index ? true : isMuted"
|
||||
autoplay
|
||||
loop
|
||||
class="absolute inset-0 size-full object-cover"
|
||||
:src="slide.video"
|
||||
/>
|
||||
</template>
|
||||
<img
|
||||
v-else
|
||||
class="absolute inset-0 size-full object-cover"
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
// types
|
||||
// state
|
||||
|
||||
type Props = {}
|
||||
import useGetArticles from "~/composables/api/blog/useGetArticles";
|
||||
|
||||
// props
|
||||
const page = ref(1);
|
||||
const { data: articles, suspense } = useGetArticles(page);
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const {} = toRefs(props);
|
||||
// ssr
|
||||
|
||||
await suspense();
|
||||
|
||||
</script>
|
||||
|
||||
@@ -23,49 +25,8 @@ const {} = toRefs(props);
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="flex gap-12">
|
||||
<div class="flex-1 flex flex-col gap-12">
|
||||
<BlogPost
|
||||
image="/img/blog-1.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
/>
|
||||
<BlogPost
|
||||
image="/img/blog-2.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-[0.8] flex flex-col">
|
||||
<BlogPost
|
||||
image="/img/blog-3.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
variant="sm"
|
||||
/>
|
||||
<BlogPost
|
||||
image="/img/blog-4.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
variant="sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<ArticlesList :articles="articles!.results" />
|
||||
</ClientOnly>
|
||||
</section>
|
||||
</template>
|
||||
@@ -3,6 +3,7 @@
|
||||
// import
|
||||
|
||||
import useGetProduct from "~/composables/api/product/useGetProduct";
|
||||
import { sanitize } from "isomorphic-dompurify";
|
||||
|
||||
// state
|
||||
|
||||
@@ -14,6 +15,9 @@ const { data: product } = useGetProduct(id);
|
||||
const quantity = ref(1);
|
||||
|
||||
const selectedSlide = ref(0);
|
||||
|
||||
// computed
|
||||
|
||||
const slides = computed(() => {
|
||||
return [
|
||||
{
|
||||
@@ -30,6 +34,11 @@ const slides = computed(() => {
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
const sanitizedProductDescription = computed(() => {
|
||||
return sanitize(product.value!.description);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -47,16 +56,18 @@ const slides = computed(() => {
|
||||
<Rating />
|
||||
</div>
|
||||
|
||||
<p
|
||||
<div
|
||||
class="py-8 typo-p-md text-slate-500 text-justify [&_a]:text-blue-400 [&_strong]:font-bold [&_u]:text-red-400"
|
||||
v-html="product!.description"
|
||||
v-html="sanitizedProductDescription"
|
||||
/>
|
||||
|
||||
<div class="w-full flex flex-col gap-6 mt-4">
|
||||
|
||||
<RemainQuantity
|
||||
:maxQuantity="product!.in_stock"
|
||||
:quantity="quantity"
|
||||
/>
|
||||
|
||||
<div class="w-full flex gap-3 flex-col">
|
||||
<div class="w-full flex gap-3">
|
||||
<Button class="w-full rounded-full" end-icon="ci:plus">
|
||||
@@ -71,6 +82,7 @@ const slides = computed(() => {
|
||||
همین الان بخر
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<InfoCard />
|
||||
<Share />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// imports
|
||||
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { API_ENDPOINTS, QUERY_KEYS } from "~/constants";
|
||||
|
||||
// types
|
||||
|
||||
export type GetArticleResponse = Article;
|
||||
|
||||
const useGetArticle = (id: number | string | undefined) => {
|
||||
|
||||
// state
|
||||
|
||||
const { $axios: axios } = useNuxtApp();
|
||||
|
||||
// methods
|
||||
|
||||
const handleGetArticle = async () => {
|
||||
const { data } = await axios.get<GetArticleResponse>(`${API_ENDPOINTS.blog.article}/${id}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
return useQuery({
|
||||
queryKey: [QUERY_KEYS.article, id],
|
||||
queryFn: () => handleGetArticle()
|
||||
});
|
||||
};
|
||||
|
||||
export default useGetArticle;
|
||||
@@ -5,11 +5,11 @@ import { API_ENDPOINTS, QUERY_KEYS } from "~/constants";
|
||||
|
||||
// types
|
||||
|
||||
export type GetArticlesResponse = ApiPaginated<UserComment>;
|
||||
export type GetArticlesResponse = ApiPaginated<Article>;
|
||||
|
||||
const useGetArticles = (
|
||||
page: Ref<number>,
|
||||
search: Ref<string>
|
||||
search?: Ref<string>
|
||||
) => {
|
||||
|
||||
// state
|
||||
@@ -23,7 +23,7 @@ const useGetArticles = (
|
||||
params: {
|
||||
offset: (page.value * 10) - 10,
|
||||
limit: 10,
|
||||
search: search.value.length > 0 ? search.value : undefined,
|
||||
search: search ? (search.value.length > 0 ? search.value : undefined) : undefined,
|
||||
}
|
||||
});
|
||||
return data;
|
||||
|
||||
@@ -58,7 +58,8 @@ export default defineNuxtConfig({
|
||||
"@nuxt/icon",
|
||||
"reka-ui/nuxt",
|
||||
"@vueuse/nuxt",
|
||||
"@formkit/auto-animate/nuxt"
|
||||
"@formkit/auto-animate/nuxt",
|
||||
'@nuxt/test-utils/module'
|
||||
],
|
||||
|
||||
runtimeConfig: {
|
||||
|
||||
Generated
+2131
-143
File diff suppressed because it is too large
Load Diff
+11
-1
@@ -7,6 +7,7 @@
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"dev-o": "nuxt dev -- -o",
|
||||
"test": "vitest",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
@@ -25,6 +26,8 @@
|
||||
"axios": "^1.7.9",
|
||||
"fast-average-color": "^9.4.0",
|
||||
"gsap": "^3.12.5",
|
||||
"isomorphic-dompurify": "^2.21.0",
|
||||
"masonry-layout": "^4.2.2",
|
||||
"nuxt": "^3.14.1592",
|
||||
"reka-ui": "^1.0.0-alpha.6",
|
||||
"swiper": "^11.1.15",
|
||||
@@ -35,9 +38,16 @@
|
||||
"vue-skeletor": "^1.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/test-utils": "^3.15.4",
|
||||
"@tailwindcss/postcss": "^4.0.0-beta.5",
|
||||
"@types/masonry-layout": "^4.2.8",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"happy-dom": "^16.8.1",
|
||||
"msw": "^2.7.0",
|
||||
"playwright-core": "^1.50.1",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^4.0.0-beta.5"
|
||||
"tailwindcss": "^4.0.0-beta.5",
|
||||
"vitest": "^3.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
// import
|
||||
|
||||
import { sanitize } from "isomorphic-dompurify";
|
||||
import useGetArticle from "~/composables/api/blog/useGetArticle";
|
||||
|
||||
// state
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const id = route.params.id as string | undefined;
|
||||
|
||||
const { data: article, suspense } = useGetArticle(id);
|
||||
|
||||
// ssr
|
||||
|
||||
const response = await suspense();
|
||||
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Error in categories page prefetch`
|
||||
});
|
||||
}
|
||||
|
||||
// computed
|
||||
|
||||
const sanitizedArticleContent = computed(() => {
|
||||
return sanitize(article.value!.content);
|
||||
});
|
||||
|
||||
const sanitizedArticleSummery = computed(() => {
|
||||
return sanitize(article.value!.summery);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="w-full h-[80svh] rounded-3xl relative overflow-hidden">
|
||||
<img class="absolute object-cover size-full" :alt="article!.title" :src="article!.cover_image" />
|
||||
<div class="absolute bg-linear-to-t from-black/75 to-transparent size-full" />
|
||||
<div class="absolute pl-10 right-10 bottom-10 flex flex-col gap-6">
|
||||
<h1 class="typo-h-4 text-white pl-8">
|
||||
{{ article!.title }}
|
||||
</h1>
|
||||
|
||||
<div
|
||||
class="typo-p-lg text-slate-200 mb-6 text-justify w-[70%]"
|
||||
v-html="sanitizedArticleSummery"
|
||||
/>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="w-fit pr-2 pl-5 h-[50px] rounded-full flex items-center justify-center gap-3 bg-white">
|
||||
<div
|
||||
class="relative flex items-center justify-center rounded-full overflow-hidden size-[35px]">
|
||||
<img
|
||||
class="size-full object-cover absolute"
|
||||
:src="article!.author.profile_photo"
|
||||
alt="article-author"
|
||||
/>
|
||||
</div>
|
||||
<span class="typo-label-sm">
|
||||
{{ article!.author.full_name }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="w-fit pr-4 pl-5 h-[50px] rounded-full flex items-center justify-center gap-2 border-[1.5px] border-white text-white">
|
||||
<span class="typo-label-sm mt-0.5">
|
||||
دسته بندی موبایل
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="w-fit pr-4 pl-5 h-[50px] rounded-full flex items-center justify-center gap-2 border-[1.5px] border-white text-white">
|
||||
<Icon name="ci:calendar" size="24px" class="**:stroke-white" />
|
||||
<span class="typo-label-sm mt-0.5">
|
||||
۲۴ مهر 1403
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="w-fit pr-4 pl-5 h-[50px] rounded-full flex items-center justify-center gap-2 border-[1.5px] border-white text-white">
|
||||
<Icon name="ci:eye-open" size="24px" class="**:stroke-white" />
|
||||
<span class="typo-label-sm mt-0.5">
|
||||
{{ article!.views }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 mt-8">
|
||||
|
||||
<div
|
||||
class="p-8 flex-1 text-zinc-800 flex flex-col gap-6 [&_p,ul]:text-zinc-500 [&_h1]:typo-h-4 [&_h2]:typo-h-5 [&_h3]:typo-h-6 [&_p]:typo-p-md [&_ul]:list-disc [&_ul]:typo-p-md [&_ul]:space-y-2"
|
||||
v-html="sanitizedArticleContent"
|
||||
/>
|
||||
|
||||
<aside class="mt-8 p-8 h-fit bg-slate-100 w-[400px] sticky top-4 rounded-3xl">
|
||||
asdsa
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
+15
-53
@@ -3,6 +3,7 @@
|
||||
// import
|
||||
|
||||
import useGetArticles from "~/composables/api/blog/useGetArticles";
|
||||
import ArticlesList from "~/components/articles/ArticlesList.vue";
|
||||
|
||||
// state
|
||||
|
||||
@@ -14,17 +15,15 @@ const { data: articles, suspense } = useGetArticles(page, debouncedSearch);
|
||||
|
||||
// ssr
|
||||
|
||||
await useAsyncData(async () => {
|
||||
const response = await suspense();
|
||||
|
||||
const response = await suspense();
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Error in categories page prefetch`
|
||||
});
|
||||
}
|
||||
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Error in categories page prefetch`
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -50,50 +49,13 @@ await useAsyncData(async () => {
|
||||
</template>
|
||||
</Input>
|
||||
</div>
|
||||
<div class="flex gap-12">
|
||||
<div class="flex-1 flex flex-col gap-12">
|
||||
<BlogPost
|
||||
image="/img/blog-1.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
/>
|
||||
<BlogPost
|
||||
image="/img/blog-2.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-[0.8] flex flex-col">
|
||||
<BlogPost
|
||||
image="/img/blog-3.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
variant="sm"
|
||||
/>
|
||||
<BlogPost
|
||||
image="/img/blog-4.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
variant="sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- This is for masonry js package -->
|
||||
|
||||
<ClientOnly>
|
||||
<ArticlesList :articles="articles!.results" />
|
||||
</ClientOnly>
|
||||
|
||||
<div class="w-full flex-center pt-24 pb-10">
|
||||
<Pagination :items="[]" :total="100" />
|
||||
</div>
|
||||
|
||||
@@ -31,17 +31,14 @@ const filteredCategories = computed(() => {
|
||||
|
||||
// ssr
|
||||
|
||||
await useAsyncData(async () => {
|
||||
const response = await suspense();
|
||||
|
||||
const response = await suspense();
|
||||
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Error in categories page prefetch`
|
||||
});
|
||||
}
|
||||
});
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Error in categories page prefetch`
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -79,7 +76,7 @@ await useAsyncData(async () => {
|
||||
>
|
||||
<div class="w-full flex items-center justify-between gap-8">
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- <img :src="mainCategory.icon" alt="" class="w-[30px] opacity-50" />-->
|
||||
<!-- <img :src="mainCategory.icon" alt="" class="w-[30px] opacity-50" />-->
|
||||
<span class="typo-h-5">
|
||||
{{ mainCategory.name }}
|
||||
</span>
|
||||
|
||||
@@ -10,16 +10,14 @@ const { suspense } = useHomeData();
|
||||
|
||||
// ssr
|
||||
|
||||
await useAsyncData(async () => {
|
||||
const response = await suspense();
|
||||
const response = await suspense();
|
||||
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Landing error : ${response.error.message}`,
|
||||
})
|
||||
}
|
||||
});
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Landing error : ${response.error.message}`
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -9,22 +9,20 @@ const route = useRoute();
|
||||
const id = route.params.id as string | undefined;
|
||||
const page = ref(1);
|
||||
|
||||
const { suspense : suspenseProduct } = useGetProduct(id);
|
||||
const { suspense : suspenseComments} = useGetComments(id, page);
|
||||
const { suspense: suspenseProduct } = useGetProduct(id);
|
||||
const { suspense: suspenseComments } = useGetComments(id, page);
|
||||
|
||||
// ssr
|
||||
|
||||
await useAsyncData(async () => {
|
||||
const productResponse = await suspenseProduct();
|
||||
const commentsResponse = await suspenseComments();
|
||||
const productResponse = await suspenseProduct();
|
||||
const commentsResponse = await suspenseComments();
|
||||
|
||||
if (productResponse.isError || commentsResponse.isError) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: `error : product ${id} prefetch error`
|
||||
});
|
||||
}
|
||||
});
|
||||
if (productResponse.isError || commentsResponse.isError) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: `error : product ${id} prefetch error`
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ const resetForm = () => {
|
||||
class="flex items-center gap-2 w-full"
|
||||
>
|
||||
<Input
|
||||
data-testid="phone-input"
|
||||
class="w-full"
|
||||
v-model="loginInfo.phone"
|
||||
placeholder="9380123456"
|
||||
@@ -185,6 +186,7 @@ const resetForm = () => {
|
||||
/>
|
||||
|
||||
<Button
|
||||
data-testid="send-otp-code-button"
|
||||
v-if="!showOtp"
|
||||
class="rounded-full w-full mt-4"
|
||||
type="submit"
|
||||
|
||||
Vendored
+19
@@ -43,6 +43,25 @@ declare global {
|
||||
meta_rating: number | null;
|
||||
};
|
||||
|
||||
type Article = {
|
||||
"id": number,
|
||||
"title": string,
|
||||
"slug": string,
|
||||
"content": string,
|
||||
"summery": string,
|
||||
"created_at": string,
|
||||
"updated_at": string,
|
||||
"cover_image": string,
|
||||
"views": number,
|
||||
"meta_description": string,
|
||||
"meta_keywords": string,
|
||||
"author": {
|
||||
"full_name": string,
|
||||
"profile_photo": string
|
||||
},
|
||||
"category": number
|
||||
}
|
||||
|
||||
type UserComment = {
|
||||
"id": number,
|
||||
"content": string,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineVitestConfig } from "@nuxt/test-utils/config";
|
||||
|
||||
export default defineVitestConfig({
|
||||
test: {
|
||||
environment: "nuxt",
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user