import { SearchResponse, ObjectWithObjectID } from '@algolia/client-search'; import algoliasearch, { SearchClient, SearchIndex } from 'algoliasearch/lite'; import { Option, Aspect, GradeAlias } from '@tutorbook/model'; import Select, { SelectProps } from '@tutorbook/select'; import React from 'react'; const algoliaId: string = process.env.ALGOLIA_SEARCH_ID as string; const algoliaKey: string = process.env.ALGOLIA_SEARCH_KEY as string; const client: SearchClient = algoliasearch(algoliaId, algoliaKey); interface UniqueSubjectSelectProps { values?: string[]; options?: string[]; aspect: Aspect; grade?: GradeAlias; } type SubjectSelectProps = Omit< SelectProps, keyof UniqueSubjectSelectProps | 'getSuggestions' > & UniqueSubjectSelectProps; interface SubjectHit extends ObjectWithObjectID { name: string; } export default function SubjectSelect({ values, options, aspect, grade, ...props }: SubjectSelectProps): JSX.Element { const searchIndex: SearchIndex = client.initIndex(aspect); // TODO: This will become an async update function that filters our Algolia // search index to get the labels of the selected subjects (using their IDs). // TODO: Debug issue with `react-hooks/exhaustive-deps`. React.useEffect(() => { if (!values) return; const valuesHaveLabels = values.every( (value: string) => props.value.findIndex( (valueWithLabel: Option) => valueWithLabel.value === value ) >= 0 ); if (!valuesHaveLabels) props.onChange(values.map((v) => ({ label: v, value: v }))); /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, [values]); /** * Updates the suggestions shown in the select below the subjects input based * on the results of the user's current input to an Algolia search query. * @see {@link https://www.algolia.com/doc/api-reference/api-methods/search/} */ async function getSuggestions(query = ''): Promise[]> { if (options && !options.length) return []; const filters: string | undefined = options !== undefined ? options.map((subject: string) => `name:"${subject}"`).join(' OR ') : undefined; const optionalFilters: string[] | undefined = grade !== undefined ? [`grades:${grade}`] : undefined; const res: SearchResponse = await searchIndex.search(query, { filters, optionalFilters, }); return res.hits.map((subject: SubjectHit) => ({ label: subject.name, value: subject.name, })); } /* eslint-disable-next-line react/jsx-props-no-spreading */ return