From 76647df52707e9eb16df079ce8d5bbe959c0e594 Mon Sep 17 00:00:00 2001 From: loretdemolas Date: Tue, 25 Jun 2024 20:02:01 -0400 Subject: [PATCH 1/9] Fixed a corrupted git like a real dev --- src/app/_type/attribute.type.ts | 8 ++ src/app/_type/phrase.type.ts | 24 ++++ .../_services/attributes.api.service.ts | 9 +- .../_services/attributes.model.service.ts | 11 +- .../attributes-page/attributes.page.html | 8 +- .../attributes-page/attributes.page.module.ts | 6 +- .../pages/attributes-page/attributes.page.ts | 104 +++++++++++++++--- src/environments/environment.dev | 0 8 files changed, 140 insertions(+), 30 deletions(-) create mode 100644 src/app/_type/attribute.type.ts create mode 100644 src/app/_type/phrase.type.ts create mode 100644 src/environments/environment.dev diff --git a/src/app/_type/attribute.type.ts b/src/app/_type/attribute.type.ts new file mode 100644 index 00000000..40ccb577 --- /dev/null +++ b/src/app/_type/attribute.type.ts @@ -0,0 +1,8 @@ +import {Phrase} from './phrase.type'; + +export interface Attribute { + phrase: Phrase; + userCount: number; + sequence: number; + } + diff --git a/src/app/_type/phrase.type.ts b/src/app/_type/phrase.type.ts new file mode 100644 index 00000000..c5effbad --- /dev/null +++ b/src/app/_type/phrase.type.ts @@ -0,0 +1,24 @@ +export type Phrase = { + id: number; + adverb: string; + verb: string; + preposition: string; + noun: string; + } + + + /* + getAttributes() + + { + "phrase": { + "id": 2, + "adverb": "", + "verb": "plays", + "preposition": "", + "noun": "chess", + "sequence": 2 + }, + "userCount": 2 + } +*/ 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 f9f60aba..890ffd65 100644 --- a/src/app/pages/attributes-page/_services/attributes.api.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.api.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import {AuthService, JWTApiService} from '@savvato-software/savvato-javascript-services'; +import {Attribute} from '../../../_type/attribute.type' import { environment } from '../../../_environments/environment'; @@ -34,12 +35,12 @@ export class AttributesApiService { 'preposition': model['inputPrepositionText'], 'noun': model['inputNounText'] }; - + return new Promise((resolve, reject) => { this._apiService.post(url, data).subscribe( (response: any) => { const isPhraseReviewed = response; - + resolve(isPhraseReviewed); }, (err) => { @@ -51,7 +52,7 @@ export class AttributesApiService { delete(id: number): Promise { const url = environment.apiUrl + '/api/attributes/?phraseId=' + id + '&userId=' + this._authService.getUser().id; - + return new Promise((resolve, reject) => { this._apiService.delete(url, {}).subscribe( (response: any) => { @@ -64,5 +65,5 @@ export class AttributesApiService { ); }); } - + } 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 8d7794c3..14313849 100644 --- a/src/app/pages/attributes-page/_services/attributes.model.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.model.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import {AuthService, JWTApiService} from '@savvato-software/savvato-javascript-services'; import { AttributesApiService } from "./attributes.api.service"; +import {Attribute} from '../../../_type/attribute.type' import { Constants } from "../../../_constants/constants"; @@ -10,7 +11,7 @@ import { Constants } from "../../../_constants/constants"; }) export class AttributesModelService { - model: any = {}; + model:Attribute[] = []; constructor(private _apiService: JWTApiService, private _authService: AuthService, @@ -22,7 +23,7 @@ export class AttributesModelService { init() { return new Promise((resolve, reject) => { this._attributesApiService.getAttributesByUser().then( - (rtn) => { + (rtn:Attribute[]) => { this.model = rtn; resolve(rtn); } @@ -36,7 +37,7 @@ export class AttributesModelService { } - save(model: {}) { + save(model:Attribute) { return new Promise((resolve, reject) => { this._attributesApiService.save(model).then( (isPhraseReviewed: boolean) => { @@ -48,8 +49,8 @@ export class AttributesModelService { } ); }); - } - + } + delete(id: number): Promise { return this._attributesApiService.delete(id); } diff --git a/src/app/pages/attributes-page/attributes.page.html b/src/app/pages/attributes-page/attributes.page.html index 8657167c..b8a43db1 100644 --- a/src/app/pages/attributes-page/attributes.page.html +++ b/src/app/pages/attributes-page/attributes.page.html @@ -1,8 +1,12 @@ - + + 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.module.ts b/src/app/pages/attributes-page/attributes.page.module.ts index e406d7cf..bbc0161d 100644 --- a/src/app/pages/attributes-page/attributes.page.module.ts +++ b/src/app/pages/attributes-page/attributes.page.module.ts @@ -11,6 +11,7 @@ import { CreateAttributePage } from './create/create'; import { DetailAttributePage } from "./detail/detail"; import { EditAttributePage } from "./edit/edit"; import { SharedComponentsModule } from '../../_shared-components/shared-components/shared-components.module'; +import { SequenceService } from '@savvato-software/savvato-javascript-services'; @NgModule({ imports: [ @@ -25,6 +26,9 @@ import { SharedComponentsModule } from '../../_shared-components/shared-componen CreateAttributePage, DetailAttributePage, EditAttributePage - ] + ], + providers: [ + SequenceService // Provide the service here + ], }) export class AttributesPageModule {} diff --git a/src/app/pages/attributes-page/attributes.page.ts b/src/app/pages/attributes-page/attributes.page.ts index 40e14228..8dd7b5a6 100644 --- a/src/app/pages/attributes-page/attributes.page.ts +++ b/src/app/pages/attributes-page/attributes.page.ts @@ -6,46 +6,93 @@ import { AlertService } from '../../_services/alert/alert.service'; import { UserService } from '../../_services/user/user.service'; 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', templateUrl: './attributes.page.html', styleUrls: ['./attributes.page.scss'], }) + export class AttributesPage implements OnInit { headerPageTitle: string = 'Attributes'; headerPagePrimaryActionButtonText: string = 'Create'; + attributes: Attribute[] = []; + selectedAttr: Attribute | null = null; + originalAttributes: Attribute[] = []; constructor(private _userService: UserService, private _alertService: AlertService, private _loadingService: LoadingService, private _attributesModelService: AttributesModelService, - private router: Router) { } + private router: Router, + private sequenceService: SequenceService + ) { } + + public ngOnInit() { + this.loadAttributes(); + } + + ionViewWillEnter() { + this.loadAttributes(); + } + + loadAttributes() { + this._loadingService.show({message: "..loading.."}).then(() => { + this._attributesModelService.init().then(() => { + this.attributes = this.getAttributes(); + this.originalAttributes = [...this.attributes]; + this._loadingService.dismiss(); + }); + }); + } - public ngOnInit() { - + getAttributes(): Attribute[] { + const attributes: Attribute[] = this._attributesModelService.get(); + return attributes.sort((a, b) => a.sequence - b.sequence); } - ionViewWillEnter() { - this._loadingService.show({message: "..loading.."}).then(() => { - this._attributesModelService.init().then(() => { - this._loadingService.dismiss(); - }) - }) + selectAttribute(attr: Sequenceable) { + this.selectedAttr = attr; + } + + canMoveUp(): boolean { + return this.selectedAttr && this.selectedAttr['phrase'].sequence > 1; + } + + canMoveDown(): boolean { + return this.selectedAttr && this.selectedAttr['phrase'].sequence < this.attributes.length; + } + + hasChanges(): boolean { + return JSON.stringify(this.attributes) !== JSON.stringify(this.originalAttributes); } + moveUp() { + if (this.selectedAttr && this.canMoveUp()) { + this.sequenceService.moveSequenceByOne(this.attributes, this.selectedAttr, this.sequenceService.UP); + } + } - getAttributes() { - const attributes = this._attributesModelService.get(); - return Object.values(attributes); + moveDown() { + if (this.selectedAttr && this.canMoveDown()) { + this.sequenceService.moveSequenceByOne(this.attributes, this.selectedAttr, this.sequenceService.DOWN); + } + } + + saveChanges() { + // not implemented + this.originalAttributes = [...this.attributes]; + console.log('Changes saved:', this.attributes); } - deleteAttribute(id: number) { const self = this; let msg = "Deleting attribute..."; - + self._loadingService.show({message: msg}).then(() => { self._loadingService.dismiss().then(() => { self._alertService.show({ @@ -55,7 +102,7 @@ export class AttributesPage implements OnInit { { text: 'Yes', handler: () => { - + this._attributesModelService.delete(id).then( (response) => { console.log("Call to attributeApiService was successful"); @@ -73,14 +120,14 @@ export class AttributesPage implements OnInit { }, { text: 'No', - role: 'cancel' + role: 'cancel' } ] }) }) }); - } - + } + getAttrString(attr) { let rtn = ""; @@ -114,4 +161,25 @@ export class AttributesPage implements OnInit { this.router.navigate([url], { replaceUrl: true }); } + ionViewWillLeave() { + if (this.hasChanges()) { + this._alertService.show({ + header: 'Unsaved Changes', + message: 'You have unsaved changes. Do you want to save before leaving?', + buttons: [ + { + text: 'Yes', + handler: () => { + this.saveChanges(); + this.navigateTo(); + } + }, + { + text: 'No', + role: 'cancel' + } + ] + }); + } + } } diff --git a/src/environments/environment.dev b/src/environments/environment.dev new file mode 100644 index 00000000..e69de29b From 3a0528d29d66ad2d06fdb3e90bd55d848436e693 Mon Sep 17 00:00:00 2001 From: loretdemolas Date: Tue, 25 Jun 2024 22:04:12 -0400 Subject: [PATCH 2/9] Added logic to move attributes up and down --- .../attributes-page/attributes.page.scss | 3 ++ .../pages/attributes-page/attributes.page.ts | 33 ++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/app/pages/attributes-page/attributes.page.scss b/src/app/pages/attributes-page/attributes.page.scss index e69de29b..60c05b0f 100644 --- a/src/app/pages/attributes-page/attributes.page.scss +++ b/src/app/pages/attributes-page/attributes.page.scss @@ -0,0 +1,3 @@ +.selected { + color: red; /* Change to your desired color */ +} diff --git a/src/app/pages/attributes-page/attributes.page.ts b/src/app/pages/attributes-page/attributes.page.ts index 8dd7b5a6..a18998a1 100644 --- a/src/app/pages/attributes-page/attributes.page.ts +++ b/src/app/pages/attributes-page/attributes.page.ts @@ -9,6 +9,7 @@ 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' +import { ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'app-attributes', @@ -29,8 +30,11 @@ export class AttributesPage implements OnInit { private _loadingService: LoadingService, private _attributesModelService: AttributesModelService, private router: Router, - private sequenceService: SequenceService - ) { } + private sequenceService: SequenceService, + private cd: ChangeDetectorRef + ) { + + } public ngOnInit() { this.loadAttributes(); @@ -55,16 +59,16 @@ export class AttributesPage implements OnInit { return attributes.sort((a, b) => a.sequence - b.sequence); } - selectAttribute(attr: Sequenceable) { + selectAttribute(attr: Attribute) { this.selectedAttr = attr; } canMoveUp(): boolean { - return this.selectedAttr && this.selectedAttr['phrase'].sequence > 1; + return this.selectedAttr && this.selectedAttr.sequence > 1; } canMoveDown(): boolean { - return this.selectedAttr && this.selectedAttr['phrase'].sequence < this.attributes.length; + return this.selectedAttr && this.selectedAttr.sequence < this.attributes.length; } hasChanges(): boolean { @@ -72,14 +76,27 @@ export class AttributesPage implements OnInit { } moveUp() { - if (this.selectedAttr && this.canMoveUp()) { - this.sequenceService.moveSequenceByOne(this.attributes, this.selectedAttr, this.sequenceService.UP); - } + if (this.selectedAttr && this.canMoveUp()) { + console.log("Before move: Attribute", this.selectedAttr); + this.sequenceService.moveSequenceByOne(this.attributes, this.selectedAttr, this.sequenceService.UP); + this.cd.detectChanges(); + console.log("After move: Attribute", this.selectedAttr); + console.log("Moving Up!"); + this.printList() + } + } + + printList(){ + console.log(this.attributes) } moveDown() { if (this.selectedAttr && this.canMoveDown()) { + console.log("Before move: Attribute", this.selectedAttr); this.sequenceService.moveSequenceByOne(this.attributes, this.selectedAttr, this.sequenceService.DOWN); + this.cd.detectChanges(); + console.log("After move: Attribute", this.selectedAttr); + console.log("Moving Down!") } } From d74bf1868492ef49ef5e0eba93c25195b47bedfa Mon Sep 17 00:00:00 2001 From: loretdemolas Date: Tue, 9 Jul 2024 22:11:43 -0400 Subject: [PATCH 3/9] end of night quick commit --- .../_services/attributes.api.service.ts | 17 ++++++++ .../_services/attributes.model.service.ts | 39 +++++++++++++++++- .../attributes-page/attributes.page.html | 3 +- .../pages/attributes-page/attributes.page.ts | 41 +++++++------------ 4 files changed, 70 insertions(+), 30 deletions(-) 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 890ffd65..9fcd7373 100644 --- a/src/app/pages/attributes-page/_services/attributes.api.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.api.service.ts @@ -49,6 +49,23 @@ export class AttributesApiService { ); }); } + saveSequence(data) { + const url = environment.apiUrl + '/api/attributes/update'; + console.log(data); + + return new Promise((resolve, reject) => { + this._apiService.post(url, data).subscribe( + (response: any) => { + resolve(response); + console.log(response) + }, + (err) => { + reject(err); + } + ); + }); + } + delete(id: number): Promise { const url = environment.apiUrl + '/api/attributes/?phraseId=' + id + '&userId=' + this._authService.getUser().id; 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 14313849..31ef3538 100644 --- a/src/app/pages/attributes-page/_services/attributes.model.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.model.service.ts @@ -12,6 +12,7 @@ import { Constants } from "../../../_constants/constants"; export class AttributesModelService { model:Attribute[] = []; + originalAttributes: Attribute[] = []; constructor(private _apiService: JWTApiService, private _authService: AuthService, @@ -24,7 +25,8 @@ export class AttributesModelService { return new Promise((resolve, reject) => { this._attributesApiService.getAttributesByUser().then( (rtn:Attribute[]) => { - this.model = rtn; + this.originalAttributes = rtn; + this.model = [...this.originalAttributes] resolve(rtn); } ) @@ -36,10 +38,13 @@ export class AttributesModelService { return this.model; } + isDirty(): boolean{ + return JSON.stringify(this.model) !== JSON.stringify(this.originalAttributes); + } save(model:Attribute) { return new Promise((resolve, reject) => { - this._attributesApiService.save(model).then( + this._attributesApiService.save(this.model).then( (isPhraseReviewed: boolean) => { console.log("Call to attributeApiService was successful"); resolve(isPhraseReviewed); @@ -50,6 +55,36 @@ export class AttributesModelService { ); }); } +/* { + "phrase": { + "id": 3, + "adverb": "", + "verb": "sculpts", + "preposition": "with", + "noun": "clay" + }, + "userCount": 1, + "sequence": 1 +} */ + saveAttributeSequence() { + return new Promise((resolve, reject) => { + let phrase = this.model.map(item => {return { + 'sequence': item.sequence, + 'phraseId': item.phrase.id + }; + }) + let data = {'phrases': phrase} + this._attributesApiService.saveSequence(data).then( + (isPhraseReviewed: boolean) => { + console.log("Call to attributeApiService was successful"); + resolve(isPhraseReviewed); + }, + (err) => { + reject(err); + } + ); + }); + } delete(id: number): Promise { return this._attributesApiService.delete(id); diff --git a/src/app/pages/attributes-page/attributes.page.html b/src/app/pages/attributes-page/attributes.page.html index b8a43db1..33f1e262 100644 --- a/src/app/pages/attributes-page/attributes.page.html +++ b/src/app/pages/attributes-page/attributes.page.html @@ -3,9 +3,10 @@ Up Down - Save + Save + {{attr['phrase']['adverb'] || ''}} {{attr['phrase']['verb'] || ''}} {{attr['phrase']['preposition'] || ''}} {{attr['phrase']['noun'] || ''}} diff --git a/src/app/pages/attributes-page/attributes.page.ts b/src/app/pages/attributes-page/attributes.page.ts index a18998a1..45a68a56 100644 --- a/src/app/pages/attributes-page/attributes.page.ts +++ b/src/app/pages/attributes-page/attributes.page.ts @@ -21,9 +21,8 @@ export class AttributesPage implements OnInit { headerPageTitle: string = 'Attributes'; headerPagePrimaryActionButtonText: string = 'Create'; - attributes: Attribute[] = []; selectedAttr: Attribute | null = null; - originalAttributes: Attribute[] = []; + constructor(private _userService: UserService, private _alertService: AlertService, @@ -32,9 +31,7 @@ export class AttributesPage implements OnInit { private router: Router, private sequenceService: SequenceService, private cd: ChangeDetectorRef - ) { - - } + ) {} public ngOnInit() { this.loadAttributes(); @@ -47,14 +44,12 @@ export class AttributesPage implements OnInit { loadAttributes() { this._loadingService.show({message: "..loading.."}).then(() => { this._attributesModelService.init().then(() => { - this.attributes = this.getAttributes(); - this.originalAttributes = [...this.attributes]; this._loadingService.dismiss(); }); }); } - getAttributes(): Attribute[] { + get attributes(): Attribute[] { const attributes: Attribute[] = this._attributesModelService.get(); return attributes.sort((a, b) => a.sequence - b.sequence); } @@ -71,38 +66,30 @@ export class AttributesPage implements OnInit { return this.selectedAttr && this.selectedAttr.sequence < this.attributes.length; } - hasChanges(): boolean { - return JSON.stringify(this.attributes) !== JSON.stringify(this.originalAttributes); - } + isDirty(): boolean { + return this._attributesModelService.isDirty(); + } moveUp() { if (this.selectedAttr && this.canMoveUp()) { - console.log("Before move: Attribute", this.selectedAttr); - this.sequenceService.moveSequenceByOne(this.attributes, this.selectedAttr, this.sequenceService.UP); - this.cd.detectChanges(); - console.log("After move: Attribute", this.selectedAttr); + console.log("Before move: Attribute", this._attributesModelService.get()); + this.sequenceService.moveSequenceByOne(this._attributesModelService.get(), this.selectedAttr, this.sequenceService.UP); + console.log("After move: Attribute", this._attributesModelService.get()); console.log("Moving Up!"); - this.printList() } } - printList(){ - console.log(this.attributes) - } - moveDown() { if (this.selectedAttr && this.canMoveDown()) { - console.log("Before move: Attribute", this.selectedAttr); - this.sequenceService.moveSequenceByOne(this.attributes, this.selectedAttr, this.sequenceService.DOWN); - this.cd.detectChanges(); - console.log("After move: Attribute", this.selectedAttr); + console.log("Before move: Attribute", this._attributesModelService.get()); + this.sequenceService.moveSequenceByOne(this._attributesModelService.get(), this.selectedAttr, this.sequenceService.DOWN); + console.log("After move: Attribute", this._attributesModelService.get()); console.log("Moving Down!") } } saveChanges() { - // not implemented - this.originalAttributes = [...this.attributes]; + this._attributesModelService.saveAttributeSequence() console.log('Changes saved:', this.attributes); } @@ -179,7 +166,7 @@ export class AttributesPage implements OnInit { } ionViewWillLeave() { - if (this.hasChanges()) { + if (this.isDirty()) { this._alertService.show({ header: 'Unsaved Changes', message: 'You have unsaved changes. Do you want to save before leaving?', From cfc00d7679dc07c7796b1b764f0efdb08a431523 Mon Sep 17 00:00:00 2001 From: loretdemolas Date: Thu, 25 Jul 2024 21:34:46 -0400 Subject: [PATCH 4/9] a --- .../_services/attributes.api.service.ts | 2 +- .../_services/attributes.model.service.ts | 14 +++++--- .../attributes-page/attributes.page.html | 8 ++--- .../pages/attributes-page/attributes.page.ts | 34 ++++++++++++------- 4 files changed, 36 insertions(+), 22 deletions(-) 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 9fcd7373..2e7e3d5b 100644 --- a/src/app/pages/attributes-page/_services/attributes.api.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.api.service.ts @@ -27,7 +27,7 @@ export class AttributesApiService { } - save(model) { + save(model:Attribute) { const url = environment.apiUrl + '/api/attributes/'; let data = { 'adverb': model['inputAdverbText'], 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 31ef3538..6a2c7891 100644 --- a/src/app/pages/attributes-page/_services/attributes.model.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.model.service.ts @@ -26,7 +26,8 @@ export class AttributesModelService { this._attributesApiService.getAttributesByUser().then( (rtn:Attribute[]) => { this.originalAttributes = rtn; - this.model = [...this.originalAttributes] + this.model = JSON.parse(JSON.stringify(rtn)); + resolve(rtn); } ) @@ -39,12 +40,14 @@ export class AttributesModelService { } isDirty(): boolean{ +// if (!!this.model || !!this.originalAttributes) +// return false return JSON.stringify(this.model) !== JSON.stringify(this.originalAttributes); } save(model:Attribute) { return new Promise((resolve, reject) => { - this._attributesApiService.save(this.model).then( + this._attributesApiService.save(model).then( (isPhraseReviewed: boolean) => { console.log("Call to attributeApiService was successful"); resolve(isPhraseReviewed); @@ -75,9 +78,10 @@ export class AttributesModelService { }) let data = {'phrases': phrase} this._attributesApiService.saveSequence(data).then( - (isPhraseReviewed: boolean) => { - console.log("Call to attributeApiService was successful"); - resolve(isPhraseReviewed); + (booleanMessage: boolean) => { + console.log("Call to attributeApiService was successful(model)"); + resolve(booleanMessage); + console.log(booleanMessage) }, (err) => { reject(err); diff --git a/src/app/pages/attributes-page/attributes.page.html b/src/app/pages/attributes-page/attributes.page.html index 33f1e262..a93f0767 100644 --- a/src/app/pages/attributes-page/attributes.page.html +++ b/src/app/pages/attributes-page/attributes.page.html @@ -1,13 +1,13 @@ - 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 45a68a56..c1ef7634 100644 --- a/src/app/pages/attributes-page/attributes.page.ts +++ b/src/app/pages/attributes-page/attributes.page.ts @@ -37,9 +37,9 @@ export class AttributesPage implements OnInit { this.loadAttributes(); } - ionViewWillEnter() { - this.loadAttributes(); - } +// ionViewWillEnter() { +// this.loadAttributes(); +// } loadAttributes() { this._loadingService.show({message: "..loading.."}).then(() => { @@ -54,6 +54,11 @@ export class AttributesPage implements OnInit { return attributes.sort((a, b) => a.sequence - b.sequence); } + getAttributes():Attribute[] { + const attributes: Attribute[] = this._attributesModelService.get(); + return attributes.sort((a, b) => a.sequence - b.sequence); + } + selectAttribute(attr: Attribute) { this.selectedAttr = attr; } @@ -66,31 +71,36 @@ export class AttributesPage implements OnInit { return this.selectedAttr && this.selectedAttr.sequence < this.attributes.length; } - isDirty(): boolean { + isSaveEnabled(): boolean { + //returns true if there is a change return this._attributesModelService.isDirty(); } moveUp() { if (this.selectedAttr && this.canMoveUp()) { - console.log("Before move: Attribute", this._attributesModelService.get()); this.sequenceService.moveSequenceByOne(this._attributesModelService.get(), this.selectedAttr, this.sequenceService.UP); - console.log("After move: Attribute", this._attributesModelService.get()); - console.log("Moving Up!"); } } moveDown() { if (this.selectedAttr && this.canMoveDown()) { - console.log("Before move: Attribute", this._attributesModelService.get()); this.sequenceService.moveSequenceByOne(this._attributesModelService.get(), this.selectedAttr, this.sequenceService.DOWN); - console.log("After move: Attribute", this._attributesModelService.get()); - console.log("Moving Down!") } } saveChanges() { this._attributesModelService.saveAttributeSequence() - console.log('Changes saved:', this.attributes); + .then((response) => { + console.log("Call to attributeApiService was successful"); + return this._attributesModelService.init() + }) + .then(() => { + this.selectedAttr = null; +// this.navigateTo('attributes'); + }) + .catch((err) => { + console.error("Error saving changes or reloading attributes", err); + }); } deleteAttribute(id: number) { @@ -166,7 +176,7 @@ export class AttributesPage implements OnInit { } ionViewWillLeave() { - if (this.isDirty()) { + if (this._attributesModelService.isDirty()) { this._alertService.show({ header: 'Unsaved Changes', message: 'You have unsaved changes. Do you want to save before leaving?', From 47c5e1f7968147499dbd6baaf3225f7e906e8c3b Mon Sep 17 00:00:00 2001 From: loretdemolas Date: Thu, 1 Aug 2024 21:04:56 -0400 Subject: [PATCH 5/9] fixed issues with model and saving sequences --- .../attributes-page/_services/attributes.model.service.ts | 4 ++++ src/app/pages/attributes-page/attributes.page.ts | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) 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 6a2c7891..1430a88b 100644 --- a/src/app/pages/attributes-page/_services/attributes.model.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.model.service.ts @@ -25,6 +25,7 @@ export class AttributesModelService { return new Promise((resolve, reject) => { this._attributesApiService.getAttributesByUser().then( (rtn:Attribute[]) => { + rtn.sort((a, b) => a.sequence - b.sequence) this.originalAttributes = rtn; this.model = JSON.parse(JSON.stringify(rtn)); @@ -42,6 +43,9 @@ export class AttributesModelService { isDirty(): boolean{ // if (!!this.model || !!this.originalAttributes) // return false + console.log("JSON.stringify(this.model):", JSON.stringify(this.model)) + console.log("JSON.stringify(this.originalAttributes):", JSON.stringify(this.originalAttributes)) + return JSON.stringify(this.model) !== JSON.stringify(this.originalAttributes); } diff --git a/src/app/pages/attributes-page/attributes.page.ts b/src/app/pages/attributes-page/attributes.page.ts index c1ef7634..84bf1f93 100644 --- a/src/app/pages/attributes-page/attributes.page.ts +++ b/src/app/pages/attributes-page/attributes.page.ts @@ -51,12 +51,12 @@ export class AttributesPage implements OnInit { get attributes(): Attribute[] { const attributes: Attribute[] = this._attributesModelService.get(); - return attributes.sort((a, b) => a.sequence - b.sequence); + return attributes; } getAttributes():Attribute[] { const attributes: Attribute[] = this._attributesModelService.get(); - return attributes.sort((a, b) => a.sequence - b.sequence); + return attributes; } selectAttribute(attr: Attribute) { @@ -73,7 +73,9 @@ export class AttributesPage implements OnInit { isSaveEnabled(): boolean { //returns true if there is a change - return this._attributesModelService.isDirty(); + let isDirty = this._attributesModelService.isDirty(); + console.log(isDirty) + return isDirty; } moveUp() { From 4a0ed5f6eb723a42710b4af3e5a1f9b9a72fa74f Mon Sep 17 00:00:00 2001 From: loretdemolas Date: Thu, 15 Aug 2024 19:19:37 -0400 Subject: [PATCH 6/9] Removed comments and added data types to api responses. Cleaned up code --- src/app/_type/phrase.type.ts | 16 --------------- .../_services/attributes.api.service.ts | 7 +++---- .../_services/attributes.model.service.ts | 20 ++----------------- .../attributes-page/attributes.page.scss | 2 +- .../pages/attributes-page/attributes.page.ts | 6 ------ 5 files changed, 6 insertions(+), 45 deletions(-) diff --git a/src/app/_type/phrase.type.ts b/src/app/_type/phrase.type.ts index c5effbad..b0b9c321 100644 --- a/src/app/_type/phrase.type.ts +++ b/src/app/_type/phrase.type.ts @@ -6,19 +6,3 @@ export type Phrase = { noun: string; } - - /* - getAttributes() - - { - "phrase": { - "id": 2, - "adverb": "", - "verb": "plays", - "preposition": "", - "noun": "chess", - "sequence": 2 - }, - "userCount": 2 - } -*/ 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 2e7e3d5b..e1433f9e 100644 --- a/src/app/pages/attributes-page/_services/attributes.api.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.api.service.ts @@ -28,7 +28,7 @@ export class AttributesApiService { save(model:Attribute) { - const url = environment.apiUrl + '/api/attributes/'; + const url = environment.apiUrl + '/api/attributes'; let data = { 'adverb': model['inputAdverbText'], 'verb': model['inputVerbText'], @@ -49,15 +49,14 @@ export class AttributesApiService { ); }); } - saveSequence(data) { + saveSequence(data: Attribute) { const url = environment.apiUrl + '/api/attributes/update'; console.log(data); return new Promise((resolve, reject) => { this._apiService.post(url, data).subscribe( - (response: any) => { + (response: boolean) => { resolve(response); - console.log(response) }, (err) => { reject(err); 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 1430a88b..a4fc1f1e 100644 --- a/src/app/pages/attributes-page/_services/attributes.model.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.model.service.ts @@ -41,11 +41,6 @@ export class AttributesModelService { } isDirty(): boolean{ -// if (!!this.model || !!this.originalAttributes) -// return false - console.log("JSON.stringify(this.model):", JSON.stringify(this.model)) - console.log("JSON.stringify(this.originalAttributes):", JSON.stringify(this.originalAttributes)) - return JSON.stringify(this.model) !== JSON.stringify(this.originalAttributes); } @@ -62,17 +57,7 @@ export class AttributesModelService { ); }); } -/* { - "phrase": { - "id": 3, - "adverb": "", - "verb": "sculpts", - "preposition": "with", - "noun": "clay" - }, - "userCount": 1, - "sequence": 1 -} */ + saveAttributeSequence() { return new Promise((resolve, reject) => { let phrase = this.model.map(item => {return { @@ -83,9 +68,8 @@ export class AttributesModelService { let data = {'phrases': phrase} this._attributesApiService.saveSequence(data).then( (booleanMessage: boolean) => { - console.log("Call to attributeApiService was successful(model)"); resolve(booleanMessage); - console.log(booleanMessage) + console.log("Call to attributeApiService was successful(model) + (booleanMessage)") }, (err) => { reject(err); diff --git a/src/app/pages/attributes-page/attributes.page.scss b/src/app/pages/attributes-page/attributes.page.scss index 60c05b0f..41d069bb 100644 --- a/src/app/pages/attributes-page/attributes.page.scss +++ b/src/app/pages/attributes-page/attributes.page.scss @@ -1,3 +1,3 @@ .selected { - color: red; /* Change to your desired color */ + color: red; } diff --git a/src/app/pages/attributes-page/attributes.page.ts b/src/app/pages/attributes-page/attributes.page.ts index 84bf1f93..f0e33085 100644 --- a/src/app/pages/attributes-page/attributes.page.ts +++ b/src/app/pages/attributes-page/attributes.page.ts @@ -9,7 +9,6 @@ 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' -import { ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'app-attributes', @@ -30,17 +29,12 @@ export class AttributesPage implements OnInit { private _attributesModelService: AttributesModelService, private router: Router, private sequenceService: SequenceService, - private cd: ChangeDetectorRef ) {} public ngOnInit() { this.loadAttributes(); } -// ionViewWillEnter() { -// this.loadAttributes(); -// } - loadAttributes() { this._loadingService.show({message: "..loading.."}).then(() => { this._attributesModelService.init().then(() => { From 203346ddbb0b9380bb6506b6bf1bed3795f83035 Mon Sep 17 00:00:00 2001 From: loretdemolas Date: Thu, 29 Aug 2024 21:12:34 -0400 Subject: [PATCH 7/9] src/app/pages/attributes-page/_services/attributes.api.service.ts -changed type for save sequence method. --- .../pages/attributes-page/_services/attributes.api.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1a519acb..08ad6dc3 100644 --- a/src/app/pages/attributes-page/_services/attributes.api.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.api.service.ts @@ -48,7 +48,7 @@ export class AttributesApiService { ); }); } - saveSequence(data: Attribute) { + saveSequence(data: {phrases: {sequence: number, phraseId: number}[]}) { const url = environment.apiUrl + '/api/attributes/update'; console.log(data); From f4510c3b0a4b4904db828e8e735a29a9467b451b Mon Sep 17 00:00:00 2001 From: loretdemolas Date: Thu, 19 Sep 2024 20:29:00 -0400 Subject: [PATCH 8/9] src/app/pages/attributes-page/attributes.page.html Changed variables to functions --- .../_services/attributes.api.service.ts | 2 +- .../_services/attributes.model.service.ts | 46 ++++++++++++++++--- .../attributes-page/attributes.page.html | 6 +-- .../pages/attributes-page/attributes.page.ts | 25 ++++------ 4 files changed, 54 insertions(+), 25 deletions(-) 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 08ad6dc3..97360d82 100644 --- a/src/app/pages/attributes-page/_services/attributes.api.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.api.service.ts @@ -48,7 +48,7 @@ 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); 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 efb4bad8..9966f618 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 48f01240..d8de80b5 100644 --- a/src/app/pages/attributes-page/attributes.page.html +++ b/src/app/pages/attributes-page/attributes.page.html @@ -1,13 +1,13 @@ - Up - Down + 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 f0e33085..ae34bb78 100644 --- a/src/app/pages/attributes-page/attributes.page.ts +++ b/src/app/pages/attributes-page/attributes.page.ts @@ -43,26 +43,25 @@ 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 { @@ -73,19 +72,15 @@ export class AttributesPage implements OnInit { } 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() From c43053dbc1a503b27e5bcb647264ab2b71b58854 Mon Sep 17 00:00:00 2001 From: loretdemolas Date: Thu, 26 Sep 2024 21:53:31 -0400 Subject: [PATCH 9/9] modified: cypress/e2e/attributes.page.cy.js -Added e2e test for rearranging attributes modified: src/app/pages/attributes-page/_services/attributes.api.service.ts -cleaned and formatted code modified: src/app/pages/attributes-page/attributes.page.html -added data-test tags for usage in test modified: src/app/pages/attributes-page/attributes.page.ts -clean and formatted code --- cypress/e2e/attributes.page.cy.js | 48 ++++++++++++++++++- .../_services/attributes.api.service.ts | 2 - .../attributes-page/attributes.page.html | 9 ++-- .../pages/attributes-page/attributes.page.ts | 3 -- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/cypress/e2e/attributes.page.cy.js b/cypress/e2e/attributes.page.cy.js index 34c03b1b..350ffff6 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 97360d82..46a458c3 100644 --- a/src/app/pages/attributes-page/_services/attributes.api.service.ts +++ b/src/app/pages/attributes-page/_services/attributes.api.service.ts @@ -50,8 +50,6 @@ export class AttributesApiService { } 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/attributes.page.html b/src/app/pages/attributes-page/attributes.page.html index d8de80b5..386c9814 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 ae34bb78..3765f4d4 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() { @@ -67,7 +65,6 @@ export class AttributesPage implements OnInit { isSaveEnabled(): boolean { //returns true if there is a change let isDirty = this._attributesModelService.isDirty(); - console.log(isDirty) return isDirty; }