Skip to content

Commit

Permalink
Merge pull request #1075 from akvo/feature/1050-modify-cascade-type-q…
Browse files Browse the repository at this point in the history
…uestion-to-support-entities

Feature/1050 modify cascade type question to support entities
  • Loading branch information
dedenbangkit authored Jan 30, 2024
2 parents d79025b + 0b43aee commit 4484fd0
Show file tree
Hide file tree
Showing 13 changed files with 777 additions and 184 deletions.
38 changes: 30 additions & 8 deletions app/src/form/components/Question.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,29 @@ const Question = memo(({ group, activeQuestions = [], index }) => {
const [preload, setPreload] = useState(true);
const values = FormState.useState((s) => s.currentValues);
const currentPreFilled = FormState.useState((s) => s.prefilled);
const prevAdmAnswer = FormState.useState((s) => s.prevAdmAnswer);
const entityOptions = FormState.useState((s) => s.entityOptions);
const flatListRef = useRef(null);

const questions = useMemo(() => {
try {
if (group?.question?.length) {
return group.question.filter((q) => onFilterDependency(group, values, q));
}
return [];
} catch {
return [];
if (group?.question?.length) {
return group.question
.filter((q) => onFilterDependency(group, values, q))
.filter((q) => {
return (q?.extra?.type === 'entity' && prevAdmAnswer) || !q?.extra?.type;
})
.filter((q) => {
if (q?.extra?.type === 'entity' && entityOptions?.[q?.id]?.length) {
/**
* Make sure the entity cascade has administration answer and options
*/
return entityOptions[q.id].filter((opt) => prevAdmAnswer.includes(opt?.parent)).length;
}
return q;
});
}
}, [group, values]);
return [];
}, [group, values, prevAdmAnswer, entityOptions]);

const handleOnGenerateUUID = useCallback(() => {
if (preload) {
Expand Down Expand Up @@ -73,6 +84,17 @@ const Question = memo(({ group, activeQuestions = [], index }) => {
});
}
}

if (field?.source?.file === 'administrator.sqlite') {
activeQuestions
?.filter((q) => q?.source?.cascade_parent)
?.forEach((q) => {
/**
* Delete entity cascade response when the administration changes
*/
delete fieldValues[q?.id];
});
}
FormState.update((s) => {
s.currentValues = fieldValues;
});
Expand Down
17 changes: 1 addition & 16 deletions app/src/form/components/QuestionField.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ import {
} from '../fields';
import { View, Text } from 'react-native';
import { styles } from '../styles';
import { cascades } from '../../lib';
import { FormState } from '../../store';

const QuestionField = ({ keyform, field: questionField, onChange, value }) => {
const [cascadeData, setCascadeData] = useState([]);
const questionType = questionField?.type;
const displayValue = questionField?.hidden ? 'none' : 'flex';
const formFeedback = FormState.useState((s) => s.feedback);
Expand All @@ -29,18 +27,6 @@ const QuestionField = ({ keyform, field: questionField, onChange, value }) => {
onChange(id, val, questionField);
};

const loadCascadeDataSource = useCallback(async (source) => {
const { rows } = await cascades.loadDataSource(source);
setCascadeData(rows._array);
}, []);

useEffect(() => {
if (questionField?.type === 'cascade' && questionField?.source?.file) {
const cascadeSource = questionField.source;
loadCascadeDataSource(cascadeSource);
}
}, []);

const renderField = useCallback(() => {
switch (questionType) {
case 'date':
Expand Down Expand Up @@ -106,7 +92,6 @@ const QuestionField = ({ keyform, field: questionField, onChange, value }) => {
onChange={handleOnChangeField}
value={value}
{...questionField}
dataSource={cascadeData}
/>
);
case 'autofield':
Expand All @@ -123,7 +108,7 @@ const QuestionField = ({ keyform, field: questionField, onChange, value }) => {
/>
);
}
}, [questionField, keyform, handleOnChangeField, value, cascadeData]);
}, [questionField, keyform, handleOnChangeField, value]);

return (
<View testID="question-view" style={{ display: displayValue }}>
Expand Down
67 changes: 54 additions & 13 deletions app/src/form/fields/TypeCascade.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ const TypeCascade = ({
required,
requiredSign,
source,
dataSource = [],
}) => {
const [dataSource, setDataSource] = useState([]);
const [dropdownItems, setDropdownItems] = useState([]);
const prevAdmAnswer = FormState.useState((s) => s.prevAdmAnswer);
const activeLang = FormState.useState((s) => s.lang);
const trans = i18n.text(activeLang);
const requiredValue = required ? requiredSign : null;
Expand Down Expand Up @@ -65,20 +66,35 @@ const TypeCascade = ({
const cascadeName = findSelected?.name;
FormState.update((s) => {
s.cascades = { ...s.cascades, [id]: cascadeName };
s.prevAdmAnswer = source?.file === 'administrator.sqlite' ? finalValues : s.prevAdmAnswer;
});
}
setDropdownItems(updatedItems);
};

const initialDropdowns = useMemo(() => {
const parentIDs = source?.parent_id?.length ? source.parent_id : [0];
const filterDs = dataSource.filter(
(ds) =>
parentIDs.includes(ds?.parent) ||
parentIDs.includes(ds?.id) ||
value?.includes(ds?.id) ||
value?.includes(ds?.parent),
);
const { cascade_parent, cascade_type, parent_id } = source || {};
const parentIDs =
cascade_parent === 'administrator.sqlite' ? prevAdmAnswer || [] : parent_id || [0];
const filterDs = dataSource
?.filter((ds) => {
if (cascade_parent) {
return parentIDs.includes(ds?.parent);
}
return (
parentIDs.includes(ds?.parent) ||
parentIDs.includes(ds?.id) ||
value?.includes(ds?.id) ||
value?.includes(ds?.parent)
);
})
?.filter((ds) => {
if (cascade_type && ds?.entity) {
return ds.entity === cascade_type;
}
return ds;
});

const groupedDs = groupBy(filterDs, 'parent');
if (parentIDs.length > 1 && Object.keys(groupedDs).length > 1) {
const parentOptions = Object.keys(groupedDs).map((keyID) =>
Expand Down Expand Up @@ -107,7 +123,7 @@ const TypeCascade = ({
value: value?.[ox] || null,
};
});
}, [dataSource, source, value, id]);
}, [dataSource, source, value, id, prevAdmAnswer]);

const fetchCascade = useCallback(async () => {
if (source && value?.length) {
Expand All @@ -131,10 +147,34 @@ const TypeCascade = ({
}, [fetchCascade]);

useEffect(() => {
if (dropdownItems.length === 0 && initialDropdowns.length) {
if (
(dropdownItems.length === 0 && initialDropdowns.length) ||
(source?.cascade_parent && prevAdmAnswer)
) {
/**
* Reset entity cascade options when the prevAdmAnswer changes.
*/
setDropdownItems(initialDropdowns);
}
}, [dropdownItems, initialDropdowns]);
}, [dropdownItems, initialDropdowns, source, prevAdmAnswer]);

const loadDataSource = useCallback(async () => {
const { rows } = await cascades.loadDataSource(source);
setDataSource(rows._array);
if (source?.cascade_type) {
FormState.update((s) => {
s.entityOptions[id] = rows._array?.filter((a) => a?.entity === source.cascade_type);
});
}
}, [source, id]);

useEffect(() => {
loadDataSource();
}, [loadDataSource]);

if (!dropdownItems.length) {
return;
}

return (
<View testID="view-type-cascade">
Expand All @@ -144,14 +184,15 @@ const TypeCascade = ({
</Text>
<View style={styles.cascadeContainer}>
{dropdownItems.map((item, index) => {
const hasSearch = item?.options.length > 3;
return (
<Dropdown
key={index}
labelField="name"
valueField="id"
testID={`dropdown-cascade-${index}`}
data={item?.options}
search={item?.options.length > 3}
search={hasSearch}
searchPlaceholder={trans.searchPlaceholder}
onChange={({ id: selectedID }) => handleOnChange(index, selectedID)}
value={item.value}
Expand Down
Loading

0 comments on commit 4484fd0

Please sign in to comment.