diff options
author | benal01 <bja4245@rit.edu> | 2025-04-02 23:00:14 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-02 23:00:14 -0400 |
commit | 2f67d2218b48937a370c5562eff5141b80475524 (patch) | |
tree | c744dd407f46f71b692aaaad6d4c66237c8ba9a5 | |
parent | 2b7c42ffacaaf884bc9497e975c0c3274e9f966e (diff) | |
parent | fb6d8140830bbb5081056105eaa775f26885da8f (diff) | |
download | JellySolutions-2f67d2218b48937a370c5562eff5141b80475524.tar.gz JellySolutions-2f67d2218b48937a370c5562eff5141b80475524.tar.bz2 JellySolutions-2f67d2218b48937a370c5562eff5141b80475524.zip |
Merge pull request #25 from RIT-SWEN-261-02/need-image
Need image support + need page image styling
23 files changed, 163 insertions, 73 deletions
diff --git a/ufund-api/data/cupboard.json b/ufund-api/data/cupboard.json index 0e3917f..493d7fa 100644 --- a/ufund-api/data/cupboard.json +++ b/ufund-api/data/cupboard.json @@ -1 +1 @@ -[{"name":"Pollution Filters","location":"New York Harbor","id":5,"maxGoal":1000.0,"type":"PHYSICAL","urgent":true,"filterAttributes":[],"current":0.0,"description":"One thousand heavy duty pollution filters need to be installed as soon as possible throughout the new york harbor."},{"name":"Diving Equipment","location":"Gulf of Mexico","id":6,"maxGoal":120000.0,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"Our research team in mexico needs new deep sea rated equipment to continue their research on the ecosystem! Until the equipment funding is fully reached and gear installed, no more progress can be made."},{"name":"Dog Fish research","location":"RIT","id":8,"maxGoal":1000000.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"Dog Fish are a very interesting species that many research facilities are not researching. The funding will go towards starting research papers on the species."},{"name":"Fish Food Promotion","location":"","id":10,"maxGoal":1000.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"The money will be spent on a small advertising campaign at a local pet store, where free fish food will be given out to people willing to listen to our cause."},{"name":"Harpoons","location":"Atlantic City","id":11,"maxGoal":25.0,"type":"PHYSICAL","urgent":false,"filterAttributes":[],"current":0.0,"description":"Harpoons are needed for an upcoming whaling museum exhibit. We aren't picky, and need any harpoons that would be interesting for display. We are accepting any harpoons at least 30 years old, but the older the better."},{"name":"Sea Urchin Hats","location":"Seaworld","id":12,"maxGoal":90.0,"type":"PHYSICAL","urgent":true,"filterAttributes":[],"current":0.0,"description":"Seaworld's sea urchins will be wearing your hats as a part of a promo! Hats donated will be used as a part of an awareness exhibit at Seaworld."},{"name":"Awareness Exhibit","location":"Seneca Park Zoo","id":13,"maxGoal":1.0E7,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"Getting the Jelly Solutions message out is our top priority. We would like to open a large exhibit featuring all the endangered species due to climate change, overfishing, and pollution. Sadly, this wouldn't be cheap as we want to really get the message across to visitors through different animals, scenes, info boards, and videos."},{"name":"New Whale","location":"Seneca Park Zoo","id":14,"maxGoal":1.0,"type":"PHYSICAL","urgent":false,"filterAttributes":[],"current":0.0,"description":"Seneca park zoo needs a new whale for the upcoming promotional exhibit! The zoo will provide a commemorative plaque for the donor."},{"name":"New Gosnell Algae Filter","location":"Gosnell College","id":15,"maxGoal":40.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"The Gosnell College fish tank gets way to dirty way too quickly. $40 surely could replace the filter with a more effective one."},{"name":"Awareness Lunches","location":"Colleges and Highschools","id":16,"maxGoal":5000.0,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"College and Highschool students would be great recruits for our volunteer events. The lunches would promote our cause and demonstrate ways that they could easily contribute to the movement."},{"name":"Fish Column for RIT Tours","location":"Wallace Library","id":17,"maxGoal":2.0E7,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"RIT tours always run through Wallace Library into the SHED. What better way to encourage students to attend the college than a massive aquarium column? This would not only encourage students to join RIT, and could also raise awareness for our cause."},{"name":"Submarine Matinience","location":"New York Harbor","id":18,"maxGoal":1000000.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"Submarines are necessary tools that many other needs cannot function without. These urgent repairs need to happen as soon as possible."},{"name":"Volunteering for Volunteer Lunches ","location":"Lake Ontario","id":19,"maxGoal":5.0,"type":"PHYSICAL","urgent":false,"filterAttributes":[],"current":0.0,"description":"We need staff to help organize our awareness lunches! The lunches will recruit interested people into volunteering for upcoming events and cleanups."},{"name":"Volunteer Misc. Equipment","location":"Lake Ontario","id":20,"maxGoal":2500.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"$2500 is needed to cover the unforeseen costs of the upcoming beach cleanup. The money will be spent on nets and other tools for volunteers."},{"name":"Invasive eel removal","location":"Pacific Seafloor","id":21,"maxGoal":1.0E8,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"The population of invasive eels are destroying ecosystems. We need to remove these eels and restore balance to these ecosystems as soon as possible."},{"name":"Fishing Liscense Enforcement","location":"Rochester Bridges","id":22,"maxGoal":10000.0,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"Rochester citizens won't stop fishing off bridges no matter how many times we ask. The funding will go to increased enforcement of fishing licenses in delegated stocked areas."},{"name":"Waste Runoff Management","location":"RIT Watershed","id":23,"maxGoal":98000.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"Waste runoff into the protected watershed near RIT is a consistent issue. We need funding to research how to best solve this problem."},{"name":"Lobbying for anti-dynamite fishing legislation","location":"Washington DC","id":24,"maxGoal":50000.0,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"Dynamite fishing destroys ecosystems. Legislation needs to be passed as soon as possible! However, lobbyists aren't cheap. Any support to the fund would be greatly appreciated."},{"name":"Bioluminescence Tunnel","location":"Golisano College of Computing","id":26,"maxGoal":2.8E7,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"Golisano needs a biolumenescence tunnel! The addition would be costly, but would make the building much more interesting to be in. People sometimes complain about the way the building feels, and this would be a great way to fix that."}]
\ No newline at end of file +[{"name":"Pollution Filters","image":"https://www.water-pollutionsolutions.com/image-files/trash-boom-400x400.webp","location":"New York Harbor","id":5,"maxGoal":1000.0,"type":"PHYSICAL","urgent":true,"filterAttributes":[],"current":0.0,"description":"One thousand heavy duty pollution filters need to be installed as soon as possible throughout the new york harbor."},{"name":"Diving Equipment","image":"https://www.tripsavvy.com/thmb/1Kb4aOEh0hulYSUdvc2lfYp9xUU=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/full-set-of-scuba-diving-equipment--scuba-gear-and-accessories--1223442535-7472dd1b3cc647b7a0240e4ff28577c1.jpg","location":"Gulf of Mexico","id":6,"maxGoal":120000.0,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"Our research team in mexico needs new deep sea rated equipment to continue their research on the ecosystem! Until the equipment funding is fully reached and gear installed, no more progress can be made."},{"name":"Dog Fish research","image":"https://i.ytimg.com/vi/5y9wG0o2oys/maxresdefault.jpg","location":"RIT","id":8,"maxGoal":1000000.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"Dog Fish are a very interesting species that many research facilities are not researching. The funding will go towards starting research papers on the species."},{"name":"Fish Food Promotion","image":"https://www.newsservice.org/getimage.php?p=c2dpZD0zNDA1OSZzaWQ9MQ==","location":"PETCO","id":10,"maxGoal":1000.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"The money will be spent on a small advertising campaign at a local pet store, where free fish food will be given out to people willing to listen to our cause."},{"name":"Harpoons","image":"https://cdn.images.express.co.uk/img/dynamic/151/590x/Sperm-whales-sperm-1411080.jpg?r=1616059298592","location":"Atlantic City","id":11,"maxGoal":25.0,"type":"PHYSICAL","urgent":false,"filterAttributes":[],"current":0.0,"description":"Harpoons are needed for an upcoming whaling museum exhibit. We aren't picky, and need any harpoons that would be interesting for display. We are accepting any harpoons at least 30 years old, but the older the better."},{"name":"Sea Urchin Hats","image":"https://i.etsystatic.com/iap/3c0935/4347866713/iap_640x640.4347866713_7nns9y41.jpg?version=0","location":"Seaworld","id":12,"maxGoal":90.0,"type":"PHYSICAL","urgent":true,"filterAttributes":[],"current":0.0,"description":"Seaworld's sea urchins will be wearing your hats as a part of a promo! Hats donated will be used as a part of an awareness exhibit at Seaworld."},{"name":"Awareness Exhibit","image":null,"location":"Seneca Park Zoo","id":13,"maxGoal":1.0E7,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"Getting the Jelly Solutions message out is our top priority. We would like to open a large exhibit featuring all the endangered species due to climate change, overfishing, and pollution. Sadly, this wouldn't be cheap as we want to really get the message across to visitors through different animals, scenes, info boards, and videos."},{"name":"New Whale","image":"https://s.abcnews.com/images/GMA/150721_gma_aquarium_4x3_992.jpg","location":"Seneca Park Zoo","id":14,"maxGoal":1.0,"type":"PHYSICAL","urgent":false,"filterAttributes":[],"current":0.0,"description":"Seneca park zoo needs a new whale for the upcoming promotional exhibit! The zoo will provide a commemorative plaque for the donor."},{"name":"New Gosnell Algae Filter","image":"https://i.redd.it/my-tank-is-gross-and-im-confused-v0-4mje2n8ggawc1.jpg?width=4032&format=pjpg&auto=webp&s=68ef0f53cc4cf1287b3804241708b67de00f4744","location":"Gosnell College","id":15,"maxGoal":40.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"The Gosnell College fish tank gets way to dirty way too quickly. $40 surely could replace the filter with a more effective one."},{"name":"Awareness Lunches","image":"https://northernontario.travel/sites/default/files/Shore-lunch-CTA.jpg","location":"Colleges and Highschools","id":16,"maxGoal":5000.0,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"College and Highschool students would be great recruits for our volunteer events. The lunches would promote our cause and demonstrate ways that they could easily contribute to the movement."},{"name":"Fish Column for RIT Tours","image":"https://compote.slate.com/images/0217aba0-941b-4744-b33a-5d32a202db2f.jpeg?crop=1560%2C1040%2Cx0%2Cy0","location":"Wallace Library","id":17,"maxGoal":2.0E7,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"RIT tours always run through Wallace Library into the SHED. What better way to encourage students to attend the college than a massive aquarium column? This would not only encourage students to join RIT, and could also raise awareness for our cause."},{"name":"Submarine Matinience","image":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSerbMu_phBKjF_tsA4JYthKQDJgcU9OAGzeg&s","location":"New York Harbor","id":18,"maxGoal":1000000.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"Submarines are necessary tools that many other needs cannot function without. These urgent repairs need to happen as soon as possible."},{"name":"Volunteering for Volunteer Lunches ","image":"https://images.squarespace-cdn.com/content/v1/62827154bc55076eba9aef96/1654613291356-UEY844JXKFN9UXDVRTKQ/image-asset.jpeg","location":"Lake Ontario","id":19,"maxGoal":5.0,"type":"PHYSICAL","urgent":false,"filterAttributes":[],"current":0.0,"description":"We need staff to help organize our awareness lunches! The lunches will recruit interested people into volunteering for upcoming events and cleanups."},{"name":"Volunteer Misc. Equipment","image":"https://montereybay.noaa.gov/media/getinvolved/images/bn2.jpg","location":"Lake Ontario","id":20,"maxGoal":2500.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"$2500 is needed to cover the unforeseen costs of the upcoming beach cleanup. The money will be spent on nets and other tools for volunteers."},{"name":"Invasive eel removal","image":"https://www.louisianasportsman.com/wp-content/uploads/2019/06/IMG_2252.jpg","location":"Pacific Seafloor","id":21,"maxGoal":1.0E8,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"The population of invasive eels are destroying ecosystems. We need to remove these eels and restore balance to these ecosystems as soon as possible."},{"name":"Fishing Liscense Enforcement","image":"https://i.ytimg.com/vi/vNHAutSs4o4/sddefault.jpg","location":"Rochester Bridges","id":22,"maxGoal":10000.0,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"Rochester citizens won't stop fishing off bridges no matter how many times we ask. The funding will go to increased enforcement of fishing licenses in delegated stocked areas."},{"name":"Waste Runoff Management","image":"https://cdn.rit.edu/images/news/2024-09/WEB-StormDrain.jpg","location":"RIT Watershed","id":23,"maxGoal":98000.0,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"Waste runoff into the protected watershed near RIT is a consistent issue. We need funding to research how to best solve this problem."},{"name":"Lobbying for anti-dynamite fishing legislation","image":"https://s.abcnews.com/images/US/dynamite-gty-jpo-180907_hpMain_16x9_992.jpg","location":"Washington DC","id":24,"maxGoal":50000.0,"type":"MONETARY","urgent":false,"filterAttributes":[],"current":0.0,"description":"Dynamite fishing destroys ecosystems. Legislation needs to be passed as soon as possible! However, lobbyists aren't cheap. Any support to the fund would be greatly appreciated."},{"name":"Bioluminescence Tunnel","image":"https://img.atlasobscura.com/sKxcO-wY9ue650ypcUmSqJAWNMxQZGtaYpZZ1Z7jQ-E/rs:fill:780:520:1/g:ce/q:81/sm:1/scp:1/ar:1/aHR0cHM6Ly9hdGxh/cy1kZXYuczMuYW1h/em9uYXdzLmNvbS91/cGxvYWRzL2Fzc2V0/cy9kMzg0NzYyNmZl/OWJhNTEwNzJfZ2xv/d3dvcm1jYXZlLmpw/Zw.jpg","location":"Golisano College of Computing","id":26,"maxGoal":2.8E7,"type":"MONETARY","urgent":true,"filterAttributes":[],"current":0.0,"description":"Golisano needs a biolumenescence tunnel! The addition would be costly, but would make the building much more interesting to be in. People sometimes complain about the way the building feels, and this would be a great way to fix that."}]
\ No newline at end of file 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..c6e622c 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); + int count = 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/CupboardService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java index 993e7c1..859194a 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java @@ -119,6 +119,9 @@ public class CupboardService { if (checkoutAmount <= 0) { throw new IllegalArgumentException("Amount must be greater than 0"); } + if ((checkoutAmount % 1 != 0) && (cupboardDAO.getNeed(id).getType() == Need.GoalType.PHYSICAL)) { + throw new IllegalArgumentException("Physical amounts must be whole numbers"); + } authService.keyIsValid(key); Need need = cupboardDAO.getNeed(id); need.incrementCurrent(checkoutAmount); 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/public/placeholder.png b/ufund-ui/public/placeholder.png Binary files differnew file mode 100644 index 0000000..5b86fb7 --- /dev/null +++ b/ufund-ui/public/placeholder.png diff --git a/ufund-ui/src/app/app.component.html b/ufund-ui/src/app/app.component.html index 959eada..f697695 100644 --- a/ufund-ui/src/app/app.component.html +++ b/ufund-ui/src/app/app.component.html @@ -5,9 +5,9 @@ </a> </div> <div> - <a routerLink="/dashboard">Dashboard</a> + <a *ngIf="(currentUser | async)?.type === userType.MANAGER" routerLink="/dashboard">Dashboard</a> <a routerLink="/cupboard">Cupboard</a> - <a routerLink="/basket">Basket</a> + <a *ngIf="(currentUser | async)?.type === userType.HELPER" routerLink="/basket">Basket</a> <!-- <span>{{currentUser$ | async}}</span>--> <button *ngIf="currentUser | async" (click)="logout()"> Log Out</button> <button *ngIf="!(currentUser | async)" (click)="login()"> Log In</button> diff --git a/ufund-ui/src/app/app.component.ts b/ufund-ui/src/app/app.component.ts index 2f98334..bc0e71a 100644 --- a/ufund-ui/src/app/app.component.ts +++ b/ufund-ui/src/app/app.component.ts @@ -3,7 +3,7 @@ import {BehaviorSubject} from 'rxjs'; import { DOCUMENT } from '@angular/common'; import {AuthService} from './services/auth.service'; import {ToastsService} from './services/toasts.service'; -import {User} from './models/User'; +import {User, userType} from './models/User'; import {ActivatedRoute, Router} from '@angular/router'; @Component({ @@ -48,4 +48,6 @@ export class AppComponent implements OnInit { localStorage.removeItem("credential") location.reload() } + + protected readonly userType = userType; } 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.css b/ufund-ui/src/app/components/dashboard/dashboard.component.css index 78a69ba..185fdc2 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.css +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.css @@ -1,7 +1,7 @@ :host { display: flex; flex-direction: column; - width: 1000px; + width: 800px; align-self: center; gap: 20px } diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.html b/ufund-ui/src/app/components/dashboard/dashboard.component.html index 6a95ecd..2d7b4c3 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.html +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.html @@ -1,5 +1,10 @@ -<h1>Your 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"/> +<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> 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 dcacca1..847baee 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 @@ -70,11 +70,11 @@ export class FundingBasketComponent implements OnInit { this.cupboardService.checkoutNeed(need.id, +contribution.value) .pipe(catchError((ex, _) => { if (ex.status == 500) { - this.toastService.sendToast(ToastType.INFO, 'Fields cannot be blank'); + this.toastService.sendToast(ToastType.ERROR, 'Fields cannot be blank'); } else if (ex.status == 400) { - this.toastService.sendToast(ToastType.INFO, 'Goal must be greater than 0'); + this.toastService.sendToast(ToastType.ERROR, ex.error); } else { - this.toastService.sendToast(ToastType.INFO, 'Error on creating need'); + this.toastService.sendToast(ToastType.ERROR, 'Error on creating need'); } return new Observable<string>(); })) diff --git a/ufund-ui/src/app/components/login/login.component.html b/ufund-ui/src/app/components/login/login.component.html index c67b903..1017d0f 100644 --- a/ufund-ui/src/app/components/login/login.component.html +++ b/ufund-ui/src/app/components/login/login.component.html @@ -1,7 +1,7 @@ <div id="box"> <h1>Login</h1> - <input placeholder="Username" type="text" #username> - <input placeholder="Password" type="password" #password> + <input placeholder="Username" type="text" #username (keydown.enter)="login(username.value, password.value)"> + <input placeholder="Password" type="password" #password (keydown.enter)="login(username.value, password.value)"> <button type="button" (click)="login(username.value, password.value)">Login</button> <div> New? <a routerLink="/signup">Create an account</a> 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 5f2e5e1..e17609b 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 @@ -89,6 +89,10 @@ select { background-color: #3a3a3a; border-radius: 5px; cursor: pointer; + height: 130px; + display: flex; + flex-direction: column; + justify-content: space-between; } .clickable:hover { 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 0d64c99..84f80dc 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 @@ -40,10 +40,9 @@ </div> </div> - <br> - <div class="prog"> <span id="hover-status-label-{{need.id}}"> </span> + <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> @@ -65,7 +64,9 @@ </div> <div id="page-selector"> - <button *ngIf="currentPage > 0" (click)="decrementPage()"><span class="icon">arrow_back_ios</span></button> + <button [disabled]="!(currentPage !== 0)" (click)="firstPage()"><span class="icon">first_page</span></button> + <button [disabled]="!(currentPage > 0)" (click)="decrementPage()"><span class="icon">arrow_back_ios_new</span></button> <span>Page {{currentPage + 1}} of {{totalPages}}</span> - <button *ngIf="currentPage < totalPages - 1" (click)="incrementPage()"><span class="icon">arrow_forward_ios</span></button> + <button [disabled]="!(currentPage < totalPages - 1)" (click)="incrementPage()"><span class="icon">arrow_forward_ios</span></button> + <button [disabled]="!(currentPage !== totalPages - 1)" (click)="lastPage()"><span class="icon">last_page</span></button> </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 ed14d6a..ae6bc99 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,14 +72,28 @@ 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(); + this.updateVisibleNeeds(); } incrementPage() { this.currentPage++; - this.updateVisibleNeeds(); + this.updateVisibleNeeds(); + } + + lastPage() { + this.currentPage = this.totalPages - 1 + this.updateVisibleNeeds() + } + + firstPage() { + this.currentPage = 0 + this.updateVisibleNeeds() } editNeedsPerPage() { @@ -140,7 +154,7 @@ export class NeedListComponent { ngOnInit(): void { this.refresh() - + } changeSortMode(form : any) { @@ -224,6 +238,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); @@ -273,8 +288,7 @@ export class NeedListComponent { button.style.visibility = 'hidden'; } } -} -function not(location: string) { - throw new Error('Function not implemented.'); + + 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 844410f..44db4b4 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 @@ -31,6 +31,7 @@ .left { display: flex; flex-direction: column; + width : 50%; } .right { @@ -40,6 +41,15 @@ } } +.need-image { + width: 400px; + height: auto; + aspect-ratio: 16/9; + object-fit: cover; + border-radius: 10px; + box-shadow: rgb(0, 40, 70) 0 0 50px; +} + .urgent { font-size: 11pt; background-color: rgba(255, 165, 0, 0.27); 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 12e9b74..6921eac 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,35 +1,41 @@ <div id="box"> - <h1>{{need?.name}}</h1> - <span class="needType">{{need?.type}} GOAL</span> - - <img *ngIf="need.image" alt="Need image" [src]="need?.image"/> - - <p>{{need?.description}}</p> + <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 ?? 0)*100) / (need?.maxGoal ?? 0)).toFixed(0)}}%</strong> complete!</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> - <span><strong>Target Goal:</strong> {{need.type.toString() == '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 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"/> + </div> </div> + <div class="actionArea"> <button *ngIf="isHelper()" (click)="add(need!)"> diff --git a/ufund-ui/src/app/components/signup/signup.component.html b/ufund-ui/src/app/components/signup/signup.component.html index bc3aaf0..84f15e4 100644 --- a/ufund-ui/src/app/components/signup/signup.component.html +++ b/ufund-ui/src/app/components/signup/signup.component.html @@ -8,7 +8,7 @@ <div> <div> <input placeholder="Password" type="password" (input)="validate(username.value, confirmPass.value, password.value)" #password> - <progress [ngClass]="'color' + strength.getValue()" id="bar" [value]="strength | async" max="6"> </progress> + <progress [ngClass]="'color' + strength.getValue()" id="bar" [value]="strength | async" max="5"> </progress> <span *ngIf="passwordStatusText">{{passwordStatusText | async}}</span> </div> @@ -24,8 +24,6 @@ <div> <button [disabled]="!(ableToCreateAccount | async)" (click)="signup(username.value, password.value)">Create Account</button> - <span *ngIf="showSuccessMessage | async">Account created <a routerLink="/login">Proceed to login</a></span> - <span *ngIf="statusText | async">{{statusText | async}}</span> </div> <span>Already have an account? <a routerLink="/login">Log in</a></span> </div> diff --git a/ufund-ui/src/app/components/signup/signup.component.ts b/ufund-ui/src/app/components/signup/signup.component.ts index a20d828..9c37211 100644 --- a/ufund-ui/src/app/components/signup/signup.component.ts +++ b/ufund-ui/src/app/components/signup/signup.component.ts @@ -3,10 +3,10 @@ import {UsersService} from '../../services/users.service'; import {Router} from '@angular/router'; import {BehaviorSubject} from 'rxjs'; import {ToastsService, ToastType} from '../../services/toasts.service'; +import {AuthService} from '../../services/auth.service'; class PasswordRequirements { sixLong: {title: string, value: boolean} = {title: 'Is 6 characters or longer' , value: false} - twelveLong: {title: string, value: boolean} = {title: 'Is 12 characters or longer', value: false} lowercase: {title: string, value: boolean} = {title: 'Includes lowercase letter' , value: false} uppercase: {title: string, value: boolean} = {title: 'Includes uppercase letter' , value: false} number: {title: string, value: boolean} = {title: 'Includes number' , value: false} @@ -25,17 +25,16 @@ export class SignupComponent { protected passwordStatusText = new BehaviorSubject("") protected passwordsMatch = new BehaviorSubject(false) protected usernameStatusText = new BehaviorSubject("") - protected showSuccessMessage = new BehaviorSubject(false) protected passwordStrongEnough = new BehaviorSubject(false) protected ableToCreateAccount = new BehaviorSubject(false) protected passwordRequirements: PasswordRequirements = new PasswordRequirements() protected strength = new BehaviorSubject(0) - protected statusText = new BehaviorSubject(""); constructor( protected usersService: UsersService, protected router: Router, - protected toastService: ToastsService + protected toastService: ToastsService, + protected authService: AuthService ) {} signup(username: string | null, password: string | null) { @@ -45,7 +44,10 @@ export class SignupComponent { } this.usersService.createUser(username, password).then(() => { - this.showSuccessMessage.next(true); + // let action = {label: 'Proceed to login', onAction: () => this.router.navigate(["/login"])} + this.toastService.sendToast(ToastType.INFO, "Account successfully created") + this.authService.login(username, password) + this.router.navigate(["/"]) }).catch(ex => { this.toastService.sendToast(ToastType.ERROR, "Unable to create account: " + friendlyHttpStatus[ex.status]) console.log(ex) @@ -74,16 +76,12 @@ export class SignupComponent { if (password.match(/[^!-~]/g)) { this.passwordStatusText.next("Invalid characters") - return } if (password.length > 6) { this.passwordRequirements.sixLong.value = true } - if (password.length > 12) { - this.passwordRequirements.twelveLong.value = true - } if (password.match(/[a-z]/g)) { this.passwordRequirements.lowercase.value = true } @@ -108,7 +106,7 @@ export class SignupComponent { } else if (strength == 0) { this.passwordStatusText.next("") } else { - this.passwordStatusText.next("5/6 checks required") + 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/models/User.ts b/ufund-ui/src/app/models/User.ts index e6848fa..d7d67b5 100644 --- a/ufund-ui/src/app/models/User.ts +++ b/ufund-ui/src/app/models/User.ts @@ -1,6 +1,6 @@ export enum userType { - HELPER, - MANAGER + HELPER = "HELPER", + MANAGER = "MANAGER" } export interface User { |