diff --git a/app/models/registration.ts b/app/models/registration.ts index ba57a314928..0c2e97694a1 100644 --- a/app/models/registration.ts +++ b/app/models/registration.ts @@ -151,7 +151,7 @@ export default class RegistrationModel extends NodeModel.extend(Validations) { originalResponse!: AsyncBelongsTo | SchemaResponseModel; @belongsTo('schema-response', { inverse: null }) - latestResponse!: AsyncBelongsTo | SchemaResponseModel; + latestResponse!: AsyncBelongsTo | SchemaResponseModel; // Latest accepted response // Write-only relationships @belongsTo('draft-registration', { inverse: null }) diff --git a/app/packages/registration-schema/validations.ts b/app/packages/registration-schema/validations.ts index 924bcfaf60d..101d36442ec 100644 --- a/app/packages/registration-schema/validations.ts +++ b/app/packages/registration-schema/validations.ts @@ -1,7 +1,7 @@ import { assert } from '@ember/debug'; import { set } from '@ember/object'; import { ValidationObject, ValidatorFunction } from 'ember-changeset-validations'; -import { validateLength, validatePresence } from 'ember-changeset-validations/validators'; +import { validatePresence } from 'ember-changeset-validations/validators'; import DraftNode from 'ember-osf-web/models/draft-node'; import LicenseModel from 'ember-osf-web/models/license'; @@ -178,11 +178,5 @@ export function buildSchemaResponseValidations() { type: 'blank', })]; set(validationObj, 'revisionJustification', notBlank); - set(validationObj, 'updatedResponseKeys', [validateLength({ - min: 1, - allowBlank: false, - allowNone: false, - type: 'no_updated_responses', - })]); return validationObj; } diff --git a/lib/registries/addon/drafts/draft/-components/right-nav/styles.scss b/lib/registries/addon/drafts/draft/-components/right-nav/styles.scss index 1a154b1d11a..da84a63ee65 100644 --- a/lib/registries/addon/drafts/draft/-components/right-nav/styles.scss +++ b/lib/registries/addon/drafts/draft/-components/right-nav/styles.scss @@ -1,6 +1,7 @@ .NextButton, .ReviewButton, -.BackButton { +.BackButton, +.DeleteButton { button { width: 100%; } diff --git a/lib/registries/addon/drafts/draft/-components/right-nav/template.hbs b/lib/registries/addon/drafts/draft/-components/right-nav/template.hbs index 56b38d5a1dd..fbc700bc749 100644 --- a/lib/registries/addon/drafts/draft/-components/right-nav/template.hbs +++ b/lib/registries/addon/drafts/draft/-components/right-nav/template.hbs @@ -79,3 +79,13 @@ @date={{@draftManager.draftRegistration.datetimeUpdated}} /> + +{{#if @draftManager.currentUserIsAdmin}} + +{{/if}} diff --git a/lib/registries/addon/drafts/draft/draft-registration-manager.ts b/lib/registries/addon/drafts/draft/draft-registration-manager.ts index 9a5b8d0fe3a..67ce80a43b2 100644 --- a/lib/registries/addon/drafts/draft/draft-registration-manager.ts +++ b/lib/registries/addon/drafts/draft/draft-registration-manager.ts @@ -203,6 +203,19 @@ export default class DraftRegistrationManager { } } + @task + @waitFor + async deleteDraft() { + try { + await this.draftRegistration.destroyRecord(); + this.router.transitionTo('registries.my-registrations'); + } catch (e) { + const errorMessage = this.intl.t('registries.drafts.draft.delete_modal.delete_error'); + captureException(e, { errorMessage }); + this.toast.error(getApiErrorMessage(e), errorMessage); + } + } + @action onPageChange(currentPage: number) { if (this.hasVisitedPages) { diff --git a/lib/registries/addon/edit-revision/-components/right-nav/styles.scss b/lib/registries/addon/edit-revision/-components/right-nav/styles.scss index 1a154b1d11a..da84a63ee65 100644 --- a/lib/registries/addon/edit-revision/-components/right-nav/styles.scss +++ b/lib/registries/addon/edit-revision/-components/right-nav/styles.scss @@ -1,6 +1,7 @@ .NextButton, .ReviewButton, -.BackButton { +.BackButton, +.DeleteButton { button { width: 100%; } diff --git a/lib/registries/addon/edit-revision/-components/right-nav/template.hbs b/lib/registries/addon/edit-revision/-components/right-nav/template.hbs index f370bef96d4..5fe1b9f7ada 100644 --- a/lib/registries/addon/edit-revision/-components/right-nav/template.hbs +++ b/lib/registries/addon/edit-revision/-components/right-nav/template.hbs @@ -78,3 +78,13 @@ @date={{@revisionManager.revision.dateModified}} /> + +{{#if @revisionManager.showDeleteButton}} + +{{/if}} diff --git a/lib/registries/addon/edit-revision/controller.ts b/lib/registries/addon/edit-revision/controller.ts index 68ab628bdea..64eeffaa31e 100644 --- a/lib/registries/addon/edit-revision/controller.ts +++ b/lib/registries/addon/edit-revision/controller.ts @@ -1,14 +1,10 @@ import Controller from '@ember/controller'; -import { assert } from '@ember/debug'; import { alias, not } from '@ember/object/computed'; import RouterService from '@ember/routing/router-service'; import { inject as service } from '@ember/service'; -import { waitFor } from '@ember/test-waiters'; -import { task } from 'ember-concurrency'; import IntlService from 'ember-intl/services/intl'; import RegistrationModel from 'ember-osf-web/models/registration'; import SchemaResponseModel from 'ember-osf-web/models/schema-response'; -import captureException, { getApiErrorMessage } from 'ember-osf-web/utils/capture-exception'; import Media from 'ember-responsive'; export default class EditRevisionController extends Controller { @@ -21,19 +17,4 @@ export default class EditRevisionController extends Controller { @alias('model.revisionManager.revision') revision?: SchemaResponseModel; @alias('model.revisionManager.registration') registration?: RegistrationModel; - - @task - @waitFor - async deleteRevision() { - assert('this.revision is required to delete a revision', this.revision); - assert('this.registration is required to redirect after deleting a revision', this.registration); - try { - await this.revision.destroyRecord(); - this.router.transitionTo('registries.overview.index', this.registration.id); - } catch (e) { - const errorMessage = this.intl.t('registries.edit_revision.delete_modal.delete_error'); - captureException(e, { errorMessage }); - this.toast.error(getApiErrorMessage(e), errorMessage); - } - } } diff --git a/lib/registries/addon/edit-revision/justification/styles.scss b/lib/registries/addon/edit-revision/justification/styles.scss index b6911da574a..9bce3039ab2 100644 --- a/lib/registries/addon/edit-revision/justification/styles.scss +++ b/lib/registries/addon/edit-revision/justification/styles.scss @@ -21,7 +21,3 @@ .SchemaBlockLongText { composes: SchemaBlockLongText from '../../drafts/draft/metadata/styles.scss'; } - -.RevisedResponses { - font-style: italic; -} diff --git a/lib/registries/addon/edit-revision/justification/template.hbs b/lib/registries/addon/edit-revision/justification/template.hbs index 25bc13c10f6..bffe62d010e 100644 --- a/lib/registries/addon/edit-revision/justification/template.hbs +++ b/lib/registries/addon/edit-revision/justification/template.hbs @@ -27,20 +27,7 @@ @uniqueID={{reasonFieldId}} /> {{/let}} - {{#let (unique-id 'questions') as |questionsFieldId|}} - - - {{/let}} -{{/if}} \ No newline at end of file +{{/if}} diff --git a/lib/registries/addon/edit-revision/revision-manager.ts b/lib/registries/addon/edit-revision/revision-manager.ts index 47f53bd01db..436caff3f3e 100644 --- a/lib/registries/addon/edit-revision/revision-manager.ts +++ b/lib/registries/addon/edit-revision/revision-manager.ts @@ -1,3 +1,4 @@ +import { setOwner } from '@ember/application'; import { action, computed, set } from '@ember/object'; import { dependentKeyCompat } from '@ember/object/compat'; import { alias, filterBy, not, notEmpty, or } from '@ember/object/computed'; @@ -88,6 +89,11 @@ export default class RevisionManager { return this.revision?.reviewsState === RevisionReviewStates.RevisionInProgress; } + @computed('currentUserIsAdmin', 'isEditable') + get showDeleteButton() { + return this.currentUserIsAdmin && this.isEditable; + } + @computed('revision.reviewsState') get isPendingAdminApproval() { return this.revision.reviewsState === RevisionReviewStates.Unapproved; @@ -103,7 +109,8 @@ export default class RevisionManager { return this.registration.currentUserPermissions.includes(Permission.Admin); } - constructor(loadModelsTask: LoadModelsTask, revisionId: string) { + constructor(owner: any, loadModelsTask: LoadModelsTask, revisionId: string) { + setOwner(this, owner); set(this, 'loadModelsTask', loadModelsTask); set(this, 'revisionId', revisionId); taskFor(this.initializePageManagers).perform(); @@ -215,6 +222,19 @@ export default class RevisionManager { } } + @task + @waitFor + async deleteRevision() { + try { + await this.revision.destroyRecord(); + this.router.transitionTo('registries.overview.index', this.registration.id); + } catch (e) { + const errorMessage = this.intl.t('registries.edit_revision.delete_modal.delete_error'); + captureException(e, { errorMessage }); + this.toast.error(getApiErrorMessage(e), errorMessage); + } + } + @action onPageChange(currentPage: number) { if (this.hasVisitedPages) { diff --git a/lib/registries/addon/edit-revision/route.ts b/lib/registries/addon/edit-revision/route.ts index 32957114473..d7ed705a062 100644 --- a/lib/registries/addon/edit-revision/route.ts +++ b/lib/registries/addon/edit-revision/route.ts @@ -1,3 +1,4 @@ +import { getOwner } from '@ember/application'; import Store from '@ember-data/store'; import { action } from '@ember/object'; import Route from '@ember/routing/route'; @@ -53,7 +54,7 @@ export default class EditRevisionRoute extends Route { model(params: { revisionId: string }) { const { revisionId } = params; const loadModelsTask = taskFor(this.loadModels).perform(revisionId) as LoadModelsTask; - const revisionManager = new RevisionManager(loadModelsTask, revisionId); + const revisionManager = new RevisionManager(getOwner(this), loadModelsTask, revisionId); const navigationManager = new RevisionNavigationManager(revisionManager); return { navigationManager, diff --git a/lib/registries/addon/edit-revision/template.hbs b/lib/registries/addon/edit-revision/template.hbs index c3ba9c79644..57a75a6d597 100644 --- a/lib/registries/addon/edit-revision/template.hbs +++ b/lib/registries/addon/edit-revision/template.hbs @@ -76,18 +76,6 @@ @label='{{t 'registries.drafts.draft.review.page_label'}}' @navMode={{leftNav.leftGutterMode}} /> - - - - {{outlet}} diff --git a/mirage/config.ts b/mirage/config.ts index 80e908a1f85..a28b3b95d72 100644 --- a/mirage/config.ts +++ b/mirage/config.ts @@ -136,7 +136,7 @@ export default function(this: Server) { }); osfResource(this, 'draft-registration', { - only: ['index', 'show', 'update'], + only: ['index', 'show', 'update', 'delete'], path: '/draft_registrations', }); this.post('/draft_registrations', createDraftRegistration); diff --git a/tests/engines/registries/acceptance/draft/draft-test.ts b/tests/engines/registries/acceptance/draft/draft-test.ts index 6f01cd5ec41..8338cdca746 100644 --- a/tests/engines/registries/acceptance/draft/draft-test.ts +++ b/tests/engines/registries/acceptance/draft/draft-test.ts @@ -115,6 +115,7 @@ module('Registries | Acceptance | draft form', hooks => { assert.dom('[data-test-goto-register]').doesNotExist('RightNav: Register button not shown'); assert.dom('[data-test-nonadmin-warning-text]').exists('RightNav: Warning non-admins cannot register shown'); assert.dom('[data-test-goto-previous-page]').doesNotExist('RightNav: Back button not shown'); + assert.dom('[data-test-delete-button]').doesNotExist('RightNav: Delete button not shown'); // check metadata and form renderer assert.dom('[data-test-edit-button]').doesNotExist('MetadataRenderer: Edit button not shown'); @@ -170,6 +171,27 @@ module('Registries | Acceptance | draft form', hooks => { assert.equal(currentRouteName(), 'registries.page-not-found', 'At page not found'); }); + test('delete draft', async function( + this: DraftFormTestContext, assert, + ) { + const initiator = server.create('user', 'loggedIn'); + const registrationSchema = server.schema.registrationSchemas.find('testSchema'); + const registration = server.create( + 'draft-registration', { + registrationSchema, + initiator, + branchedFrom: this.branchedFrom, + }, + ); + + await visit(`/registries/drafts/${registration.id}/`); + + assert.equal(currentRouteName(), 'registries.drafts.draft.metadata', 'On metadata page'); + await click('[data-test-delete-button]'); + await click('[data-test-confirm-delete]'); + assert.equal(currentRouteName(), 'registries.my-registrations', 'Reroutes to my-registrations'); + }); + test('left nav controls', async function(this: DraftFormTestContext, assert) { const initiator = server.create('user', 'loggedIn'); const registrationSchema = server.schema.registrationSchemas.find('testSchema'); diff --git a/tests/engines/registries/acceptance/edit-revision/revision-test.ts b/tests/engines/registries/acceptance/edit-revision/revision-test.ts index 772aa479327..2c63d2fc39c 100644 --- a/tests/engines/registries/acceptance/edit-revision/revision-test.ts +++ b/tests/engines/registries/acceptance/edit-revision/revision-test.ts @@ -126,6 +126,7 @@ module('Registries | Acceptance | registries revision', hooks => { assert.dom('[data-test-submit-revision]').doesNotExist('RightNav: Register button not shown'); assert.dom('[data-test-nonadmin-warning-text]').exists('RightNav: Warning non-admins cannot register shown'); assert.dom('[data-test-goto-previous-page]').doesNotExist('RightNav: Back button not shown'); + assert.dom('[data-test-delete-button]').doesNotExist('RightNav: Delete button not shown'); // check form renderer await percySnapshot('Read-only Revision Review page: Desktop'); @@ -158,6 +159,23 @@ module('Registries | Acceptance | registries revision', hooks => { assert.equal(currentRouteName(), 'registries.edit-revision.justification', 'At the expected route'); }); + test('delete revision in progress', async function(this: RevisionTestContext, assert) { + const initiatedBy = server.create('user', 'loggedIn'); + const revision = server.create( + 'schema-response', + { + initiatedBy, + revisionResponses: {}, + registration: this.registration, + }, + ); + await visit(`/registries/revisions/${revision.id}/`); + assert.equal(currentRouteName(), 'registries.edit-revision.justification', 'At the expected route'); + await click('[data-test-delete-button]'); + await click('[data-test-confirm-delete]'); + assert.equal(currentRouteName(), 'registries.overview.index', 'At the expected route'); + }); + test('left nav controls', async function(this: RevisionTestContext, assert) { const initiatedBy = server.create('user', 'loggedIn'); const revision = server.create( @@ -426,7 +444,6 @@ module('Registries | Acceptance | registries revision', hooks => { assert.dom('[data-test-invalid-responses-text]').isVisible('Invalid response text shown'); assert.dom('[data-test-validation-errors="revisionJustification"]').exists('justification invalid'); - assert.dom('[data-test-validation-errors="updatedResponseKeys"]').exists('revised responses invalid'); assert.dom(`[data-test-validation-errors="${deserializeResponseKey('page-one_short-text')}"]`) .exists('short text invalid'); assert.dom(`[data-test-validation-errors="${deserializeResponseKey('page-one_long-text')}"]`) @@ -450,7 +467,6 @@ module('Registries | Acceptance | registries revision', hooks => { await click('[data-test-link="justification"]'); await fillIn('textarea[name="revisionJustification"]', 'Tell the world that ditto is the best'); assert.dom('[data-test-validation-errors="revisionJustification"]').doesNotExist('justification valid'); - assert.dom('[data-test-revised-responses-list]').exists({ count: 1 }, 'revised responses list shown'); // check the review page to see if text is valid again await click('[data-test-link="review"]'); diff --git a/translations/en-us.yml b/translations/en-us.yml index 7670825ef37..e85d784de25 100644 --- a/translations/en-us.yml +++ b/translations/en-us.yml @@ -1188,6 +1188,11 @@ registries: register: Register unable_to_fetch_children_count: 'Unable to fetch project''s components' submit_error: 'Unable to create a registration.' + delete_modal: + title: 'Delete this draft registration' + body: 'Are you sure you want to delete this draft registration?' + button: 'Delete Draft' + delete_error: 'Unable to delete draft registration' edit_revision: revisionJustification: 'Justification for update' updatedResponseKeys: 'List of updated registration questions' @@ -1216,9 +1221,9 @@ registries: reason: 'What additional changes need to be made and why?' submit: 'Submit' delete_modal: - title: 'Delete this update' - body: 'Are you sure to delete this update?' - button: 'Cancel update' + title: 'Delete this draft update' + body: 'Are you sure you want to delete this draft update?' + button: 'Delete Draft Update' delete_error: 'Unable to delete update' index: lead: 'The open registries network'