diff --git a/pom.xml b/pom.xml index c0c00fe..1c813cf 100644 --- a/pom.xml +++ b/pom.xml @@ -63,11 +63,7 @@ org.springframework.boot spring-boot-starter-thymeleaf - - - - - + diff --git a/src/main/java/org/example/lab1springboot/BookNotFoundException.java b/src/main/java/org/example/lab1springboot/BookNotFoundException.java new file mode 100644 index 0000000..cc86f84 --- /dev/null +++ b/src/main/java/org/example/lab1springboot/BookNotFoundException.java @@ -0,0 +1,7 @@ +package org.example.lab1springboot; + +public class BookNotFoundException extends RuntimeException { + public BookNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java b/src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java new file mode 100644 index 0000000..5487504 --- /dev/null +++ b/src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java @@ -0,0 +1,56 @@ +package org.example.lab1springboot; + +import org.springframework.http.HttpStatus; +import org.springframework.ui.Model; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.servlet.View; + +import java.util.HashMap; +import java.util.Map; + +import static org.springframework.util.ReflectionUtils.getField; + +/* +Global exception handler to handle all exceptions more effectively + + */ +@ControllerAdvice +public class GlobalExceptionHandler { + + + + @ExceptionHandler(BookNotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) // 404 + public String notFoundException(BookNotFoundException ex, Model model) { + + model.addAttribute("errorMessage", ex.getMessage()); + + return "error"; + } + + @ExceptionHandler(RuntimeException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 500 + public String handleRuntimeException(Exception ex, Model model) { + + model.addAttribute("errorMessage", "Unexpected error"); + return "error"; + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public String handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, Model model) { + Map errors = new HashMap<>(); + + ex.getBindingResult().getFieldErrors().forEach(error -> + errors.put(error.getField(), error.getDefaultMessage()) + ); + + model.addAttribute("errors", errors); + return "error"; + } + +} \ No newline at end of file diff --git a/src/main/java/org/example/lab1springboot/book/BookService.java b/src/main/java/org/example/lab1springboot/book/BookService.java index eea40b9..a2c90f9 100644 --- a/src/main/java/org/example/lab1springboot/book/BookService.java +++ b/src/main/java/org/example/lab1springboot/book/BookService.java @@ -1,17 +1,24 @@ package org.example.lab1springboot.book; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +import lombok.extern.slf4j.Slf4j; +import org.example.lab1springboot.BookNotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.List; - +@Slf4j @Service + public class BookService { - private static final Logger log = LoggerFactory.getLogger(BookService.class); private final BookRepository bookRepository; public BookService(BookRepository bookRepository) { @@ -19,32 +26,39 @@ public BookService(BookRepository bookRepository) { this.bookRepository = bookRepository; } + @Transactional(readOnly = true) public List getAllBooks() { return bookRepository.findAll(); } + @Transactional public void deleteBook(Long id) { - if (!bookRepository.existsById(id)) { - throw new RuntimeException("Book not found with id: " + id); - } + if(bookRepository.findById(id).isEmpty()) + throw new BookNotFoundException("Book not found with id: " + id); + bookRepository.deleteById(id); } + @Transactional public Book getBookById(Long id) { return bookRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Book not found with id: " + id)); + .orElseThrow(() -> new BookNotFoundException("Book not found with id: " + id)); } + + @Transactional public Book updateBook(Long id, UpdateBookDTO dto) { Book book = getBookById(id); book.setTitle(dto.getTitle()); book.setAuthor(dto.getName()); - bookRepository.save(book); + Book updatedBook = bookRepository.save(book); + log.info("Book updated successfully"); - return book; + return updatedBook; } + @Transactional public Book createBook(CreateBookDTO dto) { Book book = BookMapper.toEntity(dto); @@ -52,4 +66,9 @@ public Book createBook(CreateBookDTO dto) { return bookRepository.save(book); } + public Page getAllBooksPaginated(int page, int size){ + Pageable pageable = PageRequest.of(page, size); + return bookRepository.findAll(pageable); + } + } diff --git a/src/main/java/org/example/lab1springboot/book/CreateBookDTO.java b/src/main/java/org/example/lab1springboot/book/CreateBookDTO.java index ec3c824..1d4e0ce 100644 --- a/src/main/java/org/example/lab1springboot/book/CreateBookDTO.java +++ b/src/main/java/org/example/lab1springboot/book/CreateBookDTO.java @@ -5,17 +5,24 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import lombok.Data; import lombok.Getter; import lombok.Setter; @Getter @Setter +@Data public class CreateBookDTO { @NotBlank(message = "name cannot be empty") private String name; + @NotBlank(message = "title cannot be empty") + private String title; + @NotBlank(message = "author cannot be empty") + private String author; + @NotBlank(message = "Description cannot be empty") private String description; diff --git a/src/main/java/org/example/lab1springboot/controller/BookController.java b/src/main/java/org/example/lab1springboot/controller/BookController.java index c094005..f662a83 100644 --- a/src/main/java/org/example/lab1springboot/controller/BookController.java +++ b/src/main/java/org/example/lab1springboot/controller/BookController.java @@ -2,16 +2,29 @@ import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.example.lab1springboot.BookNotFoundException; import org.example.lab1springboot.book.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; + + + + @Controller -@RequestMapping("/books") + public class BookController { + Logger log = LoggerFactory.getLogger(BookController.class); + BookService bookService; @@ -22,26 +35,20 @@ public class BookController { - @GetMapping("/{id}") - public String getBookById(@PathVariable Long id, Model model) { - Book book = bookService.getBookById(id); - model.addAttribute("book", book); - return "books"; - } - - @GetMapping - public String getAllBooks(Model model) { - model.addAttribute("books", bookService.getAllBooks()); - return "books"; - } - +// @GetMapping("/{id}") +// public String getBookById(@PathVariable Long id, Model model) { +// Book book = bookService.getBookById(id); +// +// model.addAttribute("book", book); +// return "books"; +// } @PostMapping("books/create") public String createBook(@Valid CreateBookDTO dto, BindingResult result) { if(result.hasErrors()){ - return "createbook"; + return "create-book"; } bookService.createBook(dto); return "redirect:/books"; @@ -50,8 +57,7 @@ public String createBook(@Valid CreateBookDTO dto, BindingResult result) { @PutMapping("/{id}") public String updateBook(@PathVariable Long id, @Valid UpdateBookDTO updateBookDTO, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors()) { - model.addAttribute("updateBookDTO", updateBookDTO); - return "updatebook"; + return "update-book"; } bookService.updateBook(id, updateBookDTO); @@ -61,10 +67,51 @@ public String updateBook(@PathVariable Long id, @Valid UpdateBookDTO updateBookD @DeleteMapping("/{id}") public String deleteBook(@PathVariable Long id) { + bookService.deleteBook(id); return "redirect:/books"; } + @GetMapping("/update/{id}") + public String showUpdateForm(@PathVariable Long id, Model model) { + Book book = bookService.getBookById(id); + + UpdateBookDTO dto = new UpdateBookDTO(); + + + model.addAttribute("updateBookDTO", dto); + model.addAttribute("id", id); + return "update-book"; + } + + @GetMapping("books/create") + public String showCreateForm(Model model) { + + model.addAttribute("book", new CreateBookDTO()); + return "create-book"; + } + + @ExceptionHandler(BookNotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public String handleBookNotFound(BookNotFoundException ex, Model model) { + model.addAttribute("errorMessage", ex.getMessage()); + return "error"; + } + + @GetMapping("/books") + public String getAllBooks( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "5") int size, + Model model) { + + Page bookPage = bookService.getAllBooksPaginated(page, size); + + model.addAttribute("books", bookPage.getContent()); + model.addAttribute("currentPage", page); + model.addAttribute("totalPages", bookPage.getTotalPages()); + + return "books"; + } diff --git a/src/main/java/org/example/lab1springboot/controller/HomeController.java b/src/main/java/org/example/lab1springboot/controller/HomeController.java index 96e9eea..7d66534 100644 --- a/src/main/java/org/example/lab1springboot/controller/HomeController.java +++ b/src/main/java/org/example/lab1springboot/controller/HomeController.java @@ -11,11 +11,8 @@ public class HomeController { @GetMapping("/") public String home(Model model) { - System.out.println("DEBUG: Home controller triggered"); - - model.addAttribute("message", "Welcome to Thymeleaf!"); - model.addAttribute("currentDate", java.time.LocalDate.now()); - + model.addAttribute("activePage", "home"); return "home"; } + } diff --git a/src/main/java/org/example/lab1springboot/controller/UserController.java b/src/main/java/org/example/lab1springboot/controller/UserController.java new file mode 100644 index 0000000..f2ae78e --- /dev/null +++ b/src/main/java/org/example/lab1springboot/controller/UserController.java @@ -0,0 +1,19 @@ +package org.example.lab1springboot.controller; + +import jakarta.validation.Valid; +import org.example.lab1springboot.user.User; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@Controller +public class UserController { + + @PostMapping("/user") + public String CreateUser(@RequestBody @Valid User user) { + + return "user"; + + + } +} \ No newline at end of file diff --git a/src/main/java/org/example/lab1springboot/user/User.java b/src/main/java/org/example/lab1springboot/user/User.java new file mode 100644 index 0000000..e67ba7e --- /dev/null +++ b/src/main/java/org/example/lab1springboot/user/User.java @@ -0,0 +1,31 @@ +package org.example.lab1springboot.user; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class User { + + @NotNull(message = "Name cannot be empty") + private String name; + + @NotEmpty + @NotNull(message = "Username cannot be empty") + private String username; + + @NotNull(message = "Email cannot be empty") + private String email; + + @NotBlank(message = "Password cannot be blank") + private String password; + + @NotNull(message = "Id cannot be empty") + private String id; + + + +} diff --git a/src/main/jte/books.jte b/src/main/jte/books.jte deleted file mode 100644 index 68ef6be..0000000 --- a/src/main/jte/books.jte +++ /dev/null @@ -1,22 +0,0 @@ -@param String nameError - - - - - - Form demo - - -

Form demo

-
-
-
- @if(nameError != null) -
${nameError}

- @endif -
-

- -
- - diff --git a/src/main/jte/deletebook.jte b/src/main/jte/deletebook.jte deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/jte/home.jte b/src/main/jte/home.jte deleted file mode 100644 index 5ca76dc..0000000 --- a/src/main/jte/home.jte +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - Title - - -
-

Hello Home

-
- - \ No newline at end of file diff --git a/src/main/jte/updatebook.jte b/src/main/jte/updatebook.jte deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6eed576..ae767d3 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,4 +6,5 @@ spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.h2.console.enabled=true spring.jpa.hibernate.ddl-auto=update +spring.mvc.hiddenmethod.filter.enabled=true diff --git a/src/main/resources/static/css/base.css b/src/main/resources/static/css/base.css new file mode 100644 index 0000000..dcb2aa2 --- /dev/null +++ b/src/main/resources/static/css/base.css @@ -0,0 +1,3 @@ +body { + font-family: Arial, sans-serif; +} diff --git a/src/main/resources/static/css/books.css b/src/main/resources/static/css/books.css new file mode 100644 index 0000000..26ac67a --- /dev/null +++ b/src/main/resources/static/css/books.css @@ -0,0 +1,12 @@ +.pagination-container a { + padding: 8px 16px; + text-decoration: none; + border: 1px solid #ddd; + color: black; +} + +.pagination-container a.active { + background-color: #6d15b5; + color: white; + border: 1px solid #6d15b5; +} \ No newline at end of file diff --git a/src/main/jte/createbook.jte b/src/main/resources/static/css/forms.css similarity index 100% rename from src/main/jte/createbook.jte rename to src/main/resources/static/css/forms.css diff --git a/src/main/resources/static/css/navbar.css b/src/main/resources/static/css/navbar.css new file mode 100644 index 0000000..33ad45f --- /dev/null +++ b/src/main/resources/static/css/navbar.css @@ -0,0 +1,4 @@ +.navbar { + display:flex; + justify-content:space-between; +} \ No newline at end of file diff --git a/src/main/resources/static/style.css b/src/main/resources/static/style.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/resources/templates/books.html b/src/main/resources/templates/books.html index da67182..9a80221 100644 --- a/src/main/resources/templates/books.html +++ b/src/main/resources/templates/books.html @@ -2,15 +2,58 @@ - Bookstore + Book List + -

Bookstore

-
-

Book Name

-
-
    -
  • Book
  • -
+ + + +
+

All Books

+ + + + + + + + + + + + + + + + + + +
IDTitleAuthorActions
1TitelFörfattare + Edit + +
+ +
+
+ +
+ Add New Book + +
+ Previous + + + + + + Next +
+
+ \ No newline at end of file diff --git a/src/main/resources/templates/create-book.html b/src/main/resources/templates/create-book.html new file mode 100644 index 0000000..d96c968 --- /dev/null +++ b/src/main/resources/templates/create-book.html @@ -0,0 +1,19 @@ + + + + + Bookstore + + + + +
+ + +
+ + +
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html new file mode 100644 index 0000000..d2867f5 --- /dev/null +++ b/src/main/resources/templates/error.html @@ -0,0 +1,19 @@ + + + + + Title + +< +
+
🚀
+

404

+

Woopsie daisy! Page went to the moon.

+ + +

We cannot find the page you were looking for

+ + Back to Earth (Home) +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 83ea100..7bb7e69 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -1,32 +1,17 @@ - - + - Home Page - - + Bookstore + -
-

Default Welcome Message

-
+
-

Today is: 2023-01-01

- -
-

Date is available!

-
- - - - +

Welcome Message

+

Today's date is: Date

- -
-

© 2026 My Application

-
\ No newline at end of file diff --git a/src/main/resources/templates/navbar.html b/src/main/resources/templates/navbar.html new file mode 100644 index 0000000..94016ca --- /dev/null +++ b/src/main/resources/templates/navbar.html @@ -0,0 +1,27 @@ + + + + + Bookstore + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/update-book.html b/src/main/resources/templates/update-book.html new file mode 100644 index 0000000..22f9820 --- /dev/null +++ b/src/main/resources/templates/update-book.html @@ -0,0 +1,23 @@ + + + + + Bookstore + + + + +
+ +
+ + + + + +
+ +
+ + + \ No newline at end of file