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>
<div class="other-users-div"> <div class="other-users-div">
<h3>Andre aktive:</h3> <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> <table>
<tr> <tr>
<th>Navn</th><th>Total avstand</th><th>Første aktivitet</th><th>Siste aktivitet</th> <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, removeAdministrator: (userId: number) => void,
deleteUser: (userId: number) => void, deleteUser: (userId: number) => void,
onClickHideCheckbox: (event: Event) => void,
init: () => void, init: () => void,
setServerMessage: (newMessage: Sykkelaksjon) => void, setServerMessage: (newMessage: Sykkelaksjon) => void,
getServerMessage: () => Sykkelaksjon, getServerMessage: () => Sykkelaksjon,
@ -59,6 +61,8 @@ let alpineState: AlpineState = {
removeAdministrator: removeAdministrator, removeAdministrator: removeAdministrator,
deleteUser: deleteUser, deleteUser: deleteUser,
onClickHideCheckbox: onClickHideCheckbox,
init() { init() {
this.data = defaultData; this.data = defaultData;
}, },
@ -291,3 +295,20 @@ if (receivedData) {
loadingDiv?.setAttribute("style", "display: none"); loadingDiv?.setAttribute("style", "display: none");
contentDiv?.setAttribute("style", "display: block"); 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 * Whether the connected user is an administrator
*/ */
isAdmin: boolean; isAdmin: boolean;
/**
* Whether the connected user is hidden from the score lists
*/
isHidden?: boolean;
/** /**
* Available types of activities * Available types of activities
*/ */
@ -69,6 +73,10 @@ export interface Sykkelaksjon {
* Administrator info: whether the user listed is an administrator * Administrator info: whether the user listed is an administrator
*/ */
isAdmin?: boolean; 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 * Administrator info: the activity templates this user has defined
*/ */

View File

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

View File

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

View File

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

View File

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

View File

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