diff --git a/app/course/courseApp.js b/app/course/courseApp.js index e785a5fb1..4312e3dee 100644 --- a/app/course/courseApp.js +++ b/app/course/courseApp.js @@ -24,6 +24,7 @@ import censusChart from './directives/censusChart.js'; import courseTable from './directives/courseTable.js'; import activeFilters from './directives/activeFilters/activeFilters.js'; import moveCourseModal from './directives/moveCourseModal/moveCourseModal.js'; +import convertSectionsModal from './directives/convertSectionsModal/convertSectionsModal.js'; // Dependencies var dependencies = [ @@ -88,6 +89,7 @@ const courseApp = angular.module("courseApp", dependencies) // eslint-disable-li .directive('censusChart', censusChart) .directive('courseTable', courseTable) .directive('activeFilters', activeFilters) +.directive('convertSectionsModal', convertSectionsModal) .constant('ActionTypes', { INIT_STATE: "INIT_STATE", NEW_COURSE: "NEW_COURSE", @@ -97,6 +99,7 @@ const courseApp = angular.module("courseApp", dependencies) // eslint-disable-li GET_COURSE_CENSUS: "GET_COURSE_CENSUS", BEGIN_FETCH_CENSUS: "BEGIN_FETCH_CENSUS", ADD_SECTION_GROUP: "ADD_SECTION_GROUP", + CREATE_SECTION_GROUP: "CREATE_SECTION_GROUP", REMOVE_SECTION_GROUP: "REMOVE_SECTION_GROUP", UPDATE_SECTION_GROUP: "UPDATE_SECTION_GROUP", TOGGLE_TERM_FILTER: "TOGGLE_TERM_FILTER", @@ -124,7 +127,8 @@ const courseApp = angular.module("courseApp", dependencies) // eslint-disable-li CLOSE_COURSE_DELETION_MODAL: "CLOSE_COURSE_DELETION_MODAL", TOGGLE_MOVE_COURSE_MODAL: "TOGGLE_MOVE_COURSE_MODAL", DELETE_MULTIPLE_COURSES: "DELETE_MULTIPLE_COURSES", - MASS_ASSIGN_TAGS: "MASS_ASSIGN_TAGS" + MASS_ASSIGN_TAGS: "MASS_ASSIGN_TAGS", + TOGGLE_CONVERT_SECTIONS_MODAL: "TOGGLE_CONVERT_SECTIONS_MODAL" }); export default courseApp; diff --git a/app/course/directives/convertSectionsModal/convertSectionsModal.css b/app/course/directives/convertSectionsModal/convertSectionsModal.css new file mode 100644 index 000000000..0be1df4cb --- /dev/null +++ b/app/course/directives/convertSectionsModal/convertSectionsModal.css @@ -0,0 +1,34 @@ +.convert-sections-modal .convert-sections-modal-footer { + padding: 10px; + display: flex; + justify-content: flex-end; + padding-bottom: 15px; + padding-top: 15px; +} + +.convert-sections-modal .convert-sections-warning { + padding: 20px; +} + +.convert-sections-modal .sub-detail { + font-weight: bold; + font-size: 14px; + padding-top: 15px; +} + +.convert-sections-modal .btn-default { + background-color: white; + margin-right: 10px; +} + +.convertion-details { + display: flex; +} + +.conversion-details-example { + flex: 50%; +} + +.course-details_update-sequence { + width: 135px; +} diff --git a/app/course/directives/convertSectionsModal/convertSectionsModal.html b/app/course/directives/convertSectionsModal/convertSectionsModal.html new file mode 100644 index 000000000..dcb19439e --- /dev/null +++ b/app/course/directives/convertSectionsModal/convertSectionsModal.html @@ -0,0 +1,87 @@ +
+
+
+
+ New Sequence Pattern +
+
+ + +
+
+
+ After conversion teaching assignments and the Live Data budget scenario will be updated to reflect the change. +
+ All non-live data budget scenarios will be updated unless they have a conflicting offering. +
+ Budget requests will not be updated. +

+ Additionally, sections that belong to this course offering will be converted from {{isSeries() ? 'discussions to lectures' : 'lectures to discussions'}} +

For Example:

+
+
+ Before

+ XYZ 10 - 001 +
  • + Section 001: 100 Seats +
  • +
    +
    + After

    + XYZ 10 - A +
  • + Section A01: 100 Seats +
  • +
    +
    +
    +
    + Before

    + XYZ 10 - A +
  • + Section A01: 25 Seats +
  • +
  • + Section A02: 25 Seats +
  • +
  • + Section A03: 25 Seats +
  • +
  • + Section A04: 25 Seats +
  • +
    +
    + After

    + XYZ 10 - 001 +
  • + Section 001 Seats 100 +
  • +
    +
    +
    +
    + + + +
    diff --git a/app/course/directives/convertSectionsModal/convertSectionsModal.js b/app/course/directives/convertSectionsModal/convertSectionsModal.js new file mode 100644 index 000000000..0297db1dd --- /dev/null +++ b/app/course/directives/convertSectionsModal/convertSectionsModal.js @@ -0,0 +1,106 @@ +import './convertSectionsModal.css'; +import { isNumber, isLetter } from 'shared/helpers/types'; + +let convertSectionsModal = function (CourseActionCreators) { + return { + restrict: 'E', + template: require('./convertSectionsModal.html'), + replace: true, + scope: { + workgroupId: '<', + year: '<', + selectedEntity: '<', + isVisible: '=', + state: '=' + }, + link: function (scope) { + + scope.convertCourseOffering = function () { + CourseActionCreators.convertCourseOffering(scope.workgroupId, scope.year, scope.selectedEntity, scope.selectedEntity.sequencePattern.toUpperCase()); + scope.isVisible = false; + }; + + scope.close = function() { + scope.isVisible = false; + }; + + scope.isSeries = function () { + if (scope.selectedEntity){ + let selectedEntity = scope.selectedEntity; + let course = scope.state.courses.list[selectedEntity.courseId]; + if (course){ + return course.isSeries(); + } else { + return false; + } + } + return false; + }; + + scope.isValid = function () { + var isValid = false; + if (scope.selectedEntity && scope.selectedEntity.sequencePattern){ + if (!scope.isSeries()){ + if (scope.selectedEntity.sequencePattern.length === 1 && isLetter(scope.selectedEntity.sequencePattern[0].toUpperCase())){ + scope.selectedEntity.sequencePatternTooltipMessage = null; + isValid = true; + } else { + scope.selectedEntity.sequencePatternTooltipMessage = "Sequence pattern format is incorrect. Valid format is '1 letter' (ex: 'A')."; + isValid = false; + } + } else { + if ( + scope.selectedEntity.sequencePattern.length === 3 && + isNumber(scope.selectedEntity.sequencePattern[0]) && + isNumber(scope.selectedEntity.sequencePattern[1]) && + isNumber(scope.selectedEntity.sequencePattern[2]) + ){ + scope.selectedEntity.sequencePatternTooltipMessage = null; + isValid = true; + } else { + scope.selectedEntity.sequencePatternTooltipMessage = "Sequence pattern format is incorrect. Valid format is '3 numbers' (ex: '002')."; + isValid = false; + } + } + } + return isValid; + }; + + scope.isUnique = function () { + var isUnique = false; + if (scope.selectedEntity && scope.selectedEntity.sequencePattern){ + var sectionGroup = scope.selectedEntity; + var course = course = scope.state.courses.list[sectionGroup.courseId]; + var courseDescription = course.subjectCode + "-" + course.courseNumber + "-" + sectionGroup.sequencePattern.toUpperCase(); + isUnique = true; + scope.selectedEntity.sequencePatternTooltipMessage = null; + + scope.state.courses.ids.forEach(function(courseId) { + var c = scope.state.courses.list[courseId]; + let cDescription = c.subjectCode + "-" + c.courseNumber + "-" + c.sequencePattern; + + if (courseDescription == cDescription) { + scope.state.sectionGroups.ids.forEach(function(sectionGroupId) { + var sg = scope.state.sectionGroups.list[sectionGroupId]; + if (sg.courseId == c.id && sg.termCode == sectionGroup.termCode){ + scope.selectedEntity.sequencePatternTooltipMessage = "Sequence pattern already in use."; + isUnique = false; + } + }); + } + }); + } + + return isUnique; + }; + + scope.updateSequencePattern = function(){ + if (scope.selectedEntity.sequencePattern){ + scope.selectedEntity.sequencePattern = scope.selectedEntity.sequencePattern.toUpperCase(); + } + }; + } // end link + }; +}; + +export default convertSectionsModal; diff --git a/app/course/directives/courseDetails/courseDetails.html b/app/course/directives/courseDetails/courseDetails.html index e4f284b79..ec9264165 100644 --- a/app/course/directives/courseDetails/courseDetails.html +++ b/app/course/directives/courseDetails/courseDetails.html @@ -53,7 +53,7 @@

    diff --git a/app/course/directives/courseDetails/courseDetails.js b/app/course/directives/courseDetails/courseDetails.js index 3938e80d1..64627b310 100644 --- a/app/course/directives/courseDetails/courseDetails.js +++ b/app/course/directives/courseDetails/courseDetails.js @@ -60,7 +60,7 @@ let courseDetails = function (CourseActionCreators, SectionService) { } if (scope.isSequencePatternUnique(sequencePattern) == false) { - scope.courseDetails.sequencePatternTooltipMessage = "Sequence pattern already in use"; + scope.courseDetails.sequencePatternTooltipMessage = "Sequence pattern already in use."; return; } diff --git a/app/course/directives/courseTable.js b/app/course/directives/courseTable.js index 8597e29b2..abf70e4f6 100644 --- a/app/course/directives/courseTable.js +++ b/app/course/directives/courseTable.js @@ -38,6 +38,7 @@ let courseTable = function ($rootScope, $timeout, CourseActionCreators, $compile ActionTypes.UPDATE_SECTION_GROUP, ActionTypes.REMOVE_SECTION_GROUP, ActionTypes.ADD_SECTION_GROUP, + ActionTypes.CREATE_SECTION_GROUP, ActionTypes.DELETE_MULTIPLE_COURSES, ActionTypes.MASS_ASSIGN_TAGS, ActionTypes.CREATE_SECTION, @@ -56,11 +57,30 @@ let courseTable = function ($rootScope, $timeout, CourseActionCreators, $compile return; } - if (data.action.type == ActionTypes.ADD_SECTION_GROUP) { + if (data.action.type == ActionTypes.ADD_SECTION_GROUP || data.action.type == ActionTypes.CREATE_SECTION_GROUP) { // Indicate on the textbox that the sectionGroup is offered $('tr[data-course-id="' + data.action.payload.sectionGroup.courseId + '"] td[data-term-code="' + data.action.payload.sectionGroup.termCode + '"]').addClass("is-offered"); // eslint-disable-line no-undef $('tr[data-course-id="' + data.action.payload.sectionGroup.courseId + '"] td[data-term-code="' + data.action.payload.sectionGroup.termCode + '"] input').attr( 'placeholder', '0 planned seats' ); // eslint-disable-line + $('tr[data-course-id="' + data.action.payload.sectionGroup.courseId + '"] td[data-term-code="' + data.action.payload.sectionGroup.termCode + '"] input').attr( 'value', data.action.payload.sectionGroup.plannedSeats ); // eslint-disable-line + if (data.action.type == ActionTypes.CREATE_SECTION_GROUP){ + scope.previouslySelectedCourseId = data.state.uiState.selectedCourseId; + scope.previouslySelectedTermCode = data.state.uiState.selectedTermCode; + + // Remove existing highlighting + element.find('tbody > tr').removeClass("selected-tr"); + element.find('tbody > tr > td').removeClass("selected-td"); + + data.state.uiState.selectedCourseId = data.action.payload.sectionGroup.courseId; + $('tr[data-course-id="' + data.state.uiState.selectedCourseId + '"] td[data-term-code="' + data.state.uiState.selectedTermCode + '"]').addClass("selected-td"); // eslint-disable-line no-undef + + scope.manuallyDeselectAllCourseRows(); + scope.manuallyToggleSelectedCourse(data.state.uiState.selectedCourseId); + CourseActionCreators.deselectAllCourseRows(); + CourseActionCreators.toggleSelectCourse(data.state.uiState.selectedCourseId); + + return; + } return; } diff --git a/app/course/directives/sectionGroupDetails/sectionGroupDetails.css b/app/course/directives/sectionGroupDetails/sectionGroupDetails.css new file mode 100644 index 000000000..6dbf4f9e2 --- /dev/null +++ b/app/course/directives/sectionGroupDetails/sectionGroupDetails.css @@ -0,0 +1,3 @@ +.section-group-details__convert-offering-container { + margin-bottom: 20px +} diff --git a/app/course/directives/sectionGroupDetails/sectionGroupDetails.html b/app/course/directives/sectionGroupDetails/sectionGroupDetails.html index 4881a8438..dbe24a895 100644 --- a/app/course/directives/sectionGroupDetails/sectionGroupDetails.html +++ b/app/course/directives/sectionGroupDetails/sectionGroupDetails.html @@ -111,6 +111,15 @@

    overflow-auto="true" > +
    + +
    +