diff --git a/packages/frontend/tests/acceptance/course/publish-all-sessions-test.js b/packages/frontend/tests/acceptance/course/publish-all-sessions-test.js new file mode 100644 index 0000000000..c862613484 --- /dev/null +++ b/packages/frontend/tests/acceptance/course/publish-all-sessions-test.js @@ -0,0 +1,648 @@ +import { module, test } from 'qunit'; +import { currentURL } from '@ember/test-helpers'; +import { setupAuthentication } from 'ilios-common'; +import { setupApplicationTest } from 'frontend/tests/helpers'; +import page from 'ilios-common/page-objects/components/course/publish-all-sessions'; + +module('Acceptance | Course - Publish All Sessions', function (hooks) { + setupApplicationTest(hooks); + + hooks.beforeEach(async function () { + this.school = await this.server.create('school'); + this.user = await setupAuthentication({ administeredSchools: [this.school] }, true); + this.cohort = await this.server.create('cohort'); + this.course = await this.server.create('course', { + year: 2013, + school: this.school, + published: true, + cohorts: [this.cohort], + }); + this.sessionTypes = await this.server.createList('session-type', 2, { + school: this.school, + }); + this.vocabulary = await this.server.create('vocabulary', { + school: this.school, + }); + this.term = await this.server.create('term', { vocabulary: this.vocabulary }); + this.meshDescriptor = await this.server.create('mesh-descriptor'); + }); + + test('published sessions do not appear in the cannot publish list #1658', async function (assert) { + const term = await this.server.create('term'); + const course = await this.server.create('course', { + year: 2013, + school: this.school, + published: true, + cohorts: [this.cohort], + }); + const session1 = await this.server.create('session', { + course, + published: true, + publishedAsTbd: false, + terms: [term], + }); + await this.server.create('session-objective', { session: session1 }); + await this.server.create('offering', { session: session1 }); + const session2 = await this.server.create('session', { + course, + published: true, + publishedAsTbd: false, + terms: [term], + }); + await this.server.create('session-objective', { session: session2 }); + await this.server.create('offering', { session: session2 }); + await this.server.create('ilm-session', { session: session2 }); + const session3 = await this.server.create('session', { + course, + published: true, + publishedAsTbd: true, + terms: [term], + }); + await this.server.create('session-objective', { session: session3 }); + await this.server.create('offering', { session: session3 }); + + await page.visit({ + courseId: course.id, + }); + + assert.ok(page.publishAllSessions.isVisible); + assert.notOk(page.publishAllSessions.hasUnlinkedWarning); + assert.strictEqual( + page.publishAllSessions.unpublishableSessions.text, + 'Incomplete Sessions: cannot publish (0)', + ); + assert.notOk(page.publishAllSessions.unpublishableSessions.isExpanded); + assert.ok(page.publishAllSessions.unpublishableSessions.canExpandCollapse); + + assert.strictEqual(page.publishAllSessions.publishableSessions.text, 'Published Sessions (3)'); + assert.notOk(page.publishAllSessions.publishableSessions.isExpanded); + assert.strictEqual(page.publishAllSessions.publishableSessions.sessions.length, 0); + assert.ok(page.publishAllSessions.publishableSessions.canExpandCollapse); + await page.publishAllSessions.publishableSessions.toggle(); + assert.ok(page.publishAllSessions.publishableSessions.isExpanded); + assert.strictEqual(page.publishAllSessions.publishableSessions.sessions.length, 3); + + assert.strictEqual(page.publishAllSessions.publishableSessions.sessions[0].title, 'session 0'); + assert.strictEqual( + page.publishAllSessions.publishableSessions.sessions[0].offerings, + 'Yes (1)', + ); + assert.strictEqual(page.publishAllSessions.publishableSessions.sessions[0].terms, 'Yes (1)'); + assert.strictEqual( + page.publishAllSessions.publishableSessions.sessions[0].objectives.text, + 'Yes (1)', + ); + assert.ok(page.publishAllSessions.publishableSessions.sessions[0].objectives.isLinked); + + assert.strictEqual(page.publishAllSessions.publishableSessions.sessions[1].title, 'session 1'); + assert.strictEqual( + page.publishAllSessions.publishableSessions.sessions[1].offerings, + 'Yes (1)', + ); + assert.strictEqual(page.publishAllSessions.publishableSessions.sessions[1].terms, 'Yes (1)'); + assert.strictEqual( + page.publishAllSessions.publishableSessions.sessions[1].objectives.text, + 'Yes (1)', + ); + assert.ok(page.publishAllSessions.publishableSessions.sessions[1].objectives.isLinked); + + assert.strictEqual(page.publishAllSessions.publishableSessions.sessions[2].title, 'session 2'); + assert.strictEqual( + page.publishAllSessions.publishableSessions.sessions[2].offerings, + 'Yes (1)', + ); + assert.strictEqual(page.publishAllSessions.publishableSessions.sessions[2].terms, 'Yes (1)'); + assert.strictEqual( + page.publishAllSessions.publishableSessions.sessions[2].objectives.text, + 'Yes (1)', + ); + assert.ok(page.publishAllSessions.publishableSessions.sessions[2].objectives.isLinked); + }); + + test('after publishing user is returned to the courses route #4099', async function (assert) { + const terms = await this.server.createList('term', 1); + + // const course = await this.server.create('course', { + // year: 2013, + // school: this.school, + // published: true, + // cohorts: [this.cohort], + // }); + const session = await this.server.create('session', { + course: this.course, + published: false, + publishedAsTbd: false, + terms, + }); + await this.server.create('session-objective', { session }); + await this.server.create('session-type', { + sessions: [session], + }); + await this.server.create('offering', { session }); + + await page.visit({ + courseId: this.course.id, + }); + + assert.ok(page.publishAllSessions.isVisible); + await page.publishAllSessions.review.save(); + assert.strictEqual(currentURL(), '/courses/1'); + assert.strictEqual( + page.courseSessions.sessionsGrid.sessions[0].row.publicationStatus.icon.title, + 'Published', + ); + }); + + test('updating course objectives updates the unlinked objective warning', async function (assert) { + const programYear = await this.server.create('program-year', { + cohort: this.cohort, + }); + await this.server.create('program', { + school: this.school, + programYears: [programYear], + }); + await this.server.create('program-year-objective', { + programYear, + }); + + const course = await this.server.create('course', { + year: 2020, + school: this.school, + published: true, + cohorts: [this.cohort], + }); + await this.server.create('course-objective', { + course, + }); + const session = await this.server.create('session', { + course, + published: false, + publishedAsTbd: false, + }); + await this.server.create('session-type', { + sessions: [session], + }); + await page.visit({ + courseId: course.id, + details: true, + courseObjectiveDetails: true, + }); + assert.ok(page.publishAllSessions.isVisible); + assert.ok(page.publishAllSessions.hasUnlinkedWarning); + + await page.details.objectives.objectiveList.objectives[0].parents.manage(); + const m = page.details.objectives.objectiveList.objectives[0].parentManager; + await m.competencies[0].objectives[0].add(); + await page.details.objectives.objectiveList.objectives[0].parents.save(); + assert.notOk(page.publishAllSessions.hasUnlinkedWarning); + }); + + test('expand/collapse state of sections are tracked in url', async function (assert) { + const sessionObjectives = await this.server.createList('session-objective', 2); + const offerings = await this.server.createList('offering', 4); + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [sessionObjectives[0]], + offerings: [offerings[0], offerings[1]], + }); + + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [sessionObjectives[1]], + offerings: [offerings[2], offerings[3]], + }); + + await page.visit({ + courseId: this.course.id, + }); + + assert.strictEqual(currentURL(), '/courses/1/publishall'); + + const { publishableSessions, unpublishableSessions } = page.publishAllSessions; + + assert.notOk(publishableSessions.isExpanded); + assert.notOk(unpublishableSessions.isExpanded); + + await publishableSessions.toggle(); + assert.strictEqual(currentURL(), '/courses/1/publishall?expandCompleteSessions=true'); + assert.ok(publishableSessions.isExpanded); + assert.notOk(unpublishableSessions.isExpanded); + await unpublishableSessions.toggle(); + assert.strictEqual( + currentURL(), + '/courses/1/publishall?expandCompleteSessions=true&expandIncompleteSessions=true', + ); + assert.ok(publishableSessions.isExpanded); + assert.ok(unpublishableSessions.isExpanded); + await publishableSessions.toggle(); + assert.strictEqual(currentURL(), '/courses/1/publishall?expandIncompleteSessions=true'); + assert.notOk(publishableSessions.isExpanded); + assert.ok(unpublishableSessions.isExpanded); + await unpublishableSessions.toggle(); + assert.strictEqual(currentURL(), '/courses/1/publishall'); + assert.notOk(publishableSessions.isExpanded); + assert.notOk(unpublishableSessions.isExpanded); + }); + + test('publish publishable sessions', async function (assert) { + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + offerings: await this.server.createList('offering', 2), + }); + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + offerings: await this.server.createList('offering', 2), + }); + + await page.coursePage.visit({ + courseId: this.course.id, + }); + + const { sessions } = page.coursePage.courseSessions.sessionsGrid; + assert.strictEqual(sessions.length, 2, 'course sessions length correct'); + assert.strictEqual(sessions[0].row.title, 'session 0', 'first course session title correct'); + assert.strictEqual( + sessions[0].row.publicationStatus.icon.title, + 'Not Published', + 'first session is unpublished', + ); + assert.strictEqual(sessions[1].row.title, 'session 1', 'second course session title correct'); + assert.strictEqual( + sessions[1].row.publicationStatus.icon.title, + 'Not Published', + 'second course session is unpublished', + ); + + await page.visit({ + courseId: this.course.id, + }); + + const { publishableSessions } = page.publishAllSessions; + assert.strictEqual( + publishableSessions.title, + 'Published Sessions (2)', + 'publishable sessions header correct', + ); + await publishableSessions.toggle(); + assert.strictEqual(publishableSessions.sessions.length, 2); + assert.strictEqual(publishableSessions.sessions[0].title, 'session 0'); + assert.strictEqual(publishableSessions.sessions[0].url, '/courses/1/sessions/1'); + assert.strictEqual(publishableSessions.sessions[1].title, 'session 1'); + assert.strictEqual(publishableSessions.sessions[1].url, '/courses/1/sessions/2'); + + assert.strictEqual( + page.publishAllSessions.review.confirmation, + 'Publish 2, schedule 0, and ignore 0 sessions', + ); + + await page.publishAllSessions.review.save(); + assert.strictEqual(sessions.length, 2); + assert.strictEqual(sessions[0].row.title, 'session 0'); + assert.strictEqual(sessions[0].row.publicationStatus.icon.title, 'Published'); + assert.strictEqual(sessions[1].row.title, 'session 1'); + assert.strictEqual(sessions[1].row.publicationStatus.icon.title, 'Published'); + }); + + test('publish overridable sessions #2816', async function (assert) { + await this.server.create('session', { + course: this.course, + sessionType: this.sessionTypes[0], + offerings: await this.server.createList('offering', 2), + }); + await this.server.create('session', { + course: this.course, + sessionType: this.sessionTypes[0], + offerings: await this.server.createList('offering', 2), + }); + + await page.coursePage.visit({ + courseId: this.course.id, + }); + + const { sessions } = page.coursePage.courseSessions.sessionsGrid; + assert.strictEqual(sessions.length, 2); + assert.strictEqual(sessions[0].row.title, 'session 0'); + assert.strictEqual(sessions[0].row.publicationStatus.icon.title, 'Not Published'); + assert.strictEqual(sessions[1].row.title, 'session 1'); + assert.strictEqual(sessions[1].row.publicationStatus.icon.title, 'Not Published'); + + await page.visit({ + courseId: this.course.id, + }); + + const { overridableSessions } = page.publishAllSessions; + + assert.strictEqual(overridableSessions.title, 'Unpublished Sessions: for review (2)'); + assert.ok(overridableSessions.markAllAsScheduled.isVisible); + assert.ok(overridableSessions.publishAllAsIs.isVisible); + const { sessions: list } = overridableSessions; + assert.strictEqual(list.length, 2); + assert.strictEqual(list[0].title, 'session 0'); + assert.strictEqual(list[0].url, '/courses/1/sessions/1'); + assert.notOk(list[0].publishAsIs.isChecked); + assert.ok(list[0].markAsScheduled.isChecked); + assert.strictEqual(list[1].title, 'session 1'); + assert.strictEqual(list[1].url, '/courses/1/sessions/2'); + assert.notOk(list[1].publishAsIs.isChecked); + assert.ok(list[1].markAsScheduled.isChecked); + + await overridableSessions.publishAllAsIs.click(); + assert.ok(list[0].publishAsIs.isChecked); + assert.notOk(list[0].markAsScheduled.isChecked); + assert.ok(list[1].publishAsIs.isChecked); + assert.notOk(list[1].markAsScheduled.isChecked); + + assert.strictEqual( + page.publishAllSessions.review.confirmation, + 'Publish 2, schedule 0, and ignore 0 sessions', + ); + + await page.publishAllSessions.review.save(); + assert.strictEqual(sessions.length, 2); + assert.strictEqual(sessions[0].row.title, 'session 0'); + assert.strictEqual(sessions[0].row.publicationStatus.icon.title, 'Published'); + assert.strictEqual(sessions[1].row.title, 'session 1'); + assert.strictEqual(sessions[1].row.publicationStatus.icon.title, 'Published'); + }); + + test('tables are sortable', async function (assert) { + await this.server.create('session', { + course: this.course, + terms: await this.server.createList('term', 5, { vocabulary: this.vocabulary }), + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + }); + await this.server.create('session', { + course: this.course, + terms: await this.server.createList('term', 3, { vocabulary: this.vocabulary }), + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + }); + await this.server.create('session', { + course: this.course, + terms: await this.server.createList('term', 8, { vocabulary: this.vocabulary }), + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: await this.server.createList('session-objective', 5), + }); + + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + offerings: await this.server.createList('offering', 5), + }); + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + offerings: await this.server.createList('offering', 3), + }); + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + offerings: await this.server.createList('offering', 2), + }); + + await this.server.create('session', { + course: this.course, + sessionType: this.sessionTypes[0], + sessionObjectives: await this.server.createList('session-objective', 7), + offerings: await this.server.createList('offering', 2), + }); + await this.server.create('session', { + course: this.course, + sessionType: this.sessionTypes[0], + sessionObjectives: await this.server.createList('session-objective', 4), + offerings: await this.server.createList('offering', 2), + }); + await this.server.create('session', { + course: this.course, + sessionType: this.sessionTypes[0], + offerings: await this.server.createList('offering', 3), + }); + + await page.visit({ + courseId: this.course.id, + expandIncompleteSessions: true, + expandCompleteSessions: true, + }); + + const pubReview = page.publishAllSessions; + + assert.strictEqual( + currentURL(), + '/courses/1/publishall?expandIncompleteSessions=true&expandCompleteSessions=true', + ); + + // test 1/3 section: Incomplete/Unpublishable Sessions + + assert.ok(pubReview.unpublishableSessions.table.headers.title.isSortedOn); + assert.ok(pubReview.unpublishableSessions.table.headers.title.isSortedAscending); + assert.notOk(pubReview.unpublishableSessions.table.headers.offerings.isSortedOn); + assert.notOk(pubReview.unpublishableSessions.table.headers.terms.isSortedOn); + assert.notOk(pubReview.unpublishableSessions.table.headers.objectives.isSortedOn); + assert.strictEqual(pubReview.unpublishableSessions.sessions[0].title, 'session 0'); + + await pubReview.unpublishableSessions.table.headers.title.click(); + assert.notOk(pubReview.unpublishableSessions.table.headers.title.isSortedAscending); + assert.ok(pubReview.unpublishableSessions.table.headers.title.isSortedDescending); + assert.strictEqual(pubReview.unpublishableSessions.sessions[0].title, 'session 2'); + assert.strictEqual( + currentURL(), + '/courses/1/publishall?expandCompleteSessions=true&expandIncompleteSessions=true&sortIncompleteBy=title%3Adesc', + ); + + await pubReview.unpublishableSessions.table.headers.title.click(); + assert.ok(pubReview.unpublishableSessions.table.headers.title.isSortedAscending); + assert.notOk(pubReview.unpublishableSessions.table.headers.title.isSortedDescending); + assert.strictEqual(pubReview.unpublishableSessions.sessions[0].title, 'session 0'); + assert.strictEqual( + currentURL(), + '/courses/1/publishall?expandCompleteSessions=true&expandIncompleteSessions=true', + ); + + await pubReview.unpublishableSessions.table.headers.offerings.click(); + assert.ok(pubReview.unpublishableSessions.table.headers.offerings.isSortedOn); + assert.notOk(pubReview.unpublishableSessions.table.headers.title.isSortedOn); + assert.ok(pubReview.unpublishableSessions.table.headers.offerings.isSortedAscending); + assert.strictEqual(pubReview.unpublishableSessions.sessions[0].title, 'session 0'); + assert.strictEqual( + currentURL(), + '/courses/1/publishall?expandCompleteSessions=true&expandIncompleteSessions=true&sortIncompleteBy=offerings', + ); + + // test 2/3 section: Complete/Published Sessions + + assert.ok(pubReview.publishableSessions.table.headers.title.isSortedOn); + assert.ok(pubReview.publishableSessions.table.headers.title.isSortedAscending); + assert.notOk(pubReview.publishableSessions.table.headers.offerings.isSortedOn); + assert.notOk(pubReview.publishableSessions.table.headers.terms.isSortedOn); + assert.notOk(pubReview.publishableSessions.table.headers.objectives.isSortedOn); + assert.strictEqual(pubReview.publishableSessions.sessions[0].title, 'session 3'); + + await pubReview.publishableSessions.table.headers.title.click(); + assert.notOk(pubReview.publishableSessions.table.headers.title.isSortedAscending); + assert.ok(pubReview.publishableSessions.table.headers.title.isSortedDescending); + assert.strictEqual(pubReview.publishableSessions.sessions[0].title, 'session 5'); + + await pubReview.publishableSessions.table.headers.title.click(); + assert.ok(pubReview.publishableSessions.table.headers.title.isSortedAscending); + assert.notOk(pubReview.publishableSessions.table.headers.title.isSortedDescending); + assert.strictEqual(pubReview.publishableSessions.sessions[0].title, 'session 3'); + + await pubReview.publishableSessions.table.headers.terms.click(); + assert.ok(pubReview.publishableSessions.table.headers.terms.isSortedOn); + assert.notOk(pubReview.publishableSessions.table.headers.title.isSortedOn); + assert.ok(pubReview.publishableSessions.table.headers.terms.isSortedAscending); + assert.strictEqual(pubReview.publishableSessions.sessions[0].title, 'session 3'); + + // test 3/3 section: Unpublished/Overridable Sessions + + // await this.pauseTest(); + + assert.ok(pubReview.overridableSessions.table.headers.title.isSortedOn); + assert.ok(pubReview.overridableSessions.table.headers.title.isSortedAscending); + assert.notOk(pubReview.overridableSessions.table.headers.offerings.isSortedOn); + assert.notOk(pubReview.overridableSessions.table.headers.terms.isSortedOn); + assert.notOk(pubReview.overridableSessions.table.headers.objectives.isSortedOn); + assert.strictEqual(pubReview.overridableSessions.sessions[0].title, 'session 6'); + + await pubReview.overridableSessions.table.headers.title.click(); + assert.notOk(pubReview.overridableSessions.table.headers.title.isSortedAscending); + assert.ok(pubReview.overridableSessions.table.headers.title.isSortedDescending); + assert.strictEqual(pubReview.overridableSessions.sessions[0].title, 'session 8'); + + await pubReview.overridableSessions.table.headers.title.click(); + assert.ok(pubReview.overridableSessions.table.headers.title.isSortedAscending); + assert.notOk(pubReview.overridableSessions.table.headers.title.isSortedDescending); + assert.strictEqual(pubReview.overridableSessions.sessions[0].title, 'session 6'); + + await pubReview.overridableSessions.table.headers.objectives.click(); + assert.ok(pubReview.overridableSessions.table.headers.objectives.isSortedOn); + assert.notOk(pubReview.overridableSessions.table.headers.title.isSortedOn); + assert.ok(pubReview.overridableSessions.table.headers.objectives.isSortedAscending); + assert.strictEqual(pubReview.overridableSessions.sessions[0].title, 'session 8'); + }); + + test('querystring sorts tables', async function (assert) { + await this.server.create('session', { + course: this.course, + terms: await this.server.createList('term', 5, { vocabulary: this.vocabulary }), + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + }); + await this.server.create('session', { + course: this.course, + terms: await this.server.createList('term', 3, { vocabulary: this.vocabulary }), + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + }); + await this.server.create('session', { + course: this.course, + terms: await this.server.createList('term', 8, { vocabulary: this.vocabulary }), + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: await this.server.createList('session-objective', 5), + }); + + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + offerings: await this.server.createList('offering', 5), + }); + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + offerings: await this.server.createList('offering', 3), + }); + await this.server.create('session', { + course: this.course, + terms: [this.term], + meshDescriptors: [this.meshDescriptor], + sessionType: this.sessionTypes[0], + sessionObjectives: [await this.server.create('session-objective')], + offerings: await this.server.createList('offering', 2), + }); + + await this.server.create('session', { + course: this.course, + sessionType: this.sessionTypes[0], + sessionObjectives: await this.server.createList('session-objective', 7), + offerings: await this.server.createList('offering', 2), + }); + await this.server.create('session', { + course: this.course, + sessionType: this.sessionTypes[0], + sessionObjectives: await this.server.createList('session-objective', 4), + offerings: await this.server.createList('offering', 2), + }); + await this.server.create('session', { + course: this.course, + sessionType: this.sessionTypes[0], + offerings: await this.server.createList('offering', 3), + }); + + await page.visit({ + courseId: this.course.id, + expandIncompleteSessions: true, + expandCompleteSessions: true, + sortIncompleteBy: 'offerings', + sortCompleteBy: 'terms:desc', + sortUnpublishedBy: 'objectives', + }); + + const pubReview = page.publishAllSessions; + + assert.strictEqual( + currentURL(), + '/courses/1/publishall?expandIncompleteSessions=true&expandCompleteSessions=true&sortIncompleteBy=offerings&sortCompleteBy=terms%3Adesc&sortUnpublishedBy=objectives', + ); + assert.notOk(pubReview.unpublishableSessions.table.headers.title.isSortedOn); + assert.ok(pubReview.unpublishableSessions.table.headers.offerings.isSortedOn); + assert.ok(pubReview.unpublishableSessions.table.headers.offerings.isSortedAscending); + assert.notOk(pubReview.unpublishableSessions.table.headers.offerings.isSortedDescending); + assert.strictEqual(pubReview.unpublishableSessions.sessions[0].title, 'session 0'); + + assert.notOk(pubReview.publishableSessions.table.headers.title.isSortedOn); + assert.ok(pubReview.publishableSessions.table.headers.terms.isSortedOn); + assert.notOk(pubReview.publishableSessions.table.headers.terms.isSortedAscending); + assert.ok(pubReview.publishableSessions.table.headers.terms.isSortedDescending); + assert.strictEqual(pubReview.publishableSessions.sessions[0].title, 'session 5'); + + assert.notOk(pubReview.overridableSessions.table.headers.title.isSortedOn); + assert.ok(pubReview.overridableSessions.table.headers.objectives.isSortedOn); + assert.ok(pubReview.overridableSessions.table.headers.objectives.isSortedAscending); + assert.notOk(pubReview.overridableSessions.table.headers.objectives.isSortedDescending); + assert.strictEqual(pubReview.overridableSessions.sessions[0].title, 'session 8'); + }); +}); diff --git a/packages/frontend/tests/acceptance/course/publishall-test.js b/packages/frontend/tests/acceptance/course/publishall-test.js deleted file mode 100644 index b90780bfec..0000000000 --- a/packages/frontend/tests/acceptance/course/publishall-test.js +++ /dev/null @@ -1,171 +0,0 @@ -import { currentURL } from '@ember/test-helpers'; -import { module, test } from 'qunit'; -import { setupAuthentication } from 'ilios-common'; - -import { setupApplicationTest } from 'frontend/tests/helpers'; -import page from 'ilios-common/page-objects/course-publish-all'; -import coursePage from 'ilios-common/page-objects/sessions'; - -module('Acceptance | Course - Publish All Sessions', function (hooks) { - setupApplicationTest(hooks); - hooks.beforeEach(async function () { - this.school = await this.server.create('school'); - this.user = await setupAuthentication({ administeredSchools: [this.school] }, true); - this.cohort = await this.server.create('cohort'); - }); - - test('published sessions do not appear in the cannot publish list #1658', async function (assert) { - const term = await this.server.create('term'); - - const course = await this.server.create('course', { - year: 2013, - school: this.school, - published: true, - cohorts: [this.cohort], - }); - const session1 = await this.server.create('session', { - course, - published: true, - publishedAsTbd: false, - terms: [term], - }); - await this.server.create('session-objective', { session: session1 }); - await this.server.create('offering', { session: session1 }); - const session2 = await this.server.create('session', { - course, - published: true, - publishedAsTbd: false, - terms: [term], - }); - await this.server.create('session-objective', { session: session2 }); - - await this.server.create('offering', { session: session2 }); - await this.server.create('ilm-session', { session: session2 }); - const session3 = await this.server.create('session', { - course, - published: true, - publishedAsTbd: true, - terms: [term], - }); - await this.server.create('session-objective', { session: session3 }); - - await this.server.create('offering', { session: session3 }); - - await page.visit({ - courseId: course.id, - }); - assert.ok(page.publishAll.isVisible); - assert.notOk(page.publishAll.hasUnlinkedWarning); - assert.strictEqual( - page.publishAll.unpublishableSessions.text, - 'Sessions Incomplete: cannot publish (0)', - ); - assert.notOk(page.publishAll.unpublishableSessions.isExpanded); - assert.ok(page.publishAll.unpublishableSessions.canExpandCollapse); - - assert.strictEqual( - page.publishAll.publishableSessions.text, - 'Sessions Complete: ready to publish (3)', - ); - assert.notOk(page.publishAll.publishableSessions.isExpanded); - assert.strictEqual(page.publishAll.publishableSessions.sessions.length, 0); - assert.ok(page.publishAll.publishableSessions.canExpandCollapse); - await page.publishAll.publishableSessions.toggle(); - assert.ok(page.publishAll.publishableSessions.isExpanded); - assert.strictEqual(page.publishAll.publishableSessions.sessions.length, 3); - assert.strictEqual(page.publishAll.publishableSessions.sessions[0].title, 'session 0'); - assert.strictEqual(page.publishAll.publishableSessions.sessions[0].offerings, 'Yes (1)'); - assert.strictEqual(page.publishAll.publishableSessions.sessions[0].terms, 'Yes (1)'); - assert.strictEqual(page.publishAll.publishableSessions.sessions[0].objectives.text, 'Yes (1)'); - assert.ok(page.publishAll.publishableSessions.sessions[0].objectives.isLinked); - - assert.strictEqual(page.publishAll.publishableSessions.sessions[1].title, 'session 1'); - assert.strictEqual(page.publishAll.publishableSessions.sessions[1].offerings, 'Yes (1)'); - assert.strictEqual(page.publishAll.publishableSessions.sessions[1].terms, 'Yes (1)'); - assert.strictEqual(page.publishAll.publishableSessions.sessions[1].objectives.text, 'Yes (1)'); - assert.ok(page.publishAll.publishableSessions.sessions[1].objectives.isLinked); - - assert.strictEqual(page.publishAll.publishableSessions.sessions[2].title, 'session 2'); - assert.strictEqual(page.publishAll.publishableSessions.sessions[2].offerings, 'Yes (1)'); - assert.strictEqual(page.publishAll.publishableSessions.sessions[2].terms, 'Yes (1)'); - assert.strictEqual(page.publishAll.publishableSessions.sessions[2].objectives.text, 'Yes (1)'); - assert.ok(page.publishAll.publishableSessions.sessions[2].objectives.isLinked); - }); - - test('After publishing user is returned to the courses route #4099', async function (assert) { - const terms = await this.server.createList('term', 1); - - const course = await this.server.create('course', { - year: 2013, - school: this.school, - published: true, - cohorts: [this.cohort], - }); - const session = await this.server.create('session', { - course, - published: false, - publishedAsTbd: false, - terms, - }); - await this.server.create('session-objective', { session }); - await this.server.create('session-type', { - sessions: [session], - }); - await this.server.create('offering', { session }); - - await page.visit({ - courseId: course.id, - }); - assert.ok(page.publishAll.isVisible); - await page.publishAll.review.save(); - assert.strictEqual(currentURL(), '/courses/1'); - assert.strictEqual( - coursePage.courseSessions.sessionsGrid.sessions[0].row.publicationStatus.icon.title, - 'Published', - ); - }); - - test('Updating course objectives updates the unlinked objective warning', async function (assert) { - const programYear = await this.server.create('program-year', { - cohort: this.cohort, - }); - await this.server.create('program', { - school: this.school, - programYears: [programYear], - }); - await this.server.create('program-year-objective', { - programYear, - }); - - const course = await this.server.create('course', { - year: 2020, - school: this.school, - published: true, - cohorts: [this.cohort], - }); - await this.server.create('course-objective', { - course, - }); - const session = await this.server.create('session', { - course, - published: false, - publishedAsTbd: false, - }); - await this.server.create('session-type', { - sessions: [session], - }); - await page.visit({ - courseId: course.id, - details: true, - courseObjectiveDetails: true, - }); - assert.ok(page.publishAll.isVisible); - assert.ok(page.publishAll.hasUnlinkedWarning); - - await page.details.objectives.objectiveList.objectives[0].parents.manage(); - const m = page.details.objectives.objectiveList.objectives[0].parentManager; - await m.competencies[0].objectives[0].add(); - await page.details.objectives.objectiveList.objectives[0].parents.save(); - assert.notOk(page.publishAll.hasUnlinkedWarning); - }); -}); diff --git a/packages/frontend/tests/acceptance/course/session/publish-all-test.js b/packages/frontend/tests/acceptance/course/session/publish-all-test.js deleted file mode 100644 index 2f2c7474d5..0000000000 --- a/packages/frontend/tests/acceptance/course/session/publish-all-test.js +++ /dev/null @@ -1,184 +0,0 @@ -import { module, test } from 'qunit'; -import { currentURL } from '@ember/test-helpers'; -import { setupAuthentication } from 'ilios-common'; -import { setupApplicationTest } from 'frontend/tests/helpers'; -import page from 'ilios-common/page-objects/publish-all-sessions'; -import sessionsPage from 'ilios-common/page-objects/sessions'; - -module('Acceptance | Session - Publish All', function (hooks) { - setupApplicationTest(hooks); - hooks.beforeEach(async function () { - const school = await this.server.create('school'); - await setupAuthentication( - { - school, - administeredSchools: [school], - }, - true, - ); - const vocabulary = await this.server.create('vocabulary', { - school, - }); - this.course = await this.server.create('course', { school }); - this.sessionTypes = await this.server.createList('session-type', 2, { - school, - }); - this.term = await this.server.create('term', { vocabulary }); - this.meshDescriptor = await this.server.create('mesh-descriptor'); - }); - - test('expand/collapse state of sections are tracked in url', async function (assert) { - const sessionObjectives = await this.server.createList('session-objective', 2); - const offerings = await this.server.createList('offering', 4); - await this.server.create('session', { - course: this.course, - terms: [this.term], - meshDescriptors: [this.meshDescriptor], - sessionType: this.sessionTypes[0], - sessionObjectives: [sessionObjectives[0]], - offerings: [offerings[0], offerings[1]], - }); - - await this.server.create('session', { - course: this.course, - terms: [this.term], - meshDescriptors: [this.meshDescriptor], - sessionType: this.sessionTypes[0], - sessionObjectives: [sessionObjectives[1]], - offerings: [offerings[2], offerings[3]], - }); - - await page.visit({ courseId: this.course.id }); - assert.strictEqual(currentURL(), '/courses/1/publishall'); - - const { publishableSessions, unpublishableSessions } = page.publishAllSessions; - - assert.notOk(publishableSessions.isExpanded); - assert.notOk(unpublishableSessions.isExpanded); - - await publishableSessions.toggle(); - assert.strictEqual(currentURL(), '/courses/1/publishall?expandCompleteSessions=true'); - assert.ok(publishableSessions.isExpanded); - assert.notOk(unpublishableSessions.isExpanded); - await unpublishableSessions.toggle(); - assert.strictEqual( - currentURL(), - '/courses/1/publishall?expandCompleteSessions=true&expandIncompleteSessions=true', - ); - assert.ok(publishableSessions.isExpanded); - assert.ok(unpublishableSessions.isExpanded); - await publishableSessions.toggle(); - assert.strictEqual(currentURL(), '/courses/1/publishall?expandIncompleteSessions=true'); - assert.notOk(publishableSessions.isExpanded); - assert.ok(unpublishableSessions.isExpanded); - await unpublishableSessions.toggle(); - assert.strictEqual(currentURL(), '/courses/1/publishall'); - assert.notOk(publishableSessions.isExpanded); - assert.notOk(unpublishableSessions.isExpanded); - }); - - test('publish publishable sessions', async function (assert) { - await this.server.create('session', { - course: this.course, - terms: [this.term], - meshDescriptors: [this.meshDescriptor], - sessionType: this.sessionTypes[0], - sessionObjectives: [await this.server.create('session-objective')], - offerings: await this.server.createList('offering', 2), - }); - await this.server.create('session', { - course: this.course, - terms: [this.term], - meshDescriptors: [this.meshDescriptor], - sessionType: this.sessionTypes[0], - sessionObjectives: [await this.server.create('session-objective')], - offerings: await this.server.createList('offering', 2), - }); - - await sessionsPage.visit({ courseId: this.course.id }); - const { sessions } = sessionsPage.courseSessions.sessionsGrid; - assert.strictEqual(sessions.length, 2); - assert.strictEqual(sessions[0].row.title, 'session 0'); - assert.strictEqual(sessions[0].row.publicationStatus.icon.title, 'Not Published'); - assert.strictEqual(sessions[1].row.title, 'session 1'); - assert.strictEqual(sessions[1].row.publicationStatus.icon.title, 'Not Published'); - - await page.visit({ courseId: this.course.id }); - const { publishableSessions } = page.publishAllSessions; - assert.strictEqual(publishableSessions.title, 'Sessions Complete: ready to publish (2)'); - await publishableSessions.toggle(); - assert.strictEqual(publishableSessions.sessions.length, 2); - assert.strictEqual(publishableSessions.sessions[0].title, 'session 0'); - assert.strictEqual(publishableSessions.sessions[0].url, '/courses/1/sessions/1'); - assert.strictEqual(publishableSessions.sessions[1].title, 'session 1'); - assert.strictEqual(publishableSessions.sessions[1].url, '/courses/1/sessions/2'); - - assert.strictEqual( - page.publishAllSessions.review.confirmation, - 'Publish 2, schedule 0, and ignore 0 sessions', - ); - - await page.publishAllSessions.review.save(); - assert.strictEqual(sessions.length, 2); - assert.strictEqual(sessions[0].row.title, 'session 0'); - assert.strictEqual(sessions[0].row.publicationStatus.icon.title, 'Published'); - assert.strictEqual(sessions[1].row.title, 'session 1'); - assert.strictEqual(sessions[1].row.publicationStatus.icon.title, 'Published'); - }); - - test('publish overridable sessions #2816', async function (assert) { - await this.server.create('session', { - course: this.course, - sessionType: this.sessionTypes[0], - offerings: await this.server.createList('offering', 2), - }); - await this.server.create('session', { - course: this.course, - sessionType: this.sessionTypes[0], - offerings: await this.server.createList('offering', 2), - }); - - await sessionsPage.visit({ courseId: this.course.id }); - const { sessions } = sessionsPage.courseSessions.sessionsGrid; - assert.strictEqual(sessions.length, 2); - assert.strictEqual(sessions[0].row.title, 'session 0'); - assert.strictEqual(sessions[0].row.publicationStatus.icon.title, 'Not Published'); - assert.strictEqual(sessions[1].row.title, 'session 1'); - assert.strictEqual(sessions[1].row.publicationStatus.icon.title, 'Not Published'); - - await page.visit({ courseId: this.course.id }); - const { overridableSessions } = page.publishAllSessions; - - assert.strictEqual(overridableSessions.title, 'Sessions Requiring Review (2)'); - assert.ok(overridableSessions.markAllAsScheduled.isVisible); - assert.ok(overridableSessions.publishAllAsIs.isVisible); - const { sessions: list } = overridableSessions; - assert.strictEqual(list.length, 2); - assert.strictEqual(list[0].title, 'session 0'); - assert.strictEqual(list[0].url, '/courses/1/sessions/1'); - assert.notOk(list[0].publishAsIs.isChecked); - assert.ok(list[0].markAsScheduled.isChecked); - assert.strictEqual(list[1].title, 'session 1'); - assert.strictEqual(list[1].url, '/courses/1/sessions/2'); - assert.notOk(list[1].publishAsIs.isChecked); - assert.ok(list[1].markAsScheduled.isChecked); - - await overridableSessions.publishAllAsIs.click(); - assert.ok(list[0].publishAsIs.isChecked); - assert.notOk(list[0].markAsScheduled.isChecked); - assert.ok(list[1].publishAsIs.isChecked); - assert.notOk(list[1].markAsScheduled.isChecked); - - assert.strictEqual( - page.publishAllSessions.review.confirmation, - 'Publish 2, schedule 0, and ignore 0 sessions', - ); - - await page.publishAllSessions.review.save(); - assert.strictEqual(sessions.length, 2); - assert.strictEqual(sessions[0].row.title, 'session 0'); - assert.strictEqual(sessions[0].row.publicationStatus.icon.title, 'Published'); - assert.strictEqual(sessions[1].row.title, 'session 1'); - assert.strictEqual(sessions[1].row.publicationStatus.icon.title, 'Published'); - }); -}); diff --git a/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/course/publish-all-sessions.js b/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/course/publish-all-sessions.js new file mode 100644 index 0000000000..ce8f8d2621 --- /dev/null +++ b/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/course/publish-all-sessions.js @@ -0,0 +1,19 @@ +import { create, visitable } from 'ember-cli-page-object'; + +import courseSessions from './sessions'; +import coursePage from '../../sessions'; +import overview from '../session/overview'; +import details from './details'; +import publishAllSessions from '../publish-all-sessions'; + +export default create({ + visit: visitable('/courses/:courseId/publishall'), + coursePage, + courseSessions, + overview, + backToCourse: { + scope: '[data-test-back-to-course]', + }, + details, + publishAllSessions, +}); diff --git a/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/publish-all-sessions.js b/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/publish-all-sessions.js index 5662172015..213e42e198 100644 --- a/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/publish-all-sessions.js +++ b/packages/ilios-common/addon-test-support/ilios-common/page-objects/components/publish-all-sessions.js @@ -3,7 +3,9 @@ import { collection, create, clickable, + hasClass, isVisible, + notHasClass, property, text, } from 'ember-cli-page-object'; @@ -16,10 +18,48 @@ const definition = { }, unpublishableSessions: { scope: '[data-test-unpublishable]', - title: text('> div > [data-test-title]'), + title: text('> [data-test-title]'), isExpanded: isVisible('[data-test-content]'), canExpandCollapse: isVisible('[data-test-expand-collapse]'), toggle: clickable('[data-test-expand-collapse]'), + table: { + scope: 'table', + headers: { + scope: 'thead', + title: { + scope: 'th:nth-of-type(1)', + isSortedAscending: hasClass('fa-arrow-down-a-z', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-z-a', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + }, + offerings: { + scope: 'th:nth-of-type(2)', + isSortedAscending: hasClass('fa-arrow-down-1-9', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-9-1', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + }, + terms: { + scope: 'th:nth-of-type(3)', + isSortedAscending: hasClass('fa-arrow-down-1-9', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-9-1', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + }, + objectives: { + scope: 'th:nth-of-type(4)', + isSortedAscending: hasClass('fa-arrow-down-1-9', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-9-1', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + isLinked: isVisible('[data-test-session-link]'), + transitionToSession: { + scope: '[data-test-session-link]', + }, + }, + }, + }, sessions: collection('tbody tr', { url: attribute('href', '[data-test-title] a'), title: text('[data-test-title]'), @@ -36,10 +76,48 @@ const definition = { }, publishableSessions: { scope: '[data-test-publishable]', - title: text('> div > [data-test-title]'), + title: text('> [data-test-title]'), isExpanded: isVisible('[data-test-content]'), canExpandCollapse: isVisible('[data-test-expand-collapse]'), toggle: clickable('[data-test-expand-collapse]'), + table: { + scope: 'table', + headers: { + scope: 'thead', + title: { + scope: 'th:nth-of-type(1)', + isSortedAscending: hasClass('fa-arrow-down-a-z', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-z-a', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + }, + offerings: { + scope: 'th:nth-of-type(2)', + isSortedAscending: hasClass('fa-arrow-down-1-9', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-9-1', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + }, + terms: { + scope: 'th:nth-of-type(3)', + isSortedAscending: hasClass('fa-arrow-down-1-9', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-9-1', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + }, + objectives: { + scope: 'th:nth-of-type(4)', + isSortedAscending: hasClass('fa-arrow-down-1-9', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-9-1', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + isLinked: isVisible('[data-test-session-link]'), + transitionToSession: { + scope: '[data-test-session-link]', + }, + }, + }, + }, sessions: collection('tbody tr', { url: attribute('href', '[data-test-title] a'), title: text('[data-test-title]'), @@ -65,6 +143,44 @@ const definition = { scope: '[data-test-mark-all-as-scheduled]', isDisabled: property('disabled'), }, + table: { + scope: 'table', + headers: { + scope: 'thead', + title: { + scope: 'th:nth-of-type(2)', + isSortedAscending: hasClass('fa-arrow-down-a-z', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-z-a', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + }, + offerings: { + scope: 'th:nth-of-type(3)', + isSortedAscending: hasClass('fa-arrow-down-1-9', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-9-1', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + }, + terms: { + scope: 'th:nth-of-type(4)', + isSortedAscending: hasClass('fa-arrow-down-1-9', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-9-1', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + }, + objectives: { + scope: 'th:nth-of-type(5)', + isSortedAscending: hasClass('fa-arrow-down-1-9', 'svg'), + isSortedDescending: hasClass('fa-arrow-down-9-1', 'svg'), + isSortedOn: notHasClass('fa-sort', 'svg'), + click: clickable('button'), + isLinked: isVisible('[data-test-session-link]'), + transitionToSession: { + scope: '[data-test-session-link]', + }, + }, + }, + }, sessions: collection('tbody tr', { publishAsIs: { scope: '[data-test-publish-as-is]', diff --git a/packages/ilios-common/addon-test-support/ilios-common/page-objects/course-publish-all.js b/packages/ilios-common/addon-test-support/ilios-common/page-objects/course-publish-all.js deleted file mode 100644 index 1a40251136..0000000000 --- a/packages/ilios-common/addon-test-support/ilios-common/page-objects/course-publish-all.js +++ /dev/null @@ -1,10 +0,0 @@ -import { create, visitable } from 'ember-cli-page-object'; - -import publishAll from './components/publish-all-sessions'; -import details from './components/course/details'; - -export default create({ - visit: visitable('/courses/:courseId/publishall'), - details, - publishAll, -}); diff --git a/packages/ilios-common/addon-test-support/ilios-common/page-objects/publish-all-sessions.js b/packages/ilios-common/addon-test-support/ilios-common/page-objects/publish-all-sessions.js deleted file mode 100644 index 2a3d61671a..0000000000 --- a/packages/ilios-common/addon-test-support/ilios-common/page-objects/publish-all-sessions.js +++ /dev/null @@ -1,13 +0,0 @@ -import { create, visitable } from 'ember-cli-page-object'; - -import overview from './components/session/overview'; -import publishAllSessions from './components/publish-all-sessions'; - -export default create({ - visit: visitable('/courses/:courseId/publishall'), - overview, - backToCourse: { - scope: '[data-test-back-to-course]', - }, - publishAllSessions, -}); diff --git a/packages/ilios-common/addon/components/publish-all-sessions.gjs b/packages/ilios-common/addon/components/publish-all-sessions.gjs index 795bbf41d9..527b2ba344 100644 --- a/packages/ilios-common/addon/components/publish-all-sessions.gjs +++ b/packages/ilios-common/addon/components/publish-all-sessions.gjs @@ -7,7 +7,7 @@ import { task, timeout } from 'ember-concurrency'; import { TrackedAsyncData } from 'ember-async-data'; import { uniqueValues } from 'ilios-common/utils/array-helpers'; import { on } from '@ember/modifier'; -import { not } from 'ember-truth-helpers'; +import { eq, or, not } from 'ember-truth-helpers'; import t from 'ember-intl/helpers/t'; import FaIcon from '@fortawesome/ember-fontawesome/components/fa-icon'; import { LinkTo } from '@ember/routing'; @@ -17,6 +17,7 @@ import mapBy from 'ilios-common/helpers/map-by'; import SaveButton from 'ilios-common/components/save-button'; import perform from 'ember-concurrency/helpers/perform'; import scrollIntoView from 'ilios-common/modifiers/scroll-into-view'; +import SortableTh from 'ilios-common/components/sortable-th'; import { faLinkSlash, faCaretRight, faCaretDown } from '@fortawesome/free-solid-svg-icons'; export default class PublishAllSessionsComponent extends Component { @@ -111,12 +112,70 @@ export default class PublishAllSessionsComponent extends Component { }); } + get sortedPublishableSessions() { + if (this.args.sortCompleteBy.includes('offerings')) { + return this.publishableSessions.sort((a, b) => a.offerings.length - b.offerings.length); + } + + if (this.args.sortCompleteBy.includes('terms')) { + return this.publishableSessions.sort((a, b) => a.terms.length - b.terms.length); + } + + if (this.args.sortCompleteBy.includes('objectives')) { + return this.publishableSessions.sort( + (a, b) => a.sessionObjectives.length - b.sessionObjectives.length, + ); + } + + const locale = this.intl.get('primaryLocale'); + return this.publishableSessions.sort((a, b) => a.title.localeCompare(b.title, locale)); + } + + get sortedPublishableAscending() { + return this.args.sortCompleteBy.search(/desc/) === -1; + } + + get orderedPublishableSessions() { + return this.sortedPublishableAscending + ? this.sortedPublishableSessions + : this.sortedPublishableSessions.reverse(); + } + get unPublishableSessions() { return this.sessions.filter((session) => { return session.requiredPublicationIssues.length > 0; }); } + get sortedUnPublishableSessions() { + if (this.args.sortIncompleteBy.includes('offerings')) { + return this.unPublishableSessions.sort((a, b) => a.offerings.length - b.offerings.length); + } + + if (this.args.sortIncompleteBy.includes('terms')) { + return this.unPublishableSessions.sort((a, b) => a.terms.length - b.terms.length); + } + + if (this.args.sortIncompleteBy.includes('objectives')) { + return this.unPublishableSessions.sort( + (a, b) => a.sessionObjectives.length - b.sessionObjectives.length, + ); + } + + const locale = this.intl.get('primaryLocale'); + return this.unPublishableSessions.sort((a, b) => a.title.localeCompare(b.title, locale)); + } + + get sortedUnPublishableAscending() { + return this.args.sortIncompleteBy.search(/desc/) === -1; + } + + get orderedUnPublishableSessions() { + return this.sortedUnPublishableAscending + ? this.sortedUnPublishableSessions + : this.sortedUnPublishableSessions.reverse(); + } + get overridableSessions() { return this.sessions.filter((session) => { return ( @@ -126,6 +185,35 @@ export default class PublishAllSessionsComponent extends Component { }); } + get sortedOverridableSessions() { + if (this.args.sortUnpublishedBy.includes('offerings')) { + return this.overridableSessions.sort((a, b) => a.offerings.length - b.offerings.length); + } + + if (this.args.sortUnpublishedBy.includes('terms')) { + return this.overridableSessions.sort((a, b) => a.terms.length - b.terms.length); + } + + if (this.args.sortUnpublishedBy.includes('objectives')) { + return this.overridableSessions.sort( + (a, b) => a.sessionObjectives.length - b.sessionObjectives.length, + ); + } + + const locale = this.intl.get('primaryLocale'); + return this.overridableSessions.sort((a, b) => a.title.localeCompare(b.title, locale)); + } + + get sortedOverridableAscending() { + return this.args.sortUnpublishedBy.search(/desc/) === -1; + } + + get orderedOverridableSessions() { + return this.sortedOverridableAscending + ? this.sortedOverridableSessions + : this.sortedOverridableSessions.reverse(); + } + get publishCount() { return this.publishableSessions.length + this.sessionsToPublish.length; } @@ -165,6 +253,30 @@ export default class PublishAllSessionsComponent extends Component { this.userSelectedSessionsToSchedule = [...this.overridableSessions]; } + @action + setSortIncompleteBy(what) { + if (this.args.sortIncompleteBy === what) { + what += ':desc'; + } + this.args.setSortIncompleteBy(what); + } + + @action + setSortCompleteBy(what) { + if (this.args.sortCompleteBy === what) { + what += ':desc'; + } + this.args.setSortCompleteBy(what); + } + + @action + setSortUnpublishedBy(what) { + if (this.args.sortUnpublishedBy === what) { + what += ':desc'; + } + this.args.setSortUnpublishedBy(what); + } + async saveSomeSessions(sessions) { const chunk = sessions.splice(0, 6); @@ -207,43 +319,74 @@ export default class PublishAllSessionsComponent extends Component {
-
- -
+ + {{#if @expandIncompleteSessions}}
- - - - + - {{#each this.unPublishableSessions as |session|}} + {{#each this.orderedUnPublishableSessions as |session|}} -