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;