btcpayserver/BTCPayServer/Components/StoreWalletBalance/Default.cshtml

150 lines
7.8 KiB
Text

@using BTCPayServer.Abstractions.TagHelpers
@using BTCPayServer.Client.Models
@using BTCPayServer.TagHelpers
@model BTCPayServer.Components.StoreWalletBalance.StoreWalletBalanceViewModel
<div id="StoreWalletBalance-@Model.StoreId" class="widget store-wallet-balance">
<div class="d-flex gap-3 align-items-center justify-content-between mb-2">
<h6 text-translate="true">Wallet Balance</h6>
@if (Model.CryptoCode != Model.DefaultCurrency)
{
<div class="btn-group btn-group-sm gap-0 currency-toggle" role="group">
<input type="radio" class="btn-check" name="StoreWalletBalance-currency" id="StoreWalletBalance-currency_@Model.CryptoCode" value="@Model.CryptoCode" autocomplete="off" checked>
<label class="btn btn-outline-secondary px-2 py-1" for="StoreWalletBalance-currency_@Model.CryptoCode">@Model.CryptoCode</label>
<input type="radio" class="btn-check" name="StoreWalletBalance-currency" id="StoreWalletBalance-currency_@Model.DefaultCurrency" value="@Model.DefaultCurrency" autocomplete="off">
<label class="btn btn-outline-secondary px-2 py-1" for="StoreWalletBalance-currency_@Model.DefaultCurrency">@Model.DefaultCurrency</label>
</div>
}
</div>
<header class="mb-3">
@if (Model.Balance != null)
{
<div class="balance d-flex align-items-baseline gap-1">
<h3 class="d-inline-block me-1" data-balance="@Model.Balance" data-sensitive>@Model.Balance</h3>
<span class="text-secondary fw-semibold currency">@Model.CryptoCode</span>
</div>
}
@if (Model.Series != null)
{
<div class="btn-group only-for-js mt-1" role="group" aria-label="Period">
<input type="radio" class="btn-check" name="StoreWalletBalancePeriod-@Model.StoreId" id="StoreWalletBalancePeriodWeek-@Model.StoreId" value="@HistogramType.Week" @(Model.Type == HistogramType.Week ? "checked" : "")>
<label class="btn btn-link" for="StoreWalletBalancePeriodWeek-@Model.StoreId">1W</label>
<input type="radio" class="btn-check" name="StoreWalletBalancePeriod-@Model.StoreId" id="StoreWalletBalancePeriodMonth-@Model.StoreId" value="@HistogramType.Month" @(Model.Type == HistogramType.Month ? "checked" : "")>
<label class="btn btn-link" for="StoreWalletBalancePeriodMonth-@Model.StoreId">1M</label>
<input type="radio" class="btn-check" name="StoreWalletBalancePeriod-@Model.StoreId" id="StoreWalletBalancePeriodYear-@Model.StoreId" value="@HistogramType.Year" @(Model.Type == HistogramType.Year ? "checked" : "")>
<label class="btn btn-link" for="StoreWalletBalancePeriodYear-@Model.StoreId">1Y</label>
</div>
}
</header>
@if (Model.Series != null)
{
<div class="ct-chart"></div>
}
else if (Model.MissingWalletConfig)
{
<p>
We would like to show you a chart of your balance but you have not yet <a href="@Url.Action("SetupWallet", "UIStores", new { storeId = Model.StoreId, cryptoCode = Model.CryptoCode })">configured a wallet</a>.
</p>
}
else
{
<p>
We would like to show you a chart of your balance. Please set <a href="https://docs.btcpayserver.org/Deployment/ManualDeploymentExtended/#3-create-a-configuration-file" target="_blank" rel="noreferrer noopener">NBXPlorer's PostgreSQL connection string</a> to make this data available.
</p>
}
<script>
(function () {
const storeId = @Safe.Json(Model.StoreId);
const cryptoCode = @Safe.Json(Model.CryptoCode);
const defaultCurrency = @Safe.Json(Model.DefaultCurrency);
const divisibility = @Safe.Json(Model.CurrencyData.Divisibility);
let data = { series: @Safe.Json(Model.Series), labels: @Safe.Json(Model.Labels), balance: @Safe.Json(Model.Balance) };
let rate = null;
const id = `StoreWalletBalance-${storeId}`;
const baseUrl = @Safe.Json(Url.Action("WalletHistogram", "UIWallets", new { walletId = Model.WalletId, type = HistogramType.Week }));
const valueTransform = value => rate ? DashboardUtils.displayDefaultCurrency(value, rate, defaultCurrency, divisibility) : value
const labelCount = 6
const tooltip = Chartist.plugins.tooltip2({
template: '<div class="chartist-tooltip-value">{{value}}</div><div class="chartist-tooltip-line"></div>',
offset: {
x: 0,
y: -16
},
valueTransformFunction(value, label) {
return valueTransform(value) + ' ' + (rate ? defaultCurrency : cryptoCode)
}
})
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat
const dateFormatter = new Intl.DateTimeFormat('default', { month: 'short', day: 'numeric' })
const chartOpts = {
fullWidth: true,
showArea: true,
axisY: {
showLabel: false,
offset: 0
},
plugins: [tooltip]
};
const render = data => {
let { series, labels } = data;
const currency = rate ? defaultCurrency : cryptoCode;
document.querySelectorAll(`#${id} .currency`).forEach(c => c.innerText = currency)
document.querySelectorAll(`#${id} [data-balance]`).forEach(c => {
const value = Number.parseFloat(c.dataset.balance);
c.innerText = valueTransform(value)
});
if (!series) return;
const min = Math.min(...series);
const max = Math.max(...series);
const low = Math.max(min - ((max - min) / 5), 0);
const renderOpts = Object.assign({}, chartOpts, { low, axisX: {
labelInterpolationFnc(date, i) {
return i % labelEvery === 0 ? dateFormatter.format(new Date(date)) : null
}
} });
const pointCount = series.length;
const labelEvery = pointCount / labelCount;
new Chartist.Line(`#${id} .ct-chart`, {
labels: labels,
series: [series]
}, renderOpts);
};
const update = async type => {
const url = baseUrl.replace(/\/week$/gi, `/${type}`);
const response = await fetch(url);
if (response.ok) {
data = await response.json();
render(data);
}
};
render(data);
function addEventListeners() {
delegate('change', `#${id} [name="StoreWalletBalancePeriod-${storeId}"]`, async e => {
const type = e.target.value;
await update(type);
})
delegate('change', `#${id} .currency-toggle input`, async e => {
const { target } = e;
if (target.value === defaultCurrency) {
rate = await DashboardUtils.fetchRate(`${cryptoCode}_${defaultCurrency}`);
if (rate) render(data);
} else {
rate = null;
render(data);
}
});
}
if (document.readyState === "loading") {
window.addEventListener("DOMContentLoaded", addEventListeners);
} else {
addEventListeners();
}
})();
</script>
</div>