Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions app/pages/clinicworkspace/ConnectionIssues/ConnectionIssues.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Trans } from 'react-i18next';
import React, { useEffect, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { Trans, useTranslation } from 'react-i18next';
import { colors as vizColors } from '@tidepool/viz';
import Table from '../../../components/elements/Table';
import { Flex, Text } from 'theme-ui';

import AppliedFiltersList from './filters/AppliedFiltersList';
import FilterByCategory from './filters/FilterByCategory';
import FilterByTags from './filters/FilterByTags';
import FilterBySites from './filters/FilterBySites';
import PaginationController from './PaginationController';
import EmptyContentNode from './EmptyNodeContent';

import { resetConnectionIssuesState } from './connectionIssuesSlice';
import { useGetConnectionIssuesPatientsQuery } from './connectionIssuesApi';
import useTableColumns from './useTableColumns';
import useConnectionIssuesPatients from './useConnectionIssuesPatients';

const LIMIT = 12;
const tableContainerProps = { sx: { containerType: 'inline-size' } };

const ConnectionIssues = () => {
const { t } = useTranslation();
const dispatch = useDispatch();

const selectedClinicId = useSelector(state => state.blip.selectedClinicId);
const category = useSelector(state => state.blip.connectionIssues.category);
const offset = useSelector(state => state.blip.connectionIssues.offset);
const { data } = useConnectionIssuesPatients();

const columns = useTableColumns();

const { data } = useGetConnectionIssuesPatientsQuery(
{ clinicId: selectedClinicId, offset, category, limit: LIMIT },
{ skip: !selectedClinicId }
);

// reset state on dismount
useEffect(() => {
return () => dispatch(resetConnectionIssuesState());
}, []);

const emptyContentNode = useMemo(() => <EmptyContentNode />, []);

if (!data) return null;

const patients = data?.data || [];
Expand All @@ -49,17 +49,25 @@ const ConnectionIssues = () => {
</Trans>
</Flex>

<Flex id="connection-issues-filters" mb={3} sx={{ gap: 2, alignItems: 'center', flexWrap: 'wrap' }}>
<Text sx={{ fontSize: 0, color: 'grays.4' }}>{t('Filter By')}</Text>
<FilterByTags />
<FilterBySites />
</Flex>

<Flex mb={3} sx={{ justifyContent: 'center' }}>
<FilterByCategory />
</Flex>

<AppliedFiltersList patientCount={total} />
<Table
id="deviceIssuesPatientsTable"
variant="condensed"
label="deviceIssuesPatientsTable"
columns={columns}
data={patients}
// emptyContentNode={}
containerProps={tableContainerProps}
emptyContentNode={emptyContentNode}
/>

<PaginationController total={total} />
Expand Down
62 changes: 62 additions & 0 deletions app/pages/clinicworkspace/ConnectionIssues/EmptyNodeContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useTranslation } from 'react-i18next';
import colorPalette from '../../../themes/colorPalette';
import { Flex, Text } from 'theme-ui';
import { resetConnectionIssuesFilters } from './connectionIssuesFiltersSlice';
import { setOffset } from './connectionIssuesSlice';
import ClearFilterButtons, { PATIENT_QUERY_STATE } from '../components/ClearFilterButtons';
import noop from 'lodash/noop';
import without from 'lodash/without';

const EmptyContentNode = () => {
const { t } = useTranslation();
const dispatch = useDispatch();

const { patientTags, clinicSites } = useSelector(state => state.blip.connectionIssuesFilters);

const activeFiltersCount = without([
patientTags?.length,
clinicSites?.length,
], null, 0, undefined).length;

const hasActiveFilters = activeFiltersCount > 0;

const handleResetFilters = () => {
dispatch(resetConnectionIssuesFilters());
dispatch(setOffset(0));
};

const emptyContentCopy = hasActiveFilters
? t('There are no patients with the current filter(s)')
: t('There are no results to show');

const patientQueryState = hasActiveFilters ? PATIENT_QUERY_STATE.FILTER_ONLY : PATIENT_QUERY_STATE.NONE;

return (
<Flex data-testid="connection-issues-empty-content" sx={{
backgroundColor: colorPalette.primary.bluePrimary00,
justifyContent: 'center',
alignItems: 'center',
minHeight: '90px',
flexDirection: 'column',
gap: 2,
marginBottom: 4,
borderBottom: '1px solid #D1D6E1',
}}>
<Text className="table-empty-text" sx={{ fontWeight: 'medium' }}>
{emptyContentCopy}
</Text>

{ hasActiveFilters &&
<ClearFilterButtons
patientQueryState={patientQueryState}
onResetFilters={handleResetFilters}
onClearSearch={noop}
/>
}
</Flex>
);
};

export default EmptyContentNode;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Grid, Flex } from 'theme-ui';
import { colors as vizColors } from '@tidepool/viz';
import { setOffset } from './connectionIssuesSlice';

import { LIMIT } from './connectionIssuesApi';
import { LIMIT } from './useConnectionIssuesPatients';

const PaginationController = ({ total }) => {
const dispatch = useDispatch();
Expand Down
44 changes: 34 additions & 10 deletions app/pages/clinicworkspace/ConnectionIssues/connectionIssuesApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,49 @@ const getConnectionIssuesParam = (category) => {
}
};

export const LIMIT = 12;
export const buildGetConnectionIssuesPatientsParams = (offset, limit, category, tags = [], sites = []) => {
const deviceIssues = getConnectionIssuesParam(category);
const omitHiddenDevicesIssues = category !== CATEGORY.HIDDEN;

const formattedTags = tags?.length > 0 ? tags.join(',') : undefined;
const formattedSites = sites?.length > 0 ? sites.join(',') : undefined;

return {
offset,
limit,
deviceIssues,
omitHiddenDevicesIssues,
tags: formattedTags,
sites: formattedSites,
};
};

export const tagTypes = {
CONNECTION_ISSUES_PATIENTS: 'CONNECTION_ISSUES_PATIENTS',
};

const { CONNECTION_ISSUES_PATIENTS } = tagTypes;

RTKQueryApi.enhanceEndpoints({
addTagTypes: [CONNECTION_ISSUES_PATIENTS],
});

const connectionIssuesApi = RTKQueryApi.injectEndpoints({
endpoints: (builder) => ({
getConnectionIssuesPatients: builder.query({
query: ({ clinicId, offset, category, limit }) => {
const deviceIssues = getConnectionIssuesParam(category);
const omitHiddenDevicesIssues = category !== CATEGORY.HIDDEN;
query: ({ clinicId, offset, category, limit, tags, sites }) => {
const params = buildGetConnectionIssuesPatientsParams(offset, limit, category, tags, sites);

return {
url: `/clinics/${clinicId}/patients`,
params: {
offset,
limit,
deviceIssues,
omitHiddenDevicesIssues,
},
params,
};
},
transformResponse: (response, _meta, arg) => ({
...response,
category: arg.category,
}),
providesTags: [CONNECTION_ISSUES_PATIENTS],
}),
}),
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createSlice } from '@reduxjs/toolkit';
import { SELECT_CLINIC_SUCCESS } from '../../../redux/constants/actionTypes';

const getInitialState = () => ({
patientTags: [],
clinicSites: [],
});

const connectionIssuesFiltersSlice = createSlice({
name: 'connectionIssuesFilters',
initialState: getInitialState(),
reducers: {
setConnectionIssuesFilters: (_state, action) => action.payload || getInitialState(),
setPatientTagsFilter: (state, action) => {
state.patientTags = action.payload;
},
setClinicSitesFilter: (state, action) => {
state.clinicSites = action.payload;
},
resetConnectionIssuesFilters: () => getInitialState(),
},
extraReducers: (builder) => {
builder.addCase(SELECT_CLINIC_SUCCESS, (_state, action) => {
return action.payload?.connectionIssuesFilters || getInitialState();
});
},
});

export const {
setConnectionIssuesFilters,
setClinicSitesFilter,
setPatientTagsFilter,
resetConnectionIssuesFilters,
} = connectionIssuesFiltersSlice.actions;

export default connectionIssuesFiltersSlice.reducer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import without from 'lodash/without';
import noop from 'lodash/noop';

import ActiveFiltersTray from '../../components/filters/ActiveFiltersTray';
import ClearFilterButtons, { PATIENT_QUERY_STATE } from '../../components/ClearFilterButtons';
import { Box } from 'theme-ui';
import { setClinicSitesFilter, setPatientTagsFilter } from '../connectionIssuesFiltersSlice';
import { setOffset } from '../connectionIssuesSlice';

export const getPatientQueryState = (patientTags, clinicSites) => {
const hasFiltersActive = clinicSites?.length > 0 || patientTags?.length > 0;

if (hasFiltersActive) return PATIENT_QUERY_STATE.FILTER_ONLY;

return PATIENT_QUERY_STATE.NONE;
};

const AppliedFiltersList = ({ patientCount = 0 }) => {
const dispatch = useDispatch();

const { clinicSites, patientTags } = useSelector(state => state.blip.connectionIssuesFilters);

const activeFilters = { clinicSites, patientTags };

const handleResetFilters = () => {
dispatch(setPatientTagsFilter([]));
dispatch(setClinicSitesFilter([]));
dispatch(setOffset(0));
};

const handleRemoveFilter = (filterKey, value) => {
switch (filterKey) {
case 'patientTags': {
const updatedTags = without(patientTags, value);
dispatch(setPatientTagsFilter(updatedTags));
dispatch(setOffset(0));
break;
}

case 'clinicSites': {
const updatedSites = without(clinicSites, value);
dispatch(setClinicSitesFilter(updatedSites));
dispatch(setOffset(0));
break;
}
}
};

const patientQueryState = getPatientQueryState(patientTags, clinicSites);

return (
<ActiveFiltersTray
patientCount={patientCount}
hasSearchActive={false} // No patient search in Connection Issues
filters={activeFilters}
onRemoveFilter={handleRemoveFilter}
rightContent={
<Box sx={{ fontSize: 0 }}>
<ClearFilterButtons
patientQueryState={patientQueryState}
onClearSearch={noop}
onResetFilters={handleResetFilters}
/>
</Box>
}
/>
);
};

AppliedFiltersList.propTypes = {
patientCount: PropTypes.number,
};

export default AppliedFiltersList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { setOffset } from '../connectionIssuesSlice';
import { setClinicSitesFilter } from '../connectionIssuesFiltersSlice';
import SiteFilterDropdown from '../../components/filters/SiteFilterDropdown';

const FilterBySites = () => {
const dispatch = useDispatch();
const { clinicSites } = useSelector(state => state.blip.connectionIssuesFilters);

const handleChange = (clinicSites) => {
dispatch(setClinicSitesFilter(clinicSites));
dispatch(setOffset(0));
};

return <SiteFilterDropdown onChange={handleChange} clinicSites={clinicSites} />;
};

export default FilterBySites;
19 changes: 19 additions & 0 deletions app/pages/clinicworkspace/ConnectionIssues/filters/FilterByTags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { setOffset } from '../connectionIssuesSlice';
import { setPatientTagsFilter } from '../connectionIssuesFiltersSlice';
import TagFilterDropdown from '../../components/filters/TagFilterDropdown';

const FilterByTags = () => {
const dispatch = useDispatch();
const { patientTags } = useSelector(state => state.blip.connectionIssuesFilters);

const handleChange = (tags) => {
dispatch(setPatientTagsFilter(tags));
dispatch(setOffset(0));
};

return <TagFilterDropdown onChange={handleChange} patientTags={patientTags} />;
};

export default FilterByTags;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { useGetConnectionIssuesPatientsQuery } from './connectionIssuesApi';

export const LIMIT = 12;

const useConnectionIssuesPatients = () => {
const selectedClinicId = useSelector(state => state.blip.selectedClinicId);
const category = useSelector(state => state.blip.connectionIssues.category);
const offset = useSelector(state => state.blip.connectionIssues.offset);
const patientTags = useSelector(state => state.blip.connectionIssuesFilters.patientTags);
const clinicSites = useSelector(state => state.blip.connectionIssuesFilters.clinicSites);

return useGetConnectionIssuesPatientsQuery(
{ clinicId: selectedClinicId, offset, category, limit: LIMIT, tags: patientTags, sites: clinicSites },
{ skip: !selectedClinicId }
);
};

export default useConnectionIssuesPatients;
Loading