From c4019a0be643e860e180d4c89bc79f3e342deaf0 Mon Sep 17 00:00:00 2001
From: The Nguyen <6950941+treoden@users.noreply.github.com>
Date: Sun, 5 May 2024 17:38:20 +0700
Subject: [PATCH 1/2] Add status and type filter to product grid
---
.../src/components/common/list/Filter.jsx | 83 +++++++++++
.../src/components/common/list/Filter.scss | 39 +++++
.../catalog/pages/admin/productGrid/Grid.jsx | 139 +++++++++++++++---
...registerDefaultProductCollectionFilters.js | 23 +++
4 files changed, 264 insertions(+), 20 deletions(-)
create mode 100644 packages/evershop/src/components/common/list/Filter.jsx
create mode 100644 packages/evershop/src/components/common/list/Filter.scss
diff --git a/packages/evershop/src/components/common/list/Filter.jsx b/packages/evershop/src/components/common/list/Filter.jsx
new file mode 100644
index 000000000..07348d583
--- /dev/null
+++ b/packages/evershop/src/components/common/list/Filter.jsx
@@ -0,0 +1,83 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import './Filter.scss';
+
+export default function Filter({ title, options, selectedOption }) {
+ const [show, setShow] = React.useState(false);
+
+ return (
+
+
+ {show && (
+
+ )}
+
+ );
+}
+
+Filter.propTypes = {
+ title: PropTypes.string,
+ options: PropTypes.arrayOf(
+ PropTypes.shape({
+ label: PropTypes.string,
+ value: PropTypes.string,
+ onSelect: PropTypes.func
+ })
+ ),
+ selectedOption: PropTypes.string
+};
+
+Filter.defaultProps = {
+ title: '',
+ options: [],
+ selectedOption: ''
+};
diff --git a/packages/evershop/src/components/common/list/Filter.scss b/packages/evershop/src/components/common/list/Filter.scss
new file mode 100644
index 000000000..000a1a6bf
--- /dev/null
+++ b/packages/evershop/src/components/common/list/Filter.scss
@@ -0,0 +1,39 @@
+.filter-container {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ position: relative;
+}
+
+.filter-container button {
+ background: none;
+ border-bottom: 1px solid #ccc;
+ color: var(--color-text);
+ font-size: 1.5rem;
+ text-align: left;
+ font-size: 12px;
+ padding: 0 5px;
+ text-transform: capitalize;
+}
+
+.filter-container ul {
+ list-style: none;
+ padding: 10px;
+ position: absolute;
+ background: white;
+ border: 1px solid #ccc;
+ border-radius: 0.5rem;
+ box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1);
+ z-index: 1;
+ top: 100%;
+ left: 0;
+ width: max-content;
+}
+
+.filter-container ul li {
+ padding: 3px 5px;
+ text-transform: capitalize;
+ &:hover {
+ background: #f9f9f9;
+ }
+}
\ No newline at end of file
diff --git a/packages/evershop/src/modules/catalog/pages/admin/productGrid/Grid.jsx b/packages/evershop/src/modules/catalog/pages/admin/productGrid/Grid.jsx
index cce1157bf..d8e58b421 100644
--- a/packages/evershop/src/modules/catalog/pages/admin/productGrid/Grid.jsx
+++ b/packages/evershop/src/modules/catalog/pages/admin/productGrid/Grid.jsx
@@ -1,4 +1,4 @@
-/* eslint-disable react/no-unstable-nested-components */
+/* eslint-disable react/no-unstable-nested-components,no-nested-ternary */
import PropTypes from 'prop-types';
import React, { useState } from 'react';
import axios from 'axios';
@@ -17,6 +17,7 @@ import QtyRow from '@components/admin/catalog/productGrid/rows/QtyRow';
import SortableHeader from '@components/common/grid/headers/Sortable';
import { Form } from '@components/common/form/Form';
import { Field } from '@components/common/form/Field';
+import Filter from '@components/common/list/Filter';
function Actions({ products = [], selectedIds = [] }) {
const { openAlert, closeAlert } = useAlertContext();
@@ -173,32 +174,130 @@ export default function ProductGrid({
- f.key === 'keyword')?.value}
- onKeyPress={(e) => {
- // If the user press enter, we should submit the form
- if (e.key === 'Enter') {
- const url = new URL(document.location);
- const keyword = document.getElementById('keyword')?.value;
- if (keyword) {
- url.searchParams.set('keyword', keyword);
- } else {
- url.searchParams.delete('keyword');
+
+
(
+
f.key === 'keyword')
+ ?.value
+ }
+ onKeyPress={(e) => {
+ // If the user press enter, we should submit the form
+ if (e.key === 'Enter') {
+ const url = new URL(document.location);
+ const keyword =
+ document.getElementById('keyword')?.value;
+ if (keyword) {
+ url.searchParams.set('keyword', keyword);
+ } else {
+ url.searchParams.delete('keyword');
+ }
+ window.location.href = url;
+ }
+ }}
+ />
+ )
+ },
+ sortOrder: 5
+ },
+ {
+ component: {
+ default: () => (
+ {
+ const url = new URL(document.location);
+ url.searchParams.set('status', 1);
+ window.location.href = url;
+ }
+ },
+ {
+ label: 'Disabled',
+ value: '0',
+ onSelect: () => {
+ const url = new URL(document.location);
+ url.searchParams.set('status', 0);
+ window.location.href = url;
+ }
+ }
+ ]}
+ selectedOption={
+ currentFilters.find((f) => f.key === 'status')
+ ? currentFilters.find((f) => f.key === 'status')
+ .value === '1'
+ ? 'Enabled'
+ : 'Disabled'
+ : undefined
+ }
+ title="Status"
+ />
+ )
+ },
+ sortOrder: 10
+ },
+ {
+ component: {
+ default: () => (
+ {
+ const url = new URL(document.location);
+ url.searchParams.set('type', 'simple');
+ window.location.href = url;
+ }
+ },
+ {
+ label: 'Configurable',
+ value: '0',
+ onSelect: () => {
+ const url = new URL(document.location);
+ url.searchParams.set('type', 'configurable');
+ window.location.href = url;
+ }
+ }
+ ]}
+ selectedOption={
+ currentFilters.find((f) => f.key === 'type')
+ ? currentFilters.find((f) => f.key === 'type')
+ .value
+ : undefined
+ }
+ title="Product type"
+ />
+ )
+ },
+ sortOrder: 15
}
- window.location.href = url;
- }
- }}
- />
+ ]}
+ currentFilters={currentFilters}
+ />
+
}
actions={[
{
variant: 'interactive',
name: 'Clear filter',
- onAction: () => {}
+ onAction: () => {
+ const url = new URL(document.location);
+ url.search = '';
+ window.location.href = url.href;
+ }
}
]}
/>
diff --git a/packages/evershop/src/modules/catalog/services/registerDefaultProductCollectionFilters.js b/packages/evershop/src/modules/catalog/services/registerDefaultProductCollectionFilters.js
index 5cc2621ad..8bc1aa527 100644
--- a/packages/evershop/src/modules/catalog/services/registerDefaultProductCollectionFilters.js
+++ b/packages/evershop/src/modules/catalog/services/registerDefaultProductCollectionFilters.js
@@ -117,6 +117,29 @@ module.exports = async function registerDefaultProductCollectionFilters() {
});
}
},
+ {
+ key: 'type',
+ operation: ['eq'],
+ callback: (query, operation, value, currentFilters) => {
+ if (['simple', 'configurable'].includes(value)) {
+ switch (value) {
+ case 'simple':
+ query.andWhere('product.variant_group_id', 'IS NULL', null);
+ break;
+ case 'configurable':
+ query.andWhere('product.variant_group_id', 'IS NOT NULL', null);
+ break;
+ default:
+ break;
+ }
+ currentFilters.push({
+ key: 'type',
+ operation,
+ value
+ });
+ }
+ }
+ },
{
key: 'cat',
operation: ['eq', 'in', 'nin'],
From fbcb0fea4ec203d8520efd3ce4af20293be42d2f Mon Sep 17 00:00:00 2001
From: The Nguyen <6950941+treoden@users.noreply.github.com>
Date: Sun, 5 May 2024 17:51:31 +0700
Subject: [PATCH 2/2] Add payment status and shipment status filter to order
grid
---
.../oms/pages/admin/orderGrid/Grid.jsx | 157 +++++++++++++-----
1 file changed, 119 insertions(+), 38 deletions(-)
diff --git a/packages/evershop/src/modules/oms/pages/admin/orderGrid/Grid.jsx b/packages/evershop/src/modules/oms/pages/admin/orderGrid/Grid.jsx
index 9a7fdc9e7..665f2aca5 100644
--- a/packages/evershop/src/modules/oms/pages/admin/orderGrid/Grid.jsx
+++ b/packages/evershop/src/modules/oms/pages/admin/orderGrid/Grid.jsx
@@ -16,6 +16,7 @@ import CreateAt from '@components/admin/customer/customerGrid/rows/CreateAt';
import { Form } from '@components/common/form/Form';
import { Field } from '@components/common/form/Field';
import SortableHeader from '@components/common/grid/headers/Sortable';
+import Filter from '@components/common/list/Filter';
function Actions({ orders = [], selectedIds = [] }) {
const { openAlert, closeAlert } = useAlertContext();
@@ -102,7 +103,9 @@ Actions.propTypes = {
};
export default function OrderGrid({
- orders: { items: orders, total, currentFilters = [] }
+ orders: { items: orders, total, currentFilters = [] },
+ paymentStatusList,
+ shipmentStatusList
}) {
const page = currentFilters.find((filter) => filter.key === 'page')
? currentFilters.find((filter) => filter.key === 'page').value
@@ -119,41 +122,107 @@ export default function OrderGrid({
- (
- f.key === 'keyword')?.value
- }
- onKeyPress={(e) => {
- // If the user press enter, we should submit the form
- if (e.key === 'Enter') {
- const url = new URL(document.location);
- const keyword =
- document.getElementById('keyword')?.value;
- if (keyword) {
- url.searchParams.set('keyword', keyword);
- } else {
- url.searchParams.delete('keyword');
+
+
(
+
f.key === 'keyword')
+ ?.value
+ }
+ onKeyPress={(e) => {
+ // If the user press enter, we should submit the form
+ if (e.key === 'Enter') {
+ const url = new URL(document.location);
+ const keyword =
+ document.getElementById('keyword')?.value;
+ if (keyword) {
+ url.searchParams.set('keyword', keyword);
+ } else {
+ url.searchParams.delete('keyword');
+ }
+ window.location.href = url;
+ }
+ }}
+ />
+ )
+ },
+ sortOrder: 5
+ },
+ {
+ component: {
+ default: () => (
+ ({
+ label: status.name,
+ value: status.code,
+ onSelect: () => {
+ const url = new URL(document.location);
+ url.searchParams.set(
+ 'payment_status',
+ status.code
+ );
+ window.location.href = url;
}
- window.location.href = url;
+ }))}
+ selectedOption={
+ currentFilters.find(
+ (f) => f.key === 'payment_status'
+ )
+ ? currentFilters.find(
+ (f) => f.key === 'payment_status'
+ ).value
+ : undefined
}
- }}
- />
- )
+ title="Payment status"
+ />
+ )
+ },
+ sortOrder: 10
},
- sortOrder: 10
- }
- ]}
- />
+ {
+ component: {
+ default: () => (
+ ({
+ label: status.name,
+ value: status.code,
+ onSelect: () => {
+ const url = new URL(document.location);
+ url.searchParams.set(
+ 'shipment_status',
+ status.code
+ );
+ window.location.href = url;
+ }
+ }))}
+ selectedOption={
+ currentFilters.find(
+ (f) => f.key === 'shipment_status'
+ )
+ ? currentFilters.find(
+ (f) => f.key === 'shipment_status'
+ ).value
+ : undefined
+ }
+ title="Shipment status"
+ />
+ )
+ },
+ sortOrder: 15
+ }
+ ]}
+ currentFilters={currentFilters}
+ />
+
}
actions={[
@@ -396,12 +465,16 @@ OrderGrid.propTypes = {
})
).isRequired
}).isRequired,
- total: PropTypes.number.isRequired,
- currentFilters: PropTypes.arrayOf(
+ paymentStatusList: PropTypes.arrayOf(
+ PropTypes.shape({
+ code: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ })
+ ).isRequired,
+ shipmentStatusList: PropTypes.arrayOf(
PropTypes.shape({
- key: PropTypes.string.isRequired,
- operation: PropTypes.string.isRequired,
- value: PropTypes.string.isRequired
+ code: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
})
).isRequired
};
@@ -449,6 +522,14 @@ export const query = `
value
}
}
+ paymentStatusList {
+ code
+ name
+ }
+ shipmentStatusList {
+ code
+ name
+ }
}
`;