Added create account page, and more info for accounts.
This commit is contained in:
parent
27a7ee6e72
commit
046d6dda09
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
|||
</parent>
|
||||
<groupId>nl.andrewl</groupId>
|
||||
<artifactId>coyotecredit</artifactId>
|
||||
<version>1.2.0</version>
|
||||
<version>1.3.0</version>
|
||||
<name>coyotecredit</name>
|
||||
<description>Banking and stock trading application to teach students.</description>
|
||||
<properties>
|
||||
|
|
|
@ -12,6 +12,8 @@ public record FullAccountData (
|
|||
String number,
|
||||
String name,
|
||||
boolean admin,
|
||||
long userId,
|
||||
String username,
|
||||
boolean userAdmin,// If the current user is an admin of the exchange this account is in.
|
||||
boolean userIsOwner,// If the current user is the owner of this account.
|
||||
ExchangeData exchange,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package nl.andrewl.coyotecredit.ctl.exchange;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import nl.andrewl.coyotecredit.ctl.exchange.dto.AddAccountPayload;
|
||||
import nl.andrewl.coyotecredit.ctl.exchange.dto.AddSupportedTradeablePayload;
|
||||
import nl.andrewl.coyotecredit.ctl.exchange.dto.EditExchangePayload;
|
||||
import nl.andrewl.coyotecredit.ctl.exchange.dto.InviteUserPayload;
|
||||
|
@ -66,6 +67,18 @@ public class ExchangeController {
|
|||
return "redirect:/users/" + user.getId();
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{exchangeId}/createAccount")
|
||||
public String getCreateAccountPage(@PathVariable long exchangeId, @AuthenticationPrincipal User user) {
|
||||
exchangeService.ensureAdminAccount(exchangeId, user);
|
||||
return "exchange/create_account";
|
||||
}
|
||||
|
||||
@PostMapping(path = "/{exchangeId}/createAccount")
|
||||
public String postCreateAccount(@PathVariable long exchangeId, @AuthenticationPrincipal User user, @ModelAttribute AddAccountPayload payload) {
|
||||
exchangeService.addAccount(exchangeId, user, payload);
|
||||
return "redirect:/exchanges/" + exchangeId + "/accounts";
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{exchangeId}/removeAccount/{accountId}")
|
||||
public String getRemoveAccountPage(@PathVariable long exchangeId, @PathVariable long accountId, @AuthenticationPrincipal User user) {
|
||||
exchangeService.ensureAdminAccount(exchangeId, user);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package nl.andrewl.coyotecredit.ctl.exchange.dto;
|
||||
|
||||
public record AddAccountPayload(
|
||||
String username,
|
||||
String password,
|
||||
String email,
|
||||
String accountName
|
||||
) {}
|
|
@ -12,4 +12,5 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
|||
boolean existsByUsername(String username);
|
||||
|
||||
Optional<User> findByEmail(String email);
|
||||
boolean existsByEmail(String email);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package nl.andrewl.coyotecredit.model;
|
|||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
|
@ -53,7 +54,7 @@ public class Account {
|
|||
* Administrators have special permissions to add and remove other accounts,
|
||||
* custom tradeables, exchange rates, and more.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@Column(nullable = false) @Setter
|
||||
private boolean admin;
|
||||
|
||||
/**
|
||||
|
|
|
@ -115,23 +115,6 @@ public class AccountService {
|
|||
notificationRepository.save(new UserNotification(recipient.getUser(), recipientMessage));
|
||||
}
|
||||
|
||||
public static record AccountData (
|
||||
long id,
|
||||
String accountNumber,
|
||||
String exchangeName
|
||||
) {}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<AccountData> getAccountsOverview(User user) {
|
||||
return accountRepository.findAllByUser(user).stream()
|
||||
.map(a -> new AccountData(
|
||||
a.getId(),
|
||||
a.getNumber(),
|
||||
a.getExchange().getName()
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public FullAccountData getAccountData(User user, long accountId) {
|
||||
Account account = accountRepository.findById(accountId)
|
||||
|
@ -149,6 +132,8 @@ public class AccountService {
|
|||
account.getNumber(),
|
||||
account.getName(),
|
||||
account.isAdmin(),
|
||||
account.getUser().getId(),
|
||||
account.getUser().getUsername(),
|
||||
userAccount.isAdmin(),
|
||||
account.getUser().getId().equals(user.getId()),
|
||||
new ExchangeData(
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.springframework.data.domain.Pageable;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
@ -41,6 +42,7 @@ public class ExchangeService {
|
|||
private final ExchangeInvitationRepository invitationRepository;
|
||||
private final UserNotificationRepository notificationRepository;
|
||||
private final JavaMailSender mailSender;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
@Value("${coyote-credit.base-url}")
|
||||
private String baseUrl;
|
||||
|
@ -117,6 +119,23 @@ public class ExchangeService {
|
|||
return exchange;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void addAccount(long exchangeId, User user, AddAccountPayload payload) {
|
||||
Exchange exchange = getExchangeIfAdmin(exchangeId, user);
|
||||
if (userRepository.existsByUsername(payload.username())) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Username is taken.");
|
||||
}
|
||||
if (userRepository.existsByEmail(payload.email())) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "There is already an account with that email.");
|
||||
}
|
||||
User newUser = userRepository.save(new User(payload.username(), passwordEncoder.encode(payload.password()), payload.email()));
|
||||
Account account = accountRepository.save(new Account(AccountNumberUtils.generate(), newUser, payload.accountName(), exchange));
|
||||
for (var t : exchange.getAllTradeables()) {
|
||||
account.getBalances().add(new Balance(account, t, BigDecimal.ZERO));
|
||||
}
|
||||
accountRepository.save(account);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void removeAccount(long exchangeId, long accountId, User user) {
|
||||
Exchange exchange = exchangeRepository.findById(exchangeId)
|
||||
|
|
|
@ -67,6 +67,9 @@ public class UserService {
|
|||
if (userRepository.existsByUsername(payload.username())) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Username is already taken.");
|
||||
}
|
||||
if (userRepository.existsByEmail(payload.email())) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "There is already an account with that email.");
|
||||
}
|
||||
User user = new User(payload.username(), passwordEncoder.encode(payload.password()), payload.email());
|
||||
user = userRepository.save(user);
|
||||
if (payload.inviteCode() != null) {
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
<dd class="col-sm-6">
|
||||
<span class="monospace" th:text="${account.totalBalance()}"></span> <span th:text="${account.exchange().primaryTradeable()}"></span>
|
||||
</dd>
|
||||
<dt class="col-sm-6">Username</dt>
|
||||
<dd class="col-sm-6" th:text="${account.username()}"></dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
>
|
||||
<div id="content" class="container">
|
||||
<h1 class="display-4">Accounts</h1>
|
||||
<p class="lead">
|
||||
View all accounts in the exchange.
|
||||
</p>
|
||||
|
||||
<table class="table table-dark">
|
||||
<thead>
|
||||
|
@ -33,4 +36,5 @@
|
|||
</table>
|
||||
|
||||
<a class="btn btn-primary" th:href="@{/exchanges/{eId}/inviteUser(eId=${exchangeId})}">Invite User</a>
|
||||
<a class="btn btn-secondary" th:href="@{/exchanges/{eId}/createAccount(eId=${exchangeId})}">Create Account</a>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html
|
||||
lang="en"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
th:replace="~{layout/basic_page :: layout (title='Exchange Accounts', content=~{::#content})}"
|
||||
>
|
||||
<div id="content" class="container">
|
||||
<h1 class="display-4">Create Account</h1>
|
||||
<p class="lead">
|
||||
Create a new account for someone to join the exchange. Only for users who don't yet have an account on Coyote Credit.
|
||||
</p>
|
||||
|
||||
<form th:action="@{/exchanges/{eId}/createAccount(eId=${exchangeId})}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="usernameInput" class="form-label">Username</label>
|
||||
<input id="usernameInput" name="username" class="form-control" type="text" required/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="passwordInput" class="form-label">Password</label>
|
||||
<input id="passwordInput" name="password" class="form-control" type="password" required/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="emailInput" class="form-label">Email</label>
|
||||
<input id="emailInput" name="email" class="form-control" type="email" required/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="accountNameInput" class="form-label">Account Name</label>
|
||||
<input id="accountNameInput" name="accountName" class="form-control" type="text" required/>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Create</button>
|
||||
</form>
|
||||
</div>
|
Loading…
Reference in New Issue