diff --git a/packages/frontend/tests/acceptance/course/session/offerings-test.js b/packages/frontend/tests/acceptance/course/session/offerings-test.js index acac54ca44..21a985f0ac 100644 --- a/packages/frontend/tests/acceptance/course/session/offerings-test.js +++ b/packages/frontend/tests/acceptance/course/session/offerings-test.js @@ -96,7 +96,8 @@ module('Acceptance | Session - Offerings', function (hooks) { await takeScreenshot(assert); await percySnapshot(assert); - assert.strictEqual(page.details.offerings.header.title, 'Offerings (3)'); + assert.ok(page.details.offerings.header.isVisible); + assert.strictEqual(page.details.offerings.top.title, 'Offerings (3)'); assert.strictEqual(page.details.offerings.dateBlocks.length, 3); }); @@ -317,7 +318,7 @@ module('Acceptance | Session - Offerings', function (hooks) { await page.visit({ courseId: 1, sessionId: 1 }); await page.details.offerings.dateBlocks[0].timeBlockOfferings.offerings[0].remove(); await page.details.offerings.dateBlocks[0].timeBlockOfferings.offerings[0].confirmRemoval(); - assert.strictEqual(page.details.offerings.header.title, 'Offerings (2)'); + assert.strictEqual(page.details.offerings.top.title, 'Offerings (2)'); assert.strictEqual(page.details.offerings.dateBlocks.length, 2); }); @@ -326,14 +327,14 @@ module('Acceptance | Session - Offerings', function (hooks) { await page.visit({ courseId: 1, sessionId: 1 }); await page.details.offerings.dateBlocks[0].timeBlockOfferings.offerings[0].remove(); await page.details.offerings.dateBlocks[0].timeBlockOfferings.offerings[0].cancelRemoval(); - assert.strictEqual(page.details.offerings.header.title, 'Offerings (3)'); + assert.strictEqual(page.details.offerings.top.title, 'Offerings (3)'); assert.strictEqual(page.details.offerings.dateBlocks.length, 3); }); test('users can create a new offering single day', async function (assert) { this.user.update({ administeredSchools: [this.school] }); await page.visit({ courseId: 1, sessionId: 1 }); - await page.details.offerings.header.createNew(); + await page.details.offerings.top.createNew(); const { offeringForm: form } = page.details.offerings; await page.details.offerings.singleOffering(); await form.startDate.datePicker.set(new Date(2011, 8, 11)); @@ -382,7 +383,7 @@ module('Acceptance | Session - Offerings', function (hooks) { this.user.update({ administeredSchools: [this.school] }); const sep112011 = DateTime.fromObject({ year: 2011, month: 9, day: 11, hour: 2, minute: 15 }); await page.visit({ courseId: 1, sessionId: 1 }); - await page.details.offerings.header.createNew(); + await page.details.offerings.top.createNew(); const { offeringForm: form } = page.details.offerings; await page.details.offerings.singleOffering(); await form.startDate.datePicker.set(sep112011.toJSDate()); @@ -449,7 +450,7 @@ module('Acceptance | Session - Offerings', function (hooks) { test('users can create a new small group offering', async function (assert) { this.user.update({ administeredSchools: [this.school] }); await page.visit({ courseId: 1, sessionId: 1 }); - await page.details.offerings.header.createNew(); + await page.details.offerings.top.createNew(); const { offeringForm: form } = page.details.offerings; await page.details.offerings.smallGroup(); await form.startDate.datePicker.set(new Date(2011, 8, 11)); @@ -514,7 +515,9 @@ module('Acceptance | Session - Offerings', function (hooks) { test('users can edit existing offerings', async function (assert) { this.user.update({ administeredSchools: [this.school] }); await page.visit({ courseId: 1, sessionId: 1 }); + assert.ok(page.details.offerings.header.isVisible); await page.details.offerings.dateBlocks[0].timeBlockOfferings.offerings[0].edit(); + assert.notOk(page.details.offerings.header.isVisible); const { offeringForm: form } = page.details.offerings.dateBlocks[0].timeBlockOfferings.offerings[0]; @@ -533,6 +536,7 @@ module('Acceptance | Session - Offerings', function (hooks) { await form.instructorSelectionManager.selectedInstructorGroups.instructorGroups[0].remove(); await form.save(); + assert.ok(page.details.offerings.header.isVisible); const block = page.details.offerings.dateBlocks[0]; @@ -559,11 +563,91 @@ module('Acceptance | Session - Offerings', function (hooks) { assert.strictEqual(offering.url, 'https://example.org/'); }); + test('user can cancel offering edits', async function (assert) { + this.user.update({ administeredSchools: [this.school] }); + await page.visit({ courseId: 1, sessionId: 1 }); + + assert.ok(page.details.offerings.header.isVisible); + let block = page.details.offerings.dateBlocks[0]; + assert.ok(block.hasStartTime); + assert.ok(block.hasEndTime); + assert.notOk(block.hasMultiDay); + assert.strictEqual(block.dayOfWeek, 'Friday'); + assert.strictEqual(block.dayOfMonth, 'December 11'); + assert.strictEqual(block.startTime, 'Starts: 09:00 AM'); + assert.strictEqual(block.endTime, 'Ends: 10:00 AM'); + assert.strictEqual(block.timeBlockOfferings.offerings.length, 1); + + let offering = block.timeBlockOfferings.offerings[0]; + + assert.strictEqual(offering.learnerGroups.length, 2); + assert.strictEqual(offering.learnerGroups[0].title, 'learner group 0'); + assert.strictEqual(offering.instructors.length, 8); + assert.strictEqual(offering.instructors[0].userNameInfo.fullName, '1 guy M. Mc1son'); + assert.strictEqual(offering.instructors[1].userNameInfo.fullName, '2 guy M. Mc2son'); + assert.strictEqual(offering.instructors[2].userNameInfo.fullName, '3 guy M. Mc3son'); + assert.strictEqual(offering.instructors[3].userNameInfo.fullName, '4 guy M. Mc4son'); + assert.strictEqual(offering.instructors[4].userNameInfo.fullName, '5 guy M. Mc5son'); + assert.strictEqual(offering.instructors[5].userNameInfo.fullName, '6 guy M. Mc6son'); + assert.strictEqual(offering.instructors[6].userNameInfo.fullName, '7 guy M. Mc7son'); + assert.strictEqual(offering.instructors[7].userNameInfo.fullName, '8 guy M. Mc8son'); + assert.strictEqual(offering.location, 'room 0'); + assert.strictEqual(offering.url, 'https://ucsf.edu/'); + await page.details.offerings.dateBlocks[0].timeBlockOfferings.offerings[0].edit(); + assert.notOk(page.details.offerings.header.isVisible); + + const { offeringForm: form } = + page.details.offerings.dateBlocks[0].timeBlockOfferings.offerings[0]; + + await form.startDate.datePicker.set(new Date(2011, 9, 5)); + await form.startTime.timePicker.hour.select('11'); + await form.startTime.timePicker.minute.select('45'); + await form.startTime.timePicker.ampm.select('AM'); + await form.duration.hours.set(6); + await form.duration.minutes.set(10); + await form.location.set('Rm. 111'); + await form.url.set('https://example.org'); + + await form.learnerManager.learnergroupSelectionManager.selectedLearnerGroups.detailLearnergroupsList.trees[0].items[0].remove(); + await form.instructorSelectionManager.selectedInstructors.instructors[0].remove(); + await form.instructorSelectionManager.selectedInstructorGroups.instructorGroups[0].remove(); + + await form.close(); + assert.ok(page.details.offerings.header.isVisible); + + block = page.details.offerings.dateBlocks[0]; + + assert.ok(block.hasStartTime); + assert.ok(block.hasEndTime); + assert.notOk(block.hasMultiDay); + assert.strictEqual(block.dayOfWeek, 'Friday'); + assert.strictEqual(block.dayOfMonth, 'December 11'); + assert.strictEqual(block.startTime, 'Starts: 09:00 AM'); + assert.strictEqual(block.endTime, 'Ends: 10:00 AM'); + assert.strictEqual(block.timeBlockOfferings.offerings.length, 1); + + offering = block.timeBlockOfferings.offerings[0]; + + assert.strictEqual(offering.learnerGroups.length, 2); + assert.strictEqual(offering.learnerGroups[0].title, 'learner group 0'); + assert.strictEqual(offering.instructors.length, 8); + assert.strictEqual(offering.instructors[0].userNameInfo.fullName, '1 guy M. Mc1son'); + assert.strictEqual(offering.instructors[1].userNameInfo.fullName, '2 guy M. Mc2son'); + assert.strictEqual(offering.instructors[2].userNameInfo.fullName, '3 guy M. Mc3son'); + assert.strictEqual(offering.instructors[3].userNameInfo.fullName, '4 guy M. Mc4son'); + assert.strictEqual(offering.instructors[4].userNameInfo.fullName, '5 guy M. Mc5son'); + assert.strictEqual(offering.instructors[5].userNameInfo.fullName, '6 guy M. Mc6son'); + assert.strictEqual(offering.instructors[6].userNameInfo.fullName, '7 guy M. Mc7son'); + assert.strictEqual(offering.instructors[7].userNameInfo.fullName, '8 guy M. Mc8son'); + assert.strictEqual(offering.location, 'room 0'); + assert.strictEqual(offering.url, 'https://ucsf.edu/'); + }); + test('users can create recurring small groups', async function (assert) { this.user.update({ administeredSchools: [this.school] }); await page.visit({ courseId: 1, sessionId: 1 }); - await page.details.offerings.header.createNew(); + await page.details.offerings.top.createNew(); const { offeringForm: form } = page.details.offerings; await page.details.offerings.smallGroup(); await form.startDate.datePicker.set(new Date(2015, 4, 22)); @@ -637,7 +721,7 @@ module('Acceptance | Session - Offerings', function (hooks) { this.user.update({ administeredSchools: [this.school] }); await page.visit({ courseId: 1, sessionId: 1 }); - await page.details.offerings.header.createNew(); + await page.details.offerings.top.createNew(); const { offeringForm: form } = page.details.offerings; await page.details.offerings.singleOffering(); await form.startDate.datePicker.set(new Date(2015, 4, 22)); diff --git a/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/session-details.js b/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/session-details.js index cb63d3cb47..647b17148d 100644 --- a/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/session-details.js +++ b/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/session-details.js @@ -30,11 +30,14 @@ export default create({ instructorsAreVisible: isVisible('[data-test-detail-instructors]'), offerings: { scope: '[data-test-session-offerings]', - header: { + top: { scope: '.offering-section-top', title: text('.title'), createNew: clickable('.actions button'), }, + header: { + scope: '[data-test-session-offerings-header]', + }, dateBlocks: collection('[data-test-session-offerings-list] .offering-block', { dayOfWeek: text('.offering-block-date-dayofweek'), dayOfMonth: text('.offering-block-date-dayofmonth'), diff --git a/packages/ilios-common/addon/components/offering-manager.gjs b/packages/ilios-common/addon/components/offering-manager.gjs index ed2e410e15..87e68ce19b 100644 --- a/packages/ilios-common/addon/components/offering-manager.gjs +++ b/packages/ilios-common/addon/components/offering-manager.gjs @@ -7,7 +7,6 @@ import { modifier } from 'ember-modifier'; import { TrackedAsyncData } from 'ember-async-data'; import { mapBy, sortBy } from 'ilios-common/utils/array-helpers'; import OfferingForm from 'ilios-common/components/offering-form'; -import toggle from 'ilios-common/helpers/toggle'; import mouseHoverToggle from 'ilios-common/modifiers/mouse-hover-toggle'; import { fn, get, concat } from '@ember/helper'; import FaIcon from 'ilios-common/components/fa-icon'; @@ -37,6 +36,14 @@ export default class OfferingManagerComponent extends Component { set(this, `learnerGroupElement${id}`, element); }); + @action + toggleIsEditing() { + this.isEditing = !this.isEditing; + if (this.args.toggleIsEditing) { + this.args.toggleIsEditing(); + } + } + @cached get learnerGroupsData() { return new TrackedAsyncData(this.args.offering.learnerGroups); @@ -122,7 +129,7 @@ export default class OfferingManagerComponent extends Component { instructorGroups, instructors, }); - + this.toggleIsEditing(); return this.args.offering.save(); } @@ -152,7 +159,7 @@ export default class OfferingManagerComponent extends Component { @cohorts={{this.cohorts}} @courseStartDate={{this.course.startDate}} @courseEndDate={{this.course.endDate}} - @close={{toggle "isEditing" this}} + @close={{this.toggleIsEditing}} @save={{this.save}} @smallGroupMode={{false}} @offering={{@offering}} @@ -234,7 +241,7 @@ export default class OfferingManagerComponent extends Component { class="link-button edit" type="button" title={{t "general.edit"}} - {{on "click" (toggle "isEditing" this)}} + {{on "click" this.toggleIsEditing}} > diff --git a/packages/ilios-common/addon/components/session-offerings-list.gjs b/packages/ilios-common/addon/components/session-offerings-list.gjs index 263aa85465..ca1fc1b253 100644 --- a/packages/ilios-common/addon/components/session-offerings-list.gjs +++ b/packages/ilios-common/addon/components/session-offerings-list.gjs @@ -109,6 +109,7 @@ export default class SessionOfferingsListComponent extends Component { @offeringTimeBlock={{offeringTimeBlock}} @removeOffering={{this.removeOffering}} @editable={{@editable}} + @toggleIsEditing={{@toggleIsEditing}} /> {{/each}} diff --git a/packages/ilios-common/addon/components/session-offerings-time-block-offerings.gjs b/packages/ilios-common/addon/components/session-offerings-time-block-offerings.gjs index ee2208fdc2..61d6aac8e5 100644 --- a/packages/ilios-common/addon/components/session-offerings-time-block-offerings.gjs +++ b/packages/ilios-common/addon/components/session-offerings-time-block-offerings.gjs @@ -35,6 +35,7 @@ export default class SessionOfferingsTimeBlockOfferingsComponent extends Compone @offering={{offering}} @remove={{@removeOffering}} @editable={{@editable}} + @toggleIsEditing={{@toggleIsEditing}} /> {{/each}} diff --git a/packages/ilios-common/addon/components/session-offerings.gjs b/packages/ilios-common/addon/components/session-offerings.gjs index a782a5b692..645fced225 100644 --- a/packages/ilios-common/addon/components/session-offerings.gjs +++ b/packages/ilios-common/addon/components/session-offerings.gjs @@ -1,12 +1,15 @@ import Component from '@glimmer/component'; +import { cached, tracked } from '@glimmer/tracking'; import { TrackedAsyncData } from 'ember-async-data'; -import { cached } from '@glimmer/tracking'; +import toggle from 'ilios-common/helpers/toggle'; import t from 'ember-intl/helpers/t'; import ExpandCollapseButton from 'ilios-common/components/expand-collapse-button'; import NewOffering from 'ilios-common/components/new-offering'; import SessionOfferingsList from 'ilios-common/components/session-offerings-list'; export default class SessionOfferingsComponent extends Component { + @tracked isEditing = false; + @cached get courseData() { return new TrackedAsyncData(this.args.session.course); @@ -53,24 +56,30 @@ export default class SessionOfferingsComponent extends Component { {{/if}}
{{#if @session.offerings.length}} -
-
- {{t "general.dateTime"}} -
-
- {{t "general.learnersAndLearnerGroups"}} -
-
- {{t "general.location"}} -
-
- {{t "general.instructors"}} -
-
- {{t "general.actions"}} + {{#unless this.isEditing}} +
+
+ {{t "general.dateTime"}} +
+
+ {{t "general.learnersAndLearnerGroups"}} +
+
+ {{t "general.location"}} +
+
+ {{t "general.instructors"}} +
+
+ {{t "general.actions"}} +
-
- + {{/unless}} + {{else}}

{{t "general.noOfferings"}}