diff options
Diffstat (limited to '')
18 files changed, 554 insertions, 664 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..cd8fec2 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" 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 [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..289618f 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,127 @@ 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; + // selectedNeed: Need | null = null; + needs: Need[] = []; + searchResults: Need[] = []; + sortMode: 'Ascending' | 'Descending' = 'Ascending' + itemsPerPage = 5; + currentSortAlgo = 'sortByPriority'; + // activeEdit?: Need; + 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()) + } + + 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); } - selectedNeed: any = { - name: '', - location:'', - id: null, - maxGoal: null, - type: '', - urgent: false - }; - selectedNeedId: number | null = null; - searchResults: any[] = []; + 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 - selectForm(name: string) { - //get search results from the need list - if (this.needList) { - this.searchResults = this.needList.searchResults; + //remove previous search if it exists + if (this.searchDelay) { + clearTimeout(this.searchDelay); } - console.log(this.searchResults) - this.selectedForm = name; - if (name == 'update') { - if (this.searchResults) { - this.searchResults.forEach((element: any) => { - console.log(element) - }); - } + if (form) { + this.searchDelay = setTimeout(() => { + + if (form) { + 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; } } - async updateSearchResults() { - if (this.needList) { - while (this.selectedForm == 'update') { - this.searchResults = this.needList.searchResults - await new Promise(resolve => setTimeout(resolve, 100)); - } + toggleSortMode(form : any) { + if (this.sortMode == 'Ascending'){ + this.sortMode = 'Descending' + } else { + this.sortMode = 'Ascending' } + this.search(form) } - populateForm(need: any): void { - this.selectForm('update'); - this.selectedNeed = { ...need }; + 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(); } - isManager() { - const type = this.authService.getCurrentUser()?.type; - return type === ("MANAGER" as unknown as userType); + 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(() => { + 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!") + } + } } - 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/cupboard/sorting.ts b/ufund-ui/src/app/components/cupboard/sorting.ts new file mode 100644 index 0000000..73a5ba9 --- /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: {[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/funding-basket/funding-basket.component.html b/ufund-ui/src/app/components/funding-basket/funding-basket.component.html index bba66a3..3f840e1 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,44 +1,24 @@ <div id="box"> - <h1>Funding Basket</h1> - <ng-template [ngIf]="usersService.getBasket().getValue().length"> - <div id="needList"> - <div *ngFor="let need of usersService.getBasket().getValue()" class="needEntry"> - <div [routerLink]="'/need/' + need.id" class="clickable"> - <div class="split"> - <div class="left"> - <span class="needName">{{need.name}}</span> - <span class="needType">{{need.type}}</span> - </div> - - <div class="right"> - <span *ngIf="need.urgent" class="urgent">URGENT</span> - <span *ngIf="need.location"><span class="icon">location_on</span>{{need.location}}</span> - </div> - </div> - - <br> - - <div class="prog"> - <span id="hover-status-label-{{need.id}}"> </span> - <span>{{need.current}}/{{need.maxGoal}} ({{((need.current / need.maxGoal) * 100).toFixed(0)}}%)</span> - <progress [value]="need.current" [max]="need.maxGoal"></progress> - </div> - </div> - - <div class="actionArea"> - <input type="number" placeholder="Quantity" min="1" [id]="need.id" class="contribution" (input)="resetColor($event)"> - <button class="removeNeed" title="delete need" (click)="this.usersService.removeNeed(need.id)"> - <span class="icon">delete</span> Remove from Basket - </button> - </div> + @if ((authService.getCurrentUserSubject() | async)?.type === userType.HELPER) { + <h1>Funding Basket</h1> + <ng-template [ngIf]="(usersService.getBasket() | async)?.length"> + <ng-template let-need #NLActions> + <input type="number" placeholder="Quantity" min="1" [id]="need?.id" class="contribution" (input)="resetColor($event)"> + <button class="removeNeed" (click)="this.usersService.removeNeed(need.id)"> + <span class="icon">delete</span>Remove from Basket + </button> + </ng-template> + <app-need-list [actionArea]="NLActions" [needs]="(usersService.getBasket() | async)!"/> + <br> + <div id="footer"> + <button class="button2" title="checkout" (click)="checkout()">Checkout</button> </div> + </ng-template> + <div *ngIf="!usersService.getBasket().getValue().length"> + <span>There are no needs in your basket! </span><a routerLink="/cupboard">Browse the cupboard</a> </div> - <br> - <div id="footer"> - <button class="button2" title="checkout" (click)="checkout()">Checkout</button> - </div> - </ng-template> - <div *ngIf="!usersService.getBasket().getValue().length"> - <span>There are no needs in your basket! </span><a routerLink="/cupboard">Browse the cupboard</a> - </div> + } @else { + <h1>Unauthorized</h1> + <span>This page requires you to be logged in as a user! <a routerLink="/login">Log In</a></span> + } </div> 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..c807271 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 {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,18 +19,13 @@ 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; - // this is for login rerouting ngOnInit(): void { - if (!this.authService.getCurrentUser()) { - this.router.navigate(['/login'], {queryParams: {redir: this.router.url}}); - return; - } this.usersService.refreshBasket(); } @@ -63,9 +59,10 @@ export class FundingBasketComponent implements OnInit { } resetColor(ev: any) { - for (let contribution of document.querySelectorAll<HTMLInputElement>('.contribution')!) { - - } + // for (let contribution of document.querySelectorAll<HTMLInputElement>('.contribution')!) {} (ev.target as HTMLInputElement).setAttribute("style", "border-color: unset") } + + protected readonly of = of; + protected readonly userType = userType; } 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..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 @@ -1,23 +1,44 @@ -<div id="update-form"> - <h1> Update Need </h1> - <form #updateForm="ngForm" (ngSubmit)="update(updateForm.value)"> - <label>Name:</label><br> - <input type="text" name="name" [(ngModel)]="selectedNeed.name"><br> - <label>Image:</label><br> - <input type="text" name="image" [(ngModel)]="selectedNeed.image"><br> - <label>Location:</label><br> - <input type="text" name="location" [(ngModel)]="selectedNeed.location"><br> - <label>Max Goal:</label><br> - <input type="number" name="maxGoal" [(ngModel)]="selectedNeed.maxGoal"><br> - <label>Type</label><br> - <input type="radio" name="type" value="MONETARY" [(ngModel)]="selectedNeed.type"> - <label>Monetary</label><br> - <input type="radio" name="type" value="PHYSICAL" [(ngModel)]="selectedNeed.type"> - <label>Physical</label><br> - <input type="checkbox" name="urgent" [(ngModel)]="selectedNeed.urgent"> - <label>Urgent</label> <br> - <label>Description</label> <br> - <textarea name="description" [(ngModel)]="selectedNeed.description"></textarea><br> +<div id="box"> + <button id="closeButton" (click)="modalService.hideModal()"><span class="icon">close</span></button> + <h2>{{this.mode}} Need</h2> + <form #updateForm="ngForm" id="update-form" (ngSubmit)="submit(updateForm.value)"> + <div> + <span>Name:</span> + <input type="text" name="name" [(ngModel)]="needCopy.name"> + </div> + <div> + <span>Image:</span> + <input type="text" name="image" [(ngModel)]="needCopy.image"> + </div> + <div> + <input type="text" name="location" [(ngModel)]="needCopy.location"> + </div> + <div> + <span>Max Goal:</span> + <input type="number" name="maxGoal" [(ngModel)]="needCopy.maxGoal"> + </div> + <div> + <span>Type:</span> + <label> + <input type="radio" name="type" value="MONETARY" [(ngModel)]="needCopy.type"> + Monetary + </label> + <label> + <input type="radio" name="type" value="PHYSICAL" [(ngModel)]="needCopy.type"> + Physical + </label> + </div> + <div> + <span>Urgency:</span> + <label> + <input type="checkbox" name="urgent" [(ngModel)]="needCopy.urgent"> + Urgent + </label> + </div> + <div> + <span>Description:</span> + <textarea name="description" [(ngModel)]="needCopy.description"></textarea> + </div> <input type="submit" value="Submit"> </form> 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..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,61 +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 - - ) {} - - @Input() selectedNeed!: Need; - @Output() refreshNeedList = new EventEmitter<void>(); - - 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<void>(); + + 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"); + } } - } - ); - } -}
\ No newline at end of file + ); + } +} 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 ae6bc99..40af9f5 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,293 +1,58 @@ -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; -} - -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; -} @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); +export class NeedListComponent implements OnChanges { - getPrefix(need: Need) { - return (need.type === GoalType.MONETARY) ? "$" : ""; - } + @Input({required: true}) needs!: Need[] + @Input() itemsPerPage: number = 5; + @Input() actionArea: TemplateRef<any> | null = null - decrementPage() { - this.currentPage--; - this.updateVisibleNeeds(); - } + visibleNeeds: Need[] = []; + currentPage: number = 0; + totalPages: number = 0; - incrementPage() { - this.currentPage++; - this.updateVisibleNeeds(); - } + constructor( - lastPage() { - this.currentPage = this.totalPages - 1 - this.updateVisibleNeeds() - } + ) {} - firstPage() { - this.currentPage = 0 - this.updateVisibleNeeds() - } - - editNeedsPerPage() { - if (this.itemsPerPage > this.searchResults.length) { - this.itemsPerPage = this.searchResults.length; - } - if (this.itemsPerPage < 1) { - this.itemsPerPage = 1; + ngOnChanges() { + this.updateVisibleNeeds() } - this.resetVisibleNeeds(); - } - - updateVisibleNeeds() { - this.totalPages = Math.ceil(this.searchResults.length / this.itemsPerPage); - this.visibleNeeds = this.searchResults.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); - } - - 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<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); + getPrefix(need: Need) { + return (need.type === GoalType.MONETARY) ? "$" : ""; } - 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); - } - - changeText(id : number, text : string) { - const span = document.getElementById('hover-status-label-' + id); - if (span) { - span.innerHTML = ' ' + text; + lastPage() { + this.currentPage = this.totalPages - 1 + 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.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!") - } + firstPage() { + this.currentPage = 0 + this.updateVisibleNeeds() } - } - - 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'; - } + updateVisibleNeeds() { + this.totalPages = Math.ceil(this.needs.length / this.itemsPerPage); + this.visibleNeeds = this.needs.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); } - //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; } 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..f1b7f1f 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 @@ -66,8 +66,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 6921eac..ff5990f 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,51 +1,58 @@ <div id="box"> - <h1>{{need.name}}</h1> - <span class="needType">{{need.type}} GOAL</span> - <p>{{need.description}}</p> - <div class="prog"> -<!-- <span>{{need?.current}} / {{need?.maxGoal}}</span>--> - <progress [value]="need.current" [max]="need.maxGoal"></progress> - <span>This goal is <strong>{{(((need.current)*100) / (need.maxGoal)).toFixed(0)}}%</strong> complete!</span> - </div> - - - <div class="split"> - <div class="left"> - <span><strong>Target Goal:</strong> {{(need.type === GoalType.MONETARY) ? "$" : ""}}{{need.maxGoal}}</span> - - <span><strong>Amount Currently Collected:</strong> {{need.type.toString() == 'MONETARY' ? '$' : ''}}{{need.current}}</span> - - <span><strong>Location:</strong> {{need.location}}</span> - - <span><strong>Urgency: </strong> - <span *ngIf="!need.urgent">Not urgent</span> - <span *ngIf="need.urgent" class="urgent">URGENT</span> - </span> - - <div *ngIf="need.filterAttributes.length > 0"> - <strong>Tags:</strong> - <ul style="display: flex; column-gap: 24px;"> - <li *ngFor="let tag of need?.filterAttributes"> - <p>{{tag}}</p> - </li> - </ul> + @if (need) { + <h1>{{need.name}}</h1> + <span class="needType">{{need.type}} GOAL</span> + <p>{{need.description}}</p> + <div class="prog"> + <progress [value]="need.current" [max]="need.maxGoal"></progress> + <span>This goal is <strong>{{(((need.current)*100) / (need.maxGoal)).toFixed(0)}}%</strong> complete!</span> + </div> + + + <div class="split"> + <div class="left"> + <span><strong>Target Goal:</strong> {{(need.type === GoalType.MONETARY) ? "$" : ""}}{{need.maxGoal}}</span> + + <span><strong>Amount Currently Collected:</strong> {{need.type.toString() == 'MONETARY' ? '$' : ''}}{{need.current}}</span> + + <span><strong>Location:</strong> {{need.location}}</span> + + <span><strong>Urgency: </strong> + <span *ngIf="!need.urgent">Not urgent</span> + <span *ngIf="need.urgent" class="urgent">URGENT</span> + </span> + + <div *ngIf="need.filterAttributes?.length"> + <strong>Tags:</strong> + <ul style="display: flex; column-gap: 24px;"> + <li *ngFor="let tag of need?.filterAttributes"> + <p>{{tag}}</p> + </li> + </ul> + </div> + </div> + <div class="right"> + <img *ngIf="need.image" alt="Need image" class="need-image" [src]="need.image"/> </div> </div> - <div class="right"> - <img *ngIf="need.image" alt="Need image" class="need-image" [src]="need.image"/> + + + <div class="actionArea"> + <button *ngIf="usersService.isHelper()" (click)="add(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 [mode]="'Edit'" *ngIf="need" [need]="need" (refreshNeedList)="ngOnInit()"></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)="delete(need!.id)" > + <span class="icon">delete</span>Delete Need + </button> </div> - </div> - - - <div class="actionArea"> - <button *ngIf="isHelper()" (click)="add(need!)"> - <span class="icon">add</span>Add To Basket - </button> -<!-- <button *ngIf="isManager()" (click)="edit(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> + } @else { + <h1>Need not found</h1> + <span>The requested need does not exist. <a routerLink="/cupboard">Browse the cupboard</a></span> + } </div> 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..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 @@ -1,12 +1,12 @@ -import {Component, Input} from '@angular/core'; -import {GoalType, Need} from '../../models/Need'; +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'; import {UsersService} from '../../services/users.service'; +import {ModalService} from '../../services/modal.service'; @Component({ selector: 'app-need-page', @@ -14,18 +14,17 @@ import {UsersService} from '../../services/users.service'; templateUrl: './need-page.component.html', styleUrl: './need-page.component.css' }) -export class NeedPageComponent { +export class NeedPageComponent implements OnInit { constructor( private route: ActivatedRoute, private cupboardService: CupboardService, private authService: AuthService, - private usersService: UsersService, + protected usersService: UsersService, private toastService: ToastsService, - private router: Router + private router: Router, + protected modalService: ModalService ) {} - public GoalType = GoalType; - @Input() need!: Need; ngOnInit(): void { @@ -37,16 +36,6 @@ export class NeedPageComponent { 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) @@ -69,19 +58,15 @@ export class NeedPageComponent { delete(id : number) { this.cupboardService.deleteNeed(id) - .pipe(catchError((ex, r) => { + .pipe(catchError((ex, _) => { this.toastService.sendToast(ToastType.ERROR, ex.error) return of() })) .subscribe(() => { // this.needs = this.needs.filter(n => n.id !== id) this.toastService.sendToast(ToastType.INFO, "Need deleted") - this.router.navigate(['/']) + this.router.navigate(['/cupboard']) }) // this.refresh(); } - - edit(need: Need) { - - } } diff --git a/ufund-ui/src/app/components/toast/toast.component.css b/ufund-ui/src/app/components/toast/toast.component.css index 4cd81fe..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; @@ -39,7 +39,7 @@ } } -.toast.hide { +.toast.hide:not(:has(:hover)) { transform: translateY(-90px); } 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 @@ <div class="toast" [ngClass]="ToastType[type].toLowerCase()" #toastDiv> <span>{{this.message}}</span> - <a *ngIf="this.action" (click)="this.action.onAction()">{{this.action.label}}</a> + <a *ngIf="this.action" href="#" (click)="this.action.onAction(); $event.preventDefault(); hide()">{{this.action.label}}</a> <button (click)="hide()"> <span class="icon">close</span> </button> |