diff --git a/src/app/components/SearchMenu/MenuItem/index.tsx b/src/app/components/SearchMenu/MenuItem/index.tsx index 8e5eab4..662bfd3 100644 --- a/src/app/components/SearchMenu/MenuItem/index.tsx +++ b/src/app/components/SearchMenu/MenuItem/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { SearchSource } from '../../../util/DefaultEntities'; import RoundedButton from '../../RoundedButton'; @@ -17,6 +17,7 @@ const MenuItem: React.FC = ({ item, index, }) => { + return ( <>
  • = ({ isOpen, setOpen }) => { useEffect(() => { setSelectedItem(-1); - setSearchResult(searchSourceMemo); + setSearchResult(sortMenu(searchSourceMemo)); setMaxItems(searchSourceMemo.length * 2); + if (!isOpen) { + setInputSearch(''); + } }, [isOpen, searchSourceMemo]); useEffect(() => { @@ -82,14 +87,30 @@ const SearchMenu: React.FC = ({ isOpen, setOpen }) => { [searchResult, selectedItem] ); - const globalSearchHandler = useCallback((search) => { - const retorno: any = window.api.sendSync('globalSearch', { - entity: 'Any', - value: search, + const factorySearchHandler = useCallback((ret) => { + const processed = ret.map((item: AdapterBaseProps) => { + console.log(item); + return BehaviourFactory.make(item); }); - return retorno; + return processed; }, []); + const globalSearchHandler = useCallback( + (search) => { + const ret: any = window.api.sendSync('globalSearch', { + entity: 'Any', + value: search, + }); + + return factorySearchHandler(ret); + }, + [factorySearchHandler] + ); + + const sortMenu = (results: any) => { + return results.sort((a: any, b: any) => (a.label < b.label ? -1 : 1)); + }; + const handleChange = useCallback( (e: React.ChangeEvent) => { const value = e.target.value; @@ -103,9 +124,8 @@ const SearchMenu: React.FC = ({ isOpen, setOpen }) => { results.push(...resultsDb); } - const finalResult: SearchSource[] = results.sort((a, b) => - a.label < b.label ? -1 : 1 - ); + const finalResult: SearchSource[] = sortMenu(results); + setSearchResult(finalResult); setMaxItems(finalResult.length * 2); setSelectedItem(-1); @@ -150,7 +170,7 @@ const SearchMenu: React.FC = ({ isOpen, setOpen }) => { return ( isOpen && ( <> - +
    { type: type, title: `${type}.label`, action: action, - item: undefined, + item: item ? item : undefined, }; addTab(tab); @@ -194,7 +194,7 @@ const Tabs: React.FC = () => { [tabItems] ); - useEffect(()=>{ + useEffect(() => { setActiveTab(selectedTab); }, [selectedTab]); diff --git a/src/app/components/Title/index.tsx b/src/app/components/Title/index.tsx index a623075..8164738 100644 --- a/src/app/components/Title/index.tsx +++ b/src/app/components/Title/index.tsx @@ -7,7 +7,6 @@ import TitleUpdate from './Update'; const Title: React.FC<{ action: string, item?: unknown }> = ({ action, item }) => { const title = item as Title; - return ( <> {action === 'create' && } diff --git a/src/app/factory/BehaviourFactory.ts b/src/app/factory/BehaviourFactory.ts new file mode 100644 index 0000000..f57c944 --- /dev/null +++ b/src/app/factory/BehaviourFactory.ts @@ -0,0 +1,22 @@ +import Person from './Person'; +import Title from './Title'; +import {AdapterBaseProps} from '../../electron/database/adapter/AdapterBase'; +import { SearchSource } from '../util/DefaultEntities'; + +interface Product { + [key: string]: Person | Title; +} + +class BehaviourFactory { + static readonly factories: Product = { + Person: new Person(), + Title: new Title(), + }; + + static make(item: AdapterBaseProps): SearchSource { + const classElement = this.factories[item.handler]; + return classElement.execute(item); + } +} + +export default BehaviourFactory; diff --git a/src/app/factory/IconFactory.ts b/src/app/factory/IconFactory.ts new file mode 100644 index 0000000..13cfa6b --- /dev/null +++ b/src/app/factory/IconFactory.ts @@ -0,0 +1,23 @@ +import { FaPlus, FaUser, FaBook, FaHandshake } from 'react-icons/fa'; +import { FiBook } from 'react-icons/fi'; +import { IconBaseProps } from 'react-icons'; + +interface Product { + [key: string]: React.ComponentType; +} + +class IconFactory { + static readonly icons: Product = { + FiBook: FiBook, + FaPlus: FaPlus, + FaUser: FaUser, + FaBook: FaBook, + FaHandshake: FaHandshake, + }; + + static icon(param: string): React.ComponentType { + return this.icons[param]; + } +} + +export default IconFactory; diff --git a/src/app/factory/Person.ts b/src/app/factory/Person.ts new file mode 100644 index 0000000..02b345f --- /dev/null +++ b/src/app/factory/Person.ts @@ -0,0 +1,57 @@ +import { trigger } from '../util/EventHandler'; +import { AppEvent } from '../../common/AppEvent'; +import { Actions } from '../../common/Actions'; +import IconFactory from './IconFactory'; +import {AdapterBaseProps} from '../../electron/database/adapter/AdapterBase'; +import { SearchSource } from '../util/DefaultEntities'; + +class Person { + execute(item: AdapterBaseProps): SearchSource { + const icon = IconFactory.icon(item.icon); + const iconAction = IconFactory.icon(item.iconAction); + + const newItem = { + name: item.name, + label: item.label, + complement: item.complement, + icon: icon, + iconColor: item.iconColor, + iconAction: iconAction, + handler: { + onClick: (): void => { + trigger(AppEvent.quickSearch); + trigger(AppEvent.personTab, { + action: Actions.read, + value: item.item, + }); + }, + onPress: (): void => { + trigger(AppEvent.quickSearch); + trigger(AppEvent.personTab, { + action: Actions.read, + value: item.item, + }); + }, + }, + action: { + onClick: (): void => { + trigger(AppEvent.quickSearch); + trigger(AppEvent.borrowTab, { + action: Actions.create, + value: item.item, + }); + }, + onPress: (): void => { + trigger(AppEvent.quickSearch); + trigger(AppEvent.borrowTab, { + action: Actions.create, + value: item.item, + }); + }, + }, + }; + return newItem; + } +} + +export default Person; diff --git a/src/app/factory/Title.ts b/src/app/factory/Title.ts new file mode 100644 index 0000000..a75bee0 --- /dev/null +++ b/src/app/factory/Title.ts @@ -0,0 +1,56 @@ +import { trigger } from '../util/EventHandler'; +import { AppEvent } from '../../common/AppEvent'; +import { Actions } from '../../common/Actions'; +import IconFactory from './IconFactory'; +import {AdapterBaseProps} from '../../electron/database/adapter/AdapterBase'; +import { SearchSource } from '../util/DefaultEntities'; + +class Title { + execute(item: AdapterBaseProps): SearchSource { + const icon = IconFactory.icon(item.icon); + const iconAction = IconFactory.icon(item.iconAction); + const newItem = { + name: item.name, + label: item.label, + complement: item.complement, + icon: icon, + iconColor: item.iconColor, + iconAction: iconAction, + handler: { + onClick: (): void => { + trigger(AppEvent.quickSearch); + trigger(AppEvent.titleTab, { + action: Actions.read, + value: item.item, + }); + }, + onPress: (): void => { + trigger(AppEvent.quickSearch); + trigger(AppEvent.titleTab, { + action: Actions.read, + value: item.item, + }); + }, + }, + action: { + onClick: (): void => { + trigger(AppEvent.quickSearch); + trigger(AppEvent.borrowTab, { + action: Actions.create, + value: item.item, + }); + }, + onPress: (): void => { + trigger(AppEvent.quickSearch); + trigger(AppEvent.borrowTab, { + action: Actions.create, + value: item.item, + }); + }, + }, + }; + return newItem; + } +} + +export default Title; diff --git a/src/app/util/DefaultEntities.ts b/src/app/util/DefaultEntities.ts index 75b5cbb..c0e6e70 100644 --- a/src/app/util/DefaultEntities.ts +++ b/src/app/util/DefaultEntities.ts @@ -4,12 +4,12 @@ import { FiBook, FiSettings, FiUser } from 'react-icons/fi'; import i18n from '../i18n'; import { trigger } from './EventHandler'; import { AppEvent } from '../../common/AppEvent'; -import Borrow from '../components/Borrow'; import { Actions } from '../../common/Actions'; export interface SearchSource { name: string; label: string; + complement?: string, icon: React.ComponentType; iconColor: string; iconAction?: React.ComponentType; diff --git a/src/electron/Main.ts b/src/electron/Main.ts index ae00640..59f67fc 100644 --- a/src/electron/Main.ts +++ b/src/electron/Main.ts @@ -27,6 +27,8 @@ import BorrowRepository from './database/repository/BorrowRepository'; import TitlePublisherRepository from './database/repository/TitlePublisherRepository'; import UserRepository from './database/repository/UserRepository'; import PersonRepository from './database/repository/PersonRepository'; +import TitleAdapter from '../electron/database/adapter/TitleAdapter'; +import PersonAdapter from '../electron/database/adapter/PersonAdapter'; declare const MAIN_WINDOW_WEBPACK_ENTRY: string; declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string; @@ -352,7 +354,23 @@ export default class Main { ipcMain.on('globalSearch', async (event, content: Event[]) => { try { - event.returnValue = []; + const { value, entity } = content[0]; + const titleAdapter = new TitleAdapter(); + const personAdapter = new PersonAdapter(); + + const titles = await titleAdapter.defineData( + await this.getCustomRepository('Title', TitleRepository).globalSearch( + value + ) + ); + + const people = await personAdapter.defineData( + await this.getCustomRepository('User', PersonRepository).globalSearch( + value + ) + ); + + event.returnValue = [...titles, ...people]; } catch (err) { log.error(err); } diff --git a/src/electron/database/adapter/AdapterBase.ts b/src/electron/database/adapter/AdapterBase.ts new file mode 100644 index 0000000..257db98 --- /dev/null +++ b/src/electron/database/adapter/AdapterBase.ts @@ -0,0 +1,26 @@ +import { Title } from '../models/Title.schema'; +import { User } from '../models/User.schema'; + +export type AdapterBaseProps = { + name: string; + label: string; + complement?: string; + icon: string; + iconColor: string; + iconAction: string; + handler: string; + action: string; + item: Title | User; +}; + +interface BaseAdapterContract { + defineData(dataFromRepository: Title[] | User[]): Promise; +} + +export default class AdapterBase implements BaseAdapterContract { + defineData( + dataFromRepository: Title[] | User[] + ): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/src/electron/database/adapter/PersonAdapter.ts b/src/electron/database/adapter/PersonAdapter.ts new file mode 100644 index 0000000..7ae1c7d --- /dev/null +++ b/src/electron/database/adapter/PersonAdapter.ts @@ -0,0 +1,31 @@ +import { Title } from '../models/Title.schema'; +import { User } from '../models/User.schema'; +import AdapterBase, { AdapterBaseProps } from './AdapterBase'; + +export default class PersonAdapter implements AdapterBase { + public async defineData( + dataFromRepository: Title[] | User[] + ): Promise { + try { + const data = dataFromRepository.map((item: Title | User) => { + const processedItem = { + name: item.name, + label: item.name, + complement: item.name, + icon: 'FaUser', + iconColor: '#ff78f7', + iconAction: 'FaHandshake', + handler: 'Person', + action: 'Person', + item: item, + }; + + return processedItem; + }); + return data; + } catch (err) { + console.log(err); + throw err; + } + } +} diff --git a/src/electron/database/adapter/TitleAdapter.ts b/src/electron/database/adapter/TitleAdapter.ts new file mode 100644 index 0000000..87af750 --- /dev/null +++ b/src/electron/database/adapter/TitleAdapter.ts @@ -0,0 +1,31 @@ +import { Title } from '../models/Title.schema'; +import { User } from '../models/User.schema'; +import AdapterBase, { AdapterBaseProps } from './AdapterBase'; + +export default class TitleAdapter implements AdapterBase{ + public async defineData( + dataFromRepository: Title[] | User[] + ): Promise { + try { + const data = dataFromRepository.map((item: Title | User) => { + const processedItem = { + name: item.name, + label: item.name, + complement: item.name, + icon: 'FaBook', + iconColor: '#50fa7b', + iconAction: 'FaPlus', + handler: 'Title', + action: 'Title', + item: item, + }; + return processedItem; + }); + + return data; + } catch (err) { + console.log(err); + throw err; + } + } +} diff --git a/src/electron/database/repository/PersonRepository.ts b/src/electron/database/repository/PersonRepository.ts index c50678e..77cf340 100644 --- a/src/electron/database/repository/PersonRepository.ts +++ b/src/electron/database/repository/PersonRepository.ts @@ -23,7 +23,7 @@ export default class PersonRepository extends RepositoryBase { 'borrows', 'borrows.titlePublisher', 'borrows.titlePublisher.title', - 'borrows.titlePublisher.publisher' + 'borrows.titlePublisher.publisher', ], skip: content.pageStart || 0, take: content.pageSize || 10, @@ -56,7 +56,7 @@ export default class PersonRepository extends RepositoryBase { 'borrows', 'borrows.titlePublisher', 'borrows.titlePublisher.title', - 'borrows.titlePublisher.publisher' + 'borrows.titlePublisher.publisher', ], }; @@ -70,4 +70,33 @@ export default class PersonRepository extends RepositoryBase { throw err; } } + + public async globalSearch(content: string): Promise { + try { + const filter = { + relations: [ + 'userType', + 'contacts', + 'contacts.contactType', + 'addresses', + 'addresses.city', + 'addresses.city.region', + 'addresses.city.region.country', + 'borrows', + 'borrows.titlePublisher', + 'borrows.titlePublisher.title', + 'borrows.titlePublisher.publisher', + ], + where: (qb: any) => { + qb.where(`User.name like '%${content}%'`); + }, + limit: 15, + }; + const data = await this.repository.find(filter); + return data; + } catch (err) { + console.log(err); + throw err; + } + } } diff --git a/src/electron/database/repository/TitleRepository.ts b/src/electron/database/repository/TitleRepository.ts index 39ae7f0..467bdb0 100644 --- a/src/electron/database/repository/TitleRepository.ts +++ b/src/electron/database/repository/TitleRepository.ts @@ -60,4 +60,32 @@ export default class TitleRepository extends RepositoryBase { throw err; } } + + public async globalSearch(content: string): Promise { + try { + const filter = { + relations: [ + 'titleAuthors', + 'titleAuthors.author', + 'titleCategories', + 'titleCategories.category', + 'titlePublishers', + 'titlePublishers.publisher', + ], + where: (qb: any) => { + qb.where(`Title.name like '%${content}%'`) + .orWhere( + `Title__titleCategories__category.name like '%${content}%'` + ) + .orWhere(`Title__titleAuthors__author.name like '%${content}%'`); + }, + limit: 15, + }; + const data = await this.repository.find(filter); + return data; + } catch (err) { + console.log(err); + throw err; + } + } }