diff --git a/src/main/java/com/app/config/WebConfig.java b/src/main/java/com/app/config/WebConfig.java new file mode 100644 index 0000000..f6a87b3 --- /dev/null +++ b/src/main/java/com/app/config/WebConfig.java @@ -0,0 +1,19 @@ +package com.app.config; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.web.filter.HiddenHttpMethodFilter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Bean + public FilterRegistrationBean hiddenHttpMethodFilter() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new HiddenHttpMethodFilter()); + filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); + return filterRegistrationBean; + } +} + diff --git a/src/main/java/com/app/controllers/manager/HotelManagerController.java b/src/main/java/com/app/controllers/manager/HotelManagerController.java index 12411d9..cb04e74 100644 --- a/src/main/java/com/app/controllers/manager/HotelManagerController.java +++ b/src/main/java/com/app/controllers/manager/HotelManagerController.java @@ -3,25 +3,43 @@ import com.app.constants.Role; import com.app.controllers.BaseController; import com.app.dtos.UserRegistrationDTO; +import com.app.services.BookingService; +import com.app.services.HotelService; import com.app.services.UserService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; +import org.springframework.ui.Model; @Controller @RequestMapping("/manager") @Slf4j public class HotelManagerController extends BaseController { + @Autowired + BookingService bookingService; + @Autowired + UserService userService; + @Autowired + HotelService hotelService; public HotelManagerController(UserService userService) { super(userService); } @GetMapping("/dashboard") - public String dashboard() { + public String dashboard(Model model) { + int bookingCount = bookingService.countBookings(); + int hotelCount = hotelService.countHotels(); + int userCount = userService.countUsers(); + + // Add attributes to the model to be used in the Thymeleaf template + model.addAttribute("bookingCount", bookingCount); + model.addAttribute("hotelCount", hotelCount); + model.addAttribute("userCount", userCount); return "manager/dashboard"; } diff --git a/src/main/java/com/app/controllers/manager/ManagerBookingController.java b/src/main/java/com/app/controllers/manager/ManagerBookingController.java new file mode 100644 index 0000000..c9530ac --- /dev/null +++ b/src/main/java/com/app/controllers/manager/ManagerBookingController.java @@ -0,0 +1,91 @@ +package com.app.controllers.manager; + +import com.app.dtos.BookingDTO; +import com.app.models.Booking; +import com.app.services.BookingService; +import com.app.services.UserService; +import groovy.util.logging.Slf4j; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import java.util.List; +import java.util.Optional; +import java.util.Scanner; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Controller +@RequestMapping("/manager/bookings") +@RequiredArgsConstructor +@Slf4j +public class ManagerBookingController { + + @Autowired + BookingService bookingService; + + @Autowired + UserService userService; + + @GetMapping + public String index(Model model, + @RequestParam("page") Optional page, + @RequestParam("size") Optional size) { + int currentPage = page.orElse(1); + int pageSize = size.orElse(5); + + Pageable pageable = PageRequest.of(currentPage - 1, pageSize); + Page bookingPage = bookingService.findPaginatedBookings(pageable); + int totalPages = bookingPage.getTotalPages(); + model.addAttribute("bookingPage", bookingPage); + if (totalPages > 0) { + List pageNumbers = IntStream.rangeClosed(1, totalPages) + .boxed() + .collect(Collectors.toList()); + model.addAttribute("pageNumbers", pageNumbers); + } + + return "manager/bookings/index"; + } + + @GetMapping("/new") + public String showCreateForm(Model model) { + model.addAttribute("bookingDTO", new BookingDTO()); + model.addAttribute("customers", userService.findAll()); + return "manager/bookings/add-booking"; + } + + @PostMapping("/") + public String saveBooking(@ModelAttribute("bookingDTO") BookingDTO bookingDTO, RedirectAttributes redirectAttributes) { + bookingService.saveBooking(bookingDTO); + redirectAttributes.addFlashAttribute("message", "Booking created successfully!"); + return "redirect:/manager/bookings"; + } + + @GetMapping("/{id}/edit") + public String showEditForm(@PathVariable("id") Integer id, Model model) { + Booking booking = bookingService.findById(id); + model.addAttribute("bookingDTO", bookingService.convertToDTO(booking)); + return "manager/bookings/edit-booking"; + } + + @PatchMapping("/{id}/update") + public String updateBooking(@PathVariable("id") Integer id, @ModelAttribute("bookingDTO") BookingDTO bookingDTO, RedirectAttributes redirectAttributes) { + bookingService.updateBooking(id, bookingDTO); + redirectAttributes.addFlashAttribute("message", "Booking updated successfully!"); + return "redirect:/manager/bookings"; + } + + @DeleteMapping("/{id}") + public String deleteBooking(@PathVariable("id") Integer id, RedirectAttributes redirectAttributes) { + bookingService.deleteBooking(id); + redirectAttributes.addFlashAttribute("message", "Booking deleted successfully!"); + return "redirect:/manager/bookings"; + } +} diff --git a/src/main/java/com/app/dtos/BookingDTO.java b/src/main/java/com/app/dtos/BookingDTO.java index 9d2f47e..426aca8 100644 --- a/src/main/java/com/app/dtos/BookingDTO.java +++ b/src/main/java/com/app/dtos/BookingDTO.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import lombok.*; import java.time.LocalDate; import java.time.LocalDateTime; @@ -13,6 +14,8 @@ @AllArgsConstructor @Getter @Setter +@Data +@Builder public class BookingDTO { private Integer id; private Integer customerId; diff --git a/src/main/java/com/app/initialize/TestDataInitializer.java b/src/main/java/com/app/initialize/TestDataInitializer.java index c48e88f..343fb04 100644 --- a/src/main/java/com/app/initialize/TestDataInitializer.java +++ b/src/main/java/com/app/initialize/TestDataInitializer.java @@ -1,7 +1,10 @@ package com.app.initialize; - +import com.app.models.Booking; +import com.app.models.User; +import com.app.repositories.BookingRepository; import com.app.repositories.UserRepository; +import com.app.constants.BookingStatus; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -9,11 +12,13 @@ import org.springframework.dao.DataAccessException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; -import com.app.models.User; -import com.app.constants.Role; import java.time.LocalDate; import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import com.app.constants.Role; @Component @RequiredArgsConstructor @@ -21,12 +26,12 @@ public class TestDataInitializer implements CommandLineRunner { private final UserRepository userRepository; + private final BookingRepository bookingRepository; private final PasswordEncoder passwordEncoder; @Override @Transactional public void run(String... args) { - try { log.warn("Checking if test data persistence is required..."); @@ -38,19 +43,52 @@ public void run(String... args) { User user3 = User.builder().email("manager1@hotel.com").password(passwordEncoder.encode("123456")).name("John").role(Role.valueOf("HOTEL_STAFF")).build(); User user4 = User.builder().email("manager2@hotel.com").password(passwordEncoder.encode("123456")).name("Max").role(Role.valueOf("HOTEL_STAFF")).build(); + log.debug("Saving users..."); userRepository.save(user1); userRepository.save(user2); userRepository.save(user3); userRepository.save(user4); + log.info("Users saved. Creating sample bookings..."); + createSampleBookings(user2); // Assuming user2 is a customer + + log.info("Sample bookings created successfully!"); + } else { log.info("Test data persistence is not required"); } log.warn("App ready"); } catch (DataAccessException e) { - log.error("Exception occurred during data persistence: " + e.getMessage()); + log.error("Exception occurred during data persistence: " + e.getMessage(), e); } catch (Exception e) { - log.error("Unexpected exception occurred: " + e.getMessage()); + log.error("Unexpected exception occurred: " + e.getMessage(), e); + } + } + + private void createSampleBookings(User customer) { + List statuses = Arrays.asList("PENDING", "CONFIRMED", "CANCELLED"); + Random random = new Random(); + + log.debug("Creating bookings for customer: {}", customer.getEmail()); + for (int i = 1; i <= 10; i++) { + LocalDate checkinDate = LocalDate.now().plusDays(random.nextInt(30)); + LocalDate checkoutDate = checkinDate.plusDays(random.nextInt(5) + 1); // Random stay between 1 and 5 days + + Booking booking = Booking.builder() + .customer(customer) // Assigning the customer from the sample user + .name("Booking " + i) + .email("customer" + i + "@hotel.com") + .phone("+12345678" + i) + .address("Address " + i) + .checkinDate(checkinDate) + .checkoutDate(checkoutDate) + .status(BookingStatus.valueOf(statuses.get(random.nextInt(statuses.size())))) + .totalAmount(100.0 + random.nextInt(500)) // Random amount between 100 and 600 + .build(); + + log.debug("Saving booking: {}", booking); + bookingRepository.save(booking); } + log.debug("All bookings saved successfully."); } } diff --git a/src/main/java/com/app/models/Booking.java b/src/main/java/com/app/models/Booking.java index 4074207..7a82a9e 100644 --- a/src/main/java/com/app/models/Booking.java +++ b/src/main/java/com/app/models/Booking.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import lombok.*; import java.time.LocalDate; import java.time.LocalDateTime; @@ -17,10 +18,12 @@ @AllArgsConstructor @Getter @Setter +@Data +@Builder public class Booking { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @ManyToOne diff --git a/src/main/java/com/app/services/BookingService.java b/src/main/java/com/app/services/BookingService.java index 335ee2e..0f9a60e 100644 --- a/src/main/java/com/app/services/BookingService.java +++ b/src/main/java/com/app/services/BookingService.java @@ -1,6 +1,9 @@ package com.app.services; +import com.app.dtos.BookingDTO; import com.app.models.Booking; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.List; @@ -16,8 +19,17 @@ public interface BookingService { public List findBookingsByHotelStaff() ; - public void save(Booking booking) ; + Page findPaginatedBookings(Pageable pageable); - public void update(Integer id, Booking booking) ; - public void delete(Integer id) ; + void saveBooking(BookingDTO bookingDTO); + + void updateBooking(Integer id, BookingDTO bookingDTO); + + void deleteBooking(Integer id); + + BookingDTO convertToDTO(Booking booking); + + Booking convertToEntity(BookingDTO bookingDTO); } + + diff --git a/src/main/java/com/app/services/UserService.java b/src/main/java/com/app/services/UserService.java index f3a4b7d..98cf453 100644 --- a/src/main/java/com/app/services/UserService.java +++ b/src/main/java/com/app/services/UserService.java @@ -35,4 +35,6 @@ public interface UserService { User convertToEntity(UserInfoDTO userDTO); void saveUserInfo(UserInfoDTO userDTO); + + List findAll(); } diff --git a/src/main/java/com/app/services/impl/BookingServiceImpl.java b/src/main/java/com/app/services/impl/BookingServiceImpl.java index 286b6ef..24e52ab 100644 --- a/src/main/java/com/app/services/impl/BookingServiceImpl.java +++ b/src/main/java/com/app/services/impl/BookingServiceImpl.java @@ -1,11 +1,16 @@ package com.app.services.impl; +import com.app.constants.BookingStatus; +import com.app.dtos.BookingDTO; import com.app.models.Booking; import com.app.models.User; import com.app.repositories.BookingRepository; +import com.app.repositories.UserRepository; import com.app.services.BookingService; import com.app.services.UserService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.List; @@ -16,6 +21,9 @@ public class BookingServiceImpl implements BookingService { @Autowired private BookingRepository bookingRepository; + @Autowired + private UserRepository userRepository; + @Autowired private UserService userService; @@ -43,19 +51,76 @@ public List findBookingsByHotelStaff() { } @Override - public void save(Booking booking) { + public void saveBooking(BookingDTO bookingDTO) { + Booking booking = convertToEntity(bookingDTO); bookingRepository.save(booking); } - @Override - public void update(Integer id, Booking booking) { + public void updateBooking(Integer id, BookingDTO bookingDTO) { Booking existingBooking = findById(id); + + // Lấy đối tượng User từ customerId + User customer = userRepository.findById(Long.valueOf(bookingDTO.getCustomerId())) + .orElseThrow(() -> new RuntimeException("User not found")); + + Booking updatedBooking = Booking.builder() + .id(existingBooking.getId()) + .customer(customer) + .name(bookingDTO.getName()) + .email(bookingDTO.getEmail()) + .phone(bookingDTO.getPhone()) + .address(bookingDTO.getAddress()) + .checkinDate(bookingDTO.getCheckinDate()) + .checkoutDate(bookingDTO.getCheckoutDate()) + .status(BookingStatus.valueOf(bookingDTO.getStatus())) + .totalAmount(bookingDTO.getTotalAmount()) + .createdAt(existingBooking.getCreatedAt()) + .build(); + + bookingRepository.save(updatedBooking); + } + + @Override + public void deleteBooking(Integer id) { + Booking booking = findById(id); + bookingRepository.delete(booking); } @Override - public void delete(Integer id) { - bookingRepository.deleteById(id); + public BookingDTO convertToDTO(Booking booking) { + return BookingDTO.builder() + .id(booking.getId()) + .customerId(booking.getCustomer().getId()) + .name(booking.getName()) + .email(booking.getEmail()) + .phone(booking.getPhone()) + .address(booking.getAddress()) + .checkinDate(booking.getCheckinDate()) + .checkoutDate(booking.getCheckoutDate()) + .status(String.valueOf(booking.getStatus())) + .totalAmount(booking.getTotalAmount()) + .build(); } + + @Override + public Booking convertToEntity(BookingDTO bookingDTO) { + User customer = userRepository.findById(Long.valueOf(bookingDTO.getCustomerId())) + .orElseThrow(() -> new RuntimeException("User not found")); + return Booking.builder() + .customer(customer) + .name(bookingDTO.getName()) + .email(bookingDTO.getEmail()) + .phone(bookingDTO.getPhone()) + .address(bookingDTO.getAddress()) + .checkinDate(bookingDTO.getCheckinDate()) + .checkoutDate(bookingDTO.getCheckoutDate()) + .status(BookingStatus.valueOf(bookingDTO.getStatus())) + .totalAmount(bookingDTO.getTotalAmount()) + .build(); + } + + @Override + public Page findPaginatedBookings(Pageable pageable){return bookingRepository.findAll(pageable);} } diff --git a/src/main/java/com/app/services/impl/UserServiceImpl.java b/src/main/java/com/app/services/impl/UserServiceImpl.java index 41b6935..242ea7d 100644 --- a/src/main/java/com/app/services/impl/UserServiceImpl.java +++ b/src/main/java/com/app/services/impl/UserServiceImpl.java @@ -215,4 +215,9 @@ public void saveUserInfo(UserInfoDTO userDTO) { userRepository.save(user); } + @Override + public List findAll() { + return userRepository.findAll(); + } + } diff --git a/src/main/resources/templates/manager/bookings/add-booking.html b/src/main/resources/templates/manager/bookings/add-booking.html new file mode 100644 index 0000000..bfe468f --- /dev/null +++ b/src/main/resources/templates/manager/bookings/add-booking.html @@ -0,0 +1,78 @@ + + + + Add Booking + + + + + + +
+
+
+

Create Booking

+ +
+
+ + +

+
+ +
+ + +

+
+ +
+ + +

+
+ +
+ + +

+
+ +
+ + +

+
+ +
+ + +

+
+ +
+ + +

+
+ +
+ + +

+
+ + +
+
+
+
+
+ + diff --git a/src/main/resources/templates/manager/bookings/edit-booking.html b/src/main/resources/templates/manager/bookings/edit-booking.html new file mode 100644 index 0000000..862b100 --- /dev/null +++ b/src/main/resources/templates/manager/bookings/edit-booking.html @@ -0,0 +1,68 @@ + + + + Edit Booking + + + + + + + +
+
+
+

Edit Booking

+ +
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+
+
+ +
+ + + diff --git a/src/main/resources/templates/manager/bookings/index.html b/src/main/resources/templates/manager/bookings/index.html new file mode 100644 index 0000000..7e5569c --- /dev/null +++ b/src/main/resources/templates/manager/bookings/index.html @@ -0,0 +1,90 @@ + + + + Bookings Management + + + + + + + + +
+
+
+

BOOKINGS MANAGEMENT

+ + + +
+
+ × +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Customer IDNameEmailPhoneCheck-in DateCheck-out DateStatusTotal AmountActions
1John Doejohn@example.com+1234567892024-09-102024-09-15CONFIRMED100.00 +
+ Edit +
+ + +
+
+
+ + + + +
+
+
+ +
+ + diff --git a/src/main/resources/templates/manager/dashboard.html b/src/main/resources/templates/manager/dashboard.html index 5e66a4d..d4b21a8 100644 --- a/src/main/resources/templates/manager/dashboard.html +++ b/src/main/resources/templates/manager/dashboard.html @@ -2,32 +2,142 @@ - Homepage + Admin Dashboard + + + +
-

"HOTEL MANAGER LOGIN"

-

Page /manager/dashboard: dashboard to see manager's hotel

+

HOTEL MANAGER DASHBOARD

+ + +
+ + + + + + + + + + +
+
+
+
+
+
+ Pending Requests
+
18
+
+
+ +
+
+
+
+
+
+ +
+ +
+
+ +
+
Bookings Overview
+
+ +
+
+ +
+
+
+
+ + +
+
+
+
Revenue Sources
+
+ +
+
+ +
+
+
+
+
+