diff --git a/cypress/e2e/attributes.page.cy.js b/cypress/e2e/attributes.page.cy.js
index 34c03b1..350ffff 100644
--- a/cypress/e2e/attributes.page.cy.js
+++ b/cypress/e2e/attributes.page.cy.js
@@ -7,7 +7,8 @@ describe('Attributes Page', () => {
cy.intercept('GET', 'api/attributes/' + userId, (req) => {
req.reply({
statusCode: 200,
- body: [{"phrase":{"id":3,"adverb":"","verb":"sculpts","preposition":"with","noun":"clay"},"userCount":1},{"phrase":{"id":2,"adverb":"","verb":"plays","preposition":"","noun":"chess"},"userCount":2}]
+ body: [{"phrase":{"id":3,"adverb":"","verb":"sculpts","preposition":"with","noun":"clay"},"sequence":1,"userCount":1},
+ {"phrase":{"id":2,"adverb":"","verb":"plays","preposition":"","noun":"chess"},"sequence":2,"userCount":2}]
});
});
@@ -117,4 +118,49 @@ describe('Attributes Page', () => {
})
})
})
+ describe('Reorder Attributes', () => {
+
+ it('should move an attribute up the list', () => {
+ cy.goToAttributesPage();
+ cy.get('[data-test="attributeItem"]').eq(1).click();
+ cy.get('[data-test="upButton"]').click();
+ cy.get('[data-test="attributeItem"]').eq(0).contains(' plays chess (2 users)x');
+ cy.get('[data-test="attributeItem"]').eq(1).contains( ' sculpts with clay (1 users)x');
+ });
+
+ it('should move an attribute down the list', () => {
+ cy.goToAttributesPage();
+ cy.get('[data-test="attributeItem"]').eq(0).click();
+ cy.get('[data-test="downButton"]').click();
+ cy.get('[data-test="attributeItem"]').eq(0).contains(' plays chess');
+ cy.get('[data-test="attributeItem"]').eq(1).contains( ' sculpts with clay (1 users)x');
+ });
+
+ it('should disable the "Up" button for the first item', () => {
+ cy.goToAttributesPage();
+ cy.get('[data-test="attributeItem"]').eq(0).click();
+ cy.get('[data-test="upButton"]').should('have.attr','disabled');
+ });
+
+ it('should disable the "Down" button for the last item', () => {
+ cy.goToAttributesPage();
+ cy.get('[data-test="attributeItem"]').eq(1).click();
+ cy.get('[data-test="downButton"]').should('have.attr','disabled');
+ });
+
+ it('should enable and save changes after reordering', () => {
+ cy.goToAttributesPage();
+ cy.get('[data-test="attributeItem"]').eq(1).click();
+ cy.get('[data-test="upButton"]').click();
+ cy.get('[data-test="saveButton"]').should('not.be.disabled');
+ cy.intercept('POST', '/api/attributes/update', {
+ statusCode: 200,
+ body: { success: true }
+ }).as('saveAttributesOrder');
+ cy.get('[data-test="saveButton"]').click();
+ cy.wait('@saveAttributesOrder');
+ });
+ });
+
+
})
diff --git a/src/app/pages/attributes-page/_services/attributes.api.service.ts b/src/app/pages/attributes-page/_services/attributes.api.service.ts
index 08ad6dc..46a458c 100644
--- a/src/app/pages/attributes-page/_services/attributes.api.service.ts
+++ b/src/app/pages/attributes-page/_services/attributes.api.service.ts
@@ -48,10 +48,8 @@ export class AttributesApiService {
);
});
}
- saveSequence(data: {phrases: {sequence: number, phraseId: number}[]}) {
+ saveAttributeSequences(data: {phrases: {sequence: number, phraseId: number}[]}) {
const url = environment.apiUrl + '/api/attributes/update';
- console.log(data);
-
return new Promise((resolve, reject) => {
this._apiService.post(url, data).subscribe(
(response: boolean) => {
diff --git a/src/app/pages/attributes-page/_services/attributes.model.service.ts b/src/app/pages/attributes-page/_services/attributes.model.service.ts
index efb4bad..9966f61 100644
--- a/src/app/pages/attributes-page/_services/attributes.model.service.ts
+++ b/src/app/pages/attributes-page/_services/attributes.model.service.ts
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
-import {AuthService, JWTApiService} from '@savvato-software/savvato-javascript-services';
+import {AuthService, JWTApiService, SequenceService} from '@savvato-software/savvato-javascript-services';
import { AttributesApiService } from "./attributes.api.service";
import {Attribute} from '../../../_type/attribute.type'
@@ -13,10 +13,12 @@ export class AttributesModelService {
model:Attribute[] = [];
originalAttributes: Attribute[] = [];
+ selectedAttr: Attribute | null = null;
constructor(private _apiService: JWTApiService,
private _authService: AuthService,
private _attributesApiService: AttributesApiService,
+ private _sequenceService: SequenceService,
private _constants: Constants) {
}
@@ -37,13 +39,45 @@ export class AttributesModelService {
get() {
- return this.model;
+ return this.model.sort((a, b) => a.sequence - b.sequence);
}
isDirty(): boolean{
return JSON.stringify(this.model) !== JSON.stringify(this.originalAttributes);
}
+ setSelectedAttr(attr: Attribute) {
+ this.selectedAttr = attr;
+ }
+
+ isSelected(attr: Attribute): boolean {
+ return this.selectedAttr && attr && (this.selectedAttr.phrase.id === attr.phrase.id);
+ }
+
+ canMoveUp() {
+ if (this.model.length > 0 && this.selectedAttr) {
+ return this._sequenceService.isAbleToMove(this.model, this.selectedAttr, this._sequenceService.UP);
+ }
+
+ return false;
+ }
+
+ canMoveDown() {
+ if (this.model.length > 0 && this.selectedAttr) {
+ return this._sequenceService.isAbleToMove(this.model, this.selectedAttr, this._sequenceService.DOWN);
+ }
+
+ return false;
+ }
+
+ moveUp() {
+ this._sequenceService.moveSequenceByOne(this.model, this.selectedAttr, this._sequenceService.UP);
+ }
+
+ moveDown() {
+ this._sequenceService.moveSequenceByOne(this.model, this.selectedAttr, this._sequenceService.DOWN);
+ }
+
save(model:Attribute) {
return new Promise((resolve, reject) => {
this._attributesApiService.save(model).then(
@@ -58,15 +92,15 @@ export class AttributesModelService {
});
}
- saveAttributeSequence() {
+ saveAttributeSequences() {
return new Promise((resolve, reject) => {
- let phrase = this.model.map(item => {return {
+ let listOfPhraseIdSequencePairs = this.model.map(item => {return {
'sequence': item.sequence,
'phraseId': item.phrase.id
};
})
- let data = {'phrases': phrase}
- this._attributesApiService.saveSequence(data).then(
+ let data = {'phrases': listOfPhraseIdSequencePairs}
+ this._attributesApiService.saveAttributeSequences(data).then(
(booleanMessage: boolean) => {
resolve(booleanMessage);
console.log("Call to attributeApiService was successful(model) + (booleanMessage)")
diff --git a/src/app/pages/attributes-page/attributes.page.html b/src/app/pages/attributes-page/attributes.page.html
index 48f0124..386c981 100644
--- a/src/app/pages/attributes-page/attributes.page.html
+++ b/src/app/pages/attributes-page/attributes.page.html
@@ -1,13 +1,12 @@
- Up
- Down
- Save
+ Up
+ Down
+ Save
-
-
+
{{attr['phrase']['adverb'] || ''}} {{attr['phrase']['verb'] || ''}} {{attr['phrase']['preposition'] || ''}} {{attr['phrase']['noun'] || ''}}
({{attr['userCount'] || '0'}} users)
diff --git a/src/app/pages/attributes-page/attributes.page.ts b/src/app/pages/attributes-page/attributes.page.ts
index f0e3308..3765f4d 100644
--- a/src/app/pages/attributes-page/attributes.page.ts
+++ b/src/app/pages/attributes-page/attributes.page.ts
@@ -8,7 +8,6 @@ import { LoadingService } from "../../_services/loading-spinner/loading.service"
import { AttributesModelService } from "./_services/attributes.model.service";
import { SequenceService, Sequenceable } from '@savvato-software/savvato-javascript-services';
import {Attribute} from '../../_type/attribute.type'
-import {Phrase} from '../../_type/phrase.type'
@Component({
selector: 'app-attributes',
@@ -28,7 +27,6 @@ export class AttributesPage implements OnInit {
private _loadingService: LoadingService,
private _attributesModelService: AttributesModelService,
private router: Router,
- private sequenceService: SequenceService,
) {}
public ngOnInit() {
@@ -43,49 +41,43 @@ export class AttributesPage implements OnInit {
});
}
- get attributes(): Attribute[] {
- const attributes: Attribute[] = this._attributesModelService.get();
- return attributes;
- }
-
getAttributes():Attribute[] {
const attributes: Attribute[] = this._attributesModelService.get();
return attributes;
}
selectAttribute(attr: Attribute) {
- this.selectedAttr = attr;
+ this._attributesModelService.setSelectedAttr(attr);
+ }
+
+ isSelected(attr: Attribute): boolean {
+ return this._attributesModelService.isSelected(attr);
}
canMoveUp(): boolean {
- return this.selectedAttr && this.selectedAttr.sequence > 1;
+ return this._attributesModelService.canMoveUp();
}
canMoveDown(): boolean {
- return this.selectedAttr && this.selectedAttr.sequence < this.attributes.length;
+ return this._attributesModelService.canMoveDown();
}
isSaveEnabled(): boolean {
//returns true if there is a change
let isDirty = this._attributesModelService.isDirty();
- console.log(isDirty)
return isDirty;
}
moveUp() {
- if (this.selectedAttr && this.canMoveUp()) {
- this.sequenceService.moveSequenceByOne(this._attributesModelService.get(), this.selectedAttr, this.sequenceService.UP);
- }
+ this._attributesModelService.moveUp();
}
moveDown() {
- if (this.selectedAttr && this.canMoveDown()) {
- this.sequenceService.moveSequenceByOne(this._attributesModelService.get(), this.selectedAttr, this.sequenceService.DOWN);
- }
+ this._attributesModelService.moveDown();
}
saveChanges() {
- this._attributesModelService.saveAttributeSequence()
+ this._attributesModelService.saveAttributeSequences()
.then((response) => {
console.log("Call to attributeApiService was successful");
return this._attributesModelService.init()