Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions app/models/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import InstitutionModel from './institution';
import NodeModel from './node';
import RegistrationProviderModel from './registration-provider';
import RegistrationSchemaModel, { RegistrationMetadata } from './registration-schema';
import { RevisionActionTrigger } from './revision-action';
import UserModel from './user';

export enum RegistrationReviewStates {
Expand All @@ -29,8 +30,11 @@ export enum RegistrationReviewStates {
type NonActionableStates = RegistrationReviewStates.Initial
| RegistrationReviewStates.Withdrawn | RegistrationReviewStates.Rejected;

export type ReviewsStateToDecisionMap = Exclude<RegistrationReviewStates, NonActionableStates>;
export const reviewsStateToDecisionMap: { [index in ReviewsStateToDecisionMap]: ReviewActionTrigger[] } = {
export type ReviewsStateToDecisionMap =
Exclude<RegistrationReviewStates, NonActionableStates> | RevisionReviewStates.RevisionPendingModeration;
export const reviewsStateToDecisionMap: {
[index in ReviewsStateToDecisionMap]: Array<ReviewActionTrigger | RevisionActionTrigger>
} = {
[RegistrationReviewStates.Accepted]: [ReviewActionTrigger.ForceWithdraw],
[RegistrationReviewStates.Embargo]: [ReviewActionTrigger.ForceWithdraw],
[RegistrationReviewStates.Pending]:
Expand All @@ -39,6 +43,8 @@ export const reviewsStateToDecisionMap: { [index in ReviewsStateToDecisionMap]:
[ReviewActionTrigger.AcceptWithdrawal, ReviewActionTrigger.RejectWithdrawal],
[RegistrationReviewStates.PendingWithdrawRequest]: [ReviewActionTrigger.ForceWithdraw],
[RegistrationReviewStates.PendingEmbargoTermination]: [ReviewActionTrigger.ForceWithdraw],
[RevisionReviewStates.RevisionPendingModeration]:
[RevisionActionTrigger.AcceptRevision, RevisionActionTrigger.RejectRevision],
};

const Validations = buildValidations({
Expand Down
25 changes: 15 additions & 10 deletions app/models/revision-action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { attr, belongsTo, AsyncBelongsTo } from '@ember-data/model';
import { computed } from '@ember/object';
import RevisionModel from 'ember-osf-web/models/revision';
import { inject as service } from '@ember/service';
import Intl from 'ember-intl/services/intl';
import RevisionModel, {RevisionReviewStates} from 'ember-osf-web/models/revision';
import UserModel from 'ember-osf-web/models/user';
import OsfModel from './osf-model';

Expand All @@ -13,18 +15,20 @@ export enum RevisionActionTrigger {
}

const TriggerToPastTenseTranslationKey: Record<RevisionActionTrigger, string> = {
submit_revision: 'submit',
admin_approve_revision: 'approve',
admin_reject_revision: 'reject',
accept_revision: 'accept',
reject_revision: 'reject',
submit_revision: 'registries.revisionActions.triggerPastTense.submit_revision',
admin_approve_revision: 'registries.revisionActions.triggerPastTense.admin_approve_revision',
admin_reject_revision: 'registries.revisionActions.triggerPastTense.admin_reject_revision',
accept_revision: 'registries.revisionActions.triggerPastTense.accept_revision',
reject_revision: 'registries.revisionActions.triggerPastTense.reject_revision',
};

export default class RevisionActionModel extends OsfModel {
@service intl!: Intl;

@attr('string') actionTrigger!: RevisionActionTrigger;
@attr('fixstring') comment!: string;
@attr('string') fromState!: string;
@attr('string') toState!: string;
@attr('string') fromState!: RevisionReviewStates;
@attr('string') toState!: RevisionReviewStates;
@attr('date') dateCreated!: Date;
@attr('date') dateModified!: Date;
@attr('boolean') visible!: boolean;
Expand All @@ -36,8 +40,9 @@ export default class RevisionActionModel extends OsfModel {
target!: AsyncBelongsTo<RevisionModel> & RevisionModel;

@computed('actionTrigger')
get pastTenseActionTrigger(): string {
return TriggerToPastTenseTranslationKey[this.actionTrigger] || '';
get triggerPastTense(): string {
const key = TriggerToPastTenseTranslationKey[this.actionTrigger] || '';
return key ? this.intl.t(key) : '';
}
}

Expand Down
3 changes: 2 additions & 1 deletion app/models/revision.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export default class RevisionModel extends OsfModel {
@belongsTo('registration') registration!: AsyncBelongsTo<RegistrationModel> & RegistrationModel;
@belongsTo('registration-schema')
registrationSchema!: AsyncBelongsTo<RegistrationSchemaModel> & RegistrationSchemaModel;
@hasMany('revision-action') actions!: AsyncHasMany<RevisionActionModel> & RevisionActionModel;
@hasMany('revision-action', { inverse: 'target' })
actions!: AsyncHasMany<RevisionActionModel> | RevisionActionModel[];
}

declare module 'ember-data/types/registries/model' {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ import { tagName } from '@ember-decorators/component';
import Component from '@ember/component';
import { waitFor } from '@ember/test-waiters';
import { restartableTask } from 'ember-concurrency';
import Toast from 'ember-toastr/services/toast';

import { layout } from 'ember-osf-web/decorators/component';
import Registration from 'ember-osf-web/models/registration';
import RevisionModel from 'ember-osf-web/models/revision';
import { getSchemaBlockGroups, SchemaBlock, SchemaBlockGroup } from 'ember-osf-web/packages/registration-schema';
import captureException, { getApiErrorMessage } from 'ember-osf-web/utils/capture-exception';

import template from './template';

@tagName('')
@layout(template)
export default class RegistrationFormViewSchemaBlocks extends Component {
@service store!: Store;
@service toast!: Toast;
// Required parameter
registration?: Registration;
revisionId?: string;
Expand All @@ -29,21 +32,26 @@ export default class RegistrationFormViewSchemaBlocks extends Component {
@restartableTask({ on: 'didReceiveAttrs' })
@waitFor
async fetchSchemaBlocks() {
let revision;
if (this.revisionId) {
revision = await this.store.findRecord('revision', this.revisionId);
try {
let revision;
if (this.revisionId) {
revision = await this.store.findRecord('revision', this.revisionId);
}
this.set('revision', revision);
}
if (this.registration) {
const registrationSchema = await this.registration.registrationSchema;
const responses = revision ? revision.revisionResponses : this.registration.registrationResponses;
const schemaBlocksRef = registrationSchema.hasMany('schemaBlocks');
const schemaBlocks = schemaBlocksRef.ids().length
? schemaBlocksRef.value() : (await registrationSchema.loadAll('schemaBlocks'));
const schemaBlockGroups = getSchemaBlockGroups(schemaBlocks as SchemaBlock[]);
this.set('schemaBlocks', schemaBlocks);
this.set('schemaBlockGroups', schemaBlockGroups);
this.set('responses', responses);
if (this.registration) {
const registrationSchema = await this.registration.registrationSchema;
const responses = revision ? revision.revisionResponses : this.registration.registrationResponses;
const schemaBlocksRef = registrationSchema.hasMany('schemaBlocks');
const schemaBlocks = schemaBlocksRef.ids().length
? schemaBlocksRef.value() : (await registrationSchema.loadAll('schemaBlocks'));
const schemaBlockGroups = getSchemaBlockGroups(schemaBlocks as SchemaBlock[]);
this.set('schemaBlocks', schemaBlocks);
this.set('schemaBlockGroups', schemaBlockGroups);
this.set('responses', responses);
}
} catch (e) {
captureException(e);
this.toast.error(getApiErrorMessage(e));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,74 @@
import { A } from '@ember/array';
import { inject as service } from '@ember/service';
import { waitFor } from '@ember/test-waiters';
import Component from '@glimmer/component';
import { task } from 'ember-concurrency';
import { taskFor } from 'ember-concurrency-ts';
import Toast from 'ember-toastr/services/toast';

import RegistrationModel, { RegistrationReviewStates } from 'ember-osf-web/models/registration';
import RevisionModel, { RevisionReviewStates } from 'ember-osf-web/models/revision';
import captureException, { getApiErrorMessage } from 'ember-osf-web/utils/capture-exception';

const iconMap: Partial<Record<RegistrationReviewStates, string>> = {
const iconMap: Partial<Record<RegistrationReviewStates | RevisionReviewStates.RevisionPendingModeration, string>> = {
[RegistrationReviewStates.Pending]: 'hourglass',
[RegistrationReviewStates.Withdrawn]: 'ban',
[RegistrationReviewStates.Accepted]: 'check',
[RegistrationReviewStates.Rejected]: 'times',
[RegistrationReviewStates.PendingWithdraw]: 'clock',
[RegistrationReviewStates.Embargo]: 'lock',
[RevisionReviewStates.RevisionPendingModeration]: 'hourglass',
};

interface Args {
registration: RegistrationModel;
state: RegistrationReviewStates;
state: RegistrationReviewStates | RevisionReviewStates.RevisionPendingModeration;
}

export default class RegistrationListCard extends Component<Args> {
@service toast!: Toast;
latestRevision?: RevisionModel;

get revisionIsPending() {
return this.args.registration.revisionState === RevisionReviewStates.RevisionPendingModeration;
}

get icon(): string {
const { state } = this.args;
return iconMap[state] || '';
}

get queryParams() {
const defaultQueryParams = {
mode: 'moderator',
};
if (this.args.state === RevisionReviewStates.RevisionPendingModeration &&
this.revisionIsPending && this.latestRevision) {
return {
revisionId: this.latestRevision.id,
...defaultQueryParams,
};
}
return defaultQueryParams;
}

constructor(owner: unknown, args: Args) {
super(owner, args);
if (this.args.state === RevisionReviewStates.RevisionPendingModeration) {
taskFor(this.getLatestRevision).perform();
}
}

@task
@waitFor
async getLatestRevision() {
try {
const revisions = await this.args.registration.queryHasMany('revisions');
this.latestRevision = A(revisions || []).objectAt(0);
} catch (e) {
captureException(e);
this.toast.error(getApiErrorMessage(e));
throw e;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
<div
data-test-registration-list-card
local-class='cardWrapper'
...attributes
>
<FaIcon
data-test-registration-list-card-icon='{{@state}}'
@icon={{this.icon}}
@size='2x'
local-class='filterIcon {{@state}}'
/>
<div local-class='cardInfo'>
<h3
data-test-registration-list-card-title
local-class='registrationTitle'
<ContentPlaceholders as |placeholder|>
{{#if this.getLatestRevision.isRunning}}
{{placeholder.text lines=1}}
{{else}}
<div
data-test-registration-list-card
local-class='cardWrapper'
...attributes
>
{{#if (eq @state 'rejected')}}
{{@registration.title}}
{{else}}
<OsfLink
data-test-registration-title-link
@route='registries.overview'
@models={{array @registration.id}}
@queryParams={{hash mode='moderator'}}
<FaIcon
data-test-registration-list-card-icon='{{@state}}'
@icon={{this.icon}}
@size='2x'
local-class='filterIcon {{@state}}'
/>
<div local-class='cardInfo'>
<h3
data-test-registration-list-card-title
local-class='registrationTitle'
>
{{@registration.title}}
</OsfLink>
{{/if}}
</h3>
<Registries::ReviewActionsList @registration={{@registration}}/>
</div>
</div>
{{#if (eq @state 'rejected')}}
{{@registration.title}}
{{else}}
<OsfLink
data-test-registration-title-link
@route='registries.overview'
@models={{array @registration.id}}
@queryParams={{this.queryParams}}
>
{{@registration.title}}
</OsfLink>
{{/if}}
</h3>
{{#if (eq @state 'revision_pending_moderation')}}
<Registries::ReviewActionsList @revision={{this.latestRevision}} />
{{else}}
<Registries::ReviewActionsList @registration={{@registration}}/>
{{/if}}
</div>
</div>
{{/if}}
</ContentPlaceholders>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Component from '@ember/component';
import { action, computed } from '@ember/object';
import { layout } from 'ember-osf-web/decorators/component';
import { RegistrationReviewStates } from 'ember-osf-web/models/registration';
import { RevisionReviewStates } from 'ember-osf-web/models/revision';
import template from './template';

@layout(template)
Expand All @@ -13,12 +14,17 @@ export default class RegistrationListManager extends Component {

@computed('state', 'sort')
get filterParams() {
let filter = this.state;
const filter: Record<string, string | undefined> = { review_state: this.state, revision_state: undefined };
if (this.state === RegistrationReviewStates.Embargo) {
filter = [RegistrationReviewStates.Embargo, RegistrationReviewStates.PendingEmbargoTermination].toString();
filter.review_state =
[RegistrationReviewStates.Embargo, RegistrationReviewStates.PendingEmbargoTermination].toString();
}
const query: Record<string, string | Record<string, string>> = {
filter: { reviews_state: filter || 'pending' },
if (this.state === RevisionReviewStates.RevisionPendingModeration) {
filter.revision_state = [RevisionReviewStates.RevisionPendingModeration].toString();
filter.review_state = undefined;
}
const query: Record<string, string | Record<string, string | undefined>> = {
filter,
sort: this.sort,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@ import Toast from 'ember-toastr/services/toast';
import RegistrationModel from 'ember-osf-web/models/registration';
import ReviewActionModel from 'ember-osf-web/models/review-action';
import captureException, { getApiErrorMessage } from 'ember-osf-web/utils/capture-exception';
import RevisionModel from 'ember-osf-web/models/revision';
import RevisionActionModel from 'ember-osf-web/models/revision-action';

interface Args {
registration: RegistrationModel;
revision: RevisionModel;
}

export default class ReviewActionsList extends Component<Args> {
@service toast!: Toast;
@service intl!: Intl;

@tracked showFullActionList = false;
@tracked reviewActions?: ReviewActionModel[];
@tracked reviewActions?: Array<ReviewActionModel | RevisionActionModel>;

get showOrHide() {
return this.showFullActionList ? this.intl.t('registries.reviewActionsList.hide')
Expand All @@ -43,7 +46,12 @@ export default class ReviewActionsList extends Component<Args> {
@waitFor
async fetchActions() {
try {
this.reviewActions = (await this.args.registration.reviewActions) as ReviewActionModel[];
if (this.args.registration) {
this.reviewActions = await this.args.registration.reviewActions as ReviewActionModel[];
}
if (this.args.revision) {
this.reviewActions = await this.args.revision.actions as RevisionActionModel[];
}
} catch (e) {
captureException(e);
this.toast.error(getApiErrorMessage(e));
Expand Down
Loading