forked from mirrors/thatmattlove-hyperglass
improve form state logic, fix issue with intersection directive selection
This commit is contained in:
parent
196fb43ea7
commit
ba2ce4b930
1 changed files with 32 additions and 46 deletions
|
|
@ -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 } }));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue