diff --git a/frontend/.prettierrc b/frontend/.prettierrc index 401b63c..77d5c64 100644 --- a/frontend/.prettierrc +++ b/frontend/.prettierrc @@ -1,4 +1,6 @@ { - "tabWidth": 4, - "semi": true -} \ No newline at end of file + "singleAttributePerLine": true, + "printWidth": 120, + "tabWidth": 4, + "semi": true +} diff --git a/frontend/app.vue b/frontend/app.vue index 8793e66..b55ac18 100644 --- a/frontend/app.vue +++ b/frontend/app.vue @@ -16,6 +16,7 @@ const closeModal = () => { + -// state +// states const { $gsap: gsap } = useNuxtApp(); -const disableLoadingOverlay = useState("disableLoadingOverlay"); + +const isSiteLoadingDisabled = useCookie("is-site-loading-disabled", { + default: () => false, +}); + const shouldRenderLoadingOverlay = ref(true); +const isAssetLoaded = ref(false); +const criticalLoad = ref(true); + +const progressInterval = ref(null); +const assetLoadingProgress = ref(10); const isWindowScrollLocked = useScrollLock(window); -// lifecycle +// computed -onMounted(async () => { - const timeline = gsap.timeline(); - - timeline.to("#loading-overlay", { - opacity: 1, - }); - - isWindowScrollLocked.value = true; - - const preload = (url: string) => { - return new Promise((resolve) => { - const image = new Image(); - image.src = url; - image.onload = () => resolve(true); - }); +const progressStyle = computed(() => { + return { + width: `${assetLoadingProgress.value}%`, }; +}); - await Promise.all([ - preload("/img/heymlz/heymlz-fast-loading.gif"), - preload("/img/heymlz/heymlz-pulling.gif"), - preload("/img/heymlz/heymlz-falling.gif"), - preload("/img/heymlz/heymlz-seat.gif"), - ]); +// methods - timeline.to("#loading-overlay", { +const onAssetLoaded = () => { + criticalLoad.value = false; + clearInterval(progressInterval.value!); + assetLoadingProgress.value = 100; + isAssetLoaded.value = true; +}; + +const onAssetFinished = () => { + gsap.to("#loading-overlay", { opacity: 0, - delay: 5.5, onComplete: () => { shouldRenderLoadingOverlay.value = false; isWindowScrollLocked.value = false; - disableLoadingOverlay.value = true; + isSiteLoadingDisabled.value = true; }, }); +}; + +// watch + +watch([assetLoadingProgress, criticalLoad], ([assetLoadingProgress, criticalLoad]) => { + if (criticalLoad && assetLoadingProgress >= 100) { + onAssetFinished(); + } +}); + +// lifecycle + +onMounted(() => { + isWindowScrollLocked.value = true; + + if (!isSiteLoadingDisabled.value) { + } + + const heymlzLoadingAnimation = document.querySelector("#heymlz-loading-animation") as HTMLVideoElement; + + if (heymlzLoadingAnimation?.readyState >= HTMLMediaElement.HAVE_ENOUGH_DATA) { + onAssetLoaded(); + } + + progressInterval.value = setInterval(() => { + assetLoadingProgress.value += Math.random() * 10; + }, 250); + + gsap.to("#loading-overlay", { + opacity: 1, + }); }); @@ -51,66 +82,36 @@ onMounted(async () => { id="loading-overlay" class="fixed inset-0 size-full z-9999 flex-center bg-black" > - + + + + + + + - - - diff --git a/frontend/components/global/product-detail/InfoCard.vue b/frontend/components/global/product-detail/InfoCard.vue index 9ed9cf1..1708fa0 100644 --- a/frontend/components/global/product-detail/InfoCard.vue +++ b/frontend/components/global/product-detail/InfoCard.vue @@ -13,7 +13,7 @@ معمولا طی ۲ ساعت اماده میشود - + برسی موجودی در فروشگاه های دیگر diff --git a/frontend/components/global/product-detail/QuantityCounter.vue b/frontend/components/global/product-detail/QuantityCounter.vue index 29a8748..a93a3fd 100644 --- a/frontend/components/global/product-detail/QuantityCounter.vue +++ b/frontend/components/global/product-detail/QuantityCounter.vue @@ -50,7 +50,7 @@ const onInput = (e: any) => { (); - + تعداد {{ maxQuantity }} diff --git a/frontend/components/global/product/StickyCard.vue b/frontend/components/global/product/StickyCard.vue index 153e1d3..61b486c 100644 --- a/frontend/components/global/product/StickyCard.vue +++ b/frontend/components/global/product/StickyCard.vue @@ -31,7 +31,7 @@ const { picture, price, title, color } = toRefs(props); {{ price }} - + افزودن به سبد diff --git a/frontend/components/home/Categories.vue b/frontend/components/home/Categories.vue index 47b6048..ca996a2 100644 --- a/frontend/components/home/Categories.vue +++ b/frontend/components/home/Categories.vue @@ -39,7 +39,7 @@ const onSwiper = (swiper: SwiperClass) => { - // import import { Swiper, SwiperSlide } from "swiper/vue"; @@ -15,12 +14,9 @@ const swiper_instance = ref(null); const observerTarget = ref(null); const shouldPauseVideos = ref(false); -useIntersectionObserver( - observerTarget, - ([entry], observerElement) => { - shouldPauseVideos.value = entry.rootBounds ? !entry.isIntersecting : false; - } -); +useIntersectionObserver(observerTarget, ([entry], observerElement) => { + shouldPauseVideos.value = entry.rootBounds ? !entry.isIntersecting : false; +}); const isMuted = ref(true); const slidesPerView = ref(1); @@ -37,7 +33,7 @@ const onSwiper = (swiper: SwiperClass) => { const updateVideoStates = () => { const activeIndex = swiper_instance.value?.realIndex || 0; const videosElements = document.querySelectorAll(`.slide-video`) as NodeListOf; - videosElements.forEach(videoElement => { + videosElements.forEach((videoElement) => { if (videoElement.id === `slide-video-${activeIndex}` && !shouldPauseVideos.value) { videoElement.play(); } else { @@ -48,58 +44,84 @@ const updateVideoStates = () => { // watch -watch(() => [shouldPauseVideos.value, swiper_instance.value?.realIndex], () => { - updateVideoStates(); -}); +watch( + () => [shouldPauseVideos.value, swiper_instance.value?.realIndex], + () => { + updateVideoStates(); + } +); // lifecycle const initializeGsapAnimation = () => { gsapTimeline - .fromTo(".header-slider-item", { - borderRadius: 0, - height: "100svh" - }, { - height: "80svh", - borderRadius: "20px" - }) - .fromTo(slidesPerView, { - value: 1 - }, { - value: 1.2 - }, "=") - .fromTo(".header-navbar-item", { - filter: "invert(100%)" - }, { - filter: "invert(0%)" - }, "=") - .fromTo("#header-navbar", { - background: "transparent" - }, { - background: "white" - }) - .fromTo("#header-slider-wrapper", { - marginTop: "0px", - scale: 1.025 - }, { - marginTop: () => { - const navbarEl = document.querySelector("#header-navbar") as HTMLDivElement; - return `${navbarEl.clientHeight}px`; + .fromTo( + ".header-slider-item", + { + borderRadius: 0, + height: "100svh", }, - scale: 1 - }, "="); + { + height: "80svh", + borderRadius: "20px", + } + ) + .fromTo( + slidesPerView, + { + value: 1, + }, + { + value: 1.2, + }, + "=" + ) + .fromTo( + ".header-navbar-item", + { + filter: "invert(100%)", + }, + { + filter: "invert(0%)", + }, + "=" + ) + .fromTo( + "#header-navbar", + { + background: "transparent", + }, + { + background: "white", + } + ) + .fromTo( + "#header-slider-wrapper", + { + marginTop: "0px", + scale: 1.025, + }, + { + marginTop: () => { + const navbarEl = document.querySelector("#header-navbar") as HTMLDivElement; + return `${navbarEl.clientHeight}px`; + }, + scale: 1, + }, + "=" + ); }; const resetTimelineForMobile = () => { gsap.to("#header-navbar", { - background: "white" + background: "white", }); gsap.to(".header-navbar-item", { - filter: "invert(0%)" + filter: "invert(0%)", }); gsap.set(".header-slider-item", { borderRadius: "20px", - height: "450px" + height: "450px", }); }; @@ -118,7 +140,7 @@ onMounted(() => { pin: true, start: "top top", // markers: true, - end: "bottom top" + end: "bottom top", }); const calculateOnResize = () => { @@ -141,14 +163,12 @@ onMounted(() => { window.addEventListener("resize", () => { calculateOnResize(); }); - }); onUnmounted(() => { resetTimelineForMobile(); scrollTrigger.disable(); }); - @@ -156,7 +176,10 @@ onUnmounted(() => { id="header-slider-container" class="w-full z-50" > - + { :centered-slides="true" :breakpoints="{ 768: { - spaceBetween : 40 - } + spaceBetween: 40, + }, }" @swiper="onSwiper" > @@ -174,9 +197,7 @@ onUnmounted(() => { :key="slide.id" > - + { :class="swiper_instance?.realIndex !== index ? 'opacity-0' : ''" class="w-full transition-opacity pb-6 xs:pb-10 lg:pb-16 px-6 xs:px-10 lg:px-16 gap-6 xs:gap-10 lg:gap-12 container flex flex-col h-full justify-end relative z-10" > - + class="hover:scale-110 size-[36px] md:size-[42px] lg:size-[50px] relative flex items-center justify-center" + > + class="size-full scale-75 bg-white absolute rounded-full animate-ping" + /> { id="header-slider-pagination-child" class="flex items-center justify-between" > - + + { - + > - + + - - - - \ No newline at end of file + + + diff --git a/frontend/components/home/Preview.vue b/frontend/components/home/Preview.vue index 3af9626..66168b6 100644 --- a/frontend/components/home/Preview.vue +++ b/frontend/components/home/Preview.vue @@ -19,7 +19,7 @@ const heymlzElementIsVisible = useElementVisibility(heymlzElement, { rootMargin: "0px 0px -40% 0px", }); -const showHeymlzAnimation = ref(false); +const showHeymlzAnimation = ref(true); const { x: dragAxisX } = useDraggable(draggableEl, { initialValue: { x: 0, y: 0 }, @@ -92,7 +92,7 @@ watch( :src="homeData!.difreance_section.image1" :class=" showHeymlzAnimation - ? 'brightness-50 blur-md' + ? 'brightness-25 blur-sm' : 'brightness-[95%] blur-[0px]' " class="select-none absolute size-full object-cover transition-[filter] duration-250" @@ -119,7 +119,7 @@ watch( :src="homeData!.difreance_section.image2" :class=" showHeymlzAnimation - ? 'brightness-50 blur-md' + ? 'brightness-25 blur-sm' : 'brightness-[95%] blur-[0px]' " class="overlay-image select-none absolute object-cover size-full transition-[filter] duration-250" diff --git a/frontend/components/product/ChatBox/AiLoading.vue b/frontend/components/product/ChatBox/AiLoading.vue deleted file mode 100644 index 52ab73e..0000000 --- a/frontend/components/product/ChatBox/AiLoading.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/frontend/components/product/ChatBox/ChatBoxContainer.vue b/frontend/components/product/ChatBox/ChatBoxContainer.vue index 3e40912..4c43034 100644 --- a/frontend/components/product/ChatBox/ChatBoxContainer.vue +++ b/frontend/components/product/ChatBox/ChatBoxContainer.vue @@ -1,7 +1,6 @@ - + - + - + @@ -191,12 +196,15 @@ whenever( class="text-black p-6 size-full flex justify-center items-center flex-col" v-else > - + سلام دوست عزیز! - من میتونم هر سوالی رو درمورد این محصول جواب بدم - اگه میخوای شروع کنیم روی دکمه زیر کلیک کن + من میتونم هر سوالی رو درمورد این محصول جواب بدم اگه میخوای شروع کنیم روی دکمه زیر کلیک کن diff --git a/frontend/components/product/ChatBox/ChatButton.vue b/frontend/components/product/ChatBox/ChatButton.vue index 52cc679..547e658 100644 --- a/frontend/components/product/ChatBox/ChatButton.vue +++ b/frontend/components/product/ChatBox/ChatButton.vue @@ -1,7 +1,19 @@ - - - + + + + diff --git a/frontend/components/product/ChatBox/ChatInput.vue b/frontend/components/product/ChatBox/ChatInput.vue index cb9a21f..15e2b9f 100644 --- a/frontend/components/product/ChatBox/ChatInput.vue +++ b/frontend/components/product/ChatBox/ChatInput.vue @@ -1,7 +1,6 @@ @@ -43,9 +54,7 @@ const submitComment = async () => { نظرات کاربران - - بر اساس {{ comments?.count }} نظر - + بر اساس {{ comments?.count }} نظر { > نظر بنویسید - - + + وارد شوید @@ -75,18 +90,30 @@ const submitComment = async () => { + + + نمایش همه + diff --git a/frontend/components/product/ProductHero.vue b/frontend/components/product/ProductHero.vue index e6e28f7..4322663 100644 --- a/frontend/components/product/ProductHero.vue +++ b/frontend/components/product/ProductHero.vue @@ -1,5 +1,4 @@ - {{ product!.category.name }} - {{ product!.name }} + + {{ product!.category.name }} + + {{ product!.name }} + {{ selectedVariant!.price }} - + {{ selectedVariant!.discount > 0 ? selectedVariant!.price : selectedVariant!.price }} - + {{ selectedVariant!.discount }} - درصد تخفیف + تخفیف درصد @@ -103,7 +119,9 @@ watch(() => selectedVariant.value!, (newValue) => { > {{ product!.category.name }} - {{ product!.name }} + + {{ product!.name }} + @@ -114,9 +132,7 @@ watch(() => selectedVariant.value!, (newValue) => { > {{ selectedVariant!.price }} - + {{ selectedVariant!.discount > 0 ? selectedVariant!.price : selectedVariant!.price }} @@ -124,37 +140,42 @@ watch(() => selectedVariant.value!, (newValue) => { v-if="selectedVariant!.discount > 0" class="text-white bg-blue-500 mb-1 px-4 py-2 text-xs rounded-full flex items-center gap-1" > - + {{ selectedVariant!.discount }} درصد تخفیف - - + - - + - - تنوع رنگی : - + تنوع رنگی : @@ -162,7 +183,7 @@ watch(() => selectedVariant.value!, (newValue) => { 0 ? selectedVariantId = variant.id : undefined" + @click="variant.in_stock > 0 ? (selectedVariantId = variant.id) : undefined" v-for="variant in product!.variants.filter(p => p.color === selectedColor)" :key="variant.id" :variantDetail="variant" @@ -171,7 +192,6 @@ watch(() => selectedVariant.value!, (newValue) => { - selectedVariant.value!, (newValue) => { @click="addItemToCart" :loading="isAddCartItemPending" :disabled="isAddCartItemPending" - class="w-full rounded-full" + class="w-full rounded-full max-sm:h-[48px]" end-icon="ci:plus" > - افزودن به سبد خرید - + مشاهده در سبد خرید - + ابتدا وارد شوید @@ -216,13 +243,11 @@ watch(() => selectedVariant.value!, (newValue) => { /> - - diff --git a/frontend/components/product/ProductVariant.vue b/frontend/components/product/ProductVariant.vue index 4f06799..3e16e09 100644 --- a/frontend/components/product/ProductVariant.vue +++ b/frontend/components/product/ProductVariant.vue @@ -34,7 +34,7 @@ defineProps(); - + {{ variantDetail.price }} (); {{ attribute.attribute_type.name }} {{ attribute.value }} diff --git a/frontend/components/product/ProductVideo.vue b/frontend/components/product/ProductVideo.vue index 1570b04..c69ce2b 100644 --- a/frontend/components/product/ProductVideo.vue +++ b/frontend/components/product/ProductVideo.vue @@ -1,10 +1,27 @@ - + - - - + + + + + diff --git a/frontend/package.json b/frontend/package.json index 3a287e1..ff3c169 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -49,13 +49,13 @@ "workbox-window": "^7.3.0" }, "devDependencies": { - "@tailwindcss/postcss": "^4.0.9", + "@tailwindcss/postcss": "^4.1.4", "@types/node": "^22.13.11", "@types/sanitize-html": "^2.13.0", "@types/web-push": "^3.6.4", "autoprefixer": "^10.4.20", "postcss": "^8.5.3", - "tailwindcss": "^4.0.9", + "tailwindcss": "^4.1.4", "typescript": "^5.8.2", "vue-tsc": "^2.2.8" } diff --git a/frontend/pages/index.vue b/frontend/pages/index.vue index f62598c..baa68a0 100644 --- a/frontend/pages/index.vue +++ b/frontend/pages/index.vue @@ -1,14 +1,14 @@ - + @@ -41,8 +40,6 @@ onMounted(() => { /> - - - + - \ No newline at end of file + diff --git a/frontend/pages/product/[id].vue b/frontend/pages/product/[id].vue index 966be85..25934a5 100644 --- a/frontend/pages/product/[id].vue +++ b/frontend/pages/product/[id].vue @@ -1,5 +1,4 @@ - + - + - + diff --git a/frontend/public/img/heymlz/footer-security.gif b/frontend/public/img/heymlz/footer-security.gif index 7ba32e9..2d4e5c1 100644 Binary files a/frontend/public/img/heymlz/footer-security.gif and b/frontend/public/img/heymlz/footer-security.gif differ diff --git a/frontend/public/img/heymlz/footer-send.gif b/frontend/public/img/heymlz/footer-send.gif index fe6119c..b70486d 100644 Binary files a/frontend/public/img/heymlz/footer-send.gif and b/frontend/public/img/heymlz/footer-send.gif differ diff --git a/frontend/public/img/heymlz/footer-share.gif b/frontend/public/img/heymlz/footer-share.gif index 58898f1..dce135b 100644 Binary files a/frontend/public/img/heymlz/footer-share.gif and b/frontend/public/img/heymlz/footer-share.gif differ diff --git a/frontend/public/img/heymlz/footer-support.gif b/frontend/public/img/heymlz/footer-support.gif index 0f1469b..79f38fa 100644 Binary files a/frontend/public/img/heymlz/footer-support.gif and b/frontend/public/img/heymlz/footer-support.gif differ diff --git a/frontend/public/img/heymlz/heymlz-full-body.jpg b/frontend/public/img/heymlz/heymlz-full-body.jpg new file mode 100644 index 0000000..c5b991f Binary files /dev/null and b/frontend/public/img/heymlz/heymlz-full-body.jpg differ diff --git a/frontend/public/img/heymlz/heymlz-small-idle.gif b/frontend/public/img/heymlz/heymlz-small-idle.gif index d5af69c..eb469e5 100644 Binary files a/frontend/public/img/heymlz/heymlz-small-idle.gif and b/frontend/public/img/heymlz/heymlz-small-idle.gif differ diff --git a/frontend/public/img/heymlz/heymlz-text-logo.png b/frontend/public/img/heymlz/heymlz-text-logo.png new file mode 100644 index 0000000..d406e3c Binary files /dev/null and b/frontend/public/img/heymlz/heymlz-text-logo.png differ diff --git a/frontend/public/video/heymlz/heymlz-fast-loading.mp4 b/frontend/public/video/heymlz/heymlz-fast-loading.mp4 new file mode 100644 index 0000000..7f43a1b Binary files /dev/null and b/frontend/public/video/heymlz/heymlz-fast-loading.mp4 differ
+
تعداد {{ maxQuantity }} diff --git a/frontend/components/global/product/StickyCard.vue b/frontend/components/global/product/StickyCard.vue index 153e1d3..61b486c 100644 --- a/frontend/components/global/product/StickyCard.vue +++ b/frontend/components/global/product/StickyCard.vue @@ -31,7 +31,7 @@ const { picture, price, title, color } = toRefs(props); {{ price }}
- من میتونم هر سوالی رو درمورد این محصول جواب بدم - اگه میخوای شروع کنیم روی دکمه زیر کلیک کن + من میتونم هر سوالی رو درمورد این محصول جواب بدم اگه میخوای شروع کنیم روی دکمه زیر کلیک کن