Polish order tracking layout and theme-safe desktop UI.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
.desktop-two-col__main {
|
.desktop-two-col__main {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-summary-panel {
|
.desktop-summary-panel {
|
||||||
@@ -87,17 +88,21 @@
|
|||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
box-shadow: var(--shadow-card);
|
box-shadow: var(--shadow-card);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-table-scroll {
|
.desktop-table-scroll {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
overscroll-behavior-x: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-table {
|
.desktop-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
min-width: 640px;
|
min-width: 560px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-table th {
|
.desktop-table th {
|
||||||
@@ -106,16 +111,17 @@
|
|||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
background: #f8fafc;
|
background: var(--color-bg);
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-table td {
|
.desktop-table td {
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
border-bottom: 1px solid #f1f5f9;
|
border-bottom: 1px solid var(--color-border);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
background: var(--color-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-table tbody tr:last-child td {
|
.desktop-table tbody tr:last-child td {
|
||||||
@@ -123,7 +129,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.desktop-table tbody tr:hover td {
|
.desktop-table tbody tr:hover td {
|
||||||
background: #fafbfc;
|
background: var(--color-elevated);
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-grid-2 {
|
.desktop-grid-2 {
|
||||||
|
|||||||
@@ -285,16 +285,17 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
background: #f8fafc;
|
background: var(--color-bg);
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.orders-table td {
|
.orders-table td {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-bottom: 1px solid #f1f5f9;
|
border-bottom: 1px solid var(--color-border);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
background: var(--color-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.orders-table tr:last-child td {
|
.orders-table tr:last-child td {
|
||||||
@@ -302,7 +303,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.orders-table tbody tr:hover td {
|
.orders-table tbody tr:hover td {
|
||||||
background: #fafbfc;
|
background: var(--color-elevated);
|
||||||
}
|
}
|
||||||
|
|
||||||
.orders-table__number {
|
.orders-table__number {
|
||||||
@@ -362,7 +363,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.orders-table__menu:hover {
|
.orders-table__menu:hover {
|
||||||
background: #f1f5f9;
|
background: var(--color-elevated);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,8 +549,8 @@
|
|||||||
margin-inline: 0;
|
margin-inline: 0;
|
||||||
padding: 24px 0 32px;
|
padding: 24px 0 32px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
height: auto;
|
height: 100%;
|
||||||
overflow: visible;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-detail-back {
|
.order-detail-back {
|
||||||
|
|||||||
@@ -225,6 +225,8 @@
|
|||||||
max-width: none;
|
max-width: none;
|
||||||
margin-inline: 0;
|
margin-inline: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-detail-page__footer {
|
.order-detail-page__footer {
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { useCartStore } from '@/stores/cart'
|
import { useCartStore } from '@/stores/cart'
|
||||||
import { useNotificationStore } from '@/stores/notification'
|
import { useNotificationStore } from '@/stores/notification'
|
||||||
import IconSearch from '@/components/icons/IconSearch.vue'
|
|
||||||
import IconBell from '@/components/icons/IconBell.vue'
|
import IconBell from '@/components/icons/IconBell.vue'
|
||||||
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
||||||
import AuthThemeToggle from '@/components/ui/AuthThemeToggle.vue'
|
import AuthThemeToggle from '@/components/ui/AuthThemeToggle.vue'
|
||||||
import { toPersianDigits } from '@/utils/format'
|
import { toPersianDigits } from '@/utils/format'
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const cartStore = useCartStore()
|
const cartStore = useCartStore()
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
const searchQuery = ref('')
|
|
||||||
const showUserMenu = ref(false)
|
const showUserMenu = ref(false)
|
||||||
|
|
||||||
const unreadLabel = computed(() =>
|
const unreadLabel = computed(() =>
|
||||||
@@ -35,22 +32,6 @@ const companyLabel = computed(
|
|||||||
() => cartStore.companyNamesLabel || cartStore.activeCart?.companyName || '',
|
() => cartStore.companyNamesLabel || cartStore.activeCart?.companyName || '',
|
||||||
)
|
)
|
||||||
|
|
||||||
const searchPlaceholder = computed(() => {
|
|
||||||
if (route.path.startsWith('/orders')) return 'جستجو در سفارشها...'
|
|
||||||
if (route.path.startsWith('/companies')) return 'جستجو در محصولات...'
|
|
||||||
return 'جستجو در سفارشها، شرکتها...'
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleSearch() {
|
|
||||||
const q = searchQuery.value.trim()
|
|
||||||
if (!q) return
|
|
||||||
if (route.path.startsWith('/orders')) {
|
|
||||||
router.replace({ path: '/orders', query: { ...route.query, q } })
|
|
||||||
} else if (route.path.includes('/catalogs') && !route.path.includes('/products')) {
|
|
||||||
router.replace({ query: { ...route.query, search: q } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleUserMenu() {
|
function toggleUserMenu() {
|
||||||
showUserMenu.value = !showUserMenu.value
|
showUserMenu.value = !showUserMenu.value
|
||||||
}
|
}
|
||||||
@@ -69,16 +50,7 @@ function logout() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<header class="desktop-header">
|
<header class="desktop-header">
|
||||||
<div class="desktop-header__search-wrap">
|
<div class="desktop-header__spacer" aria-hidden="true" />
|
||||||
<IconSearch :size="18" class="desktop-header__search-icon" />
|
|
||||||
<input
|
|
||||||
v-model="searchQuery"
|
|
||||||
type="search"
|
|
||||||
class="desktop-header__search"
|
|
||||||
:placeholder="searchPlaceholder"
|
|
||||||
@keydown.enter="handleSearch"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="desktop-header__actions">
|
<div class="desktop-header__actions">
|
||||||
<span v-if="companyLabel" class="desktop-header__company">{{ companyLabel }}</span>
|
<span v-if="companyLabel" class="desktop-header__company">{{ companyLabel }}</span>
|
||||||
@@ -150,41 +122,9 @@ function logout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-header__search-wrap {
|
.desktop-header__spacer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
max-width: 420px;
|
min-width: 0;
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktop-header__search-icon {
|
|
||||||
position: absolute;
|
|
||||||
inset-inline-start: 14px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktop-header__search {
|
|
||||||
width: 100%;
|
|
||||||
height: 42px;
|
|
||||||
padding-inline: 42px 16px;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
background: var(--color-summary-bg);
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktop-header__search:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--color-primary);
|
|
||||||
background: var(--color-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktop-header__search::placeholder {
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-header__actions {
|
.desktop-header__actions {
|
||||||
@@ -192,6 +132,7 @@ function logout() {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
margin-inline-start: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-header__company {
|
.desktop-header__company {
|
||||||
@@ -262,7 +203,7 @@ function logout() {
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--color-primary);
|
background: var(--color-primary);
|
||||||
color: #fff;
|
color: var(--color-surface);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -227,7 +227,8 @@ async function submitChecks() {
|
|||||||
<div class="desktop-two-col">
|
<div class="desktop-two-col">
|
||||||
<div class="desktop-two-col__main">
|
<div class="desktop-two-col__main">
|
||||||
<div class="desktop-table-wrap">
|
<div class="desktop-table-wrap">
|
||||||
<table class="desktop-table">
|
<div class="desktop-table-scroll">
|
||||||
|
<table class="desktop-table check-list-desktop__table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">تصویر</th>
|
<th scope="col">تصویر</th>
|
||||||
@@ -248,7 +249,7 @@ async function submitChecks() {
|
|||||||
/>
|
/>
|
||||||
<span v-else>—</span>
|
<span v-else>—</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ formatNumber(check.amount) }}</td>
|
<td class="check-list-desktop__amount">{{ formatNumber(check.amount) }}</td>
|
||||||
<td>{{ formatDate(check.dueDate) }}</td>
|
<td>{{ formatDate(check.dueDate) }}</td>
|
||||||
<td>چک {{ toPersianDigits(String(index + 1)) }}</td>
|
<td>چک {{ toPersianDigits(String(index + 1)) }}</td>
|
||||||
<td v-if="isCheckout && orderStore.draftChecks.length > 0">
|
<td v-if="isCheckout && orderStore.draftChecks.length > 0">
|
||||||
@@ -260,6 +261,7 @@ async function submitChecks() {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
v-if="isCheckout"
|
v-if="isCheckout"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -314,12 +316,32 @@ async function submitChecks() {
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-list-desktop__amount {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-list-desktop__table {
|
||||||
|
min-width: 560px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-list-desktop__add {
|
.check-list-desktop__add {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) and (max-width: 1099px) {
|
||||||
|
.check-list-desktop :deep(.desktop-two-col) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-list-desktop :deep(.desktop-summary-panel) {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.desktop-summary-panel__action {
|
.desktop-summary-panel__action {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { useMediaQuery } from '@vueuse/core'
|
||||||
import { useOrderStore } from '@/stores/order'
|
import { useOrderStore } from '@/stores/order'
|
||||||
import BaseButton from '@/components/ui/BaseButton.vue'
|
import BaseButton from '@/components/ui/BaseButton.vue'
|
||||||
import BaseInput from '@/components/ui/BaseInput.vue'
|
import BaseInput from '@/components/ui/BaseInput.vue'
|
||||||
@@ -8,6 +9,7 @@ import CurrencyInput from '@/components/ui/CurrencyInput.vue'
|
|||||||
import PersianDateInput from '@/components/ui/PersianDateInput.vue'
|
import PersianDateInput from '@/components/ui/PersianDateInput.vue'
|
||||||
import FormErrorBanner from '@/components/ui/FormErrorBanner.vue'
|
import FormErrorBanner from '@/components/ui/FormErrorBanner.vue'
|
||||||
import IconChevronBack from '@/components/icons/IconChevronBack.vue'
|
import IconChevronBack from '@/components/icons/IconChevronBack.vue'
|
||||||
|
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
||||||
import {
|
import {
|
||||||
formatMeterage,
|
formatMeterage,
|
||||||
formatCurrency,
|
formatCurrency,
|
||||||
@@ -33,11 +35,13 @@ import { isInvoicePriced } from '@/utils/orderInvoice'
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const orderStore = useOrderStore()
|
const orderStore = useOrderStore()
|
||||||
|
const isDesktopLayout = useMediaQuery('(min-width: 768px)')
|
||||||
|
|
||||||
const orderId = computed(() => route.params.id as string)
|
const orderId = computed(() => route.params.id as string)
|
||||||
const order = computed(() => orderStore.getOrderById(orderId.value))
|
const order = computed(() => orderStore.getOrderById(orderId.value))
|
||||||
const showTracking = ref(route.query.track === '1' || route.query.receipt === '1')
|
const showTracking = ref(route.query.track === '1' || route.query.receipt === '1')
|
||||||
const showReceiptForm = ref(route.query.receipt === '1')
|
const showReceiptForm = ref(route.query.receipt === '1')
|
||||||
|
const showTimeline = ref(true)
|
||||||
const showOrderDetails = ref(route.query.receipt === '1')
|
const showOrderDetails = ref(route.query.receipt === '1')
|
||||||
const receiptAmount = ref('')
|
const receiptAmount = ref('')
|
||||||
const receiptDate = ref('')
|
const receiptDate = ref('')
|
||||||
@@ -48,6 +52,19 @@ const receiptImage = ref('')
|
|||||||
const fieldErrors = ref<FieldErrors>({})
|
const fieldErrors = ref<FieldErrors>({})
|
||||||
const formError = ref('')
|
const formError = ref('')
|
||||||
|
|
||||||
|
watch(
|
||||||
|
isDesktopLayout,
|
||||||
|
(wide) => {
|
||||||
|
if (!wide) return
|
||||||
|
showTracking.value = true
|
||||||
|
if (route.query.track === '1' || route.query.receipt === '1') return
|
||||||
|
void router.replace({
|
||||||
|
query: { ...route.query, track: '1' },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
|
||||||
watch(receiptAmount, () => {
|
watch(receiptAmount, () => {
|
||||||
fieldErrors.value = clearFieldError(fieldErrors.value, 'receiptAmount')
|
fieldErrors.value = clearFieldError(fieldErrors.value, 'receiptAmount')
|
||||||
if (!hasFieldErrors(fieldErrors.value)) formError.value = ''
|
if (!hasFieldErrors(fieldErrors.value)) formError.value = ''
|
||||||
@@ -149,8 +166,12 @@ function openTracking() {
|
|||||||
router.replace({ query: { track: '1' } })
|
router.replace({ query: { track: '1' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
function openOrderDetails() {
|
function toggleOrderDetails() {
|
||||||
showOrderDetails.value = true
|
showOrderDetails.value = !showOrderDetails.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTimeline() {
|
||||||
|
showTimeline.value = !showTimeline.value
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleReceiptImage(event: Event) {
|
async function handleReceiptImage(event: Event) {
|
||||||
@@ -227,16 +248,14 @@ async function submitReceipt() {
|
|||||||
<button type="button" class="order-detail-back" @click="router.push('/orders')">
|
<button type="button" class="order-detail-back" @click="router.push('/orders')">
|
||||||
← بازگشت به سفارشها
|
← بازگشت به سفارشها
|
||||||
</button>
|
</button>
|
||||||
<h1 class="desktop-page__title">
|
<h1 class="desktop-page__title">پیگیری سفارش</h1>
|
||||||
{{ showTracking ? 'پیگیری سفارش' : 'جزئیات سفارش' }}
|
|
||||||
</h1>
|
|
||||||
<p class="desktop-page__subtitle">{{ order.orderNumber }} · {{ order.companyName }}</p>
|
<p class="desktop-page__subtitle">{{ order.orderNumber }} · {{ order.companyName }}</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="order-detail-page__body">
|
<div class="order-detail-page__body">
|
||||||
<p v-if="showTracking" class="order-detail-page__order-id mobile-only">{{ order.orderNumber }}</p>
|
<p v-if="showTracking" class="order-detail-page__order-id mobile-only">{{ order.orderNumber }}</p>
|
||||||
|
|
||||||
<div v-if="!showTracking" class="order-info-card">
|
<div v-if="!showTracking" class="order-info-card mobile-only">
|
||||||
<div class="order-info-card__head">
|
<div class="order-info-card__head">
|
||||||
<span class="order-info-card__label">شماره سفارش</span>
|
<span class="order-info-card__label">شماره سفارش</span>
|
||||||
<strong class="order-info-card__number">{{ order.orderNumber }}</strong>
|
<strong class="order-info-card__number">{{ order.orderNumber }}</strong>
|
||||||
@@ -269,7 +288,7 @@ async function submitReceipt() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="nextAction" class="order-next-action">
|
<div v-if="nextAction && !showTracking" class="order-next-action">
|
||||||
<span class="order-next-action__text">برای ادامه سفارش، این مرحله باقی مانده است.</span>
|
<span class="order-next-action__text">برای ادامه سفارش، این مرحله باقی مانده است.</span>
|
||||||
<BaseButton class="order-next-action__btn" @click="runNextAction">
|
<BaseButton class="order-next-action__btn" @click="runNextAction">
|
||||||
{{ nextAction.label }}
|
{{ nextAction.label }}
|
||||||
@@ -278,13 +297,54 @@ async function submitReceipt() {
|
|||||||
|
|
||||||
<div v-if="showTracking" class="order-detail-page__tracking">
|
<div v-if="showTracking" class="order-detail-page__tracking">
|
||||||
<section class="order-detail-page__tracking-main">
|
<section class="order-detail-page__tracking-main">
|
||||||
<div class="tracking-card">
|
<div v-if="nextAction" class="order-next-action">
|
||||||
<h3 class="tracking-card__title">پیگیری وضعیت</h3>
|
<span class="order-next-action__text">برای ادامه سفارش، این مرحله باقی مانده است.</span>
|
||||||
<OrderTrackingTimeline :order="order" />
|
<BaseButton class="order-next-action__btn" @click="runNextAction">
|
||||||
|
{{ nextAction.label }}
|
||||||
|
</BaseButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="showOrderDetails">
|
<div class="tracking-card tracking-collapse">
|
||||||
<div class="tracking-card">
|
<button
|
||||||
|
type="button"
|
||||||
|
class="tracking-collapse__trigger"
|
||||||
|
:aria-expanded="showTimeline"
|
||||||
|
@click="toggleTimeline"
|
||||||
|
>
|
||||||
|
<span>پیگیری وضعیت</span>
|
||||||
|
<IconChevronLeft
|
||||||
|
:size="18"
|
||||||
|
class="tracking-collapse__chevron"
|
||||||
|
:class="{ 'tracking-collapse__chevron--open': showTimeline }"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div v-show="showTimeline" class="tracking-collapse__body">
|
||||||
|
<div class="tracking-collapse__section tracking-collapse__section--flush">
|
||||||
|
<OrderTrackingTimeline :order="order" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tracking-card tracking-collapse">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="tracking-collapse__trigger"
|
||||||
|
:aria-expanded="showOrderDetails"
|
||||||
|
@click="toggleOrderDetails"
|
||||||
|
>
|
||||||
|
<span>{{ trackingFooterLabel }}</span>
|
||||||
|
<IconChevronLeft
|
||||||
|
:size="18"
|
||||||
|
class="tracking-collapse__chevron"
|
||||||
|
:class="{ 'tracking-collapse__chevron--open': showOrderDetails }"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div v-show="showOrderDetails" class="tracking-collapse__body">
|
||||||
|
<div class="tracking-collapse__section">
|
||||||
<h3 class="tracking-card__title">اقلام سفارش</h3>
|
<h3 class="tracking-card__title">اقلام سفارش</h3>
|
||||||
<div v-for="item in order.items" :key="item.id" class="item-row">
|
<div v-for="item in order.items" :key="item.id" class="item-row">
|
||||||
<div>
|
<div>
|
||||||
@@ -304,7 +364,7 @@ async function submitReceipt() {
|
|||||||
اعتبار فاکتور: {{ formatDateTime(order.cashInvoiceExpiresAt) }}
|
اعتبار فاکتور: {{ formatDateTime(order.cashInvoiceExpiresAt) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tracking-card">
|
<div class="tracking-collapse__section">
|
||||||
<h3 class="tracking-card__title">حسابهای بانکی</h3>
|
<h3 class="tracking-card__title">حسابهای بانکی</h3>
|
||||||
<div v-for="acc in bankAccounts" :key="acc.id" class="bank-row">
|
<div v-for="acc in bankAccounts" :key="acc.id" class="bank-row">
|
||||||
<strong>{{ acc.bankName }}</strong>
|
<strong>{{ acc.bankName }}</strong>
|
||||||
@@ -321,7 +381,7 @@ async function submitReceipt() {
|
|||||||
{{ order.cashReceipt ? 'ثبت رسید جدید' : 'ثبت رسید پرداخت' }}
|
{{ order.cashReceipt ? 'ثبت رسید جدید' : 'ثبت رسید پرداخت' }}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
|
|
||||||
<div v-if="order.cashReceipt" class="tracking-card">
|
<div v-if="order.cashReceipt" class="tracking-collapse__section">
|
||||||
<h3 class="tracking-card__title">رسید ثبتشده</h3>
|
<h3 class="tracking-card__title">رسید ثبتشده</h3>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span>مبلغ</span>
|
<span>مبلغ</span>
|
||||||
@@ -335,7 +395,7 @@ async function submitReceipt() {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="order.settlementType === 'check' && order.checks">
|
<template v-if="order.settlementType === 'check' && order.checks">
|
||||||
<div class="tracking-card">
|
<div class="tracking-collapse__section">
|
||||||
<h3 class="tracking-card__title">رأس چکها</h3>
|
<h3 class="tracking-card__title">رأس چکها</h3>
|
||||||
<div class="rass-display">{{ order.checkRassDays }} روز</div>
|
<div class="rass-display">{{ order.checkRassDays }} روز</div>
|
||||||
<p v-if="order.checkRassDate" class="rass-date">
|
<p v-if="order.checkRassDate" class="rass-date">
|
||||||
@@ -343,7 +403,11 @@ async function submitReceipt() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="(check, i) in order.checks" :key="check.id" class="tracking-card">
|
<div
|
||||||
|
v-for="(check, i) in order.checks"
|
||||||
|
:key="check.id"
|
||||||
|
class="tracking-collapse__section"
|
||||||
|
>
|
||||||
<strong>چک {{ i + 1 }}</strong>
|
<strong>چک {{ i + 1 }}</strong>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span>مبلغ</span>
|
<span>مبلغ</span>
|
||||||
@@ -356,49 +420,23 @@ async function submitReceipt() {
|
|||||||
<img v-if="check.frontImage" :src="check.frontImage" class="check-img" alt="چک" />
|
<img v-if="check.frontImage" :src="check.frontImage" class="check-img" alt="چک" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="order-detail-page__tracking-aside">
|
<section class="order-detail-page__tracking-aside">
|
||||||
<div class="tracking-card tracking-card--hero">
|
<div class="tracking-card tracking-card--hero">
|
||||||
<OrderStatusHero v-if="statusHero" :config="statusHero" />
|
<OrderStatusHero v-if="statusHero" :config="statusHero" />
|
||||||
<OrderTrackingSummary :order="order" />
|
<OrderTrackingSummary :order="order" />
|
||||||
<div class="order-detail-page__tracking-cta desktop-only">
|
|
||||||
<BaseButton
|
|
||||||
v-if="!showOrderDetails"
|
|
||||||
block
|
|
||||||
@click="openOrderDetails"
|
|
||||||
>
|
|
||||||
{{ trackingFooterLabel }}
|
|
||||||
</BaseButton>
|
|
||||||
<BaseButton
|
|
||||||
v-else
|
|
||||||
block
|
|
||||||
variant="outline"
|
|
||||||
@click="showOrderDetails = false"
|
|
||||||
>
|
|
||||||
بستن جزئیات
|
|
||||||
</BaseButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="showTracking && !showOrderDetails" class="order-detail-page__footer mobile-only">
|
|
||||||
<BaseButton block @click="openOrderDetails">{{ trackingFooterLabel }}</BaseButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="!showTracking" class="order-detail-page__footer mobile-only">
|
<div v-if="!showTracking" class="order-detail-page__footer mobile-only">
|
||||||
<BaseButton block @click="openTracking">مشاهده جزئیات و پیگیری</BaseButton>
|
<BaseButton block @click="openTracking">مشاهده جزئیات و پیگیری</BaseButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!showTracking" class="order-detail-page__desktop-actions desktop-only">
|
|
||||||
<BaseButton class="desktop-btn-compact" @click="openTracking">
|
|
||||||
مشاهده جزئیات و پیگیری
|
|
||||||
</BaseButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="showReceiptForm" class="dialog-overlay" @click.self="showReceiptForm = false">
|
<div v-if="showReceiptForm" class="dialog-overlay" @click.self="showReceiptForm = false">
|
||||||
<div class="dialog card">
|
<div class="dialog card">
|
||||||
<h3>ثبت رسید پرداخت</h3>
|
<h3>ثبت رسید پرداخت</h3>
|
||||||
@@ -473,8 +511,6 @@ async function submitReceipt() {
|
|||||||
max-width: none;
|
max-width: none;
|
||||||
margin-inline: 0;
|
margin-inline: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,7 +557,7 @@ async function submitReceipt() {
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 28px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,19 +750,62 @@ async function submitReceipt() {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-detail-page__tracking-cta {
|
.tracking-collapse {
|
||||||
padding-top: 4px;
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-detail-page__desktop-actions {
|
.tracking-collapse__trigger {
|
||||||
display: none;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: start;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-collapse__trigger:hover {
|
||||||
|
background: var(--color-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-collapse__chevron {
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-collapse__chevron--open {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-collapse__body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-collapse__section {
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-collapse__section--flush {
|
||||||
|
padding-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.order-detail-page__body {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-detail-page__tracking {
|
.order-detail-page__tracking {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1.35fr) minmax(260px, 0.9fr);
|
grid-template-columns: minmax(0, 1.35fr) minmax(260px, 0.9fr);
|
||||||
@@ -741,12 +820,6 @@ async function submitReceipt() {
|
|||||||
top: 16px;
|
top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-detail-page__desktop-actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-detail-page__footer {
|
.order-detail-page__footer {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -789,7 +862,6 @@ async function submitReceipt() {
|
|||||||
.item-row--summary {
|
.item-row--summary {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
border-top: 1px solid var(--color-border);
|
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ const emit = defineEmits<{
|
|||||||
aria-label="صفحه قبل"
|
aria-label="صفحه قبل"
|
||||||
@click="emit('setPage', currentPage - 1)"
|
@click="emit('setPage', currentPage - 1)"
|
||||||
>
|
>
|
||||||
<IconChevronLeft :size="16" class="orders-pagination__arrow-icon--next" />
|
<IconChevronLeft :size="16" class="orders-pagination__arrow-icon--prev" />
|
||||||
</button>
|
</button>
|
||||||
<template v-for="(page, index) in visiblePages" :key="`${page}-${index}`">
|
<template v-for="(page, index) in visiblePages" :key="`${page}-${index}`">
|
||||||
<span v-if="page === 'ellipsis'" class="orders-pagination__ellipsis">…</span>
|
<span v-if="page === 'ellipsis'" class="orders-pagination__ellipsis">…</span>
|
||||||
@@ -197,7 +197,7 @@ const emit = defineEmits<{
|
|||||||
aria-label="صفحه بعد"
|
aria-label="صفحه بعد"
|
||||||
@click="emit('setPage', currentPage + 1)"
|
@click="emit('setPage', currentPage + 1)"
|
||||||
>
|
>
|
||||||
<IconChevronLeft :size="16" class="orders-pagination__arrow-icon--prev" />
|
<IconChevronLeft :size="16" class="orders-pagination__arrow-icon--next" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ const emit = defineEmits<{
|
|||||||
aria-label="صفحه قبل"
|
aria-label="صفحه قبل"
|
||||||
@click="emit('set-page', currentPage - 1)"
|
@click="emit('set-page', currentPage - 1)"
|
||||||
>
|
>
|
||||||
<IconChevronLeft :size="16" class="orders-mobile-pagination__arrow--next" />
|
<IconChevronLeft :size="16" class="orders-mobile-pagination__arrow--prev" />
|
||||||
</button>
|
</button>
|
||||||
<template v-for="(page, index) in visiblePages" :key="`${page}-${index}`">
|
<template v-for="(page, index) in visiblePages" :key="`${page}-${index}`">
|
||||||
<span v-if="page === 'ellipsis'" class="orders-mobile-pagination__ellipsis">…</span>
|
<span v-if="page === 'ellipsis'" class="orders-mobile-pagination__ellipsis">…</span>
|
||||||
@@ -115,7 +115,7 @@ const emit = defineEmits<{
|
|||||||
aria-label="صفحه بعد"
|
aria-label="صفحه بعد"
|
||||||
@click="emit('set-page', currentPage + 1)"
|
@click="emit('set-page', currentPage + 1)"
|
||||||
>
|
>
|
||||||
<IconChevronLeft :size="16" class="orders-mobile-pagination__arrow--prev" />
|
<IconChevronLeft :size="16" class="orders-mobile-pagination__arrow--next" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -307,6 +307,10 @@ const emit = defineEmits<{
|
|||||||
transform: scaleX(-1);
|
transform: scaleX(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.orders-mobile-pagination__arrow--next {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
.orders-mobile-pagination__page--active {
|
.orders-mobile-pagination__page--active {
|
||||||
background: var(--color-primary);
|
background: var(--color-primary);
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
|
|||||||
Reference in New Issue
Block a user