From 1b90610cec1653091324c4591bc3b87293534f26 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Sat, 7 Aug 2021 20:37:11 +0200 Subject: [PATCH] feat: services quick search. --- server/src/api/controllers/Accounts.ts | 1 + .../src/api/controllers/Contacts/Customers.ts | 3 +- .../src/api/controllers/Contacts/Vendors.ts | 1 + server/src/api/controllers/Expenses.ts | 2 + server/src/api/controllers/Items.ts | 1 + server/src/api/controllers/ManualJournals.ts | 1 + server/src/api/controllers/Purchases/Bills.ts | 1 + .../controllers/Purchases/BillsPayments.ts | 1 + .../api/controllers/Sales/PaymentReceives.ts | 7 +- .../api/controllers/Sales/SalesEstimates.ts | 1 + .../api/controllers/Sales/SalesInvoices.ts | 1 + .../api/controllers/Sales/SalesReceipts.ts | 1 + .../seeds/core/20200810121807_seed_views.js | 81 ------------------- .../core/20200810121808_seed_views_roles.js | 75 ----------------- server/src/interfaces/DynamicFilter.ts | 7 ++ server/src/interfaces/SaleInvoice.ts | 1 + .../DynamicFilter/DynamicFilterAbstructor.ts | 12 ++- .../DynamicFilterAdvancedFilter.ts | 27 +++++++ .../DynamicFilter/DynamicFilterFilterRoles.ts | 24 +----- .../DynamicFilterRoleAbstructor.ts | 27 ++++++- .../lib/DynamicFilter/DynamicFilterSearch.ts | 48 +++++++++++ server/src/models/Account.js | 14 +++- server/src/models/Bill.js | 13 +++ server/src/models/BillPayment.js | 13 +++ server/src/models/Customer.Settings.ts | 23 +++--- server/src/models/Customer.js | 18 +++++ server/src/models/Expense.js | 12 +++ server/src/models/Item.js | 12 +++ server/src/models/ManualJournal.js | 13 +++ server/src/models/ModelSearchable.ts | 18 +++++ server/src/models/PaymentReceive.js | 13 +++ server/src/models/SaleEstimate.js | 13 +++ server/src/models/SaleInvoice.js | 22 ++++- server/src/models/SaleReceipt.js | 13 +++ server/src/models/Vendor.Settings.ts | 10 +++ server/src/models/Vendor.js | 18 +++++ .../DynamicListing/DynamicListFilterRoles.ts | 6 +- .../DynamicListing/DynamicListSearch.ts | 18 +++++ .../DynamicListing/DynamicListService.ts | 12 +++ 39 files changed, 380 insertions(+), 204 deletions(-) delete mode 100644 server/src/database/seeds/core/20200810121807_seed_views.js delete mode 100644 server/src/database/seeds/core/20200810121808_seed_views_roles.js create mode 100644 server/src/lib/DynamicFilter/DynamicFilterAdvancedFilter.ts create mode 100644 server/src/lib/DynamicFilter/DynamicFilterSearch.ts create mode 100644 server/src/models/ModelSearchable.ts create mode 100644 server/src/services/DynamicListing/DynamicListSearch.ts diff --git a/server/src/api/controllers/Accounts.ts b/server/src/api/controllers/Accounts.ts index 845910ac7..0965ba58e 100644 --- a/server/src/api/controllers/Accounts.ts +++ b/server/src/api/controllers/Accounts.ts @@ -139,6 +139,7 @@ export default class AccountsController extends BaseController { query('sort_order').optional().isIn(['desc', 'asc']), query('inactive_mode').optional().isBoolean().toBoolean(), + query('search_keyword').optional({ nullable: true }).isString().trim(), ]; } diff --git a/server/src/api/controllers/Contacts/Customers.ts b/server/src/api/controllers/Contacts/Customers.ts index 01e0c1020..5cca6859f 100644 --- a/server/src/api/controllers/Contacts/Customers.ts +++ b/server/src/api/controllers/Contacts/Customers.ts @@ -122,8 +122,9 @@ export default class CustomersController extends ContactsController { query('stringified_filter_roles').optional().isJSON(), query('inactive_mode').optional().isBoolean().toBoolean(), + query('search_keyword').optional({ nullable: true }).isString().trim() ]; - } + } /** * Creates a new customer. diff --git a/server/src/api/controllers/Contacts/Vendors.ts b/server/src/api/controllers/Contacts/Vendors.ts index f0707bea3..c7b4c40cf 100644 --- a/server/src/api/controllers/Contacts/Vendors.ts +++ b/server/src/api/controllers/Contacts/Vendors.ts @@ -102,6 +102,7 @@ export default class VendorsController extends ContactsController { query('page_size').optional().isNumeric().toInt(), query('inactive_mode').optional().isBoolean().toBoolean(), + query('search_keyword').optional({ nullable: true }).isString().trim() ]; } diff --git a/server/src/api/controllers/Expenses.ts b/server/src/api/controllers/Expenses.ts index 25a7084dd..3e0b6247d 100644 --- a/server/src/api/controllers/Expenses.ts +++ b/server/src/api/controllers/Expenses.ts @@ -182,6 +182,8 @@ export default class ExpensesController extends BaseController { query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), + + query('search_keyword').optional({ nullable: true }).isString().trim(), ]; } diff --git a/server/src/api/controllers/Items.ts b/server/src/api/controllers/Items.ts index 76161095d..f937ca755 100644 --- a/server/src/api/controllers/Items.ts +++ b/server/src/api/controllers/Items.ts @@ -187,6 +187,7 @@ export default class ItemsController extends BaseController { query('stringified_filter_roles').optional().isJSON(), query('inactive_mode').optional().isBoolean().toBoolean(), + query('search_keyword').optional({ nullable: true }).isString().trim(), ]; } diff --git a/server/src/api/controllers/ManualJournals.ts b/server/src/api/controllers/ManualJournals.ts index 9473ec588..d9dc74acd 100644 --- a/server/src/api/controllers/ManualJournals.ts +++ b/server/src/api/controllers/ManualJournals.ts @@ -144,6 +144,7 @@ export default class ManualJournalsController extends BaseController { query('sort_order').optional().isIn(['desc', 'asc']), query('stringified_filter_roles').optional().isJSON(), + query('search_keyword').optional({ nullable: true }).isString().trim(), ]; } diff --git a/server/src/api/controllers/Purchases/Bills.ts b/server/src/api/controllers/Purchases/Bills.ts index 17cf4a9b6..99496771b 100644 --- a/server/src/api/controllers/Purchases/Bills.ts +++ b/server/src/api/controllers/Purchases/Bills.ts @@ -171,6 +171,7 @@ export default class BillsController extends BaseController { query('page_size').optional().isNumeric().toInt(), query('column_sort_by').optional(), query('sort_order').optional().isIn(['desc', 'asc']), + query('search_keyword').optional({ nullable: true }).isString().trim(), ]; } diff --git a/server/src/api/controllers/Purchases/BillsPayments.ts b/server/src/api/controllers/Purchases/BillsPayments.ts index 23c0c6673..db23b7d95 100644 --- a/server/src/api/controllers/Purchases/BillsPayments.ts +++ b/server/src/api/controllers/Purchases/BillsPayments.ts @@ -134,6 +134,7 @@ export default class BillsPayments extends BaseController { query('sort_order').optional().isIn(['desc', 'asc']), query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), + query('search_keyword').optional({ nullable: true }).isString().trim(), ]; } diff --git a/server/src/api/controllers/Sales/PaymentReceives.ts b/server/src/api/controllers/Sales/PaymentReceives.ts index e308cc724..fdb01bec5 100644 --- a/server/src/api/controllers/Sales/PaymentReceives.ts +++ b/server/src/api/controllers/Sales/PaymentReceives.ts @@ -115,12 +115,17 @@ export default class PaymentReceivesController extends BaseController { */ get validatePaymentReceiveList(): ValidationChain[] { return [ - query('view_slug').optional({ nullable: true }).isString().trim(), query('stringified_filter_roles').optional().isJSON(), + + query('view_slug').optional({ nullable: true }).isString().trim(), + query('column_sort_by').optional(), query('sort_order').optional().isIn(['desc', 'asc']), + query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), + + query('search_keyword').optional({ nullable: true }).isString().trim(), ]; } diff --git a/server/src/api/controllers/Sales/SalesEstimates.ts b/server/src/api/controllers/Sales/SalesEstimates.ts index 3d75379f5..995d7ab6c 100644 --- a/server/src/api/controllers/Sales/SalesEstimates.ts +++ b/server/src/api/controllers/Sales/SalesEstimates.ts @@ -135,6 +135,7 @@ export default class SalesEstimatesController extends BaseController { query('sort_order').optional().isIn(['desc', 'asc']), query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), + query('search_keyword').optional({ nullable: true }).isString().trim() ]; } diff --git a/server/src/api/controllers/Sales/SalesInvoices.ts b/server/src/api/controllers/Sales/SalesInvoices.ts index 37d6cdf51..18d779fcd 100644 --- a/server/src/api/controllers/Sales/SalesInvoices.ts +++ b/server/src/api/controllers/Sales/SalesInvoices.ts @@ -135,6 +135,7 @@ export default class SaleInvoicesController extends BaseController { query('sort_order').optional().isIn(['desc', 'asc']), query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), + query('search_keyword').optional({ nullable: true }).isString().trim(), ]; } diff --git a/server/src/api/controllers/Sales/SalesReceipts.ts b/server/src/api/controllers/Sales/SalesReceipts.ts index ed7dd80bf..851376d38 100644 --- a/server/src/api/controllers/Sales/SalesReceipts.ts +++ b/server/src/api/controllers/Sales/SalesReceipts.ts @@ -124,6 +124,7 @@ export default class SalesReceiptsController extends BaseController { query('sort_order').optional().isIn(['desc', 'asc']), query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), + query('search_keyword').optional({ nullable: true }).isString().trim(), ]; } diff --git a/server/src/database/seeds/core/20200810121807_seed_views.js b/server/src/database/seeds/core/20200810121807_seed_views.js deleted file mode 100644 index 0ce08584f..000000000 --- a/server/src/database/seeds/core/20200810121807_seed_views.js +++ /dev/null @@ -1,81 +0,0 @@ -import Container from 'typedi'; -import TenancyService from 'services/Tenancy/TenancyService'; - -exports.up = (knex) => { - const tenancyService = Container.get(TenancyService); - const i18n = tenancyService.i18n(knex.userParams.tenantId); - - // Deletes ALL existing entries - return knex('views').del() - .then(() => { - // Inserts seed entries - return knex('views').insert([ - // Accounts. - { id: 15, name: i18n.__('Inactive'), slug: 'inactive', roles_logic_expression: '1', resource_model: 'Account', predefined: true }, - { id: 1, name: i18n.__('Assets'), slug: 'assets', roles_logic_expression: '1', resource_model: 'Account', predefined: true }, - { id: 2, name: i18n.__('Liabilities'), slug: 'liabilities', roles_logic_expression: '1', resource_model: 'Account', predefined: true }, - { id: 3, name: i18n.__('Equity'), slug: 'equity', roles_logic_expression: '1', resource_model: 'Account', predefined: true }, - { id: 4, name: i18n.__('Income'), slug: 'income', roles_logic_expression: '1', resource_model: 'Account', predefined: true }, - { id: 5, name: i18n.__('Expenses'), slug: 'expenses', roles_logic_expression: '1', resource_model: 'Account', predefined: true }, - - // Items. - { id: 6, name: i18n.__('Services'), slug: 'services', roles_logic_expression: '1', resource_model: 'Item', predefined: true }, - { id: 7, name: i18n.__('Inventory'), slug: 'inventory', roles_logic_expression: '1', resource_model: 'Item', predefined: true }, - { id: 8, name: i18n.__('Non-Inventory'), slug: 'non-inventory', roles_logic_expression: '1', resource_model: 'Item', predefined: true }, - - // Manual Journals - { id: 9, name: i18n.__('Journal'), roles_logic_expression: '1', resource_model: 'ManualJournal', predefined: true }, - { id: 10, name: i18n.__('Credit'), roles_logic_expression: '1', resource_model: 'ManualJournal', predefined: true }, - { id: 11, name: i18n.__('Reconciliation'), roles_logic_expression: '1', resource_model: 'ManualJournal', predefined: true }, - - // Expenses. - { id: 12, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'Expense', predefined: true, }, - { id: 13, name: i18n.__('Published'), slug: 'published', roles_logic_expression: '1', resource_model: 'Expense', predefined: true, }, - - // Sales invoices. - { id: 16, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true, }, - { id: 17, name: i18n.__('Delivered'), slug: 'delivered', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true }, - { id: 18, name: i18n.__('Unpaid'), slug: 'unpaid', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true }, - { id: 19, name: i18n.__('Overdue'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true }, - { id: 20, name: i18n.__('Partially paid'), slug: 'partially-paid', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true }, - { id: 21, name: i18n.__('Paid'), slug: 'paid', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true }, - - // Bills. - { id: 22, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'Bill', predefined: true, }, - { id: 23, name: i18n.__('Opened'), slug: 'opened', roles_logic_expression: '1', resource_model: 'Bill', predefined: true }, - { id: 24, name: i18n.__('Unpaid'), slug: 'unpaid', roles_logic_expression: '1', resource_model: 'Bill', predefined: true }, - { id: 25, name: i18n.__('Overdue'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'Bill', predefined: true }, - { id: 26, name: i18n.__('Partially paid'), slug: 'partially-paid', roles_logic_expression: '1', resource_model: 'Bill', predefined: true }, - { id: 27, name: i18n.__('Paid'), slug: 'paid', roles_logic_expression: '1', resource_model: 'Bill', predefined: true }, - - // Sale estimate. - { id: 28, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true }, - { id: 29, name: i18n.__('Delivered'), slug: 'delivered', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true }, - { id: 30, name: i18n.__('Approved'), slug: 'approved', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true }, - { id: 31, name: i18n.__('Rejected'), slug: 'rejected', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true }, - { id: 32, name: i18n.__('Invoiced'), slug: 'invoiced', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true }, - { id: 33, name: i18n.__('Expired'), slug: 'expired', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true }, - - // Sale receipts. - { id: 34, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'SaleReceipt', predefined: true }, - { id: 35, name: i18n.__('Closed'), slug: 'closed', roles_logic_expression: '1', resource_model: 'SaleReceipt', predefined: true }, - - // Customers - { id: 36, name: i18n.__('Active'), slug: 'active', roles_logic_expression: '1', resource_model: 'Customer', predefined: true }, - { id: 37, name: i18n.__('Inactive'), slug: 'inactive', roles_logic_expression: '1', resource_model: 'Customer', predefined: true }, - { id: 38, name: i18n.__('Overdue'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'Customer', predefined: true }, - { id: 39, name: i18n.__('Unpaid'), slug: 'inpaid', roles_logic_expression: '1', resource_model: 'Customer', predefined: true }, - - // Vendors - { id: 40, name: i18n.__('Active'), slug: 'active', roles_logic_expression: '1', resource_model: 'Vendor', predefined: true }, - { id: 41, name: i18n.__('Inactive'), slug: 'inactive', roles_logic_expression: '1', resource_model: 'Vendor', predefined: true }, - { id: 42, name: i18n.__('Overdue'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'Vendor', predefined: true }, - { id: 43, name: i18n.__('Unpaid'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'Vendor', predefined: true }, - ]); - }); -}; - - -exports.down = (knex) => { - -}; \ No newline at end of file diff --git a/server/src/database/seeds/core/20200810121808_seed_views_roles.js b/server/src/database/seeds/core/20200810121808_seed_views_roles.js deleted file mode 100644 index 58d88ee66..000000000 --- a/server/src/database/seeds/core/20200810121808_seed_views_roles.js +++ /dev/null @@ -1,75 +0,0 @@ - -exports.up = (knex) => { - // Deletes ALL existing entries - return knex('view_roles').del() - .then(() => { - // Inserts seed entries - return knex('view_roles').insert([ - // Accounts - { field_key: 'root_type', index: 1, comparator: 'equals', value: 'asset', view_id: 1 }, - { field_key: 'root_type', index: 1, comparator: 'equals', value: 'liability', view_id: 2 }, - { field_key: 'root_type', index: 1, comparator: 'equals', value: 'equity', view_id: 3 }, - { field_key: 'root_type', index: 1, comparator: 'equals', value: 'income', view_id: 4 }, - { field_key: 'root_type', index: 1, comparator: 'equals', value: 'expense', view_id: 5 }, - { field_key: 'active', index: 1, comparator: 'is', value: 1, view_id: 15 }, - - // Items. - { field_key: 'type', index: 1, comparator: 'equals', value: 'service', view_id: 6 }, - { field_key: 'type', index: 1, comparator: 'equals', value: 'inventory', view_id: 7 }, - { field_key: 'type', index: 1, comparator: 'equals', value: 'non-inventory', view_id: 8 }, - - // Expenses. - { field_key: 'status', index: 1, comparator: 'equals', value: 'draft', view_id: 12 }, - { field_key: 'status', index: 1, comparator: 'equals', value: 'published', view_id: 13 }, - - // Manual Journals. - { field_key: 'journal_type', index: 1, comparator: 'equals', value: 'Journal', view_id: 9 }, - { field_key: 'journal_type', index: 1, comparator: 'equals', value: 'CreditNote', view_id: 10 }, - { field_key: 'journal_type', index: 1, comparator: 'equals', value: 'Reconciliation', view_id: 11 }, - - // Sale invoice. - { field_key: 'status', index: 1, comparator: 'is', value: 'draft', view_id: 16 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'delivered', view_id: 17 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'unpaid', view_id: 18 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'overdue', view_id: 19 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'partially-paid', view_id: 20 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'paid', view_id: 21 }, - - // Bills - { field_key: 'status', index: 1, comparator: 'is', value: 'draft', view_id: 22 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'opened', view_id: 23 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'unpaid', view_id: 24 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'overdue', view_id: 25 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'partially-paid', view_id: 26 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'paid', view_id: 27 }, - - // Sale estimates - { field_key: 'status', index: 1, comparator: 'is', value: 'draft', view_id: 28 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'delivered', view_id: 29 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'approved', view_id: 30 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'rejected', view_id: 31 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'invoiced', view_id: 32 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'expired', view_id: 33 }, - - // Sale receipts. - { field_key: 'status', index: 1, comparator: 'is', value: 'draft', view_id: 34 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'closed', view_id: 35 }, - - // Customers - { field_key: 'status', index: 1, comparator: 'is', value: 'active', view_id: 36 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'inactive', view_id: 37 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'overdue', view_id: 38 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'unpaid', view_id: 39 }, - - // Vendors - { field_key: 'status', index: 1, comparator: 'is', value: 'active', view_id: 40 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'inactive', view_id: 41 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'overdue', view_id: 42 }, - { field_key: 'status', index: 1, comparator: 'is', value: 'unpaid', view_id: 43 }, - ]); - }); -}; - -exports.down = (knex) => { - -} \ No newline at end of file diff --git a/server/src/interfaces/DynamicFilter.ts b/server/src/interfaces/DynamicFilter.ts index 74115e92e..674328fb9 100644 --- a/server/src/interfaces/DynamicFilter.ts +++ b/server/src/interfaces/DynamicFilter.ts @@ -19,6 +19,7 @@ export interface IDynamicListFilter { columnSortBy: ISortOrder; sortOrder: string; stringifiedFilterRoles: string; + searchKeyword?: string; } export interface IDynamicListService { @@ -29,3 +30,9 @@ export interface IDynamicListService { ): Promise; handlerErrorsToResponse(error, req, res, next): void; } + +// Search role. +export interface ISearchRole { + fieldKey: string; + comparator: string; +} \ No newline at end of file diff --git a/server/src/interfaces/SaleInvoice.ts b/server/src/interfaces/SaleInvoice.ts index b2def5c46..adc7d9e85 100644 --- a/server/src/interfaces/SaleInvoice.ts +++ b/server/src/interfaces/SaleInvoice.ts @@ -40,6 +40,7 @@ export interface ISaleInvoiceEditDTO extends ISaleInvoiceDTO {} export interface ISalesInvoicesFilter extends IDynamicListFilter { page: number; pageSize: number; + searchKeyword?: string; } export interface ISalesInvoicesService { diff --git a/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts b/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts index 45abe81e1..3b3f6dee1 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts @@ -1,5 +1,3 @@ -import { IModel, IFilterRole } from 'interfaces'; -import { FIELD_TYPE } from './constants'; export default class DynamicFilterAbstructor { /** @@ -25,6 +23,11 @@ export default class DynamicFilterAbstructor { }); }; + /** + * Builds join queries of fields. + * @param builder - + * @param {string[]} fieldsRelations - + */ private buildFieldsJoinQueries = (builder, fieldsRelations: string[]) => { fieldsRelations.forEach((fieldRelation) => { const relation = this.model.relationMappings[fieldRelation]; @@ -38,7 +41,10 @@ export default class DynamicFilterAbstructor { }); }; - getModel() { + /** + * Retrieve the dynamic filter mode. + */ + protected getModel() { return this.model; } } diff --git a/server/src/lib/DynamicFilter/DynamicFilterAdvancedFilter.ts b/server/src/lib/DynamicFilter/DynamicFilterAdvancedFilter.ts new file mode 100644 index 000000000..f3dd5c79b --- /dev/null +++ b/server/src/lib/DynamicFilter/DynamicFilterAdvancedFilter.ts @@ -0,0 +1,27 @@ +import { IFilterRole } from 'interfaces'; +import DynamicFilterFilterRoles from './DynamicFilterFilterRoles'; + +export default class DynamicFilterAdvancedFilter extends DynamicFilterFilterRoles { + private filterRoles: IFilterRole[]; + + /** + * Constructor method. + * @param {Array} filterRoles - + * @param {Array} resourceFields - + */ + constructor(filterRoles: IFilterRole[]) { + super(); + + this.filterRoles = filterRoles; + this.setResponseMeta(); + } + + /** + * Sets response meta. + */ + private setResponseMeta() { + this.responseMeta = { + filterRoles: this.filterRoles, + }; + } +} diff --git a/server/src/lib/DynamicFilter/DynamicFilterFilterRoles.ts b/server/src/lib/DynamicFilter/DynamicFilterFilterRoles.ts index fb8374232..0588af494 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterFilterRoles.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterFilterRoles.ts @@ -5,19 +5,10 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor { private filterRoles: IFilterRole[]; /** - * Constructor method. - * @param {Array} filterRoles - - * @param {Array} resourceFields - + * On initialize filter roles. */ - constructor(filterRoles: IFilterRole[]) { - super(); - - this.filterRoles = filterRoles; - - this.setResponseMeta(); - } - public onInitialize() { + super.onInitialize(); this.setFilterRolesRelations(); } @@ -27,7 +18,7 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor { */ private buildLogicExpression(): string { let expression = ''; - + this.filterRoles.forEach((role, index) => { expression += index === 0 ? `${role.index} ` : `${role.condition} ${role.index} `; @@ -50,15 +41,6 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor { }; } - /** - * Sets response meta. - */ - private setResponseMeta() { - this.responseMeta = { - filterRoles: this.filterRoles, - }; - } - /** * Sets filter roles relations if field was relation type. */ diff --git a/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts b/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts index 83bc2566f..a73c20674 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts @@ -1,4 +1,5 @@ import moment from 'moment'; +import * as R from 'ramda'; import { IFilterRole, IDynamicFilter, IModel } from 'interfaces'; import { Lexer } from 'lib/LogicEvaluation/Lexer'; import Parser from 'lib/LogicEvaluation/Parser'; @@ -63,6 +64,18 @@ export default abstract class DynamicFilterAbstructor return queryParser.parse(); }; + /** + * Parses the logic expression to base expression. + * @param {string} logicExpression - + * @return {string} + */ + private parseLogicExpression(logicExpression: string): string { + return R.compose( + R.replace(/or|OR/g, '||'), + R.replace(/and|AND/g, '&&'), + )(logicExpression); + } + /** * Builds filter query for query builder. * @param {String} tableName - Table name. @@ -74,8 +87,10 @@ export default abstract class DynamicFilterAbstructor roles: IFilterRole[], logicExpression: string ) => { + const basicExpression = this.parseLogicExpression(logicExpression); + return (builder) => { - this.buildFilterRolesQuery(model, roles, logicExpression)(builder); + this.buildFilterRolesQuery(model, roles, basicExpression)(builder); }; }; @@ -347,11 +362,15 @@ export default abstract class DynamicFilterAbstructor } }; + /** + * Retrieve the model. + */ getModel() { return this.model; } - onInitialize() { - - } + /** + * On initialize the registered dynamic filter. + */ + onInitialize() {} } diff --git a/server/src/lib/DynamicFilter/DynamicFilterSearch.ts b/server/src/lib/DynamicFilter/DynamicFilterSearch.ts new file mode 100644 index 000000000..7c010882a --- /dev/null +++ b/server/src/lib/DynamicFilter/DynamicFilterSearch.ts @@ -0,0 +1,48 @@ +import { IFilterRole } from 'interfaces'; +import DynamicFilterFilterRoles from './DynamicFilterFilterRoles'; + +export default class DynamicFilterSearch extends DynamicFilterFilterRoles { + private searchKeyword: string; + private filterRoles: IFilterRole[]; + + /** + * Constructor method. + * @param {string} searchKeyword - Search keyword. + */ + constructor(searchKeyword: string) { + super(); + this.searchKeyword = searchKeyword; + } + + /** + * On initialize the dynamic filter. + */ + public onInitialize() { + super.onInitialize(); + this.filterRoles = this.getModelSearchFilterRoles(this.searchKeyword); + } + + /** + * Retrieve the filter roles from model search roles. + * @param {string} searchKeyword + * @returns {IFilterRole[]} + */ + private getModelSearchFilterRoles(searchKeyword: string): IFilterRole[] { + const model = this.getModel(); + + return model.searchRoles.map((searchRole, index) => ({ + ...searchRole, + value: searchKeyword, + index: index + 1, + })); + } + + /** + * + */ + setResponseMeta() { + this.responseMeta = { + searchKeyword: this.searchKeyword, + }; + } +} diff --git a/server/src/models/Account.js b/server/src/models/Account.js index 3df685151..6367a6b71 100644 --- a/server/src/models/Account.js +++ b/server/src/models/Account.js @@ -11,10 +11,12 @@ import ModelSettings from './ModelSetting'; import { ACCOUNT_TYPES } from 'data/AccountTypes'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Accounts/constants'; +import ModelSearchable from './ModelSearchable'; export default class Account extends mixin(TenantModel, [ ModelSettings, CustomViewBaseModel, + ModelSearchable, ]) { /** * Table name. @@ -257,7 +259,17 @@ export default class Account extends mixin(TenantModel, [ /** * Retrieve the default custom views, roles and columns. */ - static get defaultViews() { + static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search roles. + */ + static get searchRoles() { + return [ + { condition: 'or', fieldKey: 'name', comparator: 'contains' }, + { condition: 'or', fieldKey: 'code', comparator: 'like' }, + ]; + } } diff --git a/server/src/models/Bill.js b/server/src/models/Bill.js index 52150454c..c7527e0aa 100644 --- a/server/src/models/Bill.js +++ b/server/src/models/Bill.js @@ -6,10 +6,12 @@ import BillSettings from './Bill.Settings'; import ModelSetting from './ModelSetting'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Purchases/constants'; +import ModelSearchable from './ModelSearchable'; export default class Bill extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable ]) { /** * Table name @@ -315,4 +317,15 @@ export default class Bill extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search attributes. + */ + static get searchRoles() { + return [ + { fieldKey: 'bill_number', comparator: 'contains' }, + { condition: 'or', fieldKey: 'reference_no', comparator: 'contains' }, + { condition: 'or', fieldKey: 'amount', comparator: 'equals' }, + ]; + } } diff --git a/server/src/models/BillPayment.js b/server/src/models/BillPayment.js index 9557b478b..846ead395 100644 --- a/server/src/models/BillPayment.js +++ b/server/src/models/BillPayment.js @@ -4,10 +4,12 @@ import ModelSetting from './ModelSetting'; import BillPaymentSettings from './BillPayment.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Sales/PaymentReceives/constants'; +import ModelSearchable from './ModelSearchable'; export default class BillPayment extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable, ]) { /** * Table name @@ -90,4 +92,15 @@ export default class BillPayment extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search attributes. + */ + static get searchRoles() { + return [ + { fieldKey: 'payment_number', comparator: 'contains' }, + { condition: 'or', fieldKey: 'reference_no', comparator: 'contains' }, + { condition: 'or', fieldKey: 'amount', comparator: 'equals' }, + ]; + } } diff --git a/server/src/models/Customer.Settings.ts b/server/src/models/Customer.Settings.ts index ec517c23d..59e26cf57 100644 --- a/server/src/models/Customer.Settings.ts +++ b/server/src/models/Customer.Settings.ts @@ -1,69 +1,67 @@ export default { fields: { + first_name: { + name: 'First name', + column: 'first_name', + fieldType: 'text', + }, + last_name: { + name: 'Last name', + column: 'last_name', + fieldType: 'text', + }, display_name: { name: 'Display name', column: 'display_name', fieldType: 'text', - columnable: true, }, email: { name: 'Email', column: 'email', fieldType: 'text', - columnable: true, }, work_phone: { name: 'Work phone', column: 'work_phone', fieldType: 'text', - columnable: true, }, personal_phone: { name: 'Personal phone', column: 'personal_phone', fieldType: 'text', - columnable: true, }, company_name: { name: 'Company name', column: 'company_name', fieldType: 'text', - columnable: true, }, website: { name: 'Website', column: 'website', fieldType: 'text', - columnable: true, }, created_at: { name: 'Created at', column: 'created_at', - fieldType: 'date', - columnable: true, }, balance: { name: 'Balance', column: 'balance', fieldType: 'number', - columnable: true, }, opening_balance: { name: 'Opening balance', column: 'opening_balance', fieldType: 'number', - columnable: true, }, opening_balance_at: { name: 'Opening balance at', column: 'opening_balance_at', filterable: false, fieldType: 'date', - columnable: true, }, currency_code: { column: 'currency_code', - columnable: true, fieldType: 'text', }, status: { @@ -74,7 +72,6 @@ export default { { key: 'overdue', label: 'Overdue' }, { key: 'unpaid', label: 'Unpaid' }, ], - columnable: true, filterCustomQuery: statusFieldFilterQuery, }, }, diff --git a/server/src/models/Customer.js b/server/src/models/Customer.js index 6692a0aef..d2e213d8c 100644 --- a/server/src/models/Customer.js +++ b/server/src/models/Customer.js @@ -5,6 +5,7 @@ import ModelSetting from './ModelSetting'; import CustomerSettings from './Customer.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Contacts/Customers/constants'; +import ModelSearchable from './ModelSearchable'; class CustomerQueryBuilder extends PaginationQueryBuilder { constructor(...args) { @@ -21,6 +22,7 @@ class CustomerQueryBuilder extends PaginationQueryBuilder { export default class Customer extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable ]) { /** * Query builder. @@ -149,4 +151,20 @@ export default class Customer extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search attributes. + */ + static get searchRoles() { + return [ + { fieldKey: 'display_name', comparator: 'contains' }, + { condition: 'or', fieldKey: 'first_name', comparator: 'contains' }, + { condition: 'or', fieldKey: 'last_name', comparator: 'equals' }, + { condition: 'or', fieldKey: 'company_name', comparator: 'equals' }, + { condition: 'or', fieldKey: 'email', comparator: 'equals' }, + { condition: 'or', fieldKey: 'work_phone', comparator: 'equals' }, + { condition: 'or', fieldKey: 'personal_phone', comparator: 'equals' }, + { condition: 'or', fieldKey: 'website', comparator: 'equals' }, + ]; + } } diff --git a/server/src/models/Expense.js b/server/src/models/Expense.js index 041f9046b..eed7ee38d 100644 --- a/server/src/models/Expense.js +++ b/server/src/models/Expense.js @@ -5,10 +5,12 @@ import ModelSetting from './ModelSetting'; import ExpenseSettings from './Expense.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Expenses/constants'; +import ModelSearchable from './ModelSearchable'; export default class Expense extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable, ]) { /** * Table name @@ -160,4 +162,14 @@ export default class Expense extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search attributes. + */ + static get searchRoles() { + return [ + { fieldKey: 'reference_no', comparator: 'contains' }, + { condition: 'or', fieldKey: 'amount', comparator: 'equals' }, + ]; + } } diff --git a/server/src/models/Item.js b/server/src/models/Item.js index 12e661da7..38d75eba6 100644 --- a/server/src/models/Item.js +++ b/server/src/models/Item.js @@ -5,10 +5,12 @@ import ItemSettings from './Item.Settings'; import ModelSetting from './ModelSetting'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Items/constants'; +import ModelSearchable from './ModelSearchable'; export default class Item extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable, ]) { /** * Table name @@ -128,4 +130,14 @@ export default class Item extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search roles. + */ + static get searchRoles() { + return [ + { fieldKey: 'name', comparator: 'contains' }, + { condition: 'or', fieldKey: 'code', comparator: 'like' }, + ]; + } } diff --git a/server/src/models/ManualJournal.js b/server/src/models/ManualJournal.js index 43145c85b..0de5ac305 100644 --- a/server/src/models/ManualJournal.js +++ b/server/src/models/ManualJournal.js @@ -5,9 +5,11 @@ import ModelSetting from './ModelSetting'; import ManualJournalSettings from './ManualJournal.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/ManualJournals/constants'; +import ModelSearchable from './ModelSearchable'; export default class ManualJournal extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable, ]) { /** * Table name. @@ -115,4 +117,15 @@ export default class ManualJournal extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search attributes. + */ + static get searchRoles() { + return [ + { fieldKey: 'journal_number', comparator: 'contains' }, + { condition: 'or', fieldKey: 'reference', comparator: 'contains' }, + { condition: 'or', fieldKey: 'amount', comparator: 'equals' }, + ]; + } } diff --git a/server/src/models/ModelSearchable.ts b/server/src/models/ModelSearchable.ts new file mode 100644 index 000000000..d6b7e4017 --- /dev/null +++ b/server/src/models/ModelSearchable.ts @@ -0,0 +1,18 @@ +import { IModelMeta, ISearchRole } from 'interfaces'; + +export default (Model) => + class ModelSearchable extends Model { + /** + * Searchable model. + */ + static get searchable(): IModelMeta { + throw true; + } + + /** + * Search roles. + */ + static get searchRoles(): ISearchRole[] { + return []; + } + }; diff --git a/server/src/models/PaymentReceive.js b/server/src/models/PaymentReceive.js index 0277762f8..ace14b831 100644 --- a/server/src/models/PaymentReceive.js +++ b/server/src/models/PaymentReceive.js @@ -4,10 +4,12 @@ import ModelSetting from './ModelSetting'; import PaymentReceiveSettings from './PaymentReceive.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Sales/PaymentReceives/constants'; +import ModelSearchable from './ModelSearchable'; export default class PaymentReceive extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable ]) { /** * Table name. @@ -94,4 +96,15 @@ export default class PaymentReceive extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search attributes. + */ + static get searchRoles() { + return [ + { fieldKey: 'payment_receive_no', comparator: 'contains' }, + { condition: 'or', fieldKey: 'reference_no', comparator: 'contains' }, + { condition: 'or', fieldKey: 'amount', comparator: 'equals' }, + ]; + } } diff --git a/server/src/models/SaleEstimate.js b/server/src/models/SaleEstimate.js index 6df889b28..547f3f5c2 100644 --- a/server/src/models/SaleEstimate.js +++ b/server/src/models/SaleEstimate.js @@ -6,10 +6,12 @@ import SaleEstimateSettings from './SaleEstimate.Settings'; import ModelSetting from './ModelSetting'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Sales/Estimates/constants'; +import ModelSearchable from './ModelSearchable'; export default class SaleEstimate extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable, ]) { /** * Table name @@ -210,4 +212,15 @@ export default class SaleEstimate extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search roles. + */ + static get searchRoles() { + return [ + { fieldKey: 'amount', comparator: 'equals' }, + { condition: 'or', fieldKey: 'estimate_number', comparator: 'contains' }, + { condition: 'or', fieldKey: 'reference_no', comparator: 'contains' }, + ]; + } } diff --git a/server/src/models/SaleInvoice.js b/server/src/models/SaleInvoice.js index bec1be16a..938e97f99 100644 --- a/server/src/models/SaleInvoice.js +++ b/server/src/models/SaleInvoice.js @@ -5,10 +5,12 @@ import ModelSetting from './ModelSetting'; import SaleInvoiceMeta from './SaleInvoice.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Sales/constants'; +import ModelSearchable from './ModelSearchable'; export default class SaleInvoice extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable, ]) { /** * Table name @@ -371,4 +373,22 @@ export default class SaleInvoice extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } -} + + /** + * Model searchable. + */ + static get searchable() { + return true; + } + + /** + * Model search attributes. + */ + static get searchRoles() { + return [ + { fieldKey: 'invoice_no', comparator: 'contains' }, + { condition: 'or', fieldKey: 'reference_no', comparator: 'contains' }, + { condition: 'or', fieldKey: 'amount', comparator: 'equals' }, + ]; + } +} \ No newline at end of file diff --git a/server/src/models/SaleReceipt.js b/server/src/models/SaleReceipt.js index 85a0a945d..4925122f6 100644 --- a/server/src/models/SaleReceipt.js +++ b/server/src/models/SaleReceipt.js @@ -4,10 +4,12 @@ import ModelSetting from './ModelSetting'; import SaleReceiptSettings from './SaleReceipt.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Sales/Receipts/constants'; +import ModelSearchable from './ModelSearchable'; export default class SaleReceipt extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable, ]) { /** * Table name @@ -159,4 +161,15 @@ export default class SaleReceipt extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search attributes. + */ + static get searchRoles() { + return [ + { fieldKey: 'receipt_number', comparator: 'contains' }, + { condition: 'or', fieldKey: 'reference_no', comparator: 'contains' }, + { condition: 'or', fieldKey: 'amount', comparator: 'equals' }, + ]; + } } diff --git a/server/src/models/Vendor.Settings.ts b/server/src/models/Vendor.Settings.ts index 18443316a..d9186b8c1 100644 --- a/server/src/models/Vendor.Settings.ts +++ b/server/src/models/Vendor.Settings.ts @@ -5,6 +5,16 @@ export default { sortField: 'created_at', }, fields: { + first_name: { + name: 'First name', + column: 'first_name', + fieldType: 'text', + }, + last_name: { + name: 'Last name', + column: 'last_name', + fieldType: 'text', + }, 'display_name': { name: 'Display name', column: 'display_name', diff --git a/server/src/models/Vendor.js b/server/src/models/Vendor.js index 1f8210002..ecc90e607 100644 --- a/server/src/models/Vendor.js +++ b/server/src/models/Vendor.js @@ -5,6 +5,7 @@ import ModelSetting from './ModelSetting'; import VendorSettings from './Vendor.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; import { DEFAULT_VIEWS } from 'services/Contacts/Vendors/constants'; +import ModelSearchable from './ModelSearchable'; class VendorQueryBuilder extends PaginationQueryBuilder { constructor(...args) { @@ -21,6 +22,7 @@ class VendorQueryBuilder extends PaginationQueryBuilder { export default class Vendor extends mixin(TenantModel, [ ModelSetting, CustomViewBaseModel, + ModelSearchable ]) { /** * Query builder. @@ -148,4 +150,20 @@ export default class Vendor extends mixin(TenantModel, [ static get defaultViews() { return DEFAULT_VIEWS; } + + /** + * Model search attributes. + */ + static get searchRoles() { + return [ + { fieldKey: 'display_name', comparator: 'contains' }, + { condition: 'or', fieldKey: 'first_name', comparator: 'contains' }, + { condition: 'or', fieldKey: 'last_name', comparator: 'equals' }, + { condition: 'or', fieldKey: 'company_name', comparator: 'equals' }, + { condition: 'or', fieldKey: 'email', comparator: 'equals' }, + { condition: 'or', fieldKey: 'work_phone', comparator: 'equals' }, + { condition: 'or', fieldKey: 'personal_phone', comparator: 'equals' }, + { condition: 'or', fieldKey: 'website', comparator: 'equals' }, + ]; + } } diff --git a/server/src/services/DynamicListing/DynamicListFilterRoles.ts b/server/src/services/DynamicListing/DynamicListFilterRoles.ts index c79944b27..23e7f5f53 100644 --- a/server/src/services/DynamicListing/DynamicListFilterRoles.ts +++ b/server/src/services/DynamicListing/DynamicListFilterRoles.ts @@ -3,7 +3,7 @@ import * as R from 'ramda'; import validator from 'is-my-json-valid'; import { IFilterRole, IModel } from 'interfaces'; import DynamicListAbstruct from './DynamicListAbstruct'; -import DynamicFilterFilterRoles from 'lib/DynamicFilter/DynamicFilterFilterRoles'; +import DynamicFilterAdvancedFilter from 'lib/DynamicFilter/DynamicFilterAdvancedFilter'; import { ERRORS } from './constants'; import { ServiceError } from 'exceptions'; @@ -88,7 +88,7 @@ export default class DynamicListFilterRoles extends DynamicListAbstruct { public dynamicList = ( model: IModel, filterRoles: IFilterRole[] - ): DynamicFilterFilterRoles => { + ): DynamicFilterAdvancedFilter => { const filterRolesParsed = R.compose(this.incrementFilterRolesIndex)( filterRoles ); @@ -98,6 +98,6 @@ export default class DynamicListFilterRoles extends DynamicListAbstruct { // Validate the model resource fields. this.validateFilterRolesFieldsExistance(model, filterRoles); - return new DynamicFilterFilterRoles(filterRolesParsed); + return new DynamicFilterAdvancedFilter(filterRolesParsed); }; } diff --git a/server/src/services/DynamicListing/DynamicListSearch.ts b/server/src/services/DynamicListing/DynamicListSearch.ts new file mode 100644 index 000000000..b18f22e59 --- /dev/null +++ b/server/src/services/DynamicListing/DynamicListSearch.ts @@ -0,0 +1,18 @@ +import { Service } from 'typedi'; +import { IFilterRole, IModel } from 'interfaces'; +import DynamicListAbstruct from './DynamicListAbstruct'; +import DynamicFilterFilterRoles from 'lib/DynamicFilter/DynamicFilterFilterRoles'; +import DynamicFilterSearch from 'lib/DynamicFilter/DynamicFilterSearch'; + +@Service() +export default class DynamicListSearch extends DynamicListAbstruct { + /** + * Dynamic list filter roles. + * @param {IModel} model + * @param {IFilterRole[]} filterRoles + * @returns {DynamicFilterFilterRoles} + */ + public dynamicSearch = (model: IModel, searchKeyword: string) => { + return new DynamicFilterSearch(searchKeyword); + }; +} diff --git a/server/src/services/DynamicListing/DynamicListService.ts b/server/src/services/DynamicListing/DynamicListService.ts index 40e4f4b35..7a2462816 100644 --- a/server/src/services/DynamicListing/DynamicListService.ts +++ b/server/src/services/DynamicListing/DynamicListService.ts @@ -13,6 +13,7 @@ import TenancyService from 'services/Tenancy/TenancyService'; import DynamicListFilterRoles from './DynamicListFilterRoles'; import DynamicListSortBy from './DynamicListSortBy'; import DynamicListCustomView from './DynamicListCustomView'; +import DynamicListSearch from './DynamicListSearch'; @Service() export default class DynamicListService implements IDynamicListService { @@ -28,6 +29,9 @@ export default class DynamicListService implements IDynamicListService { @Inject() dynamicListView: DynamicListCustomView; + @Inject() + dynamicListSearch: DynamicListSearch; + /** * Parses filter DTO. * @param {IMode} model - @@ -62,6 +66,14 @@ export default class DynamicListService implements IDynamicListService { // Parses the filter object. const parsedFilter = this.parseFilterObject(model, filter); + // Search by keyword. + if (filter.searchKeyword) { + const dynamicListSearch = this.dynamicListSearch.dynamicSearch( + model, + filter.searchKeyword + ); + dynamicFilter.setFilter(dynamicListSearch); + } // Custom view filter roles. if (filter.viewSlug) { const dynamicListCustomView =