aboutsummaryrefslogtreecommitdiff
path: root/ufund-ui/src/app/components/need-list
diff options
context:
space:
mode:
Diffstat (limited to 'ufund-ui/src/app/components/need-list')
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.css37
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.html40
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.ts290
3 files changed, 29 insertions, 338 deletions
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 e17609b..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
@@ -1,9 +1,3 @@
-#header {
- display: flex;
- flex-direction: column;
- gap: 10px
-}
-
.needEntry {
background-color: #2e2e2e;
display: flex;
@@ -17,33 +11,6 @@
gap: 15px
}
-select {
- font-size: 14pt;
- padding: 5px;
-}
-
-#searchArea {
- display: flex;
-
- form {
- display: flex;
- width: 100%;
- gap: 10px;
- }
-
- input[type=text] {
- display: flex;
- width: 100%;
- }
-}
-
-#sortArea {
- display: flex;
- flex-direction: row;
- gap: 10px;
- align-items: center;
-}
-
.needName {
font-weight: bold;
}
@@ -105,6 +72,10 @@ select {
gap: 5px;
}
+.actionArea:empty {
+ padding: 0;
+}
+
#page-selector {
display: flex;
align-items: center;
diff --git a/ufund-ui/src/app/components/need-list/need-list.component.html b/ufund-ui/src/app/components/need-list/need-list.component.html
index 84f80dc..406bfa0 100644
--- a/ufund-ui/src/app/components/need-list/need-list.component.html
+++ b/ufund-ui/src/app/components/need-list/need-list.component.html
@@ -1,30 +1,4 @@
-<div id="header">
- <div id="searchArea">
- <form id="search-form" #searchForm="ngForm">
- <input type="text" name="search" class="wide-input" placeholder="Search in {{needs.length}} needs..." (input)="search(searchForm.value)" ngModel>
- <input type="reset" value="Clear" (click)="search(null)"> <br>
- </form>
- </div>
- <div id="sortArea">
- <label for="sort">Sort by: </label>
- <select id='sort' [(ngModel)] = "sortSelection" class="wide-input" (change)="search(searchForm.value)" [value]="sortSelection">
- <option *ngFor="let algorithm of SortingAlgoArrays" value="{{algorithm.name}}">
- {{algorithm.display[sortMode === 'Ascending' ? 0 : 1]}}
- </option>
- </select>
- <button (click)="changeSortMode(searchForm.value)">
- <span class="icon">{{sortMode === 'Ascending' ? 'arrow_upward': 'arrow_downward'}}</span>
- </button>
- <label>Needs per page: </label>
- <input type ="number" [(ngModel)]="itemsPerPage" (change)="editNeedsPerPage()" min="1" max="{{searchResults.length}}">
- </div>
- <!--<button (click)="close()">Close</button>-->
-</div>
-
<!-- display for when results are present and filtered-->
-<h2 *ngIf="searchResults.length < needs.length && searchResults.length != 0"> Search Results({{needs.length - searchResults.length}} needs filtered): </h2>
-<h2 *ngIf="searchResults.length == needs.length"> All Needs </h2>
-<h2 *ngIf="searchResults.length == 0"> No Results Found </h2>
<div id="needList">
<div *ngFor="let need of visibleNeeds" class="needEntry">
<div [routerLink]="'/need/' + need.id" class="clickable">
@@ -46,19 +20,9 @@
<span>{{need.type.toString() == 'MONETARY' ? '$' : ''}}{{need.current}}/{{need.type.toString() == 'MONETARY' ? '$' : ''}}{{need.maxGoal}} ({{((need.current / need.maxGoal) * 100).toFixed(0)}}%)</span>
<progress [value]="need.current" [max]="need.maxGoal"></progress>
</div>
-
</div>
-
- <div class="actionArea">
- <button *ngIf="isHelper()" (click)="add(need)">
- <span class="icon">add</span>Add To Basket
- </button>
- <button *ngIf="isManager()" (click)="select(need)">
- <span class="icon">edit</span>Edit Need
- </button>
- <button *ngIf="isManager()" (click)="delete(need.id)" >
- <span class="icon">delete</span>Delete Need
- </button>
+ <div *ngIf="actionArea" class="actionArea">
+ <ng-container [ngTemplateOutlet]="actionArea" [ngTemplateOutletContext]="{$implicit: need}"/>
</div>
</div>
</div>
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 ca92eeb..564f1f0 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,80 +1,29 @@
-import {Component, EventEmitter, Output} from '@angular/core';
+import {Component, Input, OnChanges, TemplateRef} from '@angular/core';
import {GoalType, 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;
-}
+@Component({
+ selector: 'app-need-list',
+ standalone: false,
+ templateUrl: './need-list.component.html',
+ styleUrl: './need-list.component.css'
+})
+export class NeedListComponent implements OnChanges {
-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;
-}
+ @Input({required: true}) needs!: Need[]
+ @Input() itemsPerPage: number = 5;
+ @Input() actionArea: TemplateRef<any> | null = null
-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;
-}
+ visibleNeeds: Need[] = [];
+ currentPage: number = localStorage.getItem('currentPage') ? parseInt(localStorage.getItem('currentPage')!) : 0;
+ totalPages: number = 0;
-const sortByLocation: sortAlgo = (a: Need, b: Need): number => {
- if(a.location.toLocaleLowerCase() < b.location.toLocaleLowerCase()) {
- return -1;
- }
- return 1;
-}
-
-@Component({
- 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 = localStorage.getItem('currentPage') ? parseInt(localStorage.getItem('currentPage')!) : 0;
- itemsPerPage: number = localStorage.getItem('itemsPerPage') ? parseInt(localStorage.getItem('itemsPerPage')!) : 5;
- totalPages: number = Math.ceil(this.needs.length / this.itemsPerPage);
+ ngOnChanges() {
+ this.updateVisibleNeeds()
+ }
- getPrefix(need: Need) {
- return (need.type === GoalType.MONETARY) ? "$" : "";
- }
+ getPrefix(need: Need) {
+ return (need.type === GoalType.MONETARY) ? "$" : "";
+ }
//increment/decrement
decrementPage() {
@@ -102,203 +51,10 @@ export class NeedListComponent {
this.updateVisibleNeeds()
}
- //user editing needs per page
- editNeedsPerPage() {
- if (this.itemsPerPage > this.searchResults.length) {
- this.itemsPerPage = this.searchResults.length;
- }
- if (this.itemsPerPage < 1) {
- this.itemsPerPage = 1;
+ updateVisibleNeeds() {
+ this.totalPages = Math.ceil(this.needs.length / this.itemsPerPage);
+ this.visibleNeeds = this.needs.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage);
}
- localStorage.setItem('itemsPerPage', this.itemsPerPage.toString());
- 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();
- }
-
- 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<Need>();
-
- 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'
- }
- 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);
- }
- 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);
- }
-
- changeText(id : number, text : string) {
- const span = document.getElementById('hover-status-label-' + id);
- if (span) {
- span.innerHTML = ' ' + text;
- }
- }
-
- 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.toastService.sendToast(ToastType.INFO, "Need added to your basket!")
- 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!")
- }
- }
- }
-
- back() {
- this.searchResults = this.needs;
- }
-
- 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';
- }
- }
protected readonly GoalType = GoalType;
}