Added some small fixes, and start of tradeable asset editing page.
This commit is contained in:
		
							parent
							
								
									1097d0843f
								
							
						
					
					
						commit
						9dd0db14e0
					
				| 
						 | 
					@ -1,14 +1,17 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl;
 | 
					package nl.andrewl.coyotecredit.ctl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
 | 
					import nl.andrewl.coyotecredit.ctl.dto.AddSupportedTradeablePayload;
 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.dto.EditExchangePayload;
 | 
					import nl.andrewl.coyotecredit.ctl.dto.EditExchangePayload;
 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.dto.InviteUserPayload;
 | 
					import nl.andrewl.coyotecredit.ctl.dto.InviteUserPayload;
 | 
				
			||||||
import nl.andrewl.coyotecredit.model.User;
 | 
					import nl.andrewl.coyotecredit.model.User;
 | 
				
			||||||
import nl.andrewl.coyotecredit.service.ExchangeService;
 | 
					import nl.andrewl.coyotecredit.service.ExchangeService;
 | 
				
			||||||
 | 
					import org.springframework.http.HttpStatus;
 | 
				
			||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
 | 
					import org.springframework.security.core.annotation.AuthenticationPrincipal;
 | 
				
			||||||
import org.springframework.stereotype.Controller;
 | 
					import org.springframework.stereotype.Controller;
 | 
				
			||||||
import org.springframework.ui.Model;
 | 
					import org.springframework.ui.Model;
 | 
				
			||||||
import org.springframework.web.bind.annotation.*;
 | 
					import org.springframework.web.bind.annotation.*;
 | 
				
			||||||
 | 
					import org.springframework.web.server.ResponseStatusException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.validation.Valid;
 | 
					import javax.validation.Valid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,4 +86,31 @@ public class ExchangeController {
 | 
				
			||||||
		exchangeService.edit(exchangeId, payload, user);
 | 
							exchangeService.edit(exchangeId, payload, user);
 | 
				
			||||||
		return "redirect:/exchanges/" + exchangeId;
 | 
							return "redirect:/exchanges/" + exchangeId;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@GetMapping(path = "/{exchangeId}/editTradeables")
 | 
				
			||||||
 | 
						public String getEditTradeablesPage(Model model, @PathVariable long exchangeId, @AuthenticationPrincipal User user) {
 | 
				
			||||||
 | 
							model.addAttribute("data", exchangeService.getEditTradeablesData(exchangeId, user));
 | 
				
			||||||
 | 
							return "exchange/edit_tradeables";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@PostMapping(path = "/{exchangeId}/addSupportedTradeable")
 | 
				
			||||||
 | 
						public String postAddSupportedTradeable(@PathVariable long exchangeId, @AuthenticationPrincipal User user, @ModelAttribute AddSupportedTradeablePayload payload) {
 | 
				
			||||||
 | 
							exchangeService.addSupportedTradeable(exchangeId, payload.tradeableId(), user);
 | 
				
			||||||
 | 
							return "redirect:/exchanges/" + exchangeId + "/editTradeables";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@GetMapping(path = "/{exchangeId}/removeSupportedTradeable/{tradeableId}")
 | 
				
			||||||
 | 
						public String getRemoveSupportedTradeablePage(@PathVariable long exchangeId, @PathVariable long tradeableId, @AuthenticationPrincipal User user) {
 | 
				
			||||||
 | 
							var data = exchangeService.getEditTradeablesData(exchangeId, user);
 | 
				
			||||||
 | 
							if (data.supportedPublicTradeables().stream().noneMatch(t -> t.id() == tradeableId)) {
 | 
				
			||||||
 | 
								throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "This tradeable cannot be removed from the exchange.");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "exchange/remove_supported_tradeable";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@PostMapping(path = "/{exchangeId}/removeSupportedTradeable/{tradeableId}")
 | 
				
			||||||
 | 
						public String postRemoveSupportedTradeable(@PathVariable long exchangeId, @PathVariable long tradeableId, @AuthenticationPrincipal User user) {
 | 
				
			||||||
 | 
							exchangeService.removeSupportedTradeable(exchangeId, tradeableId, user);
 | 
				
			||||||
 | 
							return "redirect:/exchanges/" + exchangeId + "/editTradeables";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					package nl.andrewl.coyotecredit.ctl.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public record AddSupportedTradeablePayload(long tradeableId) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					package nl.andrewl.coyotecredit.ctl.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public record EditTradeablesData(
 | 
				
			||||||
 | 
							List<TradeableData> supportedPublicTradeables,
 | 
				
			||||||
 | 
							List<TradeableData> customTradeables,
 | 
				
			||||||
 | 
							TradeableData primaryTradeable,
 | 
				
			||||||
 | 
							List<TradeableData> eligiblePublicTradeables
 | 
				
			||||||
 | 
					) {}
 | 
				
			||||||
| 
						 | 
					@ -40,4 +40,10 @@ public class Balance {
 | 
				
			||||||
		this.tradeable = tradeable;
 | 
							this.tradeable = tradeable;
 | 
				
			||||||
		this.amount = amount;
 | 
							this.amount = amount;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public boolean equals(Object o) {
 | 
				
			||||||
 | 
							if (o == this) return true;
 | 
				
			||||||
 | 
							return o instanceof Balance b && this.getBalanceId().equals(b.getBalanceId());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,6 +97,10 @@ public class ExchangeService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Transactional(readOnly = true)
 | 
						@Transactional(readOnly = true)
 | 
				
			||||||
	public void ensureAdminAccount(long exchangeId, User user) {
 | 
						public void ensureAdminAccount(long exchangeId, User user) {
 | 
				
			||||||
 | 
							getExchangeIfAdmin(exchangeId, user);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Exchange getExchangeIfAdmin(long exchangeId, User user) {
 | 
				
			||||||
		Exchange exchange = exchangeRepository.findById(exchangeId)
 | 
							Exchange exchange = exchangeRepository.findById(exchangeId)
 | 
				
			||||||
				.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
 | 
									.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
 | 
				
			||||||
		Account account = accountRepository.findByUserAndExchange(user, exchange)
 | 
							Account account = accountRepository.findByUserAndExchange(user, exchange)
 | 
				
			||||||
| 
						 | 
					@ -104,6 +108,7 @@ public class ExchangeService {
 | 
				
			||||||
		if (!account.isAdmin()) {
 | 
							if (!account.isAdmin()) {
 | 
				
			||||||
			throw new ResponseStatusException(HttpStatus.NOT_FOUND);
 | 
								throw new ResponseStatusException(HttpStatus.NOT_FOUND);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							return exchange;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Transactional
 | 
						@Transactional
 | 
				
			||||||
| 
						 | 
					@ -322,4 +327,66 @@ public class ExchangeService {
 | 
				
			||||||
		), true);
 | 
							), true);
 | 
				
			||||||
		mailSender.send(msg);
 | 
							mailSender.send(msg);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Transactional(readOnly = true)
 | 
				
			||||||
 | 
						public EditTradeablesData getEditTradeablesData(long exchangeId, User user) {
 | 
				
			||||||
 | 
							Exchange exchange = getExchangeIfAdmin(exchangeId, user);
 | 
				
			||||||
 | 
							List<TradeableData> supportedPublicTradeables = exchange.getSupportedTradeables().stream()
 | 
				
			||||||
 | 
									.map(TradeableData::new).sorted(Comparator.comparing(TradeableData::symbol)).toList();
 | 
				
			||||||
 | 
							List<TradeableData> customTradeables = exchange.getCustomTradeables().stream()
 | 
				
			||||||
 | 
									.map(TradeableData::new).sorted(Comparator.comparing(TradeableData::symbol)).toList();
 | 
				
			||||||
 | 
							List<TradeableData> eligiblePublicTradeables = tradeableRepository.findAllByExchangeNull().stream()
 | 
				
			||||||
 | 
									.filter(t -> !exchange.getSupportedTradeables().contains(t))
 | 
				
			||||||
 | 
									.map(TradeableData::new).sorted(Comparator.comparing(TradeableData::symbol)).toList();
 | 
				
			||||||
 | 
							return new EditTradeablesData(
 | 
				
			||||||
 | 
									supportedPublicTradeables,
 | 
				
			||||||
 | 
									customTradeables,
 | 
				
			||||||
 | 
									new TradeableData(exchange.getPrimaryTradeable()),
 | 
				
			||||||
 | 
									eligiblePublicTradeables
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Transactional
 | 
				
			||||||
 | 
						public void addSupportedTradeable(long exchangeId, long tradeableId, User user) {
 | 
				
			||||||
 | 
							Exchange exchange = getExchangeIfAdmin(exchangeId, user);
 | 
				
			||||||
 | 
							Tradeable tradeable = tradeableRepository.findById(tradeableId)
 | 
				
			||||||
 | 
									.orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Unknown tradeable."));
 | 
				
			||||||
 | 
							// Exit if the tradeable is already supported.
 | 
				
			||||||
 | 
							if (exchange.getSupportedTradeables().contains(tradeable)) return;
 | 
				
			||||||
 | 
							if (tradeable.getExchange() != null) {
 | 
				
			||||||
 | 
								throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Unknown tradeable.");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							exchange.getSupportedTradeables().add(tradeable);
 | 
				
			||||||
 | 
							// Add a zero-value balance to any account that's missing it.
 | 
				
			||||||
 | 
							for (var acc : exchange.getAccounts()) {
 | 
				
			||||||
 | 
								Balance bal = acc.getBalanceForTradeable(tradeable);
 | 
				
			||||||
 | 
								if (bal == null) {
 | 
				
			||||||
 | 
									acc.getBalances().add(new Balance(acc, tradeable, BigDecimal.ZERO));
 | 
				
			||||||
 | 
									accountRepository.save(acc);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							exchangeRepository.save(exchange);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Transactional
 | 
				
			||||||
 | 
						public void removeSupportedTradeable(long exchangeId, long tradeableId, User user) {
 | 
				
			||||||
 | 
							Exchange exchange = getExchangeIfAdmin(exchangeId, user);
 | 
				
			||||||
 | 
							Tradeable tradeable = tradeableRepository.findById(tradeableId)
 | 
				
			||||||
 | 
									.orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Unknown tradeable."));
 | 
				
			||||||
 | 
							// Quietly exit if the user is trying to remove a tradeable that isn't supported in the first place.
 | 
				
			||||||
 | 
							if (!exchange.getSupportedTradeables().contains(tradeable)) return;
 | 
				
			||||||
 | 
							if (exchange.getPrimaryTradeable().equals(tradeable)) {
 | 
				
			||||||
 | 
								throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot remove the primary tradeable asset. Change this first.");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							exchange.getSupportedTradeables().remove(tradeable);
 | 
				
			||||||
 | 
							// Delete balance for any account that has it.
 | 
				
			||||||
 | 
							for (var acc : exchange.getAccounts()) {
 | 
				
			||||||
 | 
								Balance bal = acc.getBalanceForTradeable(tradeable);
 | 
				
			||||||
 | 
								if (bal != null) {
 | 
				
			||||||
 | 
									acc.getBalances().remove(bal);
 | 
				
			||||||
 | 
									accountRepository.save(acc);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							exchangeRepository.save(exchange);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@
 | 
				
			||||||
        </thead>
 | 
					        </thead>
 | 
				
			||||||
        <tbody>
 | 
					        <tbody>
 | 
				
			||||||
            <tr th:each="account : ${accounts}">
 | 
					            <tr th:each="account : ${accounts}">
 | 
				
			||||||
                <td><a class="colored-link" th:href="@{/accounts/{id}(id=${account.id()})}" th:text="${account.number()}"></a></td>
 | 
					                <td><a class="colored-link monospace" th:href="@{/accounts/{id}(id=${account.id()})}" th:text="${account.number()}"></a></td>
 | 
				
			||||||
                <td th:text="${account.name()}"></td>
 | 
					                <td th:text="${account.name()}"></td>
 | 
				
			||||||
                <td th:text="${account.admin()}"></td>
 | 
					                <td th:text="${account.admin()}"></td>
 | 
				
			||||||
                <td class="monospace" th:text="${account.totalBalance()}"></td>
 | 
					                <td class="monospace" th:text="${account.totalBalance()}"></td>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,86 @@
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html
 | 
				
			||||||
 | 
					        lang="en"
 | 
				
			||||||
 | 
					        xmlns:th="http://www.thymeleaf.org"
 | 
				
			||||||
 | 
					        th:replace="~{layout/basic_page :: layout (title='Edit Tradeables', content=~{::#content})}"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					<div id="content" class="container">
 | 
				
			||||||
 | 
					    <h1 class="display-4">Edit Tradeable Assets</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="card text-white bg-dark mb-3">
 | 
				
			||||||
 | 
					        <div class="card-body">
 | 
				
			||||||
 | 
					            <h5 class="card-title">Supported Publicly Traded Assets</h5>
 | 
				
			||||||
 | 
					            <p class="card-text text-muted">
 | 
				
			||||||
 | 
					                These assets are publicly available to all exchanges in Coyote Credit, and their price is updated automatically with real-world data.
 | 
				
			||||||
 | 
					            </p>
 | 
				
			||||||
 | 
					            <table class="table table-dark">
 | 
				
			||||||
 | 
					                <thead>
 | 
				
			||||||
 | 
					                <tr>
 | 
				
			||||||
 | 
					                    <th>Name</th>
 | 
				
			||||||
 | 
					                    <th>Symbol</th>
 | 
				
			||||||
 | 
					                    <th>Price (USD)</th>
 | 
				
			||||||
 | 
					                    <th>Type</th>
 | 
				
			||||||
 | 
					                    <th>Remove</th>
 | 
				
			||||||
 | 
					                </tr>
 | 
				
			||||||
 | 
					                </thead>
 | 
				
			||||||
 | 
					                <tbody>
 | 
				
			||||||
 | 
					                <tr th:each="tradeable : ${data.supportedPublicTradeables()}">
 | 
				
			||||||
 | 
					                    <td>
 | 
				
			||||||
 | 
					                        <a class="colored-link" th:href="@{/tradeables/{tId}(tId=${tradeable.id()})}" th:text="${tradeable.name()}"></a>
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
 | 
					                    <td th:text="${tradeable.symbol()}"></td>
 | 
				
			||||||
 | 
					                    <td class="monospace" th:text="${tradeable.formattedPriceUsd()}"></td>
 | 
				
			||||||
 | 
					                    <td th:text="${tradeable.type()}"></td>
 | 
				
			||||||
 | 
					                    <td>
 | 
				
			||||||
 | 
					                        <a class="colored-link" th:href="@{/exchanges/{eId}/removeSupportedTradeable/{tId}(eId=${exchangeId}, tId=${tradeable.id()})}">Remove</a>
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
 | 
					                </tr>
 | 
				
			||||||
 | 
					                </tbody>
 | 
				
			||||||
 | 
					            </table>
 | 
				
			||||||
 | 
					            <form
 | 
				
			||||||
 | 
					                    th:if="${!data.eligiblePublicTradeables().isEmpty()}"
 | 
				
			||||||
 | 
					                    th:action="@{/exchanges/{eId}/addSupportedTradeable(eId=${exchangeId})}"
 | 
				
			||||||
 | 
					                    method="post"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <label for="addSupportedTradeableSelect" class="form-label">Add support for a new tradeable asset</label>
 | 
				
			||||||
 | 
					                <select id="addSupportedTradeableSelect" class="form-select" name="tradeableId">
 | 
				
			||||||
 | 
					                    <option
 | 
				
			||||||
 | 
					                        th:each="tradeable : ${data.eligiblePublicTradeables()}"
 | 
				
			||||||
 | 
					                        th:text="${tradeable.name() + ' (' + tradeable.symbol() + ')'}"
 | 
				
			||||||
 | 
					                        th:value="${tradeable.id()}"
 | 
				
			||||||
 | 
					                    ></option>
 | 
				
			||||||
 | 
					                </select>
 | 
				
			||||||
 | 
					                <button class="btn btn-primary mt-2" type="submit">Add</button>
 | 
				
			||||||
 | 
					            </form>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="card text-white bg-dark mb-3">
 | 
				
			||||||
 | 
					        <div class="card-body">
 | 
				
			||||||
 | 
					            <h5 class="card-title">Custom Traded Assets</h5>
 | 
				
			||||||
 | 
					            <p class="card-text text-muted">
 | 
				
			||||||
 | 
					                These assets are available only within this exchange.
 | 
				
			||||||
 | 
					            </p>
 | 
				
			||||||
 | 
					            <table class="table table-dark">
 | 
				
			||||||
 | 
					                <thead>
 | 
				
			||||||
 | 
					                <tr>
 | 
				
			||||||
 | 
					                    <th>Name</th>
 | 
				
			||||||
 | 
					                    <th>Symbol</th>
 | 
				
			||||||
 | 
					                    <th>Price (USD)</th>
 | 
				
			||||||
 | 
					                    <th>Type</th>
 | 
				
			||||||
 | 
					                </tr>
 | 
				
			||||||
 | 
					                </thead>
 | 
				
			||||||
 | 
					                <tbody>
 | 
				
			||||||
 | 
					                <tr th:each="tradeable : ${data.customTradeables()}">
 | 
				
			||||||
 | 
					                    <td>
 | 
				
			||||||
 | 
					                        <a class="colored-link" th:href="@{/tradeables/{tId}(tId=${tradeable.id()})}" th:text="${tradeable.name()}"></a>
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
 | 
					                    <td th:text="${tradeable.symbol()}"></td>
 | 
				
			||||||
 | 
					                    <td class="monospace" th:text="${tradeable.formattedPriceUsd()}"></td>
 | 
				
			||||||
 | 
					                    <td th:text="${tradeable.type()}"></td>
 | 
				
			||||||
 | 
					                </tr>
 | 
				
			||||||
 | 
					                </tbody>
 | 
				
			||||||
 | 
					            </table>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					@ -24,6 +24,10 @@
 | 
				
			||||||
                <dd class="col-sm-6" th:text="${exchange.accountCount()}"></dd>
 | 
					                <dd class="col-sm-6" th:text="${exchange.accountCount()}"></dd>
 | 
				
			||||||
            </dl>
 | 
					            </dl>
 | 
				
			||||||
            <a class="btn btn-primary" th:href="@{/accounts/{aId}(aId=${exchange.accountId()})}">My Account</a>
 | 
					            <a class="btn btn-primary" th:href="@{/accounts/{aId}(aId=${exchange.accountId()})}">My Account</a>
 | 
				
			||||||
 | 
					            <span th:if="${exchange.accountAdmin()}">
 | 
				
			||||||
 | 
					                <a class="btn btn-primary" th:href="@{/exchanges/{eId}/accounts(eId=${exchange.id()})}">View All Accounts</a>
 | 
				
			||||||
 | 
					                <a class="btn btn-secondary" th:href="@{/exchanges/{eId}/edit(eId=${exchange.id()})}">Edit Exchange Settings</a>
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,29 +37,26 @@
 | 
				
			||||||
            <table class="table table-dark">
 | 
					            <table class="table table-dark">
 | 
				
			||||||
                <thead>
 | 
					                <thead>
 | 
				
			||||||
                <tr>
 | 
					                <tr>
 | 
				
			||||||
                    <th>Symbol</th>
 | 
					 | 
				
			||||||
                    <th>Type</th>
 | 
					 | 
				
			||||||
                    <th>Price (in USD)</th>
 | 
					 | 
				
			||||||
                    <th>Name</th>
 | 
					                    <th>Name</th>
 | 
				
			||||||
 | 
					                    <th>Symbol</th>
 | 
				
			||||||
 | 
					                    <th>Price (USD)</th>
 | 
				
			||||||
 | 
					                    <th>Type</th>
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
                </thead>
 | 
					                </thead>
 | 
				
			||||||
                <tbody>
 | 
					                <tbody>
 | 
				
			||||||
                <tr th:each="tradeable : ${exchange.supportedTradeables()}">
 | 
					                <tr th:each="tradeable : ${exchange.supportedTradeables()}">
 | 
				
			||||||
 | 
					                    <td>
 | 
				
			||||||
 | 
					                        <a class="colored-link" th:href="@{/tradeables/{tId}(tId=${tradeable.id()})}" th:text="${tradeable.name()}"></a>
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
                    <td th:text="${tradeable.symbol()}"></td>
 | 
					                    <td th:text="${tradeable.symbol()}"></td>
 | 
				
			||||||
                    <td th:text="${tradeable.type()}"></td>
 | 
					 | 
				
			||||||
                    <td class="monospace" th:text="${tradeable.formattedPriceUsd()}"></td>
 | 
					                    <td class="monospace" th:text="${tradeable.formattedPriceUsd()}"></td>
 | 
				
			||||||
                    <td th:text="${tradeable.name()}"></td>
 | 
					                    <td th:text="${tradeable.type()}"></td>
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
                </tbody>
 | 
					                </tbody>
 | 
				
			||||||
            </table>
 | 
					            </table>
 | 
				
			||||||
        </div>
 | 
					            <span th:if="${exchange.accountAdmin()}">
 | 
				
			||||||
    </div>
 | 
					                <a class="btn btn-primary" th:href="@{/exchanges/{eId}/editTradeables(eId=${exchange.id()})}">Edit Tradeable Assets</a>
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
    <div class="card text-white bg-dark mb-3" th:if="${exchange.accountAdmin()}">
 | 
					 | 
				
			||||||
        <div class="card-body">
 | 
					 | 
				
			||||||
            <h5 class="card-title">Administrator Tools</h5>
 | 
					 | 
				
			||||||
            <a class="btn btn-primary" th:href="@{/exchanges/{eId}/accounts(eId=${exchange.id()})}">View All Accounts</a>
 | 
					 | 
				
			||||||
            <a class="btn btn-secondary" th:href="@{/exchanges/{eId}/edit(eId=${exchange.id()})}">Edit Exchange Settings</a>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html
 | 
				
			||||||
 | 
					        lang="en"
 | 
				
			||||||
 | 
					        xmlns:th="http://www.thymeleaf.org"
 | 
				
			||||||
 | 
					        th:replace="~{layout/basic_page :: layout (title='Remove Tradeable', content=~{::#content})}"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					<div id="content" class="container">
 | 
				
			||||||
 | 
					    <h1 class="display-4">Remove Supported Tradeable Asset</h1>
 | 
				
			||||||
 | 
					    <p>
 | 
				
			||||||
 | 
					        Are you sure you want to remove this tradeable asset from your exchange?
 | 
				
			||||||
 | 
					        All accounts will have their balance of this asset permanently removed,
 | 
				
			||||||
 | 
					        and it cannot be undone.
 | 
				
			||||||
 | 
					    </p>
 | 
				
			||||||
 | 
					    <form th:action="@{/exchanges/{eId}/removeSupportedTradeable/{tId}(eId=${exchangeId}, tId=${tradeableId})}" method="post">
 | 
				
			||||||
 | 
					        <a class="btn btn-secondary" th:href="@{/exchanges/{eId}/editTradeables(eId=${exchangeId})}">Cancel</a>
 | 
				
			||||||
 | 
					        <button class="btn btn-danger" type="submit">Remove</button>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
		Loading…
	
		Reference in New Issue