aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsowgro <tpoke.ferrari@gmail.com>2025-04-06 15:15:47 -0400
committersowgro <tpoke.ferrari@gmail.com>2025-04-06 15:15:47 -0400
commit9f14b3787a8cfc49fd168b1242adcc6d5fa8bfd6 (patch)
tree8af111a538a8d6361e1cf07467b9c31568284921
parent1ac878b4aaa19ab889c7a98b7dab6acd57c778b3 (diff)
parent04cb51b2e7891785c956c5faa73fb88cc04e82e0 (diff)
downloadJellySolutions-9f14b3787a8cfc49fd168b1242adcc6d5fa8bfd6.tar.gz
JellySolutions-9f14b3787a8cfc49fd168b1242adcc6d5fa8bfd6.tar.bz2
JellySolutions-9f14b3787a8cfc49fd168b1242adcc6d5fa8bfd6.zip
Merge branch 'main' into light-mode
# Conflicts: # ufund-ui/src/app/app.component.html # ufund-ui/src/app/components/funding-basket/funding-basket.component.html # ufund-ui/src/app/components/need-list/need-list.component.html # ufund-ui/src/app/components/need-page/need-page.component.css
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java21
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java2
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java18
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java48
-rw-r--r--ufund-ui/src/app/app.component.css20
-rw-r--r--ufund-ui/src/app/app.component.html37
-rw-r--r--ufund-ui/src/app/app.component.ts2
-rw-r--r--ufund-ui/src/app/components/cupboard/cupboard.component.css33
-rw-r--r--ufund-ui/src/app/components/cupboard/cupboard.component.html74
-rw-r--r--ufund-ui/src/app/components/cupboard/cupboard.component.ts201
-rw-r--r--ufund-ui/src/app/components/cupboard/sorting.ts69
-rw-r--r--ufund-ui/src/app/components/dashboard/dashboard.component.css44
-rw-r--r--ufund-ui/src/app/components/dashboard/dashboard.component.html37
-rw-r--r--ufund-ui/src/app/components/dashboard/dashboard.component.ts43
-rw-r--r--ufund-ui/src/app/components/funding-basket/funding-basket.component.css8
-rw-r--r--ufund-ui/src/app/components/funding-basket/funding-basket.component.html98
-rw-r--r--ufund-ui/src/app/components/funding-basket/funding-basket.component.ts96
-rw-r--r--ufund-ui/src/app/components/home-page/home-page.component.css2
-rw-r--r--ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css23
-rw-r--r--ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html8
-rw-r--r--ufund-ui/src/app/components/mini-need-list/mini-need-list.component.ts2
-rw-r--r--ufund-ui/src/app/components/need-edit/need-edit.component.css42
-rw-r--r--ufund-ui/src/app/components/need-edit/need-edit.component.html62
-rw-r--r--ufund-ui/src/app/components/need-edit/need-edit.component.ts158
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.css76
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.html45
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.ts309
-rw-r--r--ufund-ui/src/app/components/need-page/need-page.component.css35
-rw-r--r--ufund-ui/src/app/components/need-page/need-page.component.html79
-rw-r--r--ufund-ui/src/app/components/need-page/need-page.component.ts42
-rw-r--r--ufund-ui/src/app/components/signup/signup.component.css27
-rw-r--r--ufund-ui/src/app/components/signup/signup.component.html9
-rw-r--r--ufund-ui/src/app/components/signup/signup.component.ts9
-rw-r--r--ufund-ui/src/app/components/toast/toast.component.css4
-rw-r--r--ufund-ui/src/app/components/toast/toast.component.html2
-rw-r--r--ufund-ui/src/app/components/toast/toast.component.ts1
-rw-r--r--ufund-ui/src/app/services/cupboard.service.ts8
-rw-r--r--ufund-ui/src/app/services/modal.service.ts25
-rw-r--r--ufund-ui/src/app/services/users.service.ts35
-rw-r--r--ufund-ui/src/styles.css5
40 files changed, 981 insertions, 878 deletions
diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java
index 12fb0a9..5452c81 100644
--- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java
@@ -1,6 +1,7 @@
package com.ufund.api.ufundapi.controller;
import java.io.IOException;
+import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -189,12 +190,22 @@ public class CupboardController {
* @return OK if successful, other statuses if failure
*/
@PutMapping("/checkout")
- public ResponseEntity<Object> checkoutNeeds(@RequestBody Map<String, Integer> data, @RequestHeader("jelly-api-key") String key) {
- int needID = data.get("needID");
- int checkoutAmount = data.get("amount");
- LOG.log(Level.INFO, "PUT /need/checkout body={0}", data);
+ public ResponseEntity<Object> checkoutNeeds(@RequestBody List<Map<String, Integer>> data, @RequestHeader("jelly-api-key") String key) {
+ LOG.log(Level.INFO, "PUT /cupboard/checkout body={0}", data);
try {
- cupboardService.checkoutNeed(needID, checkoutAmount, key);
+ authService.keyIsValid(key);
+
+ for (Map<String, Integer> map : data) {
+ int needID = map.get("needID");
+ if (cupboardService.getNeed(needID) == null) {
+ return new ResponseEntity<>("One or more needs are invalid, please refresh.", HttpStatus.BAD_REQUEST);
+ }
+ }
+ for (Map<String, Integer> map : data) {
+ int needID = map.get("needID");
+ int checkoutAmount = map.get("quantity");
+ cupboardService.checkoutNeed(needID, checkoutAmount, key);
+ }
return new ResponseEntity<>(HttpStatus.OK);
} catch (IllegalArgumentException ex) {
LOG.log(Level.WARNING, ex.getLocalizedMessage());
diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java
index c6e622c..6953276 100644
--- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java
@@ -108,7 +108,7 @@ public class UserController {
try {
authService.keyHasAccessToCupboard(key);
- int count = userService.getUserCount();
+ String count = String.valueOf(userService.getUserCount());
return new ResponseEntity<>(count, HttpStatus.OK);
} catch (IllegalAccessException ex) {
LOG.log(Level.WARNING, ex.getLocalizedMessage());
diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java
index 9b6170b..d3b3065 100644
--- a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java
@@ -2,6 +2,8 @@ package com.ufund.api.ufundapi.model;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Arrays;
+
public class Need {
public enum GoalType {
@@ -129,4 +131,20 @@ public class Need {
public void setID(int id){
this.id = id;
}
+
+ @Override
+ public String toString() {
+ return "Need{" +
+ "name='" + name + '\'' +
+ ", image='" + image + '\'' +
+ ", location='" + location + '\'' +
+ ", id=" + id +
+ ", filterAttributes=" + Arrays.toString(filterAttributes) +
+ ", type=" + type +
+ ", maxGoal=" + maxGoal +
+ ", urgent=" + urgent +
+ ", current=" + current +
+ ", description='" + description + '\'' +
+ '}';
+ }
} \ No newline at end of file
diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java
index 8572ec6..7ea4455 100644
--- a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java
+++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java
@@ -1,6 +1,7 @@
package com.ufund.api.ufundapi.controller;
import java.io.IOException;
+import java.util.List;
import java.util.Map;
import static java.util.Map.entry;
@@ -418,12 +419,14 @@ public class CupboardControllerTest {
@Test
public void checkoutNeeds() throws IOException, IllegalAccessException {
+ when(mockCupboardService.getNeed(0)).thenReturn(new Need("name", "image", "location", 0, 10, GoalType.MONETARY, true, "a"));
doNothing().when(mockCupboardService).checkoutNeed(0, 20, key);
-
- Map<String, Integer> needMap = Map.ofEntries(
- entry("needID", 0),
- entry("amount", 20)
+ var needMap = List.of(
+ Map.ofEntries(
+ entry("needID", 0),
+ entry("quantity", 20)
+ )
);
var res = cupboardController.checkoutNeeds(needMap, key);
@@ -435,9 +438,15 @@ public class CupboardControllerTest {
public void checkoutNeedsBadRequest() throws IOException, IllegalAccessException {
doThrow(new IllegalArgumentException()).when(mockCupboardService).checkoutNeed(0, 20, key);
- Map<String, Integer> needMap = Map.ofEntries(
- entry("needID", 0),
- entry("amount", 20)
+ var needMap = List.of(
+ Map.ofEntries(
+ entry("needID", 0),
+ entry("quantity", 20)
+ ),
+ Map.ofEntries(
+ entry("needID", 2),
+ entry("quantity", 30)
+ )
);
var res = cupboardController.checkoutNeeds(needMap, key);
@@ -447,11 +456,17 @@ public class CupboardControllerTest {
@Test
public void checkoutNeedsUnauthorized() throws IOException, IllegalAccessException {
- doThrow(new IllegalAccessException()).when(mockCupboardService).checkoutNeed(0, 20, key);
-
- Map<String, Integer> needMap = Map.ofEntries(
- entry("needID", 0),
- entry("amount", 20)
+ doThrow(new IllegalAccessException()).when(mockAuthService).keyIsValid(key);
+
+ var needMap = List.of(
+ Map.ofEntries(
+ entry("needID", 0),
+ entry("quantity", 20)
+ ),
+ Map.ofEntries(
+ entry("needID", 2),
+ entry("quantity", 30)
+ )
);
var res = cupboardController.checkoutNeeds(needMap, key);
@@ -461,11 +476,14 @@ public class CupboardControllerTest {
@Test
public void checkoutNeedsInternalError() throws IOException, IllegalAccessException {
+ when(mockCupboardService.getNeed(0)).thenReturn(new Need("name", "image", "location", 0, 10, GoalType.MONETARY, true, "a"));
doThrow(new IOException()).when(mockCupboardService).checkoutNeed(0, 20, key);
- Map<String, Integer> needMap = Map.ofEntries(
- entry("needID", 0),
- entry("amount", 20)
+ var needMap = List.of(
+ Map.ofEntries(
+ entry("needID", 0),
+ entry("quantity", 20)
+ )
);
var res = cupboardController.checkoutNeeds(needMap, key);
diff --git a/ufund-ui/src/app/app.component.css b/ufund-ui/src/app/app.component.css
index 6cb44f6..2ed8131 100644
--- a/ufund-ui/src/app/app.component.css
+++ b/ufund-ui/src/app/app.component.css
@@ -4,6 +4,26 @@
height: 100%;
}
+#scroll {
+ overflow: auto;
+ min-height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.modal {
+ position: absolute;
+ background-color: #000000a8;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 3;
+}
+
#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 a6e6e6a..8a0fd04 100644
--- a/ufund-ui/src/app/app.component.html
+++ b/ufund-ui/src/app/app.component.html
@@ -1,18 +1,23 @@
-<div id="header">
- <div>
- <a routerLink="/">
- <h1>Jelly Solutions</h1>
- </a>
+<div id="scroll">
+ <div id="header">
+ <div>
+ <a routerLink="/">
+ <h1>Jelly Solutions</h1>
+ </a>
+ </div>
+ <div>
+ <button *ngIf="(currentUser | async)?.type === userType.MANAGER" routerLink="/dashboard">Dashboard</button>
+ <button routerLink="/cupboard">Cupboard</button>
+ <button *ngIf="(currentUser | async)?.type === userType.HELPER" routerLink="/basket">Basket</button>
+ <!-- <span>{{currentUser$ | async}}</span>-->
+ <button *ngIf="currentUser | async" (click)="logout()">Log Out</button>
+ <button *ngIf="!(currentUser | async)" (click)="login()">Log In</button>
+ <button class="button" (click)="toggleColorScheme()"><span class="icon">brightness_7</span></button>
+ </div>
</div>
- <div>
- <button *ngIf="(currentUser | async)?.type === userType.MANAGER" routerLink="/dashboard">Dashboard</button>
- <button routerLink="/cupboard">Cupboard</button>
- <button *ngIf="(currentUser | async)?.type === userType.HELPER" routerLink="/basket">Basket</button>
-<!-- <span>{{currentUser$ | async}}</span>-->
- <button *ngIf="currentUser | async" (click)="logout()"> Log Out</button>
- <button *ngIf="!(currentUser | async)" (click)="login()"> Log In</button>
- <button class="button" (click)="toggleColorScheme()"><span class="icon">brightness_7</span></button>
- </div>
-</div>
-<router-outlet />
+ <router-outlet />
+</div>
+<div *ngIf="modalService.getModalBehaviorSubject() | async" class="modal">
+ <ng-container [ngTemplateOutlet]="modalService.getModalBehaviorSubject() | async" />
+</div>
diff --git a/ufund-ui/src/app/app.component.ts b/ufund-ui/src/app/app.component.ts
index 2fecc2d..78fd050 100644
--- a/ufund-ui/src/app/app.component.ts
+++ b/ufund-ui/src/app/app.component.ts
@@ -5,6 +5,7 @@ import {AuthService} from './services/auth.service';
import {ToastsService} from './services/toasts.service';
import {User, userType} from './models/User';
import {ActivatedRoute, Router} from '@angular/router';
+import {ModalService} from './services/modal.service';
@Component({
selector: 'app-root',
@@ -22,6 +23,7 @@ export class AppComponent implements OnInit {
private route: ActivatedRoute,
protected toastService: ToastsService,
private viewContainerRef: ViewContainerRef,
+ protected modalService: ModalService,
@Inject(DOCUMENT) private document: Document
) {}
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)" ] },
+};
diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.css b/ufund-ui/src/app/components/dashboard/dashboard.component.css
index 185fdc2..54f362b 100644
--- a/ufund-ui/src/app/components/dashboard/dashboard.component.css
+++ b/ufund-ui/src/app/components/dashboard/dashboard.component.css
@@ -1,7 +1,45 @@
:host {
display: flex;
- flex-direction: column;
+ justify-content: center;
+}
+
+#box {
+ display: flex;
width: 800px;
- align-self: center;
- gap: 20px
+ flex-direction: column;
+ gap: 10px;
+}
+
+#stats {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+}
+
+.card {
+ background-color: #2e2e2e;
+ width: 400px;
+ height: 130px;
+ border-radius: 5px;
+ padding: 10px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+
+ h1 {
+ padding: 0 10px;
+ }
+}
+
+.listCard {
+ display: flex;
+ flex-direction: column;
+ background-color: #2e2e2e;
+ border-radius: 5px;
+ padding: 10px;
+ gap: 10px;
+}
+
+.content {
+ align-self: end;
}
diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.html b/ufund-ui/src/app/components/dashboard/dashboard.component.html
index 2d7b4c3..233096a 100644
--- a/ufund-ui/src/app/components/dashboard/dashboard.component.html
+++ b/ufund-ui/src/app/components/dashboard/dashboard.component.html
@@ -1,10 +1,29 @@
+<div id="box">
+ @if ((authService.getCurrentUserSubject() | async)?.type === userType.MANAGER) {
+ <h1>Admin Dashboard</h1>
+ <div id="stats">
+ <div class="card">
+ <span>Registered users</span>
+ <h1 class="content" *ngIf="count"> {{count | async}} </h1>
+ </div>
-<h1>Admin Dashboard</h1>
-<!--<app-mini-need-list [needList]="topNeeds" jtitle="Top needs" url="/cupboard"/>-->
-<!--<app-mini-need-list [needList]="almostThere" jtitle="Almost there" url="/cupboard"/>-->
-<!--<app-mini-need-list [needList]="inBasket" jtitle="In your basket" url="/basket"/>-->
-<span>_ Registered users</span>
-<span>_ Needs with overflow</span>
-<span>_ Needs in peoples baskets</span>
-<span>_ Total monetary contributions</span>
-<span>_ </span>
+ <div class="card">
+ <span>Total monetary contributions</span>
+ <h1 class="content" *ngIf="totalDonations"> ${{totalDonations | async}} </h1>
+ </div>
+ </div>
+
+ <div class="listCard">
+ <span>Fulfilled needs</span>
+ <app-mini-need-list [needList]="fulfilledNeeds.getValue()" label="Fulfilled needs"> </app-mini-need-list>
+ </div>
+ <div class="listCard">
+ <span>Most fulfilled needs</span>
+ <app-mini-need-list [needList]="mostFulfilledNeeds.getValue()" label="Most fulfilled"> </app-mini-need-list>
+ </div>
+
+ } @else {
+ <h1>Unauthorized</h1>
+ <span>This page requires you to be logged in as an admin! <a routerLink="/login">Log In</a></span>
+ }
+</div>
diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.ts b/ufund-ui/src/app/components/dashboard/dashboard.component.ts
index c94b5c6..2ab4db2 100644
--- a/ufund-ui/src/app/components/dashboard/dashboard.component.ts
+++ b/ufund-ui/src/app/components/dashboard/dashboard.component.ts
@@ -1,10 +1,11 @@
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';
+import {userType} from '../../models/User';
@Component({
selector: 'app-dashboard',
@@ -14,9 +15,11 @@ import {UsersService} from '../../services/users.service';
})
export class DashboardComponent implements OnInit{
- topNeeds?: Need[]
- almostThere?: Need[]
- inBasket?: Need[]
+ protected count = new BehaviorSubject<number | undefined>(undefined)
+ protected totalDonations = new BehaviorSubject<number | undefined>(undefined)
+ protected totalNeeds = new BehaviorSubject<number | undefined>(undefined)
+ protected fulfilledNeeds = new BehaviorSubject<Need[] | undefined>(undefined)
+ protected mostFulfilledNeeds = new BehaviorSubject<Need[] | undefined>(undefined)
constructor(
protected authService: AuthService,
@@ -26,20 +29,26 @@ export class DashboardComponent implements OnInit{
) {}
ngOnInit() {
- let user = this.authService.getCurrentUser()
- if(!localStorage.getItem("credential") && !user) {
- this.router.navigate(['/login'])
- 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 totalValue = 0
+ for (let need of needs) {
+ if (need.type === GoalType.MONETARY) {
+ totalValue += need.current
+ this.totalDonations.next(totalValue)
+ }
+
+ }
+ this.fulfilledNeeds.next(needs.filter(a => ((a.current / a.maxGoal)) >= 1))
+ needs.sort((a, b) => b.current/b.maxGoal - a.current/a.maxGoal)
- this.userService.getBasket().subscribe(r => {
- this.inBasket = r;
+ needs = needs.filter(a => a.current != 0)
+ this.totalNeeds.next(needs.length)
+ this.mostFulfilledNeeds.next(needs.slice(0, 5))
})
+
+
}
+ protected readonly userType = userType;
}
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 bd41fda..4764b0f 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 b88ef31..7158194 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,81 +1,25 @@
-
-<!--<div id="needCount">-->
-<!-- <label for="needCount">Needs in Basket:</label>-->
-<!-- <span>{{ this.usersService.getBasket().getValue().length }}</span>-->
-<!--</div>-->
-
-<!--<div *ngIf="this.usersService.getBasket().getValue().length == 0">-->
-<!-- <h2>There are no needs in the basket</h2>-->
-<!--</div>-->
-
-<!--<table class="needs" id="funding-basket" *ngIf="this.usersService.getBasket().getValue().length != 0">-->
-<!-- <thead>-->
-<!-- <tr>-->
-<!-- <th class="need"></th>-->
-<!-- </tr>-->
-<!-- </thead>-->
-<!-- <tbody>-->
-<!-- <tr *ngFor="let need of usersService.getBasket().getValue()">-->
-<!-- <td>-->
-<!-- <a routerLink="/need/{{need.id}}">{{need.name}}</a>-->
-<!-- <p>Goal: {{need.maxGoal}}</p>-->
-<!-- <p>Current: {{(need.current).toFixed(2)}}</p>-->
-<!-- <p>How much to Contribute: <input type="number" placeholder="insert value" min="1" id={{need.id}} class="contribution"></p>-->
-<!-- <br>-->
-<!-- <div>-->
-<!-- <button type="button" class="removeNeed" title="delete need"-->
-<!-- (click)="this.usersService.removeNeed(need.id)">Remove Need</button>-->
-<!-- </div>-->
-<!-- </td>-->
-<!-- </tr>-->
-<!-- </tbody>-->
-<!--</table>-->
-<!--<br>-->
<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 class="description">-->
- <!-- {{need.description}}-->
- <!-- </div>-->
- </div>
-
- <div class="actionArea">
- <input type="number" placeholder="Quantity" min="1" id={{need.id}} class="contribution sort-scheme">
- <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 [uid]="1" [actionArea]="NLActions" [needs]="(usersService.getBasket() | async)!"/>
+ <br>
+ <div id="footer">
+ <button class="button2" title="checkout" (click)="checkout()">Checkout</button>
+ <span id="running-total">Your current running total is: ${{runningTotal | async}}</span>
</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 847baee..78ce958 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,11 @@ 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 {BehaviorSubject, firstValueFrom, of} from 'rxjs';
import {AuthService} from '../../services/auth.service';
import {ToastsService, ToastType} from '../../services/toasts.service';
+import {userType} from '../../models/User';
+import {GoalType} from '../../models/Need';
@Component({
selector: 'app-funding-basket',
@@ -18,77 +20,61 @@ export class FundingBasketComponent implements OnInit {
private router: Router,
protected cupboardService: CupboardService,
protected usersService: UsersService,
- private authService: AuthService,
+ protected authService: AuthService,
private toastService: ToastsService
) {}
+ public runningTotal = new BehaviorSubject(0)
@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
}
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: { needID: number, quantity: number }[] = []
+ let isNotValid = false
+ for (let contribution of document.querySelectorAll<HTMLInputElement>('.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!")
-
- setTimeout(() => {
- contribution.setAttribute("style", "border-color: #ffffff");
- }, 3000);
}
+ order.push({needID: +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;
- let need = await firstValueFrom(this.cupboardService.getNeed(+contribution.id));
- 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<string>();
- }))
- .subscribe((result) => {
- if (result) {
- //this.needList?.refresh()
- } else {
- console.log('need update failed');
- }
- this.toastService.sendToast(ToastType.INFO, "Checkout successful");
- });
- }
+
+ if (isNotValid) {
+ this.toastService.sendToast(ToastType.ERROR, "Invalid input in funding basket!")
+ return;
+ }
+
+ try {
+ await firstValueFrom(this.cupboardService.checkoutNeed(order))
+ } catch (ex:any) {
+ this.toastService.sendToast(ToastType.ERROR, ex.error);
+ return
}
+
+ order.forEach(contribution => this.usersService.removeNeed(contribution.needID))
+ this.toastService.sendToast(ToastType.INFO, "Checkout successful");
}
+ resetColor(ev: any) {
+ let total = 0
+ this.runningTotal.next(total);
+ for (let contribution of document.querySelectorAll<HTMLInputElement>('.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")
+ }
+ 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 f6e5631..47e6ebd 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/mini-need-list/mini-need-list.component.css b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css
index ac456ab..6dceee1 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
@@ -1,29 +1,24 @@
:host {
display: flex;
flex-direction: column;
- border: solid rgba(255, 255, 255, 0.5) 1px;
border-radius: 5px;
+ height: 175px;
}
-#header {
+#empty {
+ height: 175px;
display: flex;
- flex-direction: row;
- justify-content: space-between;
- border-bottom: solid rgba(255, 255, 255, 0.5) 1px;
- padding: 10px;
-
- a {
- display: flex;
- }
+ align-items: center;
+ color: #878787;
}
#needList {
display: flex;
flex-direction: row;
- padding: 10px;
+ /*padding: 10px;*/
gap: 10px;
justify-content: start;
- overflow: clip;
+ overflow: auto;
}
.needEntry {
@@ -45,6 +40,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 a2de9e5..fc70c3d 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,9 +1,7 @@
-<div id="header">
- <span>{{jtitle}}</span>
- <a [routerLink]="url">Show All<span class="icon">arrow_forward_ios</span></a>
-</div>
-
<div id="needList">
+ <div id="empty" *ngIf="!needList?.length">
+ <span>(No needs)</span>
+ </div>
<div class="needEntry" *ngFor="let need of needList" [routerLink]="'/need/'+need.id">
<div>
<span class="needName">{{need.name}}</span>
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-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..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,23 +1,45 @@
-<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>
+ <span>Location:</span>
+ <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 56ae6a6..b3af85f 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: var(--tertiary-color);
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;
}
@@ -53,18 +20,49 @@ select {
font-size: 10pt;
}
+.need-image {
+ transition: all 0.15s ease-in-out;
+ height: 130px;
+ width: 200px;
+ margin: -10px 0 0 -10px;
+ object-fit: cover;
+ border-radius: 5px;
+ mask-image: linear-gradient(to right, rgb(255,255,255) 0, rgb(255,255,255,.1) 60%, rgb(255,255,255,0) 100%);
+}
+
+/*.clickable:hover {*/
+/* .need-image {*/
+/* mask-image: none;*/
+/* width: 210px*/
+/* }*/
+/* .left {*/
+/* width: 32.5%;*/
+/* }*/
+/* .prog {*/
+/* width: 72.5%;*/
+/* }*/
+/*}*/
+
.split {
display: flex;
flex-direction: row;
- justify-content: space-between;
-
.left {
+ width: 15%;
+ transition: all 0.2s ease-in-out;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .middle {
+ width: 42.5%;
display: flex;
+ align-items: start;
flex-direction: column;
}
.right {
+ width: 42.5%;
display: flex;
flex-direction: column;
align-items: end;
@@ -80,8 +78,12 @@ select {
}
.prog {
+ transition: all 0.2s ease-in-out;
+ width: 85%;
display: flex;
flex-direction: column;
+ align-self: end;
+ margin-top: -5.25%;
}
.clickable {
@@ -105,6 +107,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 e9f70f6..0e5b762 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,39 +1,17 @@
-<div id="header">
- <div id="searchArea">
- <form id="search-form" #searchForm="ngForm">
- <input type="text" name="search" class="wide-input sort-scheme" 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 [(ngModel)] = "sortSelection" class="wide-input sort-scheme" (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 class="sort-scheme" 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">
+
<div class="split">
<div class="left">
+ <img *ngIf="need.image" alt="Need image" class="need-image" [src]="need.image"/>
+ </div>
+
+ <div class="middle">
<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>
@@ -46,19 +24,10 @@
<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..7ca0ae7 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,62 @@
-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);
-
- getPrefix(need: Need) {
- return (need.type === GoalType.MONETARY) ? "$" : "";
- }
+export class NeedListComponent implements OnChanges {
- decrementPage() {
- this.currentPage--;
- this.updateVisibleNeeds();
- }
+ @Input({required: true}) needs!: Need[]
+ @Input({required: true}) uid!: number
+ @Input() itemsPerPage: number = 5;
+ @Input() actionArea: TemplateRef<any> | null = null
- incrementPage() {
- this.currentPage++;
- this.updateVisibleNeeds();
- }
+ visibleNeeds: Need[] = [];
+ currentPage: number = parseInt(localStorage.getItem('currentPage'+this.uid) ?? '0') ?? 0;
+ totalPages: number = 0;
- 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;
+ ngOnChanges() {
+ this.updateVisibleNeeds()
+ this.currentPage = parseInt(localStorage.getItem('currentPage'+this.uid) ?? '0') ?? 0;
}
- if (this.itemsPerPage < 1) {
- this.itemsPerPage = 1;
- }
- 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'
+ //increment/decrement
+ decrementPage() {
+ this.currentPage--;
+ localStorage.setItem('currentPage'+this.uid, this.currentPage.toString());
+ 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++;
+ localStorage.setItem('currentPage'+this.uid, this.currentPage.toString());
+ 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;
+ //skipping pages
+ lastPage() {
+ this.currentPage = this.totalPages - 1
+ localStorage.setItem('currentPage'+this.uid, this.currentPage.toString());
+ 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
+ localStorage.setItem('currentPage'+this.uid, this.currentPage.toString());
+ 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 47aa8b3..6ca1350 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,11 +4,13 @@
}
#box {
+ /*padding-top: 7.5%;*/
display: flex;
flex-direction: column;
width: 800px;
justify-content: start;
gap: 10px;
+ padding: 0 10px;
}
.needName {
@@ -22,32 +24,19 @@
/*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: calc(100% + 40px);
+ 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,.2) 80%, rgba(255,255,255,.1) 90%, transparent 100%);
border-radius: 10px;
- box-shadow: var(--dark-highlight-clor) 0 0 50px;
+ margin-left: -20px;
+ margin-right: -20px;
+ margin-bottom: -80px;
}
.urgent {
@@ -66,8 +55,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..2629346 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,50 @@
<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>
-
+ @if (need) {
+ <img *ngIf="need.image" alt="Need image" class="need-image" [src]="need.image"/>
+ <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 > 0">
- <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"/>
+ <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="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>
+ <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>
+ } @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..17e330c 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 {Component, Input, OnInit} from '@angular/core';
import {GoalType, 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 {
@@ -33,29 +32,15 @@ export class NeedPageComponent {
this.cupboardService.getNeed(id).subscribe(n => this.need = n);
}
- back() {
- 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)
if (currentUser) {
if (!currentUser.basket.includes(need.id)) {
currentUser.basket.push(need.id);
this.usersService.updateUser(currentUser)
.pipe(catchError((err, _) => {
- console.error(err);
+ let action = {label: "View Basket", onAction: () => this.router.navigate(['/basket'])}
+ this.toastService.sendToast(ToastType.INFO, `"${need.name}" Added to basket`, action)
return of();
}))
.subscribe(() => {
@@ -69,19 +54,16 @@ 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.toastService.sendToast(ToastType.INFO, "Need deleted.")
+ this.router.navigate(['/cupboard'])
})
// this.refresh();
}
- edit(need: Need) {
-
- }
+ readonly GoalType = GoalType
}
diff --git a/ufund-ui/src/app/components/signup/signup.component.css b/ufund-ui/src/app/components/signup/signup.component.css
index 429bc42..aa90e04 100644
--- a/ufund-ui/src/app/components/signup/signup.component.css
+++ b/ufund-ui/src/app/components/signup/signup.component.css
@@ -3,15 +3,26 @@
align-items: center;
justify-content: center;
height: 100%;
- margin-top: -66px
+ margin-top: -66px;
+ background: rgba(0, 0, 0, .65) url("https://4kwallpapers.com/images/walls/thumbs_2t/13136.png");
+ background-blend-mode: darken;
+ background-size: cover;
}
#box {
display: flex;
flex-direction: column;
- /*max-width: 300px;*/
+ max-width: 500px;
gap: 10px;
+ backdrop-filter: blur(25px);
+ background-color: rgba(0, 0, 0, 0.1);
+ padding: 30px;
+ color: white;
+ border-radius: 5px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: rgb(140, 140, 255);
& > div {
display: flex;
@@ -19,6 +30,11 @@
}
}
+#password {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
.border {
border-style: solid;
border-width: 1px;
@@ -34,6 +50,8 @@
width: 100%;
appearance: none;
overflow: hidden;
+ border-bottom-right-radius: 5px;
+ border-bottom-left-radius: 5px;
/*margin-top: -5px;*/
}
@@ -57,13 +75,10 @@
#passReq {
display: flex;
flex-direction: column;
+ margin-top: 10px;
}
#box > div {
- display: flex;
- flex-direction: row;
- align-items: start;
- gap: 20px;
div {
display: flex;
diff --git a/ufund-ui/src/app/components/signup/signup.component.html b/ufund-ui/src/app/components/signup/signup.component.html
index 84f15e4..ef2fc27 100644
--- a/ufund-ui/src/app/components/signup/signup.component.html
+++ b/ufund-ui/src/app/components/signup/signup.component.html
@@ -7,13 +7,12 @@
<div>
<div>
- <input placeholder="Password" type="password" (input)="validate(username.value, confirmPass.value, password.value)" #password>
+ <input id="password" placeholder="Password" type="password" (input)="validate(username.value, confirmPass.value, password.value)" #password>
<progress [ngClass]="'color' + strength.getValue()" id="bar" [value]="strength | async" max="5"> </progress>
<span *ngIf="passwordStatusText">{{passwordStatusText | async}}</span>
- </div>
-
- <div id="passReq">
- <span *ngFor="let requirement of Object.values(passwordRequirements)" [style.color]="requirement.value ? 'green' : 'red'"><span class="icon">{{requirement.value?"check":"close"}}</span> {{requirement.title}}</span>
+ <div id="passReq">
+ <span *ngFor="let requirement of Object.values(passwordRequirements)" [style.color]="requirement.value ? 'green' : 'red'"><span class="icon">{{requirement.value?"check":"close"}}</span> {{requirement.title}}</span>
+ </div>
</div>
</div>
diff --git a/ufund-ui/src/app/components/signup/signup.component.ts b/ufund-ui/src/app/components/signup/signup.component.ts
index 9c37211..2762d03 100644
--- a/ufund-ui/src/app/components/signup/signup.component.ts
+++ b/ufund-ui/src/app/components/signup/signup.component.ts
@@ -1,4 +1,4 @@
-import {Component} from '@angular/core';
+import {Component, ElementRef, ViewChild} from '@angular/core';
import {UsersService} from '../../services/users.service';
import {Router} from '@angular/router';
import {BehaviorSubject} from 'rxjs';
@@ -29,6 +29,7 @@ export class SignupComponent {
protected ableToCreateAccount = new BehaviorSubject(false)
protected passwordRequirements: PasswordRequirements = new PasswordRequirements()
protected strength = new BehaviorSubject(0)
+ @ViewChild("username") usernameInput!: ElementRef<HTMLInputElement>
constructor(
protected usersService: UsersService,
@@ -56,11 +57,11 @@ export class SignupComponent {
validate(username: string, passConfirm:string, password: string) {
this.passwordsMatch.next(false)
- this.usernameStatusText.next("")
+ this.usernameInput.nativeElement.setAttribute("style", "")
this.checkPasswordStrength(password);
if (username === "") {
- this.usernameStatusText.next("Username field can't be blank")
+ this.usernameInput.nativeElement.setAttribute("style", "border-color: #ff0000")
}
if (passConfirm && password === passConfirm) {
@@ -105,8 +106,6 @@ export class SignupComponent {
this.passwordStatusText.next("")
} else if (strength == 0) {
this.passwordStatusText.next("")
- } else {
- this.passwordStatusText.next("Password must meet requirements")
}
this.strength.next(strength)
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>
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 9232c0c..a87dee2 100644
--- a/ufund-ui/src/app/services/cupboard.service.ts
+++ b/ufund-ui/src/app/services/cupboard.service.ts
@@ -23,8 +23,8 @@ export class CupboardService {
private authService: AuthService
) {}
- createNeed(need: Need): Observable<boolean> {
- return this.http.post<boolean>(this.url, need, this.httpOptions())
+ createNeed(need: Need): Observable<Need> {
+ return this.http.post<Need>(this.url, need, this.httpOptions())
}
getNeeds(): Observable<Need[]> {
@@ -47,7 +47,7 @@ export class CupboardService {
return this.http.delete<boolean>(`${this.url}/${id}`, this.httpOptions())
}
- checkoutNeed(id: number, quantity: number) {
- return this.http.put(`${this.url}/checkout`, {needID: id, amount: quantity}, this.httpOptions())
+ checkoutNeed(data: {needID: number, quantity: number}[]) {
+ return this.http.put(`${this.url}/checkout`, data, this.httpOptions())
}
}
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<TemplateRef<any> | null>(null)
+
+ constructor() {}
+
+ showModal(template: TemplateRef<any>) {
+ console.log("got here", template)
+ this.modal.next(template)
+ }
+
+ hideModal() {
+ this.modal.next(null)
+ }
+
+ getModalBehaviorSubject() {
+ return this.modal;
+ }
+}
diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts
index 4080ebf..688d6e4 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';
@@ -21,11 +21,22 @@ 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,
private authService: AuthService
- ) {}
+ ) {
+ authService.getCurrentUserSubject().subscribe(() => this.refreshBasket())
+ }
async createUser(username:string, password:string) {
await firstValueFrom(this.http.post<User>(this.url, {username: username, password: password}, this.httpOptions()))
@@ -35,6 +46,10 @@ export class UsersService {
return this.http.get<User>(`${this.url}/${id}`, this.httpOptions())
}
+ getCount(): Observable<number> {
+ return this.http.get<number>(`${this.url}/count`, this.httpOptions2())
+ }
+
updateUser(user: User): Observable<User> {
console.log(`${this.url}/${user.username}`, user, this.httpOptions)
return this.http.put<User>(`${this.url}/${user.username}`, user, this.httpOptions())
@@ -45,7 +60,9 @@ export class UsersService {
}
refreshBasket() {
- let promiseArr = this.authService.getCurrentUser()!.basket.map(async needID => {
+ let usr = this.authService.getCurrentUser();
+ if (!usr) return;
+ let promiseArr = usr.basket.map(async needID => {
return await firstValueFrom(this.cupboardService.getNeed(needID));
})
Promise.all(promiseArr).then(r => this.basket.next(r));
@@ -71,4 +88,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);
+ }
+
}
diff --git a/ufund-ui/src/styles.css b/ufund-ui/src/styles.css
index 09e354c..44f6107 100644
--- a/ufund-ui/src/styles.css
+++ b/ufund-ui/src/styles.css
@@ -46,7 +46,9 @@ body {
font-optical-sizing: auto;
}
-input {
+input, textarea {
+ resize: none;
+ font-family: Inter, sans-serif;
font-size: 14pt;
padding: 5px;
border-radius: 5px;
@@ -70,6 +72,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: var(--hover-color);