diff --git a/src/components/selectRangeUsePlanPage/EnhancedTableHead.js b/src/components/selectRangeUsePlanPage/EnhancedTableHead.js new file mode 100644 index 00000000..1a569c38 --- /dev/null +++ b/src/components/selectRangeUsePlanPage/EnhancedTableHead.js @@ -0,0 +1,220 @@ +import React, { useEffect } from 'react'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import TableCell from '@material-ui/core/TableCell'; +import TableSortLabel from '@material-ui/core/TableSortLabel'; +import PropTypes from 'prop-types'; +import MenuItem from '@mui/material/MenuItem'; +import FormControl from '@mui/material/FormControl'; +import Select from '@mui/material/Select'; +import { Checkbox, ListItemText } from '@material-ui/core'; + +EnhancedTableHead.propTypes = { + classes: PropTypes.object.isRequired, + onRequestSort: PropTypes.func.isRequired, + order: PropTypes.oneOf(['asc', 'desc']).isRequired, + orderBy: PropTypes.string.isRequired, +}; + +const headCells = [ + { + id: 'agreement.forest_file_id', + numeric: false, + disablePadding: false, + label: 'RAN #', + sortable: true, + filterable: true, + }, + { + id: 'plan.range_name', + numeric: false, + disablePadding: false, + label: 'Range Name', + sortable: true, + filterable: true, + }, + { + id: 'agreement_holder.name', + numeric: false, + disablePadding: false, + label: 'Primary Agreement Holder', + sortable: true, + filterable: true, + }, + { + id: 'user_account.family_name', + numeric: false, + disablePadding: false, + label: 'Staff Contact', + sortable: true, + filterable: true, + }, + { + id: 'plan.plan_end_date', + numeric: false, + disablePadding: false, + label: 'Plan End Date', + sortable: true, + filterable: true, + }, + { + id: 'ref_district.code', + numeric: false, + disablePadding: false, + label: 'District', + sortable: true, + filterable: true, + }, + { + id: 'plan.status_id', + numeric: false, + disablePadding: false, + label: 'Status Code', + sortable: true, + multiSelectable: true, + }, + { + id: 'plan.status', + numeric: false, + disablePadding: false, + label: 'Status', + }, + { id: 'actions', disablePadding: true }, + { id: 'extension', label: 'Extension Requests', disablePadding: false }, +]; + +function StatusCodesMultiSelect(props) { + const { onStatusCodeChange, filters, headCellID } = props; + const ITEM_HEIGHT = 48; + const ITEM_PADDING_TOP = 8; + const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 250, + }, + }, + }; + const status_codes = [ + 'C', + 'O', + 'P', + 'D', + 'R', + 'SD', + 'WM', + 'SW', + 'S', + 'NF', + 'NA', + 'A', + 'SR', + 'SFD', + 'RR', + 'RNR', + 'RFD', + 'AC', + 'RFS', + 'MSR', + 'SNR', + 'APS', + 'APA', + 'SAM', + ]; + const [selectedCodes, setSelectedCodes] = React.useState([]); + const handleChange = (event) => { + const { + target: { value }, + } = event; + setSelectedCodes(typeof value === 'string' ? value.split(',') : value); + }; + useEffect(() => { + onStatusCodeChange(selectedCodes); + }, [selectedCodes]); + + return ( + + + + ); +} + +export default function EnhancedTableHead(props) { + const { + classes, + order, + orderBy, + onRequestSort, + onRequestFilter, + filters, + onStatusCodeChange, + } = props; + const createSortHandler = (property) => (event) => { + onRequestSort(event, property); + }; + const filterHandler = (event, property) => { + onRequestFilter(event, property); + }; + + return ( + + + {headCells.map((headCell) => ( + + + {headCell.label} + {orderBy === headCell.id ? ( + + {order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + {headCell.filterable && ( + filterHandler(e, headCell.id)} + value={ + Object.hasOwn(filters, headCell.id) + ? props.filters[headCell.id] + : '' + } + /> + )} + {headCell.multiSelectable && ( + + )} + + ))} + + + ); +} \ No newline at end of file diff --git a/src/components/selectRangeUsePlanPage/SortableAgreementTable.js b/src/components/selectRangeUsePlanPage/SortableAgreementTable.js index 7595a050..786f584f 100644 --- a/src/components/selectRangeUsePlanPage/SortableAgreementTable.js +++ b/src/components/selectRangeUsePlanPage/SortableAgreementTable.js @@ -2,232 +2,16 @@ import Table from '@material-ui/core/Table'; import TableBody from '@material-ui/core/TableBody'; import TableCell from '@material-ui/core/TableCell'; import TableContainer from '@material-ui/core/TableContainer'; -import TableHead from '@material-ui/core/TableHead'; import TablePagination from '@material-ui/core/TablePagination'; import TableRow from '@material-ui/core/TableRow'; -import TableSortLabel from '@material-ui/core/TableSortLabel'; import Typography from '@material-ui/core/Typography'; -import { makeStyles } from '@material-ui/core/styles'; import Skeleton from '@material-ui/lab/Skeleton'; -import PropTypes from 'prop-types'; -import React, { useEffect } from 'react'; +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; import { useLocation } from 'react-router-dom'; import { useUser } from '../../providers/UserProvider'; import PlanRow from './PlanRow'; -import MenuItem from '@mui/material/MenuItem'; -import FormControl from '@mui/material/FormControl'; -import Select from '@mui/material/Select'; -import { Checkbox, ListItemText } from '@material-ui/core'; - -const headCells = [ - { - id: 'agreement.forest_file_id', - numeric: false, - disablePadding: false, - label: 'RAN #', - sortable: true, - filterable: true, - }, - { - id: 'plan.range_name', - numeric: false, - disablePadding: false, - label: 'Range Name', - sortable: true, - filterable: true, - }, - { - id: 'agreement_holder.name', - numeric: false, - disablePadding: false, - label: 'Primary Agreement Holder', - sortable: true, - filterable: true, - }, - { - id: 'user_account.family_name', - numeric: false, - disablePadding: false, - label: 'Staff Contact', - sortable: true, - filterable: true, - }, - { - id: 'plan.plan_end_date', - numeric: false, - disablePadding: false, - label: 'Plan End Date', - sortable: true, - filterable: true, - }, - { - id: 'ref_district.code', - numeric: false, - disablePadding: false, - label: 'District', - sortable: true, - filterable: true, - }, - { - id: 'plan.status_id', - numeric: false, - disablePadding: false, - label: 'Status Code', - sortable: true, - multiSelectable: true, - }, - { - id: 'plan.status', - numeric: false, - disablePadding: false, - label: 'Status', - }, - { id: 'actions', disablePadding: true }, - { id: 'extension', label: 'Extension Requests', disablePadding: false }, -]; - -function EnhancedTableHead(props) { - const { - classes, - order, - orderBy, - onRequestSort, - onRequestFilter, - filters, - onStatusCodeChange, - } = props; - const createSortHandler = (property) => (event) => { - onRequestSort(event, property); - }; - const filterHandler = (event, property) => { - onRequestFilter(event, property); - }; - - return ( - - - {headCells.map((headCell) => ( - - - {headCell.label} - {orderBy === headCell.id ? ( - - {order === 'desc' ? 'sorted descending' : 'sorted ascending'} - - ) : null} - - {headCell.filterable && ( - filterHandler(e, headCell.id)} - value={ - Object.hasOwn(filters, headCell.id) - ? props.filters[headCell.id] - : '' - } - /> - )} - {headCell.multiSelectable && ( - - )} - - ))} - - - ); -} - -function StatusCodesMultiSelect(props) { - const { onStatusCodeChange, filters, headCellID } = props; - const ITEM_HEIGHT = 48; - const ITEM_PADDING_TOP = 8; - const MenuProps = { - PaperProps: { - style: { - maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, - width: 250, - }, - }, - }; - const status_codes = [ - 'C', - 'O', - 'P', - 'D', - 'R', - 'SD', - 'WM', - 'SW', - 'S', - 'NF', - 'NA', - 'A', - 'SR', - 'SFD', - 'RR', - 'RNR', - 'RFD', - 'AC', - 'RFS', - 'MSR', - 'SNR', - 'APS', - 'APA', - 'SAM', - ]; - const [selectedCodes, setSelectedCodes] = React.useState([]); - const handleChange = (event) => { - const { - target: { value }, - } = event; - setSelectedCodes(typeof value === 'string' ? value.split(',') : value); - }; - useEffect(() => { - onStatusCodeChange(selectedCodes); - }, [selectedCodes]); - - return ( - - - - ); -} - -EnhancedTableHead.propTypes = { - classes: PropTypes.object.isRequired, - onRequestSort: PropTypes.func.isRequired, - order: PropTypes.oneOf(['asc', 'desc']).isRequired, - orderBy: PropTypes.string.isRequired, -}; +import EnhancedTableHead from './EnhancedTableHead'; export const useStyles = makeStyles((theme) => ({ root: { diff --git a/src/components/selectRangeUsePlanPage/ZoneSelect.js b/src/components/selectRangeUsePlanPage/ZoneSelect.js index 46a2fd56..a1ce8939 100644 --- a/src/components/selectRangeUsePlanPage/ZoneSelect.js +++ b/src/components/selectRangeUsePlanPage/ZoneSelect.js @@ -81,194 +81,6 @@ const setSaveZoneInfo = (allSelected, allDeselected, zones) => { saveDataInLocalStorage("zone-info", zoneInfo); } -export function ZoneSelectAll({ zones, setSearchSelectedZones }) { - const classes = useStyles(); - const [selectedZones = [], setSelectedZones] = useQueryParam( - 'selectedZones', - DelimitedNumericArrayParam, - ); - const [zoneMap, setZoneMap] = useState(); - const [selectAllZones, setSelectAllZones] = useState(true); - const [deselectAllZones, setDeselectAllZones] = useState(false); - const zoneInfo = getDataFromLocalStorage("zone-info"); - - const { - data: users, - error, - isValidating, - } = useSWR( - `${API.GET_USERS}/?orderCId=desc&excludeBy=username&exclude=bceid`, - (key) => axios.get(key, getAuthHeaderConfig()).then((res) => res.data), - ); - - const setAllZonesSelected = () => { - const initialSelectedZones = zones.map((zone) => zone.id); - setSelectedZones(initialSelectedZones); - setSearchSelectedZones(initialSelectedZones); - }; - - useEffect(() => { - if (zoneInfo?.allSelected) { - setAllZonesSelected(); - setSelectAllZones(true); - setDeselectAllZones(false); - } else { - setSelectAllZones(false); - } - if (zoneInfo?.allDeselected) { - setSearchSelectedZones([]); - setSelectedZones([]); - setSelectAllZones(false); - setDeselectAllZones(true); - } - - if (zoneInfo?.zones) { - setSelectedZones(zoneInfo.zones); - setSearchSelectedZones(zoneInfo.zones); - } else if (selectedZones.length === 0) { - setAllZonesSelected(); - } else { - if (!selectedZones.length) { - setAllZonesSelected(); - } else { - setSelectedZones(selectedZones); - setSearchSelectedZones(selectedZones); - } - } - }, []); - - useEffect(() => { - if (zones) { - const zoneMap = zones.reduce( - (acc, zone) => ({ ...acc, [zone.id]: zone }), - {}, - ); - setZoneMap(zoneMap); - if (!zoneInfo) setAllZonesSelected(); - } - }, [zones]); - - const handleChange = (event) => { - if (event.target.value !== undefined) { - setSelectedZones(event.target.value); - setSaveZoneInfo(false, false, event.target.value); - } - }; - - const handleClose = () => { - setSearchSelectedZones(selectedZones); - if (zones?.length === selectedZones?.length) { - setSelectAllZones(true); - } else { - setSelectAllZones(false); - } - if (selectedZones?.length > 0) { - setDeselectAllZones(false); - } - }; - - if ((isValidating && !users) || !zoneMap) { - return Loading zones; - } - - if (error) { - return Error fetching users; - } - - return ( -
- { - setSelectAllZones(event.target.checked); - if (event.target.checked) { - setAllZonesSelected(); - setDeselectAllZones(!event.target.checked); - setSaveZoneInfo(true, false, zones.map((zone) => zone.id)); - } - }} - name="selectAllZones" - color="primary" - /> - } - label="Select All Zones" - /> - { - setDeselectAllZones(event.target.checked); - if (event.target.checked) { - setSearchSelectedZones([]); - setSelectedZones([]); - setSelectAllZones(!event.target.checked); - setSaveZoneInfo(false, true, []); - } - }} - name="deselectAllZones" - color="primary" - /> - } - label="Deselect All Zones" - /> - - Select Individual Zones - - -
- ); -} - export default function ZoneSelect({ zones, userZones, diff --git a/src/components/selectRangeUsePlanPage/ZoneSelectAll.js b/src/components/selectRangeUsePlanPage/ZoneSelectAll.js new file mode 100644 index 00000000..986b3a8c --- /dev/null +++ b/src/components/selectRangeUsePlanPage/ZoneSelectAll.js @@ -0,0 +1,270 @@ +import React, { useState, useEffect } from 'react'; +import useSWR from 'swr'; +import { makeStyles, withStyles } from '@material-ui/core/styles'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormControl from '@material-ui/core/FormControl'; +import ListItemText from '@material-ui/core/ListItemText'; +import Select from '@material-ui/core/Select'; +import Checkbox from '@material-ui/core/Checkbox'; +import { + getUserFullName, + axios, + getAuthHeaderConfig, + getDataFromLocalStorage, + saveDataInLocalStorage +} from '../../utils'; +import * as API from '../../constants/api'; +import { useQueryParam, DelimitedNumericArrayParam } from 'use-query-params'; +import { FormControlLabel } from '@material-ui/core'; + +const useStyles = makeStyles((theme) => ({ + formControl: { + minWidth: 250, + maxWidth: 400, + marginTop: -3, + marginRight: 0, + }, + chips: { + display: 'flex', + flexWrap: 'wrap', + }, + chip: { + margin: 2, + }, + noLabel: { + marginTop: theme.spacing(3), + }, + listItemTextPrimary: { + fontSize: '12px', + fontColor: '#002C71', + }, + listItemTextSecondary: { + fontSize: '16px', + color: 'grey', + }, +})); + +const checkBoxStyles = () => ({ + root: { + '&$checked': { + color: 'rgb(0, 30, 79)', + }, + marginTop: '12.6px', + }, + checked: {}, +}); + +const CustomCheckbox = withStyles(checkBoxStyles)(Checkbox); + +const ITEM_HEIGHT = 78; +const ITEM_PADDING_TOP = 8; +const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 250, + color: 'grey', + }, + }, +}; + +// Persisting zone information in localstorage +const setSaveZoneInfo = (allSelected, allDeselected, zones) => { + const currZoneInfo = getDataFromLocalStorage("zone-info"); + const zoneInfo = { + ...currZoneInfo, + allSelected: allSelected, + allDeselected: allDeselected, + zones: zones + } + saveDataInLocalStorage("zone-info", zoneInfo); +} + +export default function ZoneSelectAll({ zones, setSearchSelectedZones }) { + const classes = useStyles(); + const [selectedZones = [], setSelectedZones] = useQueryParam( + 'selectedZones', + DelimitedNumericArrayParam, + ); + const [zoneMap, setZoneMap] = useState(); + const [selectAllZones, setSelectAllZones] = useState(true); + const [deselectAllZones, setDeselectAllZones] = useState(false); + const zoneInfo = getDataFromLocalStorage("zone-info"); + + const { + data: users, + error, + isValidating, + } = useSWR( + `${API.GET_USERS}/?orderCId=desc&excludeBy=username&exclude=bceid`, + (key) => axios.get(key, getAuthHeaderConfig()).then((res) => res.data), + ); + + const setAllZonesSelected = () => { + const initialSelectedZones = zones.map((zone) => zone.id); + setSelectedZones(initialSelectedZones); + setSearchSelectedZones(initialSelectedZones); + }; + + useEffect(() => { + if (zoneInfo?.allSelected) { + setAllZonesSelected(); + setSelectAllZones(true); + setDeselectAllZones(false); + } else { + setSelectAllZones(false); + } + if (zoneInfo?.allDeselected) { + setSearchSelectedZones([]); + setSelectedZones([]); + setSelectAllZones(false); + setDeselectAllZones(true); + } + + if (zoneInfo?.zones) { + setSelectedZones(zoneInfo.zones); + setSearchSelectedZones(zoneInfo.zones); + } else if (selectedZones.length === 0) { + setAllZonesSelected(); + } else { + if (!selectedZones.length) { + setAllZonesSelected(); + } else { + setSelectedZones(selectedZones); + setSearchSelectedZones(selectedZones); + } + } + }, []); + + useEffect(() => { + if (zones) { + const zoneMap = zones.reduce( + (acc, zone) => ({ ...acc, [zone.id]: zone }), + {}, + ); + setZoneMap(zoneMap); + if (!zoneInfo) setAllZonesSelected(); + } + }, [zones]); + + const handleChange = (event) => { + if (event.target.value !== undefined) { + setSelectedZones(event.target.value); + setSaveZoneInfo(false, false, event.target.value); + } + }; + + const handleClose = () => { + setSearchSelectedZones(selectedZones); + if (zones?.length === selectedZones?.length) { + setSelectAllZones(true); + } else { + setSelectAllZones(false); + } + if (selectedZones?.length > 0) { + setDeselectAllZones(false); + } + }; + + if ((isValidating && !users) || !zoneMap) { + return Loading zones; + } + + if (error) { + return Error fetching users; + } + + return ( +
+ { + setSelectAllZones(event.target.checked); + if (event.target.checked) { + setAllZonesSelected(); + setDeselectAllZones(!event.target.checked); + setSaveZoneInfo(true, false, zones.map((zone) => zone.id)); + } + }} + name="selectAllZones" + color="primary" + /> + } + label="Select All Zones" + /> + { + setDeselectAllZones(event.target.checked); + if (event.target.checked) { + setSearchSelectedZones([]); + setSelectedZones([]); + setSelectAllZones(!event.target.checked); + setSaveZoneInfo(false, true, []); + } + }} + name="deselectAllZones" + color="primary" + /> + } + label="Deselect All Zones" + /> + + Select Individual Zones + + +
+ ); +} \ No newline at end of file diff --git a/src/components/selectRangeUsePlanPage/index.js b/src/components/selectRangeUsePlanPage/index.js index 0bc462c3..1f0b32be 100644 --- a/src/components/selectRangeUsePlanPage/index.js +++ b/src/components/selectRangeUsePlanPage/index.js @@ -30,9 +30,10 @@ import { saveDataInLocalStorage, } from '../../utils'; import useDebounce from '../../utils/hooks/useDebounce'; -import { Banner } from '../common'; import Error from './Error'; -import ZoneSelect, { ZoneSelectAll } from './ZoneSelect'; +import ZoneSelect from './ZoneSelect'; +import ZoneSelectAll from './ZoneSelectAll'; +import { Banner } from '../common'; import SortableAgreementTable from './SortableAgreementTable';