Merge pull request 'chore: Update branch' (#6) from Maradona/Backend:restfull into main

Reviewed-on: 2210970/Backend#6
pull/1/head
Mohammad Hawrami 2024-11-08 16:44:28 +01:00
commit 10cd08583b
30 changed files with 236 additions and 107 deletions

8
.gitignore vendored
View File

@ -31,3 +31,11 @@ build/
### VS Code ###
.vscode/
.history/
### macOS ###
*.DS_Store
### Database files ###
*.db
*.mv.db

Binary file not shown.

View File

@ -102,6 +102,7 @@
<destDir>${project.build.directory}/docs</destDir>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -33,13 +33,13 @@ public class DefaultValueLoader implements CommandLineRunner {
private FormOfAddressRepository formOfAddressRepository;
@Override
public void run(String... args) throws Exception {
public void run(String... args) {
// Clear all tables
// formOfAddressRepository.deleteAll();
// employeeRepository.deleteAll();
// secondarySkillRepository.deleteAll();
// primarySkillRepository.deleteAll();
employeeRepository.deleteAll();
secondarySkillRepository.deleteAll();
primarySkillRepository.deleteAll();
formOfAddressRepository.deleteAll();
// Create form of addresses
FormOfAddress formOfAddress1 = new FormOfAddress();

View File

@ -0,0 +1,113 @@
package com.maradona.backend.controllers.api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.maradona.backend.entities.FormOfAddress;
import com.maradona.backend.services.FormOfAddressService;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.PutMapping;
/**
* Controller for routing to the form of address related API endpoints.
*
* List of endpoints:
* - GET /api/form-of-adress
* - GET /api/form-of-adress/all
* - POST /api/form-of-adress
* - PUT /api/form-of-adress
* - DELETE /api/form-of-adress
*
* @see com.maradona.backend.entities.FormOfAddress
*/
@RestController
@RequestMapping("/api/form-of-adress")
class FormOfAdressController {
@Autowired
private FormOfAddressService formOfAdressService;
/**
* Returns the form of address with the given ID.
*
* @param id The ID of the form of address to return.
* @return The description of the form of address with the given ID.
* @see com.maradona.backend.entities.FormOfAddress
*/
@GetMapping({ "/", "" })
public ResponseEntity<FormOfAddress> getFormOfAdress(@RequestParam Long id) {
return formOfAdressService.getFormOfAddressById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* Returns all form of addresses.
*
* @return A list of all form of addresses.
* @see com.maradona.backend.entities.FormOfAddress
*/
@GetMapping("/all")
public ResponseEntity<Iterable<FormOfAddress>> getAllFormOfAdresses() {
Iterable<FormOfAddress> formOfAddresses = formOfAdressService.getAllFormOfAddresses();
return ResponseEntity.ok(formOfAddresses);
}
/**
* Creates a new form of address.
*
* @param description The description of the new form of address.
* @return The ID of the newly created form of address.
* @see com.maradona.backend.entities.FormOfAddress
*/
@PostMapping({ "/", "" })
public ResponseEntity<Void> createFormOfAdress(@RequestBody String description) {
var formOfAddress = new FormOfAddress();
formOfAddress.setDescription(description);
formOfAdressService.saveFormOfAddress(formOfAddress);
return ResponseEntity.ok().build();
}
/**
* Updates the description of a form of address.
*
* @param id The ID of the form of address to update.
* @param description The new description of the form of address.
* @return The updated form of address.
* @see com.maradona.backend.entities.FormOfAddress
*/
@PutMapping({ "/", "" })
public ResponseEntity<Void> updateFormOfAdress(@RequestParam Long id, @RequestBody String description) {
var formOfAddress = formOfAdressService.getFormOfAddressById(id).orElse(null);
if (formOfAddress == null) {
return ResponseEntity.notFound().build();
}
formOfAddress.setDescription(description);
formOfAdressService.saveFormOfAddress(formOfAddress);
return ResponseEntity.ok().build();
}
/**
* Deletes a form of address.
*
* @param id The ID of the form of address to delete.
* @return The deleted form of address.
* @see com.maradona.backend.entities.FormOfAddress
*/
@DeleteMapping({ "/", "" })
public ResponseEntity<Void> deleteFormOfAdress(@RequestParam Long id) {
var formOfAddress = formOfAdressService.getFormOfAddressById(id).orElse(null);
if (formOfAddress == null) {
return ResponseEntity.notFound().build();
}
formOfAdressService.deleteFormOfAddress(id);
return ResponseEntity.ok().build();
}
}

View File

@ -9,21 +9,21 @@ public class Page {
@GetMapping({ "/", "" })
public String home(Model model) {
return "redirect:/projects";
return "pages/core/home";
}
@GetMapping("/impressum")
public String impressum() {
return "core/impressum";
return "pages/core/impressum";
}
@GetMapping("/datenschutz")
public String datenschutz() {
return "core/datenschutz";
return "pages/core/datenschutz";
}
@GetMapping("/notes")
public String notes() {
return "notes/notes";
return "pages/core/notes";
}
}

View File

@ -15,7 +15,7 @@ import com.maradona.backend.services.ProjectService;
* - GET /projects
* - GET /projects/create
*
* @see Project
* @see com.maradona.backend.entities.Project
*/
@Controller
@RequestMapping("/projects")
@ -32,13 +32,13 @@ public class ProjectPage {
*
* @param model The model with the data to be displayed.
* @return The projects overview page template
* @see Project
* @see com.maradona.backend.entities.Project
*/
@GetMapping({ "/", "" })
public String projects(Model model) {
var projects = projectService.getAllProjects();
model.addAttribute("projects", projects);
return "projects/projects";
return "/pages/projects/overview";
}
/**
@ -46,11 +46,11 @@ public class ProjectPage {
*
* @param model The model with the data to be displayed.
* @return The project creation page template
* @see Project
* @see com.maradona.backend.entities.Project
*/
@GetMapping("/create")
public String create(Model model) {
model.addAttribute("project", new Project());
return "projects/projects-create";
return "/pages/projects/create";
}
}

View File

@ -7,8 +7,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import com.maradona.backend.entities.SecondarySkill;
import com.maradona.backend.entities.Employee;
import com.maradona.backend.entities.PrimarySkill;
import com.maradona.backend.services.EmployeeService;
import com.maradona.backend.services.SkillService;
import com.maradona.backend.services.PrimarySkillService;
@ -22,6 +20,7 @@ import com.maradona.backend.dto.SkillPrototype;
* - GET /skills/create
* - GET /skills/add
*
* @see com.maradona.backend.entities.SecondarySkill
*/
@Controller
@RequestMapping("/skills")
@ -47,15 +46,15 @@ public class SkillsPage {
* "employee" and a SkillsDto object "skills".
*
* @param model The model with the data to be displayed.
* @return The skills overview page template
* @see Employee
* @see SkillsDto
* @return The skills overview page template.
* @see com.maradona.backend.entities.Employee
* @see com.maradona.backend.dto.SkillsDto
*/
@GetMapping({ "/", "" })
public String profile(Model model) {
model.addAttribute("employee", employeeService.getEmployeeById(user).orElse(null));
model.addAttribute("skills", skillService.getUserSkills(user));
return "skills/skills";
return "/pages/skills/overview";
}
/**
@ -66,12 +65,12 @@ public class SkillsPage {
*
* @param model The model with the data to be displayed
* @return The create skill page template
* @see SecondarySkill
* @see com.maradona.backend.entities.SecondarySkill
*/
@GetMapping("/create")
public String createSkill(Model model) {
model.addAttribute("secondarySkill", new SecondarySkill());
return "skills/skills-create";
return "/pages/skills/create";
}
/**
@ -87,16 +86,16 @@ public class SkillsPage {
*
* @param model The model with the data to be displayed
* @return The add skill page template
* @see PrimarySkill
* @see SecondarySkill
* @see SkillPrototype
* @see com.maradona.backend.entities.PrimarySkill
* @see com.maradona.backend.entities.SecondarySkill
* @see com.maradona.backend.dto.SkillPrototype
*/
@GetMapping("/add")
public String addSkill(Model model) {
// TODO: Make sure it returns the correct initail data for secondary skills
model.addAttribute("primarySkills", primarySkillService.getAllPrimarySkills());
model.addAttribute("skillProtoype", new SkillPrototype());
return "skills/skills-add";
return "/pages/skills/add";
}
}

View File

@ -2,6 +2,7 @@ package com.maradona.backend.entities;
import jakarta.persistence.*;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import java.time.LocalTime;
import java.util.List;
import java.util.Map;
@ -10,7 +11,6 @@ import java.util.stream.Collectors;
@Entity
@Data
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ -31,6 +31,7 @@ public class Employee {
private LocalTime dEnd;
@OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference
private List<EmployeeSecondarySkill> secondarySkills;
public void setEmployeeNr(Integer employeeNr) {

View File

@ -1,13 +1,12 @@
package com.maradona.backend.entities;
import jakarta.persistence.*;
import com.fasterxml.jackson.annotation.JsonBackReference;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonBackReference;
@Entity
@Data
public class EmployeeSecondarySkill {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ -19,7 +18,6 @@ public class EmployeeSecondarySkill {
@ManyToOne
@JoinColumn(name = "secondary_skill_id", nullable = false)
@JsonBackReference
private SecondarySkill secondarySkill;
@Column(nullable = false)

View File

@ -28,4 +28,14 @@ public class FormOfAddressService {
public Iterable<FormOfAddress> getAllFormOfAddresses() {
return formOfAddressRepository.findAll();
}
public Long updateFormOfAddress(Long id, String description) {
var formOfAddress = formOfAddressRepository.findById(id).orElse(null);
if (formOfAddress != null) {
formOfAddress.setDescription(description);
formOfAddressRepository.save(formOfAddress);
return id;
}
return null;
}
}

View File

@ -0,0 +1,18 @@
document.getElementById("primarySkill").addEventListener("change", function () {
var primarySkillId = this.value;
fetch("/skills/secondary-skills?primarySkillId=" + primarySkillId)
.then((response) => response.json())
.then((data) => {
console.log("Fetch response data:", data);
var secondarySkillSelect = document.getElementById("secondarySkill");
secondarySkillSelect.innerHTML =
'<option value="" disabled selected>Select a secondary skill</option>';
data.forEach(function (secondarySkill) {
var option = document.createElement("option");
option.value = secondarySkill.ssid;
option.text = secondarySkill.description;
secondarySkillSelect.add(option);
});
})
.catch((error) => console.error("Error fetching secondary skills:", error));
});

View File

@ -1 +0,0 @@
/* Placeholder */

View File

@ -5,26 +5,9 @@
<img src="/assets/inter-brand.svg" alt="INTER Logo" class="svg-logo" />
</div>
<div class="col-md-6">
<nav>
<ul class="nav justify-content-end">
<li class="nav-item">
<a
class="nav-link"
th:href="@{/projects}"
th:classappend="${activePage == 'projects' ? 'active' : ''}"
>Projekte</a
>
</li>
<li class="nav-item">
<a
class="nav-link"
th:href="@{/skills}"
th:classappend="${activePage == 'profile' ? 'active' : ''}"
>Skills</a
>
</li>
</ul>
</nav>
<div
th:replace="~{/core/_navigation :: navigation(activePage=${activePage})}"
></div>
</div>
</div>
</div>

View File

@ -0,0 +1,22 @@
<div th:fragment="navigation">
<nav>
<ul class="nav justify-content-end">
<li class="nav-item">
<a
class="nav-link"
th:href="@{/projects}"
th:classappend="${activePage == 'projects' ? 'active' : ''}"
>Projekte</a
>
</li>
<li class="nav-item">
<a
class="nav-link"
th:href="@{/skills}"
th:classappend="${activePage == 'profile' ? 'active' : ''}"
>Skills</a
>
</li>
</ul>
</nav>
</div>

View File

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>My Skill Management System</title>
<link rel="stylesheet" href="style/notes/notes.css" />
</head>
<body>
<div class="wrapper">
<div th:replace="~{/core/_header :: header(activePage=${notes})}"></div>
<div class="content container mt-4"></div>
<div th:replace="~{/core/_footer :: footer}"></div>
</div>
</body>
</html>

View File

@ -3,10 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Create Project</title>
<link rel="stylesheet" href="/style/projects/projects.css" />
<link rel="stylesheet" href="/style/core/style.css" />
<link rel="stylesheet" href="/style/core/header.css" />
<link rel="stylesheet" href="/style/core/footer.css" />
<link rel="stylesheet" href="/css/projects/projects.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">

View File

@ -3,7 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Projects</title>
<link rel="stylesheet" href="/style/projects/projects.css" />
<link rel="stylesheet" href="/css/projects/projects.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">

View File

@ -3,10 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Add Skill</title>
<link rel="stylesheet" href="/style/skills/skills.css" />
<link rel="stylesheet" href="/style/core/style.css" />
<link rel="stylesheet" href="/style/core/header.css" />
<link rel="stylesheet" href="/style/core/footer.css" />
<link rel="stylesheet" href="/css/skills/skills.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">
@ -15,11 +15,12 @@
<h2 class="mb-4">Add Skill</h2>
<form
th:action="@{/skills/add}"
th:object="${skillForm}"
th:object="${skillProtoype}"
method="post"
class="project-card"
>
<div class="card-body">
<!-- Primary Skill Dropdown -->
<div class="form-group">
<label for="primarySkill">Primary Skill</label>
<select
@ -28,6 +29,9 @@
class="form-control"
th:field="*{primarySkillId}"
>
<option value="" disabled selected>
Select a primary skill
</option>
<option
th:each="primarySkill : ${primarySkills}"
th:value="${primarySkill.psid}"
@ -35,6 +39,8 @@
></option>
</select>
</div>
<!-- Secondary Skill Dropdown -->
<div class="form-group">
<label for="secondarySkill">Secondary Skill</label>
<select
@ -48,6 +54,8 @@
</option>
</select>
</div>
<!-- Skill Level Dropdown -->
<div class="form-group">
<label for="level">Level</label>
<select
@ -56,6 +64,7 @@
class="form-control"
th:field="*{level}"
>
<option value="" disabled selected>Select skill level</option>
<option
th:each="level : ${#numbers.sequence(1, 5)}"
th:value="${level}"
@ -63,36 +72,14 @@
></option>
</select>
</div>
<!-- Submit Button -->
<button type="submit" class="btn-create-project">Add Skill</button>
</div>
</form>
</div>
<div th:replace="~{/core/_footer :: footer}"></div>
</div>
<script>
document
.getElementById("primarySkill")
.addEventListener("change", function () {
var primarySkillId = this.value;
fetch("/skills/secondary-skills?primarySkillId=" + primarySkillId)
.then((response) => response.json())
.then((data) => {
console.log("Fetch response data:", data);
var secondarySkillSelect =
document.getElementById("secondarySkill");
secondarySkillSelect.innerHTML =
'<option value="" disabled selected>Select a secondary skill</option>';
data.forEach(function (secondarySkill) {
var option = document.createElement("option");
option.value = secondarySkill.ssid;
option.text = secondarySkill.description;
secondarySkillSelect.add(option);
});
})
.catch((error) =>
console.error("Error fetching secondary skills:", error)
);
});
</script>
<script src="/js/skills/fetchSecondarySkills.js"></script>
</body>
</html>

View File

@ -3,10 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Create Skill</title>
<link rel="stylesheet" href="/style/skills/skills.css" />
<link rel="stylesheet" href="/style/core/style.css" />
<link rel="stylesheet" href="/style/core/header.css" />
<link rel="stylesheet" href="/style/core/footer.css" />
<link rel="stylesheet" href="/css/skills/skills.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">

View File

@ -3,8 +3,10 @@
<head>
<div th:replace="~{/core/_metadata :: metadata}"></div>
<title>Skills</title>
<link rel="stylesheet" href="/style/skills/skills.css" />
<link rel="stylesheet" href="/style/core/style.css" />
<link rel="stylesheet" href="/css/skills/skills.css" />
<link rel="stylesheet" href="/css/core/style.css" />
<link rel="stylesheet" href="/css/core/header.css" />
<link rel="stylesheet" href="/css/core/footer.css" />
</head>
<body>
<div class="wrapper">