diff --git a/src/main/resources/static/add.css b/src/main/resources/static/add.css index e4cc24d..3946f18 100644 --- a/src/main/resources/static/add.css +++ b/src/main/resources/static/add.css @@ -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; +} diff --git a/src/main/resources/static/add.html b/src/main/resources/static/add.html index 30888c3..3c9f9c8 100644 --- a/src/main/resources/static/add.html +++ b/src/main/resources/static/add.html @@ -27,7 +27,7 @@ - + @@ -48,6 +48,29 @@ + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/add.js b/src/main/resources/static/add.js index 53292ad..819e55e 100644 --- a/src/main/resources/static/add.js +++ b/src/main/resources/static/add.js @@ -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 { diff --git a/src/main/resources/static/dataset.js b/src/main/resources/static/dataset.js index e7f0934..87984cd 100644 --- a/src/main/resources/static/dataset.js +++ b/src/main/resources/static/dataset.js @@ -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; diff --git a/src/main/resources/static/details.js b/src/main/resources/static/details.js index e770a5c..8c07154 100644 --- a/src/main/resources/static/details.js +++ b/src/main/resources/static/details.js @@ -18,12 +18,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(); const dataset = new Dataset(data); - console.dir(data, dataset); const upvoteComponent = dataset.createUpvoteComponent(); title.innerText = dataset.title;