Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions client/scripts/utilities/pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Renders a Bootstrap pagination control into `container` and wires up click handlers.
*
* @param {HTMLElement} container - Element to render the pagination nav into.
* @param {number} currentPage - The currently active page (1-indexed).
* @param {number} totalPages - Total number of pages.
* @param {(page: number) => void} onPageChange - Called with the new page number when the user navigates.
*/
export default function renderPagination (container, currentPage, totalPages, onPageChange) {
if (totalPages <= 1) {
container.innerHTML = '';
return;
}

const shiftLength = window.screen.width > 992 ? 10 : 5;
const shift = shiftLength * Math.floor((currentPage - 1) / shiftLength);
const rangeStart = shift;
const rangeEnd = Math.min(shift + shiftLength, totalPages);

const pageLinks = Array(rangeEnd - rangeStart).fill().map((_, idx) => {
const page = rangeStart + idx + 1;
const isActive = page === currentPage;
return `
<li class="page-item">
<a class="page-link ${isActive ? 'active' : ''}" href="#" data-page="${page}">${page}</a>
</li>
`;
}).join('');

container.innerHTML = `
<nav aria-label="pagination">
<ul class="pagination justify-content-center">
<li class="page-item">
<a class="page-link" href="#" aria-label="First" data-page="1">&laquo;</a>
</li>
<li class="page-item">
<a class="page-link" href="#" aria-label="Previous" data-page="${Math.max(1, currentPage - 1)}">&lsaquo;</a>
</li>
${pageLinks}
<li class="page-item">
<a class="page-link" href="#" aria-label="Next" data-page="${Math.min(totalPages, currentPage + 1)}">&rsaquo;</a>
</li>
<li class="page-item">
<a class="page-link" href="#" aria-label="Last" data-page="${totalPages}">&raquo;</a>
</li>
</ul>
</nav>
`;

for (const link of container.querySelectorAll('a.page-link')) {
link.addEventListener('click', function (event) {
event.preventDefault();
const page = Number(this.dataset.page);
if (page !== currentPage) {
onPageChange(page);
}
});
}
}
1 change: 1 addition & 0 deletions client/user/stars/bonuses.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
</div>
</div>
<div id="bonus-list"></div>
<div id="bonus-pagination" class="mt-3"></div>
</div>

<div class="modal" id="clear-starred-modal" tabindex="-1">
Expand Down
54 changes: 41 additions & 13 deletions client/user/stars/bonuses.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
import { downloadQuestionsAsText, downloadBonusesAsCSV, downloadQuestionsAsJSON } from '../../scripts/download.js';
import star from '../../scripts/auth/star.js';
import getBonusPartLabel from '../../scripts/utilities/get-bonus-part-label.js';
import renderPagination from '../../scripts/utilities/pagination.js';

const PAGE_SIZE = 20;

const bonuses = await star.getStarredBonuses();
const bonusList = document.getElementById('bonus-list');
const paginationContainer = document.getElementById('bonus-pagination');
document.getElementById('starred-number').innerText = bonuses.length;

for (const bonus of bonuses) {
const totalPages = Math.max(1, Math.ceil(bonuses.length / PAGE_SIZE));

function attachStarListener (bonus) {
document.getElementById(`star-bonus-${bonus._id}`).addEventListener('click', async function () {
if (this.classList.contains('selected')) {
this.innerHTML = star.unstarredSvg;
star.unstarBonus(bonus._id);
this.classList.toggle('selected');
} else if (await star.starBonus(bonus._id)) {
this.innerHTML = star.starredSvg;
this.classList.toggle('selected');
}
});
}

function renderBonusCard (bonus) {
let cardBody = '';
for (let i = 0; i < bonus.parts.length; i++) {
cardBody += `
Expand All @@ -16,7 +35,7 @@ for (const bonus of bonuses) {
`;
}

bonusList.innerHTML += `
return `
<div class="card my-2">
<div class="card-header d-flex justify-content-between">
<b class="clickable" data-bs-toggle="collapse" data-bs-target="#question-${bonus._id}" aria-expanded="true">
Expand All @@ -42,19 +61,28 @@ for (const bonus of bonuses) {
`;
}

for (const bonus of bonuses) {
document.getElementById(`star-bonus-${bonus._id}`).addEventListener('click', async function () {
if (this.classList.contains('selected')) {
this.innerHTML = star.unstarredSvg;
star.unstarBonus(bonus._id);
this.classList.toggle('selected');
} else if (await star.starBonus(bonus._id)) {
this.innerHTML = star.starredSvg;
this.classList.toggle('selected');
}
});
function renderPage (page) {
const start = (page - 1) * PAGE_SIZE;
const pageBonuses = bonuses.slice(start, start + PAGE_SIZE);

bonusList.innerHTML = pageBonuses.length > 0
? pageBonuses.map(renderBonusCard).join('')
: '<p class="text-muted">You have no starred bonuses.</p>';

for (const bonus of pageBonuses) {
attachStarListener(bonus);
}

renderPagination(paginationContainer, page, totalPages, goToPage);
}

function goToPage (page) {
renderPage(page);
bonusList.scrollIntoView({ behavior: 'smooth', block: 'start' });
}

renderPage(1);

document.getElementById('confirm').addEventListener('click', async function () {
await star.clearStarredBonuses();
window.location.reload();
Expand Down
1 change: 1 addition & 0 deletions client/user/stars/tossups.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
</div>
</div>
<div id="tossup-list"></div>
<div id="tossup-pagination" class="mt-3"></div>
</div>

<div class="modal" id="clear-starred-modal" tabindex="-1">
Expand Down
52 changes: 38 additions & 14 deletions client/user/stars/tossups.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
import { downloadQuestionsAsText, downloadTossupsAsCSV, downloadQuestionsAsJSON } from '../../scripts/download.js';
import star from '../../scripts/auth/star.js';
import renderPagination from '../../scripts/utilities/pagination.js';

const PAGE_SIZE = 20;

const tossups = await star.getStarredTossups();
const tossupList = document.getElementById('tossup-list');
const paginationContainer = document.getElementById('tossup-pagination');
document.getElementById('starred-number').innerText = tossups.length;

for (const tossup of tossups) {
tossupList.innerHTML += `
const totalPages = Math.max(1, Math.ceil(tossups.length / PAGE_SIZE));

function attachStarListener (tossup) {
document.getElementById(`star-tossup-${tossup._id}`).addEventListener('click', async function () {
if (this.classList.contains('selected')) {
this.innerHTML = star.unstarredSvg;
star.unstarTossup(tossup._id);
this.classList.toggle('selected');
} else if (await star.starTossup(tossup._id)) {
this.innerHTML = star.starredSvg;
this.classList.toggle('selected');
}
});
}

function renderPage (page) {
const start = (page - 1) * PAGE_SIZE;
const pageTossups = tossups.slice(start, start + PAGE_SIZE);

tossupList.innerHTML = pageTossups.length > 0
? pageTossups.map(tossup => `
<div class="card mb-2">
<div class="card-header d-flex justify-content-between">
<b class="clickable" data-bs-toggle="collapse" data-bs-target="#question-${tossup._id}" aria-expanded="true">
Expand All @@ -30,22 +53,23 @@ for (const tossup of tossups) {
</div>
</div>
</div>
`;
`).join('')
: '<p class="text-muted">You have no starred tossups.</p>';

for (const tossup of pageTossups) {
attachStarListener(tossup);
}

renderPagination(paginationContainer, page, totalPages, goToPage);
}

for (const tossup of tossups) {
document.getElementById(`star-tossup-${tossup._id}`).addEventListener('click', async function () {
if (this.classList.contains('selected')) {
this.innerHTML = star.unstarredSvg;
star.unstarTossup(tossup._id);
this.classList.toggle('selected');
} else if (await star.starTossup(tossup._id)) {
this.innerHTML = star.starredSvg;
this.classList.toggle('selected');
}
});
function goToPage (page) {
renderPage(page);
tossupList.scrollIntoView({ behavior: 'smooth', block: 'start' });
}

renderPage(1);

document.getElementById('confirm').addEventListener('click', async function () {
await star.clearStarredTossups();
window.location.reload();
Expand Down
Loading