Mirror of https://github.com/roostorg/osprey github.com/roostorg/osprey
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 171 lines 5.8 kB view raw
1import * as React from 'react'; 2import { Checkbox, Input, Modal, Switch } from 'antd'; 3import { CheckboxChangeEvent } from 'antd/lib/checkbox/Checkbox'; 4import classNames from 'classnames'; 5 6import useApplicationConfigStore from '../../stores/ApplicationConfigStore'; 7import useQueryStore from '../../stores/QueryStore'; 8import OspreyButton from '../../uikit/OspreyButton'; 9import Text, { TextSizes, TextWeights, TextColors } from '../../uikit/Text'; 10import FilterIcon from '../../uikit/icons/FilterIcon'; 11import ModalFooter from '../common/ModalFooter'; 12import CustomFeaturesFilter from './CustomFeaturesFilter'; 13 14import styles from './FeatureSelectModal.module.css'; 15 16const FeatureSelectModal = () => { 17 const customSummaryFeatures = useQueryStore((state) => state.customSummaryFeatures); 18 const updateCustomSummaryFeatures = useQueryStore((state) => state.updateCustomSummaryFeatures); 19 const knownFeatureCategories = useApplicationConfigStore((state) => state.knownFeatureCategories); 20 21 const [useCustomFeatures, setUseCustomFeatures] = React.useState(customSummaryFeatures != null); 22 const [selectedFeatures, setSelectedFeatures] = React.useState<Set<string>>( 23 customSummaryFeatures != null ? new Set(customSummaryFeatures) : new Set() 24 ); 25 26 const [isOpen, setIsOpen] = React.useState(false); 27 const [searchInput, setSearchInput] = React.useState(''); 28 29 const sortedKnownFeatureCategoryEntries = React.useMemo( 30 () => Object.entries(knownFeatureCategories).sort(), 31 [knownFeatureCategories] 32 ); 33 34 const handleCancel = () => { 35 setUseCustomFeatures(customSummaryFeatures != null); 36 setSelectedFeatures(customSummaryFeatures != null ? new Set(customSummaryFeatures) : new Set()); 37 setIsOpen(false); 38 }; 39 40 const handleOpenModal = () => { 41 setIsOpen(true); 42 }; 43 44 const handleSelectFeature = (e: CheckboxChangeEvent) => { 45 const selectedFeature = e.target.value; 46 const updatedFeatures = new Set([...selectedFeatures]); 47 48 if (selectedFeatures.has(selectedFeature)) { 49 updatedFeatures.delete(selectedFeature); 50 } else { 51 updatedFeatures.add(selectedFeature); 52 } 53 setSelectedFeatures(updatedFeatures); 54 }; 55 56 const handleSaveSelectedFeatures = () => { 57 const saveVal = useCustomFeatures ? [...selectedFeatures] : null; 58 59 updateCustomSummaryFeatures(saveVal); 60 setIsOpen(false); 61 }; 62 63 const handleToggleSwitch = () => { 64 if (useCustomFeatures) { 65 setSelectedFeatures(new Set()); 66 } 67 68 setUseCustomFeatures(!useCustomFeatures); 69 }; 70 71 const handleSearchInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { 72 setSearchInput(e.currentTarget.value); 73 }; 74 75 const getReadableFeatureCategory = (featureCategory: string) => { 76 return featureCategory.substring(0, featureCategory.lastIndexOf('.')).replace(/_|\//g, ' '); 77 }; 78 79 const isSetSelected = (feature: string) => { 80 if (!useCustomFeatures) return true; 81 82 return selectedFeatures.has(feature as string); 83 }; 84 85 const renderModalTitle = () => { 86 return ( 87 <> 88 <Text size={TextSizes.H4}>Set Features</Text> 89 <span className={styles.switchSpan}> 90 <Switch 91 onChange={handleToggleSwitch} 92 className={styles.switch} 93 defaultChecked={false} 94 checked={useCustomFeatures} 95 /> 96 <Text size={TextSizes.LARGE} weight={TextWeights.NORMAL} color={TextColors.LIGHT_PRIMARY}> 97 Set Custom Features 98 </Text> 99 </span> 100 <CustomFeaturesFilter 101 selectedCustomFeatures={selectedFeatures} 102 useCustomFeatures={useCustomFeatures} 103 onClearFilters={() => setSelectedFeatures(new Set())} 104 onRemoveSelectedFeature={setSelectedFeatures} 105 /> 106 <Input 107 allowClear 108 className={styles.__invalid_search} 109 disabled={!useCustomFeatures} 110 placeholder="Filter feature list" 111 onChange={handleSearchInputChange} 112 addonBefore={<FilterIcon width={12} height={12} />} 113 /> 114 </> 115 ); 116 }; 117 118 const renderFeatureCategory = (category: string, features: string[]) => { 119 const filteredFeatures = features.filter((feature) => feature.toUpperCase().includes(searchInput.toUpperCase())); 120 if (filteredFeatures.length === 0) return null; 121 122 return ( 123 <div key={category} className={styles.innerCheckboxGrid}> 124 <Text 125 size={TextSizes.H5} 126 className={classNames(styles.category, { 127 [styles.disabled]: !useCustomFeatures, 128 })} 129 > 130 {getReadableFeatureCategory(category)} 131 </Text> 132 {filteredFeatures.sort().map((feature) => { 133 return ( 134 <Checkbox 135 key={feature as string} 136 value={feature} 137 disabled={!useCustomFeatures} 138 onChange={handleSelectFeature} 139 checked={isSetSelected(feature)} 140 className={styles.featureCheckboxRow} 141 > 142 <span className={styles.featureSpan}>{feature}</span> 143 </Checkbox> 144 ); 145 })} 146 </div> 147 ); 148 }; 149 150 return ( 151 <> 152 <OspreyButton onClick={handleOpenModal}>Select Summary Features</OspreyButton> 153 <Modal 154 title={renderModalTitle()} 155 className={styles.featureModal} 156 width={526} 157 visible={isOpen} 158 onCancel={handleCancel} 159 footer={<ModalFooter onOK={handleSaveSelectedFeatures} onCancel={handleCancel} />} 160 > 161 <div className={styles.checkboxGrid}> 162 {sortedKnownFeatureCategoryEntries.map(([category, features]) => { 163 return renderFeatureCategory(category, features); 164 })} 165 </div> 166 </Modal> 167 </> 168 ); 169}; 170 171export default FeatureSelectModal;