Skip to content

Commit

Permalink
Merge pull request #5808 from beyondessential/dev
Browse files Browse the repository at this point in the history
merge: update branch with latest dev
  • Loading branch information
avaek authored Jul 24, 2024
2 parents 32c2bf0 + 439f16b commit ba25cfa
Show file tree
Hide file tree
Showing 55 changed files with 886 additions and 199 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React, { useState } from 'react';
import { Autocomplete } from './Autocomplete';
import { useVizConfigContext } from '../../context';
import { useEntityByCode, useLocations } from '../../api';
import { EntityOptionLabel } from '../../../widgets';

export const LocationField = () => {
const [locationSearch, setLocationSearch] = useState('');
Expand Down Expand Up @@ -39,7 +40,9 @@ export const LocationField = () => {
setLocationSearch(newValue);
}}
getOptionLabel={option => option.name}
renderOption={option => <span>{option.name}</span>}
renderOption={option => {
return <EntityOptionLabel {...option} />;
}}
onChange={(_, newLocation) => {
setLocation(newLocation);
}}
Expand Down
3 changes: 3 additions & 0 deletions packages/admin-panel/src/autocomplete/Autocomplete.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const Autocomplete = props => {
canCreateNewOptions,
allowMultipleValues,
optionLabelKey,
renderOption,
muiProps,
error,
tooltip,
Expand Down Expand Up @@ -110,6 +111,7 @@ export const Autocomplete = props => {
options={options}
getOptionSelected={getOptionSelected}
getOptionLabel={getOptionLabel}
renderOption={renderOption}
loading={isLoading}
onChange={onChangeSelection}
onInputChange={(event, newValue) => {
Expand All @@ -134,6 +136,7 @@ Autocomplete.propTypes = {
options: PropTypes.array.isRequired,
getOptionSelected: PropTypes.func.isRequired,
getOptionLabel: PropTypes.func.isRequired,
renderOption: PropTypes.func,
isLoading: PropTypes.bool,
onChangeSelection: PropTypes.func.isRequired,
onChangeSearchTerm: PropTypes.func,
Expand Down
13 changes: 13 additions & 0 deletions packages/admin-panel/src/autocomplete/ReduxAutocomplete.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { connect } from 'react-redux';
import { getAutocompleteState } from './selectors';
import { changeSelection, changeSearchTerm, clearState } from './actions';
import { Autocomplete } from './Autocomplete';
import { EntityOptionLabel } from '../widgets';

const getPlaceholder = (placeholder, selection) => {
if (selection && selection.length) {
Expand Down Expand Up @@ -41,6 +42,8 @@ const ReduxAutocompleteComponent = ({
error,
tooltip,
optionValueKey,
renderOption,
optionFields,
}) => {
const [hasUpdated, setHasUpdated] = React.useState(false);
React.useEffect(() => {
Expand Down Expand Up @@ -69,6 +72,12 @@ const ReduxAutocompleteComponent = ({
selectedValue = [];
}

const getOptionRendered = option => {
if (renderOption) return renderOption(option);
if (!option || !option[optionLabelKey]) return '';
return option[optionLabelKey];
};

return (
<Autocomplete
value={selectedValue}
Expand All @@ -88,6 +97,7 @@ const ReduxAutocompleteComponent = ({
required={required}
error={error}
tooltip={tooltip}
renderOption={getOptionRendered}
/>
);
};
Expand All @@ -111,6 +121,7 @@ ReduxAutocompleteComponent.propTypes = {
required: PropTypes.bool,
error: PropTypes.bool,
optionValueKey: PropTypes.string.isRequired,
renderOption: PropTypes.func,
};

ReduxAutocompleteComponent.defaultProps = {
Expand Down Expand Up @@ -149,6 +160,7 @@ const mapDispatchToProps = (
baseFilter,
pageSize,
distinct,
optionFields,
},
) => ({
programaticallyChangeSelection: initialValue => {
Expand Down Expand Up @@ -199,6 +211,7 @@ const mapDispatchToProps = (
baseFilter,
pageSize,
distinct,
optionFields,
),
),
onClearState: () => dispatch(clearState(reduxId)),
Expand Down
3 changes: 2 additions & 1 deletion packages/admin-panel/src/autocomplete/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const changeSearchTerm =
baseFilter = {},
pageSize = MAX_AUTOCOMPLETE_RESULTS,
distinct = null,
columns = null,
) =>
async (dispatch, getState, { api }) => {
const fetchId = generateId();
Expand All @@ -46,7 +47,7 @@ export const changeSearchTerm =
filter: JSON.stringify(filter),
pageSize,
sort: JSON.stringify([`${labelColumn} ASC`]),
columns: JSON.stringify([labelColumn, valueColumn]),
columns: JSON.stringify(columns ? columns : [labelColumn, valueColumn]),
distinct,
});
dispatch({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DateTimePicker, RadioGroup } from '@tupaia/ui-components';
import { stripTimezoneFromDate } from '@tupaia/utils';
import { ReduxAutocomplete } from '../autocomplete';
import { ExportModal } from './ExportModal';
import { EntityOptionLabel } from '../widgets';

const MODES = {
COUNTRY: { value: 'country', formInput: 'countryCode' },
Expand Down Expand Up @@ -92,6 +93,8 @@ export const SurveyResponsesExportModal = () => {
endpoint="entities"
optionLabelKey="name"
optionValueKey="id"
renderOption={option => <EntityOptionLabel {...option} />}
optionFields={['id', 'code', 'name']}
allowMultipleValues
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
* Copyright (c) 2017 - 2024 Beyond Essential Systems Pty Ltd
*/

import React from 'react';
import { ArrayFilter } from '../../table/columnTypes/columnFilters';
import { prettyArray } from '../../utilities';
import { EntityOptionLabel } from '../../widgets';

const RESOURCE_NAME = { singular: 'dashboard mailing list' };

Expand Down Expand Up @@ -52,6 +54,8 @@ const DASHBOARD_MAILING_LIST_FIELDS = {
optionLabelKey: 'name',
optionValueKey: 'id',
labelTooltip: 'Select the entity this dashboard mailing list should be for',
renderOption: option => <EntityOptionLabel {...option} />,
optionFields: ['id', 'code', 'name'],
},
},
admin_permission_groups: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ export const visualisationsTabRoutes = {
dashboards,
dashboardRelations,
dashboardMailingLists,
dataTables,
legacyReports,
mapOverlays,
mapOverlayGroups,
mapOverlayGroupRelations,
indicators,
dataTables,
socialFeed,
],
};
4 changes: 4 additions & 0 deletions packages/admin-panel/src/surveyResponse/ResponseFields.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { format } from 'date-fns';
import { Autocomplete } from '../autocomplete';
import { useDebounce } from '../utilities';
import { useEntities } from '../VizBuilderApp/api';
import { EntityOptionLabel } from '../widgets';

const SectionWrapper = styled.div`
display: grid;
Expand Down Expand Up @@ -81,6 +82,9 @@ export const ResponseFields = ({
return option.id === selected.id;
}}
getOptionLabel={option => option?.name || ''}
renderOption={option => {
return <EntityOptionLabel {...option} />;
}}
isLoading={entityIsLoading}
onChangeSelection={(event, selectedValue) => {
if (!selectedValue) {
Expand Down
31 changes: 31 additions & 0 deletions packages/admin-panel/src/widgets/EntityOptionLabel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Tupaia
* Copyright (c) 2017 - 2024 Beyond Essential Systems Pty Ltd
*/

import React from 'react';
import styled from 'styled-components';

const StyledEntityOptionLabel = styled.div`
display: flex;
flex-direction: column;
`;

const Name = styled.span`
font-style: ${props => props.theme.typography.fontWeightBold};
color: ${props => props.theme.palette.text.primary};
`;

const Code = styled.span`
margin-top: 0.25rem;
color: ${props => props.theme.palette.text.secondary};
`;

export const EntityOptionLabel = ({ name, code }) => {
return (
<StyledEntityOptionLabel>
<Name>{name}</Name>
<Code>{code}</Code>
</StyledEntityOptionLabel>
);
};
6 changes: 1 addition & 5 deletions packages/admin-panel/src/widgets/InputField/JsonEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ export const JsonEditor = ({
required,
tooltip,
}) => {
if (!value) {
return null;
}

let editorValue = value;

if (typeof value === 'string') {
Expand All @@ -64,7 +60,7 @@ export const JsonEditor = ({
mainMenuBar={false}
statusBar={false}
mode="code"
onChange={json => onChange(inputKey, stringify ? JSON.stringify(json) : json)}
onChange={json => onChange(inputKey, stringify ? JSON.stringify(json ?? {}) : json)}
value={editorValue}
htmlElementProps={{
className: 'jsoneditor-parent',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export const registerInputFields = () => {
endpoint={props.optionsEndpoint}
optionLabelKey={props.optionLabelKey}
optionValueKey={props.optionValueKey}
optionFields={props.optionFields}
renderOption={props.renderOption}
reduxId={props.inputKey}
onChange={inputValue => props.onChange(props.inputKey, inputValue)}
canCreateNewOptions={props.canCreateNewOptions}
Expand Down
1 change: 1 addition & 0 deletions packages/admin-panel/src/widgets/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export {
export { JsonEditor, JsonTreeEditor } from './JsonEditor';
export { SecondaryNavbar } from '../layout/navigation/SecondaryNavbar';
export { ConfirmDeleteModal } from './ConfirmDeleteModal';
export { EntityOptionLabel } from './EntityOptionLabel';
Original file line number Diff line number Diff line change
Expand Up @@ -450,13 +450,13 @@ describe('processSurveyResponse', () => {
});
});

it('should not add to recent_entities when type is entity question is not filled in', async () => {
it('should not add to recent_entities when type is entity question and is not filled in', async () => {
const result = await processSurveyResponse(mockModels, {
...responseData,
questions: [
{
questionId: 'question1',
type: QuestionType.PrimaryEntity,
type: QuestionType.Entity,
componentNumber: 1,
text: 'question1',
screenId: 'screen1',
Expand All @@ -469,6 +469,27 @@ describe('processSurveyResponse', () => {
expect(result.recent_entities).toEqual([]);
});

it('throw an error when type is primary entity question and is not filled in', async () => {
try {
const result = await processSurveyResponse(mockModels, {
...responseData,
questions: [
{
questionId: 'question1',
type: QuestionType.PrimaryEntity,
componentNumber: 1,
text: 'question1',
screenId: 'screen1',
config: {},
},
],
answers: {},
});
} catch (error: any) {
expect(error.message).toBe('Primary Entity Question is a required field');
}
});

it('should use the country id for new entities if parent id is not filled in', async () => {
const result = await processSurveyResponse(mockModels, {
...responseData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ export const processSurveyResponse = async (
surveyResponse.qr_codes_to_create?.push(entityObj);
}
}
if (type === QuestionType.PrimaryEntity && !answer) {
throw new Error(`Primary Entity Question is a required field`);
}
if (answer) {
if (typeof answer !== 'string') {
throw new Error(
Expand Down
31 changes: 25 additions & 6 deletions packages/datatrak-web/src/components/Autocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,39 @@ const SelectedOption = styled(OptionWrapper)`
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 0.425rem;
padding-right: 0.425rem;
margin-left: 0.45rem;
margin-right: 0.45rem;
padding-inline: 0.425rem;
margin-inline: 0.45rem;
border-radius: 3px;
border: 1px solid ${({ theme }) => theme.palette.primary.main};
.MuiSvgIcon-root {
font-size: 1.2rem;
}
`;

const Label = styled.span`
font-style: ${props => props.theme.typography.fontWeightBold};
color: ${props => props.theme.palette.text.primary};
`;

const Code = styled.span`
margin-inline: 0.45rem;
padding-left: 0.45rem;
border-left: 1px solid ${props => props.theme.palette.text.secondary};
color: ${props => props.theme.palette.text.secondary};
flex: 1;
`;

const DisplayOption = ({ option, state }) => {
const { selected } = state;
const label = typeof option === 'string' ? option : option.label || option.value;
const label =
typeof option === 'string' ? (
option
) : (
<>
<Label>{option.label || option.value}</Label>
{option.secondaryLabel ? <Code>{option.secondaryLabel}</Code> : null}
</>
);

if (selected)
return (
Expand All @@ -66,9 +85,9 @@ const DisplayOption = ({ option, state }) => {

export const Autocomplete = styled(BaseAutocomplete).attrs(props => ({
muiProps: {
...(props.muiProps || {}),
renderOption: (option, state) => <DisplayOption option={option} state={state} />,
PaperComponent: StyledPaper,
...(props.muiProps || {}),
},
}))`
width: 100%;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ export const EntitySelectorInput = ({ selectedEntityLevel }: EntitySelectorInput
id={selectedEntityLevel}
getOptionSelected={(option, selected) => option.value === selected.value}
options={
entities?.map(({ name: entityName, id, code, type: entityType }) => ({
entities?.map(({ name: entityName, id, code: secondaryLabel, type: entityType }) => ({
label: entityName,
value: id,
code,
secondaryLabel,
type: entityType,
})) ?? []
}
Expand Down
Loading

0 comments on commit ba25cfa

Please sign in to comment.