React Component for Student Filtering Interface
Yash
javascript
2 months ago
27 kB
6
Indexable
import { Box, Button, Divider, Flex, Input, Menu, MenuButton, MenuItem, MenuList, Checkbox, Text, Select, FormLabel, FormControl, } from '@chakra-ui/react'; import Rselect from 'react-select'; import makeAnimated from 'react-select/animated'; import { filter, find, forEach, map } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ChevronDownIcon } from '@chakra-ui/icons'; import { STATUS } from "../../constants"; import { useScholarshipTestStore } from '../../store/ScholarshipTestStore'; import { useAuthStore } from '../../store/useAuth'; import { useStaffStore } from '../../store/StaffStore'; import { PERMISSIONS } from '../../app/data/permissions'; import { useGetAllLeadStage } from '../../services/lead-source.service'; import { CUSTOM_FILTER_TYPES, NUMBER_FILTER_TYPES, STRING_FILTER_TYPES } from '../AllLeads/filters'; export const StudentsFilterContainer = ({ filters, filterByFields, handleApply, handleClear, allLeadSource, allLeadStage, getAllFilterLeadsStatus, allCustomFIelds }) => { console.log({ filters }) const [curFilters, setCurFilters] = useState([]); console.log({ curFilters }) useEffect(() => { if (filters) { setCurFilters(filters) } }, [filters]) const handleFilterChange = useCallback( (changeData) => { setCurFilters((prevFilters) => { let newFilters = [...prevFilters]; forEach(changeData, (d) => { const { key: filterKey, data } = d; if (!data) { newFilters = filter(newFilters, (f) => f.filterKey !== filterKey); } else if (filterKey === "customField") { newFilters = filter(newFilters, (f) => f?.customFieldKey !== data.lastDeletedCustomField) newFilters = filter(newFilters, (f) => f?.customFieldKey !== data.customFieldKey); newFilters.push(data); } else if (find(newFilters, (f) => f.filterKey === filterKey)) { newFilters = map(newFilters, (f) => f.filterKey === filterKey ? data : f, ); } else { newFilters.push(data); } }); return newFilters; }); }, [] ); const onApply = () => { handleApply([...curFilters]); }; const onClear = () => { handleClear([]); setCurFilters([]); }; return ( <Box> <Box mt={2} h="46vh" overflowY="scroll" css={{ '&::-webkit-scrollbar': { width: '7px', background: 'transparent', }, '&::-webkit-scrollbar-thumb': { background: '#CBD5E0', borderRadius: '10px', }, }} > <Box px={4} mt={2}> {map(filterByFields, (f) => ( <FilterTypesContainer filterApplied={find(curFilters, (ftr) => ftr?.filterKey === f.key)} handleFilterChange={handleFilterChange} key={f.key} filterData={f} allLeadSource={allLeadSource} allLeadStage={allLeadStage} allCustomFIelds={allCustomFIelds} /> ))} </Box> </Box> <Box py={4} px={4} boxShadow="0px -9px 25px -14px #41424224" borderTop="1px solid" borderColor="gray.100" > <Flex justify="end"> <Box mr={2}> <Button onClick={onClear} colorScheme="gray" size="sm" variant="outline"> Clear </Button> </Box> <Box> <Button onClick={onApply} colorScheme="blue" size="sm" isLoading={getAllFilterLeadsStatus === STATUS.FETCHING}> Apply filter </Button> </Box> </Flex> </Box> </Box> ); }; const FilterTypesContainer = ({ filterData, handleFilterChange, filterApplied, allLeadSource, allLeadStage, allCustomFIelds }) => { const [isChecked, setIsChecked] = useState() const { activeBranch } = useAuthStore(); useEffect(() => { if (filterApplied && (filterApplied.value || filterApplied.startDate || filterApplied.endDate || filterApplied.customFieldKeys)) { setIsChecked(true) } else if (!filterApplied) { setIsChecked(false) } }, [filterApplied]) const { allStaffDetails, getAllStaff } = useStaffStore() useEffect(() => { getAllStaff({ branch: activeBranch, populate: 'roleId' }) }, [getAllStaff, activeBranch]) const staffList = useMemo(() => { if (allStaffDetails) { const staffs = filter(allStaffDetails, staff => { const permissions = staff.roleId?.permissions; if (permissions) { return permissions.includes(PERMISSIONS.LEAD_MANAGER.value) || permissions.includes(PERMISSIONS.LEAD_OWNER.value) } }) return map(staffs, staff => staff.roleId.permissions.includes(PERMISSIONS.LEAD_MANAGER.value) ? { ...staff, isLeadManger: true } : staff) } }, [allStaffDetails]) const updateFilterData = useCallback((data) => { const filterDataObj = { filterKey: filterData.key, ...data, }; handleFilterChange([{ key: filterData.key, data: filterDataObj }]); }, [filterData.key, handleFilterChange]); const renderFilterInput = () => { const options = filterData.key === "leadStage" ? allLeadStage : filterData.key === "leadSource" ? allLeadSource : filterData.key === "staffId" ? staffList : filterData.key === "customField" ? allCustomFIelds : []; switch (filterData.type) { case 'number': return <NumberFilter filterApplied={filterApplied} updateFilterData={updateFilterData} />; case 'string': return <StringFilter filterApplied={filterApplied} updateFilterData={updateFilterData} />; case 'select': return <SelectFilter filterApplied={filterApplied} updateFilterData={updateFilterData} options={options} />; case 'date': return <DateFilter filterApplied={filterApplied} updateFilterData={updateFilterData} />; case 'selectClass': return <SelectClassFilter filterApplied={filterApplied} updateFilterData={updateFilterData} options={options} />; case 'selectLeadConvert': return <SelectLeadConvertFilter filterApplied={filterApplied} updateFilterData={updateFilterData} /> case 'selectLeadOwner': return <SelectLeadOwner filterApplied={filterApplied} updateFilterData={updateFilterData} options={options} /> case 'selectLeadStage': return <SelectLeadStage filterApplied={filterApplied} updateFilterData={updateFilterData} /> case 'selectLeadCustomField': return <SelectLeadCustomFields filterApplied={filterApplied} updateFilterData={updateFilterData} options={options} /> default: return null; } }; return ( <Box mb={4}> <Checkbox isChecked={isChecked} onChange={(e) => { setIsChecked(e.target.checked); if (!e.target.checked) { updateFilterData(null); } }} > {filterData.title} </Checkbox> {isChecked && renderFilterInput()} </Box> ); }; const NumberFilter = ({ filterApplied, updateFilterData }) => { const [type, setType] = useState(filterApplied?.type ?? 'equal'); const [value, setValue] = useState(filterApplied?.value ?? ''); const handleValueChange = (v) => { setValue(v); }; useEffect(() => { updateFilterData({ type, value }); }, [value, type, updateFilterData]); return ( <Flex flexDir="column" maxW="245px"> <Menu size="sm"> <MenuButton as={Button} size="sm" mt={2} variant="outline" rightIcon={<ChevronDownIcon />}> {NUMBER_FILTER_TYPES[type]} </MenuButton> <MenuList size="sm"> {map(NUMBER_FILTER_TYPES, (title, key) => ( <MenuItem onClick={() => setType(key)} key={key}> {title} </MenuItem> ))} </MenuList> </Menu> <Input value={value} onChange={(e) => handleValueChange(e.target.value)} type="number" size="sm" placeholder="Enter value" mt={2} borderColor="gray.300" _focus={{ borderColor: 'blue.400', boxShadow: '0 0 0 1px rgba(66, 153, 225, 0.6)' }} /> </Flex> ); }; const StringFilter = ({ filterApplied, updateFilterData }) => { const [type, setType] = useState(filterApplied?.type ?? 'is'); const [value, setValue] = useState(filterApplied?.value ?? ''); useEffect(() => { updateFilterData({ type, value }); }, [value, type, updateFilterData]); return ( <Flex align="center" mt={2}> <Menu size="sm" > <MenuButton as={Button} size="sm" variant="outline" rightIcon={<ChevronDownIcon />}> {STRING_FILTER_TYPES[type]} </MenuButton> <MenuList size="sm"> {map(STRING_FILTER_TYPES, (title, key) => ( <MenuItem onClick={() => setType(key)} key={key}> {title} </MenuItem> ))} </MenuList> </Menu> <Input ml={1} value={value} onChange={(e) => setValue(e.target.value)} type="text" size="sm" placeholder="Enter string" borderColor="gray.300" _focus={{ borderColor: 'blue.400', boxShadow: '0 0 0 1px rgba(66, 153, 225, 0.6)' }} /> </Flex> ); }; const DateFilter = ({ filterApplied, updateFilterData }) => { const [startDate, setStartDate] = useState(filterApplied?.startDate ?? ''); const [endDate, setEndDate] = useState(filterApplied?.endDate ?? ''); useEffect(() => { updateFilterData({ startDate, endDate, type: 'date' }); }, [startDate, endDate, updateFilterData]); return ( <Flex align="center" mt={2}> <FormControl> <FormLabel>From</FormLabel> <Input value={startDate} onChange={(e) => setStartDate(e.target.value)} type="date" size="sm" placeholder="Select date" borderColor="gray.300" _focus={{ borderColor: 'blue.400', boxShadow: '0 0 0 1px rgba(66, 153, 225, 0.6)' }} /> </FormControl> <FormControl> <FormLabel>To</FormLabel> <Input value={endDate} onChange={(e) => setEndDate(e.target.value)} type="date" size="sm" ml={1} placeholder="Select date" borderColor="gray.300" _focus={{ borderColor: 'blue.400', boxShadow: '0 0 0 1px rgba(66, 153, 225, 0.6)' }} /> </FormControl> </Flex> ); }; const LeadStringFilter = ({ filterApplied, updateFilterData, customFieldKey, data, lastDeletedCustomField }) => { const [value, setValue] = useState(''); const [type, setType] = useState('equal'); useEffect(() => { updateFilterData({ customFieldKey, lastDeletedCustomField, value, type, fieldType: data.fieldType }); }, [value, customFieldKey, lastDeletedCustomField, updateFilterData, type, data.fieldType]); useEffect(() => { if (filterApplied && filterApplied.customFieldKey === customFieldKey) { setValue(filterApplied.value || ''); setType(filterApplied.type || 'equal'); } }, [filterApplied, customFieldKey]); return ( <Flex mt={2} direction="column"> <FormLabel htmlFor={customFieldKey} mb={1} fontSize="sm"> {data?.inputState?.key || 'Custom Field'} </FormLabel> {data?.fieldType === 'select' ? ( <Select id={customFieldKey} placeholder="Select" size="sm" value={value} onChange={(e) => setValue(e.target.value)} > {map(data.options, (o) => ( <option key={o.name} value={o.name}> {o.name} </option> ))} </Select> ) : ( <Flex align="center" mt={2} w="87%"> {data?.fieldType === 'number' && ( <Menu size="sm"> <MenuButton as={Button} size="sm" variant="outline" rightIcon={<ChevronDownIcon />}> {CUSTOM_FILTER_TYPES[type]} </MenuButton> <MenuList size="sm"> {map(CUSTOM_FILTER_TYPES, (title, key) => ( <MenuItem onClick={() => setType(key)} key={key}> {title} </MenuItem> ))} </MenuList> </Menu> )} <Input id={customFieldKey} ml={1} value={value} onChange={(e) => setValue(e.target.value)} type={data?.fieldType === 'date' ? 'date' : 'text'} size="sm" placeholder="Enter Value" borderColor="gray.300" _focus={{ borderColor: 'blue.400', boxShadow: '0 0 0 1px rgba(66, 153, 225, 0.6)' }} /> </Flex> )} </Flex> ); }; const SelectFilter = ({ filterApplied, updateFilterData, options }) => { const [value, setValue] = useState(filterApplied?.value || []); const animatedComponents = makeAnimated(); const handleValueChange = (selectedOptions) => { const newValue = selectedOptions ? selectedOptions.map(option => option.value) : []; setValue(newValue); }; useEffect(() => { if (filterApplied?.value) { setValue(filterApplied.value); } }, [filterApplied]); useEffect(() => { updateFilterData({ value }); }, [value]); const formattedOptions = options.map((opt) => ({ label: opt.name, value: opt.name, })); const selectedOptions = formattedOptions.filter((option) => value.includes(option.value) ); return ( <Flex maxW="245px"> <Rselect options={formattedOptions} value={selectedOptions} onChange={handleValueChange} isMulti isClearable components={animatedComponents} closeMenuOnSelect={false} placeholder="Select" styles={{ control: (provided) => ({ ...provided, width: '245px', marginTop: "10px" }), }} /> </Flex> ); }; const SelectClassFilter = ({ filterApplied, updateFilterData }) => { const { getAllClasses, allClasses } = useScholarshipTestStore(); const animatedComponents = makeAnimated(); const [selectedValues, setSelectedValues] = useState([]); useEffect(() => { getAllClasses(); }, [getAllClasses]); useEffect(() => { if (filterApplied && filterApplied.value) { const appliedValues = Array.isArray(filterApplied.value) ? filterApplied.value : [filterApplied.value]; setSelectedValues( appliedValues.map((val) => { const matchedClass = allClasses.find((opt) => opt.name === val); return matchedClass ? { value: matchedClass.name, label: matchedClass.name } : null; }).filter(Boolean) ); } }, [filterApplied, allClasses]); const handleValueChange = (selectedOptions) => { const names = selectedOptions.map((option) => option.value); setSelectedValues(selectedOptions); updateFilterData({ value: names }); }; const options = allClasses.map((opt) => ({ value: opt.name, label: opt.name, })); return ( <Flex maxW="245px"> <Rselect options={options} isMulti value={selectedValues} components={animatedComponents} onChange={handleValueChange} placeholder="Select classes..." isClearable closeMenuOnSelect={false} styles={{ control: (provided) => ({ ...provided, width: '245px', marginTop: "10px" }), }} /> </Flex> ); }; const SelectLeadOwner = ({ filterApplied, updateFilterData, options }) => { const [value, setValue] = useState(filterApplied?.value || []); const animatedComponents = makeAnimated(); const handleValueChange = (selectedOptions) => { const newValue = selectedOptions ? selectedOptions.map((option) => option.value) : []; setValue(newValue); }; useEffect(() => { if (filterApplied?.value) { setValue(filterApplied.value); } }, [filterApplied]); useEffect(() => { updateFilterData({ value }); }, [value, updateFilterData]); const formattedOptions = [ ...options.map((opt) => ({ label: opt.name, value: opt._id, })), { label: "Not Assigned", value: "not-assigned" } ]; const selectedOptions = formattedOptions.filter((option) => value.includes(option.value) ); return ( <Flex maxW="245px"> <Rselect options={formattedOptions} value={selectedOptions} onChange={handleValueChange} isMulti isClearable components={animatedComponents} closeMenuOnSelect={false} placeholder="Select Lead Owner" styles={{ control: (provided) => ({ ...provided, width: "245px", marginTop: "10px", }), }} /> </Flex> ); }; const SelectLeadStage = ({ filterApplied, updateFilterData }) => { const { data: allLeadStages = [] } = useGetAllLeadStage({}); const [selectedStage, setSelectedStage] = useState(""); const [selectedSubStage, setSelectedSubStage] = useState(""); const handleStageChange = (stageId) => { setSelectedStage(stageId); setSelectedSubStage(""); }; const handleSubStageChange = (subStageId) => { setSelectedSubStage(subStageId); }; useEffect(() => { if (filterApplied && filterApplied.value) { setSelectedStage(filterApplied.value) } if (filterApplied && filterApplied.leadSubStage) { setSelectedSubStage(filterApplied.leadSubStage) } }, [filterApplied]) useEffect(() => { const filterData = { value: selectedStage }; if (selectedSubStage) { filterData.leadSubStage = selectedSubStage; } updateFilterData(filterData); }, [selectedStage, selectedSubStage, updateFilterData]); return ( <Flex maxW="245px" flexDirection="column"> <Select size="sm" mt={4} value={selectedStage} onChange={(e) => handleStageChange(e.target.value)} placeholder="select lead stage" > {map(allLeadStages, (opt) => ( <option key={opt._id} value={opt.name}> {opt.name} </option> ))} </Select> {selectedStage && allLeadStages.length > 0 && ( <Select size="sm" mt={4} value={selectedSubStage} onChange={(e) => handleSubStageChange(e.target.value)} placeholder="select sub stage" > {map( allLeadStages.find((stage) => stage.name === selectedStage)?.leadSubStage || [], (opt) => ( <option key={opt} value={opt}> {opt} </option> ) )} </Select> )} </Flex> ); }; const SelectLeadConvertFilter = ({ updateFilterData }) => { const { getAllClasses, allClasses } = useScholarshipTestStore(); useEffect(() => { getAllClasses(); }, [getAllClasses]); const [value, setValue] = useState(''); const handleValueChange = (v) => { let newValue = v; if (v === 'true' || v === 'false') newValue = v === 'true' ? true : false; setValue(newValue); }; useEffect(() => { updateFilterData({ value }); }, [value, updateFilterData]); return ( <Flex maxW="245px"> <Select size="sm" mt={4} value={value} onChange={(e) => handleValueChange(e.target.value)} > <option>Select</option> <option value={true}>Converted</option> <option value={false}>Not Converted</option> </Select> </Flex> ); }; const SelectLeadCustomFields = ({ filterApplied, updateFilterData, options }) => { const [selectedCustomFields, setSelectedCustomFields] = useState([]); const [lastDeletedCustomField, setLastDeletedCustomField] = useState(null); const filterAllowedOnFieldsType = ['text', 'number', 'date', 'select']; const filteredData = filter(options, (o) => filterAllowedOnFieldsType.includes(o.fieldType)); const selectedCustomFieldData = useMemo(() => { return selectedCustomFields.map((selectedCustomField) => find(options, (o) => o.key === selectedCustomField) ); }, [selectedCustomFields, options]); const handleValueChange = (selectedValues) => { const newSelectedValues = selectedValues.map(val => val.value); const removedValues = selectedCustomFields.filter(val => !newSelectedValues.includes(val)); if (removedValues.length > 0) { const lastRemovedValue = removedValues[removedValues.length - 1]; setLastDeletedCustomField(lastRemovedValue); } setSelectedCustomFields(newSelectedValues); }; useEffect(() => { if (filterApplied && filterApplied.customFieldKeys) { setSelectedCustomFields(filterApplied.customFieldKeys); } }, [filterApplied]); return ( <> <Rselect options={filteredData.map((opt) => ({ value: opt.key, label: opt.inputState.key }))} value={selectedCustomFields.map(key => ({ value: key, label: key }))} onChange={handleValueChange} isMulti closeMenuOnSelect={false} /> {selectedCustomFields.map((customFieldKey) => { const customFieldData = selectedCustomFieldData.find((data) => data.key === customFieldKey); return ( <LeadStringFilter key={customFieldKey} filterApplied={filterApplied} updateFilterData={updateFilterData} customFieldKey={customFieldKey} data={customFieldData} lastDeletedCustomField={lastDeletedCustomField} /> ); })} </> ); };
Editor is loading...
Leave a Comment