Simplifies search through relations usage (#126)
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import {
|
||||
ColumnDef,
|
||||
@ -7,15 +8,10 @@ import {
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import TableHeader from './table-header/TableHeader';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
FilterConfigType,
|
||||
SelectedFilterType,
|
||||
} from '../../interfaces/filters/interface';
|
||||
import {
|
||||
SearchableType,
|
||||
SearchConfigType,
|
||||
} from '../../interfaces/search/interface';
|
||||
import { SortType, SelectedSortType } from '../../interfaces/sorts/interface';
|
||||
|
||||
declare module 'react' {
|
||||
@ -34,19 +30,8 @@ type OwnProps<
|
||||
viewIcon?: React.ReactNode;
|
||||
availableSorts?: Array<SortType<SortField>>;
|
||||
availableFilters?: FilterConfigType<TData>[];
|
||||
filterSearchResults?: {
|
||||
results: {
|
||||
render: (value: SearchableType) => string;
|
||||
value: SearchableType;
|
||||
}[];
|
||||
loading: boolean;
|
||||
};
|
||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||
onFiltersUpdate?: (sorts: Array<SelectedFilterType<TData>>) => void;
|
||||
onFilterSearch?: (
|
||||
filter: SearchConfigType<any> | null,
|
||||
searchValue: string,
|
||||
) => void;
|
||||
onRowSelectionChange?: (rowSelection: string[]) => void;
|
||||
};
|
||||
|
||||
@ -112,10 +97,8 @@ const Table = <
|
||||
viewIcon,
|
||||
availableSorts,
|
||||
availableFilters,
|
||||
filterSearchResults,
|
||||
onSortsUpdate,
|
||||
onFiltersUpdate,
|
||||
onFilterSearch,
|
||||
onRowSelectionChange,
|
||||
}: OwnProps<TData, SortField>,
|
||||
ref: React.ForwardedRef<{ resetRowSelection: () => void } | undefined>,
|
||||
@ -156,10 +139,8 @@ const Table = <
|
||||
viewIcon={viewIcon}
|
||||
availableSorts={availableSorts}
|
||||
availableFilters={availableFilters as FilterConfigType<any>[]}
|
||||
filterSearchResults={filterSearchResults}
|
||||
onSortsUpdate={onSortsUpdate}
|
||||
onFiltersUpdate={onFiltersUpdate}
|
||||
onFilterSearch={onFilterSearch}
|
||||
/>
|
||||
<StyledTableScrollableContainer>
|
||||
<StyledTable>
|
||||
|
||||
@ -7,31 +7,19 @@ import {
|
||||
SelectedFilterType,
|
||||
} from '../../../interfaces/filters/interface';
|
||||
import {
|
||||
SearchConfigType,
|
||||
SearchableType,
|
||||
} from '../../../interfaces/search/interface';
|
||||
SearchResultsType,
|
||||
useSearch,
|
||||
} from '../../../services/api/search/search';
|
||||
import { SearchableType } from '../../../interfaces/search/interface';
|
||||
|
||||
type OwnProps<TData extends FilterableFieldsType> = {
|
||||
isFilterSelected: boolean;
|
||||
availableFilters: FilterConfigType<TData>[];
|
||||
filterSearchResults?: {
|
||||
results: {
|
||||
render: (value: SearchableType) => string;
|
||||
value: SearchableType;
|
||||
}[];
|
||||
loading: boolean;
|
||||
};
|
||||
onFilterSelect: (filter: SelectedFilterType<TData>) => void;
|
||||
onFilterSearch: (
|
||||
filter: SearchConfigType<any> | null,
|
||||
searchValue: string,
|
||||
) => void;
|
||||
};
|
||||
|
||||
export const FilterDropdownButton = <TData extends FilterableFieldsType>({
|
||||
availableFilters,
|
||||
filterSearchResults,
|
||||
onFilterSearch,
|
||||
onFilterSelect,
|
||||
isFilterSelected,
|
||||
}: OwnProps<TData>) => {
|
||||
@ -47,12 +35,14 @@ export const FilterDropdownButton = <TData extends FilterableFieldsType>({
|
||||
FilterOperandType<TData> | undefined
|
||||
>(undefined);
|
||||
|
||||
const [filterSearchResults, setSearchInput, setFilterSearch] = useSearch();
|
||||
|
||||
const resetState = useCallback(() => {
|
||||
setIsOptionUnfolded(false);
|
||||
setSelectedFilter(undefined);
|
||||
setSelectedFilterOperand(undefined);
|
||||
onFilterSearch(null, '');
|
||||
}, [onFilterSearch]);
|
||||
setFilterSearch(null);
|
||||
}, [setFilterSearch]);
|
||||
|
||||
const renderSelectOptionItems = selectedFilter?.operands.map(
|
||||
(filterOperand, index) => (
|
||||
@ -69,7 +59,7 @@ export const FilterDropdownButton = <TData extends FilterableFieldsType>({
|
||||
);
|
||||
|
||||
const renderSearchResults = (
|
||||
filterSearchResults: NonNullable<OwnProps<TData>['filterSearchResults']>,
|
||||
filterSearchResults: SearchResultsType<SearchableType>,
|
||||
selectedFilter: FilterConfigType<TData>,
|
||||
selectedFilterOperand: FilterOperandType<TData>,
|
||||
) => {
|
||||
@ -108,7 +98,8 @@ export const FilterDropdownButton = <TData extends FilterableFieldsType>({
|
||||
onClick={() => {
|
||||
setSelectedFilter(filter);
|
||||
setSelectedFilterOperand(filter.operands[0]);
|
||||
onFilterSearch(filter.searchConfig, '');
|
||||
setFilterSearch(filter.searchConfig);
|
||||
setSearchInput('');
|
||||
}}
|
||||
>
|
||||
<DropdownButton.StyledIcon>{filter.icon}</DropdownButton.StyledIcon>
|
||||
@ -134,9 +125,10 @@ export const FilterDropdownButton = <TData extends FilterableFieldsType>({
|
||||
<input
|
||||
type="text"
|
||||
placeholder={selectedFilter.label}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
||||
onFilterSearch(selectedFilter.searchConfig, event.target.value)
|
||||
}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setFilterSearch(selectedFilter.searchConfig);
|
||||
setSearchInput(event.target.value);
|
||||
}}
|
||||
/>
|
||||
</DropdownButton.StyledSearchField>
|
||||
{filterSearchResults &&
|
||||
|
||||
@ -9,10 +9,6 @@ import {
|
||||
FilterConfigType,
|
||||
SelectedFilterType,
|
||||
} from '../../../interfaces/filters/interface';
|
||||
import {
|
||||
SearchableType,
|
||||
SearchConfigType,
|
||||
} from '../../../interfaces/search/interface';
|
||||
import {
|
||||
SortType,
|
||||
SelectedSortType,
|
||||
@ -23,19 +19,8 @@ type OwnProps<SortField, TData extends FilterableFieldsType> = {
|
||||
viewIcon?: ReactNode;
|
||||
availableSorts?: Array<SortType<SortField>>;
|
||||
availableFilters?: FilterConfigType<TData>[];
|
||||
filterSearchResults?: {
|
||||
results: {
|
||||
render: (value: SearchableType) => string;
|
||||
value: SearchableType;
|
||||
}[];
|
||||
loading: boolean;
|
||||
};
|
||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||
onFiltersUpdate?: (sorts: Array<SelectedFilterType<TData>>) => void;
|
||||
onFilterSearch?: (
|
||||
filter: SearchConfigType<any> | null,
|
||||
searchValue: string,
|
||||
) => void;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@ -79,10 +64,8 @@ function TableHeader<SortField, TData extends FilterableFieldsType>({
|
||||
viewIcon,
|
||||
availableSorts,
|
||||
availableFilters,
|
||||
filterSearchResults,
|
||||
onSortsUpdate,
|
||||
onFiltersUpdate,
|
||||
onFilterSearch,
|
||||
}: OwnProps<SortField, TData>) {
|
||||
const [sorts, innerSetSorts] = useState<Array<SelectedSortType<SortField>>>(
|
||||
[],
|
||||
@ -128,13 +111,6 @@ function TableHeader<SortField, TData extends FilterableFieldsType>({
|
||||
[onFiltersUpdate, filters],
|
||||
);
|
||||
|
||||
const filterSearch = useCallback(
|
||||
(filter: SearchConfigType<any> | null, searchValue: string) => {
|
||||
onFilterSearch && onFilterSearch(filter, searchValue);
|
||||
},
|
||||
[onFilterSearch],
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledTableHeader>
|
||||
@ -146,9 +122,7 @@ function TableHeader<SortField, TData extends FilterableFieldsType>({
|
||||
<FilterDropdownButton
|
||||
isFilterSelected={filters.length > 0}
|
||||
availableFilters={availableFilters || []}
|
||||
filterSearchResults={filterSearchResults}
|
||||
onFilterSelect={filterSelect}
|
||||
onFilterSearch={filterSearch}
|
||||
/>
|
||||
<SortDropdownButton<SortField>
|
||||
isSortSelected={sorts.length > 0}
|
||||
|
||||
@ -3,13 +3,10 @@ import { lightTheme } from '../../../../layout/styles/themes';
|
||||
import { FilterDropdownButton } from '../FilterDropdownButton';
|
||||
import styled from '@emotion/styled';
|
||||
import { useCallback, useState } from 'react';
|
||||
import {
|
||||
SEARCH_PEOPLE_QUERY,
|
||||
useSearch,
|
||||
} from '../../../../services/api/search/search';
|
||||
import { SEARCH_PEOPLE_QUERY } from '../../../../services/api/search/search';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { mockData } from '../../../../pages/people/__tests__/__data__/mock-data';
|
||||
import { availableFilters } from '../../../../pages/people/people-table';
|
||||
import { availableFilters } from '../../../../pages/people/people-filters';
|
||||
import { Person } from '../../../../interfaces/entities/person.interface';
|
||||
import {
|
||||
FilterableFieldsType,
|
||||
@ -90,7 +87,6 @@ const InnerRegularFilterDropdownButton = ({
|
||||
setFilter: setFilters,
|
||||
}: OwnProps<Person>) => {
|
||||
const [, innerSetFilters] = useState<SelectedFilterType<Person>>();
|
||||
const [filterSearchResults, setSearhInput, setFilterSearch] = useSearch();
|
||||
|
||||
const outerSetFilters = useCallback(
|
||||
(filter: SelectedFilterType<Person>) => {
|
||||
@ -105,11 +101,6 @@ const InnerRegularFilterDropdownButton = ({
|
||||
availableFilters={availableFilters}
|
||||
isFilterSelected={true}
|
||||
onFilterSelect={outerSetFilters}
|
||||
filterSearchResults={filterSearchResults}
|
||||
onFilterSearch={(filter, searchValue) => {
|
||||
setSearhInput(searchValue);
|
||||
setFilterSearch(filter);
|
||||
}}
|
||||
/>
|
||||
</StyleDiv>
|
||||
);
|
||||
|
||||
@ -3,11 +3,28 @@ import { ThemeProvider } from '@emotion/react';
|
||||
import { lightTheme } from '../../../../layout/styles/themes';
|
||||
import { FaRegBuilding, FaCalendar } from 'react-icons/fa';
|
||||
import { SortType } from '../../../../interfaces/sorts/interface';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { EMPTY_QUERY } from '../../../../services/api/search/search';
|
||||
|
||||
const component = {
|
||||
title: 'TableHeader',
|
||||
component: TableHeader,
|
||||
};
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: EMPTY_QUERY,
|
||||
variables: {
|
||||
where: undefined,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
searchResults: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default component;
|
||||
|
||||
@ -21,12 +38,14 @@ export const RegularTableHeader = () => {
|
||||
},
|
||||
];
|
||||
return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<TableHeader
|
||||
viewName="Test"
|
||||
viewIcon={<FaRegBuilding />}
|
||||
availableSorts={availableSorts}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
<MockedProvider mocks={mocks}>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<TableHeader
|
||||
viewName="Test"
|
||||
viewIcon={<FaRegBuilding />}
|
||||
availableSorts={availableSorts}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
</MockedProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FaRegBuilding, FaList } from 'react-icons/fa';
|
||||
import WithTopBarContainer from '../../layout/containers/WithTopBarContainer';
|
||||
import styled from '@emotion/styled';
|
||||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { FaRegBuilding, FaList } from 'react-icons/fa';
|
||||
import styled from '@emotion/styled';
|
||||
import WithTopBarContainer from '../../layout/containers/WithTopBarContainer';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import {
|
||||
CompaniesSelectedSortType,
|
||||
@ -15,22 +15,18 @@ import {
|
||||
Company,
|
||||
mapToCompany,
|
||||
} from '../../interfaces/entities/company.interface';
|
||||
import {
|
||||
useCompaniesColumns,
|
||||
availableFilters,
|
||||
availableSorts,
|
||||
} from './companies-table';
|
||||
|
||||
import {
|
||||
reduceFiltersToWhere,
|
||||
reduceSortsToOrderBy,
|
||||
} from '../../components/table/table-header/helpers';
|
||||
import {
|
||||
Companies_Bool_Exp,
|
||||
Companies_Order_By,
|
||||
} from '../../generated/graphql';
|
||||
import { useSearch } from '../../services/api/search/search';
|
||||
import { Companies_Order_By } from '../../generated/graphql';
|
||||
import ActionBar from '../../components/table/action-bar/ActionBar';
|
||||
import { SelectedFilterType } from '../../interfaces/filters/interface';
|
||||
import { BoolExpType } from '../../interfaces/entities/generic.interface';
|
||||
import { useCompaniesColumns } from './companies-columns';
|
||||
import { availableSorts } from './companies-sorts';
|
||||
import { availableFilters } from './companies-filters';
|
||||
|
||||
const StyledCompaniesContainer = styled.div`
|
||||
display: flex;
|
||||
@ -39,12 +35,10 @@ const StyledCompaniesContainer = styled.div`
|
||||
|
||||
function Companies() {
|
||||
const [orderBy, setOrderBy] = useState<Companies_Order_By[]>(defaultOrderBy);
|
||||
const [where, setWhere] = useState<Companies_Bool_Exp>({});
|
||||
const [where, setWhere] = useState<BoolExpType<Company>>({});
|
||||
const [internalData, setInternalData] = useState<Array<Company>>([]);
|
||||
const [selectedRowIds, setSelectedRowIds] = useState<Array<string>>([]);
|
||||
|
||||
const [filterSearchResults, setSearhInput, setFilterSearch] = useSearch();
|
||||
|
||||
const updateSorts = useCallback((sorts: Array<CompaniesSelectedSortType>) => {
|
||||
setOrderBy(sorts.length ? reduceSortsToOrderBy(sorts) : defaultOrderBy);
|
||||
}, []);
|
||||
@ -113,13 +107,8 @@ function Companies() {
|
||||
viewIcon={<FaList />}
|
||||
availableSorts={availableSorts}
|
||||
availableFilters={availableFilters}
|
||||
filterSearchResults={filterSearchResults}
|
||||
onSortsUpdate={updateSorts}
|
||||
onFiltersUpdate={updateFilters}
|
||||
onFilterSearch={(filter, searchValue) => {
|
||||
setSearhInput(searchValue);
|
||||
setFilterSearch(filter);
|
||||
}}
|
||||
onRowSelectionChange={setSelectedRowIds}
|
||||
/>
|
||||
</StyledCompaniesContainer>
|
||||
|
||||
@ -1,12 +1,4 @@
|
||||
import { CellContext, createColumnHelper } from '@tanstack/react-table';
|
||||
import {
|
||||
Company,
|
||||
mapToCompany,
|
||||
} from '../../interfaces/entities/company.interface';
|
||||
import { updateCompany } from '../../services/api/companies';
|
||||
import ColumnHead from '../../components/table/ColumnHead';
|
||||
import CompanyChip from '../../components/chips/CompanyChip';
|
||||
import EditableText from '../../components/editable-cell/EditableText';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
FaRegBuilding,
|
||||
FaCalendar,
|
||||
@ -14,126 +6,27 @@ import {
|
||||
FaMapPin,
|
||||
FaRegUser,
|
||||
FaUsers,
|
||||
FaBuilding,
|
||||
} from 'react-icons/fa';
|
||||
import { CellContext, createColumnHelper } from '@tanstack/react-table';
|
||||
|
||||
import { SEARCH_USER_QUERY } from '../../services/api/search/search';
|
||||
import { SearchConfigType } from '../../interfaces/search/interface';
|
||||
|
||||
import { Company } from '../../interfaces/entities/company.interface';
|
||||
import { updateCompany } from '../../services/api/companies';
|
||||
import { User, mapToUser } from '../../interfaces/entities/user.interface';
|
||||
|
||||
import ColumnHead from '../../components/table/ColumnHead';
|
||||
import Checkbox from '../../components/form/Checkbox';
|
||||
import { SelectAllCheckbox } from '../../components/table/SelectAllCheckbox';
|
||||
import EditableDate from '../../components/editable-cell/EditableDate';
|
||||
import EditableRelation from '../../components/editable-cell/EditableRelation';
|
||||
import EditableChip from '../../components/editable-cell/EditableChip';
|
||||
import EditableText from '../../components/editable-cell/EditableText';
|
||||
import PersonChip, {
|
||||
PersonChipPropsType,
|
||||
} from '../../components/chips/PersonChip';
|
||||
import EditableChip from '../../components/editable-cell/EditableChip';
|
||||
import { Companies_Order_By } from '../../generated/graphql';
|
||||
import {
|
||||
SEARCH_COMPANY_QUERY,
|
||||
SEARCH_USER_QUERY,
|
||||
} from '../../services/api/search/search';
|
||||
import EditableDate from '../../components/editable-cell/EditableDate';
|
||||
import EditableRelation from '../../components/editable-cell/EditableRelation';
|
||||
import { User, mapToUser } from '../../interfaces/entities/user.interface';
|
||||
import { useMemo } from 'react';
|
||||
import { SelectAllCheckbox } from '../../components/table/SelectAllCheckbox';
|
||||
import Checkbox from '../../components/form/Checkbox';
|
||||
import { SortType } from '../../interfaces/sorts/interface';
|
||||
import { FilterConfigType } from '../../interfaces/filters/interface';
|
||||
import { SearchConfigType } from '../../interfaces/search/interface';
|
||||
|
||||
export const availableSorts = [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Name',
|
||||
icon: <FaBuilding />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'employees',
|
||||
label: 'Employees',
|
||||
icon: <FaUsers />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'domain_name',
|
||||
label: 'Url',
|
||||
icon: <FaLink />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: 'Address',
|
||||
icon: <FaMapPin />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
label: 'Creation',
|
||||
icon: <FaCalendar />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
] satisfies Array<SortType<Companies_Order_By>>;
|
||||
|
||||
export const availableFilters = [
|
||||
{
|
||||
key: 'company_name',
|
||||
label: 'Company',
|
||||
icon: <FaBuilding />,
|
||||
searchConfig: {
|
||||
query: SEARCH_COMPANY_QUERY,
|
||||
template: (searchInput) => ({
|
||||
name: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (company) => ({
|
||||
render: (company) => company.name,
|
||||
value: mapToCompany(company),
|
||||
}),
|
||||
},
|
||||
selectedValueRender: (company) => company.name || '',
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (company) => ({
|
||||
name: { _eq: company.name },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (company) => ({
|
||||
_not: { name: { _eq: company.name } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
} satisfies FilterConfigType<Company, Company>,
|
||||
{
|
||||
key: 'company_domain_name',
|
||||
label: 'Url',
|
||||
icon: <FaLink />,
|
||||
searchConfig: {
|
||||
query: SEARCH_COMPANY_QUERY,
|
||||
template: (searchInput) => ({
|
||||
name: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (company) => ({
|
||||
render: (company) => company.domainName,
|
||||
value: mapToCompany(company),
|
||||
}),
|
||||
},
|
||||
selectedValueRender: (company) => company.domainName || '',
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (company) => ({
|
||||
domain_name: { _eq: company.domainName },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (company) => ({
|
||||
_not: { domain_name: { _eq: company.domainName } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
} satisfies FilterConfigType<Company, Company>,
|
||||
];
|
||||
import CompanyChip from '../../components/chips/CompanyChip';
|
||||
|
||||
const columnHelper = createColumnHelper<Company>();
|
||||
|
||||
74
front/src/pages/companies/companies-filters.tsx
Normal file
74
front/src/pages/companies/companies-filters.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import {
|
||||
Company,
|
||||
mapToCompany,
|
||||
} from '../../interfaces/entities/company.interface';
|
||||
import { FaLink, FaBuilding } from 'react-icons/fa';
|
||||
import { SEARCH_COMPANY_QUERY } from '../../services/api/search/search';
|
||||
import { FilterConfigType } from '../../interfaces/filters/interface';
|
||||
|
||||
export const availableFilters = [
|
||||
{
|
||||
key: 'company_name',
|
||||
label: 'Company',
|
||||
icon: <FaBuilding />,
|
||||
searchConfig: {
|
||||
query: SEARCH_COMPANY_QUERY,
|
||||
template: (searchInput) => ({
|
||||
name: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (company) => ({
|
||||
render: (company) => company.name,
|
||||
value: mapToCompany(company),
|
||||
}),
|
||||
},
|
||||
selectedValueRender: (company) => company.name || '',
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (company) => ({
|
||||
name: { _eq: company.name },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (company) => ({
|
||||
_not: { name: { _eq: company.name } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
} satisfies FilterConfigType<Company, Company>,
|
||||
{
|
||||
key: 'company_domain_name',
|
||||
label: 'Url',
|
||||
icon: <FaLink />,
|
||||
searchConfig: {
|
||||
query: SEARCH_COMPANY_QUERY,
|
||||
template: (searchInput) => ({
|
||||
name: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (company) => ({
|
||||
render: (company) => company.domainName,
|
||||
value: mapToCompany(company),
|
||||
}),
|
||||
},
|
||||
selectedValueRender: (company) => company.domainName || '',
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (company) => ({
|
||||
domain_name: { _eq: company.domainName },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (company) => ({
|
||||
_not: { domain_name: { _eq: company.domainName } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
} satisfies FilterConfigType<Company, Company>,
|
||||
];
|
||||
42
front/src/pages/companies/companies-sorts.tsx
Normal file
42
front/src/pages/companies/companies-sorts.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import {
|
||||
FaCalendar,
|
||||
FaLink,
|
||||
FaMapPin,
|
||||
FaUsers,
|
||||
FaBuilding,
|
||||
} from 'react-icons/fa';
|
||||
import { Companies_Order_By } from '../../generated/graphql';
|
||||
import { SortType } from '../../interfaces/sorts/interface';
|
||||
|
||||
export const availableSorts = [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Name',
|
||||
icon: <FaBuilding />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'employees',
|
||||
label: 'Employees',
|
||||
icon: <FaUsers />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'domain_name',
|
||||
label: 'Url',
|
||||
icon: <FaLink />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: 'Address',
|
||||
icon: <FaMapPin />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
label: 'Creation',
|
||||
icon: <FaCalendar />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
] satisfies Array<SortType<Companies_Order_By>>;
|
||||
@ -1,18 +1,15 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { FaRegUser, FaList } from 'react-icons/fa';
|
||||
import WithTopBarContainer from '../../layout/containers/WithTopBarContainer';
|
||||
import Table from '../../components/table/Table';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
availableFilters,
|
||||
availableSorts,
|
||||
usePeopleColumns,
|
||||
} from './people-table';
|
||||
|
||||
import WithTopBarContainer from '../../layout/containers/WithTopBarContainer';
|
||||
import Table from '../../components/table/Table';
|
||||
|
||||
import {
|
||||
Person,
|
||||
mapToPerson,
|
||||
} from '../../interfaces/entities/person.interface';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
PeopleSelectedSortType,
|
||||
defaultOrderBy,
|
||||
@ -20,14 +17,16 @@ import {
|
||||
insertPerson,
|
||||
usePeopleQuery,
|
||||
} from '../../services/api/people';
|
||||
import { useSearch } from '../../services/api/search/search';
|
||||
import { People_Bool_Exp } from '../../generated/graphql';
|
||||
import {
|
||||
reduceFiltersToWhere,
|
||||
reduceSortsToOrderBy,
|
||||
} from '../../components/table/table-header/helpers';
|
||||
import ActionBar from '../../components/table/action-bar/ActionBar';
|
||||
import { SelectedFilterType } from '../../interfaces/filters/interface';
|
||||
import { BoolExpType } from '../../interfaces/entities/generic.interface';
|
||||
import { usePeopleColumns } from './people-columns';
|
||||
import { availableSorts } from './people-sorts';
|
||||
import { availableFilters } from './people-filters';
|
||||
|
||||
const StyledPeopleContainer = styled.div`
|
||||
display: flex;
|
||||
@ -37,8 +36,7 @@ const StyledPeopleContainer = styled.div`
|
||||
|
||||
function People() {
|
||||
const [orderBy, setOrderBy] = useState(defaultOrderBy);
|
||||
const [where, setWhere] = useState<People_Bool_Exp>({});
|
||||
const [filterSearchResults, setSearchInput, setFilterSearch] = useSearch();
|
||||
const [where, setWhere] = useState<BoolExpType<Person>>({});
|
||||
const [internalData, setInternalData] = useState<Array<Person>>([]);
|
||||
const [selectedRowIds, setSelectedRowIds] = useState<Array<string>>([]);
|
||||
|
||||
@ -111,13 +109,8 @@ function People() {
|
||||
viewIcon={<FaList />}
|
||||
availableSorts={availableSorts}
|
||||
availableFilters={availableFilters}
|
||||
filterSearchResults={filterSearchResults}
|
||||
onSortsUpdate={updateSorts}
|
||||
onFiltersUpdate={updateFilters}
|
||||
onFilterSearch={(filter, searchValue) => {
|
||||
setSearchInput(searchValue);
|
||||
setFilterSearch(filter);
|
||||
}}
|
||||
onRowSelectionChange={setSelectedRowIds}
|
||||
/>
|
||||
</StyledPeopleContainer>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { cityFilter } from '../people-table';
|
||||
import { cityFilter } from '../people-filters';
|
||||
|
||||
describe('PeopleFilter', () => {
|
||||
it(`should render the filter ${cityFilter.key}`, () => {
|
||||
@ -11,8 +11,9 @@ describe('PeopleFilter', () => {
|
||||
lastname: 'Doe',
|
||||
phone: '0123456789',
|
||||
creationDate: new Date(),
|
||||
pipe: null,
|
||||
pipes: [],
|
||||
company: null,
|
||||
__typename: 'people',
|
||||
}),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
FaRegBuilding,
|
||||
FaCalendar,
|
||||
@ -5,233 +6,30 @@ import {
|
||||
FaRegUser,
|
||||
FaMapPin,
|
||||
FaPhone,
|
||||
FaUser,
|
||||
FaBuilding,
|
||||
} from 'react-icons/fa';
|
||||
import { CellContext, createColumnHelper } from '@tanstack/react-table';
|
||||
import ColumnHead from '../../components/table/ColumnHead';
|
||||
import Checkbox from '../../components/form/Checkbox';
|
||||
import CompanyChip, {
|
||||
CompanyChipPropsType,
|
||||
} from '../../components/chips/CompanyChip';
|
||||
import {
|
||||
Person,
|
||||
mapToPerson,
|
||||
} from '../../interfaces/entities/person.interface';
|
||||
import EditableText from '../../components/editable-cell/EditableText';
|
||||
import { Order_By, People_Order_By } from '../../generated/graphql';
|
||||
import {
|
||||
SEARCH_COMPANY_QUERY,
|
||||
SEARCH_PEOPLE_QUERY,
|
||||
} from '../../services/api/search/search';
|
||||
|
||||
import { SEARCH_COMPANY_QUERY } from '../../services/api/search/search';
|
||||
import { SearchConfigType } from '../../interfaces/search/interface';
|
||||
|
||||
import {
|
||||
Company,
|
||||
mapToCompany,
|
||||
} from '../../interfaces/entities/company.interface';
|
||||
import { Person } from '../../interfaces/entities/person.interface';
|
||||
import { updatePerson } from '../../services/api/people';
|
||||
|
||||
import ColumnHead from '../../components/table/ColumnHead';
|
||||
import Checkbox from '../../components/form/Checkbox';
|
||||
import { SelectAllCheckbox } from '../../components/table/SelectAllCheckbox';
|
||||
import EditablePhone from '../../components/editable-cell/EditablePhone';
|
||||
import EditableFullName from '../../components/editable-cell/EditableFullName';
|
||||
import EditableDate from '../../components/editable-cell/EditableDate';
|
||||
import EditableText from '../../components/editable-cell/EditableText';
|
||||
import EditableRelation from '../../components/editable-cell/EditableRelation';
|
||||
import { updatePerson } from '../../services/api/people';
|
||||
import { useMemo } from 'react';
|
||||
import { SelectAllCheckbox } from '../../components/table/SelectAllCheckbox';
|
||||
import { SortType } from '../../interfaces/sorts/interface';
|
||||
import { FilterConfigType } from '../../interfaces/filters/interface';
|
||||
import { SearchConfigType } from '../../interfaces/search/interface';
|
||||
|
||||
export const availableSorts = [
|
||||
{
|
||||
key: 'fullname',
|
||||
label: 'People',
|
||||
icon: <FaRegUser />,
|
||||
_type: 'custom_sort',
|
||||
orderByTemplate: (order: Order_By) => ({
|
||||
firstname: order,
|
||||
lastname: order,
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: 'company_name',
|
||||
label: 'Company',
|
||||
icon: <FaRegBuilding />,
|
||||
_type: 'custom_sort',
|
||||
orderByTemplate: (order: Order_By) => ({ company: { name: order } }),
|
||||
},
|
||||
{
|
||||
key: 'email',
|
||||
label: 'Email',
|
||||
icon: <FaEnvelope />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
label: 'Phone',
|
||||
icon: <FaPhone />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
label: 'Created at',
|
||||
icon: <FaCalendar />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
label: 'City',
|
||||
icon: <FaMapPin />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
] satisfies Array<SortType<People_Order_By>>;
|
||||
|
||||
export const fullnameFilter = {
|
||||
key: 'fullname',
|
||||
label: 'People',
|
||||
icon: <FaUser />,
|
||||
searchConfig: {
|
||||
query: SEARCH_PEOPLE_QUERY,
|
||||
template: (searchInput: string) => ({
|
||||
_or: [
|
||||
{ firstname: { _ilike: `%${searchInput}%` } },
|
||||
{ lastname: { _ilike: `%${searchInput}%` } },
|
||||
],
|
||||
}),
|
||||
resultMapper: (person) => ({
|
||||
render: (person) => `${person.firstname} ${person.lastname}`,
|
||||
value: mapToPerson(person),
|
||||
}),
|
||||
},
|
||||
selectedValueRender: (person) => `${person.firstname} ${person.lastname}`,
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (person) => ({
|
||||
_and: [
|
||||
{ firstname: { _eq: `${person.firstname}` } },
|
||||
{ lastname: { _eq: `${person.lastname}` } },
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (person) => ({
|
||||
_not: {
|
||||
_and: [
|
||||
{ firstname: { _eq: `${person.firstname}` } },
|
||||
{ lastname: { _eq: `${person.lastname}` } },
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
],
|
||||
} satisfies FilterConfigType<Person, Person>;
|
||||
|
||||
export const companyFilter = {
|
||||
key: 'company_name',
|
||||
label: 'Company',
|
||||
icon: <FaBuilding />,
|
||||
searchConfig: {
|
||||
query: SEARCH_COMPANY_QUERY,
|
||||
template: (searchInput: string) => ({
|
||||
name: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (data) => ({
|
||||
value: mapToCompany(data),
|
||||
render: (company) => company.name,
|
||||
}),
|
||||
},
|
||||
selectedValueRender: (company) => company.name || '',
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (company) => ({
|
||||
company: { name: { _eq: company.name } },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (company) => ({
|
||||
_not: { company: { name: { _eq: company.name } } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
} satisfies FilterConfigType<Person, Company>;
|
||||
|
||||
export const emailFilter = {
|
||||
key: 'email',
|
||||
label: 'Email',
|
||||
icon: <FaEnvelope />,
|
||||
searchConfig: {
|
||||
query: SEARCH_PEOPLE_QUERY,
|
||||
template: (searchInput: string) => ({
|
||||
email: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (person) => ({
|
||||
render: (person) => person.email,
|
||||
value: mapToPerson(person),
|
||||
}),
|
||||
},
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (person) => ({
|
||||
email: { _eq: person.email },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (person) => ({
|
||||
_not: { email: { _eq: person.email } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
selectedValueRender: (person) => person.email || '',
|
||||
} satisfies FilterConfigType<Person, Person>;
|
||||
|
||||
export const cityFilter = {
|
||||
key: 'city',
|
||||
label: 'City',
|
||||
icon: <FaMapPin />,
|
||||
searchConfig: {
|
||||
query: SEARCH_PEOPLE_QUERY,
|
||||
template: (searchInput: string) => ({
|
||||
city: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (person) => ({
|
||||
render: (person) => person.city,
|
||||
value: mapToPerson(person),
|
||||
}),
|
||||
},
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (person) => ({
|
||||
city: { _eq: person.city },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (person) => ({
|
||||
_not: { city: { _eq: person.city } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
selectedValueRender: (person) => person.email || '',
|
||||
} satisfies FilterConfigType<Person, Person>;
|
||||
|
||||
export const availableFilters = [
|
||||
fullnameFilter,
|
||||
companyFilter,
|
||||
emailFilter,
|
||||
cityFilter,
|
||||
] satisfies FilterConfigType<Person, any>[];
|
||||
import CompanyChip, {
|
||||
CompanyChipPropsType,
|
||||
} from '../../components/chips/CompanyChip';
|
||||
|
||||
const columnHelper = createColumnHelper<Person>();
|
||||
|
||||
164
front/src/pages/people/people-filters.tsx
Normal file
164
front/src/pages/people/people-filters.tsx
Normal file
@ -0,0 +1,164 @@
|
||||
import { FaEnvelope, FaMapPin, FaUser, FaBuilding } from 'react-icons/fa';
|
||||
import {
|
||||
Person,
|
||||
mapToPerson,
|
||||
} from '../../interfaces/entities/person.interface';
|
||||
import {
|
||||
SEARCH_COMPANY_QUERY,
|
||||
SEARCH_PEOPLE_QUERY,
|
||||
} from '../../services/api/search/search';
|
||||
import {
|
||||
Company,
|
||||
mapToCompany,
|
||||
} from '../../interfaces/entities/company.interface';
|
||||
import { FilterConfigType } from '../../interfaces/filters/interface';
|
||||
|
||||
export const fullnameFilter = {
|
||||
key: 'fullname',
|
||||
label: 'People',
|
||||
icon: <FaUser />,
|
||||
searchConfig: {
|
||||
query: SEARCH_PEOPLE_QUERY,
|
||||
template: (searchInput: string) => ({
|
||||
_or: [
|
||||
{ firstname: { _ilike: `%${searchInput}%` } },
|
||||
{ lastname: { _ilike: `%${searchInput}%` } },
|
||||
],
|
||||
}),
|
||||
resultMapper: (person) => ({
|
||||
render: (person) => `${person.firstname} ${person.lastname}`,
|
||||
value: mapToPerson(person),
|
||||
}),
|
||||
},
|
||||
selectedValueRender: (person) => `${person.firstname} ${person.lastname}`,
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (person) => ({
|
||||
_and: [
|
||||
{ firstname: { _eq: `${person.firstname}` } },
|
||||
{ lastname: { _eq: `${person.lastname}` } },
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (person) => ({
|
||||
_not: {
|
||||
_and: [
|
||||
{ firstname: { _eq: `${person.firstname}` } },
|
||||
{ lastname: { _eq: `${person.lastname}` } },
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
],
|
||||
} satisfies FilterConfigType<Person, Person>;
|
||||
|
||||
export const companyFilter = {
|
||||
key: 'company_name',
|
||||
label: 'Company',
|
||||
icon: <FaBuilding />,
|
||||
searchConfig: {
|
||||
query: SEARCH_COMPANY_QUERY,
|
||||
template: (searchInput: string) => ({
|
||||
name: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (data) => ({
|
||||
value: mapToCompany(data),
|
||||
render: (company) => company.name,
|
||||
}),
|
||||
},
|
||||
selectedValueRender: (company) => company.name || '',
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (company) => ({
|
||||
company: { name: { _eq: company.name } },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (company) => ({
|
||||
_not: { company: { name: { _eq: company.name } } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
} satisfies FilterConfigType<Person, Company>;
|
||||
|
||||
export const emailFilter = {
|
||||
key: 'email',
|
||||
label: 'Email',
|
||||
icon: <FaEnvelope />,
|
||||
searchConfig: {
|
||||
query: SEARCH_PEOPLE_QUERY,
|
||||
template: (searchInput: string) => ({
|
||||
email: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (person) => ({
|
||||
render: (person) => person.email,
|
||||
value: mapToPerson(person),
|
||||
}),
|
||||
},
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (person) => ({
|
||||
email: { _eq: person.email },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (person) => ({
|
||||
_not: { email: { _eq: person.email } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
selectedValueRender: (person) => person.email || '',
|
||||
} satisfies FilterConfigType<Person, Person>;
|
||||
|
||||
export const cityFilter = {
|
||||
key: 'city',
|
||||
label: 'City',
|
||||
icon: <FaMapPin />,
|
||||
searchConfig: {
|
||||
query: SEARCH_PEOPLE_QUERY,
|
||||
template: (searchInput: string) => ({
|
||||
city: { _ilike: `%${searchInput}%` },
|
||||
}),
|
||||
resultMapper: (person) => ({
|
||||
render: (person) => person.city,
|
||||
value: mapToPerson(person),
|
||||
}),
|
||||
},
|
||||
operands: [
|
||||
{
|
||||
label: 'Equal',
|
||||
id: 'equal',
|
||||
whereTemplate: (person) => ({
|
||||
city: { _eq: person.city },
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Not equal',
|
||||
id: 'not-equal',
|
||||
whereTemplate: (person) => ({
|
||||
_not: { city: { _eq: person.city } },
|
||||
}),
|
||||
},
|
||||
],
|
||||
selectedValueRender: (person) => person.email || '',
|
||||
} satisfies FilterConfigType<Person, Person>;
|
||||
|
||||
export const availableFilters = [
|
||||
fullnameFilter,
|
||||
companyFilter,
|
||||
emailFilter,
|
||||
cityFilter,
|
||||
] satisfies FilterConfigType<Person, any>[];
|
||||
54
front/src/pages/people/people-sorts.tsx
Normal file
54
front/src/pages/people/people-sorts.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import {
|
||||
FaRegBuilding,
|
||||
FaCalendar,
|
||||
FaEnvelope,
|
||||
FaRegUser,
|
||||
FaMapPin,
|
||||
FaPhone,
|
||||
} from 'react-icons/fa';
|
||||
import { Order_By, People_Order_By } from '../../generated/graphql';
|
||||
import { SortType } from '../../interfaces/sorts/interface';
|
||||
|
||||
export const availableSorts = [
|
||||
{
|
||||
key: 'fullname',
|
||||
label: 'People',
|
||||
icon: <FaRegUser />,
|
||||
_type: 'custom_sort',
|
||||
orderByTemplate: (order: Order_By) => ({
|
||||
firstname: order,
|
||||
lastname: order,
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: 'company_name',
|
||||
label: 'Company',
|
||||
icon: <FaRegBuilding />,
|
||||
_type: 'custom_sort',
|
||||
orderByTemplate: (order: Order_By) => ({ company: { name: order } }),
|
||||
},
|
||||
{
|
||||
key: 'email',
|
||||
label: 'Email',
|
||||
icon: <FaEnvelope />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
label: 'Phone',
|
||||
icon: <FaPhone />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
label: 'Created at',
|
||||
icon: <FaCalendar />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
label: 'City',
|
||||
icon: <FaMapPin />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
] satisfies Array<SortType<People_Order_By>>;
|
||||
@ -29,7 +29,7 @@ export const SEARCH_USER_QUERY = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
const EMPTY_QUERY = gql`
|
||||
export const EMPTY_QUERY = gql`
|
||||
query EmptyQuery {
|
||||
_
|
||||
}
|
||||
@ -58,14 +58,16 @@ const debounce = <FuncArgs extends any[]>(
|
||||
};
|
||||
};
|
||||
|
||||
export type SearchResultsType<T extends SearchableType> = {
|
||||
results: {
|
||||
render: (value: T) => string;
|
||||
value: T;
|
||||
}[];
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
export const useSearch = <T extends SearchableType>(): [
|
||||
{
|
||||
results: {
|
||||
render: (value: T) => string;
|
||||
value: T;
|
||||
}[];
|
||||
loading: boolean;
|
||||
},
|
||||
SearchResultsType<T>,
|
||||
React.Dispatch<React.SetStateAction<string>>,
|
||||
React.Dispatch<React.SetStateAction<SearchConfigType<T> | null>>,
|
||||
] => {
|
||||
@ -87,18 +89,15 @@ export const useSearch = <T extends SearchableType>(): [
|
||||
);
|
||||
}, [searchConfig, searchInput]);
|
||||
|
||||
const searchFilterQueryResults = useQuery(
|
||||
searchConfig?.query || EMPTY_QUERY,
|
||||
{
|
||||
variables: {
|
||||
where,
|
||||
limit: 5,
|
||||
},
|
||||
skip: !searchConfig,
|
||||
const searchQueryResults = useQuery(searchConfig?.query || EMPTY_QUERY, {
|
||||
variables: {
|
||||
where,
|
||||
limit: 5,
|
||||
},
|
||||
);
|
||||
skip: !searchConfig,
|
||||
});
|
||||
|
||||
const searchFilterResults = useMemo<{
|
||||
const searchResults = useMemo<{
|
||||
results: { render: (value: T) => string; value: any }[];
|
||||
loading: boolean;
|
||||
}>(() => {
|
||||
@ -108,7 +107,7 @@ export const useSearch = <T extends SearchableType>(): [
|
||||
results: [],
|
||||
};
|
||||
}
|
||||
if (searchFilterQueryResults.loading) {
|
||||
if (searchQueryResults.loading) {
|
||||
return {
|
||||
loading: true,
|
||||
results: [],
|
||||
@ -116,11 +115,11 @@ export const useSearch = <T extends SearchableType>(): [
|
||||
}
|
||||
return {
|
||||
loading: false,
|
||||
results: searchFilterQueryResults.data.searchResults.map(
|
||||
results: searchQueryResults.data.searchResults.map(
|
||||
searchConfig.resultMapper,
|
||||
),
|
||||
};
|
||||
}, [searchConfig, searchFilterQueryResults]);
|
||||
}, [searchConfig, searchQueryResults]);
|
||||
|
||||
return [searchFilterResults, debouncedsetSearchInput, setSearchConfig];
|
||||
return [searchResults, debouncedsetSearchInput, setSearchConfig];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user