Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BI-563] Archive Individual Trait #70

Merged
merged 20 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d6f6c31
[BI-563-1] make dev port for bi-web unique
dmeidlin Jan 26, 2021
8da53ca
[BI-563-1] activate WarningModal when archive event fired from TraitD…
dmeidlin Jan 26, 2021
ad0f65e
[BI-563-1] display active program name in warning modal for archive
dmeidlin Jan 26, 2021
7708d2b
[BI-563-1] close details pane when archive confirmed
dmeidlin Jan 27, 2021
163b205
[BI-563-1] add restore/unarchive link to detail panel
dmeidlin Jan 27, 2021
a1fafaa
[BI-563-1] go to page 1 of trait list after confrming archive
dmeidlin Jan 27, 2021
349f760
[BI-563-1] show success notification after trait archiving
dmeidlin Jan 27, 2021
263819a
[BI-563-1] add archive tag to to bottom of trait detail panel
dmeidlin Jan 27, 2021
a75bb07
[BI-563-1] show archived tag on trait table row
dmeidlin Jan 28, 2021
2f47011
[BI-563-1] fix check for absence of trait id in trait service
dmeidlin Jan 28, 2021
0859784
[BI-563-1] update traits on backend when archiving
dmeidlin Jan 28, 2021
4977594
[BI-563-1] substitute remove/restore in user prompts for correspondin…
dmeidlin Jan 29, 2021
6e6d619
[BI-563] use archivable flag
dmeidlin Jan 29, 2021
dc07223
[BI-563] fix input type
dmeidlin Jan 29, 2021
d5213b3
[BI-563] fix type issue
dmeidlin Jan 29, 2021
e95a7fb
[BI-563] change verbage remove to archive
dmeidlin Feb 8, 2021
3714efa
[BI-732] finish up archive traits on front end
Feb 11, 2021
6d2a5eb
[BI-563] remove unnecessary archivable flag check
dmeidlin Feb 23, 2021
889debb
[BI-563] change dev port to 8080
dmeidlin Feb 23, 2021
edaa341
[BI-563] return to first page after archiving trait
dmeidlin Feb 23, 2021
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
5 changes: 5 additions & 0 deletions src/assets/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,8 @@ div.sentence-input {
fill: none;
stroke-width: 2;
}

.archive-tag {
background: lightgray;

}
10 changes: 10 additions & 0 deletions src/breeding-insight/dao/TraitDAO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,14 @@ export class TraitDAO {
}) as Response;
return new BiResponse(data);
}

static async archiveTrait(programId: string, trait: Trait): Promise<BiResponse> {
const { data } = await api.call({
url: `${process.env.VUE_APP_BI_API_V1_PATH}/programs/${programId}/traits/${trait.id}/archive`,
params: {'active': trait.active},
method: 'put'
}) as Response;
return new BiResponse(data);
}

}
11 changes: 9 additions & 2 deletions src/breeding-insight/model/Trait.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@ export class Trait {
abbreviations?: Array<string>;
synonyms?: Array<string>;
mainAbbreviation?: string;
active?: boolean;

constructor(id?: string,
traitName?: string,
programObservationLevel?: ProgramObservationLevel,
method?: Method,
scale?: Scale,
abbreviations?: Array<string>,
synonyms?: Array<string>
synonyms?: Array<string>,
active?: boolean
) {
this.id = id;
this.traitName = traitName;
Expand All @@ -56,11 +58,16 @@ export class Trait {
}
this.abbreviations = abbreviations;
this.synonyms = synonyms;
if (active !== undefined) {
this.active = active;
} else {
this.active = true;
}
}

static assign(trait: Trait): Trait {
return new Trait(trait.id, trait.traitName, trait.programObservationLevel, trait.method,
trait.scale, trait.abbreviations, trait.synonyms);
trait.scale, trait.abbreviations, trait.synonyms, trait.active);
}

checkStringListEquals(list: string[] | undefined, otherList: string[] | undefined): boolean {
Expand Down
46 changes: 32 additions & 14 deletions src/breeding-insight/service/TraitService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,40 @@ export class TraitService {
else throw 'Unable to create trait';
}

static async updateTraits(programId: string, traits: Trait[]): Promise<[Trait[], Metadata]> {
if (programId && traits) {
// Check that they all have a trait id
if (traits.filter(trait => trait.id).length === 0) {
throw 'Unable to update trait';
static async updateTraits(programId: string, traits: Trait[]): Promise<[Trait[], Metadata]> {
if (programId && traits) {
// Check that they all have a trait id
if (traits.filter(trait => trait.id).length !== traits.length) {
throw 'Unable to update trait';
}

try {
const { result: { data }, metadata } = await TraitDAO.updateTraits(programId, traits);
return [data, metadata];
} catch (error) {
throw TraitUploadService.parseError(error);
}
}
else throw 'Unable to update trait';
}

static async archiveTrait(programId: string, trait: Trait): Promise<Trait> {

try {
const { result: { data }, metadata } = await TraitDAO.updateTraits(programId, traits);
return [data, metadata];
} catch (error) {
throw TraitUploadService.parseError(error);
if (programId && trait) {
// Check that they all have a trait id
if (!trait.id){
throw 'Unable to update trait';
}

try {
const { result, metadata } = await TraitDAO.archiveTrait(programId, trait);
return Trait.assign(result);
} catch (error) {
throw TraitUploadService.parseError(error);
}
}
else throw 'Unable to update trait';
}
else throw 'Unable to update trait';
}

static getAll(programId: string, paginationQuery?: PaginationQuery, full?: boolean): Promise<[Trait[], Metadata]> {
return new Promise<[Trait[], Metadata]>(((resolve, reject) => {
Expand All @@ -64,10 +82,10 @@ export class TraitService {
if (full === undefined) {
full = false;
}

if (programId) {
TraitDAO.getAll(programId, paginationQuery, full).then((biResponse) => {

let traits: Trait[] = [];

if (biResponse.result.data) {
Expand Down
1 change: 1 addition & 0 deletions src/components/tables/SidePanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
components: {}
})
export default class SidePanel extends Vue {

@Prop()
backgroundColorClass!: string;
@Prop()
Expand Down
25 changes: 22 additions & 3 deletions src/components/trait/TraitDetailPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@
<p class="has-text-weight-bold mt-3 mb-0">Description of collection method</p>
<p>{{data.method.description}}</p>

<template v-if="!data.active">
<p class="has-text-weight-bold mt-3 mb-0">Included in Favorites</p>
<b-button
size="is-small"
style="background: lightgray"
class="archive-tag"
v-if="!data.active">
Archived
</b-button>
</template>

<!-- maybe break out controls for reuse eventually -->
<div class="columns is-mobile is-centered pt-6">
<div class="column is-narrow">
Expand All @@ -84,13 +95,21 @@
</div>
<div class="column is-narrow">
<a
v-if="archivable"
v-on:click="$emit('archive')"
v-on:keypress.enter.space="$emit('archive')"
v-if="data.active"
v-on:click="$emit('archive', data)"
v-on:keypress.enter.space="$emit('archive', data)"
tabindex="0"
>
Archive
</a>
<a
v-if="archivable && !data.active"
v-on:click="$emit('restore', data)"
v-on:keypress.enter.space="$emit('restore', data)"
tabindex="0"
>
Restore/Unarchive
</a>
</div>
</div>
</template>
Expand Down
58 changes: 55 additions & 3 deletions src/components/trait/TraitListsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<section id="traitTableLabel">
<WarningModal
v-bind:active.sync="deactivateActive"
v-bind:msg-title="'Remove trait from Program name?'"
v-bind:msg-title="deactivateWarningTitle"
v-on:deactivate="deactivateActive = false"
>
<section>
Expand All @@ -29,8 +29,18 @@
</section>
<div class="columns">
<div class="column is-whole has-text-centered buttons">
<button v-on:click="modalDeleteHandler()" class="button is-danger"><strong>Yes, remove</strong></button>
<button v-on:click="deactivateActive = false" class="button">Cancel</button>
<button
class="button is-danger"
v-on:click="modalDeleteHandler"
>
<strong>Yes, {{ focusTrait.active ? 'archive' : 'restore' }}</strong>
</button>
<button
class="button"
v-on:click="deactivateActive = false"
>
Cancel
</button>
</div>
</div>
</WarningModal>
Expand Down Expand Up @@ -77,6 +87,7 @@
</NewDataForm>

<SidePanelTable
ref="sidePanelTable"
v-bind:records="traits"
v-bind:pagination="traitsPagination"
v-bind:auto-handle-close-panel-event="false"
Expand All @@ -93,6 +104,12 @@
-->
<template v-slot:columns="data">
<TableColumn name="name" v-bind:label="'Name'">
<b-button
size="is-small"
class="archive-tag"
v-if="!data.active && data.active !== undefined">
Archived
</b-button>
{{ data.traitName }}
</TableColumn>
<TableColumn name="level" v-bind:label="'Level'" v-bind:visible="!traitSidePanelState.collapseColumns">
Expand All @@ -118,10 +135,13 @@
v-bind:editable="true"
v-bind:edit-form-state="traitSidePanelState.dataFormState"
v-bind:validation-handler="editValidationHandler"
v-bind:archivable="true"
v-on:activate-edit="activateEdit($event)"
v-on:deactivate-edit="traitSidePanelState.bus.$emit(traitSidePanelState.closePanelEvent)"
v-on:trait-change="editTrait = Trait.assign({...$event})"
v-on:submit="updateTrait"
v-on:archive="activateArchive($event)"
v-on:restore="activateArchive($event)"
/>
</template>

Expand Down Expand Up @@ -205,6 +225,8 @@ export default class TraitTable extends Vue {
private editValidationHandler: ValidationError = new ValidationError();

// Archive trait
private focusTrait: Trait = new Trait();
private deactivateWarningTitle = 'Archive trait in this program?';
private deactivateActive: boolean = false;

// TODO: Move these into an event bus in the future
Expand Down Expand Up @@ -246,6 +268,36 @@ export default class TraitTable extends Vue {
});
}

activateArchive(focusTrait: Trait){
if (focusTrait.active) {
this.deactivateWarningTitle = `Archive "${focusTrait.traitName}" in ${this.activeProgram!.name!}?`;
} else {
this.deactivateWarningTitle = `Restore "${focusTrait.traitName}" to ${this.activeProgram!.name!}?`;
}
this.focusTrait = focusTrait;
this.deactivateActive = true;
}

async modalDeleteHandler(){
try {
const traitClone = JSON.parse(JSON.stringify(this.focusTrait));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never knew that was a way to clone! There is also the Trait.assign function in the trait object if you want a trait class instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assign would probably work in this case, but I generally stay away from it because it can cause errors if a property of interest is deeply nested (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign).

Also, I remember seeing a comparison of computational effort required for the JSON parsing method for deep cloning versus using methods in underscore or other libraries, and JSON parsing came out on top, so I generally use it for cloning.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Just to clarify, the Trait.assign is a custom function of ours that is implemented in our classes.

traitClone.active = !traitClone.active;
const updatedTrait: Trait = await TraitService.archiveTrait(this.activeProgram!.id!, traitClone);

// Replace traits in queried traits
const traitIndex = this.traits.findIndex(trait => trait.id === updatedTrait.id);
if (traitIndex !== -1) { this.traits.splice(traitIndex, 1, updatedTrait); }

this.deactivateActive = false;
this.paginationController.updatePage(1);
this.traitSidePanelState.bus.$emit(this.traitSidePanelState.closePanelEvent);
this.$emit('show-success-notification', `"${traitClone.traitName}" successfully ${ traitClone.active ? 'restored' : 'archived'}`);
} catch(err) {
this.$log.error(err);
this.$emit('show-error-notification', `"${this.focusTrait.traitName}" could not be ${ this.focusTrait.active ? 'archived' : 'restored'}`);
}
}

activateEdit(editTrait: Trait) {
this.traitSidePanelState.bus.$emit(this.traitSidePanelState.activateEditEvent);
this.originalTrait = Trait.assign({...editTrait} as Trait);
Expand Down