Skip to content

Commit e263d24

Browse files
authored
fix(manager): переключатель черновиков и фильтры в списке заказов (#303)
* fix(manager): show draft orders toggle and grid filter params (#302) Vue orders grid lacked a way to include draft rows despite ms3_order_show_drafts, and column filters were sent without the filter_ prefix expected by the API. * feat(manager): add tooltip for order statistics in Vue orders grid Enhanced the Vue orders grid by adding a tooltip for the order statistics, clarifying that only placed orders are counted, excluding drafts. Updated English and Russian lexicons accordingly. Improved the logic in the OrdersController to handle draft visibility more clearly. * fix(manager): address PR #303 review for orders grid drafts toggle Move the show-drafts checkbox to the grid header next to stats, persist the user choice in localStorage, and add an xPDOQuery type hint on the backend. * fix(manager): align drafts toggle with orders stats block (#303)
1 parent 8348fe2 commit e263d24

5 files changed

Lines changed: 130 additions & 17 deletions

File tree

core/components/minishop3/controllers/mgr/orders.class.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public function loadCustomCssJs()
3838
$this->addJavascript($this->ms3->config['jsUrl'] . 'mgr/orders/orders.wrapper.js');
3939

4040
$config = $this->ms3->config;
41+
$config['order_show_drafts'] = (bool) $this->modx->getOption('ms3_order_show_drafts', null, false);
4142
$this->addHtml('<script>Object.assign(ms3.config, ' . json_encode($config) . ');</script>');
4243

4344
$this->addCss($this->ms3->config['assetsUrl'] . 'css/mgr/vue-dist/primeicons.min.css');

core/components/minishop3/lexicon/en/vue.inc.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,8 @@
489489
$_lang['orders_title'] = 'Orders';
490490
$_lang['orders_month'] = 'Orders';
491491
$_lang['orders_month_sum'] = 'Total sum';
492+
$_lang['orders_stat_tooltip'] = 'Count and sum for placed orders only; drafts are not included';
493+
$_lang['ms3_orders_show_drafts'] = 'Show drafts';
492494
$_lang['order_num'] = 'Number';
493495
$_lang['order_customer'] = 'Customer';
494496
$_lang['order_status'] = 'Status';

core/components/minishop3/lexicon/ru/vue.inc.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,8 @@
489489
$_lang['orders_title'] = 'Заказы';
490490
$_lang['orders_month'] = 'Заказов';
491491
$_lang['orders_month_sum'] = 'На сумму';
492+
$_lang['orders_stat_tooltip'] = 'Количество и сумма по оформленным заказам; черновики не учитываются';
493+
$_lang['ms3_orders_show_drafts'] = 'Показывать черновики';
492494
$_lang['order_num'] = 'Номер';
493495
$_lang['order_customer'] = 'Клиент';
494496
$_lang['order_status'] = 'Статус';

core/components/minishop3/src/Controllers/Api/Manager/OrdersController.php

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,7 @@ public function getList(array $params = []): array
155155
}
156156
}
157157

158-
$showDrafts = $this->modx->getOption('ms3_order_show_drafts', null, false);
159-
if (!$showDrafts) {
160-
$statusDrafts = (int) $this->modx->getOption('ms3_status_draft', null, 1) ?: 1;
161-
$c->where(['status_id:!=' => $statusDrafts]);
162-
}
158+
$this->applyDraftVisibilityFilter($c, $params);
163159

164160
if (!empty($query)) {
165161
if (is_numeric($query)) {
@@ -1552,6 +1548,43 @@ protected function getOrdersStats(array $params = []): array
15521548
];
15531549
}
15541550

1551+
/**
1552+
* Whether draft orders should be included in the manager orders list query.
1553+
*
1554+
* When `show_drafts` is present in request params it overrides `ms3_order_show_drafts`.
1555+
* The Vue orders grid always sends this flag (initialized from ms3.config.order_show_drafts).
1556+
*/
1557+
protected function shouldShowDrafts(array $params): bool
1558+
{
1559+
$default = (bool) $this->modx->getOption('ms3_order_show_drafts', null, false);
1560+
1561+
if (!array_key_exists('show_drafts', $params)) {
1562+
return $default;
1563+
}
1564+
1565+
$value = $params['show_drafts'];
1566+
if ($value === '' || $value === null) {
1567+
return $default;
1568+
}
1569+
1570+
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
1571+
}
1572+
1573+
/**
1574+
* Exclude draft status from getList query unless drafts are explicitly shown.
1575+
*
1576+
* @param \xPDO\Om\xPDOQuery $c Query object
1577+
*/
1578+
protected function applyDraftVisibilityFilter(\xPDO\Om\xPDOQuery $c, array $params): void
1579+
{
1580+
if ($this->shouldShowDrafts($params)) {
1581+
return;
1582+
}
1583+
1584+
$statusDrafts = (int) $this->modx->getOption('ms3_status_draft', null, 1) ?: 1;
1585+
$c->where(['status_id:!=' => $statusDrafts]);
1586+
}
1587+
15551588
/**
15561589
* Apply filter to query
15571590
*

vueManager/src/components/OrdersGrid.vue

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { useLexicon } from '@vuetools/useLexicon'
33
import Button from 'primevue/button'
44
import Card from 'primevue/card'
5+
import Checkbox from 'primevue/checkbox'
56
import Column from 'primevue/column'
67
import ConfirmDialog from 'primevue/confirmdialog'
78
import DataTable from 'primevue/datatable'
@@ -20,6 +21,34 @@ import ActionsColumn from './ActionsColumn.vue'
2021
const toast = useToast()
2122
const { _ } = useLexicon()
2223
24+
const ms3Config = typeof ms3 !== 'undefined' ? ms3.config : null
25+
const SHOW_DRAFTS_STORAGE_KEY = 'ms3_orders_show_drafts'
26+
27+
function readShowDraftsPreference() {
28+
try {
29+
const stored = localStorage.getItem(SHOW_DRAFTS_STORAGE_KEY)
30+
if (stored !== null) {
31+
return stored === '1'
32+
}
33+
} catch {
34+
/* localStorage unavailable */
35+
}
36+
return Boolean(ms3Config?.order_show_drafts)
37+
}
38+
39+
const showDrafts = ref(readShowDraftsPreference())
40+
41+
/** Filter keys sent as direct API params (not filter_ prefix). */
42+
const DIRECT_FILTER_KEYS = new Set([
43+
'query',
44+
'status_id',
45+
'delivery_id',
46+
'payment_id',
47+
'context_key',
48+
'createdon_from',
49+
'createdon_to',
50+
])
51+
2352
// Bulk selection
2453
const {
2554
selectedItems,
@@ -73,13 +102,13 @@ async function loadOrders() {
73102
limit: rows.value,
74103
sort: sortField.value,
75104
dir: sortOrder.value === 1 ? 'ASC' : 'DESC',
105+
show_drafts: showDrafts.value ? 1 : 0,
76106
}
77107
78108
// Apply filter values
79109
Object.keys(filterValues.value).forEach(key => {
80110
const value = filterValues.value[key]
81111
if (value !== null && value !== undefined && value !== '') {
82-
// Handle daterange type
83112
const filterConfig = filters.value[key]
84113
if (filterConfig?.type === 'daterange' && Array.isArray(value)) {
85114
if (value[0]) {
@@ -90,8 +119,10 @@ async function loadOrders() {
90119
}
91120
} else if (filterConfig?.type === 'datepicker' && value) {
92121
params[key] = formatDateForApi(value)
93-
} else {
122+
} else if (DIRECT_FILTER_KEYS.has(key)) {
94123
params[key] = value
124+
} else {
125+
params[`filter_${key}`] = value
95126
}
96127
}
97128
})
@@ -373,6 +404,16 @@ const hasActiveFilters = computed(() => {
373404
return Object.values(filterValues.value).some(v => v !== null && v !== '' && v !== undefined)
374405
})
375406
407+
function toggleShowDrafts() {
408+
try {
409+
localStorage.setItem(SHOW_DRAFTS_STORAGE_KEY, showDrafts.value ? '1' : '0')
410+
} catch {
411+
/* localStorage unavailable */
412+
}
413+
first.value = 0
414+
loadOrders()
415+
}
416+
376417
/**
377418
* Load grid configuration
378419
*/
@@ -547,15 +588,26 @@ onMounted(async () => {
547588
@click="createNewOrder"
548589
/>
549590
</div>
550-
<div class="grid-stats">
551-
<span class="stat-item">
552-
<i class="pi pi-calendar"></i>
553-
{{ _('orders_month') }}: <strong>{{ stats.month_total }}</strong>
554-
</span>
555-
<span class="stat-item">
556-
<i class="pi pi-wallet"></i>
557-
{{ _('orders_month_sum') }}: <strong>{{ stats.month_sum }}</strong>
558-
</span>
591+
<div class="grid-header-right">
592+
<div class="grid-stats" :title="_('orders_stat_tooltip')">
593+
<span class="stat-item">
594+
<i class="pi pi-calendar"></i>
595+
{{ _('orders_month') }}: <strong>{{ stats.month_total }}</strong>
596+
</span>
597+
<span class="stat-item">
598+
<i class="pi pi-wallet"></i>
599+
{{ _('orders_month_sum') }}: <strong>{{ stats.month_sum }}</strong>
600+
</span>
601+
</div>
602+
<div class="show-drafts-toggle">
603+
<Checkbox
604+
v-model="showDrafts"
605+
input-id="orders-show-drafts"
606+
binary
607+
@change="toggleShowDrafts"
608+
/>
609+
<label for="orders-show-drafts">{{ _('ms3_orders_show_drafts') }}</label>
610+
</div>
559611
</div>
560612
</div>
561613
</template>
@@ -819,11 +871,19 @@ onMounted(async () => {
819871
gap: 1rem;
820872
}
821873
874+
.grid-header-right {
875+
display: flex;
876+
align-items: center;
877+
gap: 1.5rem;
878+
margin-left: auto;
879+
font-size: 1rem;
880+
}
881+
822882
.grid-stats {
823883
display: flex;
824884
gap: 1.5rem;
825-
font-size: 0.9rem;
826885
color: var(--ms3-text-muted);
886+
cursor: help;
827887
}
828888
829889
.stat-item {
@@ -876,9 +936,24 @@ onMounted(async () => {
876936
877937
.filter-buttons {
878938
display: flex;
939+
flex-wrap: wrap;
940+
align-items: center;
879941
gap: 0.5rem;
880942
}
881943
944+
.show-drafts-toggle {
945+
display: flex;
946+
align-items: center;
947+
gap: 0.5rem;
948+
white-space: nowrap;
949+
}
950+
951+
.show-drafts-toggle label {
952+
cursor: pointer;
953+
user-select: none;
954+
font-size: 1rem;
955+
}
956+
882957
/* Bulk actions toolbar */
883958
.bulk-actions-bar {
884959
display: flex;

0 commit comments

Comments
 (0)