diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/controler/CategoryController.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/controler/CategoryController.java
index 06454ed..5f725a3 100644
--- a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/controler/CategoryController.java
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/controler/CategoryController.java
@@ -1,18 +1,43 @@
package de.uni_passau.fim.PADAS.group3.DataDash.controler;
+import java.util.List;
+import java.util.UUID;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import de.uni_passau.fim.PADAS.group3.DataDash.model.Category;
+import de.uni_passau.fim.PADAS.group3.DataDash.model.CategoryDto;
+import de.uni_passau.fim.PADAS.group3.DataDash.model.CategoryService;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+
@RestController
@RequestMapping("/api/v1/categories")
public class CategoryController {
+ @Autowired
+ private CategoryService categoryService;
+
@GetMapping
- public Category[] getMethodName() {
- return Category.values();
+ public List getMethodName() {
+ return categoryService.getAllCategories() ;
}
+
+ @GetMapping("/id/{id}")
+ public CategoryDto getMethodName(@PathVariable("id") UUID id) {
+ return categoryService.getCategoryById(id);
+ }
+
+ @PostMapping
+ public String postMethodName(@RequestBody CategoryDto dto) {
+ categoryService.addCategory(dto);
+ return null;
+ }
+
+
}
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/controler/PageController.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/controler/PageController.java
new file mode 100644
index 0000000..f1a0f6f
--- /dev/null
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/controler/PageController.java
@@ -0,0 +1,12 @@
+package de.uni_passau.fim.PADAS.group3.DataDash.controler;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+
+@Controller
+public class PageController {
+ @GetMapping("/add")
+ public String getAddPage() {
+ return "add";
+ }
+}
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/Category.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/Category.java
index b7cc30e..4246baa 100644
--- a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/Category.java
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/Category.java
@@ -1,14 +1,45 @@
package de.uni_passau.fim.PADAS.group3.DataDash.model;
-public enum Category {
- HEALTH,
- ENVIRONMENT,
- ECONOMY,
- POLITICS,
- TECHNOLOGY,
- SPORTS,
- SCIENCE,
- CULTURE,
- EDUCATION,
- OTHER
+import java.util.List;
+import java.util.UUID;
+
+import org.springframework.context.annotation.Lazy;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.OneToMany;
+
+@Entity
+public class Category {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private UUID id;
+
+ private String name;
+
+ @Lazy
+ @OneToMany(mappedBy = "categorie")
+ private List datasets;
+
+ public Category(String name) {
+ this.name = name;
+ }
+
+ public Category() {
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
}
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryDto.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryDto.java
new file mode 100644
index 0000000..a21d0cf
--- /dev/null
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryDto.java
@@ -0,0 +1,27 @@
+package de.uni_passau.fim.PADAS.group3.DataDash.model;
+
+import java.util.UUID;
+
+public class CategoryDto {
+
+ private String name;
+ private UUID id;
+
+ public CategoryDto() {
+ }
+
+ CategoryDto(String name, UUID id) {
+ this.name = name;
+ this.id = id;
+ }
+
+
+ public String getName() {
+ return name;
+ }
+
+ public UUID getId() {
+ return id;
+ }
+}
+
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryDtoMapper.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryDtoMapper.java
new file mode 100644
index 0000000..e7ecc10
--- /dev/null
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryDtoMapper.java
@@ -0,0 +1,10 @@
+package de.uni_passau.fim.PADAS.group3.DataDash.model;
+
+public class CategoryDtoMapper {
+
+ public static CategoryDto toDto(Category category) {
+ CategoryDto dto = new CategoryDto(category.getName(), category.getId());
+ return dto;
+ }
+
+}
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryRepository.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryRepository.java
new file mode 100644
index 0000000..a0b67e5
--- /dev/null
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryRepository.java
@@ -0,0 +1,20 @@
+package de.uni_passau.fim.PADAS.group3.DataDash.model;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+
+public interface CategoryRepository extends JpaRepository{
+
+ Category getCategoryById(UUID id);
+
+ @SuppressWarnings("null")
+ List findAll();
+ List findByName(String name);
+ @SuppressWarnings("null")
+ Optional findById(UUID id);
+
+}
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryService.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryService.java
new file mode 100644
index 0000000..ba36dc0
--- /dev/null
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/CategoryService.java
@@ -0,0 +1,32 @@
+package de.uni_passau.fim.PADAS.group3.DataDash.model;
+
+import org.springframework.stereotype.Service;
+import java.util.List;
+import java.util.UUID;
+
+
+@Service
+public class CategoryService {
+ private CategoryRepository categoryRepository;
+
+ public CategoryService(CategoryRepository categoryRepository) {
+ this.categoryRepository = categoryRepository;
+ }
+
+ public void addCategory(CategoryDto category) {
+ Category cat = new Category(category.getName());
+ categoryRepository.save(cat);
+ }
+
+ public List getAllCategories() {
+ List tmp = categoryRepository.findAll();
+ List s = tmp.stream().map(CategoryDtoMapper::toDto).toList();
+ return s;
+ }
+
+ public CategoryDto getCategoryById(UUID id) {
+ return CategoryDtoMapper.toDto(categoryRepository.getCategoryById(id));
+ }
+
+
+}
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/Dataset.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/Dataset.java
index e59ee08..91f71a2 100644
--- a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/Dataset.java
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/Dataset.java
@@ -11,6 +11,7 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
+import jakarta.persistence.ManyToOne;
@Entity
public class Dataset {
@@ -39,7 +40,8 @@ public class Dataset {
private int upvotes;
private URL url;
- @Enumerated(EnumType.STRING)
+
+ @ManyToOne
private Category categorie;
public Dataset(String title, String abst, String description, String author, URL url, Category categories, Type type) {
@@ -51,7 +53,7 @@ public class Dataset {
setAbst(abst);
setDescription(description);
setAuthor(author);
- setDate(LocalDate.now());
+ setDate(LocalDate.now());
setCategorie(categories);
setType(type);
setUrl(url);
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/DatasetService.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/DatasetService.java
index 43a0cb0..6694907 100644
--- a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/DatasetService.java
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/DatasetService.java
@@ -1,5 +1,6 @@
package de.uni_passau.fim.PADAS.group3.DataDash.model;
+import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@@ -12,9 +13,11 @@ import org.springframework.data.domain.Page;
@Service
public class DatasetService {
private dataRepository datasetRepository;
+ private CategoryRepository categoryRepository;
- public DatasetService(dataRepository datasetRepository) {
+ public DatasetService(dataRepository datasetRepository, CategoryRepository categoryRepository) {
this.datasetRepository = datasetRepository;
+ this.categoryRepository = categoryRepository;
}
public List getAllDatasets() {
@@ -26,6 +29,7 @@ public class DatasetService {
}
public void addDataset(Dataset dataset) {
+ dataset.setDate(LocalDate.now());
datasetRepository.save(dataset);
}
@@ -93,9 +97,14 @@ public class DatasetService {
public Page searchByOptionalCriteria(String search, String categories, String type, Pageable pageable) {
//TODO: make it not Crash
- Category category = categories.equals("%") ? null : Category.valueOf(categories);
+ //TODO: make it do useful stuff
+ Category category = categories.equals("%") ? null : categoryRepository.getCategoryById(UUID.fromString(categories)) ;
Type t = type.equals("%") ? null : Type.valueOf(type);
- return datasetRepository.searchByOptionalCriteria(Optional.ofNullable(search), Optional.ofNullable(category), Optional.ofNullable(t),pageable);
+ if(category == null){
+ return datasetRepository.searchByOptionalCriteria(Optional.ofNullable(search), Optional.ofNullable(t),pageable);
+ }
+ return datasetRepository.searchByOptionalCriteriaWithCategory(Optional.ofNullable(search), category, Optional.ofNullable(t),pageable);
}
+
}
\ No newline at end of file
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/LoadDummyDatabase.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/LoadDummyDatabase.java
index 42de661..18d9085 100644
--- a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/LoadDummyDatabase.java
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/LoadDummyDatabase.java
@@ -1,6 +1,7 @@
package de.uni_passau.fim.PADAS.group3.DataDash.model;
import java.util.List;
+import java.util.Random;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
@@ -15,15 +16,21 @@ public class LoadDummyDatabase {
private static final org.slf4j.Logger log = LoggerFactory.getLogger(LoadDummyDatabase.class);
@Bean
- CommandLineRunner initDatabase(dataRepository repository) {
+ CommandLineRunner initDatabase(dataRepository repository, CategoryRepository categoryRepository) {
return args -> {
for (int i = 0; i < 100; i++) {
- Dataset dataset = new Dataset("Title" + i, "Abst" + i, "Description" + i, "Author" + i,null, Category.EDUCATION, Type.API);
- repository.save(dataset);
+ Category category = new Category("Category" + i);
+ log.info("Preloading" + categoryRepository.save(category));
+
+ Dataset dataset = new Dataset("Title" + i, "Abst" + i, "Description" + i, "Author" + i,null, category, Type.API);
+ for (int j = 0; j < new Random().nextInt(50); j++) {
+ dataset.upvote();
+ }
log.info("Preloading" + repository.save(dataset));
+ log.info("Preloading" + categoryRepository.save(category));
}
List s = repository.findByTitleLike("%Title%");
log.info("Found Entry with ID: " + s.get(1).getId());};
diff --git a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/dataRepository.java b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/dataRepository.java
index 17fde64..130415d 100644
--- a/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/dataRepository.java
+++ b/src/main/java/de/uni_passau/fim/PADAS/group3/DataDash/model/dataRepository.java
@@ -65,10 +65,19 @@ public interface dataRepository extends JpaRepository {
"((LOWER(d.title) LIKE LOWER(:search)) OR " +
"(LOWER(d.description) LIKE LOWER(:search)) OR " +
"(LOWER(d.author) LIKE LOWER(:search))) AND" +
- "(:categorie IS NULL OR d.categorie = :categorie) AND" +
+ "(d.categorie = :categorie) AND" +
"(:type IS NULL OR d.type = :type)")
- Page searchByOptionalCriteria(@Param("search") Optional search,
- @Param("categorie") Optional categories,
+ Page searchByOptionalCriteriaWithCategory(@Param("search") Optional search,
+ @Param("categorie") Category categories,
@Param("type") Optional type,
Pageable pageable);
-}
\ No newline at end of file
+
+ @Query("SELECT d FROM Dataset d WHERE " +
+ "((LOWER(d.title) LIKE LOWER(:search)) OR " +
+ "(LOWER(d.description) LIKE LOWER(:search)) OR " +
+ "(LOWER(d.author) LIKE LOWER(:search))) AND" +
+ "(:type IS NULL OR d.type = :type)")
+ Page searchByOptionalCriteria(@Param("search") Optional search,
+ @Param("type") Optional type,
+ Pageable pageable);
+}
diff --git a/src/main/resources/static/add.css b/src/main/resources/static/add.css
index 4361843..6d62a6f 100644
--- a/src/main/resources/static/add.css
+++ b/src/main/resources/static/add.css
@@ -4,20 +4,6 @@
--accent-color: oklch(65.33% 0.158 247.76);
}
-form label:after {
- content: ":";
-}
-
-form :is(input[type=text], textarea) {
- background-color: var(--fg-color);
- border: none;
- border-radius: .25lh;
- color: var(--text-color, white);
- padding: .5em;
- font-family: sans-serif;
-}
-
-/* quick info box */
form {
display: grid;
grid-template-columns: 1fr 1fr auto;
@@ -30,17 +16,57 @@ form > * {
gap: 1rem;
}
-form :is(input[type=text], textarea) {
+form label:after {
+ content: ":";
+}
+
+/* text entries */
+form :is(input[type=text], input[type=url], textarea) {
+ background-color: var(--fg-color);
+ border: none;
+ border-radius: .25lh;
+ color: var(--text-color, white);
+ padding: .5em;
+ font-family: sans-serif;
flex-grow: 1;
}
+/* focus outlines */
+:is(form :is(input[type=text], input[type=url], textarea), .btn):focus-visible {
+ outline: 2px solid var(--accent-color);
+}
+
+.btn, #is-dataset-toggle {
+ transition: outline ease-in 100ms;
+}
+
+/* input validation */
+form :is(input[type=text], input[type=url], textarea):user-valid {
+ --validation-color: lime;
+}
+
+form :is(input[type=text], input[type=url], textarea):user-invalid {
+ --validation-color: red;
+ outline-style: solid;
+}
+
+form :is(input[type=text], input[type=url], textarea):is(:user-valid, :user-invalid) {
+ outline-color: var(--validation-color);
+}
+
+form :is(input[type=text], input[type=url], textarea):is(:user-valid, :user-invalid):not(:focus-visible) {
+ outline-width: 1px;
+}
+
/* switch */
label:has(#is-dataset) {
gap: 0;
}
#is-dataset {
- display: none;
+ top: -100vh;
+ left: -100vw;
+ position: absolute;
}
#is-dataset-toggle {
@@ -66,6 +92,11 @@ label:has(#is-dataset) {
box-sizing: border-box;
background-color: var(--text-color);
transition: inset-inline ease-out 50ms;
+ filter: drop-shadow(rgba(0, 0, 0, .8) 0 0 .25rem);
+}
+
+#is-dataset:focus-visible + #is-dataset-toggle {
+ outline: 2px solid var(--accent-color);
}
#is-dataset:not(:checked) + #is-dataset-toggle::before {
@@ -78,7 +109,11 @@ label:has(#is-dataset) {
/* short description box */
form :has(#short-description) {
- grid-column: 1 / 3;
+ grid-column: 1 / 2;
+}
+
+form :has(#url) {
+ grid-column: 2 / 4;
}
/* full description box */
@@ -115,13 +150,18 @@ form :has(#short-description) {
--drop-shadow-opacity: .5;
--drop-shadow-offset-y: 0;
--drop-shadow-blur: .25rem;
- filter: drop-shadow(
+ --drop-shadow: drop-shadow(
rgba(0, 0, 0, var(--drop-shadow-opacity))
0 var(--drop-shadow-offset-y) var(--drop-shadow-blur)
);
+ filter: var(--drop-shadow);
}
-.btn:hover {
+.btn:focus-visible, #is-dataset:focus-visible + #is-dataset-toggle {
+ outline-offset: 2px;
+}
+
+.btn:not(:disabled):hover {
background-color: color-mix(in oklab, var(--btn-color) 80%, var(--bg-color));
--drop-shadow-opacity: .8;
--drop-shadow-offset-y: .25rem;
@@ -131,3 +171,7 @@ form :has(#short-description) {
.btn.suggested {
--btn-color: var(--accent-color);
}
+
+.btn:disabled {
+ filter: var(--drop-shadow) grayscale(.5) brightness(.5);
+}
diff --git a/src/main/resources/static/add.js b/src/main/resources/static/add.js
new file mode 100644
index 0000000..280eb67
--- /dev/null
+++ b/src/main/resources/static/add.js
@@ -0,0 +1,59 @@
+const form = document.forms[0];
+const {
+ title: titleEntry,
+ author: authorEntry,
+ ["is-dataset"]: isDatasetSwitch,
+ ["short-description"]: shortDescriptionEntry,
+ url: urlEntry,
+ ["full-description"]: fullDescriptionEntry,
+ ["btn-add"]: addBtn,
+ ["btn-cancel"]: cancelBtn,
+} = form.elements;
+
+const validationListener = () => {
+ addBtn.disabled = !form.checkValidity();
+};
+
+// Register validationListener on all required inputs that must be valid
+[
+ titleEntry,
+ authorEntry,
+ shortDescriptionEntry,
+ urlEntry,
+ fullDescriptionEntry,
+].forEach(input => input.addEventListener("input", validationListener));
+
+form.addEventListener("submit", e => {
+ e.preventDefault();
+ if (!form.reportValidity()) return;
+
+ // Create the request body
+ const newContent = {
+ title: titleEntry.value,
+ author: authorEntry.value,
+ abst: shortDescriptionEntry.value,
+ url: urlEntry.value,
+ 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;
+
+ fetch("/api/v1/datasets", {
+ method: "POST",
+ body: JSON.stringify(newContent),
+ headers: {
+ "Content-Type": "application/json;charset=utf-8"
+ }
+ }).then(response => {
+ if (response.status == 200) {
+ location.assign("/");
+ } else {
+ addBtn.disabled = !form.checkValidity();
+ }
+ });
+});
diff --git a/src/main/resources/static/contentUtility.js b/src/main/resources/static/contentUtility.js
new file mode 100644
index 0000000..e2a032e
--- /dev/null
+++ b/src/main/resources/static/contentUtility.js
@@ -0,0 +1,27 @@
+import {searchBarTimeout, searchSection} from "./main.js"
+import Dataset from "./dataset.js"
+
+export function fetchQuery(fetchString) {
+ clearTimeout(searchBarTimeout);
+ fetch(fetchString)
+ .then(resp => resp.json())
+ .then((data) => {
+ parseContent(data.content);
+ });
+}
+
+function parseContent(content) {
+ console.log(content); //TODO: remove
+ if (content.length === 0) {
+ searchSection.querySelector("#nothing-found ").classList.remove("hidden");
+ } else {
+ searchSection.querySelector("#nothing-found").classList.add("hidden");
+ const datasets = content.map(dataset => new Dataset(dataset));
+ console.log(datasets); //TODO: remove
+ Array.from(searchSection.querySelectorAll(".datasets .dataset")).forEach(e => e.remove());
+ for (const dataset of datasets) {
+ searchSection.querySelector(".datasets").appendChild(dataset.createDatasetHTMLElement());
+ }
+ }
+
+}
diff --git a/src/main/resources/static/dataset.js b/src/main/resources/static/dataset.js
new file mode 100644
index 0000000..27245e5
--- /dev/null
+++ b/src/main/resources/static/dataset.js
@@ -0,0 +1,51 @@
+import { vote } from "./main.js";
+
+export default class Dataset {
+ #abstract;
+ #author;
+ #categories;
+ #date;
+ #description;
+ #id;
+ #rating;
+ #title;
+ #type;
+ #upvotes;
+ #url;
+ #votes;
+
+ constructor({abst: shortDescription, author, categories, date, description, id, rating, title, type, upvotes, url, votes}) {
+ this.#abstract = shortDescription;
+ this.#author = author;
+ this.#categories = categories;
+ this.#date = date;
+ this.#description = description;
+ this.#id = id;
+ this.#rating = rating;
+ this.#title = title;
+ this.#type = type;
+ this.#upvotes = upvotes;
+ this.#url = url;
+ this.#votes = votes;
+ }
+
+ createDatasetHTMLElement() {
+ let template = document.querySelector("#dataset-template");
+ const clone = template.content.cloneNode(true);
+ clone.querySelector(".dataset").dataset.id = this.#id;
+ clone.querySelector("h3").innerText = this.#title;
+ clone.querySelector("p").innerText = this.#description;
+ clone.querySelector("span").innerText = this.#upvotes;
+
+ // Event Listeners
+ clone.querySelector(".upvote-btn").addEventListener("click", () => {
+ vote(this.#id, true);
+ });
+
+ clone.querySelector(".downvote-btn").addEventListener("click", () => {
+ vote(this.#id, false);
+ })
+
+ return clone;
+ }
+}
diff --git a/src/main/resources/static/main.css b/src/main/resources/static/main.css
index 0711eca..63566c3 100644
--- a/src/main/resources/static/main.css
+++ b/src/main/resources/static/main.css
@@ -4,7 +4,7 @@
--text-color: #dbdbdb;
--pad-datasets: 1rem;
--pad-main: 2rem;
- --min-card-size: 60ch;
+ --min-card-size: min(60ch, 33vw);
--corner-radius: 1rem;
font-size: 12pt;
font-family: sans-serif;
@@ -49,6 +49,9 @@ header {
gap: .5rem;
background-color: var(--fg-color, darkgrey);
padding: .5rem 1rem;
+ margin-bottom: var(--pad-datasets);
+ margin-left: var(--pad-datasets);
+ margin-right: var(--pad-datasets);
border-radius: 1.5rem;
}
@@ -58,6 +61,37 @@ header {
color: var(--text-color);
}
+#nothing-found {
+ height: 60vh;
+ padding: 20vh 0;
+}
+
+#nothing-found-bg {
+ background: url("sad-looking-glass.svg") center no-repeat;
+ background-size: contain;
+ width: 100%;
+ height: 40vh;
+}
+
+#nothing-found-text {
+ text-align: center;
+}
+
+
+/*
+#search .datasets:empty {
+ background: url("sad-looking-glass.svg");
+ height: 7rem;
+ width: 7rem;
+ padding: var(--pad-main);
+}
+
+ */
+
+
+.hidden {
+ display: none;
+}
#search-entry:focus-visible {
outline: none;
}
@@ -69,14 +103,18 @@ header {
gap: 1rem;
}
-@container (width < 80ch) {
+
+
+
+
+@container (width < 60ch) {
.datasets {
grid-template-columns: 1fr;
}
}
.dataset {
- padding: 1rem 2rem;
+ padding: var(--pad-datasets) 2rem;
background-color: var(--fg-color, darkgrey);
border-radius: var(--corner-radius);
list-style: none;
@@ -136,10 +174,22 @@ header {
background-color: var(--bg-color);
}
-:is(.upvote-btn, .downvote-btn, #search-btn, #filter-btn, #sort-btn, #add-btn):is(:hover, :focus-visible) {
+#add-btn:is(:hover, :focus-visible) {
+ filter: brightness(1.2);
+}
+
+#add-btn:active {
+ filter: brightness(1.3);
+}
+
+.btn.flat {
+ transition: filter ease-out 50ms;
+}
+
+.btn.flat:is(:hover, :focus-visible) {
filter: brightness(1.5);
}
-:is(.upvote-btn, .downvote-btn, #search-btn, #filter-btn, #sort-btn, #add-btn):active {
+.btn.flat:active {
filter: brightness(1.75);
}
diff --git a/src/main/resources/static/main.js b/src/main/resources/static/main.js
new file mode 100644
index 0000000..65c62d6
--- /dev/null
+++ b/src/main/resources/static/main.js
@@ -0,0 +1,161 @@
+import { fetchQuery } from "./contentUtility.js";
+
+const apiEndpoint = "/api/v1/datasets";
+const baseURL = location.origin;
+const defaultPagingValue = 20;
+const lastQuery = {
+ url: "",
+ totalPages: 0,
+ currentPage: 0
+};
+
+// definition of all buttons & sections
+const addButton = document.getElementById("add-btn");
+const filterButton = document.getElementById("filter-btn");
+const searchButton = document.getElementById("search-btn");
+const searchBar = document.getElementById("search-entry");
+const sortButton = document.getElementById("sort-btn");
+const upvoteButtons = document.getElementsByClassName("upvote-btn");
+const downvoteButtons = document.getElementsByClassName("downvote-btn");
+export const searchSection = document.getElementById("search");
+const recentSection = document.getElementById("recents");
+const mostLikedSection = document.getElementById("top");
+
+// ID of the timeout, because we need to cancel it at some point
+export let searchBarTimeout;
+const searchDelay = 500;
+
+// Event listeners
+addButton.addEventListener("click", () => {
+ navigateToAdd();
+});
+
+filterButton.addEventListener("change", () => {
+ const filterString = filterButton.value;
+ filter(filterString);
+});
+
+searchButton.addEventListener("click", () => {
+ const searchString = searchBar.value;
+ search(searchString);
+});
+
+searchBar.addEventListener("input", () => {
+ updateSections();
+ clearTimeout(searchBarTimeout);
+ searchBarTimeout = setTimeout(() => {
+ const searchString = searchBar.value;
+ search(searchString);
+ }, searchDelay);
+});
+
+searchBar.addEventListener('keypress', function (e) {
+ if (e.key === 'Enter') {
+ const searchString = searchBar.value;
+ search(searchString);
+ }
+})
+
+sortButton.addEventListener("change", () => {
+ const sortString = sortButton.value;
+ sort(sortString);
+});
+
+// Consider moving this to datasets.js completely
+const upvoteButtonClickListener = e => {
+ const entryID = e.target.parentElement.parentElement.dataset.id;
+ vote(entryID, true);
+};
+for (const upvoteButton of upvoteButtons) {
+ upvoteButton.addEventListener("click", upvoteButtonClickListener);
+}
+
+// Consider moving this to datasets.js completely
+const downvoteButtonClickListener = e => {
+ const entryID = e.target.parentElement.parentElement.dataset.id;
+ vote(entryID, false);
+};
+for (const downvoteButton of downvoteButtons) {
+ downvoteButton.addEventListener("click", downvoteButtonClickListener);
+}
+
+// functions of the main page
+function navigateToAdd() {
+ //TODO: url to add page not yet implemented, add here
+ window.location.href = "/add";
+}
+
+function filter(filterString) {
+ filterString = filterString.toUpperCase();
+ let fetchURL = new URL(apiEndpoint, baseURL);
+ fetchURL.searchParams.append("type", filterString);
+ fetchURL.searchParams.append("size", defaultPagingValue);
+ console.log(fetchURL); // TODO: remove
+ fetchQuery(fetchURL);
+}
+
+function search(searchString) {
+ let fetchURL = new URL(apiEndpoint + "/search", baseURL);
+ fetchURL.searchParams.append("search", searchString.length === 0 ? "%" : searchString);
+
+ console.log(fetchURL); // TODO: remove
+ fetchQuery(fetchURL);
+}
+
+function sort(sortString) {
+ let query = sortString.toLowerCase().split(" ");
+ if (query[1] === "a-z" || query[1] === "↑" || query[1] === "oldest-newest") {
+ query[1] = "asc";
+ } else {
+ query[1] = "desc";
+ }
+ let fetchURL = new URL(apiEndpoint, baseURL);
+ fetchURL.searchParams.append("sort", query[0]);
+ fetchURL.searchParams.append("direction", query[1]);
+ console.log(fetchURL); // TODO: remove
+ fetchQuery(fetchURL);
+}
+
+export function vote(entryID, up) {
+ const fetchURL = new URL(
+ `${apiEndpoint}/id/${entryID}/${up ? "up" : "down"}vote`,
+ baseURL,
+ );
+ console.log(fetchURL); // TODO: remove
+ fetch(fetchURL, {
+ method: "PUT", //mode: 'no-cors'
+ headers: {
+ 'Content-Type': 'application/json',
+ // Add other headers as needed
+ },
+ });
+ /*.then(resp => resp.json()) // TODO: wait for backend
+ .then((data) => {
+ console.log(data.content); // TODO: remove*/
+ let dataset = document.querySelector('[data-id= ' + CSS.escape(entryID) + ']')
+ dataset.querySelector("span").innerText++; // TODO: replace by parsed vote
+ /*});*/
+}
+
+function incrementPageCount() {
+ lastQuery.currentPage++;
+}
+
+function updateSections() {
+ if (searchBar.value === "") {
+ searchSection.classList.add("hidden");
+ recentSection.classList.remove("hidden");
+ mostLikedSection.classList.remove("hidden");
+ } else {
+ searchSection.classList.remove("hidden");
+ recentSection.classList.add("hidden");
+ mostLikedSection.classList.add("hidden");
+ }
+}
+
+window.onload = function () {
+ updateSections();
+ if (searchBar.value !== "") {
+ search(searchBar.value);
+ }
+}
diff --git a/src/main/resources/static/sad-looking-glass.svg b/src/main/resources/static/sad-looking-glass.svg
index 7ad3841..ab99a6e 100644
--- a/src/main/resources/static/sad-looking-glass.svg
+++ b/src/main/resources/static/sad-looking-glass.svg
@@ -1,2 +1,7 @@
-
+
diff --git a/src/main/resources/templates/add.html b/src/main/resources/templates/add.html
index d8472c1..30888c3 100644
--- a/src/main/resources/templates/add.html
+++ b/src/main/resources/templates/add.html
@@ -5,6 +5,7 @@
DataDash – Add dataset/API
+
@@ -19,15 +20,15 @@
how to use it.
-
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html
index 172c714..a16f045 100644
--- a/src/main/resources/templates/index.html
+++ b/src/main/resources/templates/index.html
@@ -5,52 +5,65 @@
DataDash
+
-
+
+
+
+
+
+
title
+
Simply daily accountability phone call, powered by AI
+
+
+
+
+
+
Recently added:
+
- -
-
-
-
-
Standup
-
Simply daily accountability phone call, powered by AI
-
-
-
-
+
-
-
@@ -74,9 +87,9 @@
-
@@ -88,9 +101,9 @@
-
@@ -102,9 +115,9 @@
@@ -121,18 +134,21 @@
-