Applies to both user-app and user-app-v2. Logic and structure are shared; each app keeps its own look. Appearance settings - New "تنظیمات ظاهری" page (/settings/appearance), opened from the dashboard and available without login. It offers a light/dark/system theme and a "fast & light" motion switch. - appearanceStore saves the choice to localStorage. A small inline boot script in index.html applies it before first paint so there is no white flash. The "system" option follows OS changes live. - New ToggleSwitch component. Dark mode - Theme LESS tokens now compile to CSS custom properties. Light/dark values come from a generated virtual:palette.css (themeConfig/ appearance.ts, with colors.json + new colors.dark.json). - New LESS alpha() plugin replaces fade() on theme tokens. - New semantic tokens: page, onInk, heroSurface, hairlineStrong, photoMat, photoStage. - The dark palette is layered (page < card < panel < border) so surfaces separate without shadows. - Hard-coded colors were replaced by tokens according to their role. - antd inputs/text get dark overrides; vant uses its own .van-theme-dark. - Light mode looks the same as before. Reduced motion - With html.motion-reduced, transitions are capped at 120ms, keyframe loops stop, and backdrop blurs and permanent will-change are dropped. - Spinners and the toast progress bar (which closes the toast) are exempt via .keep-motion. - Carousel autoplay, the v1 Lottie and the product morph honor the setting. Product detail - Opening a product is now a container-transform morph from the tapped card (ProductMorph). It replaces the bottom Drawer and uses composited transform/opacity only. - The detail view is seeded with the card's data, so it no longer opens blank and jumps. Filters and sheets - Filters are now a full-width bottom sheet. Selections stay a draft until applied, so the result list no longer re-renders while dragging the slider. - DropDown collapse animates grid-template-rows instead of a capped max-height. - v2 IslandSheet is restyled to the v2 design language (API unchanged). Fixes - filtersStore.reset() shallow-copied shared arrays, so clearing the filters never cleared the selected brands.
153 lines
3.4 KiB
Vue
153 lines
3.4 KiB
Vue
<!--
|
|
One line of an order's contents, restyled into the redesigned design
|
|
language (see components/home/* and HomeView). Ported originally from
|
|
legacy henza_pwa/src/components/product/orderItem.vue.
|
|
|
|
Adapted: product/variant title comes from a client-side lookup (see
|
|
useOrderItemProduct.ts) since the real order-items response only carries
|
|
productVariantId.
|
|
|
|
DROPPED the "توضیحات: -" row the previous version rendered: our OrderItem
|
|
schema has no note field (legacy's per-item note was a real column there;
|
|
ours isn't), so it could only ever print a dash. An always-empty row is
|
|
noise, not information.
|
|
-->
|
|
<template>
|
|
<article class="oi">
|
|
<div class="oi__art">
|
|
<img v-if="lookup?.imageUrl" v-lazy="lookup.imageUrl" :alt="lookup?.productTitle ?? ''" />
|
|
<div v-else class="oi__artEmpty" aria-hidden="true">
|
|
<Icon icon="vuesax-linear:diamonds" size="18" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="oi__main">
|
|
<div class="oi__head">
|
|
<h4 class="oi__title">{{ lookup?.productTitle ?? '—' }}</h4>
|
|
<span v-if="lookup?.variantTitle" class="oi__variant">{{ lookup.variantTitle }}</span>
|
|
</div>
|
|
|
|
<div class="oi__figures">
|
|
<span class="oi__figure">
|
|
{{ value.quantity }}
|
|
<i>عدد</i>
|
|
</span>
|
|
<span class="oi__figure">
|
|
{{ value.computedWeightGrams }}
|
|
<i>گرم</i>
|
|
</span>
|
|
<span class="oi__figure">
|
|
{{ value.wagePercentSnapshot }}
|
|
<i>٪ اجرت</i>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, ref } from 'vue'
|
|
import Icon from '../icon/Icon.vue'
|
|
import { useOrderItemProduct, type VariantLookup } from '../../composables/useOrderItemProduct'
|
|
import type { OrderItemDetail } from '../../api/client'
|
|
|
|
const props = defineProps<{ value: OrderItemDetail }>()
|
|
const { lookup: lookupVariant } = useOrderItemProduct()
|
|
const lookup = ref<VariantLookup | undefined>()
|
|
|
|
onMounted(async () => {
|
|
lookup.value = await lookupVariant(props.value.productVariantId)
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.oi {
|
|
display: flex;
|
|
gap: 11px;
|
|
padding: 11px;
|
|
background: @canvas;
|
|
border: 1px solid @hairline;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.oi__art {
|
|
flex: 0 0 62px;
|
|
width: 62px;
|
|
height: 62px;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
background: @hairlineSoft;
|
|
}
|
|
|
|
.oi__art img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
display: block;
|
|
}
|
|
|
|
// Dark mode: product photos are shot on white, so they sit on a light mat
|
|
// instead of showing as white boxes on a dark card (@photoMat — transparent,
|
|
// i.e. no change, in light mode).
|
|
html.theme-dark .oi__art img {
|
|
background: @photoMat;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.oi__artEmpty {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: grid;
|
|
place-items: center;
|
|
color: @inkFaint;
|
|
}
|
|
|
|
.oi__main {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.oi__head {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 7px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.oi__title {
|
|
margin: 0;
|
|
font-size: 12.5px;
|
|
font-weight: 700;
|
|
color: @ink;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.oi__variant {
|
|
flex: 0 0 auto;
|
|
font-size: 10.5px;
|
|
color: @inkFaint;
|
|
}
|
|
|
|
.oi__figures {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 14px;
|
|
padding-top: 9px;
|
|
}
|
|
|
|
.oi__figure {
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
color: @inkSoft;
|
|
}
|
|
.oi__figure i {
|
|
font-style: normal;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
color: @inkFaint;
|
|
padding-inline-start: 2px;
|
|
}
|
|
</style>
|