import { useLocalStorage } from 'hooks' import { useMemo, useState } from 'react' import { Link } from 'react-router-dom' import { getGamePageRoute } from 'routes' import { ModFormState, Categories, Category } from 'types' import { getCategories, flattenCategories, addToUserCategories, capitalizeEachWord } from 'utils' interface CategoryAutocompleteProps { game: string LTags: string[] setFormState: (value: React.SetStateAction) => void } export const CategoryAutocomplete = ({ game, LTags, setFormState }: CategoryAutocompleteProps) => { // Fetch the hardcoded categories from assets const flattenedCategories = useMemo(() => getCategories(), []) // Fetch the user categories from local storage const [userHierarchies, setUserHierarchies] = useLocalStorage< (string | Category)[] >('user-hierarchies', []) const flattenedUserCategories = useMemo( () => flattenCategories(userHierarchies, []), [userHierarchies] ) // Create options and select categories from the mod LTags (hierarchies) const { selectedCategories, combinedOptions } = useMemo(() => { const combinedCategories = [ ...flattenedCategories, ...flattenedUserCategories ] const hierarchies = LTags.map((hierarchy) => { const existingCategory = combinedCategories.find( (cat) => cat.hierarchy === hierarchy.replace(/:/g, ' > ') ) if (existingCategory) { return existingCategory } else { const segments = hierarchy.split(':') const lastSegment = segments[segments.length - 1] return { name: lastSegment, hierarchy: hierarchy, l: [lastSegment] } } }) // Selected categorires (based on the LTags) const selectedCategories = Array.from(new Set([...hierarchies])) // Combine user, predefined category hierarchies and selected values (LTags in case some are missing) const combinedOptions = Array.from( new Set([...combinedCategories, ...selectedCategories]) ) return { selectedCategories, combinedOptions } }, [LTags, flattenedCategories, flattenedUserCategories]) const [inputValue, setInputValue] = useState('') const filteredOptions = useMemo( () => combinedOptions.filter((option) => option.hierarchy.toLowerCase().includes(inputValue.toLowerCase()) ), [combinedOptions, inputValue] ) const getSelectedCategories = (cats: Categories[]) => { const uniqueValues = new Set( cats.reduce((prev, cat) => [...prev, ...cat.l], []) ) const concatenatedValue = Array.from(uniqueValues) return concatenatedValue } const getSelectedHierarchy = (cats: Categories[]) => { const hierarchies = cats.reduce( (prev, cat) => [...prev, cat.hierarchy.replace(/ > /g, ':')], [] ) const concatenatedValue = Array.from(hierarchies) return concatenatedValue } const handleReset = () => { setFormState((prevState) => ({ ...prevState, ['lTags']: [], ['LTags']: [] })) setInputValue('') } const handleRemove = (option: Categories) => { const updatedCategories = selectedCategories.filter( (cat) => cat.hierarchy !== option.hierarchy ) setFormState((prevState) => ({ ...prevState, ['lTags']: getSelectedCategories(updatedCategories), ['LTags']: getSelectedHierarchy(updatedCategories) })) } const handleSelect = (option: Categories) => { if (!selectedCategories.some((cat) => cat.hierarchy === option.hierarchy)) { const updatedCategories = [...selectedCategories, option] setFormState((prevState) => ({ ...prevState, ['lTags']: getSelectedCategories(updatedCategories), ['LTags']: getSelectedHierarchy(updatedCategories) })) } setInputValue('') } const handleInputChange = (e: React.ChangeEvent) => { setInputValue(e.target.value) } const handleAddNew = () => { if (inputValue) { const value = inputValue.trim().toLowerCase() const values = value.split('>').map((s) => s.trim()) const newOption: Categories = { name: value, hierarchy: value, l: values } setUserHierarchies((prev) => { addToUserCategories(prev, value) return [...prev] }) const updatedCategories = [...selectedCategories, newOption] setFormState((prevState) => ({ ...prevState, ['lTags']: getSelectedCategories(updatedCategories), ['LTags']: getSelectedHierarchy(updatedCategories) })) setInputValue('') } } const handleAddNewCustom = (option: Categories) => { setUserHierarchies((prev) => { addToUserCategories(prev, option.hierarchy) return [...prev] }) } const Row = ({ index }: { index: number }) => { return (
handleSelect(filteredOptions[index])} > {capitalizeEachWord(filteredOptions[index].hierarchy)} {/* Show "Remove" button when the category is selected */} {selectedCategories.some( (cat) => cat.hierarchy === filteredOptions[index].hierarchy ) && ( )} {/* Show "Add" button when the category is not included in the predefined or userdefined lists */} {!flattenedCategories.some( (cat) => cat.hierarchy === filteredOptions[index].hierarchy ) && !flattenedUserCategories.some( (cat) => cat.hierarchy === filteredOptions[index].hierarchy ) && ( )}
) } return (

You can select multiple categories

{filteredOptions.length > 0 ? ( filteredOptions.map((c, i) => ) ) : (
{inputValue && !filteredOptions?.find( (option) => option.hierarchy.toLowerCase() === inputValue.toLowerCase() ) ? ( <> Add "{inputValue}" ) : ( <>No matches )}
)}
{LTags.length > 0 && (
{LTags.map((hierarchy) => { const hierarchicalCategories = hierarchy.split(`:`) const categories = hierarchicalCategories .map((c, i) => { const partialHierarchy = hierarchicalCategories .slice(0, i + 1) .join(':') return game ? (

{capitalizeEachWord(c)}

) : (

{capitalizeEachWord(c)}

) }) .reduce((prev, curr, i) => [ prev,

>

, curr ]) return (
{categories}
) })}
)}
) }