Use consistent RUB formatting: integers with narrow no-break space
Add formatRUB() helper that formats amounts without decimals and uses U+202F (narrow no-break space) as thousands separator and before ₽ symbol. This prevents unwanted line breaks within currency amounts. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3e0726d34f
commit
fbbf94c3c4
31
app.js
31
app.js
@ -1,3 +1,10 @@
|
||||
// Format RUB amount: no decimals, thin space (U+2009) as thousands separator
|
||||
function formatRUB(amount) {
|
||||
return Math.round(amount)
|
||||
.toLocaleString('ru-RU')
|
||||
.replace(/\u00a0/g, '\u202F');
|
||||
}
|
||||
|
||||
// Initialize the chart
|
||||
const chartDom = document.getElementById('chart-container');
|
||||
const myChart = echarts.init(chartDom);
|
||||
@ -44,9 +51,9 @@ function navigateToHistoryState(state) {
|
||||
option.series.data = state.data;
|
||||
|
||||
if (state.contextName) {
|
||||
option.graphic.elements[0].style.text = `${russianMonth}\n${state.contextName}\n${state.total.toFixed(0).toLocaleString()} ₽`;
|
||||
option.graphic.elements[0].style.text = `${russianMonth}\n${state.contextName}\n${formatRUB(state.total)}\u202F₽`;
|
||||
} else {
|
||||
option.graphic.elements[0].style.text = russianMonth + '\n' + state.total.toFixed(0).toLocaleString() + ' ₽';
|
||||
option.graphic.elements[0].style.text = russianMonth + '\n' + formatRUB(state.total) + '\u202F₽';
|
||||
}
|
||||
|
||||
myChart.setOption(option, { replaceMerge: ['series'] });
|
||||
@ -687,13 +694,13 @@ function renderChart(data) {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function(info) {
|
||||
const value = info.value.toLocaleString();
|
||||
const value = formatRUB(info.value);
|
||||
const name = info.name;
|
||||
|
||||
// Calculate percentage of total
|
||||
const percentage = ((info.value / sunburstData.total) * 100).toFixed(1);
|
||||
|
||||
return `${name}<br/>Amount: ${value} RUB<br/>Percentage: ${percentage}%`;
|
||||
return `${name}<br/>Amount: ${value}\u202F₽<br/>Percentage: ${percentage}%`;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -705,7 +712,7 @@ function renderChart(data) {
|
||||
top: 'middle',
|
||||
z: 100,
|
||||
style: {
|
||||
text: russianMonth + '\n' + sunburstData.total.toFixed(0).toLocaleString() + ' ₽',
|
||||
text: russianMonth + '\n' + formatRUB(sunburstData.total) + '\u202F₽',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
@ -907,7 +914,7 @@ function renderChart(data) {
|
||||
|
||||
// Update the center text to show the drilled-down category
|
||||
const russianMonth = getRussianMonthName(document.getElementById('month-select').value);
|
||||
option.graphic.elements[0].style.text = `${russianMonth}\n${params.name}\n${params.value.toFixed(0).toLocaleString()} ₽`;
|
||||
option.graphic.elements[0].style.text = `${russianMonth}\n${params.name}\n${formatRUB(params.value)}\u202F₽`;
|
||||
|
||||
myChart.setOption(option, { replaceMerge: ['series'] });
|
||||
|
||||
@ -1679,9 +1686,9 @@ async function selectMonth(index) {
|
||||
// Update the total amount in the center text
|
||||
const russianMonth = getRussianMonthName(month);
|
||||
if (targetName) {
|
||||
option.graphic.elements[0].style.text = `${russianMonth}\n${targetName}\n${targetTotal.toFixed(0).toLocaleString()} ₽`;
|
||||
option.graphic.elements[0].style.text = `${russianMonth}\n${targetName}\n${formatRUB(targetTotal)}\u202F₽`;
|
||||
} else {
|
||||
option.graphic.elements[0].style.text = russianMonth + '\n' + targetTotal.toFixed(0).toLocaleString() + ' ₽';
|
||||
option.graphic.elements[0].style.text = russianMonth + '\n' + formatRUB(targetTotal) + '\u202F₽';
|
||||
}
|
||||
|
||||
myChart.setOption({
|
||||
@ -2098,7 +2105,7 @@ function setupHoverEvents(sunburstData, contextName = null) {
|
||||
|
||||
const amountSpan = document.createElement('span');
|
||||
amountSpan.className = 'top-item-amount';
|
||||
amountSpan.textContent = item.value.toLocaleString() + ' ₽';
|
||||
amountSpan.textContent = formatRUB(item.value) + '\u202F₽';
|
||||
itemDiv.appendChild(amountSpan);
|
||||
topItemsElement.appendChild(itemDiv);
|
||||
});
|
||||
@ -2125,7 +2132,7 @@ function setupHoverEvents(sunburstData, contextName = null) {
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="hover-amount">${value.toLocaleString()} ₽</span>
|
||||
<span class="hover-amount">${formatRUB(value)}\u202F₽</span>
|
||||
`;
|
||||
// Add click handler to header eye button
|
||||
const headerEyeBtn = detailsHeader.querySelector('.header-eye-btn');
|
||||
@ -2166,7 +2173,7 @@ function setupHoverEvents(sunburstData, contextName = null) {
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="hover-amount">${sunburstData.total.toLocaleString()} ₽</span>
|
||||
<span class="hover-amount">${formatRUB(sunburstData.total)}\u202F₽</span>
|
||||
`;
|
||||
|
||||
// Add click handler to header eye button
|
||||
@ -2518,7 +2525,7 @@ function renderTransactionTable() {
|
||||
let value = transaction.originalRow[col];
|
||||
|
||||
if ((col === 'amount_rub' || col === 'amount_original') && typeof value === 'number') {
|
||||
value = value.toLocaleString();
|
||||
value = formatRUB(value);
|
||||
}
|
||||
|
||||
td.textContent = value !== undefined && value !== null ? value : '';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user