Proposal to change O3 workspace groups

Currently, the workspace system allows for multiple workspaces to be opened simultaneously (with one active and the rest minimized.) Minimized workspaces are useful in that it allows for unsaved changes to forms in the workspace while the user navigates to other workspaces or to different pages (say, different tabs within the patient chart) to look at data. The workspace system has 2 ways to prevent unsaved changes in opened workspaces from getting lost:

  1. The workspace system has a concept of a workspace group; all opened workspaces must belong to the same active workspace group. If there is an attempt to open another workspace of a different workspace group, then the user is presented with a warning modal to discard any unsaved changes to the currently opened workspaces. There are 2 ways to define workspace group(s) for a given workspace:

    a. When defining a workspace in routes.json file, we can optionally specify the workspace group(s) that it belongs to.

    b. Conversely, we can also declare a workspace group in a routes.json file and list of workspaces that are members of the workspace group.

  2. The workspace system has a concept of a contextKey. From here: “The context key is a string that appears in the URL, which defines the pages on which workspaces are valid. If the URL changes in a way such that it no longer contains the context key, then all workspaces will be closed”. The contextKey is passed in to the <WorkspaceContainer> component. When the user attempts to naviagtes to a URL that no longer matches the context keys, they will also get a warning modal to discard unsaved changes.

Thus, we get “unsaved changes” prompt when we attempt to either naviate to a different URL or try to open workspace from another workspace group. However, I think there are navigations that are not necessarily URL changes that should also prompt for unsaved changes. Here’s my use case:

  • For RDE, the vitals / visit notes / forms / order basket workspaces have a concept of the “Visit context”. For example, when creating a visit note, we set the visit context to the visit we want to add the visit note to. (For point of care, we set the visit context to the current active visit; for RDE, we select a past visit instead.) We are also starting to add ability to edit existing encounters. In particular, the visits table or the encounters table in the patient chart allows you to view / edit existing encounters across different visits, without navigating to differen tpages. When editing an existing encounter, the visit context should automatically switch to the visit associated with the encounter. However, we should only allow that to happen when there is no pending changes in existing opened workspaces.

Current, when we launch a workspace to have it opened in a specified workspace group, we do this:

launchWorkspaceGroup('ward-patient', {
  workspaceToLaunch: {
      name: 'ward-patient-workspace',
  },
});

I propose that we solve the issue with the unsaved changes prompts when switching visit contexts by having a context key pass in to `launchWorkspaceGroup, something like this:

launchWorkspaceGroup('patient-chart-forms', {
  workspaceToLaunch: {
      name: 'visit-note-workspace',
  },
  contextKey: <visitUuid>,
  URLKey: `patient/${patientUuid}`
});

Then, the workspace system shows the unsaved changes prompt if we attempt to do launchWorkspaceGroup either:

  • naviagte to a URL that does not match the URLKey
  • call launchWorkspaceGroup with a different group name, or
  • call launchWorkspaceGroup with the same group name, with different contextKey.

Let me know what you think. Thanks!

2 Likes

I thought about this more and created this ticket for it

O3-4705 - Add support for workspace context and workspace group context

Basically, I want to introduce the concept of workspace context and workspace group context. I’m leaving the URL-sensitive contextKey in the <WorkspaceContainer> as is for now.

A more concrete proposal now that I have played around with workspaces more:

  • Add workspace context and workspace group context.

    Currently, we prompt for unsaved changes when we attempt to either:

    1. open a new workspace group that’s different from the current one, or
    2. open a new workspace of type X, when there is already another workspace of type X opened.

    As mentioned in the above post, this is insufficient for some scenarios. We introduce the concept of workspace context and workspace group context. When launching a workspace group, we may specify a workspace group context. When we launch a workspace, we may specify both a workspace context and a workspace group context. When we attempt to launch a workspace, we additionally check for the workspace and workspace group contexts, and prompt for unsaved changes if either changes.

  • Lessen the distinction between “workspace” and “workspace type”.

    When declaring a workspace in a routes.json file, we can specify a "type" attribute. The attribute identifies the workspace action menu icon corresponding to the workspace, so that the icon gets highlighted when the workspace is opened. For example, in patient chart, VisitNoteActionButton is an ActionMenuButton with type='visit-note', and it gets highlighted when we open the visit-notes-form-workspace workspace, because in its declaration, its "type": "visit-note" matches the type of the button.

    Currently, the workspace system imposes a constraint that all open workspace must have both unique workspace names and unique workspace type. The distinction between “workspace name” and “workspace type” is primarily useful when we have “child workspaces”, i.e. a workspace launched from another workspace. For example, in the order basket, the add-drug-order and add-lab-order forms are child workspaces opened from the order-basket workspace, and they all declared with "type": "order". However, “child workspaces” is not an actual supported feature, and we emulate it in a hacky way: when navigating from the main order basket to the drug order form, we do so by closing the order-basket workspace and immediately opening the add-drug-order workspace.

    With the introduction of contexts, this could get unwieldy. I propose that we do away with “child workspaces”, and stop declaring multiple workspaces with the same "type". Instead, we have just one workspace for a workspace "type", and we let it handle its own navigation state for different “pages” (ex: main order basket and drug order form) within the workspace.

  • Only show workspace action menu when we do launchWorkspaceGroup.

    Currently, there are 2 slightly different ways to define the icons that shows up in the workspace action menu. The icons are extensions in the extension slot action-menu-${X}-items-slot:

    • if a workspace group is opened, then X is the workspace group name
    • else X is the name of the current module that’s mounting the <WorkspaceContainer>.

    I propose that we have the workspace action menu icons be completely defined by the workspace group. In other words, we only see the workspace action menu if we do launchWorkspaceGroup(), and they get removed when we do closeWorkspaceGroup(). (This also means we can deprecate the showSiderailAndBottomNav param of <WorkspaceContainer>)

    • For the patient chart in particular, this means we immediately call launchWorkspaceGroup when we open the patient chart, and call cloesWorkspaceGroup when we unmount. This is good, as we should be forced to define the workspace group context (which, in this case, is the visit uuid of the visit in context) for the visit note / clinical forms / order basket workspaces.
  • Add proper support for restoring minimized workspaces.

    Some workspaces, like the patient chart’s visit note workspace, can be hidden / minimized without completely closing. We can restore / un-hide the workspace by clicking on the corresponding workspace action menu icon. This is implemented incorrectly right now; clicking on the icon calls launchWorkspace, replacing any props passed into the existing workspace. This is especially bad with the introduction of workspace context (which is a param of launchWorkspace). Instead, we should make the action menu icon’s onClick do a “restore” action (by un-hiding the existing workspace), and only call launchWorkspace if no workspace of that type is currently opened.

cc: @mseaton @mogoodrich @ibacher @dkigen @vasharma05 @jayasanka

Maybe you can clarify something that’s been bothering me: what is a workspace group? As you’ve said, there’s this “type” feature that was sort of intended to group multiple workspaces that are logically together.

We also need to consider how we keep the correct “state” for each workspace that is “open” live.

We may need to just completely rewrite the whole system.

what is a workspace group? As you’ve said, there’s this “type” feature that was sort of intended to group multiple workspaces that are logically together.

(I don’t love the nomenclature, but…)

A workspace “type” maps to one action menu icon. In the order basket, the main order-basket workspace, and add-lab-order workspace and add-drug-order workspace all have the same type = "order", and so have same action menu icon. See screenshot:

I think what this implies, is that we should never actually launch a particular workspace directly. Rather we should be launching a particular workspace type, with enough props / params passed in so it knows which workspace within the workspace type you intend to open. This is, of course, very incompatible with how we launch workspaces right now . But I think we can sidestep this problem if we make it so that no 2 workspaces have the same workspace type. For example, with the order basket, we can re-implement it with just one workspace and let it manage its own navigation state to the lab order / drug order forms. More aggressively, we can even deprecate the idea of workspace type, by using the workspace name to directly map to an action menu icon, although it breaks backwards incompatibility.

(Aside: what I’ve been proposing as workspace context would be more correctly named as “workspace type context”, but again, we can sidestep this if we don’t have workspace type.)

A workspace group, as is currently defined, is (also) a group of related workspace, although it would be more correct to define it instead as a group of related “workspace types”. (Again, we can sidestep this.) In the ward app, when you click on a patient card, you not only open a workspace, but also open a workspace group, and the action icons for “transfer”, “discharge”, “order basket” etc… all belong to the same workspace group. See screenshot:

Currently, you can have multiple workspaces open, but only if they all belong to the same workspace group. (Contrast with workspace type, where you can only have one workspace of a particular type opened at a time)

We also need to consider how we keep the correct “state” for each workspace that is “open” live

We may need to just completely rewrite the whole system.

Looking at the unit tests, I think the current workspace system does a reasonable job maintaining state for each open workspace. We are using it incorrectly in some places, (like this issue), but I think it’s fixable.

If we eliminate “workspace type”, and also get rid of <WorkspaceContainer> (which my proposal above should help with, as it remove some params we currently need to pass into it), then I think the workspace system is actually alright.