Skip to content

Commit

Permalink
added fk search support on fast search field
Browse files Browse the repository at this point in the history
  • Loading branch information
mmadariaga committed Sep 8, 2023
1 parent dd2d5f7 commit 8937ef9
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 16 deletions.
2 changes: 1 addition & 1 deletion demo/src/entities/Client/Client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ const client: EntityInterface = {
toStr: (row: ClientPropertyList<EntityValue>) => row.iden as string,
properties,
columns: [
{ name: 'iden', size: 20 },
{ name: 'platform', size: 20 },
{ name: 'iden', size: 20 },
{ name: 'desktopLicences', size: 20 },
{ name: 'mobileLicences', size: 20 },
{ name: 'language', size: 20 },
Expand Down
2 changes: 1 addition & 1 deletion library/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@irontec/ivoz-ui",
"version": "1.1.8",
"version": "1.1.9",
"description": "UI library used in ivozprovider",
"license": "GPL-3.0",
"main": "index.js",
Expand Down
10 changes: 10 additions & 0 deletions library/src/components/List/Content/FastSearchField.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { styled } from '@mui/material';
import FastSearchField from './FastSearchField';

export const StyledFastSearchField = styled(FastSearchField)(() => {
return {
'& .MuiAutocomplete-endAdornment': {
top: 'auto',
},
};
});
47 changes: 45 additions & 2 deletions library/src/components/List/Content/FastSearchField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import EntityService from '../../../services/entity/EntityService';
import { StyledSearchTextField } from '../../../services/form/Field/TextField/TextField.styles';

import { useStoreActions, useStoreState } from 'store';
import { DropdownArrayChoices, isPropertyFk } from '../../../services';
import { StyledAutocomplete } from '../../../services/form/Field/Autocomplete/Autocomplete.styles';
import useFirstColumn from './hook/useFirstColumn';
import useFirstColumnCriteria from './hook/useFirstColumnCriteria';

interface FastSearchFieldProps {
export interface FastSearchFieldProps {
className?: string;
path: string;
entityService: EntityService;
ignoreColumn: string | undefined;
Expand All @@ -16,7 +20,7 @@ const FastSearchField = (
props: FastSearchFieldProps,
ref: ForwardedRef<any>
): JSX.Element => {
const { path, entityService, ignoreColumn } = props;
const { className, path, entityService, ignoreColumn } = props;

const queryStringCriteria = useStoreState((state) => [
...state.route.queryStringCriteria,
Expand All @@ -31,6 +35,16 @@ const FastSearchField = (
ignoreColumn,
});

const [firstColumnName, firstColumnSpec] = useFirstColumn({
entityService,
ignoreColumn,
});

const isFk = isPropertyFk(firstColumnSpec);
const foreignEntities = useStoreState((state) => state.list.fkChoices);
const fkChoices =
(foreignEntities[firstColumnName] as DropdownArrayChoices) || [];

const [value, setValue] = useState(firstColumnCriteria?.value || '');

const changeHandler: React.ChangeEventHandler<HTMLInputElement> = ({
Expand All @@ -57,6 +71,7 @@ const FastSearchField = (
}

let match = false;
let matchIdx: string | undefined;
for (const idx in queryStringCriteria) {
if (queryStringCriteria[idx].name !== firstColumnCriteria.name) {
continue;
Expand All @@ -67,12 +82,15 @@ const FastSearchField = (
}

queryStringCriteria[idx] = firstColumnCriteria;
matchIdx = idx;
match = true;
break;
}

if (!match) {
queryStringCriteria.push(firstColumnCriteria);
} else if (value === '' && matchIdx) {
queryStringCriteria.splice(parseInt(matchIdx, 10), 1);
}

setQueryStringCriteria(queryStringCriteria);
Expand All @@ -83,6 +101,31 @@ const FastSearchField = (
return () => clearTimeout(timeOutId);
}, [value, firstColumnCriteria]);

if (isFk) {
return (
<StyledAutocomplete
className={className}
name='fast_search'
label=''
placeholder='Search'
value={value}
multiple={false}
required={false}
disabled={false}
onChange={changeHandler}
onBlur={() => {
/* noop */
}}
choices={fkChoices}
disableClearable={false}
hasChanged={false}
InputProps={{
startAdornment: <SearchIcon />,
}}
/>
);
}

return (
<StyledSearchTextField
name='fast_search'
Expand Down
5 changes: 2 additions & 3 deletions library/src/components/List/Content/ListContentHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import { ContentFilterDialog } from '../Filter/ContentFilterDialog';
import DeleteRowsButton from './CTA/DeleteRowsButton';
import { StyledActionButtonContainer, StyledLink } from './ListContent.styles';
import { MultiselectMoreChildEntityLinks } from './Shared/MultiselectMoreChildEntityLinks';

import FastSearchField from './FastSearchField';
import { StyledFastSearchField } from './FastSearchField.styles';

interface ListContentProps {
path: string;
Expand Down Expand Up @@ -85,7 +84,7 @@ const ListContentHeader = (
return (
<StyledActionButtonContainer ref={ref} className={'list-content-header'}>
<Box className='buttons start'>
<FastSearchField
<StyledFastSearchField
path={path}
entityService={entityService}
ignoreColumn={ignoreColumn}
Expand Down
30 changes: 30 additions & 0 deletions library/src/components/List/Content/hook/useFirstColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { PropertySpec } from '../../../../services';
import EntityService from '../../../../services/entity/EntityService';
import { useStoreState } from '../../../../store';

type useFirstColumnProps = {
entityService: EntityService;
ignoreColumn: string | undefined;
};

const useFirstColumn = (props: useFirstColumnProps): [string, PropertySpec] => {
const { entityService, ignoreColumn } = props;

const storeState = useStoreState(
(state) => state,
() => {
return true;
}
);

const columns = entityService.getCollectionColumns(storeState);
const firstColumnKey = Object.keys(columns).find(
(columnKey) => columnKey !== ignoreColumn
) as string;

const firstColumnSpec = entityService.getProperties()[firstColumnKey];

return [firstColumnKey, firstColumnSpec];
};

export default useFirstColumn;
9 changes: 6 additions & 3 deletions library/src/components/List/Filter/ContentFilterDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,19 @@ export function ContentFilterDialog(
setAnchorEl,
} = props;

const queryStringCriteria: CriteriaFilterValues = useStoreState(
const queryStringCriteria = useStoreState(
(state) => state.route.queryStringCriteria
);
const foreignEntities = useStoreState((state) => state.list.fkChoices);
const setQueryStringCriteria = useStoreActions((actions) => {
return actions.route.setQueryStringCriteria;
});
const setFkChoices = useStoreActions((actions) => {
return actions.list.setFkChoices;
});

const mobile = useMediaQuery(useTheme().breakpoints.down('md'));
const [loading, setLoading] = useState<boolean>(true);
const [foreignEntities, setForeignEntities] = useState<any>({});
const [criteria, setCriteria] =
useState<CriteriaFilterValues>(queryStringCriteria);

Expand All @@ -76,7 +79,7 @@ export function ContentFilterDialog(
match,
filterContext: true,
}).then((foreignEntities: any) => {
setForeignEntities(foreignEntities);
setFkChoices(foreignEntities);
setLoading(false);
});
});
Expand Down
14 changes: 12 additions & 2 deletions library/src/components/List/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { ListContent } from './Content/';
import { CriteriaFilterValues } from './Filter/ContentFilterDialog';
import { criteriaToArray, queryStringToCriteria } from './List.helpers';
import useQueryStringParams from './useQueryStringParams';
import useFirstColumn from './Content/hook/useFirstColumn';
import { isPropertyFk } from '../../services';

type ListProps = {
path: string;
Expand Down Expand Up @@ -87,6 +89,14 @@ const List = function (props: ListProps) {
filterBy.push(Object.values(params).pop() as string);
}

const ignoreColumn = filterBy[0];
const [, firstColumnSpec] = useFirstColumn({
entityService,
ignoreColumn,
});
const isFirstColumnFk = isPropertyFk(firstColumnSpec);
const preload = currentQueryParams.length > 0 || isFirstColumnFk;

const filterValues: Array<string> = [];
const entityFilterValues = currentRoute?.filterValues || {};
for (const idx in entityFilterValues) {
Expand Down Expand Up @@ -291,8 +301,8 @@ const List = function (props: ListProps) {
empty={empty}
childEntities={currentRoute?.children || []}
path={path}
ignoreColumn={filterBy[0]}
preloadData={currentQueryParams.length > 0}
ignoreColumn={ignoreColumn}
preloadData={preload}
entityService={entityService}
cancelToken={cancelToken}
match={match}
Expand Down
14 changes: 10 additions & 4 deletions library/src/services/form/Field/Autocomplete/Autocomplete.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box } from '@mui/material';
import { Box, InputProps } from '@mui/material';
import MuiAutocomplete from '@mui/material/Autocomplete';
import React, {
JSXElementConstructor,
Expand All @@ -19,6 +19,7 @@ export interface AutocompleteProps {
className?: string;
name: string;
label: string | ReactElement<any, string | JSXElementConstructor<any>>;
placeholder?: string;
value: any;
multiple: boolean;
required: boolean;
Expand All @@ -30,6 +31,8 @@ export interface AutocompleteProps {
errorMsg?: React.ReactNode;
helperText?: string | React.ReactNode;
hasChanged: boolean;
InputProps?: Partial<InputProps>;
disableClearable?: boolean;
}

const Autocomplete = (props: AutocompleteProps): JSX.Element | null => {
Expand Down Expand Up @@ -173,9 +176,12 @@ const Autocomplete = (props: AutocompleteProps): JSX.Element | null => {
[name, label, required, disabled, error, errorMsg, helperText]
);

const disableClearable = arrayChoices.find((item) => item.id === '__null__')
? false
: true;
let disableClearable = props.disableClearable;
if (disableClearable === undefined) {
disableClearable = arrayChoices.find((item) => item.id === '__null__')
? false
: true;
}

let autocompleteValue;
if (multiple) {
Expand Down
13 changes: 13 additions & 0 deletions library/src/store/list.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { Action, action, Thunk, thunk } from 'easy-peasy';
import { EntityValues } from '../services/entity/EntityService';
import { NullablePropertyFkChoices } from '../entities';

type FkChoicesType = {
[key: string]: NullablePropertyFkChoices;
};

export interface ListState {
reloadTimestamp: number;
parentRow: EntityValues | undefined;
rows: Array<EntityValues>;
headers: Record<string, string>;
fkChoices: FkChoicesType;
}

interface ListActions {
reload: Action<ListState>;
setParentRow: Action<ListState, undefined | EntityValues>;
setRows: Action<ListState, Array<EntityValues>>;
setHeaders: Action<ListState, Record<string, string>>;
setFkChoices: Action<ListState, FkChoicesType>;
reset: Thunk<ListState, undefined, unknown>;
}

Expand All @@ -23,6 +30,7 @@ const list: ListStore = {
parentRow: undefined,
rows: [],
headers: {},
fkChoices: {},

// actions
reload: action<ListState>((state) => {
Expand All @@ -41,10 +49,15 @@ const list: ListStore = {
state.headers = { ...headers };
}),

setFkChoices: action<ListState, FkChoicesType>((state, fkChoices) => {
state.fkChoices = fkChoices;
}),

// thunks
reset: thunk<ListActions, undefined, unknown>(async (actions) => {
actions.setRows([]);
actions.setHeaders({});
actions.setFkChoices({});
}),
};

Expand Down

0 comments on commit 8937ef9

Please sign in to comment.