Sammy/t 240 frontend filtering search is refactored (#122)
* refactor: use AnyEntity instead of any * refactor: remove any and brand company type * refactor: add typename for user and people * bugfix: await company to be created before displaying it * feature: await deletion before removing the lines * refactor: remove default tyep for filters * refactor: remove default type AnyEntity * refactor: remove USers from filterable types * refactor: do not depend on Filter types in Table * Add tests --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -23,7 +23,10 @@ declare module 'react' {
|
|||||||
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
|
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
type OwnProps<TData, SortField> = {
|
type OwnProps<
|
||||||
|
TData extends { id: string; __typename: 'companies' | 'people' },
|
||||||
|
SortField,
|
||||||
|
> = {
|
||||||
data: Array<TData>;
|
data: Array<TData>;
|
||||||
columns: Array<ColumnDef<TData, any>>;
|
columns: Array<ColumnDef<TData, any>>;
|
||||||
viewName: string;
|
viewName: string;
|
||||||
@ -38,7 +41,7 @@ type OwnProps<TData, SortField> = {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
};
|
};
|
||||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
onFiltersUpdate?: (sorts: Array<SelectedFilterType>) => void;
|
onFiltersUpdate?: (sorts: Array<SelectedFilterType<TData>>) => void;
|
||||||
onFilterSearch?: (
|
onFilterSearch?: (
|
||||||
filter: SearchConfigType<any> | null,
|
filter: SearchConfigType<any> | null,
|
||||||
searchValue: string,
|
searchValue: string,
|
||||||
@ -97,7 +100,10 @@ const StyledTableScrollableContainer = styled.div`
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Table = <TData extends { id: string }, SortField>(
|
const Table = <
|
||||||
|
TData extends { id: string; __typename: 'companies' | 'people' },
|
||||||
|
SortField,
|
||||||
|
>(
|
||||||
{
|
{
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
|
|||||||
@ -72,6 +72,7 @@ const Template: StoryFn<
|
|||||||
export const EditableRelationStory = Template.bind({});
|
export const EditableRelationStory = Template.bind({});
|
||||||
EditableRelationStory.args = {
|
EditableRelationStory.args = {
|
||||||
relation: {
|
relation: {
|
||||||
|
__typename: 'companies',
|
||||||
id: '123',
|
id: '123',
|
||||||
name: 'Heroku',
|
name: 'Heroku',
|
||||||
domain_name: 'heroku.com',
|
domain_name: 'heroku.com',
|
||||||
|
|||||||
@ -50,6 +50,7 @@ it('Checks the EditableRelation editing event bubbles up', async () => {
|
|||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(func).toBeCalledWith({
|
expect(func).toBeCalledWith({
|
||||||
|
__typename: 'companies',
|
||||||
accountOwner: undefined,
|
accountOwner: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
domainName: 'abnb.com',
|
domainName: 'abnb.com',
|
||||||
|
|||||||
@ -3,14 +3,15 @@ import DropdownButton from './DropdownButton';
|
|||||||
import {
|
import {
|
||||||
FilterConfigType,
|
FilterConfigType,
|
||||||
FilterOperandType,
|
FilterOperandType,
|
||||||
|
FilterableFieldsType,
|
||||||
SearchConfigType,
|
SearchConfigType,
|
||||||
SearchableType,
|
SearchableType,
|
||||||
SelectedFilterType,
|
SelectedFilterType,
|
||||||
} from './interface';
|
} from './interface';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps<TData extends FilterableFieldsType> = {
|
||||||
isFilterSelected: boolean;
|
isFilterSelected: boolean;
|
||||||
availableFilters: FilterConfigType[];
|
availableFilters: FilterConfigType<TData>[];
|
||||||
filterSearchResults?: {
|
filterSearchResults?: {
|
||||||
results: {
|
results: {
|
||||||
render: (value: SearchableType) => string;
|
render: (value: SearchableType) => string;
|
||||||
@ -18,30 +19,30 @@ type OwnProps = {
|
|||||||
}[];
|
}[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
};
|
};
|
||||||
onFilterSelect: (filter: SelectedFilterType) => void;
|
onFilterSelect: (filter: SelectedFilterType<TData>) => void;
|
||||||
onFilterSearch: (
|
onFilterSearch: (
|
||||||
filter: SearchConfigType<any> | null,
|
filter: SearchConfigType<any> | null,
|
||||||
searchValue: string,
|
searchValue: string,
|
||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function FilterDropdownButton({
|
export const FilterDropdownButton = <TData extends FilterableFieldsType>({
|
||||||
availableFilters,
|
availableFilters,
|
||||||
filterSearchResults,
|
filterSearchResults,
|
||||||
onFilterSearch,
|
onFilterSearch,
|
||||||
onFilterSelect,
|
onFilterSelect,
|
||||||
isFilterSelected,
|
isFilterSelected,
|
||||||
}: OwnProps) {
|
}: OwnProps<TData>) => {
|
||||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||||
|
|
||||||
const [isOptionUnfolded, setIsOptionUnfolded] = useState(false);
|
const [isOptionUnfolded, setIsOptionUnfolded] = useState(false);
|
||||||
|
|
||||||
const [selectedFilter, setSelectedFilter] = useState<
|
const [selectedFilter, setSelectedFilter] = useState<
|
||||||
FilterConfigType | undefined
|
FilterConfigType<TData> | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
|
||||||
const [selectedFilterOperand, setSelectedFilterOperand] = useState<
|
const [selectedFilterOperand, setSelectedFilterOperand] = useState<
|
||||||
FilterOperandType | undefined
|
FilterOperandType<TData> | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
|
||||||
const resetState = useCallback(() => {
|
const resetState = useCallback(() => {
|
||||||
@ -66,9 +67,9 @@ export function FilterDropdownButton({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const renderSearchResults = (
|
const renderSearchResults = (
|
||||||
filterSearchResults: NonNullable<OwnProps['filterSearchResults']>,
|
filterSearchResults: NonNullable<OwnProps<TData>['filterSearchResults']>,
|
||||||
selectedFilter: FilterConfigType,
|
selectedFilter: FilterConfigType<TData>,
|
||||||
selectedFilterOperand: FilterOperandType,
|
selectedFilterOperand: FilterOperandType<TData>,
|
||||||
) => {
|
) => {
|
||||||
if (filterSearchResults.loading) {
|
if (filterSearchResults.loading) {
|
||||||
return (
|
return (
|
||||||
@ -114,8 +115,8 @@ export function FilterDropdownButton({
|
|||||||
));
|
));
|
||||||
|
|
||||||
function renderFilterDropdown(
|
function renderFilterDropdown(
|
||||||
selectedFilter: FilterConfigType,
|
selectedFilter: FilterConfigType<TData>,
|
||||||
selectedFilterOperand: FilterOperandType,
|
selectedFilterOperand: FilterOperandType<TData>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -161,4 +162,4 @@ export function FilterDropdownButton({
|
|||||||
: renderSelectFilterITems}
|
: renderSelectFilterITems}
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import SortOrFilterChip from './SortOrFilterChip';
|
import SortOrFilterChip from './SortOrFilterChip';
|
||||||
import { FaArrowDown, FaArrowUp } from 'react-icons/fa';
|
import { FaArrowDown, FaArrowUp } from 'react-icons/fa';
|
||||||
import { SelectedFilterType, SelectedSortType } from './interface';
|
import {
|
||||||
|
FilterableFieldsType,
|
||||||
|
SelectedFilterType,
|
||||||
|
SelectedSortType,
|
||||||
|
} from './interface';
|
||||||
|
|
||||||
type OwnProps<SortField> = {
|
type OwnProps<SortField, TData extends FilterableFieldsType> = {
|
||||||
sorts: Array<SelectedSortType<SortField>>;
|
sorts: Array<SelectedSortType<SortField>>;
|
||||||
onRemoveSort: (sortId: SelectedSortType<SortField>['key']) => void;
|
onRemoveSort: (sortId: SelectedSortType<SortField>['key']) => void;
|
||||||
filters: Array<SelectedFilterType>;
|
filters: Array<SelectedFilterType<TData>>;
|
||||||
onRemoveFilter: (filterId: SelectedFilterType['key']) => void;
|
onRemoveFilter: (filterId: SelectedFilterType<TData>['key']) => void;
|
||||||
onCancelClick: () => void;
|
onCancelClick: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,13 +44,13 @@ const StyledCancelButton = styled.button`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function SortAndFilterBar<SortField>({
|
function SortAndFilterBar<SortField, TData extends FilterableFieldsType>({
|
||||||
sorts,
|
sorts,
|
||||||
onRemoveSort,
|
onRemoveSort,
|
||||||
filters,
|
filters,
|
||||||
onRemoveFilter,
|
onRemoveFilter,
|
||||||
onCancelClick,
|
onCancelClick,
|
||||||
}: OwnProps<SortField>) {
|
}: OwnProps<SortField, TData>) {
|
||||||
return (
|
return (
|
||||||
<StyledBar>
|
<StyledBar>
|
||||||
{sorts.map((sort) => {
|
{sorts.map((sort) => {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import {
|
import {
|
||||||
FilterConfigType,
|
FilterConfigType,
|
||||||
|
FilterableFieldsType,
|
||||||
SearchConfigType,
|
SearchConfigType,
|
||||||
SearchableType,
|
SearchableType,
|
||||||
SelectedFilterType,
|
SelectedFilterType,
|
||||||
@ -12,11 +13,11 @@ import { SortDropdownButton } from './SortDropdownButton';
|
|||||||
import { FilterDropdownButton } from './FilterDropdownButton';
|
import { FilterDropdownButton } from './FilterDropdownButton';
|
||||||
import SortAndFilterBar from './SortAndFilterBar';
|
import SortAndFilterBar from './SortAndFilterBar';
|
||||||
|
|
||||||
type OwnProps<SortField> = {
|
type OwnProps<SortField, TData extends FilterableFieldsType> = {
|
||||||
viewName: string;
|
viewName: string;
|
||||||
viewIcon?: ReactNode;
|
viewIcon?: ReactNode;
|
||||||
availableSorts?: Array<SortType<SortField>>;
|
availableSorts?: Array<SortType<SortField>>;
|
||||||
availableFilters?: FilterConfigType[];
|
availableFilters?: FilterConfigType<TData>[];
|
||||||
filterSearchResults?: {
|
filterSearchResults?: {
|
||||||
results: {
|
results: {
|
||||||
render: (value: SearchableType) => string;
|
render: (value: SearchableType) => string;
|
||||||
@ -25,7 +26,7 @@ type OwnProps<SortField> = {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
};
|
};
|
||||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
onFiltersUpdate?: (sorts: Array<SelectedFilterType>) => void;
|
onFiltersUpdate?: (sorts: Array<SelectedFilterType<TData>>) => void;
|
||||||
onFilterSearch?: (
|
onFilterSearch?: (
|
||||||
filter: SearchConfigType<any> | null,
|
filter: SearchConfigType<any> | null,
|
||||||
searchValue: string,
|
searchValue: string,
|
||||||
@ -68,7 +69,7 @@ const StyledFilters = styled.div`
|
|||||||
margin-right: ${(props) => props.theme.spacing(2)};
|
margin-right: ${(props) => props.theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function TableHeader<SortField>({
|
function TableHeader<SortField, TData extends FilterableFieldsType>({
|
||||||
viewName,
|
viewName,
|
||||||
viewIcon,
|
viewIcon,
|
||||||
availableSorts,
|
availableSorts,
|
||||||
@ -77,11 +78,13 @@ function TableHeader<SortField>({
|
|||||||
onSortsUpdate,
|
onSortsUpdate,
|
||||||
onFiltersUpdate,
|
onFiltersUpdate,
|
||||||
onFilterSearch,
|
onFilterSearch,
|
||||||
}: OwnProps<SortField>) {
|
}: OwnProps<SortField, TData>) {
|
||||||
const [sorts, innerSetSorts] = useState<Array<SelectedSortType<SortField>>>(
|
const [sorts, innerSetSorts] = useState<Array<SelectedSortType<SortField>>>(
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
const [filters, innerSetFilters] = useState<Array<SelectedFilterType>>([]);
|
const [filters, innerSetFilters] = useState<Array<SelectedFilterType<TData>>>(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const sortSelect = useCallback(
|
const sortSelect = useCallback(
|
||||||
(newSort: SelectedSortType<SortField>) => {
|
(newSort: SelectedSortType<SortField>) => {
|
||||||
@ -102,7 +105,7 @@ function TableHeader<SortField>({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const filterSelect = useCallback(
|
const filterSelect = useCallback(
|
||||||
(filter: SelectedFilterType) => {
|
(filter: SelectedFilterType<TData>) => {
|
||||||
const newFilters = updateSortOrFilterByKey(filters, filter);
|
const newFilters = updateSortOrFilterByKey(filters, filter);
|
||||||
|
|
||||||
innerSetFilters(newFilters);
|
innerSetFilters(newFilters);
|
||||||
@ -112,7 +115,7 @@ function TableHeader<SortField>({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const filterUnselect = useCallback(
|
const filterUnselect = useCallback(
|
||||||
(filterId: SelectedFilterType['key']) => {
|
(filterId: SelectedFilterType<TData>['key']) => {
|
||||||
const newFilters = filters.filter((filter) => filter.key !== filterId);
|
const newFilters = filters.filter((filter) => filter.key !== filterId);
|
||||||
innerSetFilters(newFilters);
|
innerSetFilters(newFilters);
|
||||||
onFiltersUpdate && onFiltersUpdate(newFilters);
|
onFiltersUpdate && onFiltersUpdate(newFilters);
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { ThemeProvider } from '@emotion/react';
|
|||||||
import { lightTheme } from '../../../../layout/styles/themes';
|
import { lightTheme } from '../../../../layout/styles/themes';
|
||||||
import { FilterDropdownButton } from '../FilterDropdownButton';
|
import { FilterDropdownButton } from '../FilterDropdownButton';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { FilterConfigType, SelectedFilterType } from '../interface';
|
import { FilterableFieldsType, SelectedFilterType } from '../interface';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
SEARCH_PEOPLE_QUERY,
|
SEARCH_PEOPLE_QUERY,
|
||||||
@ -20,7 +20,7 @@ const component = {
|
|||||||
|
|
||||||
export default component;
|
export default component;
|
||||||
|
|
||||||
type OwnProps<FilterProperties> = {
|
type OwnProps<FilterProperties extends FilterableFieldsType> = {
|
||||||
setFilter: (filters: SelectedFilterType<FilterProperties>) => void;
|
setFilter: (filters: SelectedFilterType<FilterProperties>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,8 +98,8 @@ const InnerRegularFilterDropdownButton = ({
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<StyleDiv>
|
<StyleDiv>
|
||||||
<FilterDropdownButton
|
<FilterDropdownButton<Person>
|
||||||
availableFilters={availableFilters as FilterConfigType[]}
|
availableFilters={availableFilters}
|
||||||
isFilterSelected={true}
|
isFilterSelected={true}
|
||||||
onFilterSelect={outerSetFilters}
|
onFilterSelect={outerSetFilters}
|
||||||
filterSearchResults={filterSearchResults}
|
filterSearchResults={filterSearchResults}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { ThemeProvider } from '@emotion/react';
|
|||||||
import { lightTheme } from '../../../../layout/styles/themes';
|
import { lightTheme } from '../../../../layout/styles/themes';
|
||||||
import { FaArrowDown } from 'react-icons/fa';
|
import { FaArrowDown } from 'react-icons/fa';
|
||||||
import { SelectedFilterType } from '../interface';
|
import { SelectedFilterType } from '../interface';
|
||||||
|
import { Person } from '../../../../interfaces/person.interface';
|
||||||
|
|
||||||
const component = {
|
const component = {
|
||||||
title: 'SortAndFilterBar',
|
title: 'SortAndFilterBar',
|
||||||
@ -20,6 +21,31 @@ export const RegularSortAndFilterBar = ({
|
|||||||
removeFunction,
|
removeFunction,
|
||||||
cancelFunction,
|
cancelFunction,
|
||||||
}: OwnProps) => {
|
}: OwnProps) => {
|
||||||
|
const personFilter = {
|
||||||
|
label: 'People',
|
||||||
|
operand: {
|
||||||
|
label: 'Include',
|
||||||
|
id: 'include',
|
||||||
|
whereTemplate: (person: Person) => {
|
||||||
|
return { email: { _eq: person.email } };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
key: 'test_filter',
|
||||||
|
icon: <FaArrowDown />,
|
||||||
|
displayValue: 'john@doedoe.com',
|
||||||
|
value: {
|
||||||
|
__typename: 'people',
|
||||||
|
id: 'test',
|
||||||
|
email: 'john@doedoe.com',
|
||||||
|
firstname: 'John',
|
||||||
|
lastname: 'Doe',
|
||||||
|
phone: '123456789',
|
||||||
|
company: null,
|
||||||
|
creationDate: new Date(),
|
||||||
|
pipes: null,
|
||||||
|
city: 'Paris',
|
||||||
|
},
|
||||||
|
} satisfies SelectedFilterType<Person, Person>;
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={lightTheme}>
|
<ThemeProvider theme={lightTheme}>
|
||||||
<SortAndFilterBar
|
<SortAndFilterBar
|
||||||
@ -42,32 +68,7 @@ export const RegularSortAndFilterBar = ({
|
|||||||
onRemoveSort={removeFunction}
|
onRemoveSort={removeFunction}
|
||||||
onRemoveFilter={removeFunction}
|
onRemoveFilter={removeFunction}
|
||||||
onCancelClick={cancelFunction}
|
onCancelClick={cancelFunction}
|
||||||
filters={[
|
filters={[personFilter] as SelectedFilterType<Person>[]}
|
||||||
{
|
|
||||||
label: 'People',
|
|
||||||
operand: {
|
|
||||||
label: 'Include',
|
|
||||||
id: 'include',
|
|
||||||
whereTemplate: (person) => {
|
|
||||||
return { email: { _eq: person.email } };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
key: 'test_filter',
|
|
||||||
icon: <FaArrowDown />,
|
|
||||||
displayValue: 'john@doedoe.com',
|
|
||||||
value: {
|
|
||||||
id: 'test',
|
|
||||||
email: 'john@doedoe.com',
|
|
||||||
firstname: 'John',
|
|
||||||
lastname: 'Doe',
|
|
||||||
phone: '123456789',
|
|
||||||
company: null,
|
|
||||||
creationDate: new Date(),
|
|
||||||
pipe: null,
|
|
||||||
city: 'Paris',
|
|
||||||
},
|
|
||||||
} satisfies SelectedFilterType,
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,16 @@
|
|||||||
import { Order_By } from '../../../generated/graphql';
|
import { Order_By } from '../../../generated/graphql';
|
||||||
import { BoolExpType, SelectedFilterType, SelectedSortType } from './interface';
|
import {
|
||||||
|
BoolExpType,
|
||||||
|
FilterWhereType,
|
||||||
|
FilterableFieldsType,
|
||||||
|
SelectedFilterType,
|
||||||
|
SelectedSortType,
|
||||||
|
} from './interface';
|
||||||
|
|
||||||
export const reduceFiltersToWhere = <ValueType, WhereTemplateType>(
|
export const reduceFiltersToWhere = <
|
||||||
|
ValueType extends FilterableFieldsType,
|
||||||
|
WhereTemplateType extends FilterWhereType,
|
||||||
|
>(
|
||||||
filters: Array<SelectedFilterType<ValueType, WhereTemplateType>>,
|
filters: Array<SelectedFilterType<ValueType, WhereTemplateType>>,
|
||||||
): BoolExpType<WhereTemplateType> => {
|
): BoolExpType<WhereTemplateType> => {
|
||||||
const where = filters.reduce((acc, filter) => {
|
const where = filters.reduce((acc, filter) => {
|
||||||
|
|||||||
@ -35,8 +35,13 @@ export type SelectedSortType<OrderByTemplate> = SortType<OrderByTemplate> & {
|
|||||||
order: 'asc' | 'desc';
|
order: 'asc' | 'desc';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AnyEntity = {
|
||||||
|
id: string;
|
||||||
|
__typename: string;
|
||||||
|
} & Record<string, any>;
|
||||||
|
|
||||||
export type FilterableFieldsType = Person | Company;
|
export type FilterableFieldsType = Person | Company;
|
||||||
export type FilterWhereType = Person | Company | User;
|
export type FilterWhereType = Person | Company | User | AnyEntity;
|
||||||
|
|
||||||
type FilterConfigGqlType<WhereType> = WhereType extends Company
|
type FilterConfigGqlType<WhereType> = WhereType extends Company
|
||||||
? GraphqlQueryCompany
|
? GraphqlQueryCompany
|
||||||
@ -50,9 +55,14 @@ export type BoolExpType<T> = T extends Company
|
|||||||
? Companies_Bool_Exp
|
? Companies_Bool_Exp
|
||||||
: T extends Person
|
: T extends Person
|
||||||
? People_Bool_Exp
|
? People_Bool_Exp
|
||||||
|
: T extends User
|
||||||
|
? Users_Bool_Exp
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
export type FilterConfigType<FilteredType = any, WhereType = any> = {
|
export type FilterConfigType<
|
||||||
|
FilteredType extends FilterableFieldsType,
|
||||||
|
WhereType extends FilterWhereType = any,
|
||||||
|
> = {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
@ -77,17 +87,33 @@ export type SearchConfigType<SearchType extends SearchableType> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FilterOperandType<
|
export type FilterOperandType<
|
||||||
FilteredType = FilterableFieldsType,
|
FilteredType extends FilterableFieldsType,
|
||||||
WhereType = any,
|
WhereType extends FilterWhereType = AnyEntity,
|
||||||
|
> =
|
||||||
|
| FilterOperandExactMatchType<FilteredType, WhereType>
|
||||||
|
| FilterOperandComparativeType<FilteredType, WhereType>;
|
||||||
|
|
||||||
|
type FilterOperandExactMatchType<
|
||||||
|
FilteredType extends FilterableFieldsType,
|
||||||
|
WhereType extends FilterWhereType,
|
||||||
> = {
|
> = {
|
||||||
label: string;
|
label: 'Equal' | 'Not equal';
|
||||||
id: string;
|
id: 'equal' | 'not-equal';
|
||||||
|
whereTemplate: (value: WhereType) => BoolExpType<FilteredType>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FilterOperandComparativeType<
|
||||||
|
FilteredType extends FilterableFieldsType,
|
||||||
|
WhereType extends FilterWhereType,
|
||||||
|
> = {
|
||||||
|
label: 'Like' | 'Not like' | 'Include';
|
||||||
|
id: 'like' | 'not_like' | 'include';
|
||||||
whereTemplate: (value: WhereType) => BoolExpType<FilteredType>;
|
whereTemplate: (value: WhereType) => BoolExpType<FilteredType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SelectedFilterType<
|
export type SelectedFilterType<
|
||||||
FilteredType = FilterableFieldsType,
|
FilteredType extends FilterableFieldsType,
|
||||||
WhereType = any,
|
WhereType extends FilterWhereType = AnyEntity,
|
||||||
> = {
|
> = {
|
||||||
key: string;
|
key: string;
|
||||||
value: WhereType;
|
value: WhereType;
|
||||||
|
|||||||
@ -36,6 +36,7 @@ describe('Company mappers', () => {
|
|||||||
|
|
||||||
const company = mapToCompany(graphQLCompany);
|
const company = mapToCompany(graphQLCompany);
|
||||||
expect(company).toStrictEqual({
|
expect(company).toStrictEqual({
|
||||||
|
__typename: 'companies',
|
||||||
id: graphQLCompany.id,
|
id: graphQLCompany.id,
|
||||||
name: graphQLCompany.name,
|
name: graphQLCompany.name,
|
||||||
domainName: graphQLCompany.domain_name,
|
domainName: graphQLCompany.domain_name,
|
||||||
@ -43,6 +44,7 @@ describe('Company mappers', () => {
|
|||||||
employees: graphQLCompany.employees,
|
employees: graphQLCompany.employees,
|
||||||
address: graphQLCompany.address,
|
address: graphQLCompany.address,
|
||||||
accountOwner: {
|
accountOwner: {
|
||||||
|
__typename: 'users',
|
||||||
id: '7af20dea-0412-4c4c-8b13-d6f0e6e09e87',
|
id: '7af20dea-0412-4c4c-8b13-d6f0e6e09e87',
|
||||||
email: 'john@example.com',
|
email: 'john@example.com',
|
||||||
displayName: 'John Doe',
|
displayName: 'John Doe',
|
||||||
@ -66,9 +68,11 @@ describe('Company mappers', () => {
|
|||||||
id: '522d4ec4-c46b-4360-a0a7-df8df170be81',
|
id: '522d4ec4-c46b-4360-a0a7-df8df170be81',
|
||||||
email: 'john@example.com',
|
email: 'john@example.com',
|
||||||
displayName: 'John Doe',
|
displayName: 'John Doe',
|
||||||
|
__typename: 'users',
|
||||||
},
|
},
|
||||||
creationDate: now,
|
creationDate: now,
|
||||||
};
|
__typename: 'companies',
|
||||||
|
} satisfies Company;
|
||||||
const graphQLCompany = mapToGqlCompany(company);
|
const graphQLCompany = mapToGqlCompany(company);
|
||||||
expect(graphQLCompany).toStrictEqual({
|
expect(graphQLCompany).toStrictEqual({
|
||||||
id: company.id,
|
id: company.id,
|
||||||
|
|||||||
@ -28,6 +28,7 @@ describe('Person mappers', () => {
|
|||||||
|
|
||||||
const person = mapToPerson(graphQLPerson);
|
const person = mapToPerson(graphQLPerson);
|
||||||
expect(person).toStrictEqual({
|
expect(person).toStrictEqual({
|
||||||
|
__typename: 'people',
|
||||||
id: graphQLPerson.id,
|
id: graphQLPerson.id,
|
||||||
firstname: graphQLPerson.firstname,
|
firstname: graphQLPerson.firstname,
|
||||||
lastname: graphQLPerson.lastname,
|
lastname: graphQLPerson.lastname,
|
||||||
@ -36,6 +37,7 @@ describe('Person mappers', () => {
|
|||||||
city: graphQLPerson.city,
|
city: graphQLPerson.city,
|
||||||
phone: graphQLPerson.phone,
|
phone: graphQLPerson.phone,
|
||||||
company: {
|
company: {
|
||||||
|
__typename: 'companies',
|
||||||
id: '7af20dea-0412-4c4c-8b13-d6f0e6e09e87',
|
id: '7af20dea-0412-4c4c-8b13-d6f0e6e09e87',
|
||||||
accountOwner: undefined,
|
accountOwner: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
|
|||||||
@ -28,6 +28,7 @@ describe('User mappers', () => {
|
|||||||
|
|
||||||
const User = mapToUser(graphQLUser);
|
const User = mapToUser(graphQLUser);
|
||||||
expect(User).toStrictEqual({
|
expect(User).toStrictEqual({
|
||||||
|
__typename: 'users',
|
||||||
id: graphQLUser.id,
|
id: graphQLUser.id,
|
||||||
displayName: graphQLUser.display_name,
|
displayName: graphQLUser.display_name,
|
||||||
email: graphQLUser.email,
|
email: graphQLUser.email,
|
||||||
@ -47,6 +48,7 @@ describe('User mappers', () => {
|
|||||||
const now = new Date();
|
const now = new Date();
|
||||||
now.setMilliseconds(0);
|
now.setMilliseconds(0);
|
||||||
const user = {
|
const user = {
|
||||||
|
__typename: 'users',
|
||||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
|
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
|
||||||
displayName: 'John Doe',
|
displayName: 'John Doe',
|
||||||
email: 'john.doe@gmail.com',
|
email: 'john.doe@gmail.com',
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { GraphqlQueryUser, User, mapToUser } from './user.interface';
|
|||||||
import { GraphqlQueryPipe } from './pipe.interface';
|
import { GraphqlQueryPipe } from './pipe.interface';
|
||||||
|
|
||||||
export type Company = {
|
export type Company = {
|
||||||
|
__typename: 'companies';
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
domainName?: string;
|
domainName?: string;
|
||||||
@ -43,6 +44,7 @@ export type GraphqlMutationCompany = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const mapToCompany = (company: GraphqlQueryCompany): Company => ({
|
export const mapToCompany = (company: GraphqlQueryCompany): Company => ({
|
||||||
|
__typename: 'companies',
|
||||||
id: company.id,
|
id: company.id,
|
||||||
employees: company.employees,
|
employees: company.employees,
|
||||||
name: company.name,
|
name: company.name,
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
import { Pipe } from './pipe.interface';
|
import { Pipe } from './pipe.interface';
|
||||||
|
|
||||||
export type Person = {
|
export type Person = {
|
||||||
|
__typename: 'people';
|
||||||
id: string;
|
id: string;
|
||||||
firstname?: string;
|
firstname?: string;
|
||||||
lastname?: string;
|
lastname?: string;
|
||||||
@ -44,10 +45,11 @@ export type GraphqlMutationPerson = {
|
|||||||
city?: string;
|
city?: string;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
company_id?: string;
|
company_id?: string;
|
||||||
__typename: string;
|
__typename: 'people';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mapToPerson = (person: GraphqlQueryPerson): Person => ({
|
export const mapToPerson = (person: GraphqlQueryPerson): Person => ({
|
||||||
|
__typename: 'people',
|
||||||
id: person.id,
|
id: person.id,
|
||||||
firstname: person.firstname,
|
firstname: person.firstname,
|
||||||
lastname: person.lastname,
|
lastname: person.lastname,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
} from './workspace_member.interface';
|
} from './workspace_member.interface';
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
|
__typename: 'users';
|
||||||
id: string;
|
id: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
@ -28,6 +29,7 @@ export type GraphqlMutationUser = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const mapToUser = (user: GraphqlQueryUser): User => ({
|
export const mapToUser = (user: GraphqlQueryUser): User => ({
|
||||||
|
__typename: 'users',
|
||||||
id: user.id,
|
id: user.id,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
displayName: user.display_name,
|
displayName: user.display_name,
|
||||||
|
|||||||
@ -17,6 +17,7 @@ export const NavbarOnCompanies = () => {
|
|||||||
<MemoryRouter initialEntries={['/companies']}>
|
<MemoryRouter initialEntries={['/companies']}>
|
||||||
<Navbar
|
<Navbar
|
||||||
user={{
|
user={{
|
||||||
|
__typename: 'users',
|
||||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
|
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
|
||||||
email: 'charles@twenty.com',
|
email: 'charles@twenty.com',
|
||||||
displayName: 'Charles Bochet',
|
displayName: 'Charles Bochet',
|
||||||
|
|||||||
@ -25,10 +25,7 @@ import {
|
|||||||
Companies_Bool_Exp,
|
Companies_Bool_Exp,
|
||||||
Companies_Order_By,
|
Companies_Order_By,
|
||||||
} from '../../generated/graphql';
|
} from '../../generated/graphql';
|
||||||
import {
|
import { SelectedFilterType } from '../../components/table/table-header/interface';
|
||||||
FilterConfigType,
|
|
||||||
SelectedFilterType,
|
|
||||||
} from '../../components/table/table-header/interface';
|
|
||||||
import { useSearch } from '../../services/search/search';
|
import { useSearch } from '../../services/search/search';
|
||||||
import ActionBar from '../../components/table/action-bar/ActionBar';
|
import ActionBar from '../../components/table/action-bar/ActionBar';
|
||||||
|
|
||||||
@ -66,7 +63,7 @@ function Companies() {
|
|||||||
}
|
}
|
||||||
}, [loading, setInternalData, data]);
|
}, [loading, setInternalData, data]);
|
||||||
|
|
||||||
const addEmptyRow = useCallback(() => {
|
const addEmptyRow = useCallback(async () => {
|
||||||
const newCompany: Company = {
|
const newCompany: Company = {
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
name: '',
|
name: '',
|
||||||
@ -76,14 +73,15 @@ function Companies() {
|
|||||||
pipes: [],
|
pipes: [],
|
||||||
creationDate: new Date(),
|
creationDate: new Date(),
|
||||||
accountOwner: null,
|
accountOwner: null,
|
||||||
|
__typename: 'companies',
|
||||||
};
|
};
|
||||||
insertCompany(newCompany);
|
await insertCompany(newCompany);
|
||||||
setInternalData([newCompany, ...internalData]);
|
setInternalData([newCompany, ...internalData]);
|
||||||
refetch();
|
refetch();
|
||||||
}, [internalData, setInternalData, refetch]);
|
}, [internalData, setInternalData, refetch]);
|
||||||
|
|
||||||
const deleteRows = useCallback(() => {
|
const deleteRows = useCallback(async () => {
|
||||||
deleteCompanies(selectedRowIds);
|
await deleteCompanies(selectedRowIds);
|
||||||
setInternalData([
|
setInternalData([
|
||||||
...internalData.filter((row) => !selectedRowIds.includes(row.id)),
|
...internalData.filter((row) => !selectedRowIds.includes(row.id)),
|
||||||
]);
|
]);
|
||||||
@ -111,7 +109,7 @@ function Companies() {
|
|||||||
viewName="All Companies"
|
viewName="All Companies"
|
||||||
viewIcon={<FaList />}
|
viewIcon={<FaList />}
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
availableFilters={availableFilters as Array<FilterConfigType>}
|
availableFilters={availableFilters}
|
||||||
filterSearchResults={filterSearchResults}
|
filterSearchResults={filterSearchResults}
|
||||||
onSortsUpdate={updateSorts}
|
onSortsUpdate={updateSorts}
|
||||||
onFiltersUpdate={updateFilters}
|
onFiltersUpdate={updateFilters}
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import { lightTheme } from '../../../layout/styles/themes';
|
|||||||
import { GET_COMPANIES } from '../../../services/companies';
|
import { GET_COMPANIES } from '../../../services/companies';
|
||||||
import { mockData } from '../__tests__/__data__/mock-data';
|
import { mockData } from '../__tests__/__data__/mock-data';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { SEARCH_COMPANY_QUERY } from '../../../services/search/search';
|
||||||
|
import { mockCompanySearchData } from '../../../services/search/__data__/mock-search-data';
|
||||||
|
|
||||||
const component = {
|
const component = {
|
||||||
title: 'Companies',
|
title: 'Companies',
|
||||||
@ -42,6 +44,27 @@ const mocks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
query: SEARCH_COMPANY_QUERY,
|
||||||
|
variables: { where: { name: { _ilike: '%%' } }, limit: 5 },
|
||||||
|
},
|
||||||
|
result: mockCompanySearchData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
query: GET_COMPANIES,
|
||||||
|
variables: {
|
||||||
|
orderBy: [{ created_at: 'desc' }],
|
||||||
|
where: { domain_name: { _eq: 'linkedin-searched.com' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: {
|
||||||
|
companies: mockData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const CompaniesDefault = () => (
|
export const CompaniesDefault = () => (
|
||||||
|
|||||||
@ -136,3 +136,28 @@ it('Checks insert data is appending a new line', async () => {
|
|||||||
expect(tableRows.length).toBe(7);
|
expect(tableRows.length).toBe(7);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Checks filters are working', async () => {
|
||||||
|
const { getByText } = render(<CompaniesDefault />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(getByText('Airbnb')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterDropdown = getByText('Filter');
|
||||||
|
fireEvent.click(filterDropdown);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(getByText('Url')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
const urlFilter = getByText('Url');
|
||||||
|
fireEvent.click(urlFilter);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(getByText('linkedin-searched.com')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterByLinkedinOption = getByText('linkedin-searched.com');
|
||||||
|
fireEvent.click(filterByLinkedinOption);
|
||||||
|
});
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export const mockData: Array<GraphqlQueryCompany> = [
|
|||||||
name: 'Airbnb',
|
name: 'Airbnb',
|
||||||
created_at: '2023-04-26T10:08:54.724515+00:00',
|
created_at: '2023-04-26T10:08:54.724515+00:00',
|
||||||
address: '17 rue de clignancourt',
|
address: '17 rue de clignancourt',
|
||||||
employees: 12,
|
employees: '12',
|
||||||
account_owner: null,
|
account_owner: null,
|
||||||
__typename: 'companies',
|
__typename: 'companies',
|
||||||
},
|
},
|
||||||
@ -16,8 +16,8 @@ export const mockData: Array<GraphqlQueryCompany> = [
|
|||||||
domain_name: 'aircall.io',
|
domain_name: 'aircall.io',
|
||||||
name: 'Aircall',
|
name: 'Aircall',
|
||||||
created_at: '2023-04-26T10:12:42.33625+00:00',
|
created_at: '2023-04-26T10:12:42.33625+00:00',
|
||||||
address: null,
|
address: '',
|
||||||
employees: 1,
|
employees: '1',
|
||||||
account_owner: null,
|
account_owner: null,
|
||||||
__typename: 'companies',
|
__typename: 'companies',
|
||||||
},
|
},
|
||||||
@ -26,8 +26,8 @@ export const mockData: Array<GraphqlQueryCompany> = [
|
|||||||
domain_name: 'algolia.com',
|
domain_name: 'algolia.com',
|
||||||
name: 'Algolia',
|
name: 'Algolia',
|
||||||
created_at: '2023-04-26T10:10:32.530184+00:00',
|
created_at: '2023-04-26T10:10:32.530184+00:00',
|
||||||
address: null,
|
address: '',
|
||||||
employees: 1,
|
employees: '1',
|
||||||
account_owner: null,
|
account_owner: null,
|
||||||
__typename: 'companies',
|
__typename: 'companies',
|
||||||
},
|
},
|
||||||
@ -36,8 +36,8 @@ export const mockData: Array<GraphqlQueryCompany> = [
|
|||||||
domain_name: 'apple.com',
|
domain_name: 'apple.com',
|
||||||
name: 'Apple',
|
name: 'Apple',
|
||||||
created_at: '2023-03-21T06:30:25.39474+00:00',
|
created_at: '2023-03-21T06:30:25.39474+00:00',
|
||||||
address: null,
|
address: '',
|
||||||
employees: 10,
|
employees: '10',
|
||||||
account_owner: null,
|
account_owner: null,
|
||||||
__typename: 'companies',
|
__typename: 'companies',
|
||||||
},
|
},
|
||||||
@ -47,7 +47,7 @@ export const mockData: Array<GraphqlQueryCompany> = [
|
|||||||
name: 'BeReal',
|
name: 'BeReal',
|
||||||
created_at: '2023-04-26T10:13:29.712485+00:00',
|
created_at: '2023-04-26T10:13:29.712485+00:00',
|
||||||
address: '10 rue de la Paix',
|
address: '10 rue de la Paix',
|
||||||
employees: 1,
|
employees: '1',
|
||||||
account_owner: null,
|
account_owner: null,
|
||||||
__typename: 'companies',
|
__typename: 'companies',
|
||||||
},
|
},
|
||||||
@ -56,8 +56,8 @@ export const mockData: Array<GraphqlQueryCompany> = [
|
|||||||
domain_name: 'claap.com',
|
domain_name: 'claap.com',
|
||||||
name: 'Claap',
|
name: 'Claap',
|
||||||
created_at: '2023-04-26T10:09:25.656555+00:00',
|
created_at: '2023-04-26T10:09:25.656555+00:00',
|
||||||
address: null,
|
address: '',
|
||||||
employees: 1,
|
employees: '1',
|
||||||
account_owner: null,
|
account_owner: null,
|
||||||
__typename: 'companies',
|
__typename: 'companies',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -82,7 +82,7 @@ export const availableFilters = [
|
|||||||
value: mapToCompany(company),
|
value: mapToCompany(company),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
selectedValueRender: (company) => company.name,
|
selectedValueRender: (company) => company.name || '',
|
||||||
operands: [
|
operands: [
|
||||||
{
|
{
|
||||||
label: 'Equal',
|
label: 'Equal',
|
||||||
@ -99,7 +99,7 @@ export const availableFilters = [
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as FilterConfigType<Company, Company>,
|
} satisfies FilterConfigType<Company, Company>,
|
||||||
{
|
{
|
||||||
key: 'company_domain_name',
|
key: 'company_domain_name',
|
||||||
label: 'Url',
|
label: 'Url',
|
||||||
@ -114,7 +114,7 @@ export const availableFilters = [
|
|||||||
value: mapToCompany(company),
|
value: mapToCompany(company),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
selectedValueRender: (company) => company.domainName,
|
selectedValueRender: (company) => company.domainName || '',
|
||||||
operands: [
|
operands: [
|
||||||
{
|
{
|
||||||
label: 'Equal',
|
label: 'Equal',
|
||||||
@ -131,7 +131,7 @@ export const availableFilters = [
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as FilterConfigType<Company, Company>,
|
} satisfies FilterConfigType<Company, Company>,
|
||||||
];
|
];
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Company>();
|
const columnHelper = createColumnHelper<Company>();
|
||||||
@ -253,6 +253,7 @@ export const useCompaniesColumns = () => {
|
|||||||
company.accountOwner.id = relation.id;
|
company.accountOwner.id = relation.id;
|
||||||
} else {
|
} else {
|
||||||
company.accountOwner = {
|
company.accountOwner = {
|
||||||
|
__typename: 'users',
|
||||||
id: relation.id,
|
id: relation.id,
|
||||||
email: relation.email,
|
email: relation.email,
|
||||||
displayName: relation.displayName,
|
displayName: relation.displayName,
|
||||||
|
|||||||
@ -19,10 +19,7 @@ import {
|
|||||||
} from '../../services/people';
|
} from '../../services/people';
|
||||||
import { useSearch } from '../../services/search/search';
|
import { useSearch } from '../../services/search/search';
|
||||||
import { People_Bool_Exp } from '../../generated/graphql';
|
import { People_Bool_Exp } from '../../generated/graphql';
|
||||||
import {
|
import { SelectedFilterType } from '../../components/table/table-header/interface';
|
||||||
FilterConfigType,
|
|
||||||
SelectedFilterType,
|
|
||||||
} from '../../components/table/table-header/interface';
|
|
||||||
import {
|
import {
|
||||||
reduceFiltersToWhere,
|
reduceFiltersToWhere,
|
||||||
reduceSortsToOrderBy,
|
reduceSortsToOrderBy,
|
||||||
@ -64,7 +61,8 @@ function People() {
|
|||||||
}, [loading, setInternalData, data]);
|
}, [loading, setInternalData, data]);
|
||||||
|
|
||||||
const addEmptyRow = useCallback(async () => {
|
const addEmptyRow = useCallback(async () => {
|
||||||
const newCompany: Person = {
|
const newPerson: Person = {
|
||||||
|
__typename: 'people',
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
firstname: '',
|
firstname: '',
|
||||||
lastname: '',
|
lastname: '',
|
||||||
@ -75,13 +73,13 @@ function People() {
|
|||||||
creationDate: new Date(),
|
creationDate: new Date(),
|
||||||
city: '',
|
city: '',
|
||||||
};
|
};
|
||||||
await insertPerson(newCompany);
|
await insertPerson(newPerson);
|
||||||
setInternalData([newCompany, ...internalData]);
|
setInternalData([newPerson, ...internalData]);
|
||||||
refetch();
|
refetch();
|
||||||
}, [internalData, setInternalData, refetch]);
|
}, [internalData, setInternalData, refetch]);
|
||||||
|
|
||||||
const deleteRows = useCallback(() => {
|
const deleteRows = useCallback(async () => {
|
||||||
deletePeople(selectedRowIds);
|
await deletePeople(selectedRowIds);
|
||||||
setInternalData([
|
setInternalData([
|
||||||
...internalData.filter((row) => !selectedRowIds.includes(row.id)),
|
...internalData.filter((row) => !selectedRowIds.includes(row.id)),
|
||||||
]);
|
]);
|
||||||
@ -109,7 +107,7 @@ function People() {
|
|||||||
viewName="All People"
|
viewName="All People"
|
||||||
viewIcon={<FaList />}
|
viewIcon={<FaList />}
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
availableFilters={availableFilters as Array<FilterConfigType>}
|
availableFilters={availableFilters}
|
||||||
filterSearchResults={filterSearchResults}
|
filterSearchResults={filterSearchResults}
|
||||||
onSortsUpdate={updateSorts}
|
onSortsUpdate={updateSorts}
|
||||||
onFiltersUpdate={updateFilters}
|
onFiltersUpdate={updateFilters}
|
||||||
|
|||||||
@ -227,7 +227,7 @@ export const availableFilters = [
|
|||||||
companyFilter,
|
companyFilter,
|
||||||
emailFilter,
|
emailFilter,
|
||||||
cityFilter,
|
cityFilter,
|
||||||
];
|
] satisfies FilterConfigType<Person, any>[];
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Person>();
|
const columnHelper = createColumnHelper<Person>();
|
||||||
|
|
||||||
|
|||||||
36
front/src/services/search/__data__/mock-search-data.ts
Normal file
36
front/src/services/search/__data__/mock-search-data.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export const mockCompanySearchData = {
|
||||||
|
data: {
|
||||||
|
searchResults: [
|
||||||
|
{
|
||||||
|
id: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408',
|
||||||
|
name: 'Linkedin',
|
||||||
|
domain_name: 'linkedin-searched.com',
|
||||||
|
__typename: 'companies',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '118995f3-5d81-46d6-bf83-f7fd33ea6102',
|
||||||
|
name: 'Facebook',
|
||||||
|
domain_name: 'facebook-searched.com',
|
||||||
|
__typename: 'companies',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '04b2e9f5-0713-40a5-8216-82802401d33e',
|
||||||
|
name: 'Qonto',
|
||||||
|
domain_name: 'qonto-searched.com',
|
||||||
|
__typename: 'companies',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '460b6fb1-ed89-413a-b31a-962986e67bb4',
|
||||||
|
name: 'Microsoft',
|
||||||
|
domain_name: 'microsoft-searched.com',
|
||||||
|
__typename: 'companies',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '0d940997-c21e-4ec2-873b-de4264d89025',
|
||||||
|
name: 'Google',
|
||||||
|
domain_name: 'google-searched.com',
|
||||||
|
__typename: 'companies',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user