merge and add show product to admin pannel

This commit is contained in:
Parsa Nazer
2026-02-22 12:49:05 +03:30
6 changed files with 49 additions and 18 deletions
@@ -27,12 +27,12 @@ const onSwiper = (swiper: SwiperClass) => {
</script> </script>
<template> <template>
<section class="w-full flex flex-col gap-10 md:gap-[4rem] lg:container"> <section class="w-full flex flex-col gap-10 md:gap-16 lg:container">
<div class="w-full flex justify-between items-center max-lg:container"> <div class="w-full flex justify-between items-center max-lg:container">
<span class="text-black typo-h-6 md:typo-h-5 lg:typo-h-4"> <span class="text-black typo-h-6 md:typo-h-5 lg:typo-h-4">
{{ title }} {{ title }}
</span> </span>
<div class="flex-center gap-[.5rem]"> <div class="flex-center gap-2">
<button <button
@click="swiper_instance?.slidePrev()" @click="swiper_instance?.slidePrev()"
:disabled="swiper_instance?.isBeginning" :disabled="swiper_instance?.isBeginning"
@@ -41,7 +41,7 @@ const onSwiper = (swiper: SwiperClass) => {
? 'border-slate-200 cursor-not-allowed' ? 'border-slate-200 cursor-not-allowed'
: 'border-black cursor-pointer' : 'border-black cursor-pointer'
" "
class="size-[30px] md:size-[35px] lg:size-[2.75rem] rounded-full border-[1.5px] click-effect flex-center" class="size-[30px] md:size-[35px] lg:size-11 rounded-full border-[1.5px] click-effect flex-center"
> >
<Icon <Icon
name="ci:chevron-right" name="ci:chevron-right"
@@ -55,7 +55,7 @@ const onSwiper = (swiper: SwiperClass) => {
:class=" :class="
swiper_instance?.isEnd ? 'border-slate-200 cursor-not-allowed' : 'border-black cursor-pointer' swiper_instance?.isEnd ? 'border-slate-200 cursor-not-allowed' : 'border-black cursor-pointer'
" "
class="size-[30px] md:size-[35px] lg:size-[2.75rem] rounded-full border-[1.5px] click-effect flex-center" class="size-[30px] md:size-[35px] lg:size-11 rounded-full border-[1.5px] click-effect flex-center"
> >
<Icon <Icon
name="ci:chevron-left" name="ci:chevron-left"
@@ -108,7 +108,7 @@ const parallaxStyle = computed(() => {
</div> </div>
<div class="flex flex-col gap-1 px-2 items-start w-full text-black mt-4"> <div class="flex flex-col gap-1 px-2 items-start w-full text-black mt-4">
<span class="typo-sub-h-sm font-normal w-full truncate"> <span class="typo-sub-h-sm font-normal w-full line-clamp-2 leading-[175%]">
{{ title }} {{ title }}
</span> </span>
<div class="flex flex-col w-full mt-1"> <div class="flex flex-col w-full mt-1">
@@ -118,7 +118,11 @@ const parallaxStyle = computed(() => {
> >
{{ price }} {{ price }}
</span> </span>
<span v-if="priceAfter" class="whitespace-nowrap !font-bold">{{ priceAfter }}</span> <span
v-if="priceAfter"
class="whitespace-nowrap !font-bold"
>{{ priceAfter }}</span
>
</div> </div>
</div> </div>
</div> </div>
+7 -1
View File
@@ -16,6 +16,12 @@ const activeIndex = ref(0);
const slideElement = ref<HTMLDivElement | null>(null); const slideElement = ref<HTMLDivElement | null>(null);
const { width: slideWidth } = useElementSize(slideElement); const { width: slideWidth } = useElementSize(slideElement);
// computed
const slides = computed(() => {
return [...homeData.value!.main_categories, ...homeData.value!.main_categories];
});
// methods // methods
const onSwiper = (swiper: SwiperClass) => { const onSwiper = (swiper: SwiperClass) => {
@@ -101,7 +107,7 @@ const onSlideChange = (swiper: SwiperClass) => {
> >
<SwiperSlide <SwiperSlide
ref="slideElement" ref="slideElement"
v-for="(slide, index) in homeData!.main_categories" v-for="(slide, index) in slides"
:key="slide.id" :key="slide.id"
> >
<CategoryCard <CategoryCard
+4 -1
View File
@@ -2,7 +2,6 @@
import { useQuery } from "@tanstack/vue-query"; import { useQuery } from "@tanstack/vue-query";
import { API_ENDPOINTS, QUERY_KEYS } from "~/constants"; import { API_ENDPOINTS, QUERY_KEYS } from "~/constants";
import type { GetArticleResponse } from "~/composables/api/blog/useGetArticle";
// types // types
@@ -16,6 +15,10 @@ export type GetHomeDataResponse = {
video: string | null; video: string | null;
}[]; }[];
main_categories: Category[]; main_categories: Category[];
top_seller_products: ProductListItem[];
lot_of_discount_products: ProductListItem[];
most_viewed_products: ProductListItem[];
trends_products: ProductListItem[];
products: ProductListItem[]; products: ProductListItem[];
difreance_section: { difreance_section: {
image1: string; image1: string;
+21 -4
View File
@@ -2,7 +2,6 @@
// import // import
import useHomeData from "~/composables/api/home/useHomeData"; import useHomeData from "~/composables/api/home/useHomeData";
import ProductsGrid from "~/components/global/ProductsGrid.vue";
// state // state
@@ -23,13 +22,31 @@ if (response.isError) {
<template> <template>
<div class="w-full"> <div class="w-full">
<!-- <LoadingOverlay /> --> <!-- <LoadingOverlay /> -->
<Hero class="mb-20 max-md:mt-[80px]" /> <Hero class="mb-20 max-md:mt-20" />
<Preview /> <Preview />
<div class="py-20">
<ProductsSlider
title="محصولات پرتخفیف"
:products="homeData!.lot_of_discount_products"
/>
</div>
<div class="py-20">
<ProductsSlider
title="محصولات محبوب"
:products="homeData!.trends_products"
/>
</div>
<ProductsShowcase class="lg:mb-12" /> <ProductsShowcase class="lg:mb-12" />
<div class="py-[5rem]"> <div class="py-20">
<ProductsSlider <ProductsSlider
title="محصولات پرفروش" title="محصولات پرفروش"
:products="homeData!.products" :products="homeData!.top_seller_products"
/>
</div>
<div class="py-20">
<ProductsSlider
title="محصولات پربازدید"
:products="homeData!.most_viewed_products"
/> />
</div> </div>
<Categories class="mt-12" /> <Categories class="mt-12" />
+4 -3
View File
@@ -33,7 +33,7 @@ const personalData = ref<UpdateAccountRequest>({
first_name: account.value?.first_name ?? "", first_name: account.value?.first_name ?? "",
last_name: account.value?.last_name ?? "", last_name: account.value?.last_name ?? "",
phone: account.value?.phone ?? "", phone: account.value?.phone ?? "",
gender: account.value?.gender ?? undefined, gender: account.value?.gender || undefined,
email: account.value?.email ?? "", email: account.value?.email ?? "",
birth_date: account.value?.birth_date ?? "", birth_date: account.value?.birth_date ?? "",
}); });
@@ -64,7 +64,7 @@ const formRules = computed(() => {
required: helpers.withMessage("فیلد شماره تلفن الزامی می باشد", required), required: helpers.withMessage("فیلد شماره تلفن الزامی می باشد", required),
phoneValidator: helpers.withMessage( phoneValidator: helpers.withMessage(
"شماره تلفن وارد شده معتبر نمی باشد", "شماره تلفن وارد شده معتبر نمی باشد",
helpers.regex(/^0?[1-9][0-9]{9}$/) helpers.regex(/^0?[1-9][0-9]{9}$/),
), ),
}, },
}; };
@@ -99,7 +99,7 @@ const updateData = () => {
}, },
}); });
}, },
} },
); );
}; };
@@ -243,6 +243,7 @@ const handleSubmit = (withValidation: boolean) => {
<Input <Input
v-model="personalData.phone!" v-model="personalData.phone!"
variant="outlined" variant="outlined"
disabled
:error="formValidator$.phone.$error" :error="formValidator$.phone.$error"
/> />
</DataField> </DataField>