chore(apps): remove unused code and packages (DEAD-01, DEAD-02, DEAD-03)
Owner approved the deletions. Found by walking imports from each app's main.ts; sw.ts stays (the PWA plugin's entry). - user-app: 8 unreachable components, loadingStore. - user-app-v2: 11 unreachable components, loadingStore. - both: the unused validators and price helpers, and the gsap and @ant-design/icons-vue packages. - the stray root user-app-v2/ folder (an old package.json and vite.config.ts that nothing referenced). Both apps type-check clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
2d74f53d0a
commit
7bb5c2e00f
@@ -1,19 +0,0 @@
|
||||
<!-- Ported from legacy henza_pwa/src/components/viewAll.vue.
|
||||
Legacy left `to` undeclared and relied on Vue's attribute fallthrough to
|
||||
land it on the root <router-link>; declared explicitly here only because
|
||||
vue-tsc's template type-checking (which legacy's own build never ran)
|
||||
requires router-link's `to` to be a known prop — same runtime value ends
|
||||
up bound either way, no behavior change. -->
|
||||
<template>
|
||||
<router-link :to="to" class="text-primary w-[280px] rounded-[10px] p-[8px] bg-light flex flex-col justify-center items-center">
|
||||
<Icon icon="vuesax-linear:arrow-left-1" class="cursor-pointer" size="25" />
|
||||
مشاهده همه
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
import Icon from './icon/Icon.vue'
|
||||
|
||||
defineProps<{ to: RouteLocationRaw }>()
|
||||
</script>
|
||||
@@ -1,229 +0,0 @@
|
||||
<!--
|
||||
The "حساب" block that sits BELOW the shop content on the home screen.
|
||||
|
||||
Two genuinely different states:
|
||||
- signed out: a quiet invitation, no fabricated numbers.
|
||||
- signed in: the customer's real gram position from GET /customers/:id
|
||||
(goldDebtGrams / fiatDebtRial), labelled بدهکار or بستانکار the same way
|
||||
DashboardView does, since the ledger stores debt as a signed figure.
|
||||
-->
|
||||
<template>
|
||||
<section class="acct">
|
||||
<template v-if="customer">
|
||||
<div class="acct__head">
|
||||
<div>
|
||||
<div class="acct__eyebrow">{{ positionLabel }}</div>
|
||||
<div class="acct__figure">
|
||||
{{ grams }}
|
||||
<span class="acct__unit">گرم</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="acct__seal" :class="isDebtor ? 'is-debtor' : 'is-creditor'">
|
||||
{{ isDebtor ? 'بدهکار' : 'بستانکار' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="acct__row">
|
||||
<span class="acct__rowLabel">بدهی ریالی</span>
|
||||
<span class="acct__rowValue">{{ price(fiat) }} <i>ریال</i></span>
|
||||
</div>
|
||||
|
||||
<div class="acct__actions">
|
||||
<button type="button" class="acct__action" @click="emit('go', 'ordersList')">
|
||||
<Icon icon="vuesax-linear:box" size="18" />
|
||||
سفارشها
|
||||
</button>
|
||||
<button type="button" class="acct__action" @click="emit('go', 'transactionsList')">
|
||||
<Icon icon="vuesax-linear:receipt-text" size="18" />
|
||||
اسناد
|
||||
</button>
|
||||
<button type="button" class="acct__action" @click="emit('go', 'profileData')">
|
||||
<Icon icon="vuesax-linear:user" size="18" />
|
||||
پروفایل
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="acct__invite">
|
||||
<div class="acct__inviteIcon" aria-hidden="true">
|
||||
<Icon icon="vuesax-linear:security-user" size="20" />
|
||||
</div>
|
||||
<div class="acct__inviteCopy">
|
||||
<h3>حساب گرمی شما</h3>
|
||||
<p>برای دیدن موجودی، سفارشها و اسناد خود وارد شوید.</p>
|
||||
</div>
|
||||
<button type="button" class="acct__inviteCta" @click="emit('login')">ورود / ثبتنام</button>
|
||||
</div>
|
||||
</template>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import Icon from '../icon/Icon.vue'
|
||||
import { price } from '../../utils/filters'
|
||||
import type { CustomerDetail } from '../../api/client'
|
||||
|
||||
const props = defineProps<{ customer: CustomerDetail | null }>()
|
||||
const emit = defineEmits<{ (e: 'go', route: string): void; (e: 'login'): void }>()
|
||||
|
||||
const isDebtor = computed(() => (props.customer ? parseFloat(props.customer.goldDebtGrams) > 0 : false))
|
||||
const positionLabel = computed(() => (isDebtor.value ? 'مانده بدهی گرمی' : 'مانده بستانکاری گرمی'))
|
||||
const grams = computed(() => (props.customer ? Math.abs(parseFloat(props.customer.goldDebtGrams)) : 0))
|
||||
const fiat = computed(() => (props.customer ? props.customer.fiatDebtRial.split('.')[0] : '0'))
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.acct {
|
||||
border-radius: 20px;
|
||||
padding: 18px;
|
||||
background: @primary;
|
||||
color: #fff;
|
||||
box-shadow: 0 22px 44px -32px rgba(20, 22, 26, 0.9);
|
||||
}
|
||||
|
||||
.acct__head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.acct__eyebrow {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
|
||||
.acct__figure {
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.acct__unit {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
padding-inline-start: 3px;
|
||||
}
|
||||
|
||||
.acct__seal {
|
||||
font-size: 10.5px;
|
||||
font-weight: 700;
|
||||
padding: 5px 11px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
.acct__seal.is-debtor {
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
}
|
||||
.acct__seal.is-creditor {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.13);
|
||||
}
|
||||
|
||||
.acct__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 16px;
|
||||
padding-top: 13px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.acct__rowLabel {
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
.acct__rowValue {
|
||||
font-weight: 700;
|
||||
}
|
||||
.acct__rowValue i {
|
||||
font-style: normal;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.acct__actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.acct__action {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 11px 4px;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
border-radius: 13px;
|
||||
font-family: inherit;
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.88);
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
transition: background 0.18s ease, transform 0.18s ease;
|
||||
}
|
||||
.acct__action:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.acct__invite {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 13px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.acct__inviteIcon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 13px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.acct__inviteCopy {
|
||||
flex: 1 1 150px;
|
||||
}
|
||||
.acct__inviteCopy h3 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
.acct__inviteCopy p {
|
||||
margin: 5px 0 0;
|
||||
font-size: 11.5px;
|
||||
line-height: 1.8;
|
||||
color: rgba(255, 255, 255, 0.58);
|
||||
}
|
||||
|
||||
.acct__inviteCta {
|
||||
flex: 1 1 100%;
|
||||
margin-top: 4px;
|
||||
padding: 12px;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
border-radius: 13px;
|
||||
background: #fff;
|
||||
color: @ink;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
transition: transform 0.18s ease;
|
||||
}
|
||||
.acct__inviteCta:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
</style>
|
||||
@@ -1,45 +0,0 @@
|
||||
<!--
|
||||
Ported from legacy henza_pwa/src/components/product/category.vue.
|
||||
Not yet wired to real data: the new API has no categories-with-icon endpoint
|
||||
(Prisma has a Category model, but no controller/route exposes it, and there
|
||||
are no per-category icon images). HomeView passes an empty list for now, so
|
||||
this renders nothing rather than fabricated placeholder tiles — see the
|
||||
"existing issue found" note in the report about the legacy `id === 45`
|
||||
special-case, preserved here for whenever real category data exists.
|
||||
-->
|
||||
<template>
|
||||
<router-link :to="{ name: 'search' }" @click="setCats" class="cursor-pointer h-full block">
|
||||
<div
|
||||
class="h-full w-full text-center flex flex-col items-center justify-between rounded-[4px]"
|
||||
:class="value.id === '45' ? 'bg-primary' : 'border border-1 border-secondary'"
|
||||
>
|
||||
<div class="flex justify-center mt-4 shrink-0">
|
||||
<img v-lazy="value.icon?.image" alt="category" class="w-[32px] h-[32px] text-center" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mb-4 mt-4 py-1 px-1 font-semibold flex-1 flex items-center justify-center text-center min-h-[40px]"
|
||||
:class="value.id === '45' ? 'text-white' : 'text-primary'"
|
||||
>
|
||||
{{ value.title }}
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFiltersStore } from '../../stores/filtersStore'
|
||||
|
||||
interface CategoryValue {
|
||||
id: string
|
||||
title: string
|
||||
icon?: { image: string }
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{ value: CategoryValue }>(), {})
|
||||
const filters = useFiltersStore()
|
||||
|
||||
const setCats = () => {
|
||||
filters.query.cats = [props.value.id]
|
||||
}
|
||||
</script>
|
||||
@@ -1,146 +0,0 @@
|
||||
<!--
|
||||
Ported from legacy henza_pwa/src/components/slider/homeSlider.vue.
|
||||
`$prefix` (legacy global property, always the compile-time-constant 'montzar'
|
||||
from themeConfig) is inlined directly here rather than re-registered as a
|
||||
global — same resolved value, no behavior change.
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<Swiper
|
||||
:grabCursor="true"
|
||||
:centeredSlides="true"
|
||||
:space-between="5"
|
||||
:loop="true"
|
||||
:slidesPerView="1.3"
|
||||
class="w-full home-slider"
|
||||
:autoplay="data.autoplay"
|
||||
:pagination="{ clickable: true }"
|
||||
>
|
||||
<SwiperSlide class="bg-center bg-cover h-auto" v-for="(item, index) in slides" :key="index">
|
||||
<img @click="clickHandler(item)" v-lazy="item.src" width="100%" class="block w-full rounded-[10px] object-cover" :style="`height:${height}`" />
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
<Drawer
|
||||
v-model:visible="data.show"
|
||||
placement="bottom"
|
||||
:closable="false"
|
||||
@close="setAutoPlay"
|
||||
height="60%"
|
||||
class="montzar-rounded-drawer rtl text-white bg-primaryLighten whitespace-pre-wrap text[13px]"
|
||||
>
|
||||
{{ data.slideContent.text }}
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue'
|
||||
import 'swiper/css'
|
||||
import 'swiper/css/navigation'
|
||||
import 'swiper/css/pagination'
|
||||
import 'swiper/css/scrollbar'
|
||||
import SwiperCore, { Autoplay, Navigation, Pagination } from 'swiper'
|
||||
SwiperCore.use([Pagination, Navigation, Autoplay])
|
||||
import { reactive } from 'vue'
|
||||
import { Drawer } from 'ant-design-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useFiltersStore } from '../../stores/filtersStore'
|
||||
|
||||
interface Slide {
|
||||
text: string | undefined
|
||||
type: string | undefined
|
||||
src: string | undefined
|
||||
link: string | undefined
|
||||
}
|
||||
|
||||
interface Data {
|
||||
show: boolean
|
||||
slideContent: Slide
|
||||
autoplay: { delay: number; disableOnInteraction: boolean } | boolean
|
||||
}
|
||||
|
||||
withDefaults(defineProps<{ slides: Slide[]; height: string }>(), {})
|
||||
|
||||
const data: Data = reactive({
|
||||
show: false,
|
||||
slideContent: { text: undefined, type: undefined, src: undefined, link: undefined },
|
||||
autoplay: { delay: 4000, disableOnInteraction: false },
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const filters = useFiltersStore()
|
||||
|
||||
const clickHandler = async (item: Slide) => {
|
||||
if (item) {
|
||||
data.slideContent = item
|
||||
switch (item.type) {
|
||||
case 'popup':
|
||||
if (item && !(!item.text || item.text.length === 0)) data.show = true
|
||||
break
|
||||
case 'link':
|
||||
if (item && !(!item.link || item.link.length === 0)) window.open(item.link)?.focus()
|
||||
break
|
||||
case 'discount':
|
||||
if (!(!item.link || item.link.length === 0)) {
|
||||
// EXISTING ISSUE, ported as-is: legacy sets `query.value.discount`,
|
||||
// but the actual filter field is `has_discount` — this assignment
|
||||
// never affected search filtering in legacy either. Not "fixed"
|
||||
// here; flagged separately.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
;(filters.query as any).discount = true
|
||||
await router.push({ name: 'search' })
|
||||
}
|
||||
break
|
||||
default:
|
||||
// code block
|
||||
}
|
||||
data.autoplay = false
|
||||
}
|
||||
}
|
||||
const setAutoPlay = async () => {
|
||||
data.autoplay = { delay: 4000, disableOnInteraction: false }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.home-slider {
|
||||
&.swiper {
|
||||
padding: 24px 0 24px 0;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullet {
|
||||
transition: all 0.2s;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullet-active,
|
||||
.swiper-pagination-bullet-active-main {
|
||||
width: 24px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.swiper-horizontal > .swiper-pagination-bullets,
|
||||
.swiper-pagination-bullets.swiper-pagination-horizontal,
|
||||
.swiper-pagination-custom,
|
||||
.swiper-pagination-fraction {
|
||||
bottom: 0 !important;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullets-dynamic * {
|
||||
transform: unset !important;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullet-active {
|
||||
background: @primary;
|
||||
}
|
||||
|
||||
.swiper-slide {
|
||||
transition: all 0.6s;
|
||||
}
|
||||
|
||||
.swiper-slide:not(.swiper-slide-active) {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,139 +0,0 @@
|
||||
<!--
|
||||
Ported from legacy henza_pwa/src/components/slider/slider.vue.
|
||||
EXISTING ISSUE, ported as-is (not fixed): `showPopup` takes (src, index) but
|
||||
the template calls it with a single argument (`showPopup(item)`), and the
|
||||
v-for doesn't even destructure an index. `index` is therefore always
|
||||
undefined inside showPopup, so its "rotate the slides array to start at the
|
||||
tapped image" logic never runs — tapping any thumbnail always opens the
|
||||
full-screen zoom viewer starting from the first image, never the tapped one.
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<Swiper :grabCursor="true" :centeredSlides="true" :loop="false" :slidesPerView="1" class="w-full slider" :pagination="{ clickable: true }">
|
||||
<SwiperSlide class="bg-center bg-cover h-auto" v-for="item in slides" :key="item">
|
||||
<img @click="showPopup(item)" v-lazy="item" class="block w-auto object-cover z-0 pb-1" :style="`height:${height}`" />
|
||||
</SwiperSlide>
|
||||
<transition name="slideUp" mode="out-in">
|
||||
<svg alt="منتزر" class="absolute bottom-0 z-[2] m-auto pointer-events-none w-full h-[90px]" v-if="flagSvg">
|
||||
<path fill="#FFFFFF" class="w-full h-[90px]" :d="whiteShape" />
|
||||
<path fill="#3E5279" class="w-full h-[90px]" :d="curvedShape" />
|
||||
</svg>
|
||||
</transition>
|
||||
</Swiper>
|
||||
|
||||
<Drawer v-model:visible="show" placement="bottom" :closable="false" :bodyStyle="{ padding: 0 }" class="rtl whitespace-pre-wrap bg-white" height="100vh">
|
||||
<div @click="show = false" class="fixed top-[30px] right-[20px] z-[5] pb-2 pl-5">
|
||||
<Icon icon="vuesax-linear:arrow-right-1" @click="show = false" class="cursor-pointer text-white bg-secondary rounded-[5px] p-[2.5px]" size="28" />
|
||||
</div>
|
||||
|
||||
<Swiper :grabCursor="true" :zoom="{ maxRatio: 1.2 }" :modules="[Zoom]" :centeredSlides="true" :loop="false" :slidesPerView="1" class="w-full slider" :key="show">
|
||||
<SwiperSlide class="bg-center bg-cover h-auto" v-for="item in slides" :key="item">
|
||||
<div class="swiper-zoom-container">
|
||||
<img v-lazy="item" class="block object-contain w-[100vw] h-screen" />
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeMount, ref } from 'vue'
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue'
|
||||
import 'swiper/css'
|
||||
import 'swiper/css/navigation'
|
||||
import 'swiper/css/pagination'
|
||||
import 'swiper/css/scrollbar'
|
||||
import 'swiper/css/zoom'
|
||||
import SwiperCore, { Navigation, Pagination, Zoom } from 'swiper'
|
||||
import { Drawer } from 'ant-design-vue'
|
||||
import Icon from '../icon/Icon.vue'
|
||||
import { useWindowDataStore } from '../../stores/windowDataStore'
|
||||
|
||||
SwiperCore.use([Pagination, Navigation])
|
||||
|
||||
const props = withDefaults(defineProps<{ slides: string[]; height: string; width?: string }>(), {})
|
||||
|
||||
const show = ref(false)
|
||||
const slides = ref<string[]>([...props.slides])
|
||||
|
||||
const showPopup = (src: string, index?: number) => {
|
||||
slides.value = [...props.slides]
|
||||
for (let i = 0; i < (index ?? 0); i++) {
|
||||
const helper = slides.value.shift()
|
||||
if (helper) slides.value.push(helper)
|
||||
}
|
||||
show.value = true
|
||||
}
|
||||
|
||||
const flagSvg = ref(false)
|
||||
onBeforeMount(() => {
|
||||
setTimeout(() => {
|
||||
flagSvg.value = true
|
||||
}, 100)
|
||||
})
|
||||
|
||||
const windowData = useWindowDataStore()
|
||||
const shapeHeight = 90
|
||||
const wcp1x = computed(() => Math.min(windowData.width, 768) - Math.min(windowData.width, 768) / 21.8878249)
|
||||
const wcp2x = computed(() => Math.min(windowData.width, 768) - Math.min(windowData.width, 768) / 2.37406336)
|
||||
const crcr1cp1x = computed(() => Math.min(windowData.width, 768) - Math.min(windowData.width, 768) / 1.56947374)
|
||||
const crcr1cp2x = computed(() => Math.min(windowData.width, 768) - Math.min(windowData.width, 768) / 5.47609844)
|
||||
const crcr2cp1x = computed(() => Math.min(windowData.width, 768) - Math.min(windowData.width, 768) / 15.8024691)
|
||||
const crcr2cp2x = computed(() => Math.min(windowData.width, 768) - Math.min(windowData.width, 768) / 2.2606383)
|
||||
const whiteShape = computed(() => `M0,${shapeHeight}H${Math.min(windowData.width, 768)}V0C${wcp1x.value},19.17,${wcp2x.value},119.08,0,49Z`)
|
||||
const curvedShape = computed(
|
||||
() => `M0,39.77C${crcr1cp1x.value},91.74,${crcr1cp2x.value},61.5,${Math.min(windowData.width, 768)},0,C${crcr2cp1x.value},29.34,${crcr2cp2x.value},112.54,0,57.1Z`,
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.slider {
|
||||
.swiper-pagination-bullet {
|
||||
transition: all 0.2s;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullet-active,
|
||||
.swiper-pagination-bullet-active-main {
|
||||
width: 24px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.swiper-horizontal > .swiper-pagination-bullets,
|
||||
.swiper-pagination-bullets.swiper-pagination-horizontal,
|
||||
.swiper-pagination-custom,
|
||||
.swiper-pagination-fraction {
|
||||
bottom: 40px !important;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullets-dynamic * {
|
||||
transform: unset !important;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullet-active {
|
||||
background: @primary;
|
||||
}
|
||||
|
||||
.swiper-slide img {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.slideUp-enter-active,
|
||||
.slideUp-leave-active {
|
||||
opacity: 1;
|
||||
transition: all @transition-duration ease @transition-delay;
|
||||
}
|
||||
|
||||
.slideUp-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.slideUp-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +0,0 @@
|
||||
<!-- Ported from legacy henza_pwa/src/components/content.vue -->
|
||||
<template>
|
||||
<main :style="{ marginTop: transition.transitionEnded ? `${transition.headerHeight}px` : 'unset' }">
|
||||
<slot />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTransitionStore } from '../../stores/transitionStore'
|
||||
|
||||
const transition = useTransitionStore()
|
||||
</script>
|
||||
@@ -1,41 +0,0 @@
|
||||
<!-- Ported from legacy henza_pwa/src/components/ui/ScrollFetch.vue — the plain
|
||||
infinite-scroll container (default ant Empty, no custom illustration). -->
|
||||
<template>
|
||||
<main
|
||||
class="overflow-auto no-scrollbar w-[100vw] h-screen max-w-screen-md m-auto"
|
||||
:style="{ paddingTop: padding || transition.headerHeight + 'px' }"
|
||||
ref="scrollArea"
|
||||
>
|
||||
<slot></slot>
|
||||
<Spinner cls="my-[10px]" :spinning="loading" :indicator="true" tip="درحال دریافت اطلاعات" />
|
||||
<EmptyState v-if="flag" />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useInfiniteScroll } from '@vueuse/core'
|
||||
import Spinner from '../Spinner.vue'
|
||||
import EmptyState from '../EmptyState.vue'
|
||||
import { useTransitionStore } from '../../stores/transitionStore'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
loading: boolean
|
||||
flag: boolean
|
||||
padding?: string
|
||||
}>(),
|
||||
{},
|
||||
)
|
||||
const emit = defineEmits<{ (e: 'fetch'): void }>()
|
||||
const transition = useTransitionStore()
|
||||
const scrollArea = ref(null)
|
||||
|
||||
useInfiniteScroll(
|
||||
scrollArea,
|
||||
() => {
|
||||
emit('fetch')
|
||||
},
|
||||
{ distance: 50 },
|
||||
)
|
||||
</script>
|
||||
@@ -1,48 +0,0 @@
|
||||
<!--
|
||||
Ported from legacy henza_pwa/src/components/ui/ScrollFetchAfterSearch.vue.
|
||||
Legacy imported emp_after_search.svg / emp_before_search.svg and an `isSearch`
|
||||
prop to pick between them, but the live template used neither — it rendered
|
||||
`ordersEmpty.svg` unconditionally. Ported as-is (unused imports dropped,
|
||||
`isSearch` kept since it is part of the component's declared prop contract).
|
||||
-->
|
||||
<template>
|
||||
<main
|
||||
class="overflow-auto no-scrollbar w-[100vw] h-screen max-w-screen-md m-auto"
|
||||
:style="{ paddingTop: padding || transition.headerHeight + 'px' }"
|
||||
ref="scrollArea"
|
||||
>
|
||||
<slot></slot>
|
||||
<Spinner cls="my-[10px]" :spinning="loading" :indicator="true" tip="درحال دریافت اطلاعات" />
|
||||
<EmptyState v-if="flag && !loading" :image="ordersEmpty" />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useInfiniteScroll } from '@vueuse/core'
|
||||
import Spinner from '../Spinner.vue'
|
||||
import EmptyState from '../EmptyState.vue'
|
||||
import ordersEmpty from '../../assets/img/empty/ordersEmpty.svg'
|
||||
import { useTransitionStore } from '../../stores/transitionStore'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
loading: boolean
|
||||
flag: boolean
|
||||
padding?: string
|
||||
isSearch?: boolean
|
||||
}>(),
|
||||
{},
|
||||
)
|
||||
const emit = defineEmits<{ (e: 'fetch'): void }>()
|
||||
const transition = useTransitionStore()
|
||||
const scrollArea = ref(null)
|
||||
|
||||
useInfiniteScroll(
|
||||
scrollArea,
|
||||
() => {
|
||||
emit('fetch')
|
||||
},
|
||||
{ distance: 50 },
|
||||
)
|
||||
</script>
|
||||
Reference in New Issue
Block a user