improve form state logic, fix issue with intersection directive selection

This commit is contained in:
thatmattlove 2021-12-17 23:00:36 -07:00
parent 196fb43ea7
commit ba2ce4b930

View file

@ -5,10 +5,10 @@ import plur from 'plur';
import isEqual from 'react-fast-compare'; import isEqual from 'react-fast-compare';
import { all, andJoin, dedupObjectArray, withDev } from '~/util'; import { all, andJoin, dedupObjectArray, withDev } from '~/util';
import type { SingleValue, MultiValue } from 'react-select';
import type { StateCreator } from 'zustand'; import type { StateCreator } from 'zustand';
import { UseFormSetError, UseFormClearErrors } from 'react-hook-form'; import type { UseFormSetError, UseFormClearErrors } from 'react-hook-form';
import type { SingleOption, Directive, FormData, Text } from '~/types'; import type { SingleValue, MultiValue } from 'react-select';
import type { SingleOption, Directive, FormData, Text, Device } from '~/types';
import type { UseDevice } from './types'; import type { UseDevice } from './types';
type FormStatus = 'form' | 'results'; type FormStatus = 'form' | 'results';
@ -146,62 +146,48 @@ const formState: StateCreator<FormStateType> = (set, get) => ({
const { setError, clearErrors, getDevice, text } = extra; const { setError, clearErrors, getDevice, text } = extra;
clearErrors('queryLocation'); clearErrors('queryLocation');
const locationNames = [] as string[];
const allGroups = [] as string[][];
const allTypes = [] as Directive[][];
const allDevices = [];
set(state => ({ form: { ...state.form, queryLocation: locations } })); set(state => ({ form: { ...state.form, queryLocation: locations } }));
for (const loc of locations) { // Get device configuration objects for each selected location ID.
const device = getDevice(loc); const allDevices = locations
if (device !== null) { .map(getDevice)
locationNames.push(device.name); .filter((device): device is Device => device !== null);
allDevices.push(device);
const groups = new Set<string>();
for (const directive of device.directives) {
for (const group of directive.groups) {
groups.add(group);
}
}
allGroups.push(Array.from(groups));
}
}
// Determine all unique group names.
const allGroups = allDevices.map(dev =>
Array.from(new Set(dev.directives.map(dir => dir.groups).flat())),
);
// Get group names that are common between all selected locations.
const intersecting = intersectionWith(...allGroups, isEqual); const intersecting = intersectionWith(...allGroups, isEqual);
for (const group of intersecting) { // Get all directives of all selected devices.
for (const device of allDevices) { const allDirectives = locations
for (const directive of device.directives) { .map(getDevice)
if (directive.groups.includes(group)) { .filter((device): device is Device => device !== null)
allTypes.push(device.directives); .map(device => device.directives);
}
}
}
}
let intersectingTypes = intersectionWith(...allTypes, isEqual); // Get directive objects that are common between selected locations.
intersectingTypes = dedupObjectArray<Directive>(intersectingTypes, 'id'); const intersectingDirectives = intersectionWith(...allDirectives, isEqual);
set({ filtered: { groups: intersecting, types: intersectingTypes } });
// Deduplicate all intersecting directives by ID.
const directives = dedupObjectArray(intersectingDirectives, 'id');
set({ filtered: { groups: intersecting, types: directives } });
// If there is more than one location selected, but there are no intersecting groups, show an error.
if (locations.length > 1 && intersecting.length === 0) {
setError('queryLocation', {
message: `${locationNames.join(', ')} have no groups in common.`,
});
}
// If there is only one intersecting group, set it as the form value so the user doesn't have to. // If there is only one intersecting group, set it as the form value so the user doesn't have to.
const { selections, form } = get(); const { selections, form } = get();
if (form.queryLocation.length > 1 && intersectingTypes.length === 0) { if (
(form.queryLocation.length > 1 || locations.length > 1) &&
intersectingDirectives.length === 0
) {
const start = plur(text.queryLocation, selections.queryLocation.length); const start = plur(text.queryLocation, selections.queryLocation.length);
const locationsAnd = andJoin(selections.queryLocation.map(s => s.label)); const locationsAnd = andJoin(selections.queryLocation.map(s => s.label));
const types = plur(text.queryType, 2); const types = plur(text.queryType, 2);
const message = `${start} ${locationsAnd} have no ${types} in common.`; const message = `${start} ${locationsAnd} have no ${types} in common.`;
setError('queryLocation', { setError('queryLocation', { message });
// message: `${locationNames.join(', ')} have no query types in common.`, } else if (intersectingDirectives.length === 1) {
message, set(state => ({ form: { ...state.form, queryType: intersectingDirectives[0].id } }));
});
} else if (intersectingTypes.length === 1) {
set(state => ({ form: { ...state.form, queryType: intersectingTypes[0].id } }));
} }
}, },