aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java26
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java10
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java7
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java10
-rw-r--r--ufund-ui/src/app/components/cupboard/cupboard.component.ts8
-rw-r--r--ufund-ui/src/app/components/dashboard/dashboard.component.html8
-rw-r--r--ufund-ui/src/app/components/dashboard/dashboard.component.ts35
-rw-r--r--ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css2
-rw-r--r--ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html2
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.html2
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.ts9
-rw-r--r--ufund-ui/src/app/components/need-page/need-page.component.html2
-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/models/Need.ts4
-rw-r--r--ufund-ui/src/app/services/users.service.ts13
17 files changed, 141 insertions, 42 deletions
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 a34e891..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
@@ -95,6 +95,32 @@ public class UserController {
}
/**
+ * Responds to the GET request with the total number of users
+ *
+ * @param key The authentication key of the user
+ * @return ResponseEntity with amount and HTTP status of OK<br>
+ * ResponseEntity with HTTP status of UNAUTHORIZED if user is not aa manager<br>
+ * ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise
+ */
+ @GetMapping("/count")
+ public ResponseEntity<Object> getUserCount(@RequestHeader("jelly-api-key") String key) {
+ LOG.log(Level.INFO, "GET /userAmount");
+
+ try {
+ authService.keyHasAccessToCupboard(key);
+ String count = String.valueOf(userService.getUserCount());
+ return new ResponseEntity<>(count, HttpStatus.OK);
+ } catch (IllegalAccessException ex) {
+ LOG.log(Level.WARNING, ex.getLocalizedMessage());
+ return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED);
+ } catch (IOException ex) {
+ LOG.log(Level.SEVERE, ex.getLocalizedMessage());
+ return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+
+ }
+
+ /**
* Updates a User with the provided one
*
* @param user The user to update
diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java
index 29d46cf..27ba0b9 100644
--- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java
@@ -34,6 +34,16 @@ public interface UserDAO {
User getUser(String username) throws IOException;
/**
+ * Retrieves the total count of users
+ *
+ * @return a {@link int amount} number of users
+ * <br>
+ *
+ * @throws IOException if an issue with underlying storage
+ */
+ int getUserCount() throws IOException;
+
+ /**
* Creates and saves a {@linkplain User user}
*
* @param user {@linkplain User user} object to be created and saved
diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java
index ec94da8..7f1fadd 100644
--- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java
@@ -58,6 +58,13 @@ public class UserFileDAO implements UserDAO {
}
@Override
+ public int getUserCount() {
+ synchronized (users) {
+ return users.size();
+ }
+ }
+
+ @Override
public User getUser(String username) {
synchronized (users) {
return users.getOrDefault(username, null);
diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java
index 6e27f50..8b34e68 100644
--- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java
@@ -56,6 +56,16 @@ public class UserService {
}
/**
+ * Returns the total number of users
+ *
+ * @return The number of users
+ * @throws IOException If there was any problem saving the file
+ */
+ public int getUserCount() throws IOException {
+ return userDAO.getUserCount();
+ }
+
+ /**
* Updates a user
*
* @param user The ID of the user to update
diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts
index 2230cd3..a4706b3 100644
--- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts
+++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts
@@ -1,6 +1,6 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import { CupboardService } from '../../services/cupboard.service';
-import { Need, GoalType } from '../../models/Need';
+import { Need } from '../../models/Need';
import { userType } from '../../models/User';
import { catchError, of } from 'rxjs';
import { NeedListComponent } from '../need-list/need-list.component';
@@ -90,7 +90,7 @@ export class CupboardComponent implements OnInit {
id: 0,
maxGoal: form.maxGoal,
type: form.type,
- urgent: form.urgent ? true : false,
+ urgent: !!form.urgent,
filterAttributes: [],
current: 0,
description: form.description
@@ -120,8 +120,4 @@ export class CupboardComponent implements OnInit {
);
}
-
- destroy() {
-
- }
}
diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.html b/ufund-ui/src/app/components/dashboard/dashboard.component.html
index 2d7b4c3..2af467c 100644
--- a/ufund-ui/src/app/components/dashboard/dashboard.component.html
+++ b/ufund-ui/src/app/components/dashboard/dashboard.component.html
@@ -4,7 +4,11 @@
<!--<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 *ngIf="count"> {{count | async}} </span>
+<span>_ Fulfilled needs</span>
+<app-mini-need-list [needList]="fulfilledNeeds.getValue()" jtitle="Fulfilled needs"> </app-mini-need-list>
+<span>_ Most fulfilled needs</span>
+<app-mini-need-list [needList]="mostFulfilledNeeds.getValue()" jtitle="Most fulfilled"> </app-mini-need-list>
<span>_ Total monetary contributions</span>
+<span *ngIf="totalDonations">${{totalDonations | async}} </span>
<span>_ </span>
diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.ts b/ufund-ui/src/app/components/dashboard/dashboard.component.ts
index c94b5c6..9bf7627 100644
--- a/ufund-ui/src/app/components/dashboard/dashboard.component.ts
+++ b/ufund-ui/src/app/components/dashboard/dashboard.component.ts
@@ -1,10 +1,10 @@
import {Component, OnInit} from '@angular/core';
import {AuthService} from '../../services/auth.service';
import {Router} from '@angular/router';
-import {Need} from '../../models/Need';
import {CupboardService} from '../../services/cupboard.service';
-import {firstValueFrom} from 'rxjs';
import {UsersService} from '../../services/users.service';
+import {BehaviorSubject} from 'rxjs';
+import {GoalType, Need} from '../../models/Need';
@Component({
selector: 'app-dashboard',
@@ -14,9 +14,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,
@@ -32,14 +34,25 @@ export class DashboardComponent implements OnInit{
return
}
- firstValueFrom(this.cupboardService.getNeeds()).then(r => {
- this.topNeeds = r.sort((a, b) => b.current - a.current)
- this.almostThere = r.sort((a, b) => a.current/a.maxGoal - b.current/b.maxGoal)
- })
+ this.userService.getCount().subscribe(count => this.count.next(count))
+ this.cupboardService.getNeeds().subscribe(needs => {
+ let 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))
})
+
+
}
}
diff --git a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css
index ac456ab..090bea9 100644
--- a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css
+++ b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.css
@@ -23,7 +23,7 @@
padding: 10px;
gap: 10px;
justify-content: start;
- overflow: clip;
+ overflow: auto;
}
.needEntry {
diff --git a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html
index a2de9e5..9febfa5 100644
--- a/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html
+++ b/ufund-ui/src/app/components/mini-need-list/mini-need-list.component.html
@@ -1,6 +1,6 @@
<div id="header">
<span>{{jtitle}}</span>
- <a [routerLink]="url">Show All<span class="icon">arrow_forward_ios</span></a>
+ <a *ngIf="url" [routerLink]="url">Show All<span class="icon">arrow_forward_ios</span></a>
</div>
<div id="needList">
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 88317dd..593aebf 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
@@ -42,7 +42,7 @@
<div class="prog">
<span id="hover-status-label-{{need.id}}"> </span>
- <span>{{need.current}}/{{need.maxGoal}} ({{((need.current / need.maxGoal) * 100).toFixed(0)}}%)</span>
+ <span>{{getPrefix(need)}}{{need.current}}/{{need.maxGoal}} ({{((need.current / need.maxGoal) * 100).toFixed(0)}}%)</span>
<progress [value]="need.current" [max]="need.maxGoal"></progress>
</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 06a612e..2ec850e 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,5 +1,5 @@
import {Component, EventEmitter, Output} from '@angular/core';
-import {Need} from '../../models/Need';
+import {GoalType, Need} from '../../models/Need';
import {CupboardService} from '../../services/cupboard.service';
import {UsersService} from '../../services/users.service';
import {userType} from '../../models/User';
@@ -72,6 +72,10 @@ export class NeedListComponent {
itemsPerPage: number = 5;
totalPages: number = Math.ceil(this.needs.length / this.itemsPerPage);
+ getPrefix(need: Need) {
+ return (need.type === GoalType.MONETARY) ? "$" : "";
+ }
+
decrementPage() {
this.currentPage--;
this.updateVisibleNeeds();
@@ -229,6 +233,7 @@ export class NeedListComponent {
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);
@@ -278,5 +283,7 @@ export class NeedListComponent {
button.style.visibility = 'hidden';
}
}
+
+ protected readonly GoalType = GoalType;
}
diff --git a/ufund-ui/src/app/components/need-page/need-page.component.html b/ufund-ui/src/app/components/need-page/need-page.component.html
index 522b710..3d362f5 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
@@ -11,7 +11,7 @@
<span>This goal is <strong>{{(((need.current)*100) / (need.maxGoal)).toFixed(0)}}%</strong> complete!</span>
</div>
- <span><strong>Target Goal:</strong> {{need.maxGoal}}</span>
+ <span><strong>Target Goal:</strong> {{(need.type === GoalType.MONETARY) ? "$" : ""}}{{need.maxGoal}}</span>
<span><strong>Amount Currently Collected:</strong> {{need.current}}</span>
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/models/Need.ts b/ufund-ui/src/app/models/Need.ts
index 6cf7e76..588e745 100644
--- a/ufund-ui/src/app/models/Need.ts
+++ b/ufund-ui/src/app/models/Need.ts
@@ -12,6 +12,6 @@ export interface Need {
}
export enum GoalType {
- MONETARY,
- PHYSICAL
+ MONETARY = 'MONETARY',
+ PHYSICAL = 'PHYSICAL'
}
diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts
index 4080ebf..080c394 100644
--- a/ufund-ui/src/app/services/users.service.ts
+++ b/ufund-ui/src/app/services/users.service.ts
@@ -21,6 +21,15 @@ export class UsersService {
})
});
+ httpOptions2 = () => ({
+ headers: new HttpHeaders({
+ 'Content-Type': 'application/json',
+ "jelly-api-key": this.authService.getApiKey()
+ }),
+ responseType: "text" as "json" // don't ask me how or why this works, bc i have no clue...
+ // see the relevant angular bug report https://github.com/angular/angular/issues/18586
+ });
+
constructor(
private http: HttpClient,
private cupboardService: CupboardService,
@@ -35,6 +44,10 @@ export class UsersService {
return this.http.get<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())