Files
fabric_trade_admin/src/views/orders/components/CompactOrdersPage.vue
T

239 lines
6.6 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { useMediaQuery } from '@vueuse/core'
import AdminIcon from '@/components/icons/AdminIcon.vue'
import StatusBadge from '@/components/ui/StatusBadge.vue'
import BaseModal from '@/components/ui/BaseModal.vue'
import OrderDetailContent from '@/views/orders/components/OrderDetailContent.vue'
import { useOrdersPageContext } from '@/composables/useOrdersPage'
import { ORDER_TABS } from '@/utils/orderStatus'
import { formatCurrency, formatDateTime, formatMeterage } from '@/utils/format'
const isMobile = useMediaQuery('(max-width: 767px)')
/** Matches `.compact-only` visible range (< 1280px) */
const isCompactLayout = useMediaQuery('(max-width: 1279px)')
const {
activeTab,
selectedOrderId,
searchQuery,
filteredOrders,
selectedOrder,
showDetailPanel,
showDetailModal,
selectOrder,
openOrderDetail,
closeDetailPanel,
closeDetailModal,
settlementLabel,
goPricing,
goChecks,
goCashReceipt,
changeStatus,
statusSaving,
openAddModal,
} = useOrdersPageContext()
function openDetail(order: (typeof filteredOrders.value)[number]) {
if (isMobile.value) {
selectOrder(order, true)
return
}
openOrderDetail(order)
}
function handlePricing() {
if (!selectedOrder.value) return
closeDetailModal()
closeDetailPanel()
goPricing(selectedOrder.value)
}
function handleChecks() {
if (!selectedOrder.value) return
closeDetailModal()
closeDetailPanel()
goChecks(selectedOrder.value)
}
function handleCashReceipt() {
if (!selectedOrder.value) return
closeDetailModal()
closeDetailPanel()
goCashReceipt(selectedOrder.value)
}
</script>
<template>
<div class="compact-orders">
<div class="compact-page-header">
<div class="compact-page-header__row">
<div>
<h2 class="compact-page-header__title">سفارش‌ها</h2>
<p class="compact-page-header__breadcrumb">داشبورد / سفارش‌ها</p>
</div>
<div class="compact-page-header__actions">
<button type="button" class="admin-btn admin-btn--outline admin-btn--sm">
<AdminIcon name="filter" :size="16" />
فیلتر
</button>
<button type="button" class="admin-btn admin-btn--primary admin-btn--sm" @click="openAddModal">
سفارش جدید
</button>
</div>
</div>
</div>
<div class="orders-page__search admin-card">
<AdminIcon name="search" :size="18" />
<input
v-model="searchQuery"
type="search"
placeholder="جستجو در شماره سفارش، مشتری..."
class="orders-page__search-input"
/>
</div>
<div class="admin-tabs compact-orders__tabs">
<button
v-for="tab in ORDER_TABS"
:key="tab.key"
type="button"
class="admin-tab"
:class="{ active: activeTab === tab.key }"
@click="activeTab = tab.key"
>
{{ tab.label }}
<template v-if="'count' in tab && tab.count"> ({{ tab.count }})</template>
</button>
</div>
<div class="admin-card-list">
<button
v-for="order in filteredOrders"
:key="order.id"
type="button"
class="admin-card-item"
:class="{ selected: selectedOrderId === order.id }"
@click="openDetail(order)"
>
<div class="admin-card-item__header">
<p class="admin-card-item__title">{{ order.orderNumber }}</p>
<StatusBadge :status="order.status" />
</div>
<div class="admin-card-item__meta">
<span><strong>مشتری:</strong> {{ order.customerName }}</span>
<span><strong>متراژ:</strong> {{ formatMeterage(order.meterage) }}</span>
<span><strong>مبلغ:</strong> {{ order.totalAmount ? formatCurrency(order.totalAmount) : '—' }}</span>
<span>{{ formatDateTime(order.createdAt) }}</span>
</div>
</button>
</div>
<!-- Mobile: full-screen slide panel -->
<div
class="admin-detail-panel compact-orders__mobile-panel"
:class="{ open: isMobile && showDetailPanel && selectedOrder }"
>
<div v-if="selectedOrder" class="admin-detail-panel__inner">
<div class="admin-detail-panel__header">
<button
type="button"
class="admin-detail-panel__back"
aria-label="بازگشت به لیست"
@click="closeDetailPanel"
>
<AdminIcon name="chevron-back" :size="20" />
</button>
<h2 class="admin-detail-panel__title">جزئیات سفارش</h2>
</div>
<div class="admin-detail-panel__body">
<OrderDetailContent
:order="selectedOrder"
:settlement-label="settlementLabel"
:status-saving="statusSaving"
@pricing="handlePricing"
@checks="handleChecks"
@cash-receipt="handleCashReceipt"
@status="changeStatus"
/>
</div>
</div>
</div>
<!-- Tablet / small desktop: modal -->
<BaseModal
:open="isCompactLayout && !isMobile && showDetailModal && !!selectedOrder"
title="جزئیات سفارش"
size="xl"
@close="closeDetailModal"
>
<template v-if="selectedOrder">
<p class="compact-orders__modal-breadcrumb">سفارش‌ها / جزئیات سفارش</p>
<OrderDetailContent
:order="selectedOrder"
:settlement-label="settlementLabel"
:status-saving="statusSaving"
@pricing="handlePricing"
@checks="handleChecks"
@cash-receipt="handleCashReceipt"
@status="changeStatus"
/>
</template>
</BaseModal>
</div>
</template>
<style scoped>
.compact-orders__tabs {
margin-bottom: 16px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 4px 8px 0;
border-bottom: 1px solid var(--color-border);
overflow: visible;
}
.orders-page__search {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
margin-bottom: 12px;
color: var(--color-text-muted);
}
.orders-page__search-input {
flex: 1;
border: none;
background: transparent;
font-size: 0.875rem;
outline: none;
color: var(--color-text);
}
.admin-card-item {
cursor: pointer;
text-align: right;
}
.admin-detail-panel__inner {
display: flex;
flex-direction: column;
height: 100%;
background: var(--color-bg);
}
.compact-orders__modal-breadcrumb {
margin: 0 0 16px;
font-size: 0.8125rem;
color: var(--color-text-muted);
}
@media (min-width: 768px) {
.compact-orders__mobile-panel {
display: none !important;
}
}
</style>