From 9ea001c65d0fec1131ab9d28514abe232e20d9ab Mon Sep 17 00:00:00 2001 From: Himabindu T Date: Mon, 24 Jul 2023 21:50:23 +0530 Subject: [PATCH] Phani, Bindu | BAH-3117 | Ability to add, display and update Notes section in OT Scheduling (#647) * Add Modal to create OT Notes * Make notes scrollable * Add Redirection on Save * Add ToolTip * Fix css for day view. * Add tests * Add Validations for Create Note * BAH-3092 | Show primary diagnoses in OT list view (#631) * BAH-3092 | add capability to show primary diagnosis in OT list view * BAH-3092 | refactor to display latest diagnoses and test fixes --------- Co-authored-by: Ashish Kurian * Fix build issues * Integrate with Backend for Day view * Update CSS and date * Bindu | add notes save and get support for week view * Bindu, Phani | BAH-3117 | Integrate Update API to the UI * Bindu | Fix test cases * Bindu | Change note resource to notes * Fix tests * Phani | A-1205015570874061 | Ability to delete existing notes on the OT Scheduling page. (#650) * Make css of notes text configurable and Add notes delete template * Add css for hover and fix translation * Fix Update notes functionality * Add delete note functionality * Fix fetch date issue * Fix tests * Fix UI validations and CSS * Fix CSS in notes modal --------- Co-authored-by: Phanindra-tw Co-authored-by: kavitha-sundararajan <90255023+kavitha-sundararajan@users.noreply.github.com> Co-authored-by: Ashish Kurian Co-authored-by: Phanindra-tw <126503818+Phanindra-tw@users.noreply.github.com> --- ui/app/i18n/ot/locale_en.json | 14 +- ui/app/ot/constants.js | 3 +- ui/app/ot/controller/otCalendarController.js | 207 +++++++++++++++++- ui/app/ot/directives/otNotes.js | 10 + ui/app/ot/index.html | 1 + .../ot/services/surgicalAppointmentService.js | 47 +++- ui/app/ot/views/notesModal.html | 45 ++++ ui/app/ot/views/otCalendar.html | 30 ++- ui/app/ot/views/otWeeklyCalendar.html | 17 +- ui/app/styles/ot/_otCalendar.scss | 64 +++++- ui/app/styles/ot/_otWeeklyCalendar.scss | 133 ++++++++++- .../controller/otCalendarController.spec.js | 168 +++++++++++++- .../surgicalAppointmentService.spec.js | 2 +- 13 files changed, 708 insertions(+), 33 deletions(-) create mode 100644 ui/app/ot/directives/otNotes.js create mode 100644 ui/app/ot/views/notesModal.html diff --git a/ui/app/i18n/ot/locale_en.json b/ui/app/i18n/ot/locale_en.json index 13cc969b00..5d84a73887 100644 --- a/ui/app/i18n/ot/locale_en.json +++ b/ui/app/i18n/ot/locale_en.json @@ -14,6 +14,7 @@ "DATE": "Date", "DISCARD": "Discard", "SAVE": "Save", + "UPDATE": "Update", "ADD_SURGICAL_APPOINTMENT": "Add Surgery", "EDIT_SURGICAL_APPOINTMENT": "Edit Surgery", @@ -114,5 +115,16 @@ "ACTUAL_TIME_ADDED_TO_KEY":"Actual time added to ", "OT_SURGICAL_BLOCK_ACROSS_MULTIPLE_DAYS_CONFIRMATION_MESSAGE": "Are you sure you want to create a surgical block across days?", "MOVING_KEY": "Moving", - "FROM_KEY": "from" + "FROM_KEY": "from", + "EMPTY_NOTES_ERROR": "Note cannot be empty", + "NOTE_TITLE": "Note", + "FROM": "From", + "TO": "To", + "FROM_DATE_BEFORE_TO_DATE_ERROR": "From date should be before To date", + "DATE_OUT_OF_RANGE_ERROR": "Please select date within the valid range", + "DELETE_NOTE_TITLE": "Delete Note", + "YES_KEY": "Yes", + "NO_KEY": "No", + "DELETE_NOTE_MESSAGE": "Are you sure you want to delete this note?", + "NOTES_PLACEHOLDER": "Enter a maximum of 150 characters" } diff --git a/ui/app/ot/constants.js b/ui/app/ot/constants.js index 165d59570b..c8ca8aba25 100644 --- a/ui/app/ot/constants.js +++ b/ui/app/ot/constants.js @@ -17,7 +17,8 @@ Bahmni.OT.Constants = (function () { defaultCalendarStartTime: '00:00', weekDays: {"Monday": 1, "Tuesday": 2, "Wednesday": 3, "Thursday": 4, "Friday": 5, "Saturday": 6, "Sunday": 7 }, defaultWeekStartDayName: 'Sunday', - providerSurgicalAttributeFormat: 'org.openmrs.Provider' + providerSurgicalAttributeFormat: 'org.openmrs.Provider', + notesUrl: RESTWS_V1 + '/notes' }; })(); diff --git a/ui/app/ot/controller/otCalendarController.js b/ui/app/ot/controller/otCalendarController.js index d4771d4b49..6dff3bab0f 100644 --- a/ui/app/ot/controller/otCalendarController.js +++ b/ui/app/ot/controller/otCalendarController.js @@ -1,8 +1,8 @@ 'use strict'; angular.module('bahmni.ot') - .controller('otCalendarController', ['$scope', '$rootScope', '$q', '$interval', 'spinner', 'locationService', 'surgicalAppointmentService', '$timeout', 'appService', 'surgicalAppointmentHelper', - function ($scope, $rootScope, $q, $interval, spinner, locationService, surgicalAppointmentService, $timeout, appService, surgicalAppointmentHelper) { + .controller('otCalendarController', ['$scope', '$rootScope', '$q', '$interval', '$state', 'spinner', 'locationService', 'surgicalAppointmentService', '$timeout', 'appService', 'surgicalAppointmentHelper', + function ($scope, $rootScope, $q, $interval, $state, spinner, locationService, surgicalAppointmentService, $timeout, appService, surgicalAppointmentHelper) { var updateCurrentDayTimeline = function () { $scope.currentTimeLineHeight = heightPerMin * Bahmni.Common.Util.DateUtil.diffInMinutes($scope.calendarStartDatetime, new Date()); }; @@ -10,8 +10,167 @@ angular.module('bahmni.ot') $scope.blocksStartDatetime = $scope.weekOrDay === 'day' ? $scope.viewDate : moment($scope.weekStartDate).startOf('day'); $scope.blocksEndDatetime = $scope.weekOrDay === 'day' ? moment($scope.viewDate).endOf('day') : moment(Bahmni.Common.Util.DateUtil.getWeekEndDate($scope.weekStartDate)).endOf('day'); }; + $scope.isModalVisible = false; + $scope.notesStartDate = false; + $scope.notesEndDate = false; + $scope.isEdit = false; + $scope.showDeletePopUp = false; + $scope.styleForBlock = function (index) { + if (index === 6) { + return { 'border-right': '.5px solid lightgrey'}; + } + }; + var setValidStartDate = function (viewDate) { + const currentDate = new Date(viewDate); + $scope.validStartDate = $scope.weekDates[0]; + while (currentDate > new Date($scope.weekDates[0])) { + const prev = new Date(currentDate); + currentDate.setDate(currentDate.getDate() - 1); + if ($scope.notesForWeek[currentDate]) { + $scope.validStartDate = prev; + break; + } + } + }; + var setValidEndDate = function (viewDate) { + const currentDate = new Date(viewDate); + $scope.validEndDate = $scope.weekDates[6]; + while (currentDate < new Date($scope.weekDates[6])) { + const prev = new Date(currentDate); + currentDate.setDate(currentDate.getDate() + 1); + if ($scope.notesForWeek[currentDate]) { + $scope.validEndDate = prev; + break; + } + } + }; + + $scope.showNotesPopup = function (weekStartDate, addIndex) { + const currentDate = new Date(weekStartDate); + if (addIndex === undefined) { + addIndex = 0; + } + currentDate.setDate(currentDate.getDate() + addIndex); + $scope.notesStartDate = currentDate; + $scope.notesEndDate = currentDate; + $scope.isModalVisible = true; + $scope.isDayView = $state.weekOrDay === 'day'; + if (!$scope.isDayView) { + setValidStartDate(currentDate); + setValidEndDate(currentDate); + } + }; + $scope.showNotesPopupEdit = function (weekStartDate, addIndex) { + const note = $scope.getNotesForDay(weekStartDate, addIndex); + const currentDate = new Date(weekStartDate); + $scope.otNotesField = note; + currentDate.setDate(currentDate.getDate() + addIndex); + $scope.notesStartDate = currentDate; + $scope.notesEndDate = currentDate; + $scope.isModalVisible = true; + $scope.isEdit = true; + $scope.dateOutOfRangeError = false; + }; + $scope.closeNotes = function () { + $scope.isModalVisible = false; + $scope.startDateBeforeEndDateError = false; + $scope.emptyNoteError = false; + $scope.dateOutOfRangeError = false; + $scope.notesStartDate = undefined; + $scope.notesEndDate = undefined; + $scope.otNotesField = ''; + $scope.isEdit = false; + }; + $scope.setNotesEndDate = function (date) { + $scope.dateOutOfRangeError = date === undefined; + $scope.startDateBeforeEndDateError = date < $scope.notesStartDate; + $scope.notesEndDate = date; + }; + + $scope.setNotesStartDate = function (date) { + $scope.dateOutOfRangeError = date === undefined; + $scope.startDateBeforeEndDateError = date > $scope.notesEndDate; + $scope.notesStartDate = date; + }; + + $scope.setNotes = function (notes) { + $scope.otNotesField = notes; + if (notes) { + $scope.emptyNoteError = false; + } + if (notes && notes.id) { + $scope.notesId = notes.id; + } + }; + $scope.openDeletePopup = function (weekStartDate, index) { + if (weekStartDate) { + $scope.currentDate = new Date(weekStartDate); + $scope.currentDate.setDate($scope.currentDate.getDate() + index); + } + $scope.showDeletePopUp = true; + }; + $scope.closeDeletePopup = function () { + $scope.showDeletePopUp = false; + }; + $scope.deleteNotes = function () { + var noteId; + if ($scope.weekOrDay === "week") { + noteId = $scope.notesForWeek[$scope.currentDate].noteId; + } + surgicalAppointmentService.deleteNoteForADay(noteId || $scope.noteId); + $scope.showDeletePopUp = false; + $state.go("otScheduling", {viewDate: $scope.viewDate}, {reload: true}); + }; + + $scope.saveNotes = function () { + if ($scope.startDateBeforeEndDateError || $scope.dateOutOfRangeError) { + return; + } + if (!$scope.otNotesField) { + $scope.emptyNoteError = true; + return; + } + if ($scope.isDayView) { + surgicalAppointmentService.saveNoteForADay($scope.viewDate, $scope.otNotesField); + } else { + surgicalAppointmentService.saveNoteForADay($scope.notesStartDate, $scope.otNotesField, $scope.notesEndDate); + } + $state.go("otScheduling", {viewDate: $scope.viewDate}, {reload: true}); + }; + + $scope.updateNotes = function () { + if ($scope.startDateBeforeEndDateError || $scope.dateOutOfRangeError) { + return; + } + if (!$scope.otNotesField) { + $scope.emptyNoteError = true; + return; + } + var note; + if ($scope.weekOrDay === "week") { + note = $scope.notesForWeek[$scope.notesStartDate]; + } + surgicalAppointmentService.updateNoteForADay(note ? note.noteId : $scope.noteId, $scope.otNotesField); + $state.go("otScheduling", {viewDate: $scope.viewDate}, {reload: true}); + }; var heightPerMin = 120 / $scope.dayViewSplit; + var showToolTipForNotes = function () { + $('.notes-text').tooltip({ + content: function () { + var vm = (this); + return $(vm).prop('title'); + }, + track: true + }); + }; + const getNotes = function () { + if ($scope.weekOrDay === 'day') { + return surgicalAppointmentService.getBulkNotes(new Date($scope.viewDate)); + } else if ($scope.weekOrDay === 'week') { + return surgicalAppointmentService.getBulkNotes($scope.weekStartDate, getWeekDate(7)); + } + }; var init = function () { var dayStart = ($scope.dayViewStart || Bahmni.OT.Constants.defaultCalendarStartTime).split(':'); var dayEnd = ($scope.dayViewEnd || Bahmni.OT.Constants.defaultCalendarEndTime).split(':'); @@ -20,6 +179,9 @@ angular.module('bahmni.ot') $scope.editDisabled = true; $scope.cancelDisabled = true; $scope.addActualTimeDisabled = true; + $scope.otNotes = ""; + $scope.isModalVisible = false; + $scope.showDeletePopUp = false; $scope.dayViewSplit = parseInt($scope.dayViewSplit) > 0 ? parseInt($scope.dayViewSplit) : 60; $scope.calendarStartDatetime = Bahmni.Common.Util.DateUtil.addMinutes($scope.viewDate, (dayStart[0] * 60 + parseInt(dayStart[1]))); $scope.calendarEndDatetime = Bahmni.Common.Util.DateUtil.addMinutes($scope.viewDate, (dayEnd[0] * 60 + parseInt(dayEnd[1]))); @@ -28,7 +190,8 @@ angular.module('bahmni.ot') $scope.rows = $scope.getRowsForCalendar(); return $q.all([locationService.getAllByTag('Operation Theater'), surgicalAppointmentService.getSurgicalBlocksInDateRange($scope.blocksStartDatetime, $scope.blocksEndDatetime, false, true), - surgicalAppointmentService.getSurgeons()]).then(function (response) { + surgicalAppointmentService.getSurgeons(), + getNotes()]).then(function (response) { $scope.locations = response[0].data.results; $scope.weekDates = $scope.getAllWeekDates(); var surgicalBlocksByLocation = _.map($scope.locations, function (location) { @@ -36,6 +199,13 @@ angular.module('bahmni.ot') return surgicalBlock.location.uuid === location.uuid; }); }); + if (response[3] && response[3].status === 200) { + $scope.noteForTheDay = response[3].data.length > 0 ? response[3].data[0].noteText : ''; + $scope.noteId = response[3].data.length > 0 ? response[3].data[0].noteId : ''; + } else { + $scope.noteForTheDay = ''; + $scope.noteId = ''; + } var providerNames = appService.getAppDescriptor().getConfigValue("primarySurgeonsForOT"); $scope.surgeons = surgicalAppointmentHelper.filterProvidersByName(providerNames, response[2].data.results); var surgicalBlocksBySurgeons = _.map($scope.surgeons, function (surgeon) { @@ -48,7 +218,34 @@ angular.module('bahmni.ot') return $scope.isSurgicalBlockActiveOnGivenDate(surgicalBlock, weekDate); }); }); + + $scope.getNotesForWeek = function (weekStartDate, index) { + const date = new Date(weekStartDate); + if (index === undefined) { + const notesForAWeek = {}; + response[3].data.map(function (note) { + notesForAWeek[new Date(note.noteDate)] = note; + }); + return notesForAWeek; + } + return _.filter(response[3].data, function (note) { + const currentDate = new Date(date); + currentDate.setDate(date.getDate() + index); + return new Date(note.noteDate).getDate() === (currentDate).getDate(); + }); + }; + + if ($scope.weekOrDay === 'week') { + $scope.notesForWeek = $scope.getNotesForWeek(); + } + + $scope.getNotesForDay = function (weekStartDate, index) { + var notes = $scope.getNotesForWeek(weekStartDate, index); + return notes.length > 0 ? notes[0].noteText : ''; + }; + $scope.blockedOtsOfTheWeek = getBlockedOtsOfTheWeek(); + showToolTipForNotes(); var setOTView = function (providerToggle) { $scope.providerToggle = providerToggle; @@ -66,8 +263,8 @@ angular.module('bahmni.ot') }; $scope.isSurgicalBlockActiveOnGivenDate = function (surgicalBlock, weekDate) { - return Bahmni.Common.Util.DateUtil.isSameDate(moment(surgicalBlock.startDatetime).startOf('day').toDate(), weekDate) - || moment(surgicalBlock.endDatetime).toDate() > weekDate; + return Bahmni.Common.Util.DateUtil.isSameDate(moment(surgicalBlock.startDatetime).startOf('day').toDate(), weekDate) || + moment(surgicalBlock.endDatetime).toDate() > weekDate; }; $scope.intervals = function () { diff --git a/ui/app/ot/directives/otNotes.js b/ui/app/ot/directives/otNotes.js new file mode 100644 index 0000000000..1e73580744 --- /dev/null +++ b/ui/app/ot/directives/otNotes.js @@ -0,0 +1,10 @@ +'use strict'; + +angular.module('bahmni.ot') + .directive('otNotes', [function () { + return { + restrict: 'E', + require: '^otCalendar', + templateUrl: "../ot/views/notesModal.html" + }; + }]); diff --git a/ui/app/ot/index.html b/ui/app/ot/index.html index 5933028fc5..b4bbb877da 100644 --- a/ui/app/ot/index.html +++ b/ui/app/ot/index.html @@ -246,6 +246,7 @@ + diff --git a/ui/app/ot/services/surgicalAppointmentService.js b/ui/app/ot/services/surgicalAppointmentService.js index 947a1c055e..3a1b46541d 100644 --- a/ui/app/ot/services/surgicalAppointmentService.js +++ b/ui/app/ot/services/surgicalAppointmentService.js @@ -61,9 +61,54 @@ angular.module('bahmni.ot') v: "custom:(id,uuid," + "provider:(uuid,person:(uuid,display),attributes:(attributeType:(display),value,voided))," + "location:(uuid,name),startDatetime,endDatetime,surgicalAppointments:(id,uuid,patient:(uuid,display,person:(age))," + - "actualStartDatetime,actualEndDatetime,status,notes,sortWeight,bedNumber,bedLocation,surgicalAppointmentAttributes,patientObservations))" + "actualStartDatetime,actualEndDatetime,status,notes,sortWeight,bedNumber,bedLocation,surgicalAppointmentAttributes))" }, withCredentials: true }); }; + this.getBulkNotes = function (startDate, endDate) { + return $http.get(Bahmni.OT.Constants.notesUrl, { + method: 'GET', + params: { + noteType: 'OT Module', + noteStartDate: startDate, + noteEndDate: endDate + }, + withCredentials: true + }); + }; + + var constructPayload = function (noteDate, note, noteEndDate) { + const payload = []; + const currentDate = new Date(noteDate); + // eslint-disable-next-line no-unmodified-loop-condition + while (currentDate <= noteEndDate) { + payload.push({ + noteTypeName: "OT module", + noteDate: new Date(currentDate), + noteText: note + }); + currentDate.setDate(currentDate.getDate() + 1); + } + return payload; + }; + + this.saveNoteForADay = function (noteDate, note, noteEndDate) { + const payload = noteEndDate != null ? constructPayload(noteDate, note, noteEndDate) : [{ + noteTypeName: "OT module", + noteDate: noteDate, + noteText: note + }]; + const headers = {"Accept": "application/json", "Content-Type": "application/json"}; + return $http.post(Bahmni.OT.Constants.notesUrl, payload, headers); + }; + + this.updateNoteForADay = function (noteId, note) { + const headers = {"Accept": "application/json", "Content-Type": "application/json"}; + return $http.post(Bahmni.OT.Constants.notesUrl + "/" + noteId, note, headers); + }; + this.deleteNoteForADay = function (noteId) { + const headers = {"Accept": "application/json", "Content-Type": "application/json", withCredentials: true}; + return $http.delete(Bahmni.OT.Constants.notesUrl + "/" + noteId, headers); + }; }]); diff --git a/ui/app/ot/views/notesModal.html b/ui/app/ot/views/notesModal.html new file mode 100644 index 0000000000..21f9ec5d2e --- /dev/null +++ b/ui/app/ot/views/notesModal.html @@ -0,0 +1,45 @@ +
+
+ × +
+
{{'NOTE_TITLE' | translate}}
+ +
+
{{'EMPTY_NOTES_ERROR' | translate }}
+
+
+
{{'FROM' | translate}}
+ +
+
+
{{'TO' | translate}}
+ +
+
+ {{'FROM_DATE_BEFORE_TO_DATE_ERROR' | translate}} +
+
+ {{'DATE_OUT_OF_RANGE_ERROR' | translate}} +
+
+ + + +
+
+ +
+
+ × +
+
{{'DELETE_NOTE_TITLE' | translate}}
+

{{'DELETE_NOTE_MESSAGE' | translate}}

+
+ + +
+
diff --git a/ui/app/ot/views/otCalendar.html b/ui/app/ot/views/otCalendar.html index 4651a0f369..9e7e6fc418 100644 --- a/ui/app/ot/views/otCalendar.html +++ b/ui/app/ot/views/otCalendar.html @@ -1,13 +1,27 @@
-
-

- {{location.name}} -

-

- {{surgeon.person.display}} -

+
+

{{viewDate | bahmniDateTimeWithFormat: 'ddd'}}

+

{{viewDate | bahmniDateTimeWithFormat: 'DD'}}

+
+
+
+ {{noteForTheDay}} +
+ +
+
+
+

+ {{location.name}} +

+

+ {{surgeon.person.display}} +

+
+
+
@@ -35,7 +49,7 @@ ng-if="shouldDisplayCurrentTimeLine()" ng-style = "{top : currentTimeLineHeight + 'px'}">
- +
diff --git a/ui/app/ot/views/otWeeklyCalendar.html b/ui/app/ot/views/otWeeklyCalendar.html index 4506332dfc..43c0503771 100644 --- a/ui/app/ot/views/otWeeklyCalendar.html +++ b/ui/app/ot/views/otWeeklyCalendar.html @@ -1,8 +1,16 @@
-

- {{weekStartDate | addDays: index | bahmniDateTimeWithFormat: 'Do MMM, ddd'}} -

+
+

{{weekStartDate | addDays: index | bahmniDateTimeWithFormat: 'ddd'}}

+

{{weekStartDate | addDays: index | bahmniDateTimeWithFormat: 'DD'}}

+
+
+
+ {{getNotesForDay(weekStartDate, index)}} +
+ +
+
@@ -31,9 +39,8 @@ ng-if="shouldDisplayCurrentTimeLine()" ng-style="{top : currentTimeLineHeight + 'px'}">
- +
-
diff --git a/ui/app/styles/ot/_otCalendar.scss b/ui/app/styles/ot/_otCalendar.scss index 7014f21f5e..5d31a6c031 100644 --- a/ui/app/styles/ot/_otCalendar.scss +++ b/ui/app/styles/ot/_otCalendar.scss @@ -15,26 +15,72 @@ .calendar-location { padding-left: 80px; overflow: hidden; + background: #fff; + width: 94%; float: left; - display: flex; position: absolute; z-index: 20; - background: #fff; - width: 94%; .heading-cell { width: 300px; box-sizing: border-box; text-align: center; float: left; - border-bottom: .5px solid lightgrey; padding: 10px 0; + border-bottom: 1px solid #ddd; font-family: OpenSansSemiBold; } } + .day-heading{ + position: absolute; + top: 55px; + } + .error{ + color: #da1e28; + padding-top: 5px; + height: 15px; + } + .heading-ot-date{ + font-size: 24px; + } + .heading-ot{ + padding-bottom: 10px; + } + .heading-ot-block{ + display: inline-block; + position: absolute; + left: 24px; + } + .notesTab{ + width: calc(100% - 10px); + height: 30px; + color: #161616; + &:hover{ + background-color: #EDF5FF; + } + } + .notes-text{ + background-color: #FCF4D6; + width: calc(100% - 10px); + color: #161616; + height: auto; + border: 1px solid #F1C21B; + border-left: 4px solid #F1C21B; + display: flex; + div{ + display: inline-block; + width: calc(100% - 54px); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + > * { + padding: 8px; + } + } .calendar-time-container { clear: left; position: absolute; - top: 36px; + top: 94px; background: #fff; z-index: 19; @@ -57,7 +103,7 @@ left: 80px; overflow: auto; padding-bottom: 5px; - top: 36px; + top: 95px; width: calc(100% - 90px); right: 0; } @@ -307,4 +353,10 @@ padding: 0.3%; border-radius: 4px; position: absolute; + z-index: 20; +} +hr{ + border: 0; + border-bottom: 1px solid #ddd; + margin-top: 15px; } diff --git a/ui/app/styles/ot/_otWeeklyCalendar.scss b/ui/app/styles/ot/_otWeeklyCalendar.scss index 4082a5d252..52a216d21e 100644 --- a/ui/app/styles/ot/_otWeeklyCalendar.scss +++ b/ui/app/styles/ot/_otWeeklyCalendar.scss @@ -6,8 +6,40 @@ text-align: center; float: left; border-bottom: .5px solid lightgrey; - padding: 10px 0; - font-family: OpenSansSemiBold; + padding: 5px 0; + .note{ + border-left: .5px solid lightgrey; + width: 100%; + height: 33px; + &:hover{ + background-color: #EDF5FF; + } + } + .heading-ot-date{ + font-size: 24px; + } + .heading-ot{ + padding-bottom: 10px; + } + .notes-text{ + background-color: #FCF4D6; + height: auto; + border: 1px solid #F1C21B; + border-left: 3px solid #F1C21B; + color: #161616; + text-align: left; + display: flex; + div{ + display: inline-block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 80%; + } + > * { + padding: 8px 0 8px 8px; + } + } } } .table-block { @@ -36,6 +68,103 @@ } } } + .identifier-ui { + position: fixed; + z-index: 1000; + padding-top: 100px; + width: 100%; + height: 100%; + left: 0; + top: 0; + overflow: auto; + background-color: rgba(0, 0, 0, 0.6); + + .identifier-iframe { + background-color: #fefefe; + position: relative; + margin: auto; + border: 1px solid #888; + padding: 20px 0; + width: 30%; + top: 100px; + height: 320px; + + @media screen and (max-width: 540px) { + width: 90%; + } + + @media screen and (min-width: 541px) and (max-width: 1024px) { + width: 80%; + } + .calendar-day-input { + background: #fff; + border: 1px solid #95989a; + outline: none; + width: 205px; + font-size: 14px; + padding: 5px; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); + transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); + display: inline-block; + color: #000; + } + button{ + height: 64px; + width: 50%; + color: white; + border: none; + border-radius: 0; + background-image: none; + position: absolute; + bottom: 0; + text-align: left; + } + + h6{ + color: black; + } + + .save-button{ + right: 0; + background-color: #007d79; + } + .delete-button{ + right: 0; + background-color: #da1e28; + } + + .cancel-button{ + left: 0; + background-color: #39393F; + } + .notes-text-area{ + width: 95%; + border: 0.5px solid; + resize: none; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); + border-radius: 4px; + &:focus{ + background-color: white; + } + } + } + + .close { + color: #aaaaaa; + float: right; + font-size: 28px; + font-weight: bold; + margin-bottom: 10px; + } + + .close:hover, + .close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + } + } } .surgical-block { margin: 0 1px; diff --git a/ui/test/unit/ot/controller/otCalendarController.spec.js b/ui/test/unit/ot/controller/otCalendarController.spec.js index 76ef72b89d..55c0822bdd 100644 --- a/ui/test/unit/ot/controller/otCalendarController.spec.js +++ b/ui/test/unit/ot/controller/otCalendarController.spec.js @@ -1,10 +1,11 @@ 'use strict'; describe("otCalendarController", function () { - var scope, controller, q, spinner, state; + var scope, controller, q, spinner; + var state = jasmine.createSpyObj('$state', ['go']); var locationService = jasmine.createSpyObj('locationService', ['getAllByTag']); spinner = jasmine.createSpyObj('spinner', ['forPromise', 'then', 'catch']); - var surgicalAppointmentService = jasmine.createSpyObj('surgicalAppointmentService', ['getSurgicalBlocksInDateRange', 'getSurgeons']); + var surgicalAppointmentService = jasmine.createSpyObj('surgicalAppointmentService', ['getSurgicalBlocksInDateRange', 'getSurgeons', 'getBulkNotes', 'saveNoteForADay']); var ngDialog = jasmine.createSpyObj('ngDialog', ['open']); var appService = jasmine.createSpyObj('appService', ['getAppDescriptor']); var appDescriptor = jasmine.createSpyObj('appDescriptor', ['getConfigValue']); @@ -67,6 +68,17 @@ describe("otCalendarController", function () { } ]; + var notes = [ + { + id: 1, + noteText: "note1", + noteType: "OT module", + noteDate: "2017-02-19T09:00:00.000+0530", + uuid: "note1-uuid", + location: "location1" + } + ]; + locationService.getAllByTag.and.callFake(function () { return {data: {results: [{uuid: "uuid1", name: "location1"}, {uuid: "uuid2", name: "location2"}]}}; }); @@ -86,15 +98,17 @@ describe("otCalendarController", function () { scope.dayViewStart = '09:00'; scope.dayViewEnd = '16:30'; scope.dayViewSplit = '60'; + scope.notesStartDate = false; + scope.notesEndDate = false; scope.viewDate = moment('2017-02-19').toDate(); controller('otCalendarController', { $scope: scope, locationService: locationService, + $state: state, $q: q, spinner: spinner, surgicalAppointmentService: surgicalAppointmentService, appService: appService - }); scope.$apply(); }; @@ -107,6 +121,9 @@ describe("otCalendarController", function () { surgicalAppointmentService.getSurgeons.and.callFake(function () { return {data: {results: surgeons}}; }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); createController(); var intervals = scope.intervals(); @@ -120,6 +137,9 @@ describe("otCalendarController", function () { surgicalAppointmentService.getSurgeons.and.callFake(function () { return {data: {results: surgeons}}; }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); createController(); var rows = scope.getRowsForCalendar(); @@ -138,6 +158,9 @@ describe("otCalendarController", function () { surgicalAppointmentService.getSurgeons.and.callFake(function () { return {data: {results: surgeons}}; }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); createController(); expect(locationService.getAllByTag).toHaveBeenCalledWith('Operation Theater'); @@ -153,6 +176,9 @@ describe("otCalendarController", function () { surgicalAppointmentService.getSurgeons.and.callFake(function () { return {data: {results: surgeons}}; }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); scope.weekOrDay = 'day'; createController(); expect(surgicalAppointmentService.getSurgicalBlocksInDateRange).toHaveBeenCalledWith(scope.viewDate, moment(scope.viewDate).endOf('day'), false, true); @@ -240,4 +266,140 @@ describe("otCalendarController", function () { }); }); + describe('notes', function () { + it('should set startDate and endDate to selectedDate when showNotesPopup is called', function () { + surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { + return {data: {results: weekSurgicalBlocks}}; + }); + scope.weekOrDay = 'week'; + scope.weekStartDate = moment('2020-04-06').toDate(); + surgicalAppointmentService.getSurgeons.and.callFake(function () { + return {data: {results: surgeons}}; + }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); + createController(); + const weekStartDate = moment('2023-07-02').toDate(); + const expectedDate = moment('2023-07-05').toDate(); + scope.showNotesPopup(weekStartDate, 3); + expect(scope.notesStartDate).toEqual(expectedDate); + expect(scope.notesEndDate).toEqual(expectedDate); + }); + it('should set startDate and endDate to selectedDate when we try to edit existing note', function () { + surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { + return {data: {results: weekSurgicalBlocks}}; + }); + scope.weekOrDay = 'week'; + scope.weekStartDate = moment('2020-04-06').toDate(); + surgicalAppointmentService.getSurgeons.and.callFake(function () { + return {data: {results: surgeons}}; + }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); + createController(); + const weekStartDate = moment('2023-07-02').toDate(); + const expectedDate = moment('2023-07-05').toDate(); + scope.showNotesPopupEdit(weekStartDate, 3, 'note to show'); + expect(scope.notesStartDate).toEqual(expectedDate); + expect(scope.notesEndDate).toEqual(expectedDate); + }); + it('should clear startDate and endDate notes popup is closed', function () { + surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { + return {data: {results: weekSurgicalBlocks}}; + }); + scope.weekOrDay = 'week'; + scope.weekStartDate = moment('2020-04-06').toDate(); + surgicalAppointmentService.getSurgeons.and.callFake(function () { + return {data: {results: surgeons}}; + }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); + createController(); + const weekStartDate = moment('2023-07-02').toDate(); + const expectedDate = moment('2023-07-05').toDate(); + scope.showNotesPopupEdit(weekStartDate, 3, 'note to show'); + expect(scope.notesStartDate).toEqual(expectedDate); + expect(scope.notesEndDate).toEqual(expectedDate); + scope.closeNotes(); + expect(scope.notesStartDate).toEqual(undefined); + expect(scope.notesEndDate).toEqual(undefined); + expect(scope.otNotesField).toEqual(''); + }); + it('should set the notes popup fields and save', function () { + surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { + return {data: {results: weekSurgicalBlocks}}; + }); + scope.weekOrDay = 'week'; + scope.weekStartDate = moment('2020-04-06').toDate(); + surgicalAppointmentService.getSurgeons.and.callFake(function () { + return {data: {results: surgeons}}; + }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); + createController(); + const viewDate = moment('2023-07-05').toDate(); + scope.setNotesStartDate(viewDate); + scope.setNotesEndDate(viewDate); + scope.setNotes('note to show'); + scope.saveNotes(); + expect(scope.notesStartDate).toEqual(viewDate); + expect(scope.notesEndDate).toEqual(viewDate); + expect(scope.otNotesField).toEqual('note to show'); + }); + it('should set error field for empty notes when we try to save empty notes', function () { + surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { + return {data: {results: weekSurgicalBlocks}}; + }); + scope.weekOrDay = 'week'; + scope.weekStartDate = moment('2020-04-06').toDate(); + surgicalAppointmentService.getSurgeons.and.callFake(function () { + return {data: {results: surgeons}}; + }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); + createController(); + scope.setNotes(''); + scope.saveNotes(); + expect(scope.emptyNoteError).toEqual(true); + }); + it('should set error field when start Date is before endDate', function () { + surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { + return {data: {results: weekSurgicalBlocks}}; + }); + scope.weekOrDay = 'week'; + scope.weekStartDate = moment('2020-04-06').toDate(); + surgicalAppointmentService.getSurgeons.and.callFake(function () { + return {data: {results: surgeons}}; + }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); + createController(); + scope.setNotesStartDate(moment('2023-07-05').toDate()); + scope.setNotesEndDate(moment('2023-07-02').toDate()); + scope.saveNotes(); + expect(scope.startDateBeforeEndDateError).toEqual(true); + }); + it('should get styling of last block', function () { + surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { + return {data: {results: weekSurgicalBlocks}}; + }); + scope.weekOrDay = 'week'; + scope.weekStartDate = moment('2020-04-06').toDate(); + surgicalAppointmentService.getSurgeons.and.callFake(function () { + return {data: {results: surgeons}}; + }); + surgicalAppointmentService.getBulkNotes.and.callFake(function () { + return {data: notes} + }); + createController(); + const style = scope.styleForBlock(6); + expect(style).toEqual({ 'border-right': '.5px solid lightgrey'}); + }); + }); }); diff --git a/ui/test/unit/ot/services/surgicalAppointmentService.spec.js b/ui/test/unit/ot/services/surgicalAppointmentService.spec.js index 48f8b2b94f..4c7ccdc75f 100644 --- a/ui/test/unit/ot/services/surgicalAppointmentService.spec.js +++ b/ui/test/unit/ot/services/surgicalAppointmentService.spec.js @@ -100,7 +100,7 @@ describe('surgicalAppointmentService', function () { expect(mockHttp.get.calls.mostRecent().args[1].params).toEqual({ startDatetime : '2039-08-26T12:00:00.000', endDatetime : '2039-08-26T15:00:00.000',includeVoided: false, activeBlocks: true, v: "custom:(id,uuid," + "provider:(uuid,person:(uuid,display),attributes:(attributeType:(display),value,voided))," + "location:(uuid,name),startDatetime,endDatetime,surgicalAppointments:(id,uuid,patient:(uuid,display,person:(age))," + - "actualStartDatetime,actualEndDatetime,status,notes,sortWeight,bedNumber,bedLocation,surgicalAppointmentAttributes,patientObservations))"}); + "actualStartDatetime,actualEndDatetime,status,notes,sortWeight,bedNumber,bedLocation,surgicalAppointmentAttributes))"}); expect(mockHttp.get.calls.mostRecent().args[1].withCredentials).toBeTruthy(); });