From f88e5d727ae42d0e27ae7949d0f0d4189dda464d Mon Sep 17 00:00:00 2001 From: sowgro Date: Wed, 2 Apr 2025 21:08:38 -0400 Subject: [incomplete] checkout flow improvement --- .../funding-basket/funding-basket.component.html | 39 +------------- .../funding-basket/funding-basket.component.ts | 60 ++++++---------------- 2 files changed, 18 insertions(+), 81 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html index 52b35c1..bba66a3 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html @@ -1,36 +1,3 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Funding Basket

@@ -56,14 +23,10 @@ {{need.current}}/{{need.maxGoal}} ({{((need.current / need.maxGoal) * 100).toFixed(0)}}%)
- - - -
- + diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts index 847baee..18bb9b8 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts @@ -23,7 +23,6 @@ export class FundingBasketComponent implements OnInit { ) {} @ViewChild("contribution") contribution?: Input; - @Input() isValid: boolean = true; // this is for login rerouting ngOnInit(): void { @@ -37,58 +36,33 @@ export class FundingBasketComponent implements OnInit { } async checkout() { - this.isValid = true; - for (let c of document.querySelectorAll('.contribution')!) { - let contribution = c as HTMLInputElement; - contribution.setAttribute("style", ""); - if (contribution.value == '' || contribution.valueAsNumber <= 0) { - this.isValid = false; + let order: { id: number, quantity: number }[] = [] + for (let contribution of document.querySelectorAll('.contribution')!) { + if (contribution.value == '' || contribution.valueAsNumber <= 0) { contribution.setAttribute("style", "border-color: #ff0000"); this.toastService.sendToast(ToastType.ERROR, "Invalid input in funding basket!") - - setTimeout(() => { - contribution.setAttribute("style", "border-color: #ffffff"); - }, 3000); + return; } + order.push({id: +contribution.id, quantity: contribution.valueAsNumber}); } - // if (this.usersService.getBasket().value != await firstValueFrom(this.usersService.getUser(1)) - // for (let c of this.usersService.getBasket().value) { - // if (c == null) { - // this.isValid = false; - // this.statusText.next("One or more needs have been deleted") - // } else { - // this.statusText.next("test") - // } - // } - if (this.isValid) { - for (let c of document.querySelectorAll('.contribution')!) { - let contribution = c as HTMLInputElement; + + for (let c of document.querySelectorAll('.contribution')!) { + let contribution = c as HTMLInputElement; + try { let need = await firstValueFrom(this.cupboardService.getNeed(+contribution.id)); + await firstValueFrom(this.cupboardService.checkoutNeed(need.id, +contribution.value)); need.current += +contribution.value; this.usersService.removeNeed(+need.id); - this.cupboardService.checkoutNeed(need.id, +contribution.value) - .pipe(catchError((ex, _) => { - if (ex.status == 500) { - this.toastService.sendToast(ToastType.ERROR, 'Fields cannot be blank'); - } else if (ex.status == 400) { - this.toastService.sendToast(ToastType.ERROR, ex.error); - } else { - this.toastService.sendToast(ToastType.ERROR, 'Error on creating need'); - } - return new Observable(); - })) - .subscribe((result) => { - if (result) { - //this.needList?.refresh() - } else { - console.log('need update failed'); - } - this.toastService.sendToast(ToastType.INFO, "Checkout successful"); - }); + this.toastService.sendToast(ToastType.INFO, "Checkout successful"); + } catch (ex: any) { + this.toastService.sendToast(ToastType.ERROR, ex.error); } } } - + resetColor(ev: any) { + console.log(ev); + (ev.target as HTMLInputElement).setAttribute("style", "border-color: unset") + } } -- cgit v1.2.3 From 3e5422b90a09d1ed2cfffae56abf8dbe361f6c27 Mon Sep 17 00:00:00 2001 From: sowgro Date: Wed, 2 Apr 2025 23:27:27 -0400 Subject: [incomplete] rearrange code to begin need-list abstraction --- .../components/cupboard/cupboard.component.html | 88 ++++-- .../app/components/cupboard/cupboard.component.ts | 189 +++++++++++-- ufund-ui/src/app/components/cupboard/sorting.ts | 58 ++++ .../funding-basket/funding-basket.component.html | 70 +---- .../funding-basket/funding-basket.component.ts | 3 +- .../components/need-list/need-list.component.html | 55 ++-- .../components/need-list/need-list.component.ts | 301 +++------------------ 7 files changed, 350 insertions(+), 414 deletions(-) create mode 100644 ufund-ui/src/app/components/cupboard/sorting.ts (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.html b/ufund-ui/src/app/components/cupboard/cupboard.component.html index 37954bb..bdc8a0e 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.html +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.html @@ -3,36 +3,64 @@

Cupboard

- - - -
- -
-
-

Create Need

-
-
-
-
-
-
-
-
-
-
- -
- -
- -
-
-
- -
-
+
+
+
+ +
+
+
+
+ + + + + +
+
+

Search Results({{needs.length - (searchResults | async)?.length}} needs filtered):

+

All Needs

+

No Results Found

+ +
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index 2230cd3..95845e7 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -2,10 +2,12 @@ import {Component, OnInit, ViewChild} from '@angular/core'; import { CupboardService } from '../../services/cupboard.service'; import { Need, GoalType } from '../../models/Need'; import { userType } from '../../models/User'; -import { catchError, of } from 'rxjs'; +import {BehaviorSubject, catchError, of} from 'rxjs'; import { NeedListComponent } from '../need-list/need-list.component'; import {AuthService} from '../../services/auth.service'; import {ToastsService, ToastType} from '../../services/toasts.service'; +import {UsersService} from '../../services/users.service'; +import {SortingAlgoArrays} from './sorting'; @Component({ selector: 'app-cupboard', @@ -13,17 +15,36 @@ import {ToastsService, ToastType} from '../../services/toasts.service'; templateUrl: './cupboard.component.html', styleUrl: './cupboard.component.css' }) - export class CupboardComponent implements OnInit { selectedForm?: string = undefined; - needs: any; + // needs: any; @ViewChild("needList") needList?: NeedListComponent + private searchDelay: any; + selectedNeed: Need | null = null; + needs: Need[] = []; + searchResults = new BehaviorSubject([]); + sortMode: 'Ascending' | 'Descending' = 'Ascending' + // selectedNeed: any = { + // name: '', + // location:'', + // id: null, + // maxGoal: null, + // type: '', + // urgent: false + // }; + selectedNeedId: number | null = null; + // searchResults: any[] = []; + + currentSortAlgo = SortingAlgoArrays.sortByPriority; + // sortSelection: string = 'sortByPriority'; + constructor( private cupboardService: CupboardService, private authService: AuthService, - private toastService: ToastsService + private toastService: ToastsService, + private usersService: UsersService ) {} ngOnInit(): void { @@ -35,21 +56,26 @@ export class CupboardComponent implements OnInit { } } - selectedNeed: any = { - name: '', - location:'', - id: null, - maxGoal: null, - type: '', - urgent: false - }; - selectedNeedId: number | null = null; - searchResults: any[] = []; + refresh() { + this.cupboardService.getNeeds().subscribe(n => { + if (this.sortMode == 'Ascending') { + this.needs = n.sort(this.currentSortAlgo.func); + } else { + this.needs = n.sort(this.currentSortAlgo.func).reverse(); + } + this.searchResults.next(this.needs); + // this.updateVisibleNeeds(); + }); + + const form = document.getElementById('search-form') as HTMLFormElement; + form.reset(); + this.search(null); + } selectForm(name: string) { //get search results from the need list if (this.needList) { - this.searchResults = this.needList.searchResults; + // this.searchResults = this.needList.searchResults; } console.log(this.searchResults) this.selectedForm = name; @@ -63,10 +89,72 @@ export class CupboardComponent implements OnInit { } } + async search(form: any) { + //wait .25 seconds before searching but cancel if another search is made during the wait to prevent too many api calls + + //remove previous search if it exists + if (this.searchDelay) { + clearTimeout(this.searchDelay); + } + if (form) { + this.searchDelay = setTimeout(() => { + + if (form) { + //sorting based on algo selected + // SortingAlgoArrays.forEach(algo => { + // if(algo.name === this.sortSelection) { + // this.currentSortAlgo = SortingAlgoArrays[this.sort]; + // console.log("changed sorting algorithm to: ", algo.name + this.sortMode) + // return + // } + // }); + + const currentSearchValue = form.search; //latest value of the search + this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { + if (this.sortMode == 'Ascending') { + this.searchResults.next(n.sort(this.currentSortAlgo.func)); + } else { + this.searchResults.next(n.sort(this.currentSortAlgo.func).reverse()); + } + // this.updateVisibleNeeds(); + }); + } + }, 250); + } else { + //user has cleared the search bar, we can skip the timeout for a 1/4 second faster response + //clear timeout to stop pending search + clearTimeout(this.searchDelay); + this.searchResults.next(this.needs); + } + } + + changeSortMode(form : any) { + if (this.sortMode == 'Ascending'){ + this.sortMode = 'Descending' + } else { + this.sortMode = 'Ascending' + } + this.search(form) + } + + + + isHelper() { + const type = this.authService.getCurrentUser()?.type; + return type === ("HELPER" as unknown as userType); + } + + changeText(id : number, text : string) { + const span = document.getElementById('hover-status-label-' + id); + if (span) { + span.innerHTML = ' ' + text; + } + } + async updateSearchResults() { if (this.needList) { while (this.selectedForm == 'update') { - this.searchResults = this.needList.searchResults + // this.searchResults = this.needList.searchResults await new Promise(resolve => setTimeout(resolve, 100)); } } @@ -82,6 +170,40 @@ export class CupboardComponent implements OnInit { return type === ("MANAGER" as unknown as userType); } + // -------------- DISPLAY FUNCTIONS -------------- // + + select(need : Need) { + //emit value + // this.currentNeed.emit(need); + if (this.selectedNeed) { + //revert already selected need to previous style + console.log(need.id); + let button = document.getElementById('need-button-' + this.selectedNeed.id); + if (button) { + console.log(button) + button.style.background = 'lightgray'; + button.style.marginLeft = '0%'; + button.style.width = '98%'; + } + button = document.getElementById('need-edit-button-' + this.selectedNeed.id); + if (button) { + button.style.visibility = 'visible'; + } + } + //change selected need to selected style + this.selectedNeed = need; + let button = document.getElementById('need-button-' + need.id); + if (button) { + button.style.background = 'white'; + button.style.marginLeft = '4%'; + button.style.width = '100%'; + } + button = document.getElementById('need-edit-button-' + need.id); + if (button) { + button.style.visibility = 'hidden'; + } + } + submit(form: any) { const need: Need = { name: form.name, @@ -90,7 +212,7 @@ export class CupboardComponent implements OnInit { id: 0, maxGoal: form.maxGoal, type: form.type, - urgent: form.urgent ? true : false, + urgent: !!form.urgent, filterAttributes: [], current: 0, description: form.description @@ -112,7 +234,7 @@ export class CupboardComponent implements OnInit { (result) => { if (result) { console.log("need created successfully"); - this.needList?.refresh() + // this.needList?.refresh() } else { console.log("need creation failed"); } @@ -121,7 +243,36 @@ export class CupboardComponent implements OnInit { ); } - destroy() { + // -------------- CRUD OPERATIONS -------------- // + delete(id : number) { + this.cupboardService.deleteNeed(id).subscribe(() => { + this.toastService.sendToast(ToastType.INFO, "Need deleted.") + this.needs = this.needs.filter(n => n.id !== id) + }) + this.refresh(); } + + add(need: Need) { + const currentUser = this.authService.getCurrentUser(); + //console.log("get current user in angular:", currentUser) + if (currentUser) { + if (!currentUser.basket.includes(need.id)) { + currentUser.basket.push(need.id); + this.usersService.updateUser(currentUser) + .pipe(catchError((err, _) => { + console.error(err); + return of(); + })) + .subscribe(() => { + this.usersService.refreshBasket(); + }); + } else { + this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") + } + } + } + + protected readonly SortingAlgoArrays = SortingAlgoArrays; + protected readonly Object = Object; } diff --git a/ufund-ui/src/app/components/cupboard/sorting.ts b/ufund-ui/src/app/components/cupboard/sorting.ts new file mode 100644 index 0000000..a512f22 --- /dev/null +++ b/ufund-ui/src/app/components/cupboard/sorting.ts @@ -0,0 +1,58 @@ +import {Need} from '../../models/Need'; + +interface sortAlgo { + (a: Need, b: Need): number; +} + +// sort functions +const sortByName: sortAlgo = (a: Need, b: Need): number => { + if(a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { + return -1; + } + return 1; +} + +const sortByGoal: sortAlgo = (a: Need, b: Need): number => { + if(a.maxGoal == b.maxGoal) { + return sortByName(a,b); + } + else if(a.maxGoal > b.maxGoal) { + return -1; + } + return 1; +} + +const sortByCompletion: sortAlgo = (a: Need, b: Need): number => { + if(a.current == b.current) { + return sortByGoal(a,b); + } + else if(a.current > b.current) { + return -1; + } + return 1; +} + +const sortByPriority: sortAlgo = (a: Need, b: Need): number => { + if(a.urgent == b.urgent) { + return sortByGoal(a,b); + } + else if(a.urgent && !b.urgent) { + return -1; + } + return 1; +} + +const sortByLocation: sortAlgo = (a: Need, b: Need): number => { + if(a.location.toLocaleLowerCase() < b.location.toLocaleLowerCase()) { + return -1; + } + return 1; +} + +export const SortingAlgoArrays = { + sortByPriority: { func: sortByPriority, display: ["Highest Priority", "Lowest Priority" ] }, + sortByName: { func: sortByName, display: ["Name (A to Z)", "Name (Z to A)" ] }, + sortByLocation: { func: sortByLocation, display: ["Location (A to Z)", "Location (Z to A)" ] }, + sortByCompletion: { func: sortByCompletion, display: ["Most Completed", "Least Completed" ] }, + sortByGoal: { func: sortByGoal, display: ["Largest Maximum Goal", "Smallest Maximum Goal" ] }, +}; diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html index 52b35c1..a53b47c 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html @@ -1,75 +1,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Funding Basket

-
-
-
-
-
- {{need.name}} - {{need.type}} -
- -
- URGENT - location_on{{need.location}} -
-
- -
- -
- - {{need.current}}/{{need.maxGoal}} ({{((need.current / need.maxGoal) * 100).toFixed(0)}}%) - -
- - - - -
- -
- - -
-
-
+
diff --git a/ufund-ui/src/app/components/need-list/need-list.component.ts b/ufund-ui/src/app/components/need-list/need-list.component.ts index 06a612e..d637005 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.ts +++ b/ufund-ui/src/app/components/need-list/need-list.component.ts @@ -1,282 +1,69 @@ -import {Component, EventEmitter, Output} from '@angular/core'; +import {Component, Input, OnInit} from '@angular/core'; import {Need} from '../../models/Need'; -import {CupboardService} from '../../services/cupboard.service'; -import {UsersService} from '../../services/users.service'; -import {userType} from '../../models/User'; -import {AuthService} from '../../services/auth.service'; -import {catchError, of} from 'rxjs'; -import {ToastsService, ToastType} from '../../services/toasts.service'; - -interface sortAlgo { - (a: Need,b: Need): number; -} - -// sort functions -const sortByName: sortAlgo = (a: Need, b: Need): number => { - if(a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { - return -1; - } - return 1; -} - -const sortByGoal: sortAlgo = (a: Need, b: Need): number => { - if(a.maxGoal == b.maxGoal) { - return sortByName(a,b); - } - else if(a.maxGoal > b.maxGoal) { - return -1; - } - return 1; -} - -const sortByCompletion: sortAlgo = (a: Need, b: Need): number => { - if(a.current == b.current) { - return sortByGoal(a,b); - } - else if(a.current > b.current) { - return -1; - } - return 1; -} - -const sortByPriority: sortAlgo = (a: Need, b: Need): number => { - if(a.urgent == b.urgent) { - return sortByGoal(a,b); - } - else if(a.urgent && !b.urgent) { - return -1; - } - return 1; -} - -const sortByLocation: sortAlgo = (a: Need, b: Need): number => { - if(a.location.toLocaleLowerCase() < b.location.toLocaleLowerCase()) { - return -1; - } - return 1; -} +import {BehaviorSubject, Observable} from 'rxjs'; @Component({ - selector: 'app-need-list', - standalone: false, - templateUrl: './need-list.component.html', - styleUrl: './need-list.component.css' + selector: 'app-need-list', + standalone: false, + templateUrl: './need-list.component.html', + styleUrl: './need-list.component.css' }) -export class NeedListComponent { - selectedNeed: Need | null = null; - needs: Need[] = []; - searchResults: Need[] = []; - visibleNeeds: Need[] = []; - sortMode: 'Ascending' | 'Descending' = 'Ascending' - currentPage: number = 0; - itemsPerPage: number = 5; - totalPages: number = Math.ceil(this.needs.length / this.itemsPerPage); - - decrementPage() { - this.currentPage--; - this.updateVisibleNeeds(); - } - - incrementPage() { - this.currentPage++; - this.updateVisibleNeeds(); - } - - lastPage() { - this.currentPage = this.totalPages - 1 - this.updateVisibleNeeds() - } +export class NeedListComponent implements OnInit { - firstPage() { - this.currentPage = 0 - this.updateVisibleNeeds() - } + @Input({required: true}) needs!: Observable - editNeedsPerPage(amount: number) { - this.itemsPerPage = amount; - this.updateVisibleNeeds(); - } + visibleNeeds: Need[] = []; + currentPage: number = 0; + itemsPerPage: number = 5; + totalPages: number = 0; - updateVisibleNeeds() { - this.totalPages = Math.ceil(this.searchResults.length / this.itemsPerPage); - this.visibleNeeds = this.searchResults.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); - } + constructor( - resetVisibleNeeds() { - this.currentPage = 0; - this.updateVisibleNeeds(); - } + ) {} - currentSortAlgo: sortAlgo = sortByPriority; - sortSelection: string = 'sortByPriority'; + ngOnInit() { + this.needs.subscribe(needs => { + this.totalPages = Math.ceil(needs.length / this.itemsPerPage); + this.totalPages = Math.ceil(needs.length / this.itemsPerPage); + this.visibleNeeds = needs.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); + console.log(needs.length) + }) - SortingAlgoArrays: {func:sortAlgo,name:string, display:string[]}[] = [ - {func:sortByPriority,name:"sortByPriority", display:["Highest Priority", "Lowest Priority"]}, - {func:sortByName,name:"sortByName", display:["Name (A to Z)", "Name (Z to A)"]}, - {func:sortByLocation,name:"sortByLocation", display:["Location (A to Z)", "Location (Z to A)"]}, - {func:sortByCompletion,name:"sortByCompletion", display:["Most Completed", "Least Completed"]}, - {func:sortByGoal,name:"sortByGoal", display:["Largest Maximum Goal", "Smallest Maximum Goal"]}, - ]; - - @Output() currentNeed = new EventEmitter(); - - constructor( - private cupboardService: CupboardService, - private usersService: UsersService, - private authService: AuthService, - private toastService: ToastsService - ) {} - - refresh() { - this.cupboardService.getNeeds().subscribe(n => { - if (this.sortMode == 'Ascending') { - this.needs = n.sort(this.currentSortAlgo); - } else { - this.needs = n.sort(this.currentSortAlgo).reverse(); - } - this.searchResults = this.needs; - this.updateVisibleNeeds(); - }); - - const form = document.getElementById('search-form') as HTMLFormElement; - form.reset(); - this.search(null); } - ngOnInit(): void { - this.refresh() - - } - - changeSortMode(form : any) { - if (this.sortMode == 'Ascending'){ - this.sortMode = 'Descending' - } else { - this.sortMode = 'Ascending' + decrementPage() { + this.currentPage--; + this.updateVisibleNeeds(); } - this.search(form) - } - - private searchDelay: any; - async search(form: any) { - //wait .25 seconds before searching but cancel if another search is made during the wait to prevent too many api calls - - //remove previous search if it exists - if (this.searchDelay) { - clearTimeout(this.searchDelay); + incrementPage() { + this.currentPage++; + this.updateVisibleNeeds(); } - if (form) { - this.searchDelay = setTimeout(() => { - - if (form) { - //sorting based on algo selected - this.SortingAlgoArrays.forEach(algo => { - if(algo.name === this.sortSelection) { - this.currentSortAlgo = algo.func; - console.log("changed sorting algorithm to: ", algo.name + this.sortMode) - return - } - }); - - const currentSearchValue = form.search; //latest value of the search - this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { - if (this.sortMode == 'Ascending') { - this.searchResults = n.sort(this.currentSortAlgo); - } else { - this.searchResults = n.sort(this.currentSortAlgo).reverse(); - } - this.updateVisibleNeeds(); - }); - } - }, 250); - } else { - //user has cleared the search bar, we can skip the timeout for a 1/4 second faster response - //clear timeout to stop pending search - clearTimeout(this.searchDelay); - this.searchResults = this.needs; - } - } - - delete(id : number) { - this.cupboardService.deleteNeed(id).subscribe(() => { - this.toastService.sendToast(ToastType.INFO, "Need deleted.") - this.needs = this.needs.filter(n => n.id !== id) - }) - this.refresh(); - } - isManager() { - const type = this.authService.getCurrentUser()?.type; - return type === ("MANAGER" as unknown as userType); - } - - isHelper() { - const type = this.authService.getCurrentUser()?.type; - return type === ("HELPER" as unknown as userType); - } + lastPage() { + this.currentPage = this.totalPages - 1 + this.updateVisibleNeeds() + } - changeText(id : number, text : string) { - const span = document.getElementById('hover-status-label-' + id); - if (span) { - span.innerHTML = ' ' + text; + firstPage() { + this.currentPage = 0 + this.updateVisibleNeeds() } - } - add(need: Need) { - const currentUser = this.authService.getCurrentUser(); - //console.log("get current user in angular:", currentUser) - if (currentUser) { - if (!currentUser.basket.includes(need.id)) { - currentUser.basket.push(need.id); - this.usersService.updateUser(currentUser) - .pipe(catchError((err, _) => { - console.error(err); - return of(); - })) - .subscribe(() => { - this.usersService.refreshBasket(); - }); - } else { - this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") - } + editNeedsPerPage(amount: number) { + this.itemsPerPage = amount; + this.updateVisibleNeeds(); } - } - back() { - this.searchResults = this.needs; - } + updateVisibleNeeds() { + // this.totalPages = Math.ceil(this.needs.length / this.itemsPerPage); + // this.visibleNeeds = this.needs.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); + } - select(need : Need) { - //emit value - this.currentNeed.emit(need); - if (this.selectedNeed) { - //revert already selected need to previous style - console.log(need.id); - let button = document.getElementById('need-button-' + this.selectedNeed.id); - if (button) { - console.log(button) - button.style.background = 'lightgray'; - button.style.marginLeft = '0%'; - button.style.width = '98%'; - } - button = document.getElementById('need-edit-button-' + this.selectedNeed.id); - if (button) { - button.style.visibility = 'visible'; - } + resetVisibleNeeds() { + this.currentPage = 0; + this.updateVisibleNeeds(); } - //change selected need to selected style - this.selectedNeed = need; - let button = document.getElementById('need-button-' + need.id); - if (button) { - button.style.background = 'white'; - button.style.marginLeft = '4%'; - button.style.width = '100%'; - } - button = document.getElementById('need-edit-button-' + need.id); - if (button) { - button.style.visibility = 'hidden'; - } - } } -- cgit v1.2.3 From cb6463630446503d441b37f3d62ec2d064b00269 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 3 Apr 2025 07:56:54 -0400 Subject: Added dashboard statistics --- .../components/dashboard/dashboard.component.ts | 37 +++++++++++++++------- ufund-ui/src/app/services/users.service.ts | 13 ++++++++ 2 files changed, 39 insertions(+), 11 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.ts b/ufund-ui/src/app/components/dashboard/dashboard.component.ts index c94b5c6..8c397ff 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.ts +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.ts @@ -1,10 +1,10 @@ import {Component, OnInit} from '@angular/core'; import {AuthService} from '../../services/auth.service'; import {Router} from '@angular/router'; -import {Need} from '../../models/Need'; import {CupboardService} from '../../services/cupboard.service'; -import {firstValueFrom} from 'rxjs'; import {UsersService} from '../../services/users.service'; +import {BehaviorSubject} from 'rxjs'; +import {GoalType, Need} from '../../models/Need'; @Component({ selector: 'app-dashboard', @@ -14,9 +14,10 @@ import {UsersService} from '../../services/users.service'; }) export class DashboardComponent implements OnInit{ - topNeeds?: Need[] - almostThere?: Need[] - inBasket?: Need[] + protected count = new BehaviorSubject(undefined) + protected totalDonations = new BehaviorSubject(undefined) + protected fulfilledNeeds = new BehaviorSubject(undefined) + protected mostFulfilledNeeds = new BehaviorSubject(undefined) constructor( protected authService: AuthService, @@ -32,14 +33,28 @@ export class DashboardComponent implements OnInit{ return } - firstValueFrom(this.cupboardService.getNeeds()).then(r => { - this.topNeeds = r.sort((a, b) => b.current - a.current) - this.almostThere = r.sort((a, b) => a.current/a.maxGoal - b.current/b.maxGoal) - }) + this.userService.getCount().subscribe(count => this.count.next(count)) + this.cupboardService.getNeeds().subscribe(needs => { + let fulfilledNeeds = 0 + let totalValue = 0 + for (let need of needs) { + let needPercent = need.current / need.maxGoal + if (needPercent >= 1) { + fulfilledNeeds++ + this.fulfilledNeeds.next(fulfilledNeeds) + } + if (need.type === GoalType.MONETARY) { + totalValue += need.current + this.totalDonations.next(totalValue) + } - this.userService.getBasket().subscribe(r => { - this.inBasket = r; + } + needs.sort((a, b) => b.current/b.maxGoal - a.current/a.maxGoal) + needs = needs.filter(a => a.current != 0) + this.mostFulfilledNeeds.next(needs.slice(0, 5)) }) + + } } diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts index 4080ebf..080c394 100644 --- a/ufund-ui/src/app/services/users.service.ts +++ b/ufund-ui/src/app/services/users.service.ts @@ -21,6 +21,15 @@ export class UsersService { }) }); + httpOptions2 = () => ({ + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + "jelly-api-key": this.authService.getApiKey() + }), + responseType: "text" as "json" // don't ask me how or why this works, bc i have no clue... + // see the relevant angular bug report https://github.com/angular/angular/issues/18586 + }); + constructor( private http: HttpClient, private cupboardService: CupboardService, @@ -35,6 +44,10 @@ export class UsersService { return this.http.get(`${this.url}/${id}`, this.httpOptions()) } + getCount(): Observable { + return this.http.get(`${this.url}/count`, this.httpOptions2()) + } + updateUser(user: User): Observable { console.log(`${this.url}/${user.username}`, user, this.httpOptions) return this.http.put(`${this.url}/${user.username}`, user, this.httpOptions()) -- cgit v1.2.3 From d7cbf88df5d8cb9e5b8feb563b787a3ac20816aa Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 3 Apr 2025 07:58:13 -0400 Subject: Updated statistics on dashboard --- ufund-ui/src/app/components/dashboard/dashboard.component.html | 7 ++++++- .../src/app/components/mini-need-list/mini-need-list.component.css | 2 +- .../app/components/mini-need-list/mini-need-list.component.html | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.html b/ufund-ui/src/app/components/dashboard/dashboard.component.html index 2d7b4c3..69ae66e 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.html +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.html @@ -4,7 +4,12 @@ _ Registered users + {{count | async}} _ Needs with overflow -_ Needs in peoples baskets +_ Fulfilled needs + {{fulfilledNeeds | async}} +_ Most fulfilled needs + _ Total monetary contributions +${{totalDonations | async}} _ diff --git a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css index ac456ab..090bea9 100644 --- a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css +++ b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css @@ -23,7 +23,7 @@ padding: 10px; gap: 10px; justify-content: start; - overflow: clip; + overflow: auto; } .needEntry { diff --git a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html index a2de9e5..9febfa5 100644 --- a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html +++ b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html @@ -1,6 +1,6 @@
-- cgit v1.2.3 From 6a93ead4f262bf7571192417cc2f6029e9a9e3a8 Mon Sep 17 00:00:00 2001 From: benal01 Date: Thu, 3 Apr 2025 11:55:04 -0400 Subject: need-page's transparent image in background --- .../components/need-page/need-page.component.css | 27 ++++----------- .../components/need-page/need-page.component.html | 39 ++++++++++------------ 2 files changed, 23 insertions(+), 43 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/need-page/need-page.component.css b/ufund-ui/src/app/components/need-page/need-page.component.css index 44db4b4..0bbc2aa 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.css +++ b/ufund-ui/src/app/components/need-page/need-page.component.css @@ -4,6 +4,7 @@ } #box { + padding-top: 5%; display: flex; flex-direction: column; width: 800px; @@ -22,32 +23,16 @@ /*margin-bottom: 20px;*/ } -.split { - display: flex; - flex-direction: row; - justify-content: space-between; - - - .left { - display: flex; - flex-direction: column; - width : 50%; - } - - .right { - display: flex; - flex-direction: column; - align-items: end; - } -} .need-image { - width: 400px; - height: auto; + width: 55%; + height: 40%; + position: absolute; + left: 22.5%; aspect-ratio: 16/9; object-fit: cover; + mask-image: linear-gradient(to bottom, rgba(255,255,255,1) 0%, rgba(255,255,255,.15) 35%, transparent 100%); border-radius: 10px; - box-shadow: rgb(0, 40, 70) 0 0 50px; } .urgent { diff --git a/ufund-ui/src/app/components/need-page/need-page.component.html b/ufund-ui/src/app/components/need-page/need-page.component.html index f8c2007..cd98c3b 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.html +++ b/ufund-ui/src/app/components/need-page/need-page.component.html @@ -1,4 +1,6 @@ -
+Need image + +

{{need.name}}

{{need.type}} GOAL

{{need.description}}

@@ -8,34 +10,27 @@ This goal is {{(((need.current)*100) / (need.maxGoal)).toFixed(0)}}% complete!
-
-
+ Target Goal: {{(need.type === GoalType.MONETARY) ? "$" : ""}}{{need.maxGoal}} Amount Currently Collected: {{need.current}} Location: {{need.location}} - Urgency: - Not urgent - URGENT - - -
- Tags: -
    -
  • -

    {{tag}}

    -
  • -
-
-
-
- Need image -
-
+ Urgency: + Not urgent + URGENT + - +
+ Tags: +
    +
  • +

    {{tag}}

    +
  • +
+
+
- -
- -
- -
-
-

Create Need

-
-
-
-
-
-
-
-
-
-
- -
- -
- -
-
-
- -
-
+
+
+
+ +
+
+
+
+ + + + + +
+
+

Search Results({{needs.length - searchResults.length}} needs filtered):

+

All Needs

+

No Results Found

+ +
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index 2230cd3..5139738 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -6,6 +6,8 @@ import { catchError, of } from 'rxjs'; import { NeedListComponent } from '../need-list/need-list.component'; import {AuthService} from '../../services/auth.service'; import {ToastsService, ToastType} from '../../services/toasts.service'; +import {UsersService} from '../../services/users.service'; +import {SortingAlgoArrays} from './sorting'; @Component({ selector: 'app-cupboard', @@ -13,43 +15,54 @@ import {ToastsService, ToastType} from '../../services/toasts.service'; templateUrl: './cupboard.component.html', styleUrl: './cupboard.component.css' }) - export class CupboardComponent implements OnInit { selectedForm?: string = undefined; - needs: any; + // needs: any; @ViewChild("needList") needList?: NeedListComponent + private searchDelay: any; + selectedNeed: Need | null = null; + needs: Need[] = []; + searchResults: Need[] = []; + sortMode: 'Ascending' | 'Descending' = 'Ascending' + + currentSortAlgo = SortingAlgoArrays.sortByPriority; + constructor( private cupboardService: CupboardService, private authService: AuthService, - private toastService: ToastsService + private toastService: ToastsService, + private usersService: UsersService ) {} ngOnInit(): void { - this.cupboardService.getNeeds().subscribe(n => this.needs = n); - if (this.isManager()) { - console.log("Admin view of Cupboard"); - } else { - console.log("Limited helper view of Cupboard"); - } + this.cupboardService.getNeeds().subscribe(n => { + this.needs = n; + this.refresh() + }); } - selectedNeed: any = { - name: '', - location:'', - id: null, - maxGoal: null, - type: '', - urgent: false - }; - selectedNeedId: number | null = null; - searchResults: any[] = []; + refresh() { + this.cupboardService.getNeeds().subscribe(n => { + if (this.sortMode == 'Ascending') { + this.needs = n.sort(this.currentSortAlgo.func); + } else { + this.needs = n.sort(this.currentSortAlgo.func).reverse(); + } + this.searchResults = this.needs; + // this.updateVisibleNeeds(); + }); + + const form = document.getElementById('search-form') as HTMLFormElement; + form.reset(); + this.search(null); + } selectForm(name: string) { //get search results from the need list if (this.needList) { - this.searchResults = this.needList.searchResults; + // this.searchResults = this.needList.searchResults; } console.log(this.searchResults) this.selectedForm = name; @@ -63,10 +76,65 @@ export class CupboardComponent implements OnInit { } } + async search(form: any) { + //wait .25 seconds before searching but cancel if another search is made during the wait to prevent too many api calls + + //remove previous search if it exists + if (this.searchDelay) { + clearTimeout(this.searchDelay); + } + if (form) { + this.searchDelay = setTimeout(() => { + + if (form) { + //sorting based on algo selected + // SortingAlgoArrays.forEach(algo => { + // if(algo.name === this.sortSelection) { + // this.currentSortAlgo = SortingAlgoArrays[this.sort]; + // console.log("changed sorting algorithm to: ", algo.name + this.sortMode) + // return + // } + // }); + + const currentSearchValue = form.search; //latest value of the search + this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { + if (this.sortMode == 'Ascending') { + this.searchResults = n.sort(this.currentSortAlgo.func); + } else { + this.searchResults = n.sort(this.currentSortAlgo.func).reverse(); + } + // this.updateVisibleNeeds(); + }); + } + }, 250); + } else { + //user has cleared the search bar, we can skip the timeout for a 1/4 second faster response + //clear timeout to stop pending search + clearTimeout(this.searchDelay); + this.searchResults = this.needs; + } + } + + changeSortMode(form : any) { + if (this.sortMode == 'Ascending'){ + this.sortMode = 'Descending' + } else { + this.sortMode = 'Ascending' + } + this.search(form) + } + + changeText(id : number, text : string) { + const span = document.getElementById('hover-status-label-' + id); + if (span) { + span.innerHTML = ' ' + text; + } + } + async updateSearchResults() { if (this.needList) { while (this.selectedForm == 'update') { - this.searchResults = this.needList.searchResults + // this.searchResults = this.needList.searchResults await new Promise(resolve => setTimeout(resolve, 100)); } } @@ -82,6 +150,45 @@ export class CupboardComponent implements OnInit { return type === ("MANAGER" as unknown as userType); } + isHelper() { + const type = this.authService.getCurrentUser()?.type; + return type === ("HELPER" as unknown as userType); + } + + // -------------- DISPLAY FUNCTIONS -------------- // + + select(need : Need) { + //emit value + // this.currentNeed.emit(need); + if (this.selectedNeed) { + //revert already selected need to previous style + console.log(need.id); + let button = document.getElementById('need-button-' + this.selectedNeed.id); + if (button) { + console.log(button) + button.style.background = 'lightgray'; + button.style.marginLeft = '0%'; + button.style.width = '98%'; + } + button = document.getElementById('need-edit-button-' + this.selectedNeed.id); + if (button) { + button.style.visibility = 'visible'; + } + } + //change selected need to selected style + this.selectedNeed = need; + let button = document.getElementById('need-button-' + need.id); + if (button) { + button.style.background = 'white'; + button.style.marginLeft = '4%'; + button.style.width = '100%'; + } + button = document.getElementById('need-edit-button-' + need.id); + if (button) { + button.style.visibility = 'hidden'; + } + } + submit(form: any) { const need: Need = { name: form.name, @@ -90,7 +197,7 @@ export class CupboardComponent implements OnInit { id: 0, maxGoal: form.maxGoal, type: form.type, - urgent: form.urgent ? true : false, + urgent: !!form.urgent, filterAttributes: [], current: 0, description: form.description @@ -112,7 +219,7 @@ export class CupboardComponent implements OnInit { (result) => { if (result) { console.log("need created successfully"); - this.needList?.refresh() + // this.needList?.refresh() } else { console.log("need creation failed"); } @@ -121,7 +228,36 @@ export class CupboardComponent implements OnInit { ); } - destroy() { + // -------------- CRUD OPERATIONS -------------- // + delete(id : number) { + this.cupboardService.deleteNeed(id).subscribe(() => { + this.toastService.sendToast(ToastType.INFO, "Need deleted.") + this.needs = this.needs.filter(n => n.id !== id) + }) + this.refresh(); } + + add(need: Need) { + const currentUser = this.authService.getCurrentUser(); + //console.log("get current user in angular:", currentUser) + if (currentUser) { + if (!currentUser.basket.includes(need.id)) { + currentUser.basket.push(need.id); + this.usersService.updateUser(currentUser) + .pipe(catchError((err, _) => { + console.error(err); + return of(); + })) + .subscribe(() => { + this.usersService.refreshBasket(); + }); + } else { + this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") + } + } + } + + protected readonly SortingAlgoArrays = SortingAlgoArrays; + protected readonly Object = Object; } diff --git a/ufund-ui/src/app/components/cupboard/sorting.ts b/ufund-ui/src/app/components/cupboard/sorting.ts new file mode 100644 index 0000000..a512f22 --- /dev/null +++ b/ufund-ui/src/app/components/cupboard/sorting.ts @@ -0,0 +1,58 @@ +import {Need} from '../../models/Need'; + +interface sortAlgo { + (a: Need, b: Need): number; +} + +// sort functions +const sortByName: sortAlgo = (a: Need, b: Need): number => { + if(a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { + return -1; + } + return 1; +} + +const sortByGoal: sortAlgo = (a: Need, b: Need): number => { + if(a.maxGoal == b.maxGoal) { + return sortByName(a,b); + } + else if(a.maxGoal > b.maxGoal) { + return -1; + } + return 1; +} + +const sortByCompletion: sortAlgo = (a: Need, b: Need): number => { + if(a.current == b.current) { + return sortByGoal(a,b); + } + else if(a.current > b.current) { + return -1; + } + return 1; +} + +const sortByPriority: sortAlgo = (a: Need, b: Need): number => { + if(a.urgent == b.urgent) { + return sortByGoal(a,b); + } + else if(a.urgent && !b.urgent) { + return -1; + } + return 1; +} + +const sortByLocation: sortAlgo = (a: Need, b: Need): number => { + if(a.location.toLocaleLowerCase() < b.location.toLocaleLowerCase()) { + return -1; + } + return 1; +} + +export const SortingAlgoArrays = { + sortByPriority: { func: sortByPriority, display: ["Highest Priority", "Lowest Priority" ] }, + sortByName: { func: sortByName, display: ["Name (A to Z)", "Name (Z to A)" ] }, + sortByLocation: { func: sortByLocation, display: ["Location (A to Z)", "Location (Z to A)" ] }, + sortByCompletion: { func: sortByCompletion, display: ["Most Completed", "Least Completed" ] }, + sortByGoal: { func: sortByGoal, display: ["Largest Maximum Goal", "Smallest Maximum Goal" ] }, +}; diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html index 52b35c1..2b05d01 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html @@ -1,75 +1,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Funding Basket

- -
-
-
-
-
- {{need.name}} - {{need.type}} -
- -
- URGENT - location_on{{need.location}} -
-
- -
- -
- - {{need.current}}/{{need.maxGoal}} ({{((need.current / need.maxGoal) * 100).toFixed(0)}}%) - -
- - - - -
- -
- - -
-
-
+ +
diff --git a/ufund-ui/src/app/components/need-list/need-list.component.ts b/ufund-ui/src/app/components/need-list/need-list.component.ts index 06a612e..d027690 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.ts +++ b/ufund-ui/src/app/components/need-list/need-list.component.ts @@ -1,282 +1,63 @@ -import {Component, EventEmitter, Output} from '@angular/core'; +import {Component, Input, OnChanges, OnInit} from '@angular/core'; import {Need} from '../../models/Need'; -import {CupboardService} from '../../services/cupboard.service'; -import {UsersService} from '../../services/users.service'; -import {userType} from '../../models/User'; -import {AuthService} from '../../services/auth.service'; -import {catchError, of} from 'rxjs'; -import {ToastsService, ToastType} from '../../services/toasts.service'; - -interface sortAlgo { - (a: Need,b: Need): number; -} - -// sort functions -const sortByName: sortAlgo = (a: Need, b: Need): number => { - if(a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { - return -1; - } - return 1; -} - -const sortByGoal: sortAlgo = (a: Need, b: Need): number => { - if(a.maxGoal == b.maxGoal) { - return sortByName(a,b); - } - else if(a.maxGoal > b.maxGoal) { - return -1; - } - return 1; -} - -const sortByCompletion: sortAlgo = (a: Need, b: Need): number => { - if(a.current == b.current) { - return sortByGoal(a,b); - } - else if(a.current > b.current) { - return -1; - } - return 1; -} - -const sortByPriority: sortAlgo = (a: Need, b: Need): number => { - if(a.urgent == b.urgent) { - return sortByGoal(a,b); - } - else if(a.urgent && !b.urgent) { - return -1; - } - return 1; -} - -const sortByLocation: sortAlgo = (a: Need, b: Need): number => { - if(a.location.toLocaleLowerCase() < b.location.toLocaleLowerCase()) { - return -1; - } - return 1; -} +import {Observable} from 'rxjs'; @Component({ - selector: 'app-need-list', - standalone: false, - templateUrl: './need-list.component.html', - styleUrl: './need-list.component.css' + selector: 'app-need-list', + standalone: false, + templateUrl: './need-list.component.html', + styleUrl: './need-list.component.css' }) -export class NeedListComponent { - selectedNeed: Need | null = null; - needs: Need[] = []; - searchResults: Need[] = []; - visibleNeeds: Need[] = []; - sortMode: 'Ascending' | 'Descending' = 'Ascending' - currentPage: number = 0; - itemsPerPage: number = 5; - totalPages: number = Math.ceil(this.needs.length / this.itemsPerPage); - - decrementPage() { - this.currentPage--; - this.updateVisibleNeeds(); - } - - incrementPage() { - this.currentPage++; - this.updateVisibleNeeds(); - } - - lastPage() { - this.currentPage = this.totalPages - 1 - this.updateVisibleNeeds() - } +export class NeedListComponent implements OnChanges { - firstPage() { - this.currentPage = 0 - this.updateVisibleNeeds() - } + @Input({required: true}) needs!: Need[] - editNeedsPerPage(amount: number) { - this.itemsPerPage = amount; - this.updateVisibleNeeds(); - } + visibleNeeds: Need[] = []; + currentPage: number = 0; + itemsPerPage: number = 5; + totalPages: number = 0; - updateVisibleNeeds() { - this.totalPages = Math.ceil(this.searchResults.length / this.itemsPerPage); - this.visibleNeeds = this.searchResults.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); - } + constructor( - resetVisibleNeeds() { - this.currentPage = 0; - this.updateVisibleNeeds(); - } + ) {} - currentSortAlgo: sortAlgo = sortByPriority; - sortSelection: string = 'sortByPriority'; - - SortingAlgoArrays: {func:sortAlgo,name:string, display:string[]}[] = [ - {func:sortByPriority,name:"sortByPriority", display:["Highest Priority", "Lowest Priority"]}, - {func:sortByName,name:"sortByName", display:["Name (A to Z)", "Name (Z to A)"]}, - {func:sortByLocation,name:"sortByLocation", display:["Location (A to Z)", "Location (Z to A)"]}, - {func:sortByCompletion,name:"sortByCompletion", display:["Most Completed", "Least Completed"]}, - {func:sortByGoal,name:"sortByGoal", display:["Largest Maximum Goal", "Smallest Maximum Goal"]}, - ]; - - @Output() currentNeed = new EventEmitter(); - - constructor( - private cupboardService: CupboardService, - private usersService: UsersService, - private authService: AuthService, - private toastService: ToastsService - ) {} - - refresh() { - this.cupboardService.getNeeds().subscribe(n => { - if (this.sortMode == 'Ascending') { - this.needs = n.sort(this.currentSortAlgo); - } else { - this.needs = n.sort(this.currentSortAlgo).reverse(); - } - this.searchResults = this.needs; - this.updateVisibleNeeds(); - }); - - const form = document.getElementById('search-form') as HTMLFormElement; - form.reset(); - this.search(null); + ngOnChanges() { + this.updateVisibleNeeds() } - ngOnInit(): void { - this.refresh() - - } - - changeSortMode(form : any) { - if (this.sortMode == 'Ascending'){ - this.sortMode = 'Descending' - } else { - this.sortMode = 'Ascending' + decrementPage() { + this.currentPage--; + this.updateVisibleNeeds(); } - this.search(form) - } - - private searchDelay: any; - async search(form: any) { - //wait .25 seconds before searching but cancel if another search is made during the wait to prevent too many api calls - - //remove previous search if it exists - if (this.searchDelay) { - clearTimeout(this.searchDelay); + incrementPage() { + this.currentPage++; + this.updateVisibleNeeds(); } - if (form) { - this.searchDelay = setTimeout(() => { - - if (form) { - //sorting based on algo selected - this.SortingAlgoArrays.forEach(algo => { - if(algo.name === this.sortSelection) { - this.currentSortAlgo = algo.func; - console.log("changed sorting algorithm to: ", algo.name + this.sortMode) - return - } - }); - - const currentSearchValue = form.search; //latest value of the search - this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { - if (this.sortMode == 'Ascending') { - this.searchResults = n.sort(this.currentSortAlgo); - } else { - this.searchResults = n.sort(this.currentSortAlgo).reverse(); - } - this.updateVisibleNeeds(); - }); - } - }, 250); - } else { - //user has cleared the search bar, we can skip the timeout for a 1/4 second faster response - //clear timeout to stop pending search - clearTimeout(this.searchDelay); - this.searchResults = this.needs; - } - } - - delete(id : number) { - this.cupboardService.deleteNeed(id).subscribe(() => { - this.toastService.sendToast(ToastType.INFO, "Need deleted.") - this.needs = this.needs.filter(n => n.id !== id) - }) - this.refresh(); - } - isManager() { - const type = this.authService.getCurrentUser()?.type; - return type === ("MANAGER" as unknown as userType); - } - - isHelper() { - const type = this.authService.getCurrentUser()?.type; - return type === ("HELPER" as unknown as userType); - } + lastPage() { + this.currentPage = this.totalPages - 1 + this.updateVisibleNeeds() + } - changeText(id : number, text : string) { - const span = document.getElementById('hover-status-label-' + id); - if (span) { - span.innerHTML = ' ' + text; + firstPage() { + this.currentPage = 0 + this.updateVisibleNeeds() } - } - add(need: Need) { - const currentUser = this.authService.getCurrentUser(); - //console.log("get current user in angular:", currentUser) - if (currentUser) { - if (!currentUser.basket.includes(need.id)) { - currentUser.basket.push(need.id); - this.usersService.updateUser(currentUser) - .pipe(catchError((err, _) => { - console.error(err); - return of(); - })) - .subscribe(() => { - this.usersService.refreshBasket(); - }); - } else { - this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") - } + editNeedsPerPage(amount: number) { + this.itemsPerPage = amount; + this.updateVisibleNeeds(); } - } - back() { - this.searchResults = this.needs; - } + updateVisibleNeeds() { + this.totalPages = Math.ceil(this.needs.length / this.itemsPerPage); + this.visibleNeeds = this.needs.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); + } - select(need : Need) { - //emit value - this.currentNeed.emit(need); - if (this.selectedNeed) { - //revert already selected need to previous style - console.log(need.id); - let button = document.getElementById('need-button-' + this.selectedNeed.id); - if (button) { - console.log(button) - button.style.background = 'lightgray'; - button.style.marginLeft = '0%'; - button.style.width = '98%'; - } - button = document.getElementById('need-edit-button-' + this.selectedNeed.id); - if (button) { - button.style.visibility = 'visible'; - } + resetVisibleNeeds() { + this.currentPage = 0; + this.updateVisibleNeeds(); } - //change selected need to selected style - this.selectedNeed = need; - let button = document.getElementById('need-button-' + need.id); - if (button) { - button.style.background = 'white'; - button.style.marginLeft = '4%'; - button.style.width = '100%'; - } - button = document.getElementById('need-edit-button-' + need.id); - if (button) { - button.style.visibility = 'hidden'; - } - } } -- cgit v1.2.3 From 75f5ad5fb154811d7acd236687bb7f30bb7c10aa Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 3 Apr 2025 15:35:12 -0400 Subject: Fixed incognito race condition and checkout bugs --- .../funding-basket/funding-basket.component.ts | 23 ++++++---------------- .../src/app/components/toast/toast.component.ts | 1 - ufund-ui/src/app/services/cupboard.service.ts | 3 +-- 3 files changed, 7 insertions(+), 20 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts index 015d5b5..5d94124 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts @@ -2,7 +2,7 @@ import {Component, Input, OnInit, ViewChild} from '@angular/core'; import {UsersService} from '../../services/users.service'; import {Router} from '@angular/router'; import {CupboardService} from '../../services/cupboard.service'; -import {catchError, firstValueFrom, Observable} from 'rxjs'; +import {firstValueFrom} from 'rxjs'; import {AuthService} from '../../services/auth.service'; import {ToastsService, ToastType} from '../../services/toasts.service'; @@ -32,11 +32,10 @@ export class FundingBasketComponent implements OnInit { } this.usersService.refreshBasket(); - // this.usersService.removeNeed(); <- call this to remove } async checkout() { - let order: { id: number, quantity: number }[] = [] + let order: { needID: number, quantity: number }[] = [] for (let contribution of document.querySelectorAll('.contribution')!) { if (contribution.value == '' || contribution.valueAsNumber <= 0) { @@ -44,31 +43,21 @@ export class FundingBasketComponent implements OnInit { this.toastService.sendToast(ToastType.ERROR, "Invalid input in funding basket!") return; } - order.push({id: +contribution.id, quantity: contribution.valueAsNumber}); + order.push({needID: +contribution.id, quantity: contribution.valueAsNumber}); } try { - this.cupboardService.checkoutNeed(order) + await firstValueFrom(this.cupboardService.checkoutNeed(order)) } catch (ex:any) { this.toastService.sendToast(ToastType.ERROR, ex.error); return } - console.log(order) - - for (let contribution of order) { - let need = await firstValueFrom(this.cupboardService.getNeed(contribution.id)) - need.current += contribution.quantity; - this.usersService.removeNeed(need.id); - this.toastService.sendToast(ToastType.INFO, "Checkout successful"); - } - - // this.usersService.getBasket().subscribe(console.log) - // this.usersService.refreshBasket() + order.forEach(contribution => this.usersService.removeNeed(contribution.needID)) + this.toastService.sendToast(ToastType.INFO, "Checkout successful"); } resetColor(ev: any) { - // console.log(ev); (ev.target as HTMLInputElement).setAttribute("style", "border-color: unset") } } diff --git a/ufund-ui/src/app/components/toast/toast.component.ts b/ufund-ui/src/app/components/toast/toast.component.ts index 47fd7ff..6bbae34 100644 --- a/ufund-ui/src/app/components/toast/toast.component.ts +++ b/ufund-ui/src/app/components/toast/toast.component.ts @@ -21,7 +21,6 @@ export class ToastComponent implements OnInit{ } hide() { - console.log(this.toastDiv, typeof this.toastDiv) this.toastDiv.nativeElement.classList.add('hide') } diff --git a/ufund-ui/src/app/services/cupboard.service.ts b/ufund-ui/src/app/services/cupboard.service.ts index 786973e..1060476 100644 --- a/ufund-ui/src/app/services/cupboard.service.ts +++ b/ufund-ui/src/app/services/cupboard.service.ts @@ -47,8 +47,7 @@ export class CupboardService { return this.http.delete(`${this.url}/${id}`, this.httpOptions()) } - checkoutNeed(data: {id: number, quantity: number}[]) { - console.log("GOT HERE") + checkoutNeed(data: {needID: number, quantity: number}[]) { return this.http.put(`${this.url}/checkout`, data, this.httpOptions()) } } -- cgit v1.2.3 From ad651c44ce2515d497c8e5214147c69126e25903 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 3 Apr 2025 19:48:54 -0400 Subject: abstraction working with search and sort --- .../components/cupboard/cupboard.component.html | 16 +- .../app/components/cupboard/cupboard.component.ts | 277 ++++++++++----------- ufund-ui/src/app/components/cupboard/sorting.ts | 12 +- .../components/need-list/need-list.component.ts | 12 +- 4 files changed, 152 insertions(+), 165 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.html b/ufund-ui/src/app/components/cupboard/cupboard.component.html index bdc8a0e..969b232 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.html +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.html @@ -14,23 +14,23 @@
- - +
-

Search Results({{needs.length - (searchResults | async)?.length}} needs filtered):

-

All Needs

-

No Results Found

+

Search Results({{needs.length - searchResults.length}} needs filtered):

+

All Needs

+

No Results Found

- +
diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index 5139738..f723755 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -2,7 +2,7 @@ import {Component, OnInit, ViewChild} from '@angular/core'; import { CupboardService } from '../../services/cupboard.service'; import { Need, GoalType } from '../../models/Need'; import { userType } from '../../models/User'; -import { catchError, of } from 'rxjs'; +import {BehaviorSubject, catchError, Observable, of} from 'rxjs'; import { NeedListComponent } from '../need-list/need-list.component'; import {AuthService} from '../../services/auth.service'; import {ToastsService, ToastType} from '../../services/toasts.service'; @@ -17,17 +17,17 @@ import {SortingAlgoArrays} from './sorting'; }) export class CupboardComponent implements OnInit { - selectedForm?: string = undefined; + // selectedForm?: string = undefined; // needs: any; @ViewChild("needList") needList?: NeedListComponent private searchDelay: any; - selectedNeed: Need | null = null; + // selectedNeed: Need | null = null; needs: Need[] = []; searchResults: Need[] = []; sortMode: 'Ascending' | 'Descending' = 'Ascending' - - currentSortAlgo = SortingAlgoArrays.sortByPriority; + itemsPerPage = 5; + currentSortAlgo = 'sortByPriority'; constructor( private cupboardService: CupboardService, @@ -39,16 +39,17 @@ export class CupboardComponent implements OnInit { ngOnInit(): void { this.cupboardService.getNeeds().subscribe(n => { this.needs = n; - this.refresh() + // this.refresh() + this.search(null) }); } refresh() { this.cupboardService.getNeeds().subscribe(n => { if (this.sortMode == 'Ascending') { - this.needs = n.sort(this.currentSortAlgo.func); + this.needs = n.sort(SortingAlgoArrays[this.currentSortAlgo].func); } else { - this.needs = n.sort(this.currentSortAlgo.func).reverse(); + this.needs = n.sort(SortingAlgoArrays[this.currentSortAlgo].func).reverse(); } this.searchResults = this.needs; // this.updateVisibleNeeds(); @@ -59,24 +60,8 @@ export class CupboardComponent implements OnInit { this.search(null); } - selectForm(name: string) { - //get search results from the need list - if (this.needList) { - // this.searchResults = this.needList.searchResults; - } - console.log(this.searchResults) - this.selectedForm = name; - if (name == 'update') { - if (this.searchResults) { - this.searchResults.forEach((element: any) => { - console.log(element) - }); - } - - } - } - async search(form: any) { + console.log(this.currentSortAlgo) //wait .25 seconds before searching but cancel if another search is made during the wait to prevent too many api calls //remove previous search if it exists @@ -99,9 +84,9 @@ export class CupboardComponent implements OnInit { const currentSearchValue = form.search; //latest value of the search this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { if (this.sortMode == 'Ascending') { - this.searchResults = n.sort(this.currentSortAlgo.func); + this.searchResults = n.sort(SortingAlgoArrays[this.currentSortAlgo].func); } else { - this.searchResults = n.sort(this.currentSortAlgo.func).reverse(); + this.searchResults = n.sort(SortingAlgoArrays[this.currentSortAlgo].func).reverse(); } // this.updateVisibleNeeds(); }); @@ -115,7 +100,7 @@ export class CupboardComponent implements OnInit { } } - changeSortMode(form : any) { + toggleSortMode(form : any) { if (this.sortMode == 'Ascending'){ this.sortMode = 'Descending' } else { @@ -124,26 +109,16 @@ export class CupboardComponent implements OnInit { this.search(form) } - changeText(id : number, text : string) { - const span = document.getElementById('hover-status-label-' + id); - if (span) { - span.innerHTML = ' ' + text; - } - } - async updateSearchResults() { - if (this.needList) { - while (this.selectedForm == 'update') { - // this.searchResults = this.needList.searchResults - await new Promise(resolve => setTimeout(resolve, 100)); - } - } + // if (this.needList) { + // while (this.selectedForm == 'update') { + // // this.searchResults = this.needList.searchResults + // await new Promise(resolve => setTimeout(resolve, 100)); + // } + // } } - populateForm(need: any): void { - this.selectForm('update'); - this.selectedNeed = { ...need }; - } + // ------------ HELPER FUNCTIONS --------------- // isManager() { const type = this.authService.getCurrentUser()?.type; @@ -157,107 +132,129 @@ export class CupboardComponent implements OnInit { // -------------- DISPLAY FUNCTIONS -------------- // - select(need : Need) { - //emit value - // this.currentNeed.emit(need); - if (this.selectedNeed) { - //revert already selected need to previous style - console.log(need.id); - let button = document.getElementById('need-button-' + this.selectedNeed.id); - if (button) { - console.log(button) - button.style.background = 'lightgray'; - button.style.marginLeft = '0%'; - button.style.width = '98%'; - } - button = document.getElementById('need-edit-button-' + this.selectedNeed.id); - if (button) { - button.style.visibility = 'visible'; - } - } - //change selected need to selected style - this.selectedNeed = need; - let button = document.getElementById('need-button-' + need.id); - if (button) { - button.style.background = 'white'; - button.style.marginLeft = '4%'; - button.style.width = '100%'; - } - button = document.getElementById('need-edit-button-' + need.id); - if (button) { - button.style.visibility = 'hidden'; - } + selectForm(name: string) { + // //get search results from the need list + // if (this.needList) { + // // this.searchResults = this.needList.searchResults; + // } + // console.log(this.searchResults) + // this.selectedForm = name; + // if (name == 'update') { + // if (this.searchResults) { + // this.searchResults.forEach((element: any) => { + // console.log(element) + // }); + // } + // + // } } - submit(form: any) { - const need: Need = { - name: form.name, - image: form.image, - location: form.location, - id: 0, - maxGoal: form.maxGoal, - type: form.type, - urgent: !!form.urgent, - filterAttributes: [], - current: 0, - description: form.description - }; - console.log("need:", need); - console.log("form submitted. creating need: ", need); - this.cupboardService.createNeed(need) - .pipe(catchError((ex, _) => { - if (ex.status == 500) { - this.toastService.sendToast(ToastType.ERROR, "Fields cannot be blank"); - } else if (ex.status == 400) { - this.toastService.sendToast(ToastType.ERROR, ex.error); - } else { - this.toastService.sendToast(ToastType.ERROR, "Error on creating need"); - } - return of() - })) - .subscribe( - (result) => { - if (result) { - console.log("need created successfully"); - // this.needList?.refresh() - } else { - console.log("need creation failed"); - } - } + // populateForm(need: any): void { + // this.selectForm('update'); + // this.selectedNeed = { ...need }; + // } + // + // select(need : Need) { + // //emit value + // // this.currentNeed.emit(need); + // if (this.selectedNeed) { + // //revert already selected need to previous style + // console.log(need.id); + // let button = document.getElementById('need-button-' + this.selectedNeed.id); + // if (button) { + // console.log(button) + // button.style.background = 'lightgray'; + // button.style.marginLeft = '0%'; + // button.style.width = '98%'; + // } + // button = document.getElementById('need-edit-button-' + this.selectedNeed.id); + // if (button) { + // button.style.visibility = 'visible'; + // } + // } + // //change selected need to selected style + // this.selectedNeed = need; + // let button = document.getElementById('need-button-' + need.id); + // if (button) { + // button.style.background = 'white'; + // button.style.marginLeft = '4%'; + // button.style.width = '100%'; + // } + // button = document.getElementById('need-edit-button-' + need.id); + // if (button) { + // button.style.visibility = 'hidden'; + // } + // } - ); - } + // submit(form: any) { + // const need: Need = { + // name: form.name, + // image: form.image, + // location: form.location, + // id: 0, + // maxGoal: form.maxGoal, + // type: form.type, + // urgent: !!form.urgent, + // filterAttributes: [], + // current: 0, + // description: form.description + // }; + // console.log("need:", need); + // console.log("form submitted. creating need: ", need); + // this.cupboardService.createNeed(need) + // .pipe(catchError((ex, _) => { + // if (ex.status == 500) { + // this.toastService.sendToast(ToastType.ERROR, "Fields cannot be blank"); + // } else if (ex.status == 400) { + // this.toastService.sendToast(ToastType.ERROR, ex.error); + // } else { + // this.toastService.sendToast(ToastType.ERROR, "Error on creating need"); + // } + // return of() + // })) + // .subscribe( + // (result) => { + // if (result) { + // console.log("need created successfully"); + // // this.needList?.refresh() + // } else { + // console.log("need creation failed"); + // } + // } + // + // ); + // } // -------------- CRUD OPERATIONS -------------- // - delete(id : number) { - this.cupboardService.deleteNeed(id).subscribe(() => { - this.toastService.sendToast(ToastType.INFO, "Need deleted.") - this.needs = this.needs.filter(n => n.id !== id) - }) - this.refresh(); - } - - add(need: Need) { - const currentUser = this.authService.getCurrentUser(); - //console.log("get current user in angular:", currentUser) - if (currentUser) { - if (!currentUser.basket.includes(need.id)) { - currentUser.basket.push(need.id); - this.usersService.updateUser(currentUser) - .pipe(catchError((err, _) => { - console.error(err); - return of(); - })) - .subscribe(() => { - this.usersService.refreshBasket(); - }); - } else { - this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") - } - } - } + // delete(id : number) { + // this.cupboardService.deleteNeed(id).subscribe(() => { + // this.toastService.sendToast(ToastType.INFO, "Need deleted.") + // this.needs = this.needs.filter(n => n.id !== id) + // }) + // this.refresh(); + // } + // + // add(need: Need) { + // const currentUser = this.authService.getCurrentUser(); + // //console.log("get current user in angular:", currentUser) + // if (currentUser) { + // if (!currentUser.basket.includes(need.id)) { + // currentUser.basket.push(need.id); + // this.usersService.updateUser(currentUser) + // .pipe(catchError((err, _) => { + // console.error(err); + // return of(); + // })) + // .subscribe(() => { + // this.usersService.refreshBasket(); + // }); + // } else { + // this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") + // } + // } + // } - protected readonly SortingAlgoArrays = SortingAlgoArrays; + protected readonly SortingAlgorithms = SortingAlgoArrays; protected readonly Object = Object; } diff --git a/ufund-ui/src/app/components/cupboard/sorting.ts b/ufund-ui/src/app/components/cupboard/sorting.ts index a512f22..7cb3f39 100644 --- a/ufund-ui/src/app/components/cupboard/sorting.ts +++ b/ufund-ui/src/app/components/cupboard/sorting.ts @@ -49,10 +49,10 @@ const sortByLocation: sortAlgo = (a: Need, b: Need): number => { return 1; } -export const SortingAlgoArrays = { - sortByPriority: { func: sortByPriority, display: ["Highest Priority", "Lowest Priority" ] }, - sortByName: { func: sortByName, display: ["Name (A to Z)", "Name (Z to A)" ] }, - sortByLocation: { func: sortByLocation, display: ["Location (A to Z)", "Location (Z to A)" ] }, - sortByCompletion: { func: sortByCompletion, display: ["Most Completed", "Least Completed" ] }, - sortByGoal: { func: sortByGoal, display: ["Largest Maximum Goal", "Smallest Maximum Goal" ] }, +export const SortingAlgoArrays: {[key: string]: { func: sortAlgo, display: [string, string]}} = { + "sortByPriority": { func: sortByPriority, display: ["Highest Priority", "Lowest Priority" ] }, + "sortByName": { func: sortByName, display: ["Name (A to Z)", "Name (Z to A)" ] }, + "sortByLocation": { func: sortByLocation, display: ["Location (A to Z)", "Location (Z to A)" ] }, + "sortByCompletion": { func: sortByCompletion, display: ["Most Completed", "Least Completed" ] }, + "sortByGoal": { func: sortByGoal, display: ["Largest Maximum Goal", "Smallest Maximum Goal" ] }, }; diff --git a/ufund-ui/src/app/components/need-list/need-list.component.ts b/ufund-ui/src/app/components/need-list/need-list.component.ts index d027690..cd7debb 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.ts +++ b/ufund-ui/src/app/components/need-list/need-list.component.ts @@ -11,10 +11,10 @@ import {Observable} from 'rxjs'; export class NeedListComponent implements OnChanges { @Input({required: true}) needs!: Need[] + @Input() itemsPerPage: number = 5; visibleNeeds: Need[] = []; currentPage: number = 0; - itemsPerPage: number = 5; totalPages: number = 0; constructor( @@ -45,19 +45,9 @@ export class NeedListComponent implements OnChanges { this.updateVisibleNeeds() } - editNeedsPerPage(amount: number) { - this.itemsPerPage = amount; - this.updateVisibleNeeds(); - } - updateVisibleNeeds() { this.totalPages = Math.ceil(this.needs.length / this.itemsPerPage); this.visibleNeeds = this.needs.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); } - - resetVisibleNeeds() { - this.currentPage = 0; - this.updateVisibleNeeds(); - } } -- cgit v1.2.3 From 6a5033afc3c8e0d5d12d709b35b306b6ea1f70e8 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 3 Apr 2025 23:11:05 -0400 Subject: Implemented action buttons with ng-template!!! --- .../components/cupboard/cupboard.component.html | 14 +- .../app/components/cupboard/cupboard.component.ts | 160 ++++++++++----------- .../funding-basket/funding-basket.component.html | 8 +- .../components/need-list/need-list.component.html | 21 +-- .../components/need-list/need-list.component.ts | 4 +- 5 files changed, 97 insertions(+), 110 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.html b/ufund-ui/src/app/components/cupboard/cupboard.component.html index 969b232..cd387a3 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.html +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.html @@ -24,13 +24,23 @@
-

Search Results({{needs.length - searchResults.length}} needs filtered):

All Needs

No Results Found

- + + + + + +
diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index f723755..a4f8db2 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -1,9 +1,9 @@ import {Component, OnInit, ViewChild} from '@angular/core'; -import { CupboardService } from '../../services/cupboard.service'; -import { Need, GoalType } from '../../models/Need'; -import { userType } from '../../models/User'; -import {BehaviorSubject, catchError, Observable, of} from 'rxjs'; -import { NeedListComponent } from '../need-list/need-list.component'; +import {CupboardService} from '../../services/cupboard.service'; +import {Need} from '../../models/Need'; +import {userType} from '../../models/User'; +import {catchError, of} from 'rxjs'; +import {NeedListComponent} from '../need-list/need-list.component'; import {AuthService} from '../../services/auth.service'; import {ToastsService, ToastType} from '../../services/toasts.service'; import {UsersService} from '../../services/users.service'; @@ -72,14 +72,6 @@ export class CupboardComponent implements OnInit { this.searchDelay = setTimeout(() => { if (form) { - //sorting based on algo selected - // SortingAlgoArrays.forEach(algo => { - // if(algo.name === this.sortSelection) { - // this.currentSortAlgo = SortingAlgoArrays[this.sort]; - // console.log("changed sorting algorithm to: ", algo.name + this.sortMode) - // return - // } - // }); const currentSearchValue = form.search; //latest value of the search this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { @@ -109,28 +101,52 @@ export class CupboardComponent implements OnInit { this.search(form) } - async updateSearchResults() { - // if (this.needList) { - // while (this.selectedForm == 'update') { - // // this.searchResults = this.needList.searchResults - // await new Promise(resolve => setTimeout(resolve, 100)); - // } - // } + deleteNeed(id : number) { + this.cupboardService.deleteNeed(id).subscribe(() => { + this.toastService.sendToast(ToastType.INFO, "Need deleted.") + this.needs = this.needs.filter(n => n.id !== id) + }) + this.refresh(); } - // ------------ HELPER FUNCTIONS --------------- // + addToBasket(need: Need) { + const currentUser = this.authService.getCurrentUser(); + //console.log("get current user in angular:", currentUser) + if (currentUser) { + if (!currentUser.basket.includes(need.id)) { + currentUser.basket.push(need.id); + this.usersService.updateUser(currentUser) + .pipe(catchError((err, _) => { + console.error(err); + return of(); + })) + .subscribe(() => { + this.usersService.refreshBasket(); + }); + } else { + this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") + } + } + } isManager() { - const type = this.authService.getCurrentUser()?.type; - return type === ("MANAGER" as unknown as userType); + return this.authService.getCurrentUser()?.type === userType.MANAGER } isHelper() { - const type = this.authService.getCurrentUser()?.type; - return type === ("HELPER" as unknown as userType); + return this.authService.getCurrentUser()?.type === userType.HELPER } - // -------------- DISPLAY FUNCTIONS -------------- // + // --------------- FORM STUFF NOT IMPLEMENTED YET --------------- // + + // async updateSearchResults() { + // if (this.needList) { + // while (this.selectedForm == 'update') { + // // this.searchResults = this.needList.searchResults + // await new Promise(resolve => setTimeout(resolve, 100)); + // } + // } + // } selectForm(name: string) { // //get search results from the need list @@ -154,37 +170,37 @@ export class CupboardComponent implements OnInit { // this.selectedNeed = { ...need }; // } // - // select(need : Need) { - // //emit value - // // this.currentNeed.emit(need); - // if (this.selectedNeed) { - // //revert already selected need to previous style - // console.log(need.id); - // let button = document.getElementById('need-button-' + this.selectedNeed.id); - // if (button) { - // console.log(button) - // button.style.background = 'lightgray'; - // button.style.marginLeft = '0%'; - // button.style.width = '98%'; - // } - // button = document.getElementById('need-edit-button-' + this.selectedNeed.id); - // if (button) { - // button.style.visibility = 'visible'; - // } - // } - // //change selected need to selected style - // this.selectedNeed = need; - // let button = document.getElementById('need-button-' + need.id); - // if (button) { - // button.style.background = 'white'; - // button.style.marginLeft = '4%'; - // button.style.width = '100%'; - // } - // button = document.getElementById('need-edit-button-' + need.id); - // if (button) { - // button.style.visibility = 'hidden'; - // } - // } + select(need : Need) { + // //emit value + // // this.currentNeed.emit(need); + // if (this.selectedNeed) { + // //revert already selected need to previous style + // console.log(need.id); + // let button = document.getElementById('need-button-' + this.selectedNeed.id); + // if (button) { + // console.log(button) + // button.style.background = 'lightgray'; + // button.style.marginLeft = '0%'; + // button.style.width = '98%'; + // } + // button = document.getElementById('need-edit-button-' + this.selectedNeed.id); + // if (button) { + // button.style.visibility = 'visible'; + // } + // } + // //change selected need to selected style + // this.selectedNeed = need; + // let button = document.getElementById('need-button-' + need.id); + // if (button) { + // button.style.background = 'white'; + // button.style.marginLeft = '4%'; + // button.style.width = '100%'; + // } + // button = document.getElementById('need-edit-button-' + need.id); + // if (button) { + // button.style.visibility = 'hidden'; + // } + } // submit(form: any) { // const need: Need = { @@ -225,36 +241,6 @@ export class CupboardComponent implements OnInit { // ); // } - // -------------- CRUD OPERATIONS -------------- // - - // delete(id : number) { - // this.cupboardService.deleteNeed(id).subscribe(() => { - // this.toastService.sendToast(ToastType.INFO, "Need deleted.") - // this.needs = this.needs.filter(n => n.id !== id) - // }) - // this.refresh(); - // } - // - // add(need: Need) { - // const currentUser = this.authService.getCurrentUser(); - // //console.log("get current user in angular:", currentUser) - // if (currentUser) { - // if (!currentUser.basket.includes(need.id)) { - // currentUser.basket.push(need.id); - // this.usersService.updateUser(currentUser) - // .pipe(catchError((err, _) => { - // console.error(err); - // return of(); - // })) - // .subscribe(() => { - // this.usersService.refreshBasket(); - // }); - // } else { - // this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") - // } - // } - // } - protected readonly SortingAlgorithms = SortingAlgoArrays; protected readonly Object = Object; } diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html index 2b05d01..3e884cd 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html @@ -1,7 +1,13 @@

Funding Basket

- + + + + +
- - - - - - - - - - - - - - - - - - +
+ +
diff --git a/ufund-ui/src/app/components/need-list/need-list.component.ts b/ufund-ui/src/app/components/need-list/need-list.component.ts index cd7debb..7e5c3f4 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.ts +++ b/ufund-ui/src/app/components/need-list/need-list.component.ts @@ -1,6 +1,5 @@ -import {Component, Input, OnChanges, OnInit} from '@angular/core'; +import {Component, Input, OnChanges, TemplateRef} from '@angular/core'; import {Need} from '../../models/Need'; -import {Observable} from 'rxjs'; @Component({ selector: 'app-need-list', @@ -12,6 +11,7 @@ export class NeedListComponent implements OnChanges { @Input({required: true}) needs!: Need[] @Input() itemsPerPage: number = 5; + @Input() actionArea: TemplateRef | null = null visibleNeeds: Need[] = []; currentPage: number = 0; -- cgit v1.2.3 From 43b036dc8ac03100787ec691a4f4ebe3670f861f Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 3 Apr 2025 23:22:16 -0400 Subject: code cleanup --- ufund-ui/src/app/components/cupboard/cupboard.component.html | 2 +- ufund-ui/src/app/components/cupboard/sorting.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.html b/ufund-ui/src/app/components/cupboard/cupboard.component.html index cd387a3..c055c31 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.html +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.html @@ -40,7 +40,7 @@ deleteDelete Need
- + diff --git a/ufund-ui/src/app/components/cupboard/sorting.ts b/ufund-ui/src/app/components/cupboard/sorting.ts index 7cb3f39..73a5ba9 100644 --- a/ufund-ui/src/app/components/cupboard/sorting.ts +++ b/ufund-ui/src/app/components/cupboard/sorting.ts @@ -50,9 +50,9 @@ const sortByLocation: sortAlgo = (a: Need, b: Need): number => { } export const SortingAlgoArrays: {[key: string]: { func: sortAlgo, display: [string, string]}} = { - "sortByPriority": { func: sortByPriority, display: ["Highest Priority", "Lowest Priority" ] }, - "sortByName": { func: sortByName, display: ["Name (A to Z)", "Name (Z to A)" ] }, - "sortByLocation": { func: sortByLocation, display: ["Location (A to Z)", "Location (Z to A)" ] }, - "sortByCompletion": { func: sortByCompletion, display: ["Most Completed", "Least Completed" ] }, - "sortByGoal": { func: sortByGoal, display: ["Largest Maximum Goal", "Smallest Maximum Goal" ] }, + sortByPriority: { func: sortByPriority, display: ["Highest Priority", "Lowest Priority" ] }, + sortByName: { func: sortByName, display: ["Name (A to Z)", "Name (Z to A)" ] }, + sortByLocation: { func: sortByLocation, display: ["Location (A to Z)", "Location (Z to A)" ] }, + sortByCompletion: { func: sortByCompletion, display: ["Most Completed", "Least Completed" ] }, + sortByGoal: { func: sortByGoal, display: ["Largest Maximum Goal", "Smallest Maximum Goal" ] }, }; -- cgit v1.2.3 From 5186df87c75d83eb24a2cb5d868d8d0bb6aa5b88 Mon Sep 17 00:00:00 2001 From: benal01 Date: Fri, 4 Apr 2025 10:37:45 -0400 Subject: localstorage for page (keeps current page on refresh/back arrow) --- ufund-ui/src/app/components/need-list/need-list.component.ts | 12 +++++++++++- .../src/app/components/need-page/need-page.component.html | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/need-list/need-list.component.ts b/ufund-ui/src/app/components/need-list/need-list.component.ts index ae6bc99..39ff538 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.ts +++ b/ufund-ui/src/app/components/need-list/need-list.component.ts @@ -68,7 +68,7 @@ export class NeedListComponent { searchResults: Need[] = []; visibleNeeds: Need[] = []; sortMode: 'Ascending' | 'Descending' = 'Ascending' - currentPage: number = 0; + currentPage: number = localStorage.getItem('currentPage') ? parseInt(localStorage.getItem('currentPage')!) : 0; itemsPerPage: number = 5; totalPages: number = Math.ceil(this.needs.length / this.itemsPerPage); @@ -76,26 +76,33 @@ export class NeedListComponent { return (need.type === GoalType.MONETARY) ? "$" : ""; } + //increment/decrement decrementPage() { this.currentPage--; + localStorage.setItem('currentPage', this.currentPage.toString()); this.updateVisibleNeeds(); } incrementPage() { this.currentPage++; + localStorage.setItem('currentPage', this.currentPage.toString()); this.updateVisibleNeeds(); } + //skipping pages lastPage() { this.currentPage = this.totalPages - 1 + localStorage.setItem('currentPage', this.currentPage.toString()); this.updateVisibleNeeds() } firstPage() { this.currentPage = 0 + localStorage.setItem('currentPage', this.currentPage.toString()); this.updateVisibleNeeds() } + //user editing needs per page editNeedsPerPage() { if (this.itemsPerPage > this.searchResults.length) { this.itemsPerPage = this.searchResults.length; @@ -106,13 +113,16 @@ export class NeedListComponent { this.resetVisibleNeeds(); } + //refresing visible needs updateVisibleNeeds() { this.totalPages = Math.ceil(this.searchResults.length / this.itemsPerPage); this.visibleNeeds = this.searchResults.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); } + //reset back to first page and refresh needs resetVisibleNeeds() { this.currentPage = 0; + localStorage.setItem('currentPage', this.currentPage.toString()); this.updateVisibleNeeds(); } diff --git a/ufund-ui/src/app/components/need-page/need-page.component.html b/ufund-ui/src/app/components/need-page/need-page.component.html index f5f78f6..a4937ed 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.html +++ b/ufund-ui/src/app/components/need-page/need-page.component.html @@ -13,9 +13,9 @@ Target Goal: {{(need.type === GoalType.MONETARY) ? "$" : ""}}{{need.maxGoal}} - Amount Currently Collected: {{need.type.toString() == 'MONETARY' ? '$' : ''}}{{need.current}} + Amount Currently Collected: {{need.type.toString() == 'MONETARY' ? '$' : ''}}{{need.current}} - Location: {{need.location}} + Location: {{need.location}} Urgency: Not urgent -- cgit v1.2.3 From a5567b2ed092af3a50da75eef28d46c06760a266 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 10:48:31 -0400 Subject: Improve add to basket feedback --- .../src/app/components/cupboard/cupboard.component.html | 6 +++--- ufund-ui/src/app/components/cupboard/cupboard.component.ts | 14 +++++++++++++- .../funding-basket/funding-basket.component.html | 2 +- ufund-ui/src/app/components/toast/toast.component.html | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.html b/ufund-ui/src/app/components/cupboard/cupboard.component.html index c055c31..71c258e 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.html +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.html @@ -18,7 +18,7 @@ {{SortingAlgorithms[algorithm].display[sortMode === 'Ascending' ? 0 : 1]}} - @@ -30,8 +30,8 @@

No Results Found

- diff --git a/ufund-ui/src/app/components/toast/toast.component.html b/ufund-ui/src/app/components/toast/toast.component.html index dccf869..dc33ecd 100644 --- a/ufund-ui/src/app/components/toast/toast.component.html +++ b/ufund-ui/src/app/components/toast/toast.component.html @@ -1,6 +1,6 @@
{{this.message}} - {{this.action.label}} + {{this.action.label}} -- cgit v1.2.3 From 5a0bdd977d1e0f659c9ced795def86f031665759 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 13:29:37 -0400 Subject: Improve toast --- ufund-ui/src/app/components/toast/toast.component.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/toast/toast.component.css b/ufund-ui/src/app/components/toast/toast.component.css index 4cd81fe..7f68d0f 100644 --- a/ufund-ui/src/app/components/toast/toast.component.css +++ b/ufund-ui/src/app/components/toast/toast.component.css @@ -39,7 +39,7 @@ } } -.toast.hide { +.toast.hide:not(:has(:hover)) { transform: translateY(-90px); } -- cgit v1.2.3 From 0a876b31609144c62f312ea59f074f5f79b67ae7 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Fri, 4 Apr 2025 16:13:25 -0400 Subject: Made every invalid need input in basket turn red when invalid --- .../components/funding-basket/funding-basket.component.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts index 5d94124..a39b4f3 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts @@ -36,16 +36,21 @@ export class FundingBasketComponent implements OnInit { async checkout() { let order: { needID: number, quantity: number }[] = [] + let isNotValid = false for (let contribution of document.querySelectorAll('.contribution')!) { if (contribution.value == '' || contribution.valueAsNumber <= 0) { + isNotValid = true contribution.setAttribute("style", "border-color: #ff0000"); - this.toastService.sendToast(ToastType.ERROR, "Invalid input in funding basket!") - return; } order.push({needID: +contribution.id, quantity: contribution.valueAsNumber}); } + if (isNotValid) { + this.toastService.sendToast(ToastType.ERROR, "Invalid input in funding basket!") + return; + } + try { await firstValueFrom(this.cupboardService.checkoutNeed(order)) } catch (ex:any) { @@ -58,6 +63,9 @@ export class FundingBasketComponent implements OnInit { } resetColor(ev: any) { + for (let contribution of document.querySelectorAll('.contribution')!) { + + } (ev.target as HTMLInputElement).setAttribute("style", "border-color: unset") } } -- cgit v1.2.3 From d85eeb6918d521197c2e6ad1e3da2dec8ce95398 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 17:22:10 -0400 Subject: Edit modal --- ufund-ui/src/app/app.component.css | 17 ++++++ ufund-ui/src/app/app.component.html | 35 ++++++------ ufund-ui/src/app/app.component.ts | 2 + .../components/cupboard/cupboard.component.html | 8 +-- .../app/components/cupboard/cupboard.component.ts | 13 +++-- .../components/home-page/home-page.component.css | 2 +- .../components/need-edit/need-edit.component.css | 42 ++++++++++++--- .../components/need-edit/need-edit.component.html | 62 +++++++++++++++------- .../components/need-edit/need-edit.component.ts | 9 ++-- .../components/need-page/need-page.component.css | 4 +- .../components/need-page/need-page.component.html | 9 ++-- .../components/need-page/need-page.component.ts | 4 +- ufund-ui/src/app/services/modal.service.ts | 25 +++++++++ ufund-ui/src/styles.css | 4 +- 14 files changed, 176 insertions(+), 60 deletions(-) create mode 100644 ufund-ui/src/app/services/modal.service.ts (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/app.component.css b/ufund-ui/src/app/app.component.css index f4ed668..ee6e2f7 100644 --- a/ufund-ui/src/app/app.component.css +++ b/ufund-ui/src/app/app.component.css @@ -4,6 +4,23 @@ height: 100%; } +#scroll { + overflow: auto; + min-height: 100%; +} + +.modal { + position: absolute; + background-color: #000000a8; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: flex; + justify-content: center; + align-items: center; +} + #header { display: flex; flex-direction: row; diff --git a/ufund-ui/src/app/app.component.html b/ufund-ui/src/app/app.component.html index f697695..30a5347 100644 --- a/ufund-ui/src/app/app.component.html +++ b/ufund-ui/src/app/app.component.html @@ -1,17 +1,22 @@ - - - @@ -73,4 +74,3 @@ - diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index bde8e27..f5e4c00 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -9,6 +9,7 @@ import {ToastsService, ToastType} from '../../services/toasts.service'; import {UsersService} from '../../services/users.service'; import {SortingAlgoArrays} from './sorting'; import {Router} from '@angular/router'; +import {ModalService} from '../../services/modal.service'; @Component({ selector: 'app-cupboard', @@ -29,13 +30,15 @@ export class CupboardComponent implements OnInit { sortMode: 'Ascending' | 'Descending' = 'Ascending' itemsPerPage = 5; currentSortAlgo = 'sortByPriority'; + // activeEdit?: Need; constructor( private cupboardService: CupboardService, private authService: AuthService, private toastService: ToastsService, protected usersService: UsersService, - private router: Router + private router: Router, + protected modalService: ModalService ) {} ngOnInit(): void { @@ -149,6 +152,10 @@ export class CupboardComponent implements OnInit { return basket?.map(r => r.id).includes(need.id); } + // editNeed(need : Need) { + // this.activeEdit = need + // } + // --------------- FORM STUFF NOT IMPLEMENTED YET --------------- // // async updateSearchResults() { @@ -182,7 +189,7 @@ export class CupboardComponent implements OnInit { // this.selectedNeed = { ...need }; // } // - select(need : Need) { + // select(need : Need) { // //emit value // // this.currentNeed.emit(need); // if (this.selectedNeed) { @@ -212,7 +219,7 @@ export class CupboardComponent implements OnInit { // if (button) { // button.style.visibility = 'hidden'; // } - } + // } // submit(form: any) { // const need: Need = { diff --git a/ufund-ui/src/app/components/home-page/home-page.component.css b/ufund-ui/src/app/components/home-page/home-page.component.css index a10377f..b64be18 100644 --- a/ufund-ui/src/app/components/home-page/home-page.component.css +++ b/ufund-ui/src/app/components/home-page/home-page.component.css @@ -1,5 +1,5 @@ :host { - height: 100%; + flex-grow: 1; display: flex; flex-direction: column; align-items: center; diff --git a/ufund-ui/src/app/components/need-edit/need-edit.component.css b/ufund-ui/src/app/components/need-edit/need-edit.component.css index 17605c2..b06e061 100644 --- a/ufund-ui/src/app/components/need-edit/need-edit.component.css +++ b/ufund-ui/src/app/components/need-edit/need-edit.component.css @@ -1,4 +1,4 @@ -:host { +#box { /*position: absolute;*/ /*background-color: rgba(0, 0, 0, 0.5);*/ /*display: flex;*/ @@ -8,14 +8,44 @@ /*right: 0;*/ /*z-index: 5;*/ /*justify-content: center;*/ + padding: 10px; + border-style: solid; + border-width: 1px; + border-color: #6c6c6c; + background-color: #2e2e2e; + border-radius: 5px; + position: relative; + width: 500px; } #create-form, #delete-form, #update-form { - margin-top: 50px; - background-color: #3a3a3a; - padding: 10px 20px 20px 20px; - border: 2px solid #000; - border-radius: 5px; + /*margin-top: 50px;*/ + + /*padding: 10px 20px 20px 20px;*/ + /*border: 2px solid #000;*/ + /*border-radius: 5px;*/ /*visibility: visible;*/ /*position: absolute;*/ + display: flex; + flex-direction: column; + gap: 10px; + + div { + display: flex; + flex-direction: column; + } +} + +#closeButton { + position: absolute; + width: 35px; + right: 10px; +} + +textarea { + height: 200px; +} + +label { + padding: 3px; } diff --git a/ufund-ui/src/app/components/need-edit/need-edit.component.html b/ufund-ui/src/app/components/need-edit/need-edit.component.html index e776415..60637ed 100644 --- a/ufund-ui/src/app/components/need-edit/need-edit.component.html +++ b/ufund-ui/src/app/components/need-edit/need-edit.component.html @@ -1,23 +1,45 @@ -
-

Update Need

-
-
-
-
-
-
-
-
-
-
- -
- -
- -
-
-
+
+ +

Update Need

+ +
+ Name: + +
+
+ Image: + +
+
+ Location: + +
+
+ Max Goal: + +
+
+ Type: + + +
+
+ Urgency: + +
+
+ Description: + +
diff --git a/ufund-ui/src/app/components/need-edit/need-edit.component.ts b/ufund-ui/src/app/components/need-edit/need-edit.component.ts index 2462534..d312183 100644 --- a/ufund-ui/src/app/components/need-edit/need-edit.component.ts +++ b/ufund-ui/src/app/components/need-edit/need-edit.component.ts @@ -1,8 +1,9 @@ import { Component, Input, Output, EventEmitter } from '@angular/core'; -import { Need, GoalType } from '../../models/Need'; +import { Need, GoalType } from '../../models/Need'; import { CupboardService } from '../../services/cupboard.service'; import { catchError, of } from 'rxjs'; import { ToastsService, ToastType } from '../../services/toasts.service'; +import {ModalService} from '../../services/modal.service'; @Component({ selector: 'app-need-edit', @@ -13,8 +14,8 @@ import { ToastsService, ToastType } from '../../services/toasts.service'; export class NeedEditComponent { constructor( private cupboardService: CupboardService, - private toastService: ToastsService - + private toastService: ToastsService, + protected modalService: ModalService ) {} @Input() selectedNeed!: Need; @@ -58,4 +59,4 @@ export class NeedEditComponent { ); } -} \ No newline at end of file +} diff --git a/ufund-ui/src/app/components/need-page/need-page.component.css b/ufund-ui/src/app/components/need-page/need-page.component.css index 844410f..5a92ee9 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.css +++ b/ufund-ui/src/app/components/need-page/need-page.component.css @@ -56,8 +56,8 @@ .actionArea { display: flex; - padding: 5px; - gap: 5px; + padding: 5px 0; + gap: 10px; margin-top: 10px; } diff --git a/ufund-ui/src/app/components/need-page/need-page.component.html b/ufund-ui/src/app/components/need-page/need-page.component.html index 522b710..d494ccf 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.html +++ b/ufund-ui/src/app/components/need-page/need-page.component.html @@ -35,9 +35,12 @@ - - - + + + + diff --git a/ufund-ui/src/app/components/need-page/need-page.component.ts b/ufund-ui/src/app/components/need-page/need-page.component.ts index ad4cacf..e9be093 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.ts +++ b/ufund-ui/src/app/components/need-page/need-page.component.ts @@ -7,6 +7,7 @@ import {AuthService} from '../../services/auth.service'; import {catchError, of} from 'rxjs'; import {ToastsService, ToastType} from '../../services/toasts.service'; import {UsersService} from '../../services/users.service'; +import {ModalService} from '../../services/modal.service'; @Component({ selector: 'app-need-page', @@ -21,7 +22,8 @@ export class NeedPageComponent { private authService: AuthService, private usersService: UsersService, private toastService: ToastsService, - private router: Router + private router: Router, + protected modalService: ModalService ) {} public GoalType = GoalType; diff --git a/ufund-ui/src/app/services/modal.service.ts b/ufund-ui/src/app/services/modal.service.ts new file mode 100644 index 0000000..04f2f3a --- /dev/null +++ b/ufund-ui/src/app/services/modal.service.ts @@ -0,0 +1,25 @@ +import {Injectable, TemplateRef} from '@angular/core'; +import {BehaviorSubject} from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ModalService { + + private modal = new BehaviorSubject | null>(null) + + constructor() {} + + showModal(template: TemplateRef) { + console.log("got here", template) + this.modal.next(template) + } + + hideModal() { + this.modal.next(null) + } + + getModalBehaviorSubject() { + return this.modal; + } +} diff --git a/ufund-ui/src/styles.css b/ufund-ui/src/styles.css index 75d6b36..d623726 100644 --- a/ufund-ui/src/styles.css +++ b/ufund-ui/src/styles.css @@ -19,7 +19,9 @@ body { font-optical-sizing: auto; } -input { +input, textarea { + resize: none; + font-family: Inter, sans-serif; font-size: 14pt; padding: 5px; border-radius: 5px; -- cgit v1.2.3 From 49a82c8a8a7a3ea3cfd30572944510f3aa18663d Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 19:12:53 -0400 Subject: Fix home screen being too short --- ufund-ui/src/app/app.component.css | 2 ++ 1 file changed, 2 insertions(+) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/app.component.css b/ufund-ui/src/app/app.component.css index ee6e2f7..6cbd45b 100644 --- a/ufund-ui/src/app/app.component.css +++ b/ufund-ui/src/app/app.component.css @@ -7,6 +7,8 @@ #scroll { overflow: auto; min-height: 100%; + display: flex; + flex-direction: column; } .modal { -- cgit v1.2.3 From 47a4326827a4123792e3a1165843344c8a9c7b0d Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 19:15:33 -0400 Subject: Fix modal z-index --- ufund-ui/src/app/app.component.css | 1 + ufund-ui/src/app/components/toast/toast.component.css | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/app.component.css b/ufund-ui/src/app/app.component.css index 6cbd45b..8b6b28a 100644 --- a/ufund-ui/src/app/app.component.css +++ b/ufund-ui/src/app/app.component.css @@ -21,6 +21,7 @@ display: flex; justify-content: center; align-items: center; + z-index: 3; } #header { diff --git a/ufund-ui/src/app/components/toast/toast.component.css b/ufund-ui/src/app/components/toast/toast.component.css index 7f68d0f..82e2ff3 100644 --- a/ufund-ui/src/app/components/toast/toast.component.css +++ b/ufund-ui/src/app/components/toast/toast.component.css @@ -14,7 +14,7 @@ animation: slideDown .5s ease-in-out; transition: transform .5s; align-self: center; - z-index: 3; + z-index: 4; position: absolute; top: 15px; display: flex; -- cgit v1.2.3 From feba88fed855d1694d292e401a4cb336e0ff9d69 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 20:56:02 -0400 Subject: Fix create-need dialog --- .../components/cupboard/cupboard.component.html | 36 +---- .../app/components/cupboard/cupboard.component.ts | 108 -------------- .../components/need-edit/need-edit.component.html | 20 +-- .../components/need-edit/need-edit.component.ts | 155 ++++++++++++++------- .../components/need-page/need-page.component.html | 2 +- .../components/need-page/need-page.component.ts | 16 +-- ufund-ui/src/app/models/Need.ts | 4 +- ufund-ui/src/app/services/cupboard.service.ts | 4 +- 8 files changed, 126 insertions(+), 219 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.html b/ufund-ui/src/app/components/cupboard/cupboard.component.html index 5e88582..3873719 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.html +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.html @@ -1,7 +1,10 @@
@@ -34,7 +37,7 @@ {{inBasket(usersService.getBasket() | async, need)? "check": "add" }}Add To Basket - +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index f5e4c00..56fdd70 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -152,114 +152,6 @@ export class CupboardComponent implements OnInit { return basket?.map(r => r.id).includes(need.id); } - // editNeed(need : Need) { - // this.activeEdit = need - // } - - // --------------- FORM STUFF NOT IMPLEMENTED YET --------------- // - - // async updateSearchResults() { - // if (this.needList) { - // while (this.selectedForm == 'update') { - // // this.searchResults = this.needList.searchResults - // await new Promise(resolve => setTimeout(resolve, 100)); - // } - // } - // } - - selectForm(name: string) { - // //get search results from the need list - // if (this.needList) { - // // this.searchResults = this.needList.searchResults; - // } - // console.log(this.searchResults) - // this.selectedForm = name; - // if (name == 'update') { - // if (this.searchResults) { - // this.searchResults.forEach((element: any) => { - // console.log(element) - // }); - // } - // - // } - } - - // populateForm(need: any): void { - // this.selectForm('update'); - // this.selectedNeed = { ...need }; - // } - // - // select(need : Need) { - // //emit value - // // this.currentNeed.emit(need); - // if (this.selectedNeed) { - // //revert already selected need to previous style - // console.log(need.id); - // let button = document.getElementById('need-button-' + this.selectedNeed.id); - // if (button) { - // console.log(button) - // button.style.background = 'lightgray'; - // button.style.marginLeft = '0%'; - // button.style.width = '98%'; - // } - // button = document.getElementById('need-edit-button-' + this.selectedNeed.id); - // if (button) { - // button.style.visibility = 'visible'; - // } - // } - // //change selected need to selected style - // this.selectedNeed = need; - // let button = document.getElementById('need-button-' + need.id); - // if (button) { - // button.style.background = 'white'; - // button.style.marginLeft = '4%'; - // button.style.width = '100%'; - // } - // button = document.getElementById('need-edit-button-' + need.id); - // if (button) { - // button.style.visibility = 'hidden'; - // } - // } - - // submit(form: any) { - // const need: Need = { - // name: form.name, - // image: form.image, - // location: form.location, - // id: 0, - // maxGoal: form.maxGoal, - // type: form.type, - // urgent: !!form.urgent, - // filterAttributes: [], - // current: 0, - // description: form.description - // }; - // console.log("need:", need); - // console.log("form submitted. creating need: ", need); - // this.cupboardService.createNeed(need) - // .pipe(catchError((ex, _) => { - // if (ex.status == 500) { - // this.toastService.sendToast(ToastType.ERROR, "Fields cannot be blank"); - // } else if (ex.status == 400) { - // this.toastService.sendToast(ToastType.ERROR, ex.error); - // } else { - // this.toastService.sendToast(ToastType.ERROR, "Error on creating need"); - // } - // return of() - // })) - // .subscribe( - // (result) => { - // if (result) { - // console.log("need created successfully"); - // // this.needList?.refresh() - // } else { - // console.log("need creation failed"); - // } - // } - // - // ); - // } - protected readonly SortingAlgorithms = SortingAlgoArrays; protected readonly Object = Object; } diff --git a/ufund-ui/src/app/components/need-edit/need-edit.component.html b/ufund-ui/src/app/components/need-edit/need-edit.component.html index 60637ed..ed4bfb3 100644 --- a/ufund-ui/src/app/components/need-edit/need-edit.component.html +++ b/ufund-ui/src/app/components/need-edit/need-edit.component.html @@ -1,44 +1,44 @@
-

Update Need

-
+

{{this.mode}} Need

+
Name: - +
Image: - +
Location: - +
Max Goal: - +
Type:
Urgency:
Description: - +
diff --git a/ufund-ui/src/app/components/need-edit/need-edit.component.ts b/ufund-ui/src/app/components/need-edit/need-edit.component.ts index d312183..abfa543 100644 --- a/ufund-ui/src/app/components/need-edit/need-edit.component.ts +++ b/ufund-ui/src/app/components/need-edit/need-edit.component.ts @@ -1,62 +1,109 @@ -import { Component, Input, Output, EventEmitter } from '@angular/core'; -import { Need, GoalType } from '../../models/Need'; -import { CupboardService } from '../../services/cupboard.service'; -import { catchError, of } from 'rxjs'; -import { ToastsService, ToastType } from '../../services/toasts.service'; +import {Component, EventEmitter, Input, OnChanges, Output} from '@angular/core'; +import {GoalType, Need} from '../../models/Need'; +import {CupboardService} from '../../services/cupboard.service'; +import {catchError, of} from 'rxjs'; +import {ToastsService, ToastType} from '../../services/toasts.service'; import {ModalService} from '../../services/modal.service'; +import {Router} from '@angular/router'; @Component({ - selector: 'app-need-edit', - standalone: false, - templateUrl: './need-edit.component.html', - styleUrl: './need-edit.component.css' + selector: 'app-need-edit', + standalone: false, + templateUrl: './need-edit.component.html', + styleUrl: './need-edit.component.css' }) -export class NeedEditComponent { - constructor( - private cupboardService: CupboardService, - private toastService: ToastsService, - protected modalService: ModalService - ) {} - - @Input() selectedNeed!: Need; - @Output() refreshNeedList = new EventEmitter(); - - update(form: any) { - console.log(form); - const need: Need = { - name: form.name, - image: form.image, - location: form.location, - id: this.selectedNeed.id, //system will control this - maxGoal: form.maxGoal, - type: GoalType[form.type as keyof typeof GoalType], - urgent: form.urgent, - filterAttributes: [], - current: 0, - description: form.description - }; - - this.cupboardService.updateNeed(need.id, need) - .pipe(catchError((ex, _) => { - if (ex.status == 500) { - this.toastService.sendToast(ToastType.ERROR, 'Fields cannot be blank'); - } else if (ex.status == 400) { - this.toastService.sendToast(ToastType.ERROR, ex.error); - } else { - this.toastService.sendToast(ToastType.ERROR, "Error on creating need"); - } - return of() - })) - .subscribe( - (result) => { - if (result) { - console.log("need updated successfully"); - this.refreshNeedList.emit(); +export class NeedEditComponent implements OnChanges { + + @Input() mode!: "Create" | "Edit" + @Input() need?: Need; + @Output() refreshNeedList = new EventEmitter(); + + needCopy: any = {} + + constructor( + private cupboardService: CupboardService, + private toastService: ToastsService, + protected modalService: ModalService, + private router: Router + ) {} + + ngOnChanges() { + this.needCopy = {...this.need} + } + + submit(form: any) { + const need: Need = { + name: form.name, + image: form.image, + location: form.location, + id: this.needCopy.id, //system will control this + maxGoal: form.maxGoal, + type: GoalType[form.type as keyof typeof GoalType], + urgent: form.urgent, + filterAttributes: [], + current: 0, + description: form.description + }; + + if (this.mode == "Edit") { + this.updateNeed(need) + } else if (this.mode === 'Create') { + this.createNeed(need) + } + } + + updateNeed(need: Need) { + this.cupboardService.updateNeed(need.id, need) + .pipe(catchError((ex, _) => { + if (ex.status == 500) { + this.toastService.sendToast(ToastType.ERROR, 'Fields cannot be blank'); + } else if (ex.status == 400) { + this.toastService.sendToast(ToastType.ERROR, ex.error); + } else { + this.toastService.sendToast(ToastType.ERROR, "Error on creating need"); + } + return of() + })) + .subscribe( + (result) => { + if (result) { + let action = {label: 'View Need', onAction: () => this.router.navigate([`/need/${need.id}`])} + this.toastService.sendToast(ToastType.INFO, "Need updated successfully", action) + this.modalService.hideModal() + console.log("need updated successfully"); + this.refreshNeedList.emit(); + } else { + console.log("need update failed"); + } + } + ); + } + + createNeed(need: Need) { + this.cupboardService.createNeed(need) + .pipe(catchError((ex, _) => { + if (ex.status == 500) { + this.toastService.sendToast(ToastType.ERROR, "Fields cannot be blank"); + } else if (ex.status == 400) { + this.toastService.sendToast(ToastType.ERROR, ex.error); } else { - console.log("need update failed"); + this.toastService.sendToast(ToastType.ERROR, "Error on creating need"); + } + return of() + })) + .subscribe( + (result) => { + if (result) { + let action = {label: 'View Need', onAction: () => this.router.navigate([`/need/${result.id}`])} + this.toastService.sendToast(ToastType.INFO, "Need created successfully", action) + this.modalService.hideModal() + console.log("need created successfully"); + this.refreshNeedList.emit(); + } else { + console.log("need creation failed"); + } } - } - ); - } + ); + } } diff --git a/ufund-ui/src/app/components/need-page/need-page.component.html b/ufund-ui/src/app/components/need-page/need-page.component.html index d494ccf..aed3e95 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.html +++ b/ufund-ui/src/app/components/need-page/need-page.component.html @@ -36,7 +36,7 @@ addAdd To Basket - + +
@@ -33,16 +33,16 @@

No Results Found

- - - diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index 56fdd70..289618f 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -1,7 +1,6 @@ import {Component, OnInit, ViewChild} from '@angular/core'; import {CupboardService} from '../../services/cupboard.service'; import {Need} from '../../models/Need'; -import {userType} from '../../models/User'; import {catchError, of} from 'rxjs'; import {NeedListComponent} from '../need-list/need-list.component'; import {AuthService} from '../../services/auth.service'; @@ -138,20 +137,6 @@ export class CupboardComponent implements OnInit { } } - isManager() { - return this.authService.getCurrentUser()?.type === userType.MANAGER - } - - isHelper() { - return this.authService.getCurrentUser()?.type === userType.HELPER - } - - inBasket(basket: Need[] | null, need: Need) { - console.log(basket) - console.log(need) - return basket?.map(r => r.id).includes(need.id); - } - protected readonly SortingAlgorithms = SortingAlgoArrays; protected readonly Object = Object; } diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html index edc9609..fcd5437 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html @@ -1,19 +1,24 @@
-

Funding Basket

- - - - + @if ((authService.getCurrentUserSubject() | async)?.type === userType.HELPER) { +

Funding Basket

+ + + + + + +
+
- -
- diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts index 0b3b9f3..767327e 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts @@ -5,6 +5,7 @@ import {CupboardService} from '../../services/cupboard.service'; import {catchError, firstValueFrom, Observable, of} from 'rxjs'; import {AuthService} from '../../services/auth.service'; import {ToastsService, ToastType} from '../../services/toasts.service'; +import {userType} from '../../models/User'; @Component({ selector: 'app-funding-basket', @@ -18,19 +19,14 @@ export class FundingBasketComponent implements OnInit { private router: Router, protected cupboardService: CupboardService, protected usersService: UsersService, - private authService: AuthService, + protected authService: AuthService, private toastService: ToastsService ) {} @ViewChild("contribution") contribution?: Input; @Input() isValid: boolean = true; - // this is for login rerouting ngOnInit(): void { - if (!this.authService.getCurrentUser()) { - this.router.navigate(['/login'], {queryParams: {redir: this.router.url}}); - return; - } this.usersService.refreshBasket(); // this.usersService.removeNeed(); <- call this to remove @@ -92,4 +88,5 @@ export class FundingBasketComponent implements OnInit { protected readonly of = of; + protected readonly userType = userType; } diff --git a/ufund-ui/src/app/components/need-edit/need-edit.component.html b/ufund-ui/src/app/components/need-edit/need-edit.component.html index ed4bfb3..0adbda7 100644 --- a/ufund-ui/src/app/components/need-edit/need-edit.component.html +++ b/ufund-ui/src/app/components/need-edit/need-edit.component.html @@ -11,7 +11,6 @@
- Location:
diff --git a/ufund-ui/src/app/components/need-list/need-list.component.css b/ufund-ui/src/app/components/need-list/need-list.component.css index 582b832..622b64a 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.css +++ b/ufund-ui/src/app/components/need-list/need-list.component.css @@ -72,6 +72,10 @@ gap: 5px; } +.actionArea:empty { + padding: 0; +} + #page-selector { display: flex; align-items: center; diff --git a/ufund-ui/src/app/components/need-page/need-page.component.html b/ufund-ui/src/app/components/need-page/need-page.component.html index aed3e95..592a71e 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.html +++ b/ufund-ui/src/app/components/need-page/need-page.component.html @@ -1,48 +1,52 @@
-

{{need.name}}

- {{need.type}} GOAL - - Need image - -

{{need.description}}

-
- - - This goal is {{(((need.current)*100) / (need.maxGoal)).toFixed(0)}}% complete! -
- - Target Goal: {{need.maxGoal}} - - Amount Currently Collected: {{need.current}} - - Location: {{need.location}} - - Urgency: - Not urgent - URGENT - - -
- Tags: -
    -
  • -

    {{tag}}

    -
  • -
-
- -
- - - - - - -
+ @if (need) { +

{{need.name}}

+ {{need.type}} GOAL + + Need image + +

{{need.description}}

+
+ + This goal is {{(((need.current)*100) / (need.maxGoal)).toFixed(0)}}% complete! +
+ + Target Goal: {{need.maxGoal}} + + Amount Currently Collected: {{need.current}} + + Location: {{need.location}} + + Urgency: + Not urgent + URGENT + + +
+ Tags: +
    +
  • +

    {{tag}}

    +
  • +
+
+ +
+ + + + + + +
+ } @else { +

Need not found

+ The requested need does not exist. Browse the cupboard + }
diff --git a/ufund-ui/src/app/components/need-page/need-page.component.ts b/ufund-ui/src/app/components/need-page/need-page.component.ts index 1c6d8e4..45d6db5 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.ts +++ b/ufund-ui/src/app/components/need-page/need-page.component.ts @@ -2,7 +2,6 @@ import {Component, Input, OnInit} from '@angular/core'; import {Need} from '../../models/Need'; import {ActivatedRoute, Router} from "@angular/router"; import {CupboardService} from "../../services/cupboard.service"; -import {userType} from '../../models/User'; import {AuthService} from '../../services/auth.service'; import {catchError, of} from 'rxjs'; import {ToastsService, ToastType} from '../../services/toasts.service'; @@ -20,7 +19,7 @@ export class NeedPageComponent implements OnInit { private route: ActivatedRoute, private cupboardService: CupboardService, private authService: AuthService, - private usersService: UsersService, + protected usersService: UsersService, private toastService: ToastsService, private router: Router, protected modalService: ModalService @@ -37,16 +36,6 @@ export class NeedPageComponent implements OnInit { window.history.back(); } - isManager() { - const type = this.authService.getCurrentUser()?.type; - return type === ("MANAGER" as unknown as userType); - } - - isHelper() { - const type = this.authService.getCurrentUser()?.type; - return type === ("HELPER" as unknown as userType); - } - add(need: Need) { const currentUser = this.authService.getCurrentUser(); //console.log("get current user in angular:", currentUser) diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts index 4080ebf..d23b59b 100644 --- a/ufund-ui/src/app/services/users.service.ts +++ b/ufund-ui/src/app/services/users.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; import {BehaviorSubject, catchError, firstValueFrom, Observable, of} from 'rxjs'; -import {User} from '../models/User'; +import {User, userType} from '../models/User'; import { Need } from '../models/Need'; import { CupboardService } from './cupboard.service'; import {AuthService} from './auth.service'; @@ -25,7 +25,9 @@ export class UsersService { private http: HttpClient, private cupboardService: CupboardService, private authService: AuthService - ) {} + ) { + authService.getCurrentUserSubject().subscribe(() => this.refreshBasket()) + } async createUser(username:string, password:string) { await firstValueFrom(this.http.post(this.url, {username: username, password: password}, this.httpOptions())) @@ -71,4 +73,16 @@ export class UsersService { return this.basket; } + isManager() { + return this.authService.getCurrentUser()?.type === userType.MANAGER + } + + isHelper() { + return this.authService.getCurrentUser()?.type === userType.HELPER + } + + inBasket(basket: Need[] | null, need: Need) { + return basket?.map(r => r.id).includes(need.id); + } + } -- cgit v1.2.3 From edfc9a8450d140ba1650b38e4b707000710b2faa Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 22:08:54 -0400 Subject: Fix button alignment on chrome --- ufund-ui/src/styles.css | 1 + 1 file changed, 1 insertion(+) (limited to 'ufund-ui') diff --git a/ufund-ui/src/styles.css b/ufund-ui/src/styles.css index d623726..527408a 100644 --- a/ufund-ui/src/styles.css +++ b/ufund-ui/src/styles.css @@ -44,6 +44,7 @@ button, input[type=button], input[type=reset], input[type=submit], .button { gap: 5px; text-align: center; justify-content: center; + align-items: center; &:hover { background-color: light-dark(#e1e1e1, #444444); -- cgit v1.2.3 From 7c49fcd788692a898e985cb156dd9fd910c05790 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 23:14:14 -0400 Subject: Add new authentication to dashboard, cleanup --- .../app/components/cupboard/cupboard.component.ts | 2 -- .../components/dashboard/dashboard.component.css | 10 ++++--- .../components/dashboard/dashboard.component.html | 31 ++++++++++++---------- .../components/dashboard/dashboard.component.ts | 2 ++ .../mini-need-list/mini-need-list.component.css | 4 +++ .../mini-need-list/mini-need-list.component.html | 2 +- .../mini-need-list/mini-need-list.component.ts | 2 +- .../components/need-page/need-page.component.ts | 4 ++- 8 files changed, 35 insertions(+), 22 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index 289618f..b03b77e 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -23,13 +23,11 @@ export class CupboardComponent implements OnInit { @ViewChild("needList") needList?: NeedListComponent private searchDelay: any; - // selectedNeed: Need | null = null; needs: Need[] = []; searchResults: Need[] = []; sortMode: 'Ascending' | 'Descending' = 'Ascending' itemsPerPage = 5; currentSortAlgo = 'sortByPriority'; - // activeEdit?: Need; constructor( private cupboardService: CupboardService, diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.css b/ufund-ui/src/app/components/dashboard/dashboard.component.css index 185fdc2..742a151 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.css +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.css @@ -1,7 +1,11 @@ :host { display: flex; - flex-direction: column; + justify-content: center; +} + +#box { + display: flex; width: 800px; - align-self: center; - gap: 20px + flex-direction: column; + gap: 10px; } diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.html b/ufund-ui/src/app/components/dashboard/dashboard.component.html index 2af467c..074ca76 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.html +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.html @@ -1,14 +1,17 @@ - -

Admin Dashboard

- - - -_ Registered users - {{count | async}} -_ Fulfilled needs - -_ Most fulfilled needs - -_ Total monetary contributions -${{totalDonations | async}} -_ +
+ @if ((authService.getCurrentUserSubject() | async)?.type === userType.MANAGER) { +

Admin Dashboard

+ _ Registered users + {{count | async}} + _ Fulfilled needs + + _ Most fulfilled needs + + _ Total monetary contributions + ${{totalDonations | async}} + _ + } @else { +

Unauthorized

+ This page requires you to be logged in as an admin! Log In + } +
diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.ts b/ufund-ui/src/app/components/dashboard/dashboard.component.ts index 9bf7627..1181608 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.ts +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.ts @@ -5,6 +5,7 @@ import {CupboardService} from '../../services/cupboard.service'; import {UsersService} from '../../services/users.service'; import {BehaviorSubject} from 'rxjs'; import {GoalType, Need} from '../../models/Need'; +import {userType} from '../../models/User'; @Component({ selector: 'app-dashboard', @@ -55,4 +56,5 @@ export class DashboardComponent implements OnInit{ } + protected readonly userType = userType; } diff --git a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css index 090bea9..d01ac54 100644 --- a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css +++ b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css @@ -45,6 +45,10 @@ cursor: pointer; } +.needEntry:hover { + background-color: #444444; +} + .needName { font-weight: bold; } diff --git a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html index 9febfa5..91f51cf 100644 --- a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html +++ b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html @@ -1,5 +1,5 @@ diff --git a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.ts b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.ts index c909ae6..18b176b 100644 --- a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.ts +++ b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.ts @@ -10,7 +10,7 @@ import {Need} from '../../models/Need'; export class MiniNeedListComponent { @Input() needList?: Need[] - @Input() jtitle?: string + @Input() label?: string @Input() url?: string constructor( diff --git a/ufund-ui/src/app/components/need-page/need-page.component.ts b/ufund-ui/src/app/components/need-page/need-page.component.ts index 45d6db5..0967266 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.ts +++ b/ufund-ui/src/app/components/need-page/need-page.component.ts @@ -1,5 +1,5 @@ import {Component, Input, OnInit} from '@angular/core'; -import {Need} from '../../models/Need'; +import {GoalType, Need} from '../../models/Need'; import {ActivatedRoute, Router} from "@angular/router"; import {CupboardService} from "../../services/cupboard.service"; import {AuthService} from '../../services/auth.service'; @@ -69,4 +69,6 @@ export class NeedPageComponent implements OnInit { }) // this.refresh(); } + + readonly GoalType = GoalType } -- cgit v1.2.3 From 1dcb3ca5345609fa03e73f58693327cee7ab7cf9 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 23:25:54 -0400 Subject: Add sort by type --- ufund-ui/src/app/components/cupboard/sorting.ts | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/cupboard/sorting.ts b/ufund-ui/src/app/components/cupboard/sorting.ts index 73a5ba9..5c37019 100644 --- a/ufund-ui/src/app/components/cupboard/sorting.ts +++ b/ufund-ui/src/app/components/cupboard/sorting.ts @@ -49,10 +49,21 @@ const sortByLocation: sortAlgo = (a: Need, b: Need): number => { return 1; } +const sortByType: sortAlgo = (a:Need, b:Need): number => { + if(a.type == b.type) { + return sortByName(a,b); + } + else if(a.type > b.type) { + return -1; + } + return 1; +} + export const SortingAlgoArrays: {[key: string]: { func: sortAlgo, display: [string, string]}} = { - sortByPriority: { func: sortByPriority, display: ["Highest Priority", "Lowest Priority" ] }, - sortByName: { func: sortByName, display: ["Name (A to Z)", "Name (Z to A)" ] }, - sortByLocation: { func: sortByLocation, display: ["Location (A to Z)", "Location (Z to A)" ] }, - sortByCompletion: { func: sortByCompletion, display: ["Most Completed", "Least Completed" ] }, - sortByGoal: { func: sortByGoal, display: ["Largest Maximum Goal", "Smallest Maximum Goal" ] }, + sortByPriority: { func: sortByPriority, display: ["Highest Priority", "Lowest Priority" ] }, + sortByName: { func: sortByName, display: ["Name (A to Z)", "Name (Z to A)" ] }, + sortByLocation: { func: sortByLocation, display: ["Location (A to Z)", "Location (Z to A)" ] }, + sortByCompletion: { func: sortByCompletion, display: ["Most Completed", "Least Completed" ] }, + sortByGoal: { func: sortByGoal, display: ["Largest Maximum Goal", "Smallest Maximum Goal" ] }, + sortByType: { func: sortByType, display: ["Type (Physical first)", "Type (Monetary first)" ] }, }; -- cgit v1.2.3 From 814b9c0f281176cb4e5787e47292754493427fca Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 23:28:05 -0400 Subject: fix missing location label --- ufund-ui/src/app/components/need-edit/need-edit.component.html | 1 + 1 file changed, 1 insertion(+) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/need-edit/need-edit.component.html b/ufund-ui/src/app/components/need-edit/need-edit.component.html index 0adbda7..ed4bfb3 100644 --- a/ufund-ui/src/app/components/need-edit/need-edit.component.html +++ b/ufund-ui/src/app/components/need-edit/need-edit.component.html @@ -11,6 +11,7 @@
+ Location:
-- cgit v1.2.3 From 483f74a420f7aabe4ef0eeafeb668bd9f0da12be Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 23:33:19 -0400 Subject: Remove old auth redirect code --- ufund-ui/src/app/components/dashboard/dashboard.component.ts | 6 ------ 1 file changed, 6 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.ts b/ufund-ui/src/app/components/dashboard/dashboard.component.ts index 1181608..2ab4db2 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.ts +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.ts @@ -29,12 +29,6 @@ export class DashboardComponent implements OnInit{ ) {} ngOnInit() { - let user = this.authService.getCurrentUser() - if(!localStorage.getItem("credential") && !user) { - this.router.navigate(['/login']) - return - } - this.userService.getCount().subscribe(count => this.count.next(count)) this.cupboardService.getNeeds().subscribe(needs => { let totalValue = 0 -- cgit v1.2.3 From 04db6f32b249ffb17c571cd6b16c8c54397f0be4 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 4 Apr 2025 23:55:19 -0400 Subject: Fix typos --- .../src/app/components/funding-basket/funding-basket.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts index c807271..371015a 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts @@ -2,7 +2,7 @@ import {Component, Input, OnInit, ViewChild} from '@angular/core'; import {UsersService} from '../../services/users.service'; import {Router} from '@angular/router'; import {CupboardService} from '../../services/cupboard.service'; -import {catchError, firstValueFrom, Observable, of} from 'rxjs'; +import {firstValueFrom, of} from 'rxjs'; import {AuthService} from '../../services/auth.service'; import {ToastsService, ToastType} from '../../services/toasts.service'; import {userType} from '../../models/User'; @@ -60,7 +60,7 @@ export class FundingBasketComponent implements OnInit { resetColor(ev: any) { // for (let contribution of document.querySelectorAll('.contribution')!) {} - (ev.target as HTMLInputElement).setAttribute("style", "border-color: unset") + (ev.target as HTMLInputElement).setAttribute("style", "") } protected readonly of = of; -- cgit v1.2.3 From d7c974a98de42f41f3ca16f961d2dee96a3c4313 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Sat, 5 Apr 2025 13:17:29 -0400 Subject: Added running total to checkout --- .../components/funding-basket/funding-basket.component.css | 8 ++++++++ .../funding-basket/funding-basket.component.html | 1 + .../components/funding-basket/funding-basket.component.ts | 14 ++++++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.css b/ufund-ui/src/app/components/funding-basket/funding-basket.component.css index c46ef57..a1485a0 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.css +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.css @@ -80,3 +80,11 @@ padding: 5px; gap: 5px; } + +#footer { + display: flex; + flex-direction: row; + align-items: center; + gap: 20px; + margin-bottom: 10px; +} diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html index bba66a3..67e1083 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html @@ -36,6 +36,7 @@
diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts index a39b4f3..a0ba609 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts @@ -2,9 +2,10 @@ import {Component, Input, OnInit, ViewChild} from '@angular/core'; import {UsersService} from '../../services/users.service'; import {Router} from '@angular/router'; import {CupboardService} from '../../services/cupboard.service'; -import {firstValueFrom} from 'rxjs'; +import {BehaviorSubject, firstValueFrom} from 'rxjs'; import {AuthService} from '../../services/auth.service'; import {ToastsService, ToastType} from '../../services/toasts.service'; +import {GoalType} from '../../models/Need'; @Component({ selector: 'app-funding-basket', @@ -22,6 +23,7 @@ export class FundingBasketComponent implements OnInit { private toastService: ToastsService ) {} + public runningTotal = new BehaviorSubject(0) @ViewChild("contribution") contribution?: Input; // this is for login rerouting @@ -63,9 +65,17 @@ export class FundingBasketComponent implements OnInit { } resetColor(ev: any) { + let total = 0 + this.runningTotal.next(total); for (let contribution of document.querySelectorAll('.contribution')!) { - + this.cupboardService.getNeed(+contribution.id).subscribe(need => { + if (contribution.value != '' && need.type != GoalType.PHYSICAL) { + total += contribution.valueAsNumber + } + this.runningTotal.next(total); + }) } + (ev.target as HTMLInputElement).setAttribute("style", "border-color: unset") } } -- cgit v1.2.3 From 4e59fe19150614b8a9b8033d50cb8a3e0ea1d13b Mon Sep 17 00:00:00 2001 From: benal01 Date: Sat, 5 Apr 2025 13:18:30 -0400 Subject: need items per page persistence --- ufund-ui/src/app/components/need-list/need-list.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/need-list/need-list.component.ts b/ufund-ui/src/app/components/need-list/need-list.component.ts index 39ff538..ca92eeb 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.ts +++ b/ufund-ui/src/app/components/need-list/need-list.component.ts @@ -69,7 +69,7 @@ export class NeedListComponent { visibleNeeds: Need[] = []; sortMode: 'Ascending' | 'Descending' = 'Ascending' currentPage: number = localStorage.getItem('currentPage') ? parseInt(localStorage.getItem('currentPage')!) : 0; - itemsPerPage: number = 5; + itemsPerPage: number = localStorage.getItem('itemsPerPage') ? parseInt(localStorage.getItem('itemsPerPage')!) : 5; totalPages: number = Math.ceil(this.needs.length / this.itemsPerPage); getPrefix(need: Need) { @@ -110,6 +110,7 @@ export class NeedListComponent { if (this.itemsPerPage < 1) { this.itemsPerPage = 1; } + localStorage.setItem('itemsPerPage', this.itemsPerPage.toString()); this.resetVisibleNeeds(); } -- cgit v1.2.3 From 09dabe1971f3891f472e24b5c038e4df481ee967 Mon Sep 17 00:00:00 2001 From: benal01 Date: Sat, 5 Apr 2025 13:33:47 -0400 Subject: need image gradient fix --- ufund-ui/src/app/components/need-page/need-page.component.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/need-page/need-page.component.css b/ufund-ui/src/app/components/need-page/need-page.component.css index 0bbc2aa..8263737 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.css +++ b/ufund-ui/src/app/components/need-page/need-page.component.css @@ -4,7 +4,7 @@ } #box { - padding-top: 5%; + padding-top: 7.5%; display: flex; flex-direction: column; width: 800px; @@ -31,7 +31,7 @@ left: 22.5%; aspect-ratio: 16/9; object-fit: cover; - mask-image: linear-gradient(to bottom, rgba(255,255,255,1) 0%, rgba(255,255,255,.15) 35%, transparent 100%); + mask-image: linear-gradient(to bottom, rgba(255,255,255,1) 0%, rgba(255,255,255,.2) 50%, rgba(255,255,255,.1) 90%, transparent 100%); border-radius: 10px; } -- cgit v1.2.3 From 80492e8f6f88bff8035e27b814cc9eacbee40c65 Mon Sep 17 00:00:00 2001 From: sowgro Date: Sat, 5 Apr 2025 16:29:05 -0400 Subject: Fix storage after merge --- .../components/cupboard/cupboard.component.html | 4 +- .../app/components/cupboard/cupboard.component.ts | 13 +++++- .../funding-basket/funding-basket.component.html | 2 +- .../components/need-list/need-list.component.ts | 54 +++++++++++----------- ufund-ui/src/app/services/users.service.ts | 4 +- 5 files changed, 46 insertions(+), 31 deletions(-) (limited to 'ufund-ui') diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.html b/ufund-ui/src/app/components/cupboard/cupboard.component.html index cd8fec2..4eebc2d 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.html +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.html @@ -25,7 +25,7 @@ {{sortMode === 'Ascending' ? 'arrow_upward': 'arrow_downward'}} - +

Search Results({{needs.length - searchResults.length}} needs filtered):

@@ -46,5 +46,5 @@ deleteDelete Need - +
diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index b03b77e..aca1397 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -26,7 +26,7 @@ export class CupboardComponent implements OnInit { needs: Need[] = []; searchResults: Need[] = []; sortMode: 'Ascending' | 'Descending' = 'Ascending' - itemsPerPage = 5; + itemsPerPage = parseInt(localStorage.getItem('itemsPerPage') ?? '5') ?? 5; currentSortAlgo = 'sortByPriority'; constructor( @@ -135,6 +135,17 @@ export class CupboardComponent implements OnInit { } } + editItemsPerPage() { + if (this.itemsPerPage > this.searchResults.length) { + this.itemsPerPage = this.searchResults.length + } + if (this.itemsPerPage < 1) { + this.itemsPerPage = 1 + } + localStorage.setItem('itemsPerPage', this.itemsPerPage.toString()) + this.refresh(); + } + protected readonly SortingAlgorithms = SortingAlgoArrays; protected readonly Object = Object; } diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html index 3f840e1..c4b12c9 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.html +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html @@ -8,7 +8,7 @@ deleteRemove from Basket - +