Starting impl of Symbol conf page
This commit is contained in:
parent
0ae1c08505
commit
245a599b52
9 changed files with 185 additions and 92 deletions
|
|
@ -4,6 +4,7 @@ import se.koc.trader.api.ExchangeConfig;
|
||||||
import se.koc.trader.endpoint.AlertEndpoint;
|
import se.koc.trader.endpoint.AlertEndpoint;
|
||||||
import se.koc.trader.api.TraderApiEndpoint;
|
import se.koc.trader.api.TraderApiEndpoint;
|
||||||
import se.koc.trader.daemon.TraderMarketUpdateManager;
|
import se.koc.trader.daemon.TraderMarketUpdateManager;
|
||||||
|
import se.koc.trader.endpoint.ExchangeEndpoint;
|
||||||
import se.koc.trader.struct.Exchange;
|
import se.koc.trader.struct.Exchange;
|
||||||
import se.koc.trader.struct.PluginConfig;
|
import se.koc.trader.struct.PluginConfig;
|
||||||
import se.koc.trader.api.TraderPage;
|
import se.koc.trader.api.TraderPage;
|
||||||
|
|
@ -13,6 +14,7 @@ import zutil.log.LogUtil;
|
||||||
import zutil.net.http.HttpServer;
|
import zutil.net.http.HttpServer;
|
||||||
import zutil.net.http.page.HttpFilePage;
|
import zutil.net.http.page.HttpFilePage;
|
||||||
import zutil.net.http.page.HttpRedirectPage;
|
import zutil.net.http.page.HttpRedirectPage;
|
||||||
|
import zutil.net.ws.rest.RESTHttpPage;
|
||||||
import zutil.plugin.PluginData;
|
import zutil.plugin.PluginData;
|
||||||
import zutil.plugin.PluginManager;
|
import zutil.plugin.PluginManager;
|
||||||
|
|
||||||
|
|
@ -96,6 +98,7 @@ public class TraderServer {
|
||||||
registerPage(it.next());
|
registerPage(it.next());
|
||||||
for (Iterator<TraderPage> it = pluginManager.getSingletonIterator(TraderPage.class); it.hasNext(); )
|
for (Iterator<TraderPage> it = pluginManager.getSingletonIterator(TraderPage.class); it.hasNext(); )
|
||||||
registerPage(it.next());
|
registerPage(it.next());
|
||||||
|
http.setPage(ExchangeEndpoint.API_PATH, new RESTHttpPage(new ExchangeEndpoint()));
|
||||||
http.start();
|
http.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
30
src/main/java/se/koc/trader/endpoint/ExchangeEndpoint.java
Normal file
30
src/main/java/se/koc/trader/endpoint/ExchangeEndpoint.java
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
package se.koc.trader.endpoint;
|
||||||
|
|
||||||
|
import se.koc.trader.TraderContext;
|
||||||
|
import se.koc.trader.struct.Exchange;
|
||||||
|
import se.koc.trader.struct.Symbol;
|
||||||
|
import zutil.db.DBConnection;
|
||||||
|
import zutil.net.ws.WSInterface;
|
||||||
|
import zutil.parser.DataNode;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
|
||||||
|
public class ExchangeEndpoint implements WSInterface {
|
||||||
|
public static final String API_PATH = "/api/exchange";
|
||||||
|
|
||||||
|
|
||||||
|
@WSPath(API_PATH + "/symbols")
|
||||||
|
public DataNode getAllSymbols(int exchangeId) throws SQLException {
|
||||||
|
DBConnection db = TraderContext.getDB();
|
||||||
|
DataNode root = new DataNode(DataNode.DataType.Map);
|
||||||
|
|
||||||
|
Exchange exchange = Exchange.getExchange(db, exchangeId);
|
||||||
|
DataNode symbols = root.set("symbols", DataNode.DataType.List);
|
||||||
|
for (Symbol symbol : exchange.getObject().getExchangeMarket().getSymbols()) {
|
||||||
|
symbols.add(symbol.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,9 +3,24 @@ package se.koc.trader.exchange.binance;
|
||||||
import se.koc.trader.api.ExchangeConfig;
|
import se.koc.trader.api.ExchangeConfig;
|
||||||
import se.koc.trader.api.ExchangeMarket;
|
import se.koc.trader.api.ExchangeMarket;
|
||||||
import se.koc.trader.api.ExchangeWallet;
|
import se.koc.trader.api.ExchangeWallet;
|
||||||
|
import zutil.ui.conf.Configurator;
|
||||||
|
|
||||||
public class BinanceExchange extends ExchangeConfig {
|
public class BinanceExchange extends ExchangeConfig {
|
||||||
private BinanceMarket market;
|
|
||||||
|
@Configurator.Configurable("Binance API Key")
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
transient private BinanceMarket market;
|
||||||
|
|
||||||
|
|
||||||
|
public String getApiKey() {
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApiKey(String apiKey) {
|
||||||
|
this.apiKey = apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExchangeMarket getExchangeMarket() {
|
public ExchangeMarket getExchangeMarket() {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package se.koc.trader.page;
|
||||||
|
|
||||||
import se.koc.trader.TraderContext;
|
import se.koc.trader.TraderContext;
|
||||||
import se.koc.trader.api.TraderPage;
|
import se.koc.trader.api.TraderPage;
|
||||||
|
import se.koc.trader.struct.Exchange;
|
||||||
import se.koc.trader.struct.Symbol;
|
import se.koc.trader.struct.Symbol;
|
||||||
import zutil.ObjectUtil;
|
import zutil.ObjectUtil;
|
||||||
import zutil.db.DBConnection;
|
import zutil.db.DBConnection;
|
||||||
|
|
@ -80,6 +81,7 @@ public class SymbolConfigPage extends TraderPage {
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
||||||
|
tmpl.set("exchanges", Exchange.getExchanges(db));
|
||||||
tmpl.set("symbols", Symbol.getSymbols(db));
|
tmpl.set("symbols", Symbol.getSymbols(db));
|
||||||
|
|
||||||
return tmpl;
|
return tmpl;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package se.koc.trader.struct;
|
package se.koc.trader.struct;
|
||||||
|
|
||||||
import se.koc.trader.api.ExchangeConfig;
|
import se.koc.trader.api.ExchangeConfig;
|
||||||
|
import se.koc.trader.util.ConfigExchangeValueProvider;
|
||||||
import zutil.db.DBConnection;
|
import zutil.db.DBConnection;
|
||||||
import zutil.db.bean.DBBean;
|
import zutil.db.bean.DBBean;
|
||||||
import zutil.db.bean.DBBeanObjectDSO;
|
import zutil.db.bean.DBBeanObjectDSO;
|
||||||
|
import zutil.ui.Configurator;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -19,6 +21,7 @@ public class Symbol extends DBBean {
|
||||||
private double price;
|
private double price;
|
||||||
private long priceTimestamp;
|
private long priceTimestamp;
|
||||||
|
|
||||||
|
@Configurator.Configurable(value = "Exchange", description = "The exchange instance where symbol exists.", valueProvider = ConfigExchangeValueProvider.class)
|
||||||
private ExchangeConfig exchangeConfig;
|
private ExchangeConfig exchangeConfig;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package se.koc.trader.util;
|
||||||
|
|
||||||
|
import se.koc.trader.TraderContext;
|
||||||
|
import se.koc.trader.struct.Exchange;
|
||||||
|
import zutil.db.DBConnection;
|
||||||
|
import zutil.ui.Configurator;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A value provider that will give all Enum values
|
||||||
|
*/
|
||||||
|
public class ConfigExchangeValueProvider implements Configurator.ConfigValueProvider<Exchange> {
|
||||||
|
Map<String, Exchange> map = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getPossibleValues() {
|
||||||
|
DBConnection db = TraderContext.getDB();
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (Exchange exchange : Exchange.getExchanges(db)) {
|
||||||
|
map.put(exchange.getName() + " (Id: " + exchange.getId() + ")", exchange);
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return map.keySet().toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Exchange getValueObject(String value) {
|
||||||
|
return map.get(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,6 @@
|
||||||
<div class="col-md-12 d-flex">
|
<div class="col-md-12 d-flex">
|
||||||
|
|
||||||
<div class="card flex-fill w-100">
|
<div class="card flex-fill w-100">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="card-actions float-end">
|
|
||||||
<div class="btn-group float-end" role="group">
|
|
||||||
<button type="button" class="btn btn-success"
|
|
||||||
data-bs-toggle="modal" data-bs-target="#exchangeModal" data-action="create_exchange">
|
|
||||||
<i class="bi bi-plus"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h5 class="card-title mb-0">Exchange Instances</h5>
|
<h5 class="card-title mb-0">Exchange Instances</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -21,7 +12,16 @@
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Configuration</th>
|
<th>Configuration</th>
|
||||||
<th class="text-end">Actions</th>
|
<th class="text-end">
|
||||||
|
<div class="card-actions float-end">
|
||||||
|
<div class="btn-group float-end" role="group">
|
||||||
|
<button type="button" class="btn btn-success btn-sm"
|
||||||
|
data-bs-toggle="modal" data-bs-target="#exchangeModal" data-action="create_exchange">
|
||||||
|
<i class="bi bi-plus"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -83,12 +83,12 @@
|
||||||
<input type="hidden" id="id" name="id">
|
<input type="hidden" id="id" name="id">
|
||||||
<div>
|
<div>
|
||||||
<label for="input-name" class="form-label">Name:</label>
|
<label for="input-name" class="form-label">Name:</label>
|
||||||
<input id="input-name" type="text" class="form-control" name="name">
|
<input id="input-name" type="text" name="name" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="input-type" class="form-label">Type:</label>
|
<label for="input-type" class="form-label">Type:</label>
|
||||||
<select id="input-type" class="form-control" name="type">
|
<select id="input-type" name="type" class="form-control" >
|
||||||
{{#exchangeConfigClasses}}
|
{{#exchangeConfigClasses}}
|
||||||
<option>{{.getName()}}</option>
|
<option>{{.getName()}}</option>
|
||||||
{{/exchangeConfigClasses}}
|
{{/exchangeConfigClasses}}
|
||||||
|
|
@ -116,13 +116,13 @@
|
||||||
<label for="input-{{.getName()}}" class="form-label">{{.getNiceName()}}:</label>
|
<label for="input-{{.getName()}}" class="form-label">{{.getNiceName()}}:</label>
|
||||||
|
|
||||||
{{#.isTypeString()}}<input id="input-{{.getName()}}" type="text" class="form-control" name="{{.getName()}}">{{/#.isTypeString()}}
|
{{#.isTypeString()}}<input id="input-{{.getName()}}" type="text" class="form-control" name="{{.getName()}}">{{/#.isTypeString()}}
|
||||||
{{#.isTypeInt()}}<input id="input-{{.getName()}}" type="number" class="form-control" name="{{.getName()}}">{{/#.isTypeInt()}}
|
{{#.isTypeNumber()}}<input id="input-{{.getName()}}" type="number" class="form-control" name="{{.getName()}}">{{/#.isTypeNumber()}}
|
||||||
{{#.isTypeBoolean()}}<input id="input-{{.getName()}}" type="checkbox" name="{{.getName()}}" value="true">{{/#.isTypeBoolean()}}
|
{{#.isTypeBoolean()}}<input id="input-{{.getName()}}" type="checkbox" name="{{.getName()}}" value="true">{{/#.isTypeBoolean()}}
|
||||||
{{#.isTypeEnum()}}
|
{{#.isTypeSelection()}}
|
||||||
<select id="input-{{.getName()}}" class="form-control" name="{{.getName()}}">
|
<select id="input-{{.getName()}}" class="form-control" name="{{.getName()}}">
|
||||||
{{#.getPossibleValues()}}<option>{{.}}</option>{{/.getPossibleValues()}}
|
{{#.getPossibleValues()}}<option>{{.}}</option>{{/.getPossibleValues()}}
|
||||||
</select>
|
</select>
|
||||||
{{/#.isTypeEnum()}}
|
{{/#.isTypeSelection()}}
|
||||||
|
|
||||||
{{#.getDescription()}}
|
{{#.getDescription()}}
|
||||||
<div class="form-text">{{.getDescription()}}</div>
|
<div class="form-text">{{.getDescription()}}</div>
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ function initDynamicModalForm(modalId, formTemplateId = null, templateID = null)
|
||||||
});
|
});
|
||||||
// Update dynamic inputs
|
// Update dynamic inputs
|
||||||
$("#" + modalId + " select[name=type]").change(function(){
|
$("#" + modalId + " select[name=type]").change(function(){
|
||||||
$("#" + modalId + " #"+formTemplateId).html(dynamicConf[formTemplateId][$(this).val()]);
|
$("#" + modalId + " #" + formTemplateId).html(dynamicConf[formTemplateId][$(this).val()]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,82 +1,74 @@
|
||||||
<div class="col-md-12 d-flex">
|
<div class="col-md-12 d-flex">
|
||||||
<div class="card flex-fill w-100">
|
<div class="card flex-fill w-100">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="card-actions float-end">
|
<h5 class="card-title mb-0">Symbol Instances</h5>
|
||||||
<div class="dropdown show">
|
</div>
|
||||||
<a href="#" data-bs-toggle="dropdown" data-bs-display="static">
|
|
||||||
<i class="align-middle" data-feather="more-horizontal"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="dropdown-menu dropdown-menu-end">
|
<div class="card-body">
|
||||||
<a class="dropdown-item" href="#">Action</a>
|
<div class="table-responsive">
|
||||||
<a class="dropdown-item" href="#">Another action</a>
|
<table class="table table-hover table-sm">
|
||||||
<a class="dropdown-item" href="#">Something else here</a>
|
<thead>
|
||||||
</div>
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Exchange</th>
|
||||||
|
<th>
|
||||||
|
<div class="card-actions float-end">
|
||||||
|
<div class="btn-group float-end" role="group">
|
||||||
|
<button type="button" class="btn btn-success btn-sm"
|
||||||
|
data-bs-toggle="modal" data-bs-target="#symbolModal" data-action="create_symbol">
|
||||||
|
<i class="bi bi-plus"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#symbols}}
|
||||||
|
<tr>
|
||||||
|
<td>{{.getName()}}</td>
|
||||||
|
<td>{{.getExchange().getName()}}</td>
|
||||||
|
<td>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="id" value="{{.getId()}}">
|
||||||
|
|
||||||
|
<div class="btn-toolbar pull-right">
|
||||||
|
<button type="button" class="btn btn-default btn-sm" data-toggle="modal"
|
||||||
|
data-target="#symbolModal"
|
||||||
|
data-action="modify_symbol"
|
||||||
|
data-id="{{.getId()}}"
|
||||||
|
data-name="{{.getName()}}"
|
||||||
|
data-type="{{.getType()}}"
|
||||||
|
{{#.getDeviceConfigurator().getConfiguration()}}
|
||||||
|
data-{{.getName()}}="{{.getString()}}"
|
||||||
|
{{/.getDeviceConfigurator().getConfiguration()}}
|
||||||
|
>
|
||||||
|
<span class="glyphicon glyphicon-pencil"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-danger btn-sm" name="action" value="remove_symbol">
|
||||||
|
<span class="glyphicon glyphicon-trash"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/symbols}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="card-title mb-0">Recent Movement</h5>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-sm">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Configuration</th>
|
|
||||||
<th>
|
|
||||||
<button class="btn btn-default btn-sm pull-right" data-toggle="modal"
|
|
||||||
data-target="#symbolModal"
|
|
||||||
data-action="create_symbol">
|
|
||||||
<span class="glyphicon glyphicon-plus"></span>
|
|
||||||
</button>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#symbols}}
|
|
||||||
<tr>
|
|
||||||
<td>{{.getName()}}</td>
|
|
||||||
<td>{{.getType()}}</td>
|
|
||||||
<td>{{.getDeviceConfig()}}</td>
|
|
||||||
<td>
|
|
||||||
<form method="POST">
|
|
||||||
<input type="hidden" name="id" value="{{.getId()}}">
|
|
||||||
|
|
||||||
<div class="btn-toolbar pull-right">
|
|
||||||
<button type="button" class="btn btn-default btn-sm" data-toggle="modal"
|
|
||||||
data-target="#symbolModal"
|
|
||||||
data-action="modify_symbol"
|
|
||||||
data-id="{{.getId()}}"
|
|
||||||
data-name="{{.getName()}}"
|
|
||||||
data-type="{{.getType()}}"
|
|
||||||
{{#.getDeviceConfigurator().getConfiguration()}}
|
|
||||||
data-{{.getName()}}="{{.getString()}}"
|
|
||||||
{{/.getDeviceConfigurator().getConfiguration()}}
|
|
||||||
>
|
|
||||||
<span class="glyphicon glyphicon-pencil"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-danger btn-sm" name="action" value="remove_symbol">
|
|
||||||
<span class="glyphicon glyphicon-trash"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/symbols}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!------------- MODALS --------------->
|
<!------------- MODALS --------------->
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function() {
|
||||||
initDynamicModalForm("symbolModal");
|
initDynamicModalForm("symbolModal");
|
||||||
|
|
||||||
|
$("#exchange").change(function() {
|
||||||
|
alert(this.value);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -84,24 +76,35 @@
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
|
||||||
<h4 class="modal-title">Symbol</h4>
|
<h4 class="modal-title">Symbol</h4>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="hidden" id="action" name="action">
|
<input type="hidden" id="action" name="action">
|
||||||
<input type="hidden" id="id" name="id">
|
<input type="hidden" id="id" name="id">
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label">Name:</label>
|
<div>
|
||||||
<input type="text" class="form-control" name="name">
|
<label class="control-label" for="exchange">Exchange:</label>
|
||||||
|
<select id="exchange" name="exchange" class="form-control">
|
||||||
|
<option></option>
|
||||||
|
{{#exchanges}}
|
||||||
|
<option value="{{.getId()}}">{{.getName()}}</option>
|
||||||
|
{{/exchanges}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="control-label" for="symbol">Symbol:</label>
|
||||||
|
<select id="symbol" name="symbol" class="form-control">
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="reset" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
<button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
<button type="submit" class="btn btn-primary">Save</button>
|
<button type="submit" class="btn btn-primary">Save</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue