Merge branch '33-add-ability-to-restrict-multiple-votes-in-frontend' into '32-add-paging-functionality'

Resolve "Add ability to restrict multiple votes in frontend"

See merge request padas/24ss-5430-web-and-data-eng/gruppe-3/datadash!30
This commit is contained in:
Erik Foris 2024-07-04 21:19:48 +02:00
commit 61fccc924f
5 changed files with 42 additions and 16 deletions

View File

@ -1,6 +1,6 @@
import {searchBarTimeout, searchSection, lastQuery} from "./main.js" import {searchBarTimeout, searchSection, lastQuery} from "./main.js"
import Dataset from "./dataset.js" import Dataset from "./dataset.js"
// TODO consider renaming this to "searchUtility.js"
export function fetchQuery(fetchString, clearResults) { export function fetchQuery(fetchString, clearResults) {
clearTimeout(searchBarTimeout); clearTimeout(searchBarTimeout);
fetch(fetchString) fetch(fetchString)
@ -24,8 +24,8 @@ function parseContent(content, clearResults) {
Array.from(searchSection.querySelectorAll(".datasets .dataset")).forEach(e => e.remove()); Array.from(searchSection.querySelectorAll(".datasets .dataset")).forEach(e => e.remove());
} }
for (const dataset of datasets) { for (const dataset of datasets) {
searchSection.querySelector(".datasets").appendChild(dataset.createDatasetHTMLElement()); searchSection.querySelector(".datasets")
.appendChild(dataset.createDatasetHTMLElement());
} }
} }
} }

View File

@ -36,6 +36,15 @@ export default class Dataset {
clone.querySelector("h3").innerText = this.#title; clone.querySelector("h3").innerText = this.#title;
clone.querySelector("p").innerText = this.#description; clone.querySelector("p").innerText = this.#description;
clone.querySelector("span").innerText = this.#upvotes; clone.querySelector("span").innerText = this.#upvotes;
let votedIDs = window.localStorage;
// depending on whether the button has been up/downvoted, its according button get disabled and hidden
if (votedIDs.getItem(this.#id)) {
let votedButton = clone.querySelector(votedIDs.getItem(this.#id)? ".upvote-btn":".downvote-btn");
votedButton.classList.add("isVoted");
votedButton.disabled = true;
let notVotedButton = clone.querySelector(votedIDs.getItem(this.#id)? ".downvote-btn":".upvote-btn");
notVotedButton.style.visibility = "hidden";
}
// Event Listeners // Event Listeners
clone.querySelector(".upvote-btn").addEventListener("click", () => { clone.querySelector(".upvote-btn").addEventListener("click", () => {
@ -48,4 +57,8 @@ export default class Dataset {
return clone; return clone;
} }
getID() {
return this.#id;
}
} }

View File

@ -81,6 +81,7 @@ header {
.hidden { .hidden {
display: none; display: none;
} }
#search-entry:focus-visible { #search-entry:focus-visible {
outline: none; outline: none;
} }
@ -117,6 +118,7 @@ header {
align-items: center; align-items: center;
gap: .5em; gap: .5em;
} }
/* Buttons */ /* Buttons */
.upvote-btn, .downvote-btn, #search-btn, #filter-btn, #sort-btn, #reset-tools-btn { .upvote-btn, .downvote-btn, #search-btn, #filter-btn, #sort-btn, #reset-tools-btn {
background: var(--icon-url) no-repeat; background: var(--icon-url) no-repeat;
@ -143,6 +145,7 @@ header {
--icon-url: url(looking-glass.svg); --icon-url: url(looking-glass.svg);
--icon-size: 1rem; --icon-size: 1rem;
} }
#filter-btn { #filter-btn {
--icon-url: url(filter.svg); --icon-url: url(filter.svg);
--icon-size: 1rem; --icon-size: 1rem;
@ -158,6 +161,10 @@ header {
--icon-size: 1rem; --icon-size: 1rem;
} }
.isVoted {
filter: brightness(1.75);
}
.divider { .divider {
width: .05rem; width: .05rem;
height: 1rem; height: 1rem;

View File

@ -5,10 +5,10 @@ const apiEndpoint = "/api/v1/datasets";
const baseURL = location.origin; const baseURL = location.origin;
const defaultPagingValue = 20; const defaultPagingValue = 20;
export const lastQuery = { export const lastQuery = {
url: "",
totalPages: 0, totalPages: 0,
currentPage: 0 currentPage: 0
}; };
const votedIDs = window.localStorage;
// definition of all buttons & sections // definition of all buttons & sections
const addButton = document.getElementById("add-btn"); const addButton = document.getElementById("add-btn");
@ -69,7 +69,7 @@ resetButton.addEventListener("click", () => {
updateSections(); updateSections();
}); });
// Consider moving this to datasets.js completely // Consider moving this to datasets.js completely // TODO: we dont need them, do we? there in dataset.js
const upvoteButtonClickListener = e => { const upvoteButtonClickListener = e => {
const entryID = e.target.parentElement.parentElement.dataset.id; const entryID = e.target.parentElement.parentElement.dataset.id;
vote(entryID, true); vote(entryID, true);
@ -78,7 +78,7 @@ for (const upvoteButton of upvoteButtons) {
upvoteButton.addEventListener("click", upvoteButtonClickListener); upvoteButton.addEventListener("click", upvoteButtonClickListener);
} }
// Consider moving this to datasets.js completely // Consider moving this to datasets.js completely // TODO: we dont need them, do we? there in dataset.js
const downvoteButtonClickListener = e => { const downvoteButtonClickListener = e => {
const entryID = e.target.parentElement.parentElement.dataset.id; const entryID = e.target.parentElement.parentElement.dataset.id;
vote(entryID, false); vote(entryID, false);
@ -89,7 +89,7 @@ for (const downvoteButton of downvoteButtons) {
// functions of the main page // functions of the main page
function navigateToAdd() { function navigateToAdd() {
window.location.href = "/add"; window.location.href = "/add"; //TODO: move to EventListner?
} }
function getFilterQuery() { function getFilterQuery() {
@ -146,15 +146,19 @@ export function vote(entryID, up) {
.then(resp => resp.json()) .then(resp => resp.json())
.then((data) => { .then((data) => {
console.log(data.upvotes); // TODO: remove, check einbauen: data.id === entryID? console.log(data.upvotes); // TODO: remove, check einbauen: data.id === entryID?
let dataset = document.querySelector('[data-id= ' + CSS.escape(entryID) + ']') let dataSets = document.querySelectorAll('[data-id= ' + CSS.escape(entryID) + ']');
dataset.querySelector("span").innerText = data.upvotes; for (const dataSetElement of dataSets) {
dataSetElement.querySelector("span").innerText = data.upvotes;
let votedButton = dataSetElement.querySelector(up? ".upvote-btn":".downvote-btn");
votedButton.classList.add("isVoted");
votedButton.disabled = true;
let notVotedButton = dataSetElement.querySelector(up? ".downvote-btn":".upvote-btn");
notVotedButton.style.visibility = "hidden";
votedIDs.setItem(dataSetElement.getAttribute("data-id"), up);
}
}); });
} }
function incrementPageCount() {
lastQuery.currentPage++;
}
// updates the page display. If no query is present, the initial page is shown, otherwise the search results. // updates the page display. If no query is present, the initial page is shown, otherwise the search results.
function updateSections() { function updateSections() {
if (searchBar.value === "" && sortButton.value === sortButton.querySelector("#default-sort").value if (searchBar.value === "" && sortButton.value === sortButton.querySelector("#default-sort").value
@ -197,7 +201,8 @@ function fetchInitialEntries() {
.then((data) => { .then((data) => {
const datasets = data.content.map(dataset => new Dataset(dataset)); const datasets = data.content.map(dataset => new Dataset(dataset));
for (const dataset of datasets) { for (const dataset of datasets) {
document.querySelector("#recents .datasets").appendChild(dataset.createDatasetHTMLElement()); document.querySelector("#recents .datasets")
.appendChild(dataset.createDatasetHTMLElement());
} }
}); });
@ -208,8 +213,9 @@ function fetchInitialEntries() {
fetch(topVotedQueryURL) fetch(topVotedQueryURL)
.then(resp => resp.json()) .then(resp => resp.json())
.then((data) => { .then((data) => {
let dataset = new Dataset(data.content[0]);
document.querySelector("#top .datasets") document.querySelector("#top .datasets")
.appendChild(new Dataset(data.content[0]).createDatasetHTMLElement()); .appendChild(dataset.createDatasetHTMLElement());
}); });
} }

View File

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DataDash</title> <title>DataDash</title>
<link rel="stylesheet" href="main.css"> <link rel="stylesheet" href="main.css">
<script type="module" src="main.js" defer></script> <script type="module" src="main.js" defer></script>
</head> </head>
<body> <body>
<div id="add-btn" title="Add a new API entry"></div> <div id="add-btn" title="Add a new API entry"></div>