Compare commits

...

2 Commits

Author SHA1 Message Date
af43204063 User.email WIP 2024-11-06 16:38:55 +01:00
8a6b444003 styles file structure cleanup 2024-11-06 16:19:57 +01:00
25 changed files with 186 additions and 128 deletions

View File

@ -1,4 +1,4 @@
@import "../common.less"; @import "../styles/config";
#mainMenu { #mainMenu {
border-bottom: 1px solid black; border-bottom: 1px solid black;
@ -19,12 +19,4 @@
background-color: lightskyblue; background-color: lightskyblue;
} }
.userHasPassword {
color: green;
}
.userNoPassword {
color: indianred;
}
} }

View File

@ -49,7 +49,7 @@
</div> </div>
<div class="tileContent"> <div class="tileContent">
<table> <table>
<tr [class.user_owner]="group.isOwner(user)" *ngFor="let user of group.usersByNameOwnerFirst()"> <tr *ngFor="let user of group.usersByNameOwnerFirst()">
<td (click)="userService.goto(user)"> <td (click)="userService.goto(user)">
{{ user.name }} {{ user.name }}
</td> </td>
@ -77,7 +77,7 @@
</div> </div>
<div class="tileContent"> <div class="tileContent">
<table> <table>
<tr [class.user_owner]="group.isOwner(user)" *ngFor="let user of group.bannedByName()"> <tr *ngFor="let user of group.bannedByName()">
<td (click)="userService.goto(user)"> <td (click)="userService.goto(user)">
{{ user.name }} {{ user.name }}
</td> </td>

View File

@ -1,4 +1,4 @@
@import "../../../../common.less"; @import "../../../../styles/config";
th { th {
text-align: left; text-align: left;

View File

@ -1 +1 @@
@import "../../../../common.less"; @import "../../../../styles/config";

View File

@ -1,4 +1,4 @@
@import "../../../../../common.less"; @import "../../../../../styles/config";
td { td {
border: 1px solid black; border: 1px solid black;

View File

@ -1,3 +1,5 @@
@import "../../../styles/config";
table { table {
width: 100%; width: 100%;
} }

View File

@ -1,3 +1,5 @@
@import "../../../../styles/config";
.huge { .huge {
font-size: 50vmin; font-size: 50vmin;
white-space: nowrap; white-space: nowrap;

View File

@ -1,4 +1,4 @@
@import "../../../../../common.less"; @import "../../../../../styles/config";
@page { @page {
size: A4; size: A4;

View File

@ -1,4 +1,4 @@
@import "../../../../common.less"; @import "../../../../styles/config";
.name { .name {
text-align: left; text-align: left;

View File

@ -1,4 +1,4 @@
@import "../../../../common.less"; @import "../../../../styles/config";
#VoltageDropInputs { #VoltageDropInputs {

View File

@ -1 +1 @@
@import "../../../common.less"; @import "../../../styles/config";

View File

@ -1 +1 @@
@import "../../../common"; @import "../../../styles/config";

View File

@ -1,4 +1,4 @@
@import "../../../common.less"; @import "../../../styles/config";
.unsaved { .unsaved {
background-color: yellow; background-color: yellow;

View File

@ -1,89 +1,4 @@
@import "./common.less"; @import "./styles/basic";
@import "./styles/tiles";
body { @import "./styles/button";
font-family: sans-serif; @import "./styles/user";
font-size: 4vw;
margin: 0;
}
div {
box-sizing: border-box;
overflow: hidden;
}
a {
text-decoration: none;
}
input {
all: unset;
background-color: white;
border: 1px solid lightgray;
}
select {
font-size: inherit;
}
table {
border-collapse: collapse;
}
.tileContainer {
padding: @halfSpace;
.tile {
width: 100%;
padding: @halfSpace;
.tileInner {
border: 1px solid #ddd;
border-radius: @space;
background-color: #fbfbfb;
.tileTitle {
font-weight: bold;
padding: @halfSpace @space;
background-color: lightskyblue;
}
.tileContent {
padding: @halfSpace;
table {
width: 100%;
}
td {
white-space: nowrap;
border: 0.2em solid white;
}
}
.tileFooter {
padding: @halfSpace @space;
}
}
}
}
@media (min-width: 1000px) {
body {
font-size: 18px;
}
.tileContainer {
.tile {
float: left;
width: 500px;
}
}
}

View File

@ -0,0 +1,38 @@
@import "./config";
body {
font-family: sans-serif;
font-size: 4vw;
margin: 0;
}
div {
box-sizing: border-box;
overflow: hidden;
}
a {
text-decoration: none;
}
input {
all: unset;
background-color: white;
border: 1px solid lightgray;
}
select {
font-size: inherit;
}
table {
border-collapse: collapse;
}
@media (min-width: 1000px) {
body {
font-size: 18px;
}
}

View File

@ -1,4 +1,4 @@
@import "./user.less"; @import "./config";
.buttons { .buttons {
margin-bottom: @halfSpace; margin-bottom: @halfSpace;

View File

@ -0,0 +1,60 @@
@import "./config";
.tileContainer {
padding: @halfSpace;
.tile {
width: 100%;
padding: @halfSpace;
.tileInner {
border: 1px solid #ddd;
border-radius: @space;
background-color: #fbfbfb;
.tileTitle {
font-weight: bold;
padding: @halfSpace @space;
background-color: lightskyblue;
}
.tileContent {
padding: @halfSpace;
table {
width: 100%;
}
td {
white-space: nowrap;
border: 0.2em solid white;
}
}
.tileFooter {
padding: @halfSpace @space;
}
}
}
}
@media (min-width: 1000px) {
body {
font-size: 18px;
}
.tileContainer {
.tile {
float: left;
width: 500px;
}
}
}

View File

@ -0,0 +1,9 @@
@import "./config";
.userHasPassword {
color: green;
}
.userNoPassword {
color: indianred;
}

View File

@ -1,18 +0,0 @@
@import "./config.less";
.user {
float: left;
padding: @halfSpace;
background-color: lightskyblue;
border-radius: @halfSpace;
margin-right: @halfSpace;
margin-bottom: @halfSpace;
}
.user_owner {
border: 1px solid black;
}
.user:hover {
background-color: dodgerblue;
}

View File

@ -0,0 +1,28 @@
package de.ph87.tools.common;
import lombok.NonNull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EmailHelper {
private static final Pattern REGEX = Pattern.compile("(?<username>(?<usernameFirst>[^@])[^@]*)@(?<domain>(?<domainFirst>[^.]).*(?<tld>\\.[^.]+))");
public static boolean isEmailValid(@NonNull final String email) {
return REGEX.matcher(email).matches();
}
@NonNull
public static String obfuscateEmail(@NonNull final String email) {
final Matcher matcher = REGEX.matcher(email);
if (!matcher.find()) {
return "";
}
final String usernameFirst = matcher.group("usernameFirst");
final String domainFirst = matcher.group("domainFirst");
final String tld = matcher.group("tld");
return "%s...@%s...%s".formatted(usernameFirst, domainFirst, tld);
}
}

View File

@ -72,6 +72,12 @@ public class User extends UserPublicAbstract {
@Column(nullable = false) @Column(nullable = false)
private String password = ""; private String password = "";
@Setter
@NonNull
@ToString.Exclude
@Column(nullable = false)
private String email = "";
public User(@NonNull final String name) { public User(@NonNull final String name) {
this.name = name; this.name = name;
} }

View File

@ -49,6 +49,12 @@ public class UserController {
return userService.changePassword(userUuid, password); return userService.changePassword(userUuid, password);
} }
@NonNull
@PostMapping("changeEmail")
public UserPrivateDto changeEmail(@NonNull final UserPrivateUuid userUuid, @NonNull @RequestBody final String email) {
return userService.changeEmail(userUuid, email);
}
@GetMapping("delete") @GetMapping("delete")
public void delete(@NonNull final UserPrivateUuid userUuid, @NonNull final HttpServletResponse response) { public void delete(@NonNull final UserPrivateUuid userUuid, @NonNull final HttpServletResponse response) {
userService.delete(userUuid, response); userService.delete(userUuid, response);

View File

@ -12,6 +12,8 @@ import lombok.ToString;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import static de.ph87.tools.common.EmailHelper.obfuscateEmail;
@Getter @Getter
@ToString @ToString
public class UserPrivateDto extends UserPublicAbstract { public class UserPrivateDto extends UserPublicAbstract {
@ -35,6 +37,8 @@ public class UserPrivateDto extends UserPublicAbstract {
private final boolean password; private final boolean password;
private final String email;
private final boolean admin; private final boolean admin;
public UserPrivateDto(@NonNull final User user) { public UserPrivateDto(@NonNull final User user) {
@ -43,6 +47,7 @@ public class UserPrivateDto extends UserPublicAbstract {
this.name = user.getName(); this.name = user.getName();
this.created = user.getCreated(); this.created = user.getCreated();
this.password = !user.getPassword().isEmpty(); this.password = !user.getPassword().isEmpty();
this.email = obfuscateEmail(user.getEmail());
this.admin = user.isAdmin(); this.admin = user.isAdmin();
} }

View File

@ -25,6 +25,7 @@ import java.util.Random;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static de.ph87.tools.common.EmailHelper.isEmailValid;
import static de.ph87.tools.user.uuid.UserPrivateUuidArgumentResolver.USER_UUID_COOKIE_NAME; import static de.ph87.tools.user.uuid.UserPrivateUuidArgumentResolver.USER_UUID_COOKIE_NAME;
@Slf4j @Slf4j
@ -131,6 +132,18 @@ public class UserService {
}); });
} }
@NonNull
public UserPrivateDto changeEmail(@NonNull final UserPrivateUuid privateUuid, @NonNull final String email) {
return modify(privateUuid, user -> {
if (!isEmailValid(email)) {
log.warn("Cannot change User email: not valid, user={}", user);
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
}
user.setEmail(email);
log.info("User email changed: user={}", user);
});
}
public void delete(@NonNull final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) { public void delete(@NonNull final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) {
final User user = userRepository.findByPrivateUuid(privateUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); final User user = userRepository.findByPrivateUuid(privateUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
deleteUnchecked(response, user); deleteUnchecked(response, user);