diff --git a/src/app/components/change-request/change-requests-view.jsx b/src/app/components/change-request/change-requests-view.jsx index e39d882102..4dcc00a34c 100755 --- a/src/app/components/change-request/change-requests-view.jsx +++ b/src/app/components/change-request/change-requests-view.jsx @@ -1,20 +1,12 @@ import React, { useContext, useEffect, useState } from 'react'; import { + Box, Button, - ButtonGroup, Card, CardContent, CardHeader, - CircularProgress, MenuItem, MenuList, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableRow, - Typography, makeStyles, } from '@material-ui/core'; import VisibilityIcon from '@material-ui/icons/Visibility'; @@ -28,68 +20,46 @@ import ChplChangeRequestsDownload from './change-requests-download'; import { useFetchChangeRequests } from 'api/change-requests'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; import { ChplAvatar, ChplLink, + ChplLoadingCards, ChplPagination, - ChplSortableHeaders, + ChplSearchResultCard, + ChplSearchResultControls, + ChplSortControls, } from 'components/util'; import { eventTrack } from 'services/analytics.service'; import { getDisplayDateFormat } from 'services/date-util'; import { useSessionStorage as useStorage } from 'services/storage.service'; import { UserContext, useAnalyticsContext } from 'shared/contexts'; -import { palette, theme, utilStyles } from 'themes'; +import { palette, utilStyles } from 'themes'; const useStyles = makeStyles({ ...utilStyles, - container: { - maxHeight: '64vh', - }, - tableResultsHeaderContainer: { - display: 'grid', - gap: '8px', - margin: '16px 32px', - gridTemplateColumns: '1fr', - alignItems: 'center', - justifyContent: 'space-between', - [theme.breakpoints.up('sm')]: { - gridTemplateColumns: 'auto auto', - }, + card: { + overflow: 'visible', }, resultsContainer: { - display: 'grid', - gap: '8px', - justifyContent: 'start', - gridTemplateColumns: 'auto auto', - alignItems: 'center', + padding: ({ embedded }) => (embedded ? '0' : '0 32px'), }, - wrap: { - flexFlow: 'wrap', - }, - tableFirstColumn: { - position: 'sticky', - left: 0, - boxShadow: 'rgba(149, 157, 165, 0.1) 0px 4px 8px', - backgroundColor: palette.white, - }, - tableDeveloperCell: { + developerTitle: { display: 'flex', alignItems: 'center', gap: '8px', }, - developerName: { - fontWeight: '600', - }, noResultsContainer: { padding: '16px 32px', }, }); -function ChplChangeRequestsView({ disallowedFilters, bonusQuery, dispatch, embedded = false }) { +function ChplChangeRequestsView({ + disallowedFilters, bonusQuery, dispatch, embedded = false, +}) { const storageKey = 'storageKey-changeRequestsView'; const { analytics } = useAnalyticsContext(); const { hasAnyRole } = useContext(UserContext); @@ -109,7 +79,7 @@ function ChplChangeRequestsView({ disallowedFilters, bonusQuery, dispatch, embed sortDescending: order === 'desc', query: `${queryString()}${bonusQuery}`, }); - const classes = useStyles(); + const classes = useStyles({ embedded }); useEffect(() => { if (data?.recordCount > 0 && pageNumber > 0 && data?.results?.length === 0) { @@ -128,20 +98,17 @@ function ChplChangeRequestsView({ disallowedFilters, bonusQuery, dispatch, embed } }, [data, isLoading, isSuccess]); - /* eslint object-curly-newline: ["error", { "minProperties": 5, "consistent": true }] */ - const headers = hasAnyRole(['chpl-developer']) ? [ - { property: 'change_request_type', text: 'Request Type', sortable: true }, - { property: 'change_request_status', text: 'Request Status', sortable: true }, - { property: 'current_status_change_date_time', text: 'Time Since Last Status Change', sortable: true, reverseDefault: true }, - { text: 'Actions', invisible: true }, + const isDeveloper = hasAnyRole(['chpl-developer']); + const sortOptions = isDeveloper ? [ + { property: 'change_request_type', text: 'Request Type' }, + { property: 'change_request_status', text: 'Request Status' }, + { property: 'current_status_change_date_time', text: 'Time Since Last Status Change', reverseDefault: true }, ] : [ - { property: 'developer', text: 'Developer', sortable: true }, - { property: 'change_request_type', text: 'Request Type', sortable: true }, - { property: 'submitted_date_time', text: 'Creation Date', sortable: true, reverseDefault: true }, - { property: 'change_request_status', text: 'Request Status', sortable: true }, - { property: 'current_status_change_date_time', text: 'Time Since Last Status Change', sortable: true, reverseDefault: true }, - { text: 'Associated ONC-ACBs' }, - { text: 'Actions', invisible: true }, + { property: 'developer', text: 'Developer' }, + { property: 'change_request_type', text: 'Request Type' }, + { property: 'submitted_date_time', text: 'Creation Date', reverseDefault: true }, + { property: 'change_request_status', text: 'Request Status' }, + { property: 'current_status_change_date_time', text: 'Time Since Last Status Change', reverseDefault: true }, ]; const handleDispatch = (action, payload) => { @@ -156,7 +123,7 @@ function ChplChangeRequestsView({ disallowedFilters, bonusQuery, dispatch, embed } }; - const handleTableSort = (event, property, orderDirection) => { + const handleSort = (property, orderDirection) => { eventTrack({ ...analytics, event: 'Sort Column', @@ -187,21 +154,19 @@ function ChplChangeRequestsView({ disallowedFilters, bonusQuery, dispatch, embed const pageStart = (pageNumber * pageSize) + 1; const pageEnd = Math.min((pageNumber + 1) * pageSize, data?.recordCount); + const recordCount = data?.recordCount ?? 0; const content = ( <> - - { isLoading - && ( -
- -
- )} - { !isLoading + + { isLoading && ()} + { !isLoading && ( <> { isError @@ -217,84 +182,61 @@ function ChplChangeRequestsView({ disallowedFilters, bonusQuery, dispatch, embed )} - { isSuccess + { !isError && ( <> -
-
- Search Results: - { changeRequests.length === 0 - && ( - <> - No results found - - )} - { changeRequests.length > 0 - && ( - - {`(${pageStart}-${pageEnd} of ${data?.recordCount} Results)`} - - )} -
+ + { changeRequests.length > 0 && ( - - - + )} -
+ { changeRequests.length > 0 && ( <> - - - - - {changeRequests - .map((item) => ( - - { !hasAnyRole(['chpl-developer']) - && ( - -
-
- -
-
- -
-
-
- )} - {item.changeRequestType.name} - { !hasAnyRole(['chpl-developer']) - && {getDisplayDateFormat(item.submittedDateTime)}} - {item.currentStatus.name} - + + { changeRequests.map((item) => ( + + + + + )} + fieldGroups={isDeveloper ? [ + [ + { label: 'Request Type', value: item.changeRequestType.name }, + { label: 'Request Status', value: item.currentStatus.name }, + { + label: 'Time Since Last Status Change', + value: ( {item.currentStatus.statusChangeDateTime} - - { !hasAnyRole(['chpl-developer']) - && ( - - { item.certificationBodies.length === 0 - ? ( - <> - None - - ) : ( - <> - { item.certificationBodies.map((acb) => acb.name).join('; ') } - - )} - - )} - - - -
- ))} -
-
-
+ {item.currentStatus.statusChangeDateTime} + + ), + }, + { + label: 'Associated ONC-ACBs', + style: { flex: '0 1 calc((100% - 56px) / 3)' }, + value: item.certificationBodies.length === 0 + ? 'None' + : item.certificationBodies.map((acb) => acb.name).join('; '), + }, + ], + ]} + actions={( + + )} + /> + ))} + )} +
); return ( - + { bonusQuery && ( diff --git a/src/app/components/filter/filter-chips.jsx b/src/app/components/filter/filter-chips.jsx index e820acd2d4..c0f9c3e6ba 100755 --- a/src/app/components/filter/filter-chips.jsx +++ b/src/app/components/filter/filter-chips.jsx @@ -1,60 +1,84 @@ import React, { useEffect, useState } from 'react'; import { Button, + Card, + CardContent, Chip, FormControlLabel, Switch, Typography, makeStyles, } from '@material-ui/core'; +import { bool } from 'prop-types'; import { useFilterContext } from './filter-context'; import { ChplTooltip } from 'components/util'; import { eventTrack } from 'services/analytics.service'; import { getStatusIcon } from 'services/listing.service'; -import theme from 'themes/theme'; import { palette } from 'themes'; -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles({ filterContainer: { display: 'flex', - padding: '16px 32px', - marginTop: '-4px', - position: 'relative', - zIndex: 1, - backgroundColor: palette.white, - borderBottom: `1px solid ${palette.greyBorder}`, - flexWrap: 'wrap', - flexFlow: 'column', + flexDirection: 'column', alignItems: 'flex-start', - [theme.breakpoints.up('sm')]: { - flexWrap: 'wrap', - }, + gap: '16px', + width: '100%', }, filterSelectedContainer: { display: 'flex', + flexDirection: 'column', gap: '4px', - alignItems: 'center', + alignItems: 'flex-start', justifyContent: 'flex-start', + width: '100%', + }, + filterSelectedContainerHorizontal: { + flexDirection: 'row', flexWrap: 'wrap', + alignItems: 'center', }, filterChipsContainer: { display: 'flex', - gap: '8px', + gap: '16px', alignContent: 'flex-start', - flexWrap: 'wrap', - flexDirection: 'row', - alignItems: 'center', + flexDirection: 'column', + alignItems: 'flex-start', + width: '100%', + }, + filterGroupTitleHorizontal: { + width: '100%', }, chip: { border: `1px solid ${palette.primary}`, backgroundColor: palette.white, + maxWidth: '100%', + minHeight: '24px', + height: 'auto', + '& .MuiChip-label': { + whiteSpace: 'normal', + wordBreak: 'break-word', + }, + }, + chipDeleteIcon: { + order: -2, + marginLeft: '5px', + marginRight: '-4px', + }, + chipLabel: { + alignItems: 'center', + display: 'inline-flex', + gap: '4px', }, - chipAvatar: { - backgroundColor: 'transparent !important', + card: { + width: '100%', + maxHeight: '50vh', + overflowY: 'auto', }, -})); +}); + +const ConditionalWrapper = ({ condition, wrapper, children }) => (condition ? wrapper(children) : children); const truncate = (str, n, useWordBoundary) => { if (str.length <= n) { return str; } @@ -64,7 +88,7 @@ const truncate = (str, n, useWordBoundary) => { const maxLengthForChip = 40; -function ChplFilterChips() { +function ChplFilterChips({ horizontal = false }) { const [filters, setFilters] = useState([]); const filterContext = useFilterContext(); const classes = useStyles(); @@ -127,95 +151,122 @@ function ChplFilterChips() { filterContext.dispatch('toggleShowAll', f); }; + if (filters.length === 0) { return null; } + + const getChipLabel = (f, labelText, iconName) => { + if (f.key !== 'certificationStatuses') { return labelText; } + return ( + + { labelText } + { getStatusIcon({ name: iconName }) } + + ); + }; + return ( - Filters Applied: -
- { filters.map((f) => ( - - - - {f.getFilterDisplay(f)} - - - { f.operatorKey - && ( - toggleOperator(f)} - /> - )} - label={f.operator === 'and' ? 'All' : 'Any'} - /> - )} - { f.developersListingsCriteriaOptionKey - && ( - toggleDevelopersListingsCriteriaOption(f)} - /> - )} - label={f.developersListingsCriteriaOption === 'active' ? 'Active Listings' : 'All Listings'} - /> - )} - {f.values - .filter((v, idx) => f.showAll || idx < DISPLAY_MAX) - .map((v) => ( - - { f.getValueDisplay(v).length > maxLengthForChip - ? ( - - removeChip(f, v)} - variant="outlined" - disabled={f.required && f.values.length === 1} - classes={{ avatar: classes.chipAvatar, root: classes.chip }} - /> - - ) : ( - removeChip(f, v)} - variant="outlined" - disabled={f.required && f.values.length === 1} - classes={{ avatar: classes.chipAvatar, root: classes.chip }} + + + Filters Applied: +
+ { filters.map((f) => ( + + + + {f.getFilterDisplay(f)} + + + { f.operatorKey + && ( + toggleOperator(f)} /> )} - - ))} - { f.values.length > DISPLAY_MAX - && ( - - )} - - ))} -
+ label={f.operator === 'and' ? 'All' : 'Any'} + /> + )} + { f.developersListingsCriteriaOptionKey + && ( + toggleDevelopersListingsCriteriaOption(f)} + /> + )} + label={f.developersListingsCriteriaOption === 'active' ? 'Active Listings' : 'All Listings'} + /> + )} + {f.values + .filter((v, idx) => f.showAll || idx < DISPLAY_MAX) + .map((v) => ( + + { f.getValueDisplay(v).length > maxLengthForChip + ? ( + ( + + {children} + + )} + > + removeChip(f, v)} + variant="outlined" + disabled={f.required && f.values.length === 1} + classes={{ root: classes.chip, deleteIcon: classes.chipDeleteIcon }} + /> + + ) : ( + removeChip(f, v)} + variant="outlined" + disabled={f.required && f.values.length === 1} + classes={{ root: classes.chip, deleteIcon: classes.chipDeleteIcon }} + /> + )} + + ))} + { f.values.length > DISPLAY_MAX + && ( + + )} +
+ ))} +
+ +
); } export default ChplFilterChips; -ChplFilterChips.propTypes = {}; +ChplFilterChips.propTypes = { + horizontal: bool, +}; diff --git a/src/app/components/filter/filter-layout.jsx b/src/app/components/filter/filter-layout.jsx new file mode 100644 index 0000000000..4b4b750d9f --- /dev/null +++ b/src/app/components/filter/filter-layout.jsx @@ -0,0 +1,187 @@ +import React, { useEffect, useState } from 'react'; +import { + Box, + Button, + Card, + CardContent, + Chip, + Collapse, + Typography, + makeStyles, + useMediaQuery, +} from '@material-ui/core'; +import FilterListIcon from '@material-ui/icons/FilterList'; +import LabelOffIcon from '@material-ui/icons/LabelOff'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ExpandLessIcon from '@material-ui/icons/ExpandLess'; +import { bool, node } from 'prop-types'; + +import ChplFilterChips from './filter-chips'; +import { useFilterContext } from './filter-context'; + +import { palette, theme } from 'themes'; + +const useStyles = makeStyles({ + layoutContainer: { + display: 'grid', + gridTemplateColumns: '1fr', + gap: '16px', + alignItems: 'start', + margin: '16px 0', + [theme.breakpoints.up('md')]: { + gridTemplateColumns: '260px 1fr', + gap: '24px', + }, + }, + layoutContainerMobileOnly: { + display: 'grid', + gridTemplateColumns: '1fr', + gap: '16px', + alignItems: 'start', + margin: '16px 0', + }, + sidebar: { + display: 'flex', + flexDirection: 'column', + gap: '8px', + [theme.breakpoints.up('md')]: { + position: 'sticky', + top: '190px', + }, + }, + sidebarMobileOnly: { + display: 'flex', + flexDirection: 'column', + gap: '8px', + }, + sidebarToggle: { + justifyContent: 'space-between', + color: palette.primary, + borderColor: palette.primaryBorder, + [theme.breakpoints.up('md')]: { + display: 'none', + }, + }, + sidebarToggleMobileOnly: { + justifyContent: 'space-between', + color: palette.primary, + borderColor: palette.primaryBorder, + }, + sidebarToggleLabel: { + display: 'flex', + alignItems: 'center', + gap: '8px', + }, + countChip: { + backgroundColor: palette.primary, + color: palette.white, + fontWeight: 600, + height: '18px', + fontSize: '0.7rem', + '& .MuiChip-labelSmall': { + paddingLeft: '6px', + paddingRight: '6px', + }, + }, + emptyCard: { + width: '100%', + // sit above the results-controls fade (content z-index 1) but below the sticky search bar (z-index 3) + // so the message stays readable at rest yet tucks under the search when scrolling on mobile + position: 'relative', + zIndex: 2, + }, + emptyContent: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + textAlign: 'center', + gap: '8px', + }, + emptyIcon: { + fontSize: '2rem', + }, + content: { + minWidth: 0, + position: 'relative', + zIndex: 1, + }, +}); + +function ChplFilterLayout({ children = undefined, mobileOnly = false }) { + const classes = useStyles(); + const filterContext = useFilterContext(); + const isDesktopWidth = useMediaQuery(theme.breakpoints.up('md')); + const isDesktop = isDesktopWidth && !mobileOnly; + const [expanded, setExpanded] = useState(false); + + const hasAppliedFilters = filterContext.filters + .some((filter) => filter.values?.some((v) => v.selected)); + + const appliedCount = filterContext.filters + .reduce((sum, filter) => sum + (filter.values?.filter((v) => v.selected).length ?? 0), 0); + + // On desktop the chips always render, so collapse the mobile toggle when the + // viewport crosses into desktop width; this keeps it collapsed if it returns to mobile. + useEffect(() => { + if (isDesktop) { setExpanded(false); } + }, [isDesktop]); + + if (!hasAppliedFilters) { + return ( +
+ + + + + + No filters applied. Please use the Filters button to apply filters and view results. + + + + +
+ {children} +
+
+ ); + } + + return ( +
+ + + {isDesktop + ? + : ( + + + + )} + +
+ {children} +
+
+ ); +} + +export default ChplFilterLayout; + +ChplFilterLayout.propTypes = { + children: node, + mobileOnly: bool, +}; diff --git a/src/app/components/filter/filter-panel.jsx b/src/app/components/filter/filter-panel.jsx index 4176d42d7f..ad941f15b6 100755 --- a/src/app/components/filter/filter-panel.jsx +++ b/src/app/components/filter/filter-panel.jsx @@ -12,6 +12,7 @@ import { makeStyles, } from '@material-ui/core'; import FilterListIcon from '@material-ui/icons/FilterList'; +import { number } from 'prop-types'; import { useFilterContext } from './filter-context'; @@ -31,9 +32,15 @@ const useStyles = makeStyles({ gridTemplateColumns: '1fr', rowGap: '16px', [theme.breakpoints.up('md')]: { - gridTemplateColumns: '1fr 1fr', + gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', }, }, + filterPanelFooter: { + display: 'flex', + justifyContent: 'flex-end', + padding: '8px 16px', + borderTop: `1px solid ${palette.primaryBorder}`, + }, filterPanelPrimary: { padding: '16px', }, @@ -50,15 +57,12 @@ const useStyles = makeStyles({ }, filterContainer: { display: 'grid', - gridTemplateColumns: 'repeat(auto-fit, minmax(225px, 1fr))', + gridTemplateColumns: ({ filterGridMinColWidth }) => `repeat(auto-fit, minmax(${filterGridMinColWidth}px, 1fr))`, justifyItems: 'start', alignItems: 'start', gap: '16px', padding: '0 8px', marginTop: '16px', - [theme.breakpoints.up('xl')]: { - gridTemplateColumns: 'repeat(auto-fit, minmax(275px, 1fr))', - }, }, filterHeaderContainer: { display: 'grid', @@ -81,6 +85,15 @@ const useStyles = makeStyles({ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', + gap: '8px', + flexWrap: 'nowrap', + }, + clearResetTitle: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + minWidth: 0, + flex: '1 1 auto', }, searchInput: { flexGrow: 1, @@ -103,10 +116,11 @@ const useStyles = makeStyles({ }, }); -function ChplFilterPanel() { - const classes = useStyles(); +function ChplFilterPanel({ filterGridMinColWidth }) { + const classes = useStyles({ filterGridMinColWidth }); const [anchor, setAnchor] = useState(null); const [open, setOpen] = useState(false); + const [panelWidth, setPanelWidth] = useState(null); const [activeCategory, setActiveCategory] = useState(null); const [activeCategoryKey, setActiveCategoryKey] = useState(''); const [filters, setFilters] = useState([]); @@ -134,6 +148,7 @@ function ChplFilterPanel() { }); } setAnchor(e.currentTarget.closest('[data-filter-search-bar="true"]') || e.currentTarget); + setPanelWidth((e.currentTarget.closest('[data-filter-search-bar="true"]') || e.currentTarget).getBoundingClientRect().width); setOpen(true); }; @@ -145,7 +160,7 @@ function ChplFilterPanel() { }); } setOpen(false); - setActiveCategoryKey(''); + setPanelWidth(null); }; const handleAction = (action) => { @@ -218,8 +233,6 @@ function ChplFilterPanel() { filterContext.dispatch('toggleDevelopersListingsCriteriaOption', f); }; - const panelWidth = anchor?.closest?.('[data-filter-search-bar="true"]')?.getBoundingClientRect?.().width; - return ( <> @@ -237,18 +250,18 @@ function ChplFilterPanel() { open={open} anchorEl={anchor} onClose={handleClose} - TransitionProps={{ onExited: () => setAnchor(null) }} + TransitionProps={{ onExited: () => { setAnchor(null); setActiveCategoryKey(''); } }} anchorOrigin={{ vertical: 'bottom', - horizontal: 'center', + horizontal: 'right', }} transformOrigin={{ vertical: 'top', - horizontal: 'center', + horizontal: 'right', }} PaperProps={{ style: { - width: panelWidth ? `${panelWidth}px` : 'calc(100vw - 32px)', + width: panelWidth ? `${panelWidth - 284}px` : 'calc(100vw - 316px)', maxWidth: 'calc(100vw - 32px)', alignItems: 'center', borderRadius: '0 0 8px 8px', @@ -296,6 +309,7 @@ function ChplFilterPanel() { key={f.key} color={f === activeCategory ? 'default' : 'primary'} id={`filter-panel-primary-items-${f.key}`} + style={{ whiteSpace: f.wrapText ? 'normal' : 'nowrap' }} variant="outlined" onClick={() => handleCategoryToggle(f)} > @@ -327,7 +341,7 @@ function ChplFilterPanel() { className={classes.clearResetContainer} disableGutters > - + { activeCategory.getFilterDisplay(activeCategory) }
@@ -405,6 +419,16 @@ function ChplFilterPanel() { )}
+
+ +
); @@ -413,4 +437,5 @@ function ChplFilterPanel() { export default ChplFilterPanel; ChplFilterPanel.propTypes = { + filterGridMinColWidth: number.isRequired, }; diff --git a/src/app/components/filter/filter-search-bar.jsx b/src/app/components/filter/filter-search-bar.jsx index e0dde54a00..337304ddf2 100755 --- a/src/app/components/filter/filter-search-bar.jsx +++ b/src/app/components/filter/filter-search-bar.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Box, makeStyles, @@ -6,6 +6,7 @@ import { import { arrayOf, bool, + number, object, string, } from 'prop-types'; @@ -48,19 +49,56 @@ const useStyles = makeStyles({ }, }, }, + sticky: { + position: 'sticky', + top: '100px', + zIndex: 3, + }, + stuck: { + '&::before': { + content: '""', + position: 'absolute', + left: 0, + right: 0, + bottom: '100%', + height: '100px', + background: ({ fadeBackground }) => `linear-gradient(to top, ${fadeBackground} 55%, transparent)`, + pointerEvents: 'none', + }, + }, }); function ChplFilterSearchBar({ + fadeBackground = palette.backgroundPage, + filterGridMinColWidth = 200, hideAdvancedSearch = false, hideSearchTerm = false, placeholder = 'Search by Developer, Product, or CHPL ID...', + sticky = false, toggleMultipleFilters = undefined, }) { const { filters } = useFilterContext(); - const classes = useStyles(); + const classes = useStyles({ fadeBackground }); + const sentinelRef = useRef(null); + const [isStuck, setIsStuck] = useState(false); - return ( -
+ useEffect(() => { + if (!sticky) { return undefined; } + const sentinel = sentinelRef.current; + if (!sentinel) { return undefined; } + const observer = new IntersectionObserver( + ([entry]) => setIsStuck(!entry.isIntersecting), + { rootMargin: '-100px 0px 0px 0px', threshold: 0 }, + ); + observer.observe(sentinel); + return () => observer.disconnect(); + }, [sticky]); + + const searchBar = ( +
{ !hideSearchTerm && ( { !hideAdvancedSearch && ( - + )} { filters.some((f) => f.key === 'quickFilters') && ( @@ -82,13 +120,27 @@ function ChplFilterSearchBar({
); + + if (!sticky) { + return searchBar; + } + + return ( + <> +
+ {searchBar} + + ); } export default ChplFilterSearchBar; ChplFilterSearchBar.propTypes = { + fadeBackground: string, + filterGridMinColWidth: number, hideAdvancedSearch: bool, hideSearchTerm: bool, placeholder: string, + sticky: bool, toggleMultipleFilters: arrayOf(object), }; diff --git a/src/app/components/filter/filters/certification-statuses.jsx b/src/app/components/filter/filters/certification-statuses.jsx index f3ce4c83c9..42d4af26e0 100755 --- a/src/app/components/filter/filters/certification-statuses.jsx +++ b/src/app/components/filter/filters/certification-statuses.jsx @@ -32,13 +32,14 @@ const getCertificationStatusValueEntry = ({ filter, handleFilterToggle }) => fil style={{ display: 'flex', alignItems: 'center', + gap: '4px', }} > - {getStatusIcon({ name: filter.getLongValueDisplay(value) })} - {filter.getLongValueDisplay(value)} + {getStatusIcon({ name: filter.getLongValueDisplay(value) })} + ); }); diff --git a/src/app/components/filter/index.js b/src/app/components/filter/index.js index 26e24ae9c1..2aab1c3614 100755 --- a/src/app/components/filter/index.js +++ b/src/app/components/filter/index.js @@ -3,6 +3,7 @@ import ChplFilterPanel from './filter-panel'; import ChplFilterQuickFilters from './filter-quick-filters'; import ChplFilterSearchBar from './filter-search-bar'; import ChplFilterSearchTerm from './filter-search-term'; +import ChplFilterLayout from './filter-layout'; import { FilterProvider, defaultFilter, @@ -18,6 +19,7 @@ export { ChplFilterQuickFilters, ChplFilterSearchBar, ChplFilterSearchTerm, + ChplFilterLayout, FilterProvider, defaultFilter, getDateDisplay, diff --git a/src/app/components/products/products-view.jsx b/src/app/components/products/products-view.jsx index ae8f39489a..737bffcccd 100755 --- a/src/app/components/products/products-view.jsx +++ b/src/app/components/products/products-view.jsx @@ -11,7 +11,7 @@ import { arrayOf, func } from 'prop-types'; import ChplProductView from './product-view'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -125,19 +125,17 @@ function ChplProductsView({ products = [], dispatch }) { -
- -
-
-
- Search Results: - { displayedProducts.length === 0 + +
+
+ Search Results: + { displayedProducts.length === 0 && ( <> No results found )} - { displayedProducts.length > 0 + { displayedProducts.length > 0 && ( { displayedProducts.length } @@ -146,18 +144,19 @@ function ChplProductsView({ products = [], dispatch }) { { displayedProducts.length === 1 ? '' : 's' } )} +
-
- { displayedProducts - .sort((a, b) => sortProducts(a, b)) - .map((product) => ( - - ))} + { displayedProducts + .sort((a, b) => sortProducts(a, b)) + .map((product) => ( + + ))} + ); diff --git a/src/app/components/surveillance/complaints/complaints-view.jsx b/src/app/components/surveillance/complaints/complaints-view.jsx index fbab7970f4..e49f38bb85 100755 --- a/src/app/components/surveillance/complaints/complaints-view.jsx +++ b/src/app/components/surveillance/complaints/complaints-view.jsx @@ -5,13 +5,6 @@ import { Card, CardContent, CardHeader, - CircularProgress, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableRow, Typography, makeStyles, } from '@material-ui/core'; @@ -25,42 +18,36 @@ import ChplComplaint from './complaint'; import { useFetchComplaints, usePostReportRequest } from 'api/complaints'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; -import { ChplEllipsis, ChplPagination, ChplSortableHeaders } from 'components/util'; +import { + ChplEllipsis, + ChplLoadingCards, + ChplPagination, + ChplSearchResultCard, + ChplSearchResultControls, + ChplSortControls, +} from 'components/util'; import { eventTrack } from 'services/analytics.service'; import { getDisplayDateFormat } from 'services/date-util'; import { useSessionStorage as useStorage } from 'services/storage.service'; import { UserContext, useAnalyticsContext } from 'shared/contexts'; -import { palette, theme, utilStyles } from 'themes'; +import { palette, utilStyles } from 'themes'; const useStyles = makeStyles({ ...utilStyles, - container: { - maxHeight: '64vh', - }, - tableResultsHeaderContainer: { - display: 'grid', - gap: '8px', - margin: '16px 32px', - gridTemplateColumns: '1fr', - alignItems: 'center', - justifyContent: 'space-between', - [theme.breakpoints.up('sm')]: { - gridTemplateColumns: 'auto auto', - }, + card: { + overflow: 'visible', }, resultsContainer: { - display: 'grid', - gap: '8px', - justifyContent: 'start', - gridTemplateColumns: 'auto auto', - alignItems: 'center', + padding: 0, }, - wrap: { - flexFlow: 'wrap', + emptyActions: { + display: 'flex', + gap: '8px', + padding: '16px 0', }, statusIndicatorOpen: { color: palette.active, @@ -113,23 +100,13 @@ function ChplComplaintsView(props) { }, [data, isLoading, isSuccess]); /* eslint object-curly-newline: ["error", { "minProperties": 5, "consistent": true }] */ - const headers = (hasAnyRole(['chpl-onc-acb']) || bonusQuery) ? [ - { property: 'current_status', text: 'Status', sortable: true }, - { property: 'received_date', text: 'Received Date', sortable: true, reverseDefault: true }, - { property: 'acb_complaint_id', text: 'ONC-ACB Complaint ID', sortable: true }, - { property: 'onc_complaint_id', text: 'ONC Complaint ID', sortable: true }, - { property: 'complaint_type', text: 'Complaint Type(s)' }, - { property: 'complainant_type', text: 'Complainant Type', sortable: true }, - { property: 'actions', text: 'Actions', invisible: true }, - ] : [ - { property: 'certification_body', text: 'ONC-ACB', sortable: true }, - { property: 'current_status', text: 'Status', sortable: true }, - { property: 'received_date', text: 'Received Date', sortable: true, reverseDefault: true }, - { property: 'acb_complaint_id', text: 'ONC-ACB Complaint ID', sortable: true }, - { property: 'onc_complaint_id', text: 'ONC Complaint ID', sortable: true }, - { property: 'complaint_type', text: 'Complaint Type(s)' }, - { property: 'complainant_type', text: 'Complainant Type', sortable: true }, - { property: 'actions', text: 'Actions', invisible: true }, + const sortOptions = [ + ...((hasAnyRole(['chpl-onc-acb']) || bonusQuery) ? [] : [{ property: 'certification_body', text: 'ONC-ACB' }]), + { property: 'current_status', text: 'Status' }, + { property: 'received_date', text: 'Received Date', reverseDefault: true }, + { property: 'acb_complaint_id', text: 'ONC-ACB Complaint ID' }, + { property: 'onc_complaint_id', text: 'ONC Complaint ID' }, + { property: 'complainant_type', text: 'Complainant Type' }, ]; const downloadFile = () => { @@ -176,7 +153,7 @@ function ChplComplaintsView(props) { } }; - const handleTableSort = (event, property, orderDirection) => { + const handleSort = (property, orderDirection) => { eventTrack({ ...analytics, event: 'Sort Column', @@ -272,97 +249,100 @@ function ChplComplaintsView(props) { const pageEnd = Math.min((pageNumber + 1) * pageSize, data?.recordCount); return ( - + { bonusQuery && ( )} -
- -
- { isLoading + + { isLoading && ( - + )} - { !isLoading + { !isLoading && ( <> -
-
- Search Results: - { complaints.length === 0 - && ( - <> - No results found - - )} - { complaints.length > 0 - && ( - - {`(${pageStart}-${pageEnd} of ${data?.recordCount} Results)`} - - )} -
+ + { getButtons() } -
+ + { complaints.length === 0 + && ( + + { getButtons() } + + )} { complaints.length > 0 && ( <> - - - - - {complaints - .map((complaint) => ( - - { !hasAnyRole(['chpl-onc-acb']) && !bonusQuery - && ( - {complaint.certificationBody.name} - )} - - - {complaint.closedDate ? 'Closed' : 'Open'} - - - {getDisplayDateFormat(complaint.receivedDate)} - {complaint.acbComplaintId} - - { complaint.oncComplaintId && } - - {complaint.complaintTypes?.map((t) => t.name).join(', ')} - {complaint.complainantType.name} - - - - - ))} - -
-
+ + { complaints.map((complaint) => { + const showAcb = !hasAnyRole(['chpl-onc-acb']) && !bonusQuery; + const primaryGroup = []; + if (showAcb) { + primaryGroup.push({ label: 'ONC-ACB', value: complaint.certificationBody.name }); + } + primaryGroup.push({ + label: 'Status', + value: ( + + {complaint.closedDate ? 'Closed' : 'Open'} + + ), + }); + primaryGroup.push({ label: 'Received Date', value: getDisplayDateFormat(complaint.receivedDate) }); + return ( + ) + : 'N/A', + }, + { label: 'Complaint Type(s)', value: complaint.complaintTypes?.map((t) => t.name).join(', ') || 'N/A' }, + { label: 'Complainant Type', value: complaint.complainantType.name }, + ], + ]} + actions={( + + )} + /> + ); + })} + )} +
); diff --git a/src/app/components/system-maintenance/api-key/api-keys-view.jsx b/src/app/components/system-maintenance/api-key/api-keys-view.jsx index 7d11d555fd..b6c93c0cd8 100755 --- a/src/app/components/system-maintenance/api-key/api-keys-view.jsx +++ b/src/app/components/system-maintenance/api-key/api-keys-view.jsx @@ -85,10 +85,13 @@ function ChplApiKeysView({ dispatch, apiKeys: initialApiKeys }) { { label: 'Email', value: key.email, + flex: 1, + style: { flex: '2 1 320px' }, }, { label: 'API Key', value: key.key, + style: { flex: '2 1 320px' }, }, { label: 'Last Used', diff --git a/src/app/components/system-maintenance/certification-criterion/certification-criteria-view.jsx b/src/app/components/system-maintenance/certification-criterion/certification-criteria-view.jsx index d0ed75d682..d629acbaaa 100755 --- a/src/app/components/system-maintenance/certification-criterion/certification-criteria-view.jsx +++ b/src/app/components/system-maintenance/certification-criterion/certification-criteria-view.jsx @@ -1,21 +1,24 @@ import React, { useEffect, useState } from 'react'; import { Box, - Typography, - makeStyles, } from '@material-ui/core'; import { arrayOf } from 'prop-types'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; -import { ChplLink, ChplSearchResultCard, ChplSortControls } from 'components/util'; +import { + ChplLink, + ChplSearchResultCard, + ChplSearchResultControls, + ChplSortControls, +} from 'components/util'; import { sortComparator } from 'components/util/sortable-headers'; import { getDisplayDateFormat } from 'services/date-util'; import { criterion as criterionPropType } from 'shared/prop-types'; -import { utilStyles } from 'themes'; +import { palette } from 'themes'; const sortOptions = [ { property: 'number', text: 'Number' }, @@ -24,10 +27,6 @@ const sortOptions = [ { property: 'endDay', text: 'End Date' }, ]; -const useStyles = makeStyles({ - ...utilStyles, -}); - const getDisplay = (key) => { switch (key) { case 'additionalSoftware': return 'Additional Software'; @@ -63,7 +62,6 @@ function ChplCertificationCriteriaView({ certificationCriteria: initialCertifica const [order, setOrder] = useState('asc'); const [orderBy, setOrderBy] = useState('number'); const filterContext = useFilterContext(); - const classes = useStyles(); useEffect(() => { setCertificationCriteria(initialCertificationCriteria @@ -96,74 +94,70 @@ function ChplCertificationCriteriaView({ certificationCriteria: initialCertifica <> -
- -
- - - - Search Results - - - {`(${certificationCriteria.length} Result${certificationCriteria.length !== 1 ? 's' : ''})`} - - - + + 0 ? 1 : 0} + pageEnd={certificationCriteria.length} + fadeBackground={palette.white} + > + + + {certificationCriteria + .map((item) => ( + + ) : 'N/A', + }, + { + label: 'Attributes', + value: item.displayAttributes.length > 0 ? item.displayAttributes : 'N/A', + }, + ], + ]} + /> + ))} - - - {certificationCriteria - .map((item) => ( - - ) : 'N/A', - }, - { - label: 'Attributes', - value: item.displayAttributes.length > 0 ? item.displayAttributes : 'N/A', - }, - ], - ]} - /> - ))} - + ); } diff --git a/src/app/components/system-maintenance/certification-criterion/certification-criteria.jsx b/src/app/components/system-maintenance/certification-criterion/certification-criteria.jsx index ba4c88ab56..33658dee6f 100755 --- a/src/app/components/system-maintenance/certification-criterion/certification-criteria.jsx +++ b/src/app/components/system-maintenance/certification-criterion/certification-criteria.jsx @@ -106,7 +106,7 @@ function ChplCertificationCriteria() { filters={filters} storageKey="storageKey-certificationCriteriaManagement" > - + )} /> - + diff --git a/src/app/components/system-maintenance/functionality-tested/functionalities-tested-view.jsx b/src/app/components/system-maintenance/functionality-tested/functionalities-tested-view.jsx index 1e9e2b5d75..d41596fbe8 100755 --- a/src/app/components/system-maintenance/functionality-tested/functionalities-tested-view.jsx +++ b/src/app/components/system-maintenance/functionality-tested/functionalities-tested-view.jsx @@ -3,8 +3,6 @@ import { Box, Button, IconButton, - Typography, - makeStyles, } from '@material-ui/core'; import { arrayOf, func } from 'prop-types'; import AddIcon from '@material-ui/icons/Add'; @@ -14,19 +12,23 @@ import InfoIcon from '@material-ui/icons/Info'; import { useFetchFunctionalitiesTestedActivity } from 'api/activity'; import ChplSystemMaintenanceActivity from 'components/activity/system-maintenance-activity'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; import { - ChplSearchResultCard, ChplSortControls, ChplTooltip, ChplUpdateIndicator, + ChplSearchResultCard, + ChplSearchResultControls, + ChplSortControls, + ChplTooltip, + ChplUpdateIndicator, } from 'components/util'; import { sortComparator } from 'components/util/sortable-headers'; import { sortCriteria } from 'services/criteria.service'; import { getDisplayDateFormat } from 'services/date-util'; import { UserContext } from 'shared/contexts'; import { functionalityTested as functionalityTestedPropType } from 'shared/prop-types'; -import { utilStyles } from 'themes'; +import { palette } from 'themes'; const sortOptions = [ { property: 'value', text: 'Value' }, @@ -37,17 +39,12 @@ const sortOptions = [ { property: 'endDay', text: 'End Date' }, ]; -const useStyles = makeStyles({ - ...utilStyles, -}); - function ChplFunctionalitiesTestedView({ dispatch, functionalitiesTested: initialFunctionalitiesTested }) { const { hasAnyRole } = useContext(UserContext); const [functionalitiesTested, setFunctionalitiesTested] = useState([]); const [order, setOrder] = useState('desc'); const [orderBy, setOrderBy] = useState('value'); const filterContext = useFilterContext(); - const classes = useStyles(); useEffect(() => { setFunctionalitiesTested(initialFunctionalitiesTested @@ -79,20 +76,16 @@ function ChplFunctionalitiesTestedView({ dispatch, functionalitiesTested: initia <> -
- -
- - - - Search Results - - - {`(${functionalitiesTested.length} Result${functionalitiesTested.length !== 1 ? 's' : ''})`} - - - + + 0 ? 1 : 0} + pageEnd={functionalitiesTested.length} + fadeBackground={palette.white} + > )} - - - - {functionalitiesTested - .map((item) => ( - + + + {functionalitiesTested + .map((item) => ( + )} - fieldGroups={[ - [ - { - label: 'Regulatory Text Citation', - value: item.regulatoryTextCitation || 'N/A', - iconButton: ( - - - - - - ), - }, - { - label: 'Rule', - value: item.rule?.name || 'N/A', - }, - { - label: 'Practice Type', - value: item.practiceType?.name || 'N/A', - }, - { - label: 'Applicable Criteria', - value: item.criteriaDisplay || 'N/A', - }, - ], - [ - { - label: 'Start Date', - value: getDisplayDateFormat(item.startDay) || 'N/A', - }, - { - label: 'End Date', - value: getDisplayDateFormat(item.endDay) || 'N/A', - }, - { - label: 'Required Date', - value: getDisplayDateFormat(item.requiredDay) || 'N/A', - }, - { - label: 'Extension End Date', - value: getDisplayDateFormat(item.extensionEndDay) || 'N/A', - }, - ], - ]} - actions={ + fieldGroups={[ + [ + { + label: 'Regulatory Text Citation', + value: item.regulatoryTextCitation || 'N/A', + iconButton: ( + + + + + + ), + }, + { + label: 'Rule', + value: item.rule?.name || 'N/A', + }, + { + label: 'Practice Type', + value: item.practiceType?.name || 'N/A', + }, + { + label: 'Applicable Criteria', + value: item.criteriaDisplay || 'N/A', + }, + ], + [ + { + label: 'Start Date', + value: getDisplayDateFormat(item.startDay) || 'N/A', + }, + { + label: 'End Date', + value: getDisplayDateFormat(item.endDay) || 'N/A', + }, + { + label: 'Required Date', + value: getDisplayDateFormat(item.requiredDay) || 'N/A', + }, + { + label: 'Extension End Date', + value: getDisplayDateFormat(item.extensionEndDay) || 'N/A', + }, + ], + ]} + actions={ hasAnyRole(['chpl-admin', 'chpl-onc']) && ( ) } - /> - ))} - + /> + ))} + + ); } diff --git a/src/app/components/system-maintenance/functionality-tested/functionalities-tested.jsx b/src/app/components/system-maintenance/functionality-tested/functionalities-tested.jsx index 69692a8dc2..5645ce09ca 100755 --- a/src/app/components/system-maintenance/functionality-tested/functionalities-tested.jsx +++ b/src/app/components/system-maintenance/functionality-tested/functionalities-tested.jsx @@ -257,7 +257,7 @@ function ChplFunctionalitiesTested() { filters={filters} storageKey="storageKey-functionalitiesTestedManagement" > - + )} /> - + { (deleteFunctionalityTested.isLoading || postFunctionalityTested.isLoading || putFunctionalityTested.isLoading) && ( diff --git a/src/app/components/system-maintenance/g1g2/g1g2-view.jsx b/src/app/components/system-maintenance/g1g2/g1g2-view.jsx index f729b86e0f..b3ff833032 100755 --- a/src/app/components/system-maintenance/g1g2/g1g2-view.jsx +++ b/src/app/components/system-maintenance/g1g2/g1g2-view.jsx @@ -2,21 +2,24 @@ import React, { useEffect, useState } from 'react'; import { Box, IconButton, - Typography, - makeStyles, } from '@material-ui/core'; import { arrayOf, shape, string } from 'prop-types'; import InfoIcon from '@material-ui/icons/Info'; -import { ChplSearchResultCard, ChplSortControls, ChplTooltip } from 'components/util'; +import { + ChplSearchResultCard, + ChplSearchResultControls, + ChplSortControls, + ChplTooltip, +} from 'components/util'; import { sortComparator } from 'components/util/sortable-headers'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; import { sortCriteria } from 'services/criteria.service'; -import { utilStyles } from 'themes'; +import { palette } from 'themes'; const sortOptions = [ { property: 'abbreviation', text: 'Abbreviation' }, @@ -24,16 +27,11 @@ const sortOptions = [ { property: 'name', text: 'Name' }, ]; -const useStyles = makeStyles({ - ...utilStyles, -}); - function ChplG1g2View({ g1g2: initialG1g2 }) { const [g1g2, setG1g2] = useState([]); const [order, setOrder] = useState('asc'); const [orderBy, setOrderBy] = useState('abbreviation'); const filterContext = useFilterContext(); - const classes = useStyles(); useEffect(() => { setG1g2(initialG1g2 @@ -66,70 +64,68 @@ function ChplG1g2View({ g1g2: initialG1g2 }) { <> -
- -
- - - Search Results - - {`(${g1g2.length} Result${g1g2.length !== 1 ? 's' : ''})`} - - - + + 0 ? 1 : 0} + pageEnd={g1g2.length} + fadeBackground={palette.white} + > + + + { g1g2 + .map((item) => ( + + + + + + ), + }, + { + label: 'Domain', + value: item.domainDisplay || 'N/A', + iconButton: ( + + + + + + ), + }, + { + label: 'Required Test', + value: `${item.removed ? 'Removed | ' : ''}${item.requiredTest || 'N/A'}`, + }, + { + label: 'Applicable Criteria', + value: item.criteriaDisplay || 'N/A', + }, + ], + ]} + /> + ))} - - - { g1g2 - .map((item) => ( - - - - - - ), - }, - { - label: 'Domain', - value: item.domainDisplay || 'N/A', - iconButton: ( - - - - - - ), - }, - { - label: 'Required Test', - value: `${item.removed ? 'Removed | ' : ''}${item.requiredTest || 'N/A'}`, - }, - { - label: 'Applicable Criteria', - value: item.criteriaDisplay || 'N/A', - }, - ], - ]} - /> - ))} - + ); } diff --git a/src/app/components/system-maintenance/g1g2/g1g2.jsx b/src/app/components/system-maintenance/g1g2/g1g2.jsx index cf097b22f6..b9cdf0ef21 100755 --- a/src/app/components/system-maintenance/g1g2/g1g2.jsx +++ b/src/app/components/system-maintenance/g1g2/g1g2.jsx @@ -77,7 +77,7 @@ function ChplG1g2() { filters={filters} storageKey="storageKey-g1g2Management" > - + )} /> - + diff --git a/src/app/components/system-maintenance/optional-standard/optional-standards-view.jsx b/src/app/components/system-maintenance/optional-standard/optional-standards-view.jsx index f43c51eb4d..2147449629 100755 --- a/src/app/components/system-maintenance/optional-standard/optional-standards-view.jsx +++ b/src/app/components/system-maintenance/optional-standard/optional-standards-view.jsx @@ -11,7 +11,7 @@ import InfoIcon from '@material-ui/icons/Info'; import { ChplSearchResultCard, ChplSortControls, ChplTooltip } from 'components/util'; import { sortComparator } from 'components/util/sortable-headers'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -63,58 +63,57 @@ function ChplOptionalStandardsView({ optionalStandards: initialOptionalStandards -
- -
- - - Search Results - - {`(${optionalStandards.length} Result${optionalStandards.length !== 1 ? 's' : ''})`} - + + + + Search Results + + {`(${optionalStandards.length} Result${optionalStandards.length !== 1 ? 's' : ''})`} + + + + + - - + + { optionalStandards + .map((item) => ( + + + + + + ), + }, + { + label: 'Applicable Criteria', + value: item.criteriaDisplay || 'N/A', + }, + ], + ]} + /> + ))} - - - { optionalStandards - .map((item) => ( - - - - - - ), - }, - { - label: 'Applicable Criteria', - value: item.criteriaDisplay || 'N/A', - }, - ], - ]} - /> - ))} - + ); } diff --git a/src/app/components/system-maintenance/standard/standards-view.jsx b/src/app/components/system-maintenance/standard/standards-view.jsx index af99a95016..18d88f82ed 100755 --- a/src/app/components/system-maintenance/standard/standards-view.jsx +++ b/src/app/components/system-maintenance/standard/standards-view.jsx @@ -3,8 +3,6 @@ import { Box, Button, IconButton, - Typography, - makeStyles, } from '@material-ui/core'; import { arrayOf, func } from 'prop-types'; import AddIcon from '@material-ui/icons/Add'; @@ -14,12 +12,13 @@ import InfoIcon from '@material-ui/icons/Info'; import { useFetchStandardsActivity } from 'api/activity'; import ChplSystemMaintenanceActivity from 'components/activity/system-maintenance-activity'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; import { ChplSearchResultCard, + ChplSearchResultControls, ChplSortControls, ChplUpdateIndicator, ChplTooltip, @@ -29,7 +28,7 @@ import { sortCriteria } from 'services/criteria.service'; import { getDisplayDateFormat } from 'services/date-util'; import { UserContext } from 'shared/contexts'; import { standard as standardPropType } from 'shared/prop-types'; -import { utilStyles } from 'themes'; +import { palette } from 'themes'; const sortOptions = [ { property: 'value', text: 'Value' }, @@ -40,21 +39,12 @@ const sortOptions = [ { property: 'endDay', text: 'End Date' }, ]; -const useStyles = makeStyles({ - ...utilStyles, - tableResultsHeaderContainer: { - display: 'flex', - justifyContent: 'flex-end', - }, -}); - function ChplStandardsView({ dispatch, standards: initialStandards }) { const [standards, setStandards] = useState([]); const { hasAnyRole } = useContext(UserContext); const [order, setOrder] = useState('asc'); const [orderBy, setOrderBy] = useState('value'); const filterContext = useFilterContext(); - const classes = useStyles(); useEffect(() => { setStandards(initialStandards @@ -86,18 +76,16 @@ function ChplStandardsView({ dispatch, standards: initialStandards }) { <> -
- -
- - - Search Results - - {`(${standards.length} Result${standards.length !== 1 ? 's' : ''})`} - - - + + 0 ? 1 : 0} + pageEnd={standards.length} + fadeBackground={palette.white} + > )} - - - - { standards - .map((item) => ( - + + + { standards + .map((item) => ( + )} - fieldGroups={[ - [ - { - label: 'Regulatory Text Citation', - value: item.regulatoryTextCitation || 'N/A', - iconButton: ( - - - - - - ), - }, - { - label: 'Rule', - value: item.rule?.name ?? 'N/A', - }, - { - label: 'Group', - value: item.groupName ?? 'N/A', - }, - { - label: 'Applicable Criteria', - value: item.criteriaDisplay || 'N/A', - }, - ], - [ - { - label: 'Start Date', - value: getDisplayDateFormat(item.startDay), - }, - { - label: 'Required Date', - value: getDisplayDateFormat(item.requiredDay), - }, - { - label: 'Extension End Date', - value: getDisplayDateFormat(item.extensionEndDay), - }, - { - label: 'End Date', - value: getDisplayDateFormat(item.endDay), - }, - ], - ]} - actions={ + fieldGroups={[ + [ + { + label: 'Regulatory Text Citation', + value: item.regulatoryTextCitation || 'N/A', + iconButton: ( + + + + + + ), + }, + { + label: 'Rule', + value: item.rule?.name ?? 'N/A', + }, + { + label: 'Group', + value: item.groupName ?? 'N/A', + }, + { + label: 'Applicable Criteria', + value: item.criteriaDisplay || 'N/A', + }, + ], + [ + { + label: 'Start Date', + value: getDisplayDateFormat(item.startDay), + }, + { + label: 'Required Date', + value: getDisplayDateFormat(item.requiredDay), + }, + { + label: 'Extension End Date', + value: getDisplayDateFormat(item.extensionEndDay), + }, + { + label: 'End Date', + value: getDisplayDateFormat(item.endDay), + }, + ], + ]} + actions={ hasAnyRole(['chpl-admin', 'chpl-onc']) && ( ) } - /> - ))} - + /> + ))} + + ); } diff --git a/src/app/components/system-maintenance/standard/standards.jsx b/src/app/components/system-maintenance/standard/standards.jsx index 5b8e603959..7ee58a0247 100755 --- a/src/app/components/system-maintenance/standard/standards.jsx +++ b/src/app/components/system-maintenance/standard/standards.jsx @@ -256,7 +256,7 @@ function ChplStandards() { filters={filters} storageKey="storageKey-standardsManagement" > - + )} /> - + { (deleteStandard.isLoading || postStandard.isLoading || putStandard.isLoading) && ( diff --git a/src/app/components/system-maintenance/svap/svaps-view.jsx b/src/app/components/system-maintenance/svap/svaps-view.jsx index b6059eb792..df22de09e2 100755 --- a/src/app/components/system-maintenance/svap/svaps-view.jsx +++ b/src/app/components/system-maintenance/svap/svaps-view.jsx @@ -14,7 +14,7 @@ import InfoIcon from '@material-ui/icons/Info'; import { useFetchSvapsActivity } from 'api/activity'; import ChplSystemMaintenanceActivity from 'components/activity/system-maintenance-activity'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -76,28 +76,26 @@ function ChplSvapsView({ dispatch, svaps: initialSvaps }) { -
- -
- - - Search Results - - {`(${svaps.length} Result${svaps.length !== 1 ? 's' : ''})`} - - - - - - { hasAnyRole(['chpl-admin', 'chpl-onc']) && ( + + + + Search Results + + {`(${svaps.length} Result${svaps.length !== 1 ? 's' : ''})`} + + + + + + { hasAnyRole(['chpl-admin', 'chpl-onc']) && ( - )} + )} + - - - { svaps - .map((item) => ( - - - - - - ), - }, + + { svaps + .map((item) => ( + + + + + + ), + }, - { - label: 'Replaced', - value: item.replaced ? 'Yes' : 'No', - }, - { - label: 'Applicable Criteria', - value: item.criteriaDisplay || 'N/A', - }, - ], - ]} - actions={ + { + label: 'Replaced', + value: item.replaced ? 'Yes' : 'No', + }, + { + label: 'Applicable Criteria', + value: item.criteriaDisplay || 'N/A', + }, + ], + ]} + actions={ hasAnyRole(['chpl-admin', 'chpl-onc']) && ( ) } - /> - ))} - + /> + ))} + + ); } diff --git a/src/app/components/system-maintenance/test-tool/test-tools-view.jsx b/src/app/components/system-maintenance/test-tool/test-tools-view.jsx index 1e7b361f0c..2e1876cfe7 100755 --- a/src/app/components/system-maintenance/test-tool/test-tools-view.jsx +++ b/src/app/components/system-maintenance/test-tool/test-tools-view.jsx @@ -14,7 +14,7 @@ import InfoIcon from '@material-ui/icons/Info'; import { ChplSearchResultCard, ChplSortControls, ChplTooltip } from 'components/util'; import { sortComparator } from 'components/util/sortable-headers'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -70,24 +70,22 @@ function ChplTestToolsView({ dispatch, testTools: initialTestTools }) { -
- -
- - - Search Results - - {`(${testTools.length} Result${testTools.length !== 1 ? 's' : ''})`} - - - - - { hasAnyRole(['chpl-admin', 'chpl-onc']) && ( + + + + Search Results + + {`(${testTools.length} Result${testTools.length !== 1 ? 's' : ''})`} + + + + + { hasAnyRole(['chpl-admin', 'chpl-onc']) && ( - )} + )} + - - - { testTools - .map((item) => ( - - - - - + + { testTools + .map((item) => ( + + + + + )} - fieldGroups={[ - [ - { - label: 'Start Date', - value: getDisplayDateFormat(item.startDay), - }, - { - label: 'End Date', - value: getDisplayDateFormat(item.endDay), - }, - { - label: 'Applicable Criteria', - value: item.criteriaDisplay || 'N/A', - }, - ], - ]} - actions={ + fieldGroups={[ + [ + { + label: 'Start Date', + value: getDisplayDateFormat(item.startDay), + }, + { + label: 'End Date', + value: getDisplayDateFormat(item.endDay), + }, + { + label: 'Applicable Criteria', + value: item.criteriaDisplay || 'N/A', + }, + ], + ]} + actions={ hasAnyRole(['chpl-admin', 'chpl-onc']) && ( ) } - /> - ))} - + /> + ))} + + ); } diff --git a/src/app/components/util/chpl-page-body.jsx b/src/app/components/util/chpl-page-body.jsx index 7bd6e7cb7c..1f11525ece 100644 --- a/src/app/components/util/chpl-page-body.jsx +++ b/src/app/components/util/chpl-page-body.jsx @@ -10,9 +10,14 @@ import { palette, theme } from 'themes'; const useStyles = makeStyles({ container: { - padding: theme.spacing(8), - backgroundColor: palette.backgroundPage, + padding: theme.spacing(4), minHeight: 'calc(100vh - 150px)', + backgroundColor: palette.backgroundPage, + backgroundImage: `radial-gradient(${'#d6d4cf'} 0.5px, transparent 0.25px)`, + backgroundSize: '18px 18px', + [theme.breakpoints.up('md')]: { + padding: theme.spacing(8), + }, }, }); diff --git a/src/app/components/util/chpl-search-result-controls.jsx b/src/app/components/util/chpl-search-result-controls.jsx index 556ad87d8b..b4bb60e2ba 100644 --- a/src/app/components/util/chpl-search-result-controls.jsx +++ b/src/app/components/util/chpl-search-result-controls.jsx @@ -4,7 +4,7 @@ import { Typography, makeStyles, } from '@material-ui/core'; -import { node, number } from 'prop-types'; +import { bool, node, number, string } from 'prop-types'; import { palette, theme } from 'themes'; @@ -14,14 +14,30 @@ const useStyles = makeStyles({ flexDirection: 'column', gap: '4px', marginBottom: '16px', + position: ({ sticky }) => (sticky ? 'sticky' : 'static'), + top: '190px', + zIndex: 2, alignItems: 'flex-start', justifyContent: 'space-between', flexWrap: 'wrap', padding: '16px 32px', backgroundColor: palette.white, borderRadius: '0px 0px 8px 8px', - borderTop: `1px solid ${palette.greyBorder}`, - boxShadow: `0px 2px 4px -1px ${theme.palette.grey[300]}, 0px 4px 5px 0px ${theme.palette.grey[300]}, 0px 1px 10px 0px ${theme.palette.grey[300]}`, + borderRight: `1px solid ${palette.divider}`, + borderBottom: `1px solid ${palette.divider}`, + borderLeft: `1px solid ${palette.divider}`, + boxShadow: `0px 6px 8px -4px ${theme.palette.grey[300]}`, + '&::before': { + content: ({ sticky }) => (sticky ? '""' : 'none'), + position: 'absolute', + left: '-1px', + right: '-1px', + bottom: '100%', + height: '90px', + background: ({ fadeBackground }) => `linear-gradient(to top, ${fadeBackground} 40%, transparent)`, + pointerEvents: 'none', + zIndex: 1, + }, [theme.breakpoints.down('sm')]: { gap: '12px', padding: '16px', @@ -41,13 +57,13 @@ const useStyles = makeStyles({ display: 'flex', alignItems: 'center', gap: '2px', + flexWrap: ({ wrapActions }) => (wrapActions ? 'wrap' : 'nowrap'), [theme.breakpoints.down('sm')]: { flexWrap: 'wrap', gap: '8px', width: '100%', }, [theme.breakpoints.up('md')]: { - flexWrap: 'nowrap', gap: '2px', width: 'auto', '& > *': { @@ -63,8 +79,11 @@ function ChplSearchResultControls({ pageStart, pageEnd, children = undefined, + fadeBackground = palette.backgroundPage, + sticky = true, + wrapActions = false, }) { - const classes = useStyles(); + const classes = useStyles({ fadeBackground, sticky, wrapActions }); return (
@@ -100,4 +119,7 @@ ChplSearchResultControls.propTypes = { pageStart: number.isRequired, pageEnd: number.isRequired, children: node, + fadeBackground: string, + sticky: bool, + wrapActions: bool, }; diff --git a/src/app/components/util/chpl-sort-controls.jsx b/src/app/components/util/chpl-sort-controls.jsx index c18bd2a4d3..7a5b59b530 100644 --- a/src/app/components/util/chpl-sort-controls.jsx +++ b/src/app/components/util/chpl-sort-controls.jsx @@ -3,6 +3,7 @@ import { Box, Button, ButtonGroup, + Card, Menu, MenuItem, makeStyles, @@ -19,11 +20,15 @@ import ArrowUpwardIcon from '@material-ui/icons/ArrowUpward'; import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'; import SortIcon from '@material-ui/icons/Sort'; -import { theme } from 'themes'; +import { theme, palette } from 'themes'; const useStyles = makeStyles({ container: { marginRight: '16px', + display: 'flex', + border: `1px solid ${palette.primaryBorder}`, + borderRadius: theme.shape.borderRadius, + alignItems: 'center', [theme.breakpoints.down('sm')]: { width: '100%', marginRight: 0, @@ -40,6 +45,10 @@ const useStyles = makeStyles({ justifyContent: 'flex-start', }, }, + directionButton: { + borderLeft: `1px solid ${palette.primaryBorder}`, + borderRadius: 0, + }, }); function ChplSortControls({ @@ -83,8 +92,8 @@ function ChplSortControls({ }; return ( - - + + + { hasAnyRole(['chpl-admin', 'chpl-onc']) && ( -
- - { hasAnyRole(['chpl-admin', 'chpl-onc']) - && ( - - )} -
+ )} -
+ { developers.length > 0 - && ( - <> - - - - - { developers - .map((item) => ( - - - - - - - - { item.code } - - - { item.oncAcbDisplay } - - - ))} - -
-
- + { developers.map((item) => ( + + ), + }, + { + label: 'Developer Code', + value: item.code, + }, + { + label: 'ONC-ACB for Active Listings', + value: item.oncAcbDisplay, + }, + ]]} /> - - )} + ))} + + + )} )} +
); diff --git a/src/app/pages/organizations/developers/developers.jsx b/src/app/pages/organizations/developers/developers.jsx index 37c4c5e09a..81e046e760 100755 --- a/src/app/pages/organizations/developers/developers.jsx +++ b/src/app/pages/organizations/developers/developers.jsx @@ -73,6 +73,7 @@ function ChplDevelopersPage() { ...certificationBodies, key: 'acbsForActiveListings', display: 'Has active Listings with ONC-ACB', + wrapText: true, values, })); setFilters((f) => f @@ -81,6 +82,7 @@ function ChplDevelopersPage() { ...certificationBodies, key: 'acbsForAllListings', display: 'Has any Listings with ONC-ACB', + wrapText: true, values, })); }, [acbQuery.data, acbQuery.isLoading, acbQuery.isSuccess]); diff --git a/src/app/pages/reports/activity/activity-view.jsx b/src/app/pages/reports/activity/activity-view.jsx index 1f5071ddda..40186eb640 100755 --- a/src/app/pages/reports/activity/activity-view.jsx +++ b/src/app/pages/reports/activity/activity-view.jsx @@ -1,27 +1,21 @@ import React, { useEffect, useState } from 'react'; -import { - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableRow, - Typography, - makeStyles, -} from '@material-ui/core'; +import { Box } from '@material-ui/core'; import ChplActivityDetails from './activity-details'; import { useFetchActivity } from 'api/questionable-activity'; -import { ChplLink, +import { + ChplLink, + ChplLoadingCards, + ChplPagination, ChplPageBody, ChplPageHeader, - ChplPagination, - ChplLoadingTable, - ChplSortableHeaders } - from 'components/util'; + ChplSearchResultCard, + ChplSearchResultControls, + ChplSortControls, +} from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -29,65 +23,6 @@ import { eventTrack } from 'services/analytics.service'; import { getDisplayDateFormat } from 'services/date-util'; import { useSessionStorage as useStorage } from 'services/storage.service'; import { useAnalyticsContext } from 'shared/contexts'; -import { palette, theme } from 'themes'; - -const useStyles = makeStyles({ - linkWrap: { - overflowWrap: 'anywhere', - }, - pageContent: { - display: 'grid', - gridTemplateRows: '3fr 1fr', - }, - searchContainer: { - backgroundColor: palette.grey, - padding: '16px 32px', - display: 'grid', - gridTemplateColumns: '1fr', - gap: '16px', - alignItems: 'center', - [theme.breakpoints.up('md')]: { - gridTemplateColumns: 'auto 10fr auto', - }, - }, - stickyColumn: { - position: 'sticky', - left: 0, - boxShadow: 'rgba(149, 157, 165, 0.1) 0px 4px 8px', - backgroundColor: '#ffffff', - overflowWrap: 'anywhere', - [theme.breakpoints.up('sm')]: { - minWidth: '275px', - }, - }, - tableContainer: { - overflowWrap: 'normal', - border: '.5px solid #c2c6ca', - marginTop: '16px', - width: 'auto', - }, - tableResultsHeaderContainer: { - display: 'grid', - gap: '8px', - marginTop: '16px', - gridTemplateColumns: '1fr', - alignItems: 'center', - justifyContent: 'space-between', - [theme.breakpoints.up('sm')]: { - gridTemplateColumns: 'auto auto', - }, - }, - resultsContainer: { - display: 'grid', - gap: '8px', - justifyContent: 'start', - gridTemplateColumns: 'auto auto', - alignItems: 'center', - }, - wrap: { - flexFlow: 'wrap', - }, -}); function ChplActivityView() { const storageKey = 'storageKey-activity'; @@ -98,7 +33,6 @@ function ChplActivityView() { const [pageSize, setPageSize] = useStorage(`${storageKey}-pageSize`, 25); const [sortDescending, setSortDescending] = useStorage(`${storageKey}-sortDescending`, true); const [recordCount, setRecordCount] = useState(0); - const classes = useStyles(); const filterContext = useFilterContext(); const { data, isError, isLoading } = useFetchActivity({ @@ -128,14 +62,8 @@ function ChplActivityView() { }, [data?.recordCount, pageNumber, data?.results?.length]); /* eslint object-curly-newline: ["error", { "minProperties": 5, "consistent": true }] */ - const headers = [ - { property: 'username', text: 'User' }, - { property: 'concept', text: 'Concept' }, - { property: 'activity_date', text: 'Activity Date', sortable: true }, - { text: 'Description' }, - { text: 'Reason' }, - { text: 'CHPL Link' }, - { text: 'Actions', invisible: true }, + const sortOptions = [ + { property: 'activity_date', text: 'Activity Date', reverseDefault: true }, ]; const getLink = (activity) => { @@ -215,7 +143,7 @@ function ChplActivityView() { } }; - const handleTableSort = (event, property, orderDirection) => { + const handleSort = (property, orderDirection) => { eventTrack({ ...analytics, event: 'Sort Column', @@ -233,66 +161,58 @@ function ChplActivityView() { - - { isLoading - && ( - - )} - { !isLoading + + { isLoading && ()} + { !isLoading && ( <> -
-
- Search Results: - { activities.length === 0 - && ( - - No results found - - )} - { activities.length > 0 - && ( - - {`(${pageStart}-${pageEnd} of ${recordCount} Results)`} - - )} -
-
+ + + { activities.length > 0 && ( <> - - - + { activities.map((item) => ( + } /> - - { activities - .map((item) => ( - - { item.username } - { item.concept } - { getDisplayDateFormat(item.activityDate) } - { item.description } - { item.reason } - { getLink(item) } - - - - - ))} - -
-
+ ))} + )} +
); diff --git a/src/app/pages/reports/questionable-activity/questionable-activity-view.jsx b/src/app/pages/reports/questionable-activity/questionable-activity-view.jsx index f2d8e52d09..a3fb306d71 100755 --- a/src/app/pages/reports/questionable-activity/questionable-activity-view.jsx +++ b/src/app/pages/reports/questionable-activity/questionable-activity-view.jsx @@ -1,14 +1,8 @@ import React, { useEffect, useState } from 'react'; import { + Box, Button, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableRow, Typography, - makeStyles, } from '@material-ui/core'; import CloudDownloadOutlinedIcon from '@material-ui/icons/CloudDownloadOutlined'; import { useSelector } from 'react-redux'; @@ -17,14 +11,16 @@ import { useFetchQuestionableActivity } from 'api/questionable-activity'; import ChplQuestionableActivityDetails from 'components/activity/questionable-activity-details'; import { ChplLink, + ChplLoadingCards, + ChplPagination, ChplPageBody, ChplPageHeader, - ChplPagination, - ChplLoadingTable, - ChplSortableHeaders, + ChplSearchResultCard, + ChplSearchResultControls, + ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -33,54 +29,6 @@ import { getDisplayDateFormat } from 'services/date-util'; import { eventTrack } from 'services/analytics.service'; import { useSessionStorage as useStorage } from 'services/storage.service'; import { useAnalyticsContext } from 'shared/contexts'; -import { theme } from 'themes'; - -const useStyles = makeStyles({ - linkWrap: { - overflowWrap: 'anywhere', - }, - pageContent: { - display: 'grid', - gridTemplateRows: '3fr 1fr', - }, - stickyColumn: { - position: 'sticky', - left: 0, - boxShadow: 'rgba(149, 157, 165, 0.1) 0px 4px 8px', - backgroundColor: '#ffffff', - overflowWrap: 'anywhere', - [theme.breakpoints.up('sm')]: { - minWidth: '275px', - }, - }, - tableContainer: { - overflowWrap: 'normal', - border: '.5px solid #c2c6ca', - marginTop: '16px', - width: 'auto', - }, - tableResultsHeaderContainer: { - display: 'grid', - gap: '8px', - marginTop: '16px', - gridTemplateColumns: '1fr', - alignItems: 'center', - justifyContent: 'space-between', - [theme.breakpoints.up('sm')]: { - gridTemplateColumns: 'auto auto', - }, - }, - resultsContainer: { - display: 'grid', - gap: '8px', - justifyContent: 'start', - gridTemplateColumns: 'auto auto', - alignItems: 'center', - }, - wrap: { - flexFlow: 'wrap', - }, -}); function ChplQuestionableActivityView() { const apiKey = useSelector((state) => state.browserInfo.apiKey); @@ -95,7 +43,6 @@ function ChplQuestionableActivityView() { const [pageSize, setPageSize] = useStorage(`${storageKey}-pageSize`, 25); const [sortDescending, setSortDescending] = useStorage(`${storageKey}-sortDescending`, true); const [recordCount, setRecordCount] = useState(0); - const classes = useStyles(); const filterContext = useFilterContext(); const { data, isError, isLoading } = useFetchQuestionableActivity({ @@ -129,15 +76,12 @@ function ChplQuestionableActivityView() { }, [API, authService]); /* eslint object-curly-newline: ["error", { "minProperties": 5, "consistent": true }] */ - const headers = [ - { property: 'developer', text: 'Developer', sortable: true }, - { property: 'product', text: 'Product', sortable: true }, - { property: 'version', text: 'Version', sortable: true }, - { property: 'chpl_product_number', text: 'CHPL ID', sortable: true }, - { text: 'Activity' }, - { property: 'activity_date', text: 'Activity Date', sortable: true }, - { text: 'Reason' }, - { text: 'Actions', invisible: true }, + const sortOptions = [ + { property: 'developer', text: 'Developer' }, + { property: 'product', text: 'Product' }, + { property: 'version', text: 'Version' }, + { property: 'chpl_product_number', text: 'CHPL ID' }, + { property: 'activity_date', text: 'Activity Date', reverseDefault: true }, ]; const handleClick = () => { @@ -148,7 +92,7 @@ function ChplQuestionableActivityView() { window.open(`${downloadLink}&${filterContext.queryString()}`); }; - const handleTableSort = (event, property, orderDirection) => { + const handleSort = (property, orderDirection) => { eventTrack({ ...analytics, event: 'Sort Column', @@ -172,31 +116,23 @@ function ChplQuestionableActivityView() { )} /> - - - { isLoading - && ( - - )} - { !isLoading + + + { isLoading && ()} + { !isLoading && ( <> -
-
- Search Results: - { activities.length === 0 - && ( - - No results found - - )} - { activities.length > 0 - && ( - - {`(${pageStart}-${pageEnd} of ${recordCount} Results)`} - - )} -
+ + { activities.length > 0 && (
+ { activities.length > 0 && ( <> - - - - - { activities - .map((item) => ( - - - { item.developerId - && ( - - )} - - {item.productName} - {item.versionName} - - { item.listingId - && ( + + { activities.map((item) => ( + + ) + : item.developerName || 'N/A'} + fieldGroups={[ + [ + { label: 'Product', value: item.productName }, + { label: 'Version', value: item.versionName }, + { + label: 'CHPL ID', + style: { flex: '2 1 320px' }, + value: item.listingId + ? ( - )} - - {item.triggerName} - { getDisplayDateFormat(item.activityDate) } - - { item.reason - && ( - - {item.reason} - - )} - { item.certificationStatusChangeReason - && ( - - {item.certificationStatusChangeReason} - - )} - - - - - - ))} - -
-
+ ) + : item.chplProductNumber, + }, + ], + [ + { label: 'Activity', value: item.triggerName }, + { label: 'Activity Date', value: getDisplayDateFormat(item.activityDate) }, + { + label: 'Reason', + style: { flex: '2 1 320px' }, + value: item.reason || item.certificationStatusChangeReason || 'N/A', + }, + ], + ]} + actions={} + /> + ))} + )} +
); diff --git a/src/app/pages/search/api-documentation/api-documentation-view.jsx b/src/app/pages/search/api-documentation/api-documentation-view.jsx index 4f8d76829a..b53a76a340 100755 --- a/src/app/pages/search/api-documentation/api-documentation-view.jsx +++ b/src/app/pages/search/api-documentation/api-documentation-view.jsx @@ -20,7 +20,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -170,10 +170,10 @@ function ChplApiDocumentationSearchView({ displayCriteria }) { )} /> - - - { isLoading && ()} - { !isLoading + + + { isLoading && ()} + { !isLoading && ( <> @@ -282,6 +286,7 @@ function ChplApiDocumentationSearchView({ displayCriteria }) { }, { label: 'Mandatory Disclosures URL', + style: { fontSize: '0.85em' }, value: item.mandatoryDisclosures ? ( )} + ); diff --git a/src/app/pages/search/banned-developers/banned-developers-view.jsx b/src/app/pages/search/banned-developers/banned-developers-view.jsx index a1401db6e7..32f5dc6ad4 100755 --- a/src/app/pages/search/banned-developers/banned-developers-view.jsx +++ b/src/app/pages/search/banned-developers/banned-developers-view.jsx @@ -18,7 +18,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -122,17 +122,18 @@ function ChplBannedDevelopersSearchView() { inline /> . - + )} /> - - {isLoading && ()} - {!isLoading + + {isLoading && ()} + {!isLoading && ( <> )} + ); diff --git a/src/app/pages/search/corrective-action/corrective-action-view.jsx b/src/app/pages/search/corrective-action/corrective-action-view.jsx index 34e350bf10..bdbb93792e 100755 --- a/src/app/pages/search/corrective-action/corrective-action-view.jsx +++ b/src/app/pages/search/corrective-action/corrective-action-view.jsx @@ -19,7 +19,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -139,10 +139,10 @@ function ChplCorrectiveActionSearchView() { { directReviewsAvailable && ( <> - - - { isLoading && ()} - { !isLoading + + + { isLoading && ()} + { !isLoading && ( <> )} + )} diff --git a/src/app/pages/search/decertified-products/decertified-products-view.jsx b/src/app/pages/search/decertified-products/decertified-products-view.jsx index 1774622761..ff209550d3 100755 --- a/src/app/pages/search/decertified-products/decertified-products-view.jsx +++ b/src/app/pages/search/decertified-products/decertified-products-view.jsx @@ -19,7 +19,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -124,10 +124,10 @@ function ChplDecertifiedProductsSearchView() { )} /> - - - { isLoading && ()} - { !isLoading + + + { isLoading && ()} + { !isLoading && ( <> )} + ); diff --git a/src/app/pages/search/decision-support-interventions/decision-support-interventions-view.jsx b/src/app/pages/search/decision-support-interventions/decision-support-interventions-view.jsx index 531b2bae64..6fd1bf91b2 100644 --- a/src/app/pages/search/decision-support-interventions/decision-support-interventions-view.jsx +++ b/src/app/pages/search/decision-support-interventions/decision-support-interventions-view.jsx @@ -19,7 +19,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -110,10 +110,10 @@ function ChplDecisionSupportInterventionsSearchView() { )} /> - - - { isLoading && ()} - { !isLoading + + + { isLoading && ()} + { !isLoading && ( <> )} + ); diff --git a/src/app/pages/search/inactive-certificates/inactive-certificates-view.jsx b/src/app/pages/search/inactive-certificates/inactive-certificates-view.jsx index 4084783299..b94d0774b5 100755 --- a/src/app/pages/search/inactive-certificates/inactive-certificates-view.jsx +++ b/src/app/pages/search/inactive-certificates/inactive-certificates-view.jsx @@ -19,7 +19,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -127,10 +127,10 @@ function ChplInactiveCertificatesSearchView() { )} /> - - - { isLoading && ()} - { !isLoading + + + { isLoading && ()} + { !isLoading && ( <> )} + ); diff --git a/src/app/pages/search/listings/listings-view.jsx b/src/app/pages/search/listings/listings-view.jsx index 91bf06107f..9a308ee5d3 100755 --- a/src/app/pages/search/listings/listings-view.jsx +++ b/src/app/pages/search/listings/listings-view.jsx @@ -24,7 +24,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -138,10 +138,10 @@ function ChplListingsView() { subtitle="Please note that only active and suspended listings are shown by default. Use the Certification Status filter to display retired, withdrawn, or terminated listings." /> - - - { isLoading && ()} - { !isLoading + + + { isLoading && ()} + { !isLoading && ( <> )} + ); diff --git a/src/app/pages/search/real-world-testing/real-world-testing-view.jsx b/src/app/pages/search/real-world-testing/real-world-testing-view.jsx index 8eb44f7933..448aea6f88 100755 --- a/src/app/pages/search/real-world-testing/real-world-testing-view.jsx +++ b/src/app/pages/search/real-world-testing/real-world-testing-view.jsx @@ -19,7 +19,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -306,10 +306,10 @@ function ChplRealWorldTestingSearchView() { )} /> - - - { isLoading && ()} - { !isLoading + + + { isLoading && ()} + { !isLoading && ( <> )} + ); diff --git a/src/app/pages/search/sed/sed-view.jsx b/src/app/pages/search/sed/sed-view.jsx index 95da2d9b2b..933e250d33 100755 --- a/src/app/pages/search/sed/sed-view.jsx +++ b/src/app/pages/search/sed/sed-view.jsx @@ -21,7 +21,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -124,10 +124,10 @@ function ChplSedSearchView() { )} /> - - - { isLoading && ()} - { !isLoading + + + { isLoading && ()} + { !isLoading && ( <> )} + ); diff --git a/src/app/pages/search/svap/svap-view.jsx b/src/app/pages/search/svap/svap-view.jsx index d8a13c3940..d7d15ca944 100755 --- a/src/app/pages/search/svap/svap-view.jsx +++ b/src/app/pages/search/svap/svap-view.jsx @@ -21,7 +21,7 @@ import { ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -232,10 +232,10 @@ function ChplSvapSearchView() { )} /> - - - {isLoading && ()} - {!isLoading + + + {isLoading && ()} + {!isLoading && ( <> )} + ); diff --git a/src/app/pages/subscriptions/manage-subscriptions-view.jsx b/src/app/pages/subscriptions/manage-subscriptions-view.jsx index e6ebdbf705..a690943265 100755 --- a/src/app/pages/subscriptions/manage-subscriptions-view.jsx +++ b/src/app/pages/subscriptions/manage-subscriptions-view.jsx @@ -4,10 +4,8 @@ import { Button, Card, CardHeader, - CircularProgress, List, ListItem, - Typography, makeStyles, } from '@material-ui/core'; import { shape, string } from 'prop-types'; @@ -17,12 +15,14 @@ import { NotificationsOutlined, SubscriptionsOutlined } from '@material-ui/icons import { useFetchAllSubscriptions, usePostGetDeliveredNotifications } from 'api/subscriptions'; import { ChplLink, + ChplLoadingCards, ChplPagination, ChplSearchResultCard, + ChplSearchResultControls, ChplSortControls, } from 'components/util'; import { - ChplFilterChips, + ChplFilterLayout, ChplFilterSearchBar, useFilterContext, } from 'components/filter'; @@ -38,6 +38,9 @@ const sortOptions = [ ]; const useStyles = makeStyles({ + card: { + overflow: 'visible', + }, linkWrap: { overflowWrap: 'anywhere', }, @@ -84,14 +87,7 @@ const useStyles = makeStyles({ }, }, resultsContainer: { - display: 'grid', - gap: '8px', - justifyContent: 'start', - gridTemplateColumns: 'auto auto', - alignItems: 'center', - }, - wrap: { - flexFlow: 'wrap', + padding: '0 32px', }, listContainer: { fontSize: 'smaller', @@ -173,7 +169,7 @@ function ChplManageSubscriptionsView({ analytics }) { const pageEnd = Math.min((pageNumber + 1) * pageSize, recordCount); return ( - +
-
- -
- { isLoading - && ( - - )} - { !isLoading + + { isLoading && ()} + { !isLoading && ( <> -
-
- Search Results: - { subscriptions.length === 0 - && ( - - No results found - - )} - { subscriptions.length > 0 - && ( - - {`(${pageStart}-${pageEnd} of ${recordCount} Results)`} - - )} -
+ { subscriptions.length > 0 && ( )} -
+ { subscriptions.length > 0 && ( <> - + { subscriptions.map((item) => ( )} +
);