Skip to content

Commit a7368ae

Browse files
committed
OpenConceptLab/ocl_issues#2316 | grouping bulk controls
1 parent ae18c87 commit a7368ae

5 files changed

Lines changed: 303 additions & 126 deletions

File tree

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
import React from 'react'
2+
import { useTranslation } from 'react-i18next';
3+
import Chip from '@mui/material/Chip';
4+
import FormControl from '@mui/material/FormControl';
5+
import ListItemIcon from '@mui/material/ListItemIcon';
6+
import ListItemText from '@mui/material/ListItemText';
7+
import MenuItem from '@mui/material/MenuItem';
8+
import Select from '@mui/material/Select';
9+
import Typography from '@mui/material/Typography';
10+
11+
import DoneIcon from '@mui/icons-material/Done';
12+
import CloseIcon from '@mui/icons-material/Close';
13+
import ClearIcon from '@mui/icons-material/Clear';
14+
15+
import debounce from 'lodash/debounce'
16+
import filter from 'lodash/filter'
17+
import keys from 'lodash/keys'
18+
import pickBy from 'lodash/pickBy'
19+
import startCase from 'lodash/startCase'
20+
import without from 'lodash/without'
21+
22+
import { SURFACE_COLORS, WHITE } from '../../common/colors';
23+
import ScoreBucketButton from './ScoreBucketButton'
24+
import SearchField from './SearchField'
25+
26+
const getBulkActionMeta = (action, t) => {
27+
if(action === 'approve')
28+
return {label: t('map_project.approve'), color: 'primary.main', icon: <DoneIcon fontSize='inherit' />}
29+
if(action === 'rejected')
30+
return {label: t('map_project.reject'), color: 'error.main', icon: <CloseIcon fontSize='inherit' />}
31+
if(action === 'exclude')
32+
return {label: t('map_project.decision_exclude'), color: 'error.main', icon: <CloseIcon fontSize='inherit' />}
33+
if(action === 'clear')
34+
return {label: t('map_project.bulk_clear'), color: 'secondary.main', icon: <ClearIcon fontSize='inherit' />}
35+
return {label: action, color: 'surface.dark', icon: null}
36+
}
37+
38+
const DataGridControls = ({
39+
selectedRowStatus,
40+
selectedCandidatesScoreBucket,
41+
scoreBucketSortBy,
42+
onScoreBucketSort,
43+
onScoreBucketClick,
44+
recommendedCount,
45+
availableCount,
46+
lowRankedCount,
47+
decisions,
48+
decisionFilters,
49+
setDecisionFilters,
50+
rowStatuses,
51+
selectedRowsCount,
52+
bulkDecisionAction,
53+
onBulkDecisionChange,
54+
bulkConfirm,
55+
bulkMapType,
56+
onBulkMapTypeChange,
57+
allMapTypes,
58+
onSearchTextChange
59+
}) => {
60+
const { t } = useTranslation();
61+
const raisedBulkSelectSx = {
62+
height: '34px',
63+
backgroundColor: '#7F7AF8',
64+
color: WHITE,
65+
fontSize: '14px',
66+
borderRadius: '8px',
67+
boxShadow: '0 3px 8px rgba(90, 79, 255, 0.32)',
68+
'.MuiOutlinedInput-notchedOutline': {
69+
border: 'none'
70+
},
71+
'&:hover .MuiOutlinedInput-notchedOutline': {
72+
border: 'none'
73+
},
74+
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
75+
border: 'none'
76+
},
77+
'.MuiSelect-icon': {
78+
color: WHITE,
79+
right: '10px'
80+
},
81+
'.MuiSelect-select': {
82+
display: 'flex',
83+
alignItems: 'center',
84+
fontWeight: 500,
85+
fontSize: '14px',
86+
lineHeight: 1.43,
87+
color: WHITE,
88+
padding: '7px 36px 7px 12px !important'
89+
}
90+
}
91+
92+
const debouncedSearchChange = React.useMemo(
93+
() => debounce(val => onSearchTextChange(val || ''), 300),
94+
[onSearchTextChange]
95+
)
96+
97+
React.useEffect(() => () => debouncedSearchChange.cancel(), [debouncedSearchChange])
98+
99+
return (
100+
<>
101+
<div className='col-xs-12' style={{padding: '12px 14px 8px 14px', display: 'flex', alignItems: 'center', backgroundColor: SURFACE_COLORS.main}}>
102+
<FormControl sx={{minWidth: '16px'}}>
103+
<SearchField onChange={debouncedSearchChange} />
104+
</FormControl>
105+
<ScoreBucketButton
106+
selected={selectedCandidatesScoreBucket}
107+
onSort={onScoreBucketSort}
108+
sortBy={scoreBucketSortBy}
109+
onClick={onScoreBucketClick}
110+
recommended={recommendedCount}
111+
available={availableCount}
112+
low_ranked={lowRankedCount}
113+
/>
114+
<div style={{display: 'inline-block'}}>
115+
{
116+
selectedRowStatus === 'unmapped' &&
117+
<Chip
118+
label={`${t('map_project.rejected')} (${keys(pickBy(decisions, value => value === 'rejected')).length})`}
119+
color='error'
120+
size='small'
121+
variant={decisionFilters.includes('rejected') ? 'contained' : 'outlined'}
122+
icon={
123+
decisionFilters.includes('rejected') ?
124+
<CloseIcon fontSize='inherit' /> :
125+
<DoneIcon fontSize='inherit' />
126+
}
127+
onClick={
128+
() => setDecisionFilters(
129+
decisionFilters.includes('rejected') ?
130+
without(decisionFilters, 'rejected') :
131+
[...decisionFilters, 'rejected']
132+
)
133+
}
134+
sx={{margin: '4px'}}
135+
/>
136+
}
137+
{
138+
['reviewed', 'readyForReview'].includes(selectedRowStatus) &&
139+
<React.Fragment>
140+
{
141+
['map', 'exclude', 'none', 'propose'].map(_decision => {
142+
const isApplied = decisionFilters.includes(_decision)
143+
const isExclude = _decision === 'exclude'
144+
const isNone = _decision === 'none'
145+
const isPropose = _decision === 'propose'
146+
const count = filter(keys(pickBy(decisions, value => isNone ? !value : value === _decision)), index => rowStatuses[selectedRowStatus].includes(parseInt(index))).length
147+
return (
148+
<Chip
149+
key={_decision}
150+
disabled={!count}
151+
label={`${t(`map_project.decision_${_decision}`) || startCase(_decision)} (${count})`}
152+
color={isExclude ? 'error' : (isNone ? 'secondary' : (isPropose ? 'warning' : 'primary'))}
153+
size='small'
154+
variant={isApplied ? 'contained' : 'outlined'}
155+
icon={
156+
isApplied ?
157+
<CloseIcon fontSize='inherit' /> :
158+
<DoneIcon fontSize='inherit' />
159+
}
160+
onClick={
161+
() => setDecisionFilters(
162+
isApplied ?
163+
without(decisionFilters, _decision) :
164+
[...decisionFilters, _decision]
165+
)
166+
}
167+
sx={{margin: '4px'}}
168+
/>
169+
)
170+
})
171+
}
172+
</React.Fragment>
173+
}
174+
</div>
175+
</div>
176+
{
177+
selectedRowsCount > 0 &&
178+
<div className='col-xs-12' style={{padding: '8px 14px', display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'wrap', backgroundColor: '#e9e4ff', borderTop: 'solid 1px rgba(76, 53, 255, 0.15)', borderBottom: 'solid 1px rgba(76, 53, 255, 0.15)'}}>
179+
<Typography component='span' sx={{fontSize: '14px', fontWeight: 600, color: 'surface.dark', marginRight: '8px'}}>
180+
{t('map_project.bulk_selected', {count: selectedRowsCount})}
181+
</Typography>
182+
<FormControl size='small' variant='outlined'>
183+
<Select
184+
displayEmpty
185+
value={bulkDecisionAction}
186+
onChange={onBulkDecisionChange}
187+
renderValue={selected => {
188+
if(!selected)
189+
return t('map_project.decision')
190+
const meta = getBulkActionMeta(selected, t)
191+
return (
192+
<span style={{display: 'inline-flex', alignItems: 'center', gap: '8px', color: 'inherit', fontSize: '14px', fontWeight: 500, lineHeight: 1.43}}>
193+
<span style={{display: 'inline-flex', alignItems: 'center', fontSize: '16px', color: meta.color}}>
194+
{meta.icon}
195+
</span>
196+
<span>{meta.label}</span>
197+
</span>
198+
)
199+
}}
200+
sx={{
201+
...raisedBulkSelectSx,
202+
}}
203+
>
204+
{
205+
['approve', 'rejected', 'exclude', 'clear'].map(action => {
206+
const meta = getBulkActionMeta(action, t)
207+
return (
208+
<MenuItem key={action} value={action} sx={{color: meta.color}}>
209+
<ListItemIcon sx={{color: meta.color, minWidth: '36px'}}>
210+
{meta.icon}
211+
</ListItemIcon>
212+
<ListItemText
213+
primary={meta.label}
214+
primaryTypographyProps={{sx: {fontSize: '1rem', fontWeight: 400, lineHeight: 1.5}}}
215+
/>
216+
</MenuItem>
217+
)
218+
})
219+
}
220+
</Select>
221+
</FormControl>
222+
<FormControl size='small' variant='outlined'>
223+
<Select
224+
value={bulkConfirm?.action === 'map_type' ? bulkConfirm?.mapType : bulkMapType}
225+
onChange={onBulkMapTypeChange}
226+
renderValue={selected => `${t('map_project.map_type')}: ${selected}`}
227+
sx={{
228+
...raisedBulkSelectSx,
229+
}}
230+
>
231+
{
232+
(allMapTypes?.length ? allMapTypes : ['SAME-AS']).map(option => (
233+
<MenuItem key={option} value={option} sx={{color: 'surface.dark'}}>
234+
{option}
235+
</MenuItem>
236+
))
237+
}
238+
</Select>
239+
</FormControl>
240+
</div>
241+
}
242+
</>
243+
)
244+
}
245+
246+
export default DataGridControls

0 commit comments

Comments
 (0)