diff --git a/src/assets/styles/admin.css b/src/assets/styles/admin.css
index 9262ec2..3b7de25 100644
--- a/src/assets/styles/admin.css
+++ b/src/assets/styles/admin.css
@@ -38,7 +38,7 @@
.admin-table th {
padding: 11px 16px;
- text-align: right;
+ text-align: center;
font-weight: 600;
font-size: 0.8125rem;
color: var(--color-text-muted);
@@ -49,6 +49,7 @@
.admin-table td {
padding: 13px 16px;
+ text-align: center;
border-bottom: 1px solid var(--color-border-light);
vertical-align: middle;
color: var(--color-text);
@@ -69,7 +70,7 @@
.admin-table .col-numeric,
.admin-table td.numeric,
.admin-table th.numeric {
- text-align: left;
+ text-align: center;
font-variant-numeric: tabular-nums;
}
@@ -319,6 +320,19 @@
color: var(--color-text);
}
+select.admin-input {
+ appearance: none;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.75' d='m7 10 5 5 5-5'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: left 12px center;
+ padding-inline: 14px 36px;
+ cursor: pointer;
+}
+
+html.dark select.admin-input {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='%94a3b8' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.75' d='m7 10 5 5 5-5'/%3E%3C/svg%3E");
+}
+
.admin-input:focus {
outline: none;
border-color: var(--color-primary);
@@ -633,3 +647,55 @@
grid-template-columns: 1fr 1fr;
gap: 12px;
}
+
+@media print {
+ @page {
+ margin: 12mm;
+ }
+
+ html.printing-order-detail body > *:not(.admin-modal-backdrop) {
+ display: none !important;
+ }
+
+ html.printing-order-detail .admin-modal-backdrop {
+ position: static !important;
+ inset: auto !important;
+ z-index: auto !important;
+ display: block !important;
+ padding: 0 !important;
+ background: transparent !important;
+ }
+
+ html.printing-order-detail .admin-modal {
+ width: 100% !important;
+ max-height: none !important;
+ overflow: visible !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+ background: #fff !important;
+ color: #111 !important;
+ }
+
+ html.printing-order-detail .admin-modal__header {
+ border-bottom-color: #ddd !important;
+ padding: 0 0 12px !important;
+ }
+
+ html.printing-order-detail .admin-modal__close,
+ html.printing-order-detail .admin-modal__footer,
+ html.printing-order-detail .no-print,
+ html.printing-order-detail .orders-page__modal-breadcrumb,
+ html.printing-order-detail .compact-orders__modal-breadcrumb {
+ display: none !important;
+ }
+
+ html.printing-order-detail .admin-modal__body {
+ overflow: visible !important;
+ max-height: none !important;
+ padding: 0 !important;
+ }
+
+ html.printing-order-detail .admin-modal__title {
+ color: #111 !important;
+ }
+}
diff --git a/src/components/ui/AdminSelect.vue b/src/components/ui/AdminSelect.vue
new file mode 100644
index 0000000..00e830b
--- /dev/null
+++ b/src/components/ui/AdminSelect.vue
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/cash-payments/CashPaymentsView.vue b/src/views/cash-payments/CashPaymentsView.vue
index e5fd921..792ab0f 100644
--- a/src/views/cash-payments/CashPaymentsView.vue
+++ b/src/views/cash-payments/CashPaymentsView.vue
@@ -2,6 +2,7 @@
import { computed, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import AdminIcon from '@/components/icons/AdminIcon.vue'
+import AdminSelect from '@/components/ui/AdminSelect.vue'
import { cashPaymentsApi } from '@/services/api'
import { ApiError } from '@/services/http'
import {
@@ -19,6 +20,14 @@ const statusFilter = ref<'all' | CashPayment['status']>('all')
const toast = useToast()
const busyId = ref(null)
+const statusFilterOptions = [
+ { value: 'all', label: 'همه وضعیتها' },
+ { value: 'pending_review', label: 'در انتظار تأیید' },
+ { value: 'approved', label: 'تأیید شده' },
+ { value: 'rejected', label: 'رد شده' },
+ { value: 'expired', label: 'منقضیشده' },
+]
+
const filteredPayments = computed(() => {
let list = paymentList.value
if (statusFilter.value !== 'all') {
@@ -93,13 +102,12 @@ async function rejectPayment(payment: CashPayment) {
placeholder="جستجو در شماره سفارش، مشتری، پیگیری..."
/>
-
+
@@ -221,6 +229,12 @@ async function rejectPayment(payment: CashPayment) {
background: transparent;
font-size: 0.875rem;
outline: none;
+ color: var(--color-text);
+}
+
+.cash-payments-page__status-select {
+ flex: 0 1 220px;
+ width: 220px;
}
.cash-payments-page__table {
diff --git a/src/views/catalogs/CatalogsView.vue b/src/views/catalogs/CatalogsView.vue
index 107facc..ed15641 100644
--- a/src/views/catalogs/CatalogsView.vue
+++ b/src/views/catalogs/CatalogsView.vue
@@ -5,6 +5,7 @@ import BaseSwitch from '@/components/ui/BaseSwitch.vue'
import BaseEmptyState from '@/components/ui/BaseEmptyState.vue'
import ActiveSwitchCell from '@/components/ui/ActiveSwitchCell.vue'
import AdminIcon from '@/components/icons/AdminIcon.vue'
+import AdminSelect from '@/components/ui/AdminSelect.vue'
import { catalogsApi, companiesApi } from '@/services/api'
import { ApiError } from '@/services/http'
import { useToast } from '@/composables/useToast'
@@ -20,6 +21,15 @@ const formError = ref('')
const saving = ref(false)
const toast = useToast()
+const companyFilterOptions = computed(() => [
+ { value: 'all', label: 'همه شرکتها' },
+ ...companies.value.map((company) => ({ value: company.id, label: company.name })),
+])
+
+const companyFormOptions = computed(() =>
+ companies.value.map((company) => ({ value: company.id, label: company.name })),
+)
+
const emptyForm = () => ({
companyId: companies.value[0]?.id ?? '',
code: '',
@@ -173,12 +183,12 @@ function clearFilters() {
placeholder="جستجو بر اساس کد یا نام..."
/>
-
+
{{ formError }}
diff --git a/src/views/checks/ChecksListView.vue b/src/views/checks/ChecksListView.vue
index 4bef4eb..ec4a755 100644
--- a/src/views/checks/ChecksListView.vue
+++ b/src/views/checks/ChecksListView.vue
@@ -2,6 +2,7 @@
import { computed, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import AdminIcon from '@/components/icons/AdminIcon.vue'
+import AdminSelect from '@/components/ui/AdminSelect.vue'
import StatusBadge from '@/components/ui/StatusBadge.vue'
import { checksApi, ordersApi } from '@/services/api'
import { ApiError } from '@/services/http'
@@ -17,6 +18,13 @@ const searchQuery = ref('')
const statusFilter = ref<'all' | Check['status']>('all')
const busyId = ref(null)
+const statusFilterOptions = [
+ { value: 'all', label: 'همه وضعیتها' },
+ { value: 'pending_review', label: 'در انتظار بررسی' },
+ { value: 'approved', label: 'تأیید شده' },
+ { value: 'rejected', label: 'رد شده' },
+]
+
const filteredChecks = computed(() => {
let list = checkList.value
if (statusFilter.value !== 'all') {
@@ -125,12 +133,12 @@ async function updateCheckStatus(check: Check, status: Check['status']) {
placeholder="جستجو در شماره سفارش، مشتری، بانک، صیاد..."
/>
-
+
@@ -293,6 +301,12 @@ async function updateCheckStatus(check: Check, status: Check['status']) {
background: transparent;
outline: none;
font-size: 0.875rem;
+ color: var(--color-text);
+}
+
+.checks-list-page__status-select {
+ flex: 0 1 220px;
+ width: 220px;
}
.checks-list-page__table {
diff --git a/src/views/customers/CustomersView.vue b/src/views/customers/CustomersView.vue
index e8ede4d..df33a69 100644
--- a/src/views/customers/CustomersView.vue
+++ b/src/views/customers/CustomersView.vue
@@ -5,6 +5,7 @@ import BaseSwitch from '@/components/ui/BaseSwitch.vue'
import BaseEmptyState from '@/components/ui/BaseEmptyState.vue'
import ActiveSwitchCell from '@/components/ui/ActiveSwitchCell.vue'
import AdminIcon from '@/components/icons/AdminIcon.vue'
+import AdminSelect from '@/components/ui/AdminSelect.vue'
import { customersApi } from '@/services/api'
import { ApiError } from '@/services/http'
import { useToast } from '@/composables/useToast'
@@ -20,6 +21,24 @@ const formError = ref('')
const saving = ref(false)
const toast = useToast()
+function displayCell(value: string | number | null | undefined): string {
+ if (value == null) return '-'
+ if (typeof value === 'number') return Number.isFinite(value) ? String(value) : '-'
+ const text = value.trim()
+ if (!text || text === '—') return '-'
+ return text
+}
+
+function displayMeterage(value: number | null | undefined): string {
+ if (value == null || !Number.isFinite(value)) return '-'
+ return formatMeterage(value)
+}
+
+const customerTypeOptions = [
+ { value: 'wholesale', label: 'عمدهفروش' },
+ { value: 'retail', label: 'خردهفروش' },
+]
+
const emptyForm = () => ({
name: '',
phone: '',
@@ -154,7 +173,7 @@ function clearSearch() {
v-if="!filteredCustomers.length"
class="admin-card"
icon="customers"
- :title="searchQuery.trim() ? 'نتیجهای یافت نشد' : 'مشتریای ثبت نشده'"
+ :title="searchQuery.trim() ? 'نتیجهای یافت نشد' : 'مشتریان ثبت شده'"
:description="
searchQuery.trim()
? 'عبارت جستجو را تغییر دهید یا فیلترها را پاک کنید.'
@@ -188,13 +207,13 @@ function clearSearch() {
- | {{ customer.name }} |
- {{ customer.phone }} |
- {{ customer.nationalId }} |
- {{ CUSTOMER_TYPE_LABELS[customer.customerType] }} |
- {{ customer.companyName }} |
- {{ customer.orderCount }} |
- {{ formatMeterage(customer.totalMeterage) }} |
+ {{ displayCell(customer.name) }} |
+ {{ displayCell(customer.phone) }} |
+ {{ displayCell(customer.nationalId) }} |
+ {{ displayCell(CUSTOMER_TYPE_LABELS[customer.customerType]) }} |
+ {{ displayCell(customer.companyName) }} |
+ {{ displayCell(customer.orderCount) }} |
+ {{ displayMeterage(customer.totalMeterage) }} |
|
- {{ formatDateTime(customer.registeredAt) }} |
+ {{ displayCell(formatDateTime(customer.registeredAt)) }} |
|