Skip to content
Merged
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
5 changes: 4 additions & 1 deletion CageUI/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
*
* * Copyright (c) 2025 Board of Regents of the University of Wisconsin System
* * Copyright (c) 2026 Board of Regents of the University of Wisconsin System
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
Expand All @@ -22,7 +22,10 @@ plugins {
id 'org.labkey.build.module'
}
dependencies {
implementation project(path: "${project.parent.path}:DBUtils", configuration: "apiJarFile")

BuildUtils.addLabKeyDependency(project: project, config: "implementation", depProjectPath: ":server:modules:LabDevKitModules:LDK", depProjectConfig: "apiJarFile")
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:modules:LabDevKitModules:LDK", depProjectConfig: 'published', depExtension: 'module')
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:modules:ehrModules:ehr", depProjectConfig: 'published', depExtension: 'module')
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: "${project.parent.path}:DBUtils", depProjectConfig: 'published', depExtension: 'module')
}
143 changes: 143 additions & 0 deletions CageUI/documentation/CageUI Instructions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<!--
~ /*
~ * Copyright (c) 2026 Board of Regents of the University of Wisconsin System
~ *
~ * Licensed under the Apache License, Version 2.0 (the "License");
~ * you may not use this file except in compliance with the License.
~ * You may obtain a copy of the License at
~ *
~ * http://www.apache.org/licenses/LICENSE-2.0
~ *
~ * Unless required by applicable law or agreed to in writing, software
~ * distributed under the License is distributed on an "AS IS" BASIS,
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ * See the License for the specific language governing permissions and
~ * limitations under the License.
~ */
-->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CageUI Layout Editor Rules</title>
</head>
<body>
<h3>Workflow</h3>
<ol>
<li>When initially loading the page select the grid size. For larger rooms with more cages you would want to select
a larger grid to ensure that all the items can be placed on the layout. This grid size cannot be changed later.
</li>
<li>Move items to the layout by dragging and dropping them in position</li>
<li>Open an items context menu by right-clicking the item.
<ol>
<li>
Different items have different context menus, but they are all accessed the same way.
</li>
</ol>
</li>
<li>Place cages next to each other to merge them into the same rack or connect the racks.
<ol>
<li>
Cages cannot be merged unless they share the same type.
</li>
<li>
Generally you should create your racks before assigning them a rack type.
</li>
</ol>
</li>
</ol>
<h3>Context Menus</h3>
<div>
<h4>Cage context menus support the following actions</h4>
<ol>
<li>
Removing the cage or rack from the layout.
</li>
<li>
Changing the rack.
<ul>
<li>
In this menu you can also create new racks. Open the menu and enter the rack type and rack id, and it
will be automatically assigned to that rack when you save.
</li>
</ul>
</li>
<li>
Changing the cage number.
</li>
<li>
Rotating the rack group.
<ul>
<li>
Rotations start at 90 degrees.
</li>
<li>
Rotations are done clockwise. For the starting rotation and position this means that the top of the rack group
is the right side of the group.
</li>
<li>
Rack groups are all the cages/racks that are connected to each other.
</li>
</ul>
</li>
</ol>
</div>
<div>
<h4>Room Object context menus support the following actions</h4>
<ol>
<li>
Removing the room object from the layout.
</li>
<li>Gate objects have have two additonal actions.
<ol>
<li>
Set the room that the gate is connected to. This indicates that the two rooms have a connection that animals can go through.
</li>
<li>
Closing/opening the gate, indicating if the gate is open or closed and if animals are able to go between rooms.
</li>
</ol>
</li>
</ol>
</div>

<h3>Creating Templates</h3>
<div>
<h4>Rules for creating templates</h4>
<ul>
<li>
All racks/cages in a room must have a default rack type. By default, this is what they are when placed in the room.
This ensures that real racks/cages are not placed in rooms that "do not exist."
</li>
</ul>
</div>
<h3>Creating Real Rooms</h3>
<div>
<h4>Rules for creating real room layouts</h4>
<ul>
<li>
All racks/cages in a room must have a real rack type.
This ensures that racks/cages that "do not exist" are not placed in rooms that are real.
</li>
<li>No duplicate cage numbers.</li>
</ul>
</div>
<h3>Additional Notes for Both Types of Layouts</h3>
<div>
Both real rooms and template rooms have a resizeable border that determines the border of the room.
This helps compact the size of the layout into a more realistic room size when working with different size starting
grids. Ensure that no room objects or cages are placed outside this border as they will not be shown when viewing the
room from the display page.
</div>
<div>
<h4>Additional Actions</h4>
<ol>
<li>The Grid Enabled button turns off the grid lines to show a better view of what the room will look like on
the display page.
</li>
<li>Clear Layout wipes all the items from the layout, allowing you to start over.</li>
</ol>
</div>
</body>
</html>
124 changes: 124 additions & 0 deletions CageUI/documentation/Layout Editor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Layout Editor Documentation

This readme explains how the layout editor works and how to set up new room objects.

You can access the view with this url within your main container

`/cageui/WNPRC/EHR/layoutEditor.view`

If you are running the dev server just edit it slightly.

`/cageui/WNPRC/EHR/layoutEditorDev.view`

In order to use this project, make sure to enable the module in your folder set up within labkey.

This project was built for the Chrome browser. If you use a different browser, it may not work as expected. Additionally,
the layout editor was not built for mobile devices. The idea behind this is that once the users build the rooms, they can
be modified on mobile from different endpoints but the act of room creation is better done on desktop.


# Workflow in detail

## Context Manager

The main layout editor entry point is `LayoutEditor.tsx`. This file loads in previous room data if required or simply starts
a new room layout. When starting a new room layout, the user can select a room size from a list of predefined sizes. These sizes
are present in the `constants.ts` file. Once a room is created with a certain size, this cannot be changed.

The layout editor initializes with a context manager `LayoutEditorContextManager.tsx` with its types described in
`layoutEditorContextTypes.ts`. This context manager serves as a place to store the room state and has functions
for any edits that might occur to the room state.

In the context manager (CM) there are two Room objects, **localRoom** and **room**, **room** serves as the initial room or
previous room, and **localRoom** is the room that has changes applied to. This allows development to work with the previous room and
the current room that has local changes applied to it. You can use **room** when saving to check for changes or apply previous room data
to the **localRoom** that is being saved, **room** should not be modified at all during development.

**unitLocs** is another important state within the CM. This state tracks cage positional data within the layout and
serves it in an easy-to-access object. Its keys are rack types and the values contain the x and y global coords of the cage object.
This state tracks the locations of the cages, by rack type. This is used within the editor to determine if two cages are
adjacent to each other. Any action that changes the cage location in the editor should update this state with the
new locations. Please note that the coordinates here are global coords.

The last important state in the CM is **cageConnections** this state tracks which cages
are connected/merged to other cages and is used within the CM when deleting cages to split cages into new groups.
An example this is what happens when you have a row of three connected cages and delete the middle cage? The system will
have to split up the remaining two cages on either side into new groups to handle this correctly. This is a complicated
group of functions that hopefully shouldn't need to be updated as it should be working, but it's worth noting here. Additionally,
it can be avoided by proper room creation or by deleting the entire group of cages/racks and rebuilding them if needed.

### **Important**

While the CM manages the state, this doesn't automatically handle the changes completely. There is a lot of DOM
manipulation with adding new objects, merging cages, connecting racks, etc. These DOM changes cannot
be performed within the CM and have to be done on the same file that the layout-grid SVG is served from. That is why the
`Editor.tsx` file is very long.

## Editor

The `Editor.tsx` file is another important file here in the layout editor portion of the project.
This file manages all the SVG DOM manipulation and changes that occur when adding and moving objects around the room.
The file is also where the majority of the code is written for the layout editor.
By pairing this file with the context manager and any used functions within the helper files you essentially have the
entire layout editor. The editor file uses effects to track changes to the unitLocs state to determine if a merge or connect
is requested, if the room should be reloaded with new changes to **localRoom** or when objects/cages are added to the layout.
It uses D3.js and basic DOM functions to handle the dragging and placement of the objects within the editor.

## Templates vs Real layouts

The layout editor supports two styles of rooms, templates and real. While similar in building they differ on a couple
fundamentals. Template rooms are created with the idea that they do not represent a real physcial location. They are merely
a layout that will be loaded into the editor in the future for easier building by users. The major difference and key point
here is that when a cage is dragged onto the layout it is considered to be a "default" of that cage type. "Defaults" do not
represent real racks or cages and as such they cannot be saved in real rooms. So in order to save a room as a template,
it's racks must all be a "default" type, otherwise it will throw an error. Likewise in order for a real room to be saved
it cannot have any "default" types and the user must assign a real physical rack to that position. The system will not show
racks that are already in other rooms preventing double assignment.

## Editor Context Menu

Every object that can be placed within the room has a context menu that the user can access via right-clicking.
Room objects and cages have different menus, but they use the same component. Developers can add additional components to the
context menu via the "menuItems" prop. Refer to the `CageUI Insturctions.html` file on what each current menu item will do.

## Adding New Objects

If you would like to add additional room objects or cage sizes that is also possible. Here is what you will have to do to
make this possible.

1. Create your SVG file within an editor, I used Adobe Illustrator for this and exported it as an SVG. Once you create this
file that will be used as your object add it to `CageUI/resources/web/CageUI/static`.
1. If you encounter issues with loading the file after adding it here try going to `localhost:8080/cageui/static/{filename}.svg`
to ensure that it works and is loaded in correctly.
2. Add your object under the correct enum in `typings.ts`.
1. If your new object is a rack/cage type add it under RackTypes, create a default as well.
2. If your new object is a room object (ex. door) then add it under RoomObjectTypes.
3. Add your new object to ehr_lookups.cageui_item_types. Ensure your enum value and table values are matching. Additionally,
if your item is a caging type, give it a size in the description field of the table. The size is the number of cells of the object both length and width.
This is untested for caging types that aren't perfect squares.
3. Next you should add your object into the `Editor.tsx` file via the **RoomItemTemplate** component. Look to others for examples.

If done correctly, your new object should be available to be placed within the editor.

## Important Notes

Here are some additional things to keep in mind when using the layout editor.

1. When cages are placed within the layout they are assigned a rack group, rack and then their cage number/id
2. Users can merge two cages. When this is done, it moves the dragged cage into the target cages rack.
3. Users can connect two cages. When this is done, it moves the dragged cage and rack into the target cages rack group.
4. Merging and connecting is created with the idea that it lets users build racks and then connect racks, all from single cages. These
changes are reflected within the DOM as well as the state.
1. To get a grasp on what goes on here, I would suggest inspecting the 'layout-grid' SVG within the DOM and using an
effect to console log the localRoom state variable from the Editor.tsx file.











45 changes: 45 additions & 0 deletions CageUI/documentation/Migrating to cageUI cages table.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@


# Housing Changes

### 1. Add New Cage Column
#### A.
We have to remove the old cage table that is used in ehr_lookups with the new cage table in the cageui module.
For the WNPRC this change needed to be done in the following files.

1. >WNPRC_EHR/resources/queries/study/Housing.query.xml
2. >WNPRC_EHR/resources/queries/study/Housing/.qview.xml
3. >WNPRC_EHR/resources/queries/study/Housing/Active Housing.qview.xml
4. >WNPRC_EHR/resources/queries/study/Housing/Current Housing Plus Weight.qview.xml
5. >WNPRC_EHR/resources/referenceStudy/study/datasets/datasets_metadata.xml
6. >WNPRC_EHR/resources/web/ehr/metadata/Default.js
7. >WNPRC_EHR/resources/web/ehr/metadata/Metadata.js
8. >WNPRC_EHR/resources/web/wnprc_ehr/wnprcOverRides.js

#### B.
After changing these files, go into the table definition editor in EHR and rename the cage column to cageOld.
Then add a new column named cage. This will rename the previous cage column while preserving the data and
allowing us to work with the new cage column under the old name.

As we add new data to the housing table we should insert it under the new column if using the new housing system.
If using the old housing system, insert it under the old column. This will allow us to use the old housing system while
testing the new system until we are ready to switch over to the new housing system.

At the WNPRC we will only be using the new system in a select few rooms for testing. Unfortunately, the users will have
to submit their housing changes under both systems during this phase until we are ready to switch over. This will ensure
data integrity.

### 3. Cage Details
Add override for cageDetails.html and cageDetails.view.xml including new cages table instead of cages from ehr_lookups.


Next go through and update the links so they point to your module instead of "ehr".
Add rooms.query.xml file to your ehr_lookups schema and edit the url there as well.

Old:
>/ehr/WNPRC/EHR/cageDetails.view?room=a140a&cage=0001

New:
>/wnprc_ehr/WNPRC/EHR/cageDetails.view?room=a140a&cage=0001


Loading
Loading