diff options
Diffstat (limited to 'ufund-ui/src/app/components/cupboard')
4 files changed, 261 insertions, 116 deletions
diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.css b/ufund-ui/src/app/components/cupboard/cupboard.component.css index e45d929..9c37582 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.css +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.css @@ -52,3 +52,36 @@          margin-top: 3px;      }  } + +#header2 { +    display: flex; +    flex-direction: column; +    gap: 10px +} + +#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; +} + +select { +    font-size: 14pt; +    padding: 5px; +} diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.html b/ufund-ui/src/app/components/cupboard/cupboard.component.html index 37954bb..4eebc2d 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.html +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.html @@ -1,38 +1,50 @@  <div id="box">      <div id="header">          <h1> Cupboard </h1> -        <button *ngIf="isManager()" class="button2" (click)="this.selectForm('create')"><span class="icon">add</span>Create Need</button> +        <ng-template #create> +            <app-need-edit [mode]="'Create'" (refreshNeedList)="refresh()"></app-need-edit> +        </ng-template> +        <button *ngIf="usersService.isManager()" class="button2" (click)="modalService.showModal(create)"><span class="icon">add</span>Create Need</button>      </div> -    <app-need-list (currentNeed) = populateForm($event) #needList></app-need-list> -</div> -<ng-template [ngIf]="isManager()" > -<div> -    <app-need-edit *ngIf="selectedForm === 'update'" [selectedNeed]="selectedNeed" (refreshNeedList)="needList.refresh()"></app-need-edit> -    <div> -    <div id="create-form" *ngIf="selectedForm === 'create'"> -        <h1> Create Need </h1> -        <form #cupboardForm="ngForm" (ngSubmit)="submit(cupboardForm.value)"> -            <label>Name:</label><br> -            <input type="text" name="name" ngModel><br> -            <label>Image:</label><br> -            <input type="text" name="image" ngModel><br> -            <label>Location:</label><br> -            <input type="text" name="location" ngModel><br> -            <label>Max Goal:</label><br> -            <input type="number" name="maxGoal" ngModel><br> -            <label>Type</label><br> -            <input type="radio" name="type" value="MONETARY" ngModel> -            <label>Monetary</label><br> -            <input type="radio" name="type" value="PHYSICAL" ngModel> -            <label>Physical</label><br> -            <input type="checkbox" name="urgent" value="false" ngModel> -            <label>Urgent</label><br> -            <label>Description</label><br> -            <textarea name="description"></textarea><br> -            <input type="submit" value="Submit"> -        </form> -    </div> +    <div id="header2"> +        <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)] = "currentSortAlgo" class="wide-input" (change)="search(searchForm.value)" [value]="currentSortAlgo"> +                <option *ngFor="let algorithm of Object.keys(SortingAlgorithms)" [value]="algorithm"> +                    {{SortingAlgorithms[algorithm].display[sortMode === 'Ascending' ? 0 : 1]}} +                </option> +            </select> +            <button (click)="toggleSortMode(searchForm.value)" [title]="sortMode"> +                <span class="icon">{{sortMode === 'Ascending' ? 'arrow_upward': 'arrow_downward'}}</span> +            </button> +            <label>Needs per page: </label> +            <input type ="number" [(ngModel)]="itemsPerPage" (change)="editItemsPerPage()" min="1" max="{{searchResults.length}}"> +        </div>      </div> +    <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> + +    <ng-template let-need #NLActions> +        <button *ngIf="usersService.isHelper()" (click)="addToBasket(need)" [disabled]="usersService.inBasket(usersService.getBasket() | async, need)"> +            <span class="icon">{{usersService.inBasket(usersService.getBasket() | async, need)? "check": "add" }}</span>Add To Basket +        </button> +        <ng-template #edit> +            <app-need-edit *ngIf="need" [mode]="'Edit'" [need]="need" (refreshNeedList)="refresh()"></app-need-edit> +        </ng-template> +        <button *ngIf="usersService.isManager()" (click)="modalService.showModal(edit)"> +            <span class="icon">edit</span>Edit Need +        </button> +        <button *ngIf="usersService.isManager()" (click)="deleteNeed(need.id)" > +            <span class="icon">delete</span>Delete Need +        </button> +    </ng-template> +    <app-need-list [uid]="0" [actionArea]="NLActions" *ngIf="searchResults.length > 0" [needs]="searchResults" [itemsPerPage]="itemsPerPage" #needList/>  </div> -</ng-template> diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index a4706b3..f571566 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -1,11 +1,14 @@  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 {CupboardService} from '../../services/cupboard.service'; +import {Need} from '../../models/Need'; +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'; +import {Router} from '@angular/router'; +import {ModalService} from '../../services/modal.service';  @Component({      selector: 'app-cupboard', @@ -13,111 +16,139 @@ 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; +    // selectedForm?: string = undefined; +    // needs: any;      @ViewChild("needList") needList?: NeedListComponent +    private searchDelay: any; +    needs: Need[] = []; +    searchResults: Need[] = []; +    sortMode: 'Ascending' | 'Descending' = 'Ascending' +    itemsPerPage = parseInt(localStorage.getItem('itemsPerPage') ?? '5') ?? 5; +    currentSortAlgo = 'sortByPriority'; +      constructor(          private cupboardService: CupboardService,          private authService: AuthService, -        private toastService: ToastsService +        private toastService: ToastsService, +        protected usersService: UsersService, +        private router: Router, +        protected modalService: ModalService      ) {}      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() +            this.search(null) +        }); +        this.authService.getCurrentUserSubject().subscribe( +            () => this.usersService.refreshBasket())      } -    selectedNeed: any = { -        name: '', -        location:'', -        id: null, -        maxGoal: null, -        type: '', -        urgent: false -    }; -    selectedNeedId: number | null = null; -    searchResults: any[] = []; - -    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) -                }); +    refresh() { +        this.cupboardService.getNeeds().subscribe(n => { +            if (this.sortMode == 'Ascending') { +                this.needs = n.sort(SortingAlgoArrays[this.currentSortAlgo].func); +            } else { +                this.needs = n.sort(SortingAlgoArrays[this.currentSortAlgo].func).reverse();              } +            this.searchResults = this.needs; +            // this.updateVisibleNeeds(); +        }); -        } +        const form = document.getElementById('search-form') as HTMLFormElement; +        form.reset(); +        this.search(null);      } -    async updateSearchResults() { -        if (this.needList) { -            while (this.selectedForm == 'update') { -                this.searchResults = this.needList.searchResults -                await new Promise(resolve => setTimeout(resolve, 100)); -            } +    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 +        if (this.searchDelay) { +            clearTimeout(this.searchDelay);          } -    } +        if (form) { +            this.searchDelay = setTimeout(() => { + +                if (form) { -    populateForm(need: any): void { -        this.selectForm('update'); -        this.selectedNeed = { ...need }; +                    const currentSearchValue = form.search; //latest value of the search +                    this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { +                        if (this.sortMode == 'Ascending') { +                            this.searchResults = n.sort(SortingAlgoArrays[this.currentSortAlgo].func); +                        } else { +                            this.searchResults = n.sort(SortingAlgoArrays[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; +        }      } -    isManager() { -        const type = this.authService.getCurrentUser()?.type; -        return type === ("MANAGER" as unknown as userType); +    toggleSortMode(form : any) { +        if (this.sortMode == 'Ascending'){ +            this.sortMode = 'Descending' +        } else { +            this.sortMode = 'Ascending' +        } +        this.search(form)      } -    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) +    deleteNeed(id : number) { +        this.cupboardService.deleteNeed(id)              .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"); -                } +                this.toastService.sendToast(ToastType.ERROR, ex.error)                  return of()              })) -            .subscribe( -                (result) => { -                    if (result) { -                        console.log("need created successfully"); -                        this.needList?.refresh() -                    } else { -                        console.log("need creation failed"); -                    } -                } +            .subscribe(() => { +                this.toastService.sendToast(ToastType.INFO, "Need deleted.") +                this.refresh(); +            }) +    } + +    addToBasket(need: Need) { +        const currentUser = this.authService.getCurrentUser(); +        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(() => { +                        let action = {label: "View Basket", onAction: () => this.router.navigate(['/basket'])} +                        this.toastService.sendToast(ToastType.INFO, `"${need.name}" Added to basket`, action) +                        this.usersService.refreshBasket(); +                    }); +            } else { +                this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") +            } +        } +    } -            ); +    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/cupboard/sorting.ts b/ufund-ui/src/app/components/cupboard/sorting.ts new file mode 100644 index 0000000..5c37019 --- /dev/null +++ b/ufund-ui/src/app/components/cupboard/sorting.ts @@ -0,0 +1,69 @@ +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; +} + +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" ] }, +    sortByType:       { func: sortByType,       display: ["Type (Physical first)", "Type (Monetary first)" ] }, +};  | 
