Added possibility for user to hide from global score list

This commit is contained in:
Martin Asprusten 2026-04-16 20:39:39 +02:00
parent 753b757d57
commit 3b265735ec
No known key found for this signature in database
10 changed files with 379 additions and 295 deletions

View File

@ -94,6 +94,7 @@
</div>
<div class="other-users-div">
<h3>Andre aktive:</h3>
<input type="checkbox" id="hideMeCheckbox" x-model="$store.state.data.isHidden" x-on:click="$store.state.onClickHideCheckbox" /><label id="hideMeLabel" for="hideMeCheckbox">Skjul meg fra den globale listen</label>
<table>
<tr>
<th>Navn</th><th>Total avstand</th><th>Første aktivitet</th><th>Siste aktivitet</th>

View File

@ -33,6 +33,8 @@ interface AlpineState {
removeAdministrator: (userId: number) => void,
deleteUser: (userId: number) => void,
onClickHideCheckbox: (event: Event) => void,
init: () => void,
setServerMessage: (newMessage: Sykkelaksjon) => void,
getServerMessage: () => Sykkelaksjon,
@ -59,6 +61,8 @@ let alpineState: AlpineState = {
removeAdministrator: removeAdministrator,
deleteUser: deleteUser,
onClickHideCheckbox: onClickHideCheckbox,
init() {
this.data = defaultData;
},
@ -291,3 +295,20 @@ if (receivedData) {
loadingDiv?.setAttribute("style", "display: none");
contentDiv?.setAttribute("style", "display: block");
}
function onClickHideCheckbox(event: PointerEvent) {
let possibleCheckbox = event.target;
if (possibleCheckbox instanceof HTMLInputElement) {
let formData = new FormData();
let url;
let isChecked = possibleCheckbox.checked;
if (isChecked) {
url = apiUrl + "/hideMe"
} else {
url = apiUrl + "/showMe"
}
submitFormToUrl(url, formData, "PUT");
}
}

View File

@ -17,6 +17,10 @@ export interface Sykkelaksjon {
* Whether the connected user is an administrator
*/
isAdmin: boolean;
/**
* Whether the connected user is hidden from the score lists
*/
isHidden?: boolean;
/**
* Available types of activities
*/
@ -69,6 +73,10 @@ export interface Sykkelaksjon {
* Administrator info: whether the user listed is an administrator
*/
isAdmin?: boolean;
/**
* Administrator info: whether the user is hidden from the global score table
*/
isHidden?: boolean;
/**
* Administrator info: the activity templates this user has defined
*/

View File

@ -101,3 +101,7 @@ summary {
margin: -0.5em -0.5em 0;
padding: 0.5em;
}
#hideMeLabel {
width: 80%;
}

View File

@ -13,6 +13,10 @@
"type": "boolean",
"description": "Whether the connected user is an administrator"
},
"isHidden": {
"type": "boolean",
"description": "Whether the connected user is hidden from the score lists"
},
"activityTypes": {
"type": "array",
"description": "Available types of activities",
@ -77,6 +81,10 @@
"type": "boolean",
"description": "Administrator info: whether the user listed is an administrator"
},
"isHidden": {
"type": "boolean",
"description": "Administrator info: whether the user is hidden from the global score table"
},
"activityTemplates": {
"type": "array",
"description": "Administrator info: the activity templates this user has defined",

View File

@ -74,7 +74,7 @@ public class Server {
return activityMessage;
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@GetMapping("/api")
public ServerMessageSchema respondToRequest() throws ServerExceptionHandler.InvalidUserException {
var optionalUserProfile = profileManager.getProfile();
@ -97,6 +97,7 @@ public class Server {
ServerMessageSchema serverMessage = new ServerMessageSchema();
serverMessage.setName(user.getFullName());
serverMessage.setIsAdmin(user.isAdmin());
serverMessage.setIsHidden(user.isHidden());
boolean unitConversionNecessary = false;
for (var activityType : activityTypeService.list()) {
@ -133,6 +134,10 @@ public class Server {
continue;
}
if (otherUser.isHidden() && !user.isAdmin()) {
continue;
}
OtherUser otherUserMessage = new OtherUser();
otherUserMessage.setName(otherUser.getFullName());
@ -162,6 +167,7 @@ public class Server {
otherUserMessage.setUserName(otherUser.getUsername());
otherUserMessage.setIsActive(otherUser.isActive());
otherUserMessage.setIsAdmin(otherUser.isAdmin());
otherUserMessage.setIsHidden(otherUser.isHidden());
List<Activity> userUnsortedActivities = new ArrayList<>();
for (var activity : otherUser.getActivities()) {
userUnsortedActivities.add(getActivityMessage(activity));
@ -182,7 +188,7 @@ public class Server {
return serverMessage;
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@PostMapping(path = "/api/submitActivity", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void submitActivity(
@RequestParam("activity-type") Long activityTypeId,
@ -207,7 +213,7 @@ public class Server {
});
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@PostMapping(path = "/api/submitActivityTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void submitActivityTemplate(
@RequestParam("activity-type") Long activityTypeId,
@ -232,7 +238,7 @@ public class Server {
});
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@DeleteMapping(path = "/api/deleteActivityTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void deleteActivityTemplate(@RequestParam("activity-template-id") Long activityTemplateId) {
profileManager.getProfile().ifPresent(userProfile -> {
@ -247,7 +253,7 @@ public class Server {
});
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@DeleteMapping(path = "/api/deleteActivity", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void deleteActivity(@RequestParam("activity-id") Long activityId) {
profileManager.getProfile().ifPresent(userProfile -> {
@ -262,7 +268,7 @@ public class Server {
});
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@PostMapping(path = "/api/addActivityType", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void createActivityType(
@RequestParam("activity-type-name") String name,
@ -282,7 +288,7 @@ public class Server {
});
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@DeleteMapping(path = "/api/deleteActivityType", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void deleteActivityType(@RequestParam("activity-type-id") Long activityTypeId) {
profileManager.getProfile().ifPresent(userProfile -> {
@ -295,7 +301,7 @@ public class Server {
});
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@PutMapping(path = "/api/makeAdmin", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void makeAdmin(@RequestParam("user-id") Long userId) {
profileManager.getProfile().ifPresent(userProfile -> {
@ -311,7 +317,7 @@ public class Server {
});
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@PutMapping(path = "/api/removeAdmin", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void removeAdmin(@RequestParam("user-id") Long userId) {
profileManager.getProfile().ifPresent(userProfile -> {
@ -327,7 +333,7 @@ public class Server {
});
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@DeleteMapping(path = "/api/deleteUser", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void deleteUser(@RequestParam("user-id") Long userId) {
profileManager.getProfile().ifPresent(userProfile -> {
@ -340,7 +346,7 @@ public class Server {
});
}
@CrossOrigin
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@GetMapping(path = "/api/openid")
public OpenidSchema provideOpenidConfig() {
OpenidSchema openidSchema = new OpenidSchema();
@ -349,6 +355,31 @@ public class Server {
return openidSchema;
}
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@PutMapping(path = "/api/hideMe")
public void hideUser() {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
dbUser.setHidden(true);
userService.saveUser(dbUser);
});
});
}
@CrossOrigin(origins = {"http://localhost:5173"}, allowCredentials = "true")
@PutMapping(path = "/api/showMe")
public void showUser() {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
dbUser.setHidden(false);
userService.saveUser(dbUser);
});
});
}
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Server.class);
application.addListeners(new PropertiesListener());

View File

@ -20,6 +20,8 @@ public class WebUser {
private boolean isAdmin;
@Column(nullable = false)
private Long zeroIfActive;
@Column(nullable = false)
private boolean isHidden = false;
@OneToMany(mappedBy = "activityOwner", fetch = FetchType.EAGER)
private List<Activity> activities = new ArrayList<>();
@ -70,6 +72,14 @@ public class WebUser {
isAdmin = admin;
}
public boolean isHidden() {
return isHidden;
}
public void setHidden(boolean hidden) {
isHidden = hidden;
}
public void setActive(boolean active) {
if (active) {
zeroIfActive = 0L;

View File

@ -7,7 +7,8 @@ import org.springframework.web.bind.annotation.ResponseStatus;
@ControllerAdvice
public class ServerExceptionHandler {
public static class InvalidUserException extends Exception {}
public static class InvalidUserException extends Exception {
}
@ResponseStatus(HttpStatus.FORBIDDEN)
@ExceptionHandler(InvalidUserException.class)