SearchView: Removed the dynamic collection-tile grid (e.g. "پرفروش‌های این هفته") and the "نتایج / N محصول" section header from the UI. Underlying data/filtering logic untouched.

DashboardView ("میز کار"): Rebuilt from scratch with a new structure, distinct from the old card+grid layout but visually aligned with HomeView (same blurred bar, @ink/@wine palette, staged reveal animation on mount):

Single hero card (ink→wine gradient) showing balance, gold/fiat debt, and identity — no quick-action icons inside it anymore.

Live gold rate folded into a slim inline row at the bottom instead of a titled section.
Iterated shadow/spacing on the hero card per feedback (softer, less clipped).
DialogVant / DialogVantOneButton (shared modal components, used in 7+ call sites: Dashboard, Cart, SingleProduct, Register, App): Replaced Vant's default dialog chrome with the app's own rounded-card language — circular icon badge for the illustration, pill-shaped primary/secondary buttons. No call-site markup changed.

LoginView: Rebuilt from scratch — full-bleed dark hero (brand + step copy) overlapped by a floating white form sheet, animated crossfade transition between the mobile-number and OTP steps, same staged entrance animation as Home/Dashboard. All auth logic (API check, OTP stub, session login) unchanged.
This commit is contained in:
s.esmaeili
2026-09-18 12:54:45 +03:30
parent 05fe780c89
commit 53a304b5d5
355 changed files with 22865 additions and 14 deletions
@@ -0,0 +1,156 @@
<!--
Product card for the redesigned home screen.
Shows only fields that genuinely exist on ProductSummary today: title, the
cheapest variant's rash weight, its making-charge (اجرت) percent, and the
catalogue image. No price is printed on the card, because a finished piece's
rial price is a function of the live gold rate plus wage and is computed at
order time, not stored on the product — printing one here would be invented.
-->
<template>
<article class="card" :class="{ 'card--wide': wide }" @click="emit('open', value)">
<div class="card__art">
<img v-lazy="image" :alt="title" />
<button type="button" class="card__fav" :class="{ 'is-on': favorites.isFavorite(id) }" @click.stop="favorites.toggle(id)">
<Icon :icon="favorites.isFavorite(id) ? 'vuesax-linear:heart-fill' : 'vuesax-linear:heart'" size="17" />
</button>
<span v-if="badge" class="card__badge">{{ badge }}</span>
</div>
<div class="card__body">
<h3 class="card__title">{{ title }}</h3>
<div class="card__meta">
<span class="card__weight">{{ min_gram }} گرم</span>
<span v-if="wage != null" class="card__wage">اجرت {{ wageLabel }}٪</span>
</div>
</div>
</article>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import Icon from '../icon/Icon.vue'
import { useProduct } from '../../composables/useProduct'
import { useFavoritesStore } from '../../stores/favoritesStore'
import type { ProductSummary } from '../../api/client'
const props = defineProps<{ value: ProductSummary; wide?: boolean; badge?: string }>()
const emit = defineEmits<{ (e: 'open', value: ProductSummary): void }>()
const favorites = useFavoritesStore()
const { image, id, title, min_gram, wage } = useProduct(props.value)
const wageLabel = computed(() => {
const w = wage.value
if (w == null) return ''
return w % 1 === 0 ? String(w) : w.toFixed(2)
})
</script>
<style scoped lang="less">
.card {
cursor: pointer;
background: @canvas;
border: 1px solid @hairline;
border-radius: 18px;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.card:active {
transform: scale(0.985);
}
.card__art {
position: relative;
aspect-ratio: 1 / 1;
display: flex;
align-items: center;
justify-content: center;
background: @hairlineSoft;
}
.card--wide .card__art {
aspect-ratio: 4 / 3;
}
.card__art img {
width: 100%;
max-width: 200px;
height: 100%;
max-height: 200px;
object-fit: contain;
object-position: center;
display: block;
}
.card__fav {
position: absolute;
top: 9px;
left: 9px;
width: 30px;
height: 30px;
display: grid;
place-items: center;
border: 0;
cursor: pointer;
border-radius: 50%;
color: @inkMuted;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(6px);
transition: color 0.18s ease, transform 0.18s ease;
}
.card__fav.is-on {
color: @wine;
}
.card__fav:active {
transform: scale(0.9);
}
.card__badge {
position: absolute;
top: 9px;
right: 9px;
padding: 4px 9px;
border-radius: 999px;
font-size: 10px;
font-weight: 700;
color: #fff;
background: @wine;
}
.card__body {
padding: 11px 12px 13px;
}
.card__title {
margin: 0;
font-size: 13px;
font-weight: 700;
color: @ink;
line-height: 1.6;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.card__meta {
display: flex;
align-items: center;
gap: 7px;
padding-top: 7px;
}
.card__weight {
font-size: 11.5px;
font-weight: 700;
color: @inkSoft;
}
.card__wage {
font-size: 10.5px;
color: @inkFaint;
padding-inline-start: 7px;
border-inline-start: 1px solid @hairline;
}
</style>