Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
694f79f
WEB-4454 abstract EditPatientDialog for use in both patientData and D…
henry-tp Feb 25, 2026
0b3f183
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Feb 26, 2026
8f384e6
WEB-4454 move editPatientDialog to redux
henry-tp Feb 26, 2026
4832430
WEB-4454 add edit patient modal
henry-tp Feb 26, 2026
52ccced
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Feb 26, 2026
5109540
WEB-4454 reset editPatientDialog on close
henry-tp Feb 26, 2026
2b7ea09
WEB-4454 add cache tags
henry-tp Feb 26, 2026
3bdcdc5
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Feb 26, 2026
3b2184e
WEB-4454 rename CACHE_TAGS to tagTypes
henry-tp Feb 26, 2026
18dedf8
WEB-4454 initial import of Bring Data modal
henry-tp Feb 26, 2026
b18d8c8
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Feb 27, 2026
96670aa
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Feb 27, 2026
4938bf4
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Mar 2, 2026
63bc48b
WEB-4454 rename concrete implementations based on dashboard name
henry-tp Mar 2, 2026
0d3a6cb
WEB-4454 add safeguard for undefined patient in Data Connections modal
henry-tp Mar 3, 2026
d564a0a
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Mar 3, 2026
22d95bb
WEB-4454 use "Controller" suffix for concrete implementation name
henry-tp Mar 3, 2026
044e270
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Mar 4, 2026
07cd44f
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Mar 6, 2026
b9f1c30
Merge branch 'WEB-4454-dashboard' into WEB-4454-kebab
henry-tp Mar 17, 2026
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
8 changes: 5 additions & 3 deletions app/components/datasources/DataConnectionsModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ export const DataConnectionsModal = (props) => {
dataSources,
} : patient;

const [showPatientEmailModal, setShowPatientEmailModal] = useState(false);
const [showPatientEmailModal, setShowPatientEmailModal] = useState(false);
const [processingEmailUpdate, setProcessingEmailUpdate] = useState(false);
const [patientEmailFormContext, setPatientEmailFormContext] = useState();
const dispatch = useDispatch();

const fetchPatientDetails = useCallback(() => {
dispatch(actions.async.fetchPatientFromClinic(api, selectedClinicId, patient.id));
}, [dispatch, patient.id, selectedClinicId])
dispatch(actions.async.fetchPatientFromClinic(api, selectedClinicId, patient?.id));
}, [dispatch, patient?.id, selectedClinicId]);

// Pull the patient on load to ensure the most recent dexcom connection state is made available
useEffect(() => {
Expand Down Expand Up @@ -136,6 +136,8 @@ export const DataConnectionsModal = (props) => {
? t('Learn more.')
: t('Learn more here.');

if (!patient) return null;

return (
<>
<Dialog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,26 @@ const PATIENT_FORM_SEARCH_DEBOUNCE_MS = 600;
const EditPatientDialog = ({
api,
trackMetric,
clinicPatient,
isOpen,
onClose = noop,
onEditSuccess = noop,
}) => {
const { t } = useTranslation();
const dispatch = useDispatch();

const selectedClinicId = useSelector((state) => state.blip.selectedClinicId);
const currentPatientInViewId = useSelector(state => state.blip.currentPatientInViewId);
const clinicPatient = useSelector(state => selectClinicPatient(state));
const clinic = useSelector(state => state.blip.clinics?.[selectedClinicId]);
const patientId = clinicPatient?.id;

const mrnSettings = useMemo(() => clinic?.mrnSettings ?? {}, [clinic?.mrnSettings]);
const existingMRNs = useSelector(state => state.blip.clinicMRNsForPatientFormValidation)?.filter(mrn => mrn !== clinicPatient?.mrn) || [];

const onUpdateSuccess = () => {
if (isOpen) onClose();

dispatch(actions.worker.dataWorkerRemoveDataRequest(null, currentPatientInViewId));
onEditSuccess();
dispatch(actions.worker.dataWorkerRemoveDataRequest(null, patientId));
};

const updatingClinicPatient = useUpdatingClinicPatientWorkingState({ onUpdateSuccess });
Expand All @@ -82,7 +84,7 @@ const EditPatientDialog = ({
setPatientFormContext({ ...formikContext });
};

if (!currentPatientInViewId || !selectedClinicId) return null;
if (!patientId || !selectedClinicId) return null;

return (
<Dialog
Expand Down
3 changes: 2 additions & 1 deletion app/components/navpatientheader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import DemographicInfo from './DemographicInfo';
import PatientMenuOptions from './MenuOptions/Patient';
import ClinicianMenuOptions from './MenuOptions/Clinician';
import UploadLaunchOverlay from '../../components/uploadlaunchoverlay';
import EditPatientDialog from './EditPatientDialog';
import EditPatientDialog from '../modals/EditPatientDialog';

import { isClinicianAccount } from '../../core/personutils';
import { breakpoints } from '../../themes/baseTheme';
Expand Down Expand Up @@ -101,6 +101,7 @@ const NavPatientHeader = ({ api, trackMetric, patient, clinicPatient, user, perm
<EditPatientDialog
api={api}
trackMetric={trackMetric}
clinicPatient={clinicPatient}
isOpen={isEditPatientModalOpen}
onClose={() => setIsEditPatientModalOpen(false)}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import DataConnectionsModal from '../../../components/datasources/DataConnectionsModal';
import { closeModals } from './deviceIssuesSlice';

const DataConnectionsModalController = ({ patients }) => {
const dispatch = useDispatch();

const dataConnectionsModal = useSelector(state => state.blip.deviceIssues.dataConnectionsModal);
const { patientId, isOpen } = dataConnectionsModal;

const patient = patients.find(patient => patient.id === patientId);

const handleClose = () => dispatch(closeModals());

return (
<DataConnectionsModal
open={isOpen}
patient={patient}
onClose={handleClose}
/>
);
};

export default DataConnectionsModalController;
18 changes: 15 additions & 3 deletions app/pages/clinicworkspace/DeviceIssues/DeviceIssues.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Table from '../../../components/elements/Table';
import { Flex, Text } from 'theme-ui';

import FilterByCategory from './FilterByCategory';
import EditPatientDialogController from './EditPatientDialogController';
import DataConnectionsModalController from './DataConnectionsModalController';
import PaginationControls from '../components/PaginationControls';

import { setOffset, resetDeviceIssuesState } from './deviceIssuesSlice';
Expand All @@ -15,7 +17,7 @@ import useTableColumns from './useTableColumns';

const LIMIT = 12;

const DeviceIssues = () => {
const DeviceIssues = ({ api, trackMetric }) => {
const { t } = useTranslation();
const dispatch = useDispatch();

Expand All @@ -39,7 +41,7 @@ const DeviceIssues = () => {

if (!data) return null;

const tableData = data?.data || [];
const patients = data?.data || [];

return (
<>
Expand All @@ -60,7 +62,7 @@ const DeviceIssues = () => {
variant="condensed"
label="deviceIssuesPatientsTable"
columns={columns}
data={tableData}
data={patients}
// sx={tableStyle}
// onSort={handleSortChange}
// order={sort?.substring(0, 1) === '+' ? 'asc' : 'desc'}
Expand All @@ -77,6 +79,16 @@ const DeviceIssues = () => {
onOffsetChange={handleChangeOffset}
/>
</Flex>

<EditPatientDialogController
api={api}
trackMetric={trackMetric}
patients={patients}
/>

<DataConnectionsModalController
patients={patients}
/>
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { closeModals } from './deviceIssuesSlice';
import { RTKQueryApi } from '../../../redux/api/baseApi';
import EditPatientDialog from '../../../components/modals/EditPatientDialog';
import { tagTypes } from './deviceIssuesApi';

const { DEVICE_ISSUES_PATIENTS } = tagTypes;

const trackMetric = () => {};

const EditPatientDialogController = ({ api, patients }) => {
const dispatch = useDispatch();
const editPatientDialog = useSelector(state => state.blip.deviceIssues.editPatientDialog);

const clinicPatient = patients.find(patient => patient.id === editPatientDialog.patientId);

const handleCloseModal = () => {
dispatch(closeModals());
};

const handleEditSuccess = () => {
dispatch(RTKQueryApi.util.invalidateTags([DEVICE_ISSUES_PATIENTS]));
};

return (
<>
<EditPatientDialog
api={api}
trackMetric={trackMetric}
clinicPatient={clinicPatient}
isOpen={editPatientDialog.isOpen}
onClose={handleCloseModal}
onEditSuccess={handleEditSuccess}
/>
</>
);
};

export default EditPatientDialogController;
63 changes: 63 additions & 0 deletions app/pages/clinicworkspace/DeviceIssues/MoreMenuCell.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import { useTranslation } from 'react-i18next';

import {
setEditPatientDialogIsOpen,
setEditPatientDialogPatientId,
setDataConnectionsModalIsOpen,
setDataConnectionsModalPatientId,
} from './deviceIssuesSlice';

import PopoverMenu from '../../../components/elements/PopoverMenu';
import EditIcon from '@material-ui/icons/EditRounded';
import DataInIcon from '../../../core/icons/DataInIcon.svg'
import VisibilityOffIcon from '@material-ui/icons/VisibilityOff';
import VisibilityIcon from '@material-ui/icons/Visibility';

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

const handleOpenEditPatientDialog = () => {
dispatch(setEditPatientDialogIsOpen(true));
dispatch(setEditPatientDialogPatientId(patient.id));
};

const handleOpenDataConnectionsModal = () => {
dispatch(setDataConnectionsModalIsOpen(true));
dispatch(setDataConnectionsModalPatientId(patient.id));
};

return (
<PopoverMenu
id={`action-menu-${patient?.id}`}
items={[{
icon: EditIcon,
iconLabel: t('Edit Patient Details'),
iconPosition: 'left',
id: `edit-${patient?.id}`,
variant: 'actionListItem',
onClick: (_popupState) => {
_popupState.close();
handleOpenEditPatientDialog();
},
text: t('Edit Patient Details'),
}, {
iconSrc: DataInIcon,
iconLabel: t('Bring Data into Tidepool'),
iconPosition: 'left',
id: `edit-data-connections-${patient?.id}`,
variant: 'actionListItem',
onClick: (_popupState) => {
_popupState.close();
handleOpenDataConnectionsModal();
},
text: t('Bring Data into Tidepool'),
}]}
sx={{ position: 'relative', left: '-2px' }}
/>
);
};

export default MoreMenuCell;
13 changes: 12 additions & 1 deletion app/pages/clinicworkspace/DeviceIssues/deviceIssuesApi.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { RTKQueryApi } from '../../../redux/api/baseApi';

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

const { DEVICE_ISSUES_PATIENTS } = tagTypes;

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

const deviceIssuesApi = RTKQueryApi.injectEndpoints({
endpoints: (builder) => ({
getDeviceIssuesPatients: builder.query({
Expand All @@ -9,8 +19,9 @@ const deviceIssuesApi = RTKQueryApi.injectEndpoints({
params: { offset, category, limit },
};
},
providesTags: [DEVICE_ISSUES_PATIENTS],
}),
}),
});

export const { useGetDeviceIssuesPatientsQuery } = deviceIssuesApi;
export const { useGetDeviceIssuesPatientsQuery } = deviceIssuesApi;
36 changes: 35 additions & 1 deletion app/pages/clinicworkspace/DeviceIssues/deviceIssuesSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import { CATEGORY } from './FilterByCategory';
const initialState = {
category: CATEGORY.DEFAULT,
offset: 0,
editPatientDialog: {
patientId: null,
isOpen: false,
},
dataConnectionsModal: {
patientId: null,
isOpen: false,
},
};

const deviceIssuesSlice = createSlice({
Expand All @@ -17,9 +25,35 @@ const deviceIssuesSlice = createSlice({
setOffset: (state, action) => {
state.offset = action.payload;
},
setEditPatientDialogPatientId: (state, action) => {
state.editPatientDialog.patientId = action.payload;
},
setEditPatientDialogIsOpen: (state, action) => {
state.editPatientDialog.isOpen = action.payload;
},
setDataConnectionsModalPatientId: (state, action) => {
state.dataConnectionsModal.patientId = action.payload;
},
setDataConnectionsModalIsOpen: (state, action) => {
state.dataConnectionsModal.isOpen = action.payload;
},
closeModals: (state) => {
state.editPatientDialog = initialState.editPatientDialog;
state.dataConnectionsModal = initialState.dataConnectionsModal;
},
resetDeviceIssuesState: () => initialState,
},
});

export const { setCategory, setOffset, resetDeviceIssuesState } = deviceIssuesSlice.actions;
export const {
setCategory,
setOffset,
setEditPatientDialogPatientId,
setEditPatientDialogIsOpen,
setDataConnectionsModalPatientId,
setDataConnectionsModalIsOpen,
closeModals,
resetDeviceIssuesState,
} = deviceIssuesSlice.actions;

export default deviceIssuesSlice.reducer;
2 changes: 2 additions & 0 deletions app/pages/clinicworkspace/DeviceIssues/useTableColumns.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';

import PatientCell from './PatientCell';
import TagListCell from '../components/TagListCell';
import MoreMenuCell from './MoreMenuCell';

const useTableColumns = () => {
const { t } = useTranslation();
Expand Down Expand Up @@ -50,6 +51,7 @@ const useTableColumns = () => {
title: t(''),
field: '',
align: 'left',
render: patient => <MoreMenuCell patient={patient} />,
}, // More
].filter(column => !!column);
}, [showTags]);
Expand Down