feat: per-user click stats with aggregation
This commit is contained in:
parent
c7ce89c83f
commit
387cb97d22
70
index.html
70
index.html
@ -23,22 +23,41 @@ body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,s
|
|||||||
<section class="hero"><div class="container">
|
<section class="hero"><div class="container">
|
||||||
<h1>Сбор статистики нажатий</h1>
|
<h1>Сбор статистики нажатий</h1>
|
||||||
<p>Нажимайте на кнопки — счётчик будет сохраняться в браузере.</p>
|
<p>Нажимайте на кнопки — счётчик будет сохраняться в браузере.</p>
|
||||||
|
<div style="margin-top:12px;">
|
||||||
|
<label for="userInput" style="margin-right:8px;">Пользователь:</label>
|
||||||
|
<input id="userInput" type="text" placeholder="Введите имя" style="padding:4px 8px; border:1px solid var(--gray-500); border-radius:4px;">
|
||||||
|
<button id="setUserBtn" class="btn" style="padding:4px 12px; font-size:14px;">Установить</button>
|
||||||
|
</div>
|
||||||
</div></section>
|
</div></section>
|
||||||
<section class="section"><div class="container">
|
<section class="section"><div class="container">
|
||||||
<h2>Кнопки</h2>
|
<h2>Кнопки</h2>
|
||||||
<div id="buttons"></div>
|
<div id="buttons"></div>
|
||||||
|
<div id="aggregation" style="margin-top:24px;"></div>
|
||||||
</div></section>
|
</div></section>
|
||||||
<script>
|
<script>
|
||||||
// Список кнопок, которые нужно отслеживать
|
// Список кнопок, которые нужно отслеживать
|
||||||
const buttonLabels = ['Кнопка A', 'Кнопка B', 'Кнопка C'];
|
const buttonLabels = ['Кнопка A', 'Кнопка B', 'Кнопка C'];
|
||||||
// Хранилище счётчиков в localStorage
|
// Ключ в localStorage для всех статистик
|
||||||
const storageKey = 'clickStats';
|
const storageKey = 'clickStats';
|
||||||
let stats = JSON.parse(localStorage.getItem(storageKey)) || {};
|
// Текущий пользователь (по умолчанию «guest»)
|
||||||
// Инициализируем нули, если кнопки новые
|
let currentUser = localStorage.getItem('currentUser') || 'guest';
|
||||||
buttonLabels.forEach(label => { if(!(label in stats)) stats[label]=0; });
|
// Объект {user: {label: count}}
|
||||||
// Сохраняем при загрузке (на случай новых кнопок)
|
let allStats = JSON.parse(localStorage.getItem(storageKey)) || {};
|
||||||
localStorage.setItem(storageKey, JSON.stringify(stats));
|
// Инициализируем запись для текущего пользователя, если её нет
|
||||||
|
if (!allStats[currentUser]) {
|
||||||
|
allStats[currentUser] = {};
|
||||||
|
buttonLabels.forEach(label => allStats[currentUser][label] = 0);
|
||||||
|
}
|
||||||
|
// Убедимся, что у всех пользователей присутствуют новые кнопки
|
||||||
|
Object.values(allStats).forEach(userStats => {
|
||||||
|
buttonLabels.forEach(label => {
|
||||||
|
if (!(label in userStats)) userStats[label] = 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
localStorage.setItem(storageKey, JSON.stringify(allStats));
|
||||||
const container = document.getElementById('buttons');
|
const container = document.getElementById('buttons');
|
||||||
|
function renderButtons() {
|
||||||
|
container.innerHTML = '';
|
||||||
buttonLabels.forEach((label, i) => {
|
buttonLabels.forEach((label, i) => {
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = 'btn';
|
btn.className = 'btn';
|
||||||
@ -46,15 +65,46 @@ buttonLabels.forEach((label,i)=>{
|
|||||||
const cnt = document.createElement('span');
|
const cnt = document.createElement('span');
|
||||||
cnt.className = 'counter';
|
cnt.className = 'counter';
|
||||||
cnt.id = 'cnt-' + i;
|
cnt.id = 'cnt-' + i;
|
||||||
cnt.textContent = stats[label];
|
cnt.textContent = allStats[currentUser][label];
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
stats[label]++;
|
allStats[currentUser][label]++;
|
||||||
cnt.textContent = stats[label];
|
cnt.textContent = allStats[currentUser][label];
|
||||||
localStorage.setItem(storageKey, JSON.stringify(stats));
|
localStorage.setItem(storageKey, JSON.stringify(allStats));
|
||||||
|
renderAggregation();
|
||||||
});
|
});
|
||||||
container.appendChild(btn);
|
container.appendChild(btn);
|
||||||
container.appendChild(cnt);
|
container.appendChild(cnt);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
function renderAggregation() {
|
||||||
|
const aggDiv = document.getElementById('aggregation');
|
||||||
|
const totals = {};
|
||||||
|
buttonLabels.forEach(label => totals[label] = 0);
|
||||||
|
Object.values(allStats).forEach(userStats => {
|
||||||
|
buttonLabels.forEach(label => {
|
||||||
|
totals[label] += userStats[label] || 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
aggDiv.innerHTML = '<h3>Общая статистика</h3>' +
|
||||||
|
buttonLabels.map(label => `<div>${label}: <span class="counter">${totals[label]}</span></div>`).join('');
|
||||||
|
}
|
||||||
|
// UI для установки пользователя
|
||||||
|
document.getElementById('setUserBtn').addEventListener('click', () => {
|
||||||
|
const input = document.getElementById('userInput').value.trim();
|
||||||
|
if (input) {
|
||||||
|
currentUser = input;
|
||||||
|
localStorage.setItem('currentUser', currentUser);
|
||||||
|
if (!allStats[currentUser]) {
|
||||||
|
allStats[currentUser] = {};
|
||||||
|
buttonLabels.forEach(label => allStats[currentUser][label] = 0);
|
||||||
|
}
|
||||||
|
renderButtons();
|
||||||
|
renderAggregation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Initial render
|
||||||
|
renderButtons();
|
||||||
|
renderAggregation();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user