Merge branch '46-add-missing-fields-to-the-add-page' into '22-integrate-api-and-frontend'

Resolve "Add missing fields to the add page"

See merge request padas/24ss-5430-web-and-data-eng/gruppe-3/datadash!45
This commit is contained in:
Erik Foris 2024-07-07 18:17:46 +02:00
commit 5390dc8400
5 changed files with 151 additions and 14 deletions

View File

@ -171,3 +171,30 @@ form :has(#url) {
.btn:disabled {
filter: var(--drop-shadow) grayscale(.5) brightness(.5);
}
#category[value="new"] {
display: none;
}
label[for="category"] {
width: 0;
user-select: none;
overflow: hidden;
}
span:has(#category) {
gap: unset;
}
#new-category-group:not(.hidden) {
display: flex;
justify-content: stretch;
gap: var(--gap-small);
width: 100%;
}
#new-category-group button {
background-image: url("./sort.svg");
background-size: contain;
background-origin: content-box;
}

View File

@ -27,7 +27,7 @@
</span>
<span id="author-box">
<label for="author">Author</label>
<label for="author">Author/provider</label>
<input type="text" name="author" id="author" required>
</span>
@ -48,6 +48,29 @@
<input type="url" name="url" id="url" required>
</span>
<span>
<label for="terms-of-use">Terms of Use URL</label>
<input type="url" name="terms-of-use" id="terms-of-use" required>
</span>
<span>
<label for="license">License</label>
<input type="text" name="license" id="license" required>
</span>
<span>
<label for="category">Category</label>
<select name="category" id="category" class="btn" required autocomplete="off">
<option value="" selected disabled hidden>Category</option>
<option value="new">New category</option>
<hr>
</select>
<span id="new-category-group" class="hidden">
<input type="text" id="new-category" size="2" required disabled autocomplete="off">
<button id="change-category-btn" class="btn flat" title="change category"></button>
</span>
</span>
<span id="full-description-box">
<label for="full-description">Full description</label>
<textarea name="full-description" id="full-description" spellcheck="true"></textarea>

View File

@ -7,10 +7,16 @@ const {
["is-dataset"]: isDatasetSwitch,
["short-description"]: shortDescriptionEntry,
url: urlEntry,
["terms-of-use"]: termsOfUseEntry,
license: licenseEntry,
category: categorySpinner,
["new-category"]: newCategoryEntry,
["change-category-btn"]: changeCategoryBtn,
["full-description"]: fullDescriptionEntry,
["btn-add"]: addBtn,
["btn-cancel"]: cancelBtn,
} = form.elements;
const newCategoryGroup = document.getElementById("new-category-group");
const validationListener = () => {
addBtn.disabled = !form.checkValidity();
@ -22,44 +28,127 @@ const validationListener = () => {
authorEntry,
shortDescriptionEntry,
urlEntry,
termsOfUseEntry,
licenseEntry,
newCategoryEntry,
fullDescriptionEntry,
].forEach(input => input.addEventListener("input", validationListener));
// Category spinner
const categorySpinnerSet = (...args) => {
if (args.length > 0) {
categorySpinner.value = args[0];
}
categorySpinner.setAttribute("value", categorySpinner.value);
if (categorySpinner.value == "new") {
newCategoryGroup.classList.remove("hidden");
newCategoryEntry.disabled = false;
newCategoryEntry.focus();
} else {
newCategoryGroup.classList.add("hidden");
newCategoryEntry.disabled = true;
}
};
const getCategory = () => {
return categorySpinner.value == "new"
? newCategoryEntry.value
: categorySpinner.value;
}
categorySpinner.addEventListener("input", e => {
categorySpinnerSet();
validationListener();
});
changeCategoryBtn.addEventListener("click", e => {
e.preventDefault();
categorySpinnerSet("");
validationListener();
});
let categoriesResponse = await fetch(`${location.origin}/api/v1/categories`);
let categories = [];
if (!categoriesResponse.ok) {
console.warn("Could not load categories!");
} else {
categories = await categoriesResponse.json();
for (const category of categories) {
let option = document.createElement("option");
option.value = category.id;
option.text = category.name;
categorySpinner.add(option);
}
}
// Form listeners
cancelBtn.addEventListener("click", () => {
window.location.href = location.origin;
})
form.addEventListener("submit", async e => {
e.preventDefault();
if (!form.reportValidity()) return;
if (!form.reportValidity()) {
addBtn.disabled = true;
return;
}
let categoryID = categorySpinner.value;
if (categoryID == "new") {
const newCategoryName = newCategoryEntry.value.trim();
if (!categories.map(c => c.name).includes(newCategoryName)) {
// Try to add the new category
const newCategoryResponse = await fetch(`/api/v1/categories`, {
method: "POST",
headers: { "Content-Type": "application/json;charset=utf-8" },
body: JSON.stringify({ name: newCategoryName }),
});
if (!newCategoryResponse.ok) {
newCategoryEntry.setCustomValidity(
`Could not create new category: ${newCategoryResponse.statusText}`
);
form.reportValidity();
return;
}
const newCategory = await newCategoryResponse.json();
categoryID = newCategory.id;
}
}
// Create the request body
const newContent = {
title: titleEntry.value,
author: authorEntry.value,
type: isDatasetSwitch.checked ? "API" : "DATASET",
abst: shortDescriptionEntry.value,
url: urlEntry.value,
termsOfUse: termsOfUseEntry.value,
licence: licenseEntry.value,
categorie: {
id: categoryID,
},
description: fullDescriptionEntry.value,
type: isDatasetSwitch.checked ? "API" : "DATASET",
categories: [],
};
console.debug(newContent);
// Don't allow several requests to be sent at the same time
addBtn.disabled = true;
let response = await fetch("/api/v1/datasets", {
method: "POST",
headers: { "Content-Type": "application/json;charset=utf-8" },
body: JSON.stringify(newContent),
headers: {
"Content-Type": "application/json;charset=utf-8"
}
});
let data = await response.json();
let dataset = new Dataset(data);
dataset.storageSetKey("created-locally", true);
if (response.ok) {
location.assign("/");
} else {

View File

@ -23,7 +23,7 @@ export default class Dataset {
return this.#datasets.get(id);
}
constructor({ abst: shortDescription, author, categorie, date, description, id, raiting, title, type, upvotes, url, votes, license, termsOfUse }) {
constructor({ abst: shortDescription, author, categorie, date, description, id, raiting, title, type, upvotes, url, votes, licence: license, termsOfUse }) {
this.#shortDescription = shortDescription;
this.#author = author;
this.#category = categorie;

View File

@ -24,12 +24,10 @@ const currentLocation = new URL(location.href);
if (currentLocation.searchParams.has("id")) {
const id = currentLocation.searchParams.get("id");
const response = await fetch(`${currentLocation.origin}/api/v1/datasets/id/${id}`);
console.dir(response);
if (response.ok) {
const data = await response.json();
dataset = new Dataset(data);
console.dir(data, dataset);
const dataset = new Dataset(data);
const upvoteComponent = dataset.createUpvoteComponent();
console.log(dataset.storageGet());