aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsowgro <tpoke.ferrari@gmail.com>2025-03-30 20:36:19 -0400
committersowgro <tpoke.ferrari@gmail.com>2025-03-30 20:36:19 -0400
commit2c2957da48b62d16ce24addcc46d0d0ed66f7a9d (patch)
tree209e3fa65cd61dfd6785178ae438b919d69f0de7
parent6bfbf7fa3b5b14b04f99f2dd6c33d336f6f081f6 (diff)
parentb29f29eca643648381bfb62a4b90ad29e17f48a7 (diff)
downloadJellySolutions-2c2957da48b62d16ce24addcc46d0d0ed66f7a9d.tar.gz
JellySolutions-2c2957da48b62d16ce24addcc46d0d0ed66f7a9d.tar.bz2
JellySolutions-2c2957da48b62d16ce24addcc46d0d0ed66f7a9d.zip
Merge branch 'list-and-cupboard-component-refactor' into css
# Conflicts: # ufund-api/data/cupboard.json # ufund-ui/src/app/components/cupboard/cupboard.component.css # ufund-ui/src/app/components/need-list/need-list.component.css # ufund-ui/src/app/components/need-page/need-page.component.html
-rw-r--r--ufund-api/.gitignore6
-rw-r--r--ufund-api/data/cupboard.json2
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java8
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java16
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java4
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java139
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java51
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java7
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java16
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java85
-rw-r--r--ufund-ui/public/delete.pngbin0 -> 6396 bytes
-rw-r--r--ufund-ui/public/edit.pngbin0 -> 6649 bytes
-rw-r--r--ufund-ui/public/search.pngbin0 -> 12743 bytes
-rw-r--r--ufund-ui/src/app/components/cupboard/cupboard.component.css34
-rw-r--r--ufund-ui/src/app/components/cupboard/cupboard.component.html96
-rw-r--r--ufund-ui/src/app/components/cupboard/cupboard.component.ts75
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.css70
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.html73
-rw-r--r--ufund-ui/src/app/components/need-list/need-list.component.ts326
-rw-r--r--ufund-ui/src/app/components/need-page/need-page.component.html3
-rw-r--r--ufund-ui/src/app/models/Need.ts2
21 files changed, 670 insertions, 343 deletions
diff --git a/ufund-api/.gitignore b/ufund-api/.gitignore
index 052bd9d..24d3397 100644
--- a/ufund-api/.gitignore
+++ b/ufund-api/.gitignore
@@ -32,5 +32,7 @@ build/
.vscode/
### application specific ###
-# /src/main/resources/application.properties
-# /data/cupboard.json \ No newline at end of file
+ufund-api/src/main/resources/application.properties
+ufund-api/data/cupboard.json
+ufund-api/data/users.json
+ufund-api/data/userAuths.json \ No newline at end of file
diff --git a/ufund-api/data/cupboard.json b/ufund-api/data/cupboard.json
index b0e71ca..3d49031 100644
--- a/ufund-api/data/cupboard.json
+++ b/ufund-api/data/cupboard.json
@@ -1 +1 @@
-[{"name":"Jellyfish Hats","location":null,"id":26,"maxGoal":10.0,"type":"MONETARY","urgent":false,"filterAttributes":["#savethejellyfish","Clothing","Storefront"],"current":0.0},{"name":"Coral re-re-habilitation","location":null,"id":28,"maxGoal":10000.0,"type":"MONETARY","urgent":false,"filterAttributes":["Preservation","#savethecoral","#helloPhil"],"current":0.0}] \ No newline at end of file
+[{"name":"Pollution Filters","location":"New York Harbor","id":5,"maxGoal":1000.0,"type":"PHYSICAL","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Diving Equipment","location":"Gulf of Mexico","id":6,"maxGoal":50.0,"type":"PHYSICAL","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Life Straw","location":"RIT","id":7,"maxGoal":1.0,"type":"PHYSICAL","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Dog Fish research","location":"RIT","id":8,"maxGoal":1000000.0,"type":"MONETARY","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Jellyfish Glue","location":"Pacific","id":9,"maxGoal":100000.0,"type":"MONETARY","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Fish Food","location":"","id":10,"maxGoal":1000.0,"type":"MONETARY","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Harpoons","location":"Atlantic City","id":11,"maxGoal":900.0,"type":"PHYSICAL","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Sea Urchin Hats","location":"Seaworld","id":12,"maxGoal":90.0,"type":"PHYSICAL","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Awareness Exhibit","location":"Seneca Park Zoo","id":13,"maxGoal":1.0E7,"type":"MONETARY","urgent":false,"filterAttributes":null,"current":0.0},{"name":"New Whale","location":"Seneca Park Zoo","id":14,"maxGoal":1.0,"type":"PHYSICAL","urgent":false,"filterAttributes":null,"current":0.0},{"name":"New Gosnell Algae Filter","location":"Gosnell College","id":15,"maxGoal":40.0,"type":"MONETARY","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Awareness Lunches","location":"Colleges and Highschools","id":16,"maxGoal":5000.0,"type":"MONETARY","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Fish Column for RIT Tours","location":"Wallace Library","id":17,"maxGoal":2.0E7,"type":"MONETARY","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Submarine Matinience","location":"New York Harbor","id":18,"maxGoal":1000000.0,"type":"MONETARY","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Volunteer Lunches ","location":"Lake Ontario","id":19,"maxGoal":150.0,"type":"PHYSICAL","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Volunteer Misc. Equipment","location":"Lake Ontario","id":20,"maxGoal":2500.0,"type":"MONETARY","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Invasive eel removal","location":"Pacific Seafloor","id":21,"maxGoal":1.0E8,"type":"MONETARY","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Fishing Liscense Enforcement","location":"Rochester Bridges","id":22,"maxGoal":10000.0,"type":"MONETARY","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Waste Runoff Management","location":"RIT Watershed","id":23,"maxGoal":98000.0,"type":"MONETARY","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Lobbying for anti-dynamite fishing legislation","location":"Washington DC","id":24,"maxGoal":50000.0,"type":"MONETARY","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Lobbying for better fishing practice legislation","location":"Washington DC","id":25,"maxGoal":65000.0,"type":"MONETARY","urgent":false,"filterAttributes":null,"current":0.0},{"name":"Bioluminescence Tunnel","location":"Golisano College of Computing","id":26,"maxGoal":2.8E7,"type":"MONETARY","urgent":true,"filterAttributes":null,"current":0.0},{"name":"Pollution awareness campain","location":"Middle and Highschools","id":27,"maxGoal":35000.0,"type":"MONETARY","urgent":false,"filterAttributes":null,"current":0.0}] \ No newline at end of file
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 bbfd3f6..d426aee 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
@@ -55,12 +55,14 @@ public class CupboardController {
LOG.log(Level.INFO, "POST /cupboard body={0}", params);
String name = (String) params.get("name");
+ String location = (String) params.get("location");
double maxGoal = ((Number) params.get("maxGoal")).doubleValue();
+ boolean urgent = (Boolean) params.get("urgent");
Need.GoalType goalType = GoalType.valueOf((String) params.get("type"));
try {
authService.keyHasAccessToCupboard(key);
- Need need = cupboardService.createNeed(name, maxGoal, goalType);
+ Need need = cupboardService.createNeed(name, location, maxGoal, goalType, urgent);
return new ResponseEntity<>(need, HttpStatus.OK);
} catch (DuplicateKeyException ex) {
LOG.log(Level.WARNING, ex.getLocalizedMessage());
@@ -104,8 +106,6 @@ public class CupboardController {
*
* @param name The name parameter which contains the text used to find the {@link Need need}
*
- * @deprecated Searching should now be done client side in the future
- *
* @return ResponseEntity with array of {@link Need need} objects (may be empty) and
* HTTP status of OK<br>
* ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise
@@ -181,7 +181,7 @@ public class CupboardController {
/**
* Checks out a need by checkoutAmount
- *
+ *
* @param data JSON object with parameters needID and amount
* @param key Key used to authenticate user
* @return OK if successful, other statuses if failure
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 22e86e3..55a9441 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
@@ -10,38 +10,48 @@ public class Need {
}
@JsonProperty("name") private String name;
+ @JsonProperty("location") private String location;
@JsonProperty("id") private int id;
@JsonProperty("filterAttributes") private String[] filterAttributes;
@JsonProperty("type") final private GoalType type;
@JsonProperty("maxGoal") private double maxGoal;
+ @JsonProperty("urgent") private boolean urgent;
@JsonProperty("current") private double current;
/**
* Create a new need, used by the controller
*
* @param name The name of the need
+ * @param location The physical location of the need
* @param id The unique ID of the need
* @param maxGoal The maximum goal for this need
* @param type The type of need (monetary, physical)
+ * @param urgent The urgency of the need
*/
- public Need(@JsonProperty("name") String name, @JsonProperty("id") int id, @JsonProperty("maxGoal") double maxGoal, @JsonProperty("type") GoalType type) {
+ public Need(@JsonProperty("name") String name, @JsonProperty("location") String location, @JsonProperty("id") int id, @JsonProperty("maxGoal") double maxGoal, @JsonProperty("type") GoalType type, @JsonProperty("urgent") boolean urgent) {
this.id = id;
+ this.location = location;
this.name = name;
this.maxGoal = maxGoal;
this.type = type;
+ this.urgent = urgent;
}
/**
* Create a new need
*
* @param name The name of the need
+ * @param location The location of the need
* @param maxGoal The maximum goal for this need
* @param type The type of need (monetary, physical)
+ * @param urgent The urgency of the need
*/
- public Need(String name, GoalType type, double maxGoal) {
+ public Need(String name, String location, double maxGoal, GoalType type, boolean urgent) {
this.name = name;
+ this.location = location;
this.type = type;
this.maxGoal = maxGoal;
+ this.urgent = urgent;
}
/**
@@ -51,11 +61,13 @@ public class Need {
*/
public Need(Need other) {
this.name = other.name;
+ this.location = other.location;
this.id = other.id;
this.filterAttributes = other.filterAttributes;
this.type = other.type;
this.maxGoal = other.maxGoal;
this.current = other.current;
+ this.urgent = other.urgent;
}
public String getName() {
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 aaa8cb8..4dcfcad 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
@@ -31,7 +31,7 @@ public class CupboardService {
* @throws IOException Thrown if there was any issue saving the data
* @throws DuplicateKeyException If there already exists a need with the same name
*/
- public Need createNeed(String name, double maxGoal, Need.GoalType goalType) throws IOException, DuplicateKeyException {
+ public Need createNeed(String name, String location, double maxGoal, Need.GoalType goalType, boolean urgent) throws IOException, DuplicateKeyException {
if (maxGoal <= 0) {
throw new IllegalArgumentException("Max Goal must be greater than zero");
@@ -43,7 +43,7 @@ public class CupboardService {
}
}
- Need need = new Need(name, goalType, maxGoal);
+ Need need = new Need(name, location, maxGoal, goalType, urgent);
return cupboardDAO.addNeed(need);
}
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 d775d14..75dbf84 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
@@ -39,16 +39,20 @@ public class CupboardControllerTest {
@Test
public void createNeed() throws IOException, DuplicateKeyException {
String name = "Test";
+ String location = "Atlantis";
int maxGoal = 100;
GoalType type = Need.GoalType.MONETARY;
- var need = new Need(name, type, maxGoal);
- when(mockCupboardService.createNeed(name, maxGoal, type)).thenReturn(need);
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
+ when(mockCupboardService.createNeed(name, "Atlantis", maxGoal, type, false)).thenReturn(need);
Map<String, Object> needMap = Map.ofEntries(
entry("name", "Test"),
+ entry("location", "Atlantis"),
entry("maxGoal", 100.0),
- entry("type", "MONETARY")
+ entry("type", "MONETARY"),
+ entry("urgent", false)
);
var res = cupboardController.createNeed(needMap, key);
@@ -59,12 +63,14 @@ public class CupboardControllerTest {
@Test
public void createNeedBadMaxGoal() throws IOException, DuplicateKeyException {
- when(mockCupboardService.createNeed("Name", -100, Need.GoalType.MONETARY)).thenThrow(new IllegalArgumentException());
+ when(mockCupboardService.createNeed("Test", "Atlantis", -100, Need.GoalType.MONETARY, false)).thenThrow(new IllegalArgumentException());
Map<String, Object> needMap = Map.ofEntries(
- entry("name", "Name"),
- entry("maxGoal", -100.0),
- entry("type", "MONETARY")
+ entry("name", "Test"),
+ entry("location", "Atlantis"),
+ entry("maxGoal", -100),
+ entry("type", "MONETARY"),
+ entry("urgent", false)
);
var res = cupboardController.createNeed(needMap, key);
@@ -74,12 +80,14 @@ public class CupboardControllerTest {
@Test
public void createNeedIOException() throws IOException, DuplicateKeyException {
- when(mockCupboardService.createNeed("Name", 100, Need.GoalType.MONETARY)).thenThrow(new IOException());
+ when(mockCupboardService.createNeed("Test", "Atlantis", 100, Need.GoalType.MONETARY, false)).thenThrow(new IOException());
Map<String, Object> needMap = Map.ofEntries(
- entry("name", "Name"),
- entry("maxGoal", 100.0),
- entry("type", "MONETARY")
+ entry("name", "Test"),
+ entry("location", "Atlantis"),
+ entry("maxGoal", 100),
+ entry("type", "MONETARY"),
+ entry("urgent", false)
);
var res = cupboardController.createNeed(needMap, key);
@@ -89,12 +97,14 @@ public class CupboardControllerTest {
@Test
public void createNeedConflict() throws IOException, DuplicateKeyException {
- when(mockCupboardService.createNeed("Name", 100, Need.GoalType.MONETARY)).thenThrow(new DuplicateKeyException(""));
+ when(mockCupboardService.createNeed("Test", "Atlantis", 100, Need.GoalType.MONETARY, false)).thenThrow(new DuplicateKeyException(""));
Map<String, Object> needMap = Map.ofEntries(
- entry("name", "Name"),
- entry("maxGoal", 100.0),
- entry("type", "MONETARY")
+ entry("name", "Test"),
+ entry("location", "Atlantis"),
+ entry("maxGoal", 100),
+ entry("type", "MONETARY"),
+ entry("urgent", false)
);
var res = cupboardController.createNeed(needMap, key);
@@ -107,9 +117,11 @@ public class CupboardControllerTest {
doThrow(new IllegalAccessException()).when(mockAuthService).keyHasAccessToCupboard(key);
Map<String, Object> needMap = Map.ofEntries(
- entry("name", "Name"),
- entry("maxGoal", 100.0),
- entry("type", "MONETARY")
+ entry("name", "Test"),
+ entry("location", "Atlantis"),
+ entry("maxGoal", 100),
+ entry("type", "MONETARY"),
+ entry("urgent", false)
);
var res = cupboardController.createNeed(needMap, key);
@@ -119,7 +131,12 @@ public class CupboardControllerTest {
@Test
public void getNeeds() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.getNeeds()).thenReturn(new Need[]{need});
var res = cupboardController.getNeeds();
@@ -149,7 +166,12 @@ public class CupboardControllerTest {
@Test
public void searchNeeds() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.searchNeeds("Na")).thenReturn(new Need[]{need});
var res = cupboardController.searchNeeds("Na");
@@ -179,7 +201,12 @@ public class CupboardControllerTest {
@Test
public void getNeed() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.getNeed(need.getId())).thenReturn(need);
var res = cupboardController.getNeed(need.getId());
@@ -190,7 +217,12 @@ public class CupboardControllerTest {
@Test
public void getNeedIOException() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.getNeed(need.getId())).thenThrow(new IOException());
var res = cupboardController.getNeed(need.getId());
@@ -200,7 +232,12 @@ public class CupboardControllerTest {
@Test
public void getNeedFail() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.getNeed(need.getId())).thenReturn(null);
var res = cupboardController.getNeed(need.getId());
@@ -211,7 +248,12 @@ public class CupboardControllerTest {
@Test
public void updateNeeds() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.updateNeed(need, 1)).thenReturn(need);
var res = cupboardController.updateNeed(need, 1, key);
@@ -222,7 +264,12 @@ public class CupboardControllerTest {
@Test
public void updateNeedsIOException() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.updateNeed(need, 1)).thenThrow(new IOException());
var res = cupboardController.updateNeed(need, 1, key);
@@ -232,7 +279,12 @@ public class CupboardControllerTest {
@Test
public void updateNeedMissing() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.updateNeed(need, 1)).thenReturn(null);
var res = cupboardController.updateNeed(need, 1, key);
@@ -242,7 +294,12 @@ public class CupboardControllerTest {
@Test
public void updateNeedBadRequest() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.updateNeed(need, 1)).thenThrow(new IllegalArgumentException());
var res = cupboardController.updateNeed(need, 1, key);
@@ -252,7 +309,12 @@ public class CupboardControllerTest {
@Test
public void updateNeedUnauthorized() throws IOException, IllegalAccessException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
doThrow(new IllegalAccessException()).when(mockAuthService).keyHasAccessToCupboard(key);
var res = cupboardController.updateNeed(need, 1, key);
@@ -262,7 +324,12 @@ public class CupboardControllerTest {
@Test
public void deleteNeed() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.getNeed(1)).thenReturn(need);
when(mockCupboardService.deleteNeed(1)).thenReturn(true);
@@ -283,7 +350,12 @@ public class CupboardControllerTest {
@Test
public void deleteNeedUnauthorized() throws IOException, IllegalAccessException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.getNeed(1)).thenReturn(need);
doThrow(new IllegalAccessException()).when(mockAuthService).keyHasAccessToCupboard(key);
@@ -294,7 +366,12 @@ public class CupboardControllerTest {
@Test
public void deleteNeedIOException() throws IOException {
- var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);
+ String name = "Test";
+ String location = "Atlantis";
+ int maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
when(mockCupboardService.getNeed(1)).thenReturn(need);
when(mockCupboardService.deleteNeed(1)).thenThrow(new IOException());
diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java
index 6b4ddfc..c7d17c7 100644
--- a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java
+++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java
@@ -14,23 +14,26 @@ public class NeedTest {
public void createNeed() {
String name = "Jellyfish";
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
assertNotNull(need);
}
@Test
public void testFields() {
String name = "Jellyfish";
- int id = 0;
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
assertEquals(name, need.getName());
- assertEquals(id, need.getId());
+ assertEquals(0, need.getId());
assertEquals(maxGoal, need.getMaxGoal());
assertEquals(type, need.getType());
}
@@ -38,9 +41,11 @@ public class NeedTest {
@Test
public void testCurrentGoal() {
String name = "Jellyfish";
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
double current = 0.00;
need.setCurrent(current);
@@ -60,9 +65,11 @@ public class NeedTest {
public void testFilterAttributes() {
String name = "Jellyfish";
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
String[] filterAttributes = {"seaweed", "divers", "pacific", "plankton"};
@@ -75,9 +82,11 @@ public class NeedTest {
public void testSetMaxGoal() {
String name = "Jellyfish";
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
double newGoal = 200.00;
need.setMaxGoal(newGoal);
@@ -90,9 +99,11 @@ public class NeedTest {
public void testSetName() {
String name = "Jellyfish";
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
String newName = "TESTINGFUN";
need.setName(newName);
diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java
index 517a7e2..01b558c 100644
--- a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java
+++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java
@@ -52,7 +52,7 @@ public class UserTest {
String expectedName = "Bob";
User user = User.create(expectedName, "pass");
- Need need = new Need("Test", 0, 100, Need.GoalType.MONETARY);
+ Need need = new Need("Test", "Atlantis", 0, 100, Need.GoalType.MONETARY, false);
Need[] needs = { need };
when(cupboardService.getNeed(0)).thenReturn(need);
@@ -71,9 +71,8 @@ public class UserTest {
String expectedName = "Bob";
User user = User.create(expectedName, "pass");
- Need need = new Need("Test", 0, 100, Need.GoalType.MONETARY);
- Need need2 = new Need("Test2", 0, 100, Need.GoalType.MONETARY);
-
+ Need need = new Need("Test", "Atlantis", 0, 100, Need.GoalType.MONETARY, false);
+ Need need2 = new Need("Test2", "Atlantis", 0, 100, Need.GoalType.MONETARY, false);
when(cupboardService.getNeed(0)).thenReturn(need2);
user.addToBasket(need);
diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java
index d83e825..acb759a 100644
--- a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java
+++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java
@@ -28,15 +28,13 @@ public class CupboardFileDAOTest {
public void setupCupboardFileDao() throws IOException {
ObjectMapper mockObjectMapper = mock(ObjectMapper.class);
testNeeds = new Need[] {
- new Need("one", 0, 100, Need.GoalType.MONETARY),
- new Need("two", 1, 100, Need.GoalType.MONETARY),
- new Need("three", 2, 100, Need.GoalType.MONETARY)
+ new Need("one", "Atlantis", 0, 100, Need.GoalType.MONETARY, false),
+ new Need("two", "Atlantis", 1, 100, Need.GoalType.MONETARY, false),
+ new Need("three", "Atlantis", 2, 100, Need.GoalType.MONETARY, false)
};
// When the object mapper is supposed to read from the file
// the mock object mapper will return the hero array above
- when(mockObjectMapper
- .readValue(new File("doesnt_matter.txt"), Need[].class))
- .thenReturn(testNeeds);
+ when(mockObjectMapper.readValue(new File("doesnt_matter.txt"), Need[].class)).thenReturn(testNeeds);
cupboardFileDao = new CupboardFileDAO("doesnt_matter.txt", mockObjectMapper);
}
@@ -56,7 +54,7 @@ public class CupboardFileDAOTest {
@Test
public void createNeedTest() throws IOException {
- Need newNeed = new Need("sea urchin hats", 3, 100, GoalType.PHYSICAL);
+ Need newNeed = new Need("sea urchin hats", "Atlantis", 100, GoalType.PHYSICAL, false);
Need actualNeed = cupboardFileDao.addNeed(newNeed);
@@ -92,7 +90,7 @@ public class CupboardFileDAOTest {
Need unupdatedNeed = needs[needs.length - 1];
assertNotNull(unupdatedNeed);
- Need updatedNeed = new Need("sequin sea urchin hats", 2, 100, GoalType.PHYSICAL);
+ Need updatedNeed = new Need("sequin sea urchin hats", "Atlantis", 100, GoalType.PHYSICAL, false);
Need actualNeed = cupboardFileDao.updateNeed(updatedNeed);
assertEquals(actualNeed, updatedNeed);
@@ -105,7 +103,7 @@ public class CupboardFileDAOTest {
Need unupdatedNeed = needs[needs.length - 1];
assertNotNull(unupdatedNeed);
- Need updatedNeed = new Need("sequin sea urchin hats", 20, 100, GoalType.PHYSICAL);
+ Need updatedNeed = new Need("sequin sea urchin hats", "Atlantis", 5, 100, GoalType.PHYSICAL, false);
Need actualNeed = cupboardFileDao.updateNeed(updatedNeed);
assertNull(actualNeed);
diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java
index 05ea2e8..f3cbc23 100644
--- a/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java
+++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java
@@ -36,16 +36,18 @@ public class CupboardServiceTest {
public void testCreateNeed() throws IOException, DuplicateKeyException {
// Setup
String name = "Jellyfish";
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
// When the same id is passed in, our mock User DAO will return the User object
when(mockCupboardDAO.addNeed(any())).thenReturn(need);
when(mockCupboardDAO.getNeeds()).thenReturn(new Need[0]);
// Invoke
- Need response = cupboardService.createNeed(name, maxGoal, type);
+ Need response = cupboardService.createNeed(name, location, maxGoal, type, urgent);
// Analyze
assertNotNull(response);
@@ -56,9 +58,11 @@ public class CupboardServiceTest {
public void testCreateNeedBadGoal() throws IOException {
// Setup
String name = "Jellyfish";
- double maxGoal = -100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = -100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
// When the same id is passed in, our mock User DAO will return the User object
when(mockCupboardDAO.addNeed(any())).thenReturn(need);
@@ -69,16 +73,18 @@ public class CupboardServiceTest {
// Analyze
assertThrows(IllegalArgumentException.class, () ->
- cupboardService.createNeed(name, maxGoal, type));
+ cupboardService.createNeed(name, location, maxGoal, type, urgent));
}
@Test
public void testCreateNeedDuplicate() throws IOException {
// Setup
String name = "Jellyfish";
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
Need[] needs = { need };
// When the same id is passed in, our mock User DAO will return the User object
@@ -90,16 +96,18 @@ public class CupboardServiceTest {
// Analyze
assertThrows(DuplicateKeyException.class, () ->
- cupboardService.createNeed(name, maxGoal, type));
+ cupboardService.createNeed(name, location, maxGoal, type, urgent));
}
@Test
public void testSearchNeeds() throws IOException {
// Setup
String name = "Jellyfish";
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
Need[] needs = { need };
// When the same id is passed in, our mock User DAO will return the User object
@@ -117,9 +125,11 @@ public class CupboardServiceTest {
public void testSearchNeedsFail() throws IOException {
// Setup
String name = "Jellyfish";
- double maxGoal = 100.00;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
Need[] needs = { need };
// When the same id is passed in, our mock User DAO will return the User object
@@ -136,16 +146,17 @@ public class CupboardServiceTest {
public void testGetNeed() throws IOException {
// Setup
String name = "Jellyfish";
- double maxGoal = 100.00;
- int id = 0;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ var need = new Need(name, location, maxGoal, type, urgent);
// When the same id is passed in, our mock User DAO will return the User object
- when(mockCupboardDAO.getNeed(id)).thenReturn(need);
+ when(mockCupboardDAO.getNeed(0)).thenReturn(need);
// Invoke
- Need response = cupboardService.getNeed(id);
+ Need response = cupboardService.getNeed(need.getId());
// Analyze
assertEquals(need, response);
@@ -155,17 +166,18 @@ public class CupboardServiceTest {
public void testUpdateNeed() throws IOException {
// Setup
String name = "Jellyfish";
- double maxGoal = 100.00;
- int id = 0;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
- Need newNeed = new Need("Octopus", type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ Need need = new Need(name, location, maxGoal, type, urgent);
+ Need newNeed = new Need("Octopus", location, maxGoal, type, urgent);
// When the same id is passed in, our mock User DAO will return the User object
when(mockCupboardDAO.updateNeed(any())).thenReturn(newNeed);
// Invoke
- Need response = cupboardService.updateNeed(newNeed, id);
+ Need response = cupboardService.updateNeed(newNeed, need.getId());
// Analyze
assertEquals(newNeed, response);
@@ -175,17 +187,18 @@ public class CupboardServiceTest {
public void testDeleteNeed() throws IOException {
// Setup
String name = "Jellyfish";
- double maxGoal = 100.00;
- int id = 0;
- GoalType type = GoalType.MONETARY;
- Need need = new Need(name, type, maxGoal);
+ String location = "Atlantis";
+ double maxGoal = 100.0;
+ GoalType type = Need.GoalType.MONETARY;
+ boolean urgent = false;
+ Need need = new Need(name, location, maxGoal, type, urgent);
// When the same id is passed in, our mock User DAO will return the User object
- when(mockCupboardDAO.deleteNeed(id)).thenReturn(true);
+ when(mockCupboardDAO.deleteNeed(0)).thenReturn(true);
when(mockCupboardDAO.getNeeds()).thenReturn(new Need[0]);
// Invoke
- boolean response = cupboardService.deleteNeed(id);
+ boolean response = cupboardService.deleteNeed(need.getId());
Need[] responseNeeds = cupboardService.getNeeds();
// Analyze
diff --git a/ufund-ui/public/delete.png b/ufund-ui/public/delete.png
new file mode 100644
index 0000000..6403705
--- /dev/null
+++ b/ufund-ui/public/delete.png
Binary files differ
diff --git a/ufund-ui/public/edit.png b/ufund-ui/public/edit.png
new file mode 100644
index 0000000..3b6e2d8
--- /dev/null
+++ b/ufund-ui/public/edit.png
Binary files differ
diff --git a/ufund-ui/public/search.png b/ufund-ui/public/search.png
new file mode 100644
index 0000000..1940ef5
--- /dev/null
+++ b/ufund-ui/public/search.png
Binary files differ
diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.css b/ufund-ui/src/app/components/cupboard/cupboard.component.css
index c8add60..f4b6828 100644
--- a/ufund-ui/src/app/components/cupboard/cupboard.component.css
+++ b/ufund-ui/src/app/components/cupboard/cupboard.component.css
@@ -1,21 +1,43 @@
:host {
- display: block;
- /*border: 2px solid #000;*/
+ display: flex;
+ justify-content: space-evenly;
+ border: 2px solid #000;
border-radius: 5px;
padding: 10px 20px;
+ > div {
+ width: 40%;
+ }
}
-#menu, #create-form, #delete-form, #update-form {
+
+#menu {
+ display: flex;
+
+ margin: 10px;
+
+}
+
+.tab, .selected-tab {
+ background-color: lightgray;
+ border: 3px solid #000;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ margin-right: 5px;
+ border-bottom: 0px;
+}
+
+.selected-tab {
+ background-color: white;
+}
+
+#create-form, #delete-form, #update-form {
background-color: #d9d9d9;
padding: 10px 20px 20px 20px;
border: 2px solid #000;
border-radius: 5px;
- width: 20%;
visibility: visible;
-
}
#create-button {
padding: 10px 20px;
-
}
diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.html b/ufund-ui/src/app/components/cupboard/cupboard.component.html
index 0d64475..bc5ac1c 100644
--- a/ufund-ui/src/app/components/cupboard/cupboard.component.html
+++ b/ufund-ui/src/app/components/cupboard/cupboard.component.html
@@ -1,50 +1,56 @@
-<h1> Cupboard </h1>
-<h2 *ngIf="isManager()" > Admin View </h2>
-<div id="menu" *ngIf="isManager()">
- <button (click)="opencreate()">Create new Need</button>
- <button (click)="openupdate()">Update existing Need</button>
+<div>
+ <h1> Cupboard </h1>
+ <app-need-list (currentNeed) = populateForm($event) #needList></app-need-list>
</div>
-<div id="create-form">
- <h1> Create a new need </h1>
- <form #cupboardForm="ngForm" (ngSubmit)="submit(cupboardForm.value)">
- <label>Name:</label><br>
- <input type="text" name="name" 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="submit" value="Submit">
- </form>
- <button (click)="back()">Close</button>
- <span *ngIf="statusText">‼️{{statusText | async}}</span>
+<div *ngIf="isManager()" >
+ <h2 > Admin View </h2>
+ <div id="menu">
+ <button [ngClass]="selectedForm === 'create' ? 'selected-tab' : 'tab'" (click)="selectForm('create')">Create new Need</button>
+ <button [ngClass]="selectedForm === 'update' ? 'selected-tab' : 'tab'" (click)="selectForm('update')">Update existing Need</button>
+ </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>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>
+ <input type="submit" value="Submit">
+
+ </form>
+ <span *ngIf="statusText">{{statusText | async}}</span>
-</div>
-<div id="update-form">
- <h1> Update a need </h1>
- <label>Needs:</label><br>
- <form #updateForm="ngForm" (ngSubmit)="update(updateForm.value)">
- <div *ngFor="let need of needs">
+ </div>
+ <div id="update-form" *ngIf="selectedForm === 'update'">
+ <h1> Update Need </h1>
+ <label>Needs:</label><br>
+ <form #updateForm="ngForm" (ngSubmit)="update(updateForm.value)">
+ <input type="text" name="name" [(ngModel)]="selectedNeed.name"><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>
+ <input type="submit" value="Submit">
+
+ </form>
+ <span *ngIf="statusText">{{statusText | async}}</span>
- <input type="radio" name="id" [value]=need.id [(ngModel)]="selectedNeedId" (change)="populateForm(need)">
- <label name="template">{{need.name}}</label><br>
- </div>
- <label>Name:</label><br>
- <input type="text" name="name" [(ngModel)]="selectedNeed.name"><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="submit" value="Submit">
- </form>
- <button (click)="back()">Close</button>
- <span *ngIf="statusText">{{statusText | async}}</span>
+ </div>
+ <hr>
</div>
-<hr>
-<app-need-list #needList></app-need-list>
diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts
index a812baf..85ffd17 100644
--- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts
+++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, ViewChild } from '@angular/core';
+import {Component, Input, OnInit, ViewChild} from '@angular/core';
import { CupboardService } from '../../services/cupboard.service';
import { Need, GoalType } from '../../models/Need';
import { userType } from '../../models/User';
@@ -16,7 +16,7 @@ import {AuthService} from '../../services/auth.service';
export class CupboardComponent implements OnInit {
protected statusText = new BehaviorSubject("")
-
+ selectedForm = "create";
needs: any;
@ViewChild("needList") needList?: NeedListComponent
@@ -27,9 +27,6 @@ export class CupboardComponent implements OnInit {
ngOnInit(): void {
this.cupboardService.getNeeds().subscribe(n => this.needs = n);
- this.close();
- this.openmenu();
-
if (this.isManager()) {
console.log("Admin view of Cupboard");
} else {
@@ -39,54 +36,43 @@ export class CupboardComponent implements OnInit {
selectedNeed: any = {
name: '',
+ location:'',
id: null,
maxGoal: null,
- type: ''
+ type: '',
+ urgent: false
};
selectedNeedId: number | null = null;
+ searchResults: any[] = [];
- private hideElement(element: any) {
- if (element) {
- element.style.visibility = 'hidden';
- element.style.position = 'absolute';
+ 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)
+ });
+ }
- private showElement(element: any) {
- if (element) {
- element.style.visibility = 'visible';
- element.style.position = 'relative';
}
}
- openmenu() {
- const menuElement = document.getElementById('menu');
- this.showElement(menuElement);
- }
-
- opencreate() {
- this.close();
- this.showElement(document.getElementById('create-form'));
- }
-
- openupdate() {
- this.close();
- this.showElement(document.getElementById('update-form'));
- }
-
- back() {
- this.close();
- this.openmenu();
- }
-
- close() {
- this.hideElement(document.getElementById('create-form'));
- this.hideElement(document.getElementById('destroy-form'));
- this.hideElement(document.getElementById('menu'));
- this.hideElement(document.getElementById('update-form'));
+ async updateSearchResults() {
+ if (this.needList) {
+ while (this.selectedForm == 'update') {
+ this.searchResults = this.needList.searchResults
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+ }
}
populateForm(need: any): void {
+ this.selectForm('update');
this.selectedNeed = { ...need };
}
@@ -99,14 +85,15 @@ export class CupboardComponent implements OnInit {
console.log(form);
const need: Need = {
name: form.name,
- id: form.id, //system will control this
+ 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
};
- console.log("need:", need);
- console.log(need.id, need, "need updated");
+
this.cupboardService.updateNeed(need.id, need)
.pipe(catchError((ex, _) => {
if (ex.status == 500) {
@@ -134,9 +121,11 @@ export class CupboardComponent implements OnInit {
submit(form: any) {
const need: Need = {
name: form.name,
+ location: form.location,
id: 0,
maxGoal: form.maxGoal,
type: form.type,
+ urgent: form.urgent ? true : false,
filterAttributes: [],
current: 0
};
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 fa3ed4f..5e07856 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,24 +1,88 @@
:host {
- list-style-type:circle;
+ --list-background-color: lightgray;
+ list-style-type: none;
border: 2px solid #000;
display: block;
- width: 30%;
border-radius: 5px;
}
-li, div {
+div {
border: 2px solid #000;
border-radius: 5px;
padding: 5px;
margin: 5px;
+}
+
+ul {
+ list-style-type: none;
+ padding-inline-start: 0px;
+}
+
+li {
+ background-color: var(--list-background-color);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ transition: all 0.3s ease;
+ font-weight: bold;
+ border: 2px solid #000;
+ border-radius: 5px;
+ margin: 5px;
+ > button {
+ background-color: transparent;
+ width: 88%;
+ transition: all 0.3s ease;
+ font-weight: bold;
+ border: none;
+ border-radius: 5px;
+ padding-left: 1.5%;
+ > section {
+ width: 100%;
+ flex: none;
+ display: inline-block;
+ background-color: magenta;
+ > progress {
+ width: 25%;
+ float: none;
+ }
+ }
+ }
+
+ > section {
+ width: 12%;
+ }
+}
+section button{
+ margin: 4%;
}
+li > button span {
+ font-style: italic;
+ font-weight: normal;
+}
+
+li > button:hover p {
+ text-decoration: underline;
+}
+
+
+.icon {
+ width: 18px;
+ margin: 3px -3px -1px -3px;
+}
+
+#search-container {
+ background-color: #d9d9d9;
#search-form {
background-color: light-dark(#d9d9d9, #1b1b1b);
padding: 10px 20px 20px 20px;
border: 2px solid #000;
border-radius: 5px;
+ .wide-input {
+ width: 60%;
+ }
+ border-radius: 5px;
visibility: visible;
}
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 36c12d0..866e5e4 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,28 +1,51 @@
<h1>Needs List</h1>
-<input id="search-button" type="button" value="Search" (click)="open()">
-<div id="search-form">
- <form #searchForm="ngForm">
- <label>Search:</label><br>
- <input type="text" name="search" (input)="search(searchForm.value)" ngModel>
- <input type="button" value="Clear" (click)="searchForm.reset()"> <br>
- </form>
- <button (click)="close()">Close</button>
- <div>
- <h2 id="search-status">Search Results:</h2>
- <div *ngFor="let need of searchResults">
- <a routerLink="/need/{{need.id}}">
- {{need.name}}
- </a>
- <button (click)="delete(need.id)" *ngIf="isManager()">Delete</button>
- <!-- <button (click)="add(need)" *ngIf="isHelper()">Add To Basket</button> -->
- </div>
- </div>
+<div id="search-container">
+ <section>
+ <label for="sort">Sort by: </label>
+ <select [(ngModel)] = "sortSelection" class="wide-input" (change)="search(searchForm.value)" [value]="sortSelection">
+ <option *ngFor="let algorithm of SortingAlgoArrays" value="{{algorithm.name}}">
+ {{algorithm.display[sortMode === 'Ascending' ? 0 : 1]}}
+ </option>
+ </select>
+ <button (click)="changeSortMode(searchForm.value)">
+ {{sortMode}}
+ </button>
+ </section>
+ <section>
+ <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>
+ </section>
+ <!--<button (click)="close()">Close</button>-->
</div>
-<li *ngFor="let need of needs">
- <a routerLink="/need/{{need.id}}">
- {{need.name}}
- </a>
- <button (click)="delete(need.id)" *ngIf="isManager()">Delete</button>
- <button (click)="add(need)" *ngIf="isHelper()">Add To Basket</button>
-</li>
+<!-- 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>
+
+<ul>
+ <li *ngFor="let need of searchResults" id="need-button-{{need.id}}">
+ <button [routerLink]="'/need/' + need.id" (mouseenter) ="changeText(need.id, '(details)')" (mouseleave)="changeText(need.id, '')">
+ <section> <p> {{need.name}} | {{need.location}} <span> {{need.urgent ? "URGENT" : ""}} </span> <span id="hover-status-label-{{need.id}}"> </span> </section>
+ <section>
+ <progress value="need.current" max="need.maxGoal"></progress>
+ <progress value="need.current" max="need.maxGoal"></progress>
+ <progress value="need.current" max="need.maxGoal"></progress>
+ <progress value="need.current" max="need.maxGoal"></progress>
+ </section>
+ <section>{{need.current}}/{{need.maxGoal}} {{(need.current / need.maxGoal) * 100}}% <span>{{need.type}}</span></section>
+ </button>
+
+ <button (click)="add(need)" *ngIf="isHelper()">Add To Basket</button>
+ <section *ngIf="isManager()">
+ <button (click)="select(need)" id="need-edit-button-{{need.id}}">
+ <img class="icon" src="/edit.png" alt="Select">
+ </button>
+ <button (click)="delete(need.id)" *ngIf="isManager()">
+ <img class="icon" src="/delete.png" alt="Delete">
+ </button>
+ </section>
+ </li>
+</ul> \ No newline at end of file
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 3a89a20..af8cab4 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,138 +1,246 @@
-import {Component} from '@angular/core';
+import {Component , Output, EventEmitter} from '@angular/core';
+import { NgForm } from '@angular/forms';
import {Need} from '../../models/Need';
import {CupboardService} from '../../services/cupboard.service';
-import {UsersService} from '../../services/users.service';
-import {userType} from '../../models/User';
-import {catchError, of} from 'rxjs';
+import { UsersService } from '../../services/users.service';
+import { userType } from '../../models/User';
import {AuthService} from '../../services/auth.service';
+import {catchError, of} from 'rxjs';
-@Component({
- selector: 'app-need-list',
- standalone: false,
- templateUrl: './need-list.component.html',
- styleUrl: './need-list.component.css'
-})
-export class NeedListComponent {
- needs: Need[] = [];
- searchResults: Need[] = [];
-
- constructor(
- private cupboardService: CupboardService,
- private usersService: UsersService,
- private authService: AuthService
- ) {}
+interface sortAlgo {
+ (a: Need,b: Need): number;
+}
- refresh() {
- this.cupboardService.getNeeds().subscribe(n => this.needs = n)
- }
+// sort functions
+const sortByName: sortAlgo = (a: Need, b: Need): number => {
+ if(a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) {
+ return -1;
+ }
+ return 1;
+}
- ngOnInit(): void {
- this.refresh()
+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;
+}
- this.close();
- }
+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;
+}
- private showElement(element: any) {
- if (element) {
- element.style.visibility = 'visible';
- element.style.position = 'relative';
- }
- }
+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;
+}
- private hideElement(element: any) {
- if (element) {
- element.style.visibility = 'hidden';
- element.style.position = 'absolute';
- }
- }
+const sortByLocation: sortAlgo = (a: Need, b: Need): number => {
+ if(a.location.toLocaleLowerCase() < b.location.toLocaleLowerCase()) {
+ return -1;
+ }
+ return 1;
+}
- private updateSearchStatus(text: string) {
- let element = document.getElementById('search-status');
- if (element) {
- element.innerHTML = text;
- }
- }
+@Component({
+ 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[] = [];
+ sortMode = 'Ascending'
+
+ 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
+ ) {}
- open() {
- this.hideElement(document.getElementById('search-button'));
- this.showElement(document.getElementById('search-form'));
+ 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;
+ });
+
+ const form = document.getElementById('search-form') as HTMLFormElement;
+ form.reset();
+ this.search(null);
}
- close() {
- this.hideElement(document.getElementById('search-form'));
- this.showElement(document.getElementById('search-button'));
- this.hideElement(document.getElementById('search-status'));
- }
+ ngOnInit(): void {
+ this.refresh()
+ }
- 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);
- }
-
- this.searchDelay = setTimeout(() => {
- const currentSearchValue = form.search; //latest value of the search
- this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => {
- this.searchResults = n;
- console.log(currentSearchValue, this.searchResults);
- this.showElement(document.getElementById('search-results'));
- this.showElement(document.getElementById('search-status'));
- if (this.searchResults.length === this.needs.length) {
- this.updateSearchStatus("Please refine your search");
- this.searchResults = [];
- } else if (this.searchResults.length === 0) {
- this.updateSearchStatus("No results found");
- } else {
- this.updateSearchStatus("Search results:");
- }
- });
- }, 250);
+ changeSortMode(form : any) {
+ if (this.sortMode == 'Ascending'){
+ this.sortMode = 'Descending'
+ } else {
+ this.sortMode = 'Ascending'
}
+ this.search(form)
+ }
- delete(id: number) {
- this.cupboardService.deleteNeed(id).subscribe(() => {
- this.needs = this.needs.filter(n => n.id !== id)
- })
- }
+ private searchDelay: any;
- isManager() {
- const type = this.authService.getCurrentUser()?.type;
- return type === ("MANAGER" as unknown as userType);
- }
+ 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
- isHelper() {
- const type = this.authService.getCurrentUser()?.type;
- return type === ("HELPER" as unknown as userType);
+ //remove previous search if it exists
+ if (this.searchDelay) {
+ clearTimeout(this.searchDelay);
}
+ 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
+ }
+ });
- 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: any, _) => {
- console.error(err);
- return of();
- }))
- .subscribe(() => {
- this.usersService.refreshBasket();
- });
+ 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 {
- window.alert("This need is already in your basket!")
+ this.searchResults = n.sort(this.currentSortAlgo).reverse();
}
+ console.log(currentSearchValue, this.searchResults);
+ });
+ }
+ }, 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.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;
+ }
+ }
+
+ 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);
+ return of();
+ }))
+ .subscribe(() => {
+ this.usersService.refreshBasket();
+ });
+ } else {
+ window.alert("This need is already in your basket!")
+ }
- }
}
- back() {
- this.searchResults = [];
+ }
+
+ 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';
+ }
}
+ //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';
+ }
+ }
}
+function not(location: string) {
+ throw new Error('Function not implemented.');
+}
+
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 b2579c9..7ce7633 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
@@ -16,7 +16,8 @@
</div>
<hr>
-
+<p>Location: {{need?.location}}</p>
<p>Goal: {{need?.maxGoal}}</p>
<p>Current: {{need?.current}}</p>
+<p>Urgent: {{need?.urgent}}</p>
<p>This goal is <strong>{{(((need?.current ?? 0)*100) / (need?.maxGoal ?? 0)).toFixed(0)}}%</strong> complete!</p>
diff --git a/ufund-ui/src/app/models/Need.ts b/ufund-ui/src/app/models/Need.ts
index 5cd4e39..1451cad 100644
--- a/ufund-ui/src/app/models/Need.ts
+++ b/ufund-ui/src/app/models/Need.ts
@@ -2,9 +2,11 @@ export interface Need {
name: string,
id: number,
filterAttributes: string[],
+ location: string;
type: GoalType;
maxGoal: number;
current: number;
+ urgent: boolean;
}
export enum GoalType {