React Component for Student Filtering Interface
Yash
javascript
10 months ago
27 kB
17
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