Skip to content

Commit

Permalink
Merge branch 'release-0.2.0' into fix/daniel-use-correct-compliance-p…
Browse files Browse the repository at this point in the history
…eriod-1497
  • Loading branch information
dhaselhan authored Jan 2, 2025
2 parents 6ba6c0f + ddac6d8 commit a41e13d
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import CheckBoxIcon from '@mui/icons-material/CheckBox'

const icon = <CheckBoxOutlineBlankIcon fontSize="small" />
const checkedIcon = <CheckBoxIcon fontSize="small" />

/**
* @deprecated
*/
export const BCColumnSetFilter = forwardRef((props, ref) => {
const { apiQuery, params } = props
const [options, setOptions] = useState([])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { useState, useEffect, useCallback } from 'react'
import { FormControl, IconButton, InputAdornment } from '@mui/material'
import {
Clear as ClearIcon,
CalendarToday as CalendarIcon
} from '@mui/icons-material'
import { DatePicker } from '@mui/x-date-pickers'
import { format, isValid } from 'date-fns'

export const BCDateFloatingFilter = ({
model,
onModelChange,
disabled = false,
initialFilterType = 'equals',
label = 'Select Date'
}) => {
const [selectedDate, setSelectedDate] = useState(null)
const [open, setOpen] = useState(false)

const handleChange = useCallback((newDate) => {
setSelectedDate(newDate)

if (newDate && isValid(newDate)) {
onModelChange({
type: initialFilterType,
dateFrom: format(newDate, 'yyyy-MM-dd'),
dateTo: null,
filterType: 'date'
})
} else {
onModelChange(null)
}
}, [])

const handleClear = (event) => {
event.stopPropagation()
setSelectedDate(null)
onModelChange(null)
}

const handleOpen = () => {
setOpen(true)
}

const handleClose = () => {
setOpen(false)
}

useEffect(() => {
if (!model) {
setSelectedDate(null)
return
}

if (model.filter) {
const date = new Date(model.dateFrom)
setSelectedDate(isValid(date) ? date : null)
}
}, [model])

return (
<FormControl
className="bc-column-date-filter"
fullWidth
size="small"
role="group"
aria-labelledby="date-picker-label"
sx={{
border: 'none',
'& .MuiOutlinedInput-root': { p: 0 },
'& .MuiOutlinedInput-notchedOutline': { border: 'none' },
'& .Mui-focused': {
border: '1px solid #495057',
boxShadow: '0 0 0 1px #495057'
}
}}
>
<DatePicker
id="date-picker"
aria-label="Date Picker"
aria-describedby="date-picker-description"
sx={{ border: 'none', borderBottom: '2px solid #495057' }}
value={selectedDate}
onChange={handleChange}
open={open}
onOpen={handleOpen}
onClose={handleClose}
disabled={disabled}
format="yyyy-MM-dd"
slotProps={{
textField: {
size: 'small',
label,
InputProps: {
startAdornment: (
<InputAdornment position="start">
<IconButton
size="small"
edge="start"
onClick={() => setOpen(true)}
aria-label="Open calendar"
>
<CalendarIcon fontSize="small" />
</IconButton>
</InputAdornment>
),
endAdornment: selectedDate && (
<InputAdornment position="end">
<IconButton
size="small"
onClick={handleClear}
onMouseDown={(event) => event.stopPropagation()}
edge="end"
aria-label="Clear date"
>
<ClearIcon fontSize="small" />
</IconButton>
</InputAdornment>
)
}
}
}}
/>
</FormControl>
)
}

BCDateFloatingFilter.displayName = 'BCDateFloatingFilter'
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { useState, useCallback, useEffect } from 'react'
import { IconButton } from '@mui/material'
import { Clear as ClearIcon } from '@mui/icons-material'
const ITEM_HEIGHT = 48
const ITEM_PADDING_TOP = 8

export const BCSelectFloatingFilter = ({
model,
onModelChange,
optionsQuery,
valueKey = 'value',
labelKey = 'label',
disabled = false,
params,
initialFilterType = 'equals',
multiple = false,
initialSelectedValues = []
}) => {
const [selectedValues, setSelectedValues] = useState([])
const { data: optionsData, isLoading, isError, error } = optionsQuery(params)

const handleChange = (event) => {
const { options } = event.target
const newValues = Array.from(options)
.filter((option) => option.selected)
.map((option) => option.value)

if (!multiple) {
setSelectedValues([newValues[0] || ''])
onModelChange(
!newValues[0] || newValues[0] === '0'
? null
: {
type: initialFilterType,
filter: newValues[0]
}
)
} else {
setSelectedValues(newValues)
onModelChange({
type: initialFilterType,
filter: newValues
})
}
}

const handleClear = (event) => {
event.stopPropagation()
setSelectedValues([])
onModelChange(null)
}

const renderSelectContent = useCallback(() => {
if (isLoading) {
return (
<option disabled value="">
Loading...
</option>
)
}

if (isError) {
return (
<option disabled value="">
Error loading options: {error?.message}
</option>
)
}

return (optionsData || []).map((option) => (
<option key={option[valueKey]} value={option[valueKey]}>
{option[labelKey]}
</option>
))
}, [isLoading, isError, optionsData, error])

useEffect(() => {
if (!model) {
setSelectedValues(initialSelectedValues)
} else {
setSelectedValues([model?.filter])
}
}, [model, initialSelectedValues])

return (
<div
style={{ position: 'relative', width: '100%' }}
role="group"
aria-labelledby="select-filter-label"
>
<div
className="select-container"
style={{
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP
}}
role="combobox"
aria-expanded={selectedValues.length > 0}
aria-controls="select-filter"
>
<select
id="select-filter"
multiple={multiple}
value={selectedValues}
onChange={handleChange}
disabled={disabled || isLoading}
aria-multiselectable={multiple}
aria-disabled={disabled || isLoading}
aria-describedby={
isError ? 'select-filter-error' : 'select-filter-description'
}
style={{
color: selectedValues.length > 0 ? '#999' : '#000'
}}
>
<option
value=""
disabled={!multiple}
style={{ display: multiple ? 'none' : 'block' }}
>
Select
</option>
{renderSelectContent()}
</select>
{selectedValues.length > 0 && (
<IconButton
size="small"
sx={{ mr: 2 }}
onClick={handleClear}
onMouseDown={(event) => event.stopPropagation()}
aria-label="Clear selection"
>
<ClearIcon fontSize="small" />
</IconButton>
)}
</div>
</div>
)
}

BCSelectFloatingFilter.displayName = 'BCSelectFloatingFilter'
2 changes: 2 additions & 0 deletions frontend/src/components/BCDataGrid/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export { BCPagination } from './StatusBar/BCPagination'
export { LargeTextareaEditor } from './Editors/LargeTextareaEditor'
export { TextCellEditor } from './Editors/TextCellEditor'
export { NumberEditor } from './Editors/NumberEditor'
export { BCDateFloatingFilter } from './Filters/BCDateFloatingFilter'
export { BCSelectFloatingFilter } from './Filters/BCSelectFloatingFilter'
30 changes: 30 additions & 0 deletions frontend/src/themes/base/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,36 @@ const globals = {
fontWeight: 700,
color: grey[700]
},
'.select-container': {
fontFamily: "'BCSans', 'Noto Sans', 'Verdana', 'Arial', 'sans-serif'",
fontSize: '1.6rem',
width: '100%',
display: 'flex',
alignItems: 'center',
gap: '8px',
border: 'none',
borderBottom: '2px solid #495057',
borderRadius: '0px',
padding: '0px',
background: '#fff',
transition: 'border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out'
},
'.select-container:focus-within': {
border: '2px solid #495057',
borderWidth: '0px 0.01rem'
},
'.select-container #select-filter': {
width: '100%',
padding: '11px',
border: 'none',
outline: 'none',
appearance: 'none',
background: 'transparent'
},
'.select-container option': {
fontSize: '1rem',
fontFamily: 'inherit'
},
// editor theme for ag-grid quertz theme
'.ag-theme-quartz': {
'--ag-borders': `0.5px solid ${grey[400]} !important`,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/views/Admin/AdminMenu/components/Users.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const Users = () => {
navigate(ROUTES.ADMIN_USERS_ADD)
}
const getRowId = useCallback((params) => {
return params.data.userProfileId
return params.data.userProfileId.toString()
}, [])

const handleRowClicked = useCallback((params) => {
Expand Down
28 changes: 16 additions & 12 deletions frontend/src/views/Admin/AdminMenu/components/_schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import {
RoleRenderer,
StatusRenderer
} from '@/utils/grid/cellRenderers'
import { BCColumnSetFilter } from '@/components/BCDataGrid/components'
import { useRoleList } from '@/hooks/useRole'
import {
BCSelectFloatingFilter,
BCDateFloatingFilter
} from '@/components/BCDataGrid/components/index'

export const usersColumnDefs = (t) => [
{
Expand Down Expand Up @@ -44,14 +47,14 @@ export const usersColumnDefs = (t) => [
},
suppressFilterButton: true
},
floatingFilterComponent: BCColumnSetFilter,
floatingFilterComponent: BCSelectFloatingFilter,
suppressFloatingFilterButton: true,
suppressHeaderFilterButton: true,
floatingFilterComponentParams: {
apiQuery: useRoleList, // all data returned should be an array which includes an object of key 'name'
// Eg: [{id: 1, name: 'EntryListItem' }] except name all others are optional
optionsQuery: useRoleList,
params: 'government_roles_only=true',
key: 'admin-users',
disableCloseOnSelect: false,
multiple: false
valueKey: 'name',
labelKey: 'name'
},
cellRenderer: RoleRenderer,
cellClass: 'vertical-middle'
Expand Down Expand Up @@ -84,20 +87,21 @@ export const usersColumnDefs = (t) => [
},
cellRenderer: StatusRenderer,
cellClass: 'vertical-middle',
floatingFilterComponent: BCColumnSetFilter,
floatingFilterComponent: BCSelectFloatingFilter,
floatingFilterComponentParams: {
apiQuery: () => ({
optionsQuery: () => ({
data: [
{ id: 1, name: t('admin:userColLabels.active') },
{ id: 0, name: t('admin:userColLabels.inactive') }
],
isLoading: false
}),
disableCloseOnSelect: false,
multiple: false
valueKey: 'name',
labelKey: 'name'
},
minWidth: 120,
suppressHeaderMenuButton: false
suppressFloatingFilterButton: true,
suppressHeaderFilterButton: true
},
{
colId: 'organizationId',
Expand Down
Loading

0 comments on commit a41e13d

Please sign in to comment.