The Discharge Without Delay app is a Dataverse for Teams canvas app. A demo of the functionality is at https://youtu.be/m_1GWgwSOpg but in general it is a database of patient information for patients admitted to acute hospital. New patients are created on the first screen, which shows a filterable and sortable list of all patients in the system. The Edit icon can be used to open a patient record. The patient record is spread across several tabs for each step in the discharge information-gathering process. There are notifications sent to various Teams channels for the acute and district teams when key data points are saved for a patient (e.g. PDD date is set).
There are 5 related Dataverse tables used by the app:
- Patient: the primary table for all patient records, with 75+ fields. Several of the fields are internal choice fields.
- District: table of district names (which must match the Teams channel names), their related Teams channel IDs, and links to the Teams tab where the app is deployed for that channel. These are used by Power Automate to post adapative cards to the correct channel that contain links to the app for easy access.
- Hospital: table of hospital names, their related Teams channel IDs, and links to the Teams tab where the app is deployed for that channel. These are used by Power Automate to post adapative cards to the correct channel that contain links to the app for easy access.
- Ward: table of ward to hospital name mappings
- UserPreference: table storing the last district, hospital, and ward filters applied by each user of the app so that the last filter applied can be persisted across sessions
- DWD Audit: table that records each save of each patient record to provide an audit trail of who made which changes, and also to be used for detailed reporting in PowerBI.
There are 8 Power Automate flows used by the app:
- New patient: When a new patient record is created in the Patients table, this flow fetches the District, Hospital, and ward information from the relevant tables, posts an adaptive card to the Teams channel for the district specified in the record,1 and updates the patient record with a link to that adaptive card post to serve as the primary conversation thread in Teams about that patient's progress to discharge.
- PDD Changed: When a user changes the PDD field for a patient, the varPDDchanged variable in the app is set to true, and this flow is then triggered when the user saves the record. The flow uses the CHI number it's sent by the app to look up the patient record, get the district for that patient, and then post an adaptive card in the Teams channel for that district updating the community team that the acute team has set this date.
- DMT Changed: Same as above, but the card is posted to the General channel (or whichever channel is being used by the acute team).
- Viable Date Changed: Same as above, but the card is posted to the General channel (or whichever channel is being used by the acute team).
- Save Audit for Patient Edit: Each time a patient record is saved, this flow is run which uses the CHI number to look up the newly changed record, the relevant hospital, ward, and district names, and the identity of the user making the change. It then saves all of this to a new row in the DWD Audit table.2
- ExportPDFtoSharepoint: Triggered by the export screen in the app, it takes the patient name, CHI number, and PDF file generated by the app and creates a new PDF file in the location configured in the final step.
- Delete patient record: Deletes ALL rows in the Patients table matching the CHI number of the record the user has confirmed to delete. Note that there should only be one patient record for any given CHI number in the system at any time (unless users aren't consistently deleting records on discharge).
- Daily check, DHD: This may still be packaged with the solution but is no longer used.
There is a fair amount of logic contained in the scripts in the Discharge Coordination PowerApp. Below is a description of the major UI components and important logic scripts in them.
The architecture makes use of "containers" to provide a responsive design that expands/shrinks for different screen sizes.
The App object only has the OnStart parameter configured to:
- Set several variables
- Create a record in UserPreferences for the current user if there isn't one
There are four screens in the app:
- list: The home screen showing a list of all patients in the Patients table
- editpatientform: The edit screen for a patient record
- export: A screen collating all the fields for a patient record into one view for export to PDF
- newpatient: A separate screen for creating a new patient record with the minimum required fields
List Screen:
- OnVisible sets the varNextAction variable to 0. varNextAction is used to filter the list for who has the next action.
- List_topbar container has icons that change the sorting variables with OnSelect. The Icon parameter also changes based on the sorting variables. The width of the icons are based on the width of the header titles in the container below. The New Patient button simply navigates to the newpatient screen OnSelect.
- List_header container is mostly headings with Width params set to the same fractions of the parent.width as their data items in the list_container below. The District drop down has an important script in the OnChange param which filters the patientlist gallery and sets the user preferences for district, hospital, and ward based on the current selections.
- List_container just contains the patientlist gallery. Its Items param has the important sort and filter script which uses the three drop downs and varNextAction to filter the list. If varNextAction = 1 and atBat = true, the list will only show records for whom the community is now responsible for the next action. If varNextAction = 1 and atBat = false, it will only show records that are for acute's action. The atbat_label and Decision_label text params use emoticons to show the symbols. The PDD_label, discharge_label, and Pathway_label Text and Color params have conditional statements that change the text and color based on record values. The edit_icon creates a new collection variable (varCurrentPatient) and adds the patient name and CHI to it. This is to collect changes for the audit. It then navigates to the edit screen. The chat_icon navigates to the URL for the Teams conversations created by the New Patient flow.
- List_footer container has two dropdowns similar to the district dropdown with an identical OnChange script, but for Hospital and Ward selections.
Edit Patient Form Screen:
- OnVisible turns off the "spinner" control, hides any "explainer" popup, and sets the selected tab to 1 (though the latter isn't working??). It also creates a collection variable (TabVars) that configures all of the tabs on the screen, including the tab names, hover text, the height of the container for the fields when that tab is selected, and a variable for whether any changes have been made on that tab.
- There are four groups of objects (cancel_confirm, spinner, delete_confirm, and explainer) that are shown/hidden based on the variables in their Visible property. The confirm_delete button is what runs the actual DeletePatientRecord flow, and is only enabled if the confirm_check box is checked.
- Screen Container contains four primary containers:
- Header: icon_cancel will navigate back to the list IF the user confirms, icon_delete will show the delete_confirm group, icon_export navigates to the export screen, icon1 navigates to the URL for the Teams conversations created by the New Patient flow, and icon_save which turns on the spinner while the rest of the script runs: the Patch function which saves all the changes in the varCurrentPatient collection to the first record matching the current record's CHI number (NOTE the importance of only having one record with a given CHI number, which should be the case if they're deleting patients on discharge), the SaveAudit... flow, and flows for any of the three important changes (PDD, etc.). It then turns off the spinner group and navigates to the list.
- FormTabRow gallery: gets items (tabs) from TabVars. The FormTab button in the gallery sets two variables from OnSelect: the selected tab, and the height of the MainFormContainer. The text, Tooltip, and Fill params are set by conditional statements.
- PatientNameCHI: contains the patient name, CHI (view only), and the tbAtBat switch, which indicates the team currently on point to provide data. These are a separate container so that they're always visible at the top, no matter what tab is selected. If you select the tbPatientName field, and check the OnChange param, you will see the pattern that's repeated for most of the other fields on this screen: UpdateIf(varCurrentPatient, varFormMode="edit", {Name: tbPatientName.Value});Patch(TabVars,LookUp(TabVars,ID=varTabSelected),{Changed:true}). This updates the in-memory varCurrentPatient collection (if varFormMode=edit, which it always will) with a name-value pair (in this case, "Name: tbPatientName.Value"), and then updates the Changed field in the TabVars collection to true for the currently selected tab. This has the effect of changing the current tab red and storing the changed data in memory.
- MainFormContainer: contains the IntakeFields container, which contains two more containers: Labels and Fields. This is for dynamic layout purposes.
- Labels: each label has a Visible param that shows the conditions by which it will be visible (e.g. tab 4 is selected and the "Falls" checkbox is checked). Labels with the "?" in the text will also have an OnSelect param that turns on the "explainer" group and sets the text of the label in that group. This serves as a popup explanation for those fields.
- Fields: each fiels also has a Visible param with conditions by which it's visible. They all will also have OnChange param scripts that work like the tbPatientName explained above.
- Labels and fields can easily be changed to collect different data points. You may want to change the underlying column names in the Dataverse tables as well.
Export Screen:
- There are two groups of objects (spinner_1 and Group1) that are shown/hidden based on other actions. Group1 contains the PdfViewer object that renders the PDF generated by the script on the export button.
- ScreenContainer1 contains 3 containers for layout purposes.
- HeaderContainer3_1 has a cancel icon and Icon_export_1 which has the primary function of the screen: a script which turns on the spinner group, kicks off the ExportPDF flow, then turns on the PDF group and sets the PDF for the viewer object to the PDF that the flow just created. Note that the "ExpandContainers:true" item in the JSON is important to capture all of the fields that aren't visible. Finally, it turns off the spinner group.
- ExportContainer is used to collect and format all of the content that's exported into the PDF.
New Patient Screen:
- A relatively simple screen with controls corresponding to the fields required to create a new record. The icon_save_new object in the header uses the Patch function in the OnSelect param to save the values in all the fields. It also checks for a duplicate CHI and throws an error message if one already exists for the entered CHI.
- The OnVisible property for the screen resets most of the fields from any previous new entries.
As a Dataverse for Teams app, there are limits to the size of the database. Again, the assumption is that no more than 500 records would be in the system at any given time. The app can be shared with any number of Microsoft Teams, assuming the underlying Dataverse table permissions are adjusted. This would allow you to create teams for each acute hospital. However, all users of a single instance of the app will see all records in the app.
That said, it is my recommendation that if this app is to scale to the board and especially to a multi-board level, it should be upgraded to a full Dataverse architecture and the audit feature addressed through more native auditing capabilities.
- This app as of now has NOT been fully developed to meet accessibility standards (tool tips, Tab indexes, etc.) and so should not be considered production-ready per Microsoft standards. There are 205 alerts in the Accessibility checker to address.
- The 13 Delegation warnings in the list screen are known. It's not anticipated that there will be any more than 500 patients in the system at any one time, so these formulas are at low risk of causing any issues. If that assumption changes, the delegation limit will need to be increased or the formulas refactored.
Footnotes
-
All the adaptive cards in the flows include references in the "text" elements to items in the Patient record in Dataverse. All the "text" elements can be edited as you see fit, or you can replace the entire card with a new design you can create with https://www.adaptivecards.io/designer/ Note also there is a URL element that includes a link to a logo file that you will need to replace, or remove the entire "Image" item in the first "Column". ↩
-
This could be optimised to save only those fields changed by the user, but I couldn't get this to work. As a result, this file may grow large and need to be cleansed with old rows archived elsewhere periodically. ↩