Index: READ.me
===================================================================
--- READ.me	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ READ.me	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,10 @@
+How to start the project:
+
+mvn clean install
+
+To create the database, change the property 'spring.jpa.hibernate.ddl-auto' from validate to create and run the application.
+(Don't forget to revert this before re-running the application. If you don't do this, you will delete all your inserted data.)
+
+To insert test data, change the property 'spring.sql.init.mode' from never to always.
+(Don't forget to revert this before re-running the application. If you don't do this, you will get duplicate data, or the script will fail
+and as a result of this, the application won't start.)
Index: pom.xml
===================================================================
--- pom.xml	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ pom.xml	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -6,5 +6,5 @@
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
-        <version>2.7.4</version>
+        <version>2.7.6</version>
         <relativePath/> <!-- lookup parent from repository -->
     </parent>
@@ -17,4 +17,5 @@
     <properties>
         <java.version>17</java.version>
+        <org.modelmapper.version>3.2.0</org.modelmapper.version>
     </properties>
     <dependencies>
@@ -26,4 +27,9 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.modelmapper</groupId>
+            <artifactId>modelmapper</artifactId>
+            <version>${org.modelmapper.version}</version>
         </dependency>
         <dependency>
Index: c/main/java/edu/gjoko/schedlr/config/AppAuthenticationSuccessHandler.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/config/AppAuthenticationSuccessHandler.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ 	(revision )
@@ -1,67 +1,0 @@
-package edu.gjoko.schedlr.config;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.web.DefaultRedirectStrategy;
-import org.springframework.security.web.RedirectStrategy;
-import org.springframework.security.web.WebAttributes;
-import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-public class AppAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
-
-    protected Log logger = LogFactory.getLog(this.getClass());
-    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
-
-    @Override
-    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
-        handle(request, response, authentication);
-        clearAuthenticationAttributes(request);
-    }
-
-    protected void handle(HttpServletRequest request,
-                          HttpServletResponse response,
-                          Authentication authentication) throws IOException {
-        String targetUrl = determineTargetUrl(authentication);
-
-        if (response.isCommitted()) {
-            logger.debug("Response has already been committed. Unable to redirect to " +
-                    targetUrl);
-            return;
-        }
-        redirectStrategy.sendRedirect(request, response, targetUrl);
-    }
-
-    protected String determineTargetUrl(final Authentication authentication) {
-        Map<String, String> roleTargetUrlMap = new HashMap<>();
-        roleTargetUrlMap.put("DATE", "/date");
-        roleTargetUrlMap.put("GUESS_NUMBER", "/number");
-
-        final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
-        for (final GrantedAuthority grantedAuthority : authorities) {
-            String authorityName = grantedAuthority.getAuthority();
-            if (roleTargetUrlMap.containsKey(authorityName)) {
-                return roleTargetUrlMap.get(authorityName);
-            }
-        }
-        throw new IllegalStateException();
-    }
-
-    protected void clearAuthenticationAttributes(HttpServletRequest request) {
-        HttpSession session = request.getSession(false);
-        if (session == null) {
-            return;
-        }
-        session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
-    }
-}
Index: src/main/java/edu/gjoko/schedlr/config/AppConfig.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/config/AppConfig.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/config/AppConfig.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -15,8 +15,4 @@
     }
 
-    @Bean
-    public AuthenticationSuccessHandler appAuthenticationSuccessHandler() {
-        return new AppAuthenticationSuccessHandler();
-    }
 
     @Bean
Index: src/main/java/edu/gjoko/schedlr/config/AppFilter.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/config/AppFilter.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/config/AppFilter.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -64,5 +64,9 @@
                             break;
                         case "CUSTOMER":
-                            page = "/homepage";
+                            if ("/customer_admin".equals(httpServletRequest.getRequestURI())) {
+                                page = "/customer_admin";
+                            } else {
+                                page = "/homepage";
+                            }
                             break;
                         case "BUSINESS_OWNER":
Index: src/main/java/edu/gjoko/schedlr/config/AppSecurityConfig.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/config/AppSecurityConfig.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/config/AppSecurityConfig.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -32,6 +32,4 @@
     private final BCryptPasswordEncoder passwordEncoder;
 
-    private final AuthenticationSuccessHandler authenticationSuccessHandler;
-
     private final AuthenticationFailureHandler authenticationFailureHandler;
 
@@ -57,7 +55,6 @@
                 .loginPage("/login")
                 .loginProcessingUrl("/login")
-                .successHandler(authenticationSuccessHandler)
                 .failureHandler(authenticationFailureHandler)
-                .defaultSuccessUrl("/homepage")
+                .defaultSuccessUrl("/login")
                 .and()
                 .logout(logout -> logout
Index: src/main/java/edu/gjoko/schedlr/config/ExceptionHandlerAdvice.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/config/ExceptionHandlerAdvice.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/config/ExceptionHandlerAdvice.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,19 @@
+package edu.gjoko.schedlr.config;
+
+import edu.gjoko.schedlr.exceptions.BlockingTimeException;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+@ControllerAdvice
+public class ExceptionHandlerAdvice extends ResponseEntityExceptionHandler {
+
+    @ExceptionHandler(value = BlockingTimeException.class)
+    protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) {
+        return handleExceptionInternal(ex, ex.getMessage(), new HttpHeaders(), HttpStatus.CONFLICT, request);
+    }
+}
Index: src/main/java/edu/gjoko/schedlr/config/MvcConfig.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/config/MvcConfig.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/config/MvcConfig.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -16,4 +16,5 @@
         registry.addViewController("/admin").setViewName("admin");
         registry.addViewController("/business_admin").setViewName("business_admin");
+        registry.addViewController("/customer_admin").setViewName("customer_admin");
     }
 }
Index: c/main/java/edu/gjoko/schedlr/controllers/AdminController.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/AdminController.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ 	(revision )
@@ -1,29 +1,0 @@
-package edu.gjoko.schedlr.controllers;
-
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.GetMapping;
-
-import javax.servlet.http.HttpServletRequest;
-import java.security.Principal;
-
-@Controller
-public class AdminController {
-
-    @GetMapping(path = "/admin")
-    public String getAdminPageTemplate(Model model, HttpServletRequest request) {
-        return "admin";
-    }
-
-    @GetMapping(path = "/business_admin")
-    public String getBusinessAdminPageTemplate(Model model, HttpServletRequest request) {
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        String currentPrincipalName = authentication.getName();
-
-        return "business_admin";
-    }
-
-
-}
Index: src/main/java/edu/gjoko/schedlr/controllers/AdministrationController.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/AdministrationController.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/controllers/AdministrationController.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,30 @@
+package edu.gjoko.schedlr.controllers;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Controller
+public class AdministrationController {
+
+    @GetMapping(path = "/admin")
+    public String getAdminPageTemplate(Model model) {
+        return "admin";
+    }
+
+    @GetMapping(path = "/business_admin")
+    public String getBusinessAdminPageTemplate(Model model) {
+        return "business_admin";
+    }
+
+    @GetMapping(path = "/customer_admin")
+    public String getCustomerAdminPageTemplate(Model model) {
+        return "customer_admin";
+    }
+
+
+}
Index: src/main/java/edu/gjoko/schedlr/controllers/rest/AppointmentApi.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/AppointmentApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/controllers/rest/AppointmentApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,59 @@
+package edu.gjoko.schedlr.controllers.rest;
+
+import edu.gjoko.schedlr.dto.AppointmentDto;
+import edu.gjoko.schedlr.dto.AppointmentInfoDto;
+import edu.gjoko.schedlr.entity.Appointment;
+import edu.gjoko.schedlr.entity.Stakeholder;
+import edu.gjoko.schedlr.mappers.AppointmentDtoMapper;
+import edu.gjoko.schedlr.services.AppointmentsService;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.security.sasl.AuthenticationException;
+import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("api/appointment")
+@AllArgsConstructor
+public class AppointmentApi {
+
+    private final AppointmentsService appointmentsService;
+    private final AppointmentDtoMapper appointmentsMapper;
+
+    @PostMapping
+    public void getBusinessTypes(@RequestBody Appointment appointment, HttpServletRequest request) {
+        Long customerId = (long) request.getSession(true).getAttribute("stakeholderId");
+        Stakeholder customer = new Stakeholder();
+        customer.setId(customerId);
+        appointment.setCustomer(customer);
+        appointmentsService.saveAppointment(appointment);
+    }
+
+    @GetMapping(path = "/business/{businessId}")
+    public List<AppointmentDto> getAppointmentsByBusiness(@PathVariable("businessId") Long businessId) {
+        return appointmentsService.getActiveAppointmentsByBusiness(businessId).stream()
+                .map(appointmentsMapper::appointmentToAppointmentDto)
+                .collect(Collectors.toList());
+    }
+
+    @GetMapping(path = "/future/me")
+    public List<AppointmentInfoDto> getFutureAppointmentsInfoList(HttpServletRequest request) {
+        Long businessOwnerId = (long) request.getSession(true).getAttribute("stakeholderId");
+        return appointmentsService.getFutureAppointmentInfoList(businessOwnerId);
+    }
+
+    @GetMapping(path = "/past/me")
+    public List<AppointmentInfoDto> getPastAppointmentsInfoList(HttpServletRequest request) {
+        Long stakeholderId = (long) request.getSession(true).getAttribute("stakeholderId");
+        return appointmentsService.getPastAppointmentInfoList(stakeholderId);
+    }
+
+    @DeleteMapping("/{appointmentId}")
+    public void cancelAppointment(@PathVariable("appointmentId") Long appointmentId, HttpServletRequest request) throws AuthenticationException {
+        Long stakeholderId = (long) request.getSession(true).getAttribute("stakeholderId");
+        appointmentsService.deleteAppointment(appointmentId, stakeholderId);
+    }
+}
Index: c/main/java/edu/gjoko/schedlr/controllers/rest/AppointmentController.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/AppointmentController.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ 	(revision )
@@ -1,37 +1,0 @@
-package edu.gjoko.schedlr.controllers.rest;
-
-import edu.gjoko.schedlr.entity.Appointment;
-import edu.gjoko.schedlr.entity.Business;
-import edu.gjoko.schedlr.entity.Stakeholder;
-import edu.gjoko.schedlr.repositories.AppointmentRepository;
-import edu.gjoko.schedlr.services.AppointmentsService;
-import edu.gjoko.schedlr.services.StakeholderService;
-import lombok.AllArgsConstructor;
-import org.springframework.web.bind.annotation.*;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.List;
-
-@RestController
-@RequestMapping("api/appointment")
-@AllArgsConstructor
-public class AppointmentController {
-
-    private final AppointmentsService appointmentsService;
-
-    @PostMapping
-    public void getBusinessTypes(@RequestBody Appointment appointment, HttpServletRequest request) {
-        Long customerId = (long) request.getSession(true).getAttribute("stakeholderId");
-        Stakeholder customer = new Stakeholder();
-        customer.setId(customerId);
-        appointment.setCustomer(customer);
-        appointmentsService.saveAppointment(appointment);
-    }
-
-    @GetMapping(path = "/business/{businessId}")
-    public List<Appointment> getAppointmentsByBusiness(@PathVariable("businessId") Long businessId) {
-        var business = new Business();
-        business.setId(businessId);
-        return appointmentsService.getAppointmentsByBusiness(business);
-    }
-}
Index: src/main/java/edu/gjoko/schedlr/controllers/rest/BusinessApi.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/BusinessApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/controllers/rest/BusinessApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,43 @@
+package edu.gjoko.schedlr.controllers.rest;
+
+import edu.gjoko.schedlr.entity.Business;
+import edu.gjoko.schedlr.services.BusinessService;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@RestController
+@RequestMapping("api/business")
+@AllArgsConstructor
+public class BusinessApi {
+
+    private final BusinessService businessService;
+
+    @PostMapping
+    public void saveBusiness(@RequestBody Business business) {
+        businessService.saveBusiness(business);
+    }
+
+    @GetMapping
+    public List<Business> findAll() {
+        return businessService.findAll();
+    }
+
+    @PatchMapping
+    public void updateBusinesses(@RequestBody List<Business> businessList) {
+        businessService.updateBusinesses(businessList);
+    }
+
+    @GetMapping(path = "/me")
+    public Business getPersonalInfo(HttpServletRequest request) {
+        Long businessOwnerId = (long) request.getSession(true).getAttribute("stakeholderId");
+        return businessService.findByOwner(businessOwnerId);
+    }
+
+    @GetMapping(path = "/{businessTypeId}")
+    public List<Business> getBusinessesByBusinessType(@PathVariable("businessTypeId") Long id) {
+        return businessService.findByBusinessTypeAndActiveStatus(id);
+    }
+}
Index: c/main/java/edu/gjoko/schedlr/controllers/rest/BusinessController.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/BusinessController.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ 	(revision )
@@ -1,46 +1,0 @@
-package edu.gjoko.schedlr.controllers.rest;
-
-import edu.gjoko.schedlr.entity.Business;
-import edu.gjoko.schedlr.entity.BusinessType;
-import edu.gjoko.schedlr.services.BusinessService;
-import lombok.AllArgsConstructor;
-import org.springframework.web.bind.annotation.*;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.List;
-
-@RestController
-@RequestMapping("api/business")
-@AllArgsConstructor
-public class BusinessController {
-
-    final BusinessService businessService;
-
-    @PostMapping
-    public void getBusinessTypes(@RequestBody Business business) {
-        businessService.saveBusiness(business);
-    }
-
-    @GetMapping
-    public List<Business> findAll() {
-        return businessService.findAll();
-    }
-
-    @PatchMapping
-    public void updateBusinesses(@RequestBody List<Business> businessList) {
-        businessService.updateBusinesses(businessList);
-    }
-
-    @GetMapping(path = "/me")
-    public Business getPersonalInfo(HttpServletRequest request) {
-        Long businessOwnerId = (long) request.getSession(true).getAttribute("stakeholderId");
-        return businessService.findByOwner(businessOwnerId);
-    }
-
-    @GetMapping(path = "/{businessTypeId}")
-    public List<Business> getBusinessesByBusinessType(@PathVariable("businessTypeId") Long id) {
-        BusinessType businessType = new BusinessType();
-        businessType.setId(id);
-        return businessService.findByBusinessTypeAndActiveStatus(businessType);
-    }
-}
Index: src/main/java/edu/gjoko/schedlr/controllers/rest/CustomerApi.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/CustomerApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/controllers/rest/CustomerApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,29 @@
+package edu.gjoko.schedlr.controllers.rest;
+
+import edu.gjoko.schedlr.entity.Business;
+import edu.gjoko.schedlr.entity.Stakeholder;
+import edu.gjoko.schedlr.services.StakeholderService;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@RestController
+@RequestMapping("api/customer")
+@AllArgsConstructor
+public class CustomerApi {
+
+    private final StakeholderService stakeholderService;
+
+    @GetMapping(path = "/me")
+    public Stakeholder getCustomerInfo(HttpServletRequest request) {
+        Long customerId = (long) request.getSession(true).getAttribute("stakeholderId");
+        return stakeholderService.findById(customerId);
+    }
+
+    @PatchMapping
+    public void updateCustomer(@RequestBody Stakeholder stakeholder) {
+        stakeholderService.saveOrUpdateStakeholder(stakeholder);
+    }
+}
Index: src/main/java/edu/gjoko/schedlr/controllers/rest/NomenclatureApi.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/NomenclatureApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/controllers/rest/NomenclatureApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,22 @@
+package edu.gjoko.schedlr.controllers.rest;
+
+import edu.gjoko.schedlr.entity.BusinessType;
+import edu.gjoko.schedlr.services.NomenclaturesService;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("api/nomenclatures")
+@AllArgsConstructor
+public class NomenclatureApi {
+
+    private final NomenclaturesService nomenclaturesService;
+    @GetMapping( "/businessTypes")
+    public List<BusinessType> getBusinessTypes() {
+        return nomenclaturesService.getBusinessTypes();
+    }
+}
Index: c/main/java/edu/gjoko/schedlr/controllers/rest/NomenclatureController.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/NomenclatureController.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ 	(revision )
@@ -1,22 +1,0 @@
-package edu.gjoko.schedlr.controllers.rest;
-
-import edu.gjoko.schedlr.entity.BusinessType;
-import edu.gjoko.schedlr.services.NomenclaturesService;
-import lombok.AllArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.List;
-
-@RestController
-@RequestMapping("api/nomenclatures")
-@AllArgsConstructor
-public class NomenclatureController {
-
-    private final NomenclaturesService nomenclaturesService;
-    @GetMapping( "/businessTypes")
-    public List<BusinessType> getBusinessTypes() {
-        return nomenclaturesService.getBusinessTypes();
-    }
-}
Index: src/main/java/edu/gjoko/schedlr/controllers/rest/ReviewApi.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/ReviewApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/controllers/rest/ReviewApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,44 @@
+package edu.gjoko.schedlr.controllers.rest;
+
+import edu.gjoko.schedlr.dto.ReviewDto;
+import edu.gjoko.schedlr.entity.Review;
+import edu.gjoko.schedlr.entity.Stakeholder;
+import edu.gjoko.schedlr.mappers.ReviewDtoMapper;
+import edu.gjoko.schedlr.services.ReviewService;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@RestController
+@RequestMapping("api/review")
+@AllArgsConstructor
+public class ReviewApi {
+
+    private final ReviewService reviewService;
+    private final ReviewDtoMapper reviewDtoMapper;
+
+    @PostMapping
+    public void getBusinessTypes(@RequestBody Review review, HttpServletRequest request) {
+        Long customerId = (long) request.getSession(true).getAttribute("stakeholderId");
+        Stakeholder customer = new Stakeholder();
+        customer.setId(customerId);
+        reviewService.save(review, customerId);
+    }
+
+    @GetMapping(path = "/me")
+    public List<ReviewDto> getReviews(HttpServletRequest request) {
+        Long customerId = (long) request.getSession(true).getAttribute("stakeholderId");
+        return reviewService.getReviewsByCustomerId(customerId).stream()
+                .map(reviewDtoMapper::mapToReviewDto)
+                .toList();
+    }
+
+    @GetMapping(path = "/{serviceId}")
+    public List<ReviewDto> getReviewsForService(@PathVariable("serviceId") Long serviceId) {
+        return reviewService.getReviewsByServiceId(serviceId).stream()
+                .map(reviewDtoMapper::mapToReviewDto)
+                .toList();
+    }
+}
Index: src/main/java/edu/gjoko/schedlr/controllers/rest/UserApi.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/UserApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/controllers/rest/UserApi.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,19 @@
+package edu.gjoko.schedlr.controllers.rest;
+
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping("api/user")
+@AllArgsConstructor
+public class UserApi {
+
+    @GetMapping(path = "/me")
+    public boolean findAll(HttpServletRequest request) {
+        return request.getSession(true).getAttribute("stakeholderId") != null;
+    }
+}
Index: c/main/java/edu/gjoko/schedlr/controllers/rest/UserController.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/controllers/rest/UserController.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ 	(revision )
@@ -1,21 +1,0 @@
-package edu.gjoko.schedlr.controllers.rest;
-
-import edu.gjoko.schedlr.entity.Business;
-import lombok.AllArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.List;
-
-@RestController
-@RequestMapping("api/user")
-@AllArgsConstructor
-public class UserController {
-
-    @GetMapping(path = "/me")
-    public boolean findAll(HttpServletRequest request) {
-        return request.getSession(true).getAttribute("stakeholderId") != null;
-    }
-}
Index: src/main/java/edu/gjoko/schedlr/dto/AppointmentDto.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/dto/AppointmentDto.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/dto/AppointmentDto.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,17 @@
+package edu.gjoko.schedlr.dto;
+
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class AppointmentDto {
+
+    private String title;
+    private LocalDateTime startTime;
+    private LocalDateTime endTime;
+}
Index: src/main/java/edu/gjoko/schedlr/dto/AppointmentInfoDto.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/dto/AppointmentInfoDto.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/dto/AppointmentInfoDto.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,18 @@
+package edu.gjoko.schedlr.dto;
+
+import lombok.*;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class AppointmentInfoDto {
+
+    private Long appointmentId;
+    private String fullName;
+    private String email;
+    private String phoneNumber;
+    private String timePeriod;
+    private String serviceName;
+    private String status;
+}
Index: src/main/java/edu/gjoko/schedlr/dto/ReviewDto.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/dto/ReviewDto.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/dto/ReviewDto.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,20 @@
+package edu.gjoko.schedlr.dto;
+
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class ReviewDto {
+
+    private String customerName;
+    private String businessName;
+    private String serviceName;
+    private Long rating;
+    private String comment;
+    private LocalDateTime created;
+}
Index: src/main/java/edu/gjoko/schedlr/entity/Appointment.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/entity/Appointment.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/entity/Appointment.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -1,8 +1,6 @@
 package edu.gjoko.schedlr.entity;
 
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import com.fasterxml.jackson.annotation.JsonBackReference;
+import lombok.*;
 import org.springframework.data.annotation.LastModifiedDate;
 import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -14,6 +12,5 @@
 @EntityListeners(AuditingEntityListener.class)
 @Table(name = "appointment")
-@Getter
-@Setter
+@Data
 @NoArgsConstructor
 @AllArgsConstructor
@@ -30,15 +27,17 @@
     private LocalDateTime endTime;
 
-    @OneToOne
-    @JoinColumn(name = "customer_id", referencedColumnName = "id")
+    @ManyToOne
+    @JoinColumn(name = "stakeholder_id")
+    @JsonBackReference(value = "customerAppointments")
     private Stakeholder customer;
 
     @ManyToOne
-    @JoinColumn(name = "business_id")
-    private Business business;
+    @JoinColumn(name = "service_id")
+    @JsonBackReference(value = "serviceAppointments")
+    private Service service;
 
-    @ManyToOne
-    @JoinColumn(name = "service_id")
-    private Service service;
+    @Column(name = "appointment_status", length = 32, columnDefinition = "varchar(32) default 'NEW'")
+    @Enumerated(EnumType.STRING)
+    private AppointmentStatus appointmentStatus = AppointmentStatus.NEW;
 
     @Column(name = "created")
@@ -49,3 +48,8 @@
     @LastModifiedDate
     private LocalDateTime modified;
+
+    public String getTimePeriod() {
+        return startTime + " - " + endTime;
+    }
+
 }
Index: src/main/java/edu/gjoko/schedlr/entity/AppointmentStatus.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/entity/AppointmentStatus.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/entity/AppointmentStatus.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,8 @@
+package edu.gjoko.schedlr.entity;
+
+public enum AppointmentStatus {
+    NEW,
+    CANCELLED_BY_CUSTOMER,
+    CANCELLED_BY_BUSINESS_OWNER,
+    FINISHED_AND_REVIEWED_BY_USER;
+}
Index: src/main/java/edu/gjoko/schedlr/entity/Business.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/entity/Business.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/entity/Business.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -37,5 +37,5 @@
     private BusinessType businessType;
 
-    @ManyToOne()
+    @OneToOne(cascade = CascadeType.PERSIST)
     @JoinColumn(name = "owner_id", referencedColumnName = "id", nullable = false)
     @JsonProperty("owner")
@@ -43,5 +43,5 @@
 
     @OneToMany(mappedBy = "business", cascade = CascadeType.PERSIST)
-    @JsonManagedReference
+    @JsonManagedReference(value = "services")
     private List<Service> services;
 
Index: src/main/java/edu/gjoko/schedlr/entity/Review.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/entity/Review.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/entity/Review.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,46 @@
+package edu.gjoko.schedlr.entity;
+
+import com.fasterxml.jackson.annotation.JsonBackReference;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+
+@Entity
+@EntityListeners(AuditingEntityListener.class)
+@Table(name = "review")
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class Review {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.SEQUENCE)
+    private Long id;
+
+    @Column(name = "rating")
+    private Integer rating;
+
+    @Column(name = "comment")
+    private String comment;
+
+    @OneToOne
+    @JoinColumn(name = "appointment_id", referencedColumnName = "id")
+    @JsonProperty("appointment")
+    private Appointment appointment;
+
+    @Column(name = "created")
+    @LastModifiedDate
+    private LocalDateTime created;
+
+    @Column(name = "modified")
+    @LastModifiedDate
+    private LocalDateTime modified;
+}
Index: src/main/java/edu/gjoko/schedlr/entity/Service.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/entity/Service.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/entity/Service.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -3,4 +3,5 @@
 import com.fasterxml.jackson.annotation.JsonBackReference;
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonManagedReference;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -13,4 +14,5 @@
 import javax.persistence.*;
 import java.time.LocalDateTime;
+import java.util.List;
 
 @Entity
@@ -33,5 +35,11 @@
     private Integer price;
 
-    @OneToOne(cascade = CascadeType.PERSIST)
+    @Column(name = "cumulated_rating")
+    private Float rating = 0.0f;
+
+    @Column(name = "reviews_count")
+    private Integer reviewsCount = 0;
+
+    @OneToOne(cascade = CascadeType.MERGE)
     @JoinColumn(name = "service_type_id", referencedColumnName = "id")
     private ServiceType serviceType;
@@ -39,6 +47,10 @@
     @ManyToOne
     @JoinColumn(name = "business_id")
-    @JsonBackReference
+    @JsonBackReference(value = "services")
     private Business business;
+
+    @OneToMany(mappedBy="service")
+    @JsonManagedReference(value = "serviceAppointments")
+    private List<Appointment> appointments;
 
     @Column(name = "created")
Index: src/main/java/edu/gjoko/schedlr/entity/Stakeholder.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/entity/Stakeholder.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/entity/Stakeholder.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -2,4 +2,5 @@
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonManagedReference;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -12,4 +13,5 @@
 import javax.persistence.*;
 import java.time.LocalDateTime;
+import java.util.List;
 
 @Entity
@@ -39,4 +41,7 @@
     private String email;
 
+    @Column(name = "phone_number")
+    private String phoneNumber;
+
     @Column(name = "username")
     private String username;
@@ -44,4 +49,8 @@
     @Column(name = "password")
     private String password;
+
+    @OneToMany(mappedBy = "customer")
+    @JsonManagedReference(value = "customerAppointments")
+    private List<Appointment> appointments;
 
     @Column(name = "created")
@@ -54,3 +63,7 @@
     @JsonIgnore
     private LocalDateTime modified;
+
+    public String getFullName() {
+        return firstName + " " + lastName;
+    }
 }
Index: src/main/java/edu/gjoko/schedlr/exceptions/BlockingTimeException.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/exceptions/BlockingTimeException.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/exceptions/BlockingTimeException.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -3,5 +3,5 @@
 public class BlockingTimeException extends RuntimeException {
 
-    public static final String MESSAGE = "The selected dates are overlapping with another appointment";
+    public static final String MESSAGE = "Error! The selected dates are overlapping with another appointment.";
     public BlockingTimeException() {
         super(MESSAGE);
Index: src/main/java/edu/gjoko/schedlr/mappers/AppointmentDtoMapper.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/mappers/AppointmentDtoMapper.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/mappers/AppointmentDtoMapper.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,25 @@
+package edu.gjoko.schedlr.mappers;
+
+import edu.gjoko.schedlr.dto.AppointmentDto;
+import edu.gjoko.schedlr.entity.Appointment;
+import org.modelmapper.Condition;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.TypeMap;
+import org.modelmapper.config.Configuration;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AppointmentDtoMapper extends ModelMapper {
+
+    public AppointmentDtoMapper() {
+        this.getConfiguration().setFieldMatchingEnabled(true).setFieldAccessLevel(Configuration.AccessLevel.PRIVATE);
+        TypeMap<Appointment, AppointmentDto> propertyMapper = this.createTypeMap(Appointment.class, AppointmentDto.class);
+        Condition<String, String> hasTitle = ctx -> ctx.getSource() != null;
+        propertyMapper.addMappings(
+                mapper -> mapper.when(hasTitle).map(appointment -> appointment.getService().getServiceType().getName(), AppointmentDto::setTitle)
+        );
+    }
+    public AppointmentDto appointmentToAppointmentDto(Appointment appointment) {
+        return this.map(appointment, AppointmentDto.class);
+    }
+}
Index: src/main/java/edu/gjoko/schedlr/mappers/AppointmentInfoDtoBusinessMapper.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/mappers/AppointmentInfoDtoBusinessMapper.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/mappers/AppointmentInfoDtoBusinessMapper.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,32 @@
+package edu.gjoko.schedlr.mappers;
+
+import edu.gjoko.schedlr.dto.AppointmentInfoDto;
+import edu.gjoko.schedlr.entity.Appointment;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.TypeMap;
+import org.modelmapper.config.Configuration;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AppointmentInfoDtoBusinessMapper extends ModelMapper {
+
+    public AppointmentInfoDtoBusinessMapper() {
+        this.getConfiguration().setFieldMatchingEnabled(true).setFieldAccessLevel(Configuration.AccessLevel.PRIVATE);
+        TypeMap<Appointment, AppointmentInfoDto> propertyMapper = this.createTypeMap(Appointment.class, AppointmentInfoDto.class);
+        propertyMapper.addMappings(
+                mapper -> {
+                    mapper.map(Appointment::getId, AppointmentInfoDto::setAppointmentId);
+                    mapper.map(appointment -> appointment.getCustomer().getFullName(), AppointmentInfoDto::setFullName);
+                    mapper.map(appointment -> appointment.getCustomer().getEmail(), AppointmentInfoDto::setEmail);
+                    mapper.map(appointment -> appointment.getCustomer().getPhoneNumber(),AppointmentInfoDto::setPhoneNumber);
+                    mapper.map(Appointment::getTimePeriod, AppointmentInfoDto::setTimePeriod);
+                    mapper.map(appointment -> appointment.getService().getServiceType().getName(), AppointmentInfoDto::setServiceName);
+                    mapper.map(Appointment::getAppointmentStatus, AppointmentInfoDto::setStatus);
+                }
+        );
+    }
+
+    public AppointmentInfoDto appointmentToAppointmentInfoDto(Appointment appointment) {
+        return this.map(appointment, AppointmentInfoDto.class);
+    }
+}
Index: src/main/java/edu/gjoko/schedlr/mappers/AppointmentInfoDtoCustomerMapper.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/mappers/AppointmentInfoDtoCustomerMapper.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/mappers/AppointmentInfoDtoCustomerMapper.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,32 @@
+package edu.gjoko.schedlr.mappers;
+
+import edu.gjoko.schedlr.dto.AppointmentInfoDto;
+import edu.gjoko.schedlr.entity.Appointment;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.TypeMap;
+import org.modelmapper.config.Configuration;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AppointmentInfoDtoCustomerMapper extends ModelMapper {
+
+    public AppointmentInfoDtoCustomerMapper() {
+        this.getConfiguration().setFieldMatchingEnabled(true).setFieldAccessLevel(Configuration.AccessLevel.PRIVATE);
+        TypeMap<Appointment, AppointmentInfoDto> propertyMapper = this.createTypeMap(Appointment.class, AppointmentInfoDto.class);
+        propertyMapper.addMappings(
+                mapper -> {
+                    mapper.map(Appointment::getId, AppointmentInfoDto::setAppointmentId);
+                    mapper.map(appointment -> appointment.getService().getBusiness().getOwner().getFullName(), AppointmentInfoDto::setFullName);
+                    mapper.map(appointment -> appointment.getCustomer().getEmail(), AppointmentInfoDto::setEmail);
+                    mapper.map(appointment -> appointment.getCustomer().getPhoneNumber(),AppointmentInfoDto::setPhoneNumber);
+                    mapper.map(Appointment::getTimePeriod, AppointmentInfoDto::setTimePeriod);
+                    mapper.map(appointment -> appointment.getService().getServiceType().getName(), AppointmentInfoDto::setServiceName);
+                    mapper.map(Appointment::getAppointmentStatus, AppointmentInfoDto::setStatus);
+                }
+        );
+    }
+
+    public AppointmentInfoDto appointmentToAppointmentInfoDto(Appointment appointment) {
+        return this.map(appointment, AppointmentInfoDto.class);
+    }
+}
Index: src/main/java/edu/gjoko/schedlr/mappers/ReviewDtoMapper.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/mappers/ReviewDtoMapper.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/mappers/ReviewDtoMapper.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,32 @@
+package edu.gjoko.schedlr.mappers;
+
+import edu.gjoko.schedlr.dto.AppointmentDto;
+import edu.gjoko.schedlr.dto.ReviewDto;
+import edu.gjoko.schedlr.entity.Appointment;
+import edu.gjoko.schedlr.entity.Review;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.TypeMap;
+import org.modelmapper.config.Configuration;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ReviewDtoMapper extends ModelMapper {
+
+    public ReviewDtoMapper() {
+        this.getConfiguration().setFieldMatchingEnabled(true).setFieldAccessLevel(Configuration.AccessLevel.PRIVATE);
+        TypeMap<Review, ReviewDto> propertyMapper = this.createTypeMap(Review.class, ReviewDto.class);
+        propertyMapper.addMappings(mapper -> {
+            mapper.map(review -> review.getAppointment().getService().getBusiness().getCompanyName(), ReviewDto::setBusinessName);
+            mapper.map(review -> review.getAppointment().getCustomer().getFullName(), ReviewDto::setCustomerName);
+            mapper.map(review -> review.getAppointment().getService().getServiceType().getName(), ReviewDto::setServiceName);
+            mapper.map(Review::getComment, ReviewDto::setComment);
+            mapper.map(Review::getRating, ReviewDto::setRating);
+            mapper.map(Review::getCreated, ReviewDto::setCreated);
+        }
+        );
+    }
+
+    public ReviewDto mapToReviewDto(Review review) {
+        return this.map(review, ReviewDto.class);
+    }
+}
Index: src/main/java/edu/gjoko/schedlr/repositories/AppointmentRepository.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/repositories/AppointmentRepository.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/repositories/AppointmentRepository.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -2,5 +2,4 @@
 
 import edu.gjoko.schedlr.entity.Appointment;
-import edu.gjoko.schedlr.entity.Business;
 import edu.gjoko.schedlr.entity.Stakeholder;
 import org.springframework.data.jpa.repository.JpaRepository;
@@ -10,21 +9,46 @@
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Optional;
 
 @Repository
 public interface AppointmentRepository extends JpaRepository<Appointment, Long> {
 
-    List<Appointment> getAppointmentsByBusiness(Business business);
+    @Query(value = "select ap from Appointment as ap " +
+            "where ap.service.business.id = :businessId " +
+            "and ap.appointmentStatus = 'NEW'")
+    List<Appointment> getActiveAppointmentsByBusiness(Long businessId);
 
-    List<Appointment> getAppointmentsByCustomer(Stakeholder customer);
+    @Query( value = "select ap from Appointment as ap " +
+            "where ap.service.business.id = :businessId " +
+            "and (" +
+            "(ap.startTime between :startDate and :endDate) " +
+            "or (ap.endTime between :startDate and :endDate) " +
+            "or (:startDate = ap.startTime and ap.startTime = :endDate)" +
+            "or (:startDate > ap.startTime and ap.endTime > :endDate)" +
+            ")")
+    List<Appointment> findBlockingAppointments(Long businessId, LocalDateTime startDate, LocalDateTime endDate);
 
-    List<Appointment> findAppointmentsByBusinessAndStartTimeBetweenOrEndTimeBetween(Business business, LocalDateTime startTime, LocalDateTime endTime, LocalDateTime startTime1, LocalDateTime endTime1);
+    @Query(value = "select ap from Appointment  as ap " +
+            "where ap.service.business.owner.id = :businessOwnerId " +
+            "and ap.startTime > :now ")
+    List<Appointment> findFutureAppointmentsByBusinessOwnerId(Long businessOwnerId, LocalDateTime now);
 
-    @Query(value = "select a from Appointment a " +
-            "where business_id = :businessId " +
-            "and (" +
-            "(start_time between :startDate and :endDate) " +
-            "or (end_time between :startDate and :endDate) " +
-            "or (:startDate <= start_time and end_time >= :endDate)" +
-            ")", nativeQuery = true)
-    List<Appointment> findBlockingAppointments(Long businessId, LocalDateTime startDate, LocalDateTime endDate);
+    @Query(value = "select ap from Appointment  as ap " +
+            "where ap.customer.id = :customerId " +
+            "and ap.startTime < :now")
+    List<Appointment> findPastAppointmentsByCustomerId(Long customerId, LocalDateTime now);
+
+    @Query(value = "select ap from Appointment  as ap " +
+            "where ap.service.business.owner.id = :businessOwnerId " +
+            "and ap.startTime < :now ")
+    List<Appointment> findPastAppointmentsByBusinessOwnerId(Long businessOwnerId, LocalDateTime now);
+
+    @Query(value = "select ap from Appointment  as ap " +
+            "where ap.customer.id = :customerId " +
+            "and ap.startTime > :now ")
+    List<Appointment> findFutureAppointmentsByCustomerId(Long customerId, LocalDateTime now);
+
+    Optional<Appointment> findAppointmentByIdAndCustomer_Id(Long id, Long customerId);
+
+    Optional<Appointment> findAppointmentByIdAndService_Business_Owner_Id(Long id, Long ownerId);
 }
Index: src/main/java/edu/gjoko/schedlr/repositories/BusinessRepository.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/repositories/BusinessRepository.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/repositories/BusinessRepository.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -17,3 +17,5 @@
 
     List<Business> findBusinessesByBusinessTypeAndBusinessStatus(BusinessType businessType, BusinessStatus businessStatus);
+
+    List<Business> findBusinessesByBusinessStatusAndBusinessType_Id(BusinessStatus businessStatus, Long id);
 }
Index: src/main/java/edu/gjoko/schedlr/repositories/ReviewRepository.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/repositories/ReviewRepository.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/repositories/ReviewRepository.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,16 @@
+package edu.gjoko.schedlr.repositories;
+
+import edu.gjoko.schedlr.entity.Review;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface ReviewRepository extends JpaRepository<Review, Long> {
+
+
+    List<Review> findAllByAppointment_Customer_Id(Long customerId);
+
+    List<Review> findAllByAppointment_Service_Id(Long serviceId);
+}
Index: src/main/java/edu/gjoko/schedlr/services/AppointmentsService.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/services/AppointmentsService.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/services/AppointmentsService.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -1,15 +1,21 @@
 package edu.gjoko.schedlr.services;
 
-import edu.gjoko.schedlr.entity.Appointment;
-import edu.gjoko.schedlr.entity.Business;
-import edu.gjoko.schedlr.entity.Stakeholder;
+import edu.gjoko.schedlr.dto.AppointmentInfoDto;
+import edu.gjoko.schedlr.entity.*;
 import edu.gjoko.schedlr.exceptions.BlockingTimeException;
+import edu.gjoko.schedlr.mappers.AppointmentInfoDtoBusinessMapper;
+import edu.gjoko.schedlr.mappers.AppointmentInfoDtoCustomerMapper;
 import edu.gjoko.schedlr.repositories.AppointmentRepository;
 import edu.gjoko.schedlr.repositories.ServiceRepository;
-import edu.gjoko.schedlr.repositories.ServiceTypeRepository;
+import edu.gjoko.schedlr.repositories.StakeholderRepository;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 
+import javax.persistence.EntityNotFoundException;
+import javax.security.sasl.AuthenticationException;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 @Service
@@ -18,13 +24,18 @@
 
     private final AppointmentRepository appointmentRepository;
+    private final StakeholderRepository stakeholderRepository;
+    private final ServiceRepository serviceRepository;
+    private final AppointmentInfoDtoBusinessMapper appointmentInfoDtoBusinessMapper;
+    private final AppointmentInfoDtoCustomerMapper appointmentInfoDtoCustomerMapper;
 
-    private final ServiceRepository serviceRepository;
 
     public void saveAppointment(Appointment appointment) {
         var service = serviceRepository.findById(appointment.getService().getId()).get();
-        appointment.setEndTime(appointment.getStartTime().plusMinutes(service.getDuration()));
+
+        // remove 1 minute in order to prevent overlapping
+        appointment.setEndTime(appointment.getStartTime().plusMinutes(service.getDuration() - 1));
         List<Appointment> blockingAppointments = appointmentRepository.
                 findBlockingAppointments(
-                        appointment.getBusiness().getId(), appointment.getStartTime(), appointment.getEndTime());
+                        appointment.getService().getBusiness().getId(), appointment.getStartTime(), appointment.getEndTime());
 
         // check to see if there are blocking exceptions
@@ -35,10 +46,64 @@
     }
 
-    public List<Appointment> getAppointmentsByBusiness(Business business) {
-        return appointmentRepository.getAppointmentsByBusiness(business);
+    public List<AppointmentInfoDto> getFutureAppointmentInfoList(Long stakeholderId) {
+        StakeholderType type = stakeholderRepository.findById(stakeholderId).get().getStakeholderType();
+        List<AppointmentInfoDto> appointmentInfoDtoList = new ArrayList<>();
+
+        switch (type) {
+            case BUSINESS_OWNER -> appointmentInfoDtoList = appointmentRepository.findFutureAppointmentsByBusinessOwnerId(stakeholderId, LocalDateTime.now())
+                    .stream()
+                    .map(appointmentInfoDtoBusinessMapper::appointmentToAppointmentInfoDto)
+                    .toList();
+            case CUSTOMER -> appointmentInfoDtoList = appointmentRepository.findFutureAppointmentsByCustomerId(stakeholderId, LocalDateTime.now())
+                    .stream()
+                    .map(appointmentInfoDtoCustomerMapper::appointmentToAppointmentInfoDto)
+                    .toList();
+        }
+
+        return appointmentInfoDtoList;
     }
 
-    private List<Appointment> getAppointmentsByCustomer(Stakeholder stakeholder) {
-        return appointmentRepository.getAppointmentsByCustomer(stakeholder);
+    public List<AppointmentInfoDto> getPastAppointmentInfoList(Long stakeholderId) {
+        StakeholderType type = stakeholderRepository.findById(stakeholderId).get().getStakeholderType();
+        List<Appointment> appointmentInfoDtoList = new ArrayList<>();
+
+        switch (type) {
+            case BUSINESS_OWNER -> appointmentInfoDtoList = appointmentRepository.findPastAppointmentsByBusinessOwnerId(stakeholderId, LocalDateTime.now());
+            case CUSTOMER -> appointmentInfoDtoList = appointmentRepository.findPastAppointmentsByCustomerId(stakeholderId, LocalDateTime.now());
+        }
+
+        return appointmentInfoDtoList
+                .stream()
+                .map(appointmentInfoDtoBusinessMapper::appointmentToAppointmentInfoDto)
+                .toList();
+
+    }
+
+    public List<Appointment> getActiveAppointmentsByBusiness(Long businessId) {
+        return appointmentRepository.getActiveAppointmentsByBusiness(businessId);
+    }
+
+    public void deleteAppointment(Long appointmentId, Long stakeholderId) throws AuthenticationException {
+        Optional<Stakeholder> stakeholderOptional = stakeholderRepository.findById(stakeholderId);
+        if (stakeholderOptional.isPresent()) {
+            Optional<Appointment> optional = appointmentRepository.findAppointmentByIdAndCustomer_Id(appointmentId, stakeholderId);
+            if (optional.isEmpty()) {
+                optional = appointmentRepository.findAppointmentByIdAndService_Business_Owner_Id(appointmentId, stakeholderId);
+                if (optional.isEmpty()) {
+                    throw new EntityNotFoundException("No appointment was found for the give stakeholder and appointment id.");
+                } else {
+                    softDeleteAppointment(optional.get(), AppointmentStatus.CANCELLED_BY_BUSINESS_OWNER);
+                }
+            } else {
+                softDeleteAppointment(optional.get(), AppointmentStatus.CANCELLED_BY_CUSTOMER);
+            }
+        } else {
+            throw new AuthenticationException();
+        }
+    }
+
+    public void softDeleteAppointment(Appointment appointment, AppointmentStatus appointmentStatus) {
+        appointment.setAppointmentStatus(appointmentStatus);
+        appointmentRepository.save(appointment);
     }
 }
Index: src/main/java/edu/gjoko/schedlr/services/BusinessService.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/services/BusinessService.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/services/BusinessService.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -2,11 +2,9 @@
 
 import edu.gjoko.schedlr.entity.*;
-import edu.gjoko.schedlr.repositories.BusinessRepository;
-import edu.gjoko.schedlr.repositories.ServiceRepository;
-import edu.gjoko.schedlr.repositories.ServiceTypeRepository;
-import edu.gjoko.schedlr.repositories.StakeholderRepository;
+import edu.gjoko.schedlr.repositories.*;
 import lombok.AllArgsConstructor;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 
 import java.util.List;
@@ -22,4 +20,5 @@
     private final ServiceTypeRepository serviceTypeRepository;
     private final ServiceRepository serviceRepository;
+    private final StakeholderService stakeholderService;
 
     private final StakeholderRepository stakeholderRepository;
@@ -54,7 +53,7 @@
                 .stream()
                 .forEach(business -> {
-                    stakeholderRepository.save(business.getOwner());
-                    serviceRepository.saveAll(business.getServices());
-                    businessRepository.save(business);
+                    stakeholderService.saveOrUpdateStakeholder(business.getOwner());
+                    saveOrUpdateServices(business.getServices());
+                    saveOrUpdateBusiness(business);
                 });
     }
@@ -66,6 +65,28 @@
     }
 
-    public List<Business> findByBusinessTypeAndActiveStatus(BusinessType businessType) {
-        return businessRepository.findBusinessesByBusinessTypeAndBusinessStatus(businessType, ACTIVE);
+    public List<Business> findByBusinessTypeAndActiveStatus(Long businessTypeId) {
+        return businessRepository.findBusinessesByBusinessStatusAndBusinessType_Id(ACTIVE, businessTypeId);
+    }
+
+
+
+    private void saveOrUpdateBusiness(Business business) {
+        if (business.getId() != null) {
+            var foundBusinessEntity = businessRepository.findById(business.getId());
+            business.setCreated(foundBusinessEntity.get().getCreated());
+        }
+        businessRepository.save(business);
+    }
+
+    private void saveOrUpdateServices(List<edu.gjoko.schedlr.entity.Service> serviceList) {
+        if (!CollectionUtils.isEmpty(serviceList)) {
+            serviceList.forEach( service -> {
+                if (service.getId() != null) {
+                    var found = serviceRepository.findById(service.getId());
+                    service.setCreated(found.get().getCreated());
+                }
+            });
+            serviceRepository.saveAll(serviceList);
+        }
     }
 }
Index: src/main/java/edu/gjoko/schedlr/services/ReviewService.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/services/ReviewService.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/java/edu/gjoko/schedlr/services/ReviewService.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,57 @@
+package edu.gjoko.schedlr.services;
+
+import edu.gjoko.schedlr.entity.Appointment;
+import edu.gjoko.schedlr.entity.AppointmentStatus;
+import edu.gjoko.schedlr.entity.Review;
+import edu.gjoko.schedlr.repositories.AppointmentRepository;
+import edu.gjoko.schedlr.repositories.ReviewRepository;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.persistence.EntityNotFoundException;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+@AllArgsConstructor
+public class ReviewService {
+
+    private final ReviewRepository reviewRepository;
+    private final AppointmentRepository appointmentRepository;
+
+    public void save(Review review, Long customerId) {
+        Optional<Appointment> appointmentOptional = appointmentRepository.findById(review.getAppointment().getId());
+        if (appointmentOptional.isEmpty()) {
+            throw new EntityNotFoundException();
+        }
+        Appointment appointment = appointmentOptional.get();
+        if (appointment.getCustomer().getId().equals(customerId)) {
+            review.setAppointment(appointment);
+            markAppointmentAsReviewed(review.getAppointment());
+            calculateAndUpdateCumulatedRatingForService(appointment.getService(), review.getRating());
+            reviewRepository.save(review);
+        } else {
+            throw new EntityNotFoundException();
+        }
+    }
+
+    public List<Review> getReviewsByServiceId(Long businessId) {
+        return reviewRepository.findAllByAppointment_Service_Id(businessId);
+    }
+
+    public List<Review> getReviewsByCustomerId(Long customerId) {
+        return reviewRepository.findAllByAppointment_Customer_Id(customerId);
+    }
+
+    private void markAppointmentAsReviewed(Appointment appointment) {
+        appointment.setAppointmentStatus(AppointmentStatus.FINISHED_AND_REVIEWED_BY_USER);
+    }
+
+    private void calculateAndUpdateCumulatedRatingForService(edu.gjoko.schedlr.entity.Service service, int rating) {
+        int newReviewsCount = service.getReviewsCount() + 1;
+        float currentRating = service.getRating();
+        float newRating = (currentRating * service.getReviewsCount() + rating) / newReviewsCount;
+        service.setReviewsCount(newReviewsCount);
+        service.setRating(newRating);
+    }
+}
Index: src/main/java/edu/gjoko/schedlr/services/StakeholderService.java
===================================================================
--- src/main/java/edu/gjoko/schedlr/services/StakeholderService.java	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/java/edu/gjoko/schedlr/services/StakeholderService.java	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -6,4 +6,6 @@
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.stereotype.Service;
+
+import java.util.Optional;
 
 @Service
@@ -28,3 +30,15 @@
         return stakeholderRepository.findById(id).get();
     }
+
+    public void saveOrUpdateStakeholder(Stakeholder stakeholder) {
+        if (stakeholder.getId() != null) {
+            var found = stakeholderRepository.findById(stakeholder.getId()).get();
+            found.setFirstName(stakeholder.getFirstName());
+            found.setLastName(stakeholder.getLastName());
+            found.setPhoneNumber(stakeholder.getPhoneNumber());
+            found.setEmail(stakeholder.getEmail());
+            found.setUsername(stakeholder.getUsername());
+            stakeholderRepository.save(found);
+        }
+    }
 }
Index: src/main/resources/application.properties
===================================================================
--- src/main/resources/application.properties	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/application.properties	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -1,5 +1,5 @@
-spring.datasource.url=jdbc:postgresql://localhost:5433/schedlr
-spring.datasource.username=postgres
-spring.datasource.password=silic0n
+spring.datasource.url=jdbc:postgresql://localhost:5432/schedlr
+spring.datasource.username=gjoko
+spring.datasource.password=gjoko
 
 spring.sql.init.mode=never
Index: src/main/resources/data.sql
===================================================================
--- src/main/resources/data.sql	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/data.sql	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -1,8 +1,2 @@
-insert into stakeholder (id, created, email, first_name, last_name, modified, password, stakeholder_type, username)
-values (nextval('hibernate_sequence'), current_timestamp, 'admin@schedlr.com', 'admin', 'admin', current_timestamp, '$2a$10$DJyjt.Vj/U8MEuYX1PXL9ukQSMwXHVdhre3POlTqpYzNxHB5gu/MW','ADMIN', 'admin');
-
-insert into stakeholder (id, created, email, first_name, last_name, modified, password, stakeholder_type, username)
-values (nextval('hibernate_sequence'), current_timestamp, 'user1@schedlr.com', 'gjoko', 'kostadinov', current_timestamp, '$2a$10$Zc28AcCpAgxB.e67UMF/2.FgchjH9QWB7z8nP0TdkrFneV4IHPXji','CUSTOMER', 'user');
-
 insert into business_type (id, created, modified, name)
 values (1, current_timestamp, current_timestamp, 'tailor'),
@@ -21,2 +15,44 @@
        (nextval('hibernate_sequence'), current_timestamp, current_timestamp, 'dress shortening', 1),
        (nextval('hibernate_sequence'), current_timestamp, current_timestamp, 'holes fixing', 1);
+
+insert into stakeholder (id, created, phone_number, email, first_name, last_name, modified, password, stakeholder_type, username)
+values (nextval('hibernate_sequence'), current_timestamp, '075658123', 'admin@schedlr.com', 'admin', 'admin', current_timestamp, '$2a$10$DJyjt.Vj/U8MEuYX1PXL9ukQSMwXHVdhre3POlTqpYzNxHB5gu/MW','ADMIN', 'admin'),
+        (nextval('hibernate_sequence'), current_timestamp, '071658125', 'user1@schedlr.com', 'gjoko', 'kostadinov', current_timestamp, '$2a$10$Zc28AcCpAgxB.e67UMF/2.FgchjH9QWB7z8nP0TdkrFneV4IHPXji','CUSTOMER', 'user'),
+        (11111, current_timestamp, '078658523',  'c@c.com', 'Kliment', 'Nedelkovski', current_timestamp, '$2a$10$OratLSMKNsqd7Re.Md3I4.jrRHtTQNaLz/wUKo.I98..GufnD48uG','CUSTOMER', 'kliment123'),
+        (10000, current_timestamp, '071218123', 'q@c.com', 'Stefan', 'Petrovski', current_timestamp, '$2a$10$x10O8eLp1r1wHwxDGvyo5.TBL216p8h797JJjQYEfFTbj/1bnCMki','BUSINESS_OWNER', 'stefan123');
+
+insert into business (id, business_status, company_name, created, modified, business_type_id, owner_id)
+values (10001, 'ACTIVE', 'krojach stefan', current_timestamp, current_timestamp, 1, 10000);
+
+insert into service (id, created, modified, duration, price, cumulated_rating, reviews_count, business_id, service_type_id)
+values (10002, current_timestamp, current_timestamp, 60, 400, 0, 0, 10001, 6),
+       (10003, current_timestamp, current_timestamp, 30, 200, 0, 0, 10001, 7);
+
+insert into appointment (id, created, modified, start_time, end_time, stakeholder_id, service_id)
+values
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '10:00:00', current_date + time '10:59:00', 11111, 10002),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '11:00:00', current_date + time '11:29:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '13:00:00', current_date + time '13:59:00', 11111, 10002),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '14:00:00', current_date + time '14:29:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '14:30:00', current_date + time '14:59:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 1 + time '11:30:00', current_date + 1 + time '11:59:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 1 + time '13:30:00', current_date + 1 + time '14:29:00', 11111, 10002),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 1 + time '15:00:00', current_date + 1 + time '15:29:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 1 + time '14:30:00', current_date + 1 + time '14:59:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 1 + time '09:00:00', current_date - 1 + time '09:29:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 1 + time '10:00:00', current_date - 1 + time '10:59:00', 11111, 10002),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 1 + time '12:00:00', current_date - 1 + time '12:29:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 1 + time '12:40:00', current_date - 1 + time '12:09:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 2 + time '10:30:00', current_date - 2 + time '10:59:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 2 + time '12:10:00', current_date - 2 + time '13:09:00', 11111, 10002),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 2 + time '15:10:00', current_date - 2 + time '15:39:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 2 + time '14:15:00', current_date - 2 + time '14:44:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 3 + time '11:10:00', current_date - 3 + time '11:39:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 3 + time '14:30:00', current_date - 3 + time '14:59:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 3 + time '08:00:00', current_date - 3 + time '08:29:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 2 + time '10:10:00', current_date + 2 + time '10:29:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 2 + time '11:15:00', current_date + 2 + time '11:44:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 2 + time '14:30:00', current_date + 2 + time '14:59:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 3 + time '11:14:00', current_date + 3 + time '11:44:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 3 + time '13:25:00', current_date + 3 + time '13:54:00', 11111, 10003),
+    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 3 + time '14:00:00', current_date + 3 + time '14:29:00', 11111, 10003);
Index: src/main/resources/static/css/customer_admin.css
===================================================================
--- src/main/resources/static/css/customer_admin.css	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/resources/static/css/customer_admin.css	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,66 @@
+.rate{
+
+    border-bottom-right-radius: 12px;
+    border-bottom-left-radius: 12px;
+
+}
+
+.rating {
+    display: flex;
+    flex-direction: row-reverse;
+    justify-content: center
+}
+
+.rating>input {
+    display: none
+}
+
+.rating>label {
+    position: relative;
+    width: 1em;
+    font-size: 30px;
+    font-weight: 300;
+    color: #FFD600;
+    cursor: pointer
+}
+
+.rating>label::before {
+    content: "\2605";
+    position: absolute;
+    opacity: 0
+}
+
+.rating>label:hover:before,
+.rating>label:hover~label:before {
+    opacity: 1 !important
+}
+
+.rating>input:checked~label:before {
+    opacity: 1
+}
+
+.rating:hover>input:checked~label:before {
+    opacity: 0.4
+}
+
+
+.buttons{
+    top: 36px;
+    position: relative;
+}
+
+
+.rating-submit{
+    border-radius: 15px;
+    color: #fff;
+    height: 49px;
+}
+
+
+.rating-submit:hover{
+    color: #fff;
+}
+
+#my-reviews {
+    padding: 20px;
+}
Index: src/main/resources/static/css/homepage.css
===================================================================
--- src/main/resources/static/css/homepage.css	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/static/css/homepage.css	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -74,4 +74,3 @@
 .card {
     padding: 10px;
-    
 }
Index: src/main/resources/static/js/admin.js
===================================================================
--- src/main/resources/static/js/admin.js	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/static/js/admin.js	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -1,48 +1,36 @@
 $(document).ready(function() {
-    var companies = {};
+    let companies = [];
 
-    $.ajax({
-       url: "http://localhost:8080/api/business"
-    }).then(function (data) {
+    getCompanies().then(function (data) {
         companies = data;
-        var $el = $("#table_body");
-
-        $.each(data, function (index, obj) {
-            if(obj.businessStatus == "NEW" || obj.businessStatus == "DEACTIVATED") {
-                $el.append("<tr>\n" +
-                    "            <th scope=\"row\">" + obj.id + "</th>\n" +
-                    "            <td>" + obj.companyName + "</td>\n" +
-                    "            <td>" + obj.owner.firstName + " " + obj.owner.lastName + "</td>\n" +
-                    "            <td><input class=\"form-check-input\" type=\"checkbox\" value=\"" + obj.id +"\"></td>\n" +
-                    "        </tr>")
-            }
-            if (obj.businessStatus == "ACTIVE") {
-                $el.append("<tr>\n" +
-                    "            <th scope=\"row\">" + obj.id + "</th>\n" +
-                    "            <td>" + obj.companyName + "</td>\n" +
-                    "            <td>" + obj.owner.firstName + " " + obj.owner.lastName + "</td>\n" +
-                    "            <td><input class=\"form-check-input\" type=\"checkbox\" checked value=\"" + obj.id +"\"></td>\n" +
-                    "        </tr>")
-            }
-        });
     });
 
     $('#save_button').click(function () {
+        var companiesToSave = [];
         $.each($('#table_body tr'), function(index, row) {
-            if(companies[index].businessStatus == "NEW" &&  $($($(row).children() [3]).children()[0]).is(':checked')) {
-                companies[index]['businessStatus'] = 'ACTIVE';
+            if((companies[index].businessStatus === "NEW" || companies[index].businessStatus === "DEACTIVATED") &&  $($($(row).children() [3]).children()[0]).is(':checked')) {
+                let cloneCompany = { ...companies[index] }
+                cloneCompany['businessStatus'] = 'ACTIVE';
+                companiesToSave.push(cloneCompany);
             }
-            if(companies[index].businessStatus == "ACTIVE" && ! $($($(row).children() [3]).children()[0]).is(':checked')) {
-                companies[index]['businessStatus'] = 'DEACTIVATED';
+            if(companies[index].businessStatus === "ACTIVE" && !$($($(row).children() [3]).children()[0]).is(':checked')) {
+                let cloneCompany = { ...companies[index] }
+                cloneCompany['businessStatus'] = 'DEACTIVATED';
+                companiesToSave.push(cloneCompany);
             }
         });
-        console.log(JSON.stringify(companies));
+        console.log(JSON.stringify(companiesToSave));
+
+
         $.ajax({
             url: "http://localhost:8080/api/business",
             type:"PATCH",
-            data: JSON.stringify(companies),
+            data: JSON.stringify(companiesToSave),
             contentType:"application/json; charset=utf-8",
             dataType: 'text',
             success: function(succ){
+                getCompanies().then(function (data) {
+                    companies = data;
+                });
                 alert( "Updates applied successfully" );
             },
@@ -53,3 +41,31 @@
         event.preventDefault();
     });
+
+    function getCompanies() {
+        return $.ajax({
+            url: "http://localhost:8080/api/business"
+        }).then(function (data) {
+            var $el = $("#table_body");
+            $("#new_table tbody").html("");
+            $.each(data, function (index, obj) {
+                if(obj.businessStatus == "NEW" || obj.businessStatus == "DEACTIVATED") {
+                    $el.append("<tr>\n" +
+                        "            <th scope=\"row\">" + obj.id + "</th>\n" +
+                        "            <td>" + obj.companyName + "</td>\n" +
+                        "            <td>" + obj.owner.firstName + " " + obj.owner.lastName + "</td>\n" +
+                        "            <td><input class=\"form-check-input\" type=\"checkbox\" value=\"" + obj.id +"\"></td>\n" +
+                        "        </tr>")
+                }
+                if (obj.businessStatus == "ACTIVE") {
+                    $el.append("<tr>\n" +
+                        "            <th scope=\"row\">" + obj.id + "</th>\n" +
+                        "            <td>" + obj.companyName + "</td>\n" +
+                        "            <td>" + obj.owner.firstName + " " + obj.owner.lastName + "</td>\n" +
+                        "            <td><input class=\"form-check-input\" type=\"checkbox\" checked value=\"" + obj.id +"\"></td>\n" +
+                        "        </tr>")
+                }
+            });
+            return data;
+        });
+    }
 });
Index: src/main/resources/static/js/business_admin.js
===================================================================
--- src/main/resources/static/js/business_admin.js	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/static/js/business_admin.js	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -1,51 +1,8 @@
 $(document).ready(function() {
-
     var business = {};
 
-    $.ajax({
-        url: "http://localhost:8080/api/business/me"
-    }).then(function (data) {
-        business = data;
-        var $header = $("#header");
-
-        // header
-        $header.text($header.text() + " " + business["owner"]['firstName'] + " " + business['owner']['lastName']);
-
-        // business info
-        if(business['businessStatus'] == 'NEW') {
-            $('#new_business_warning').removeAttr("hidden");
-        }
-        $('#business_status').val(business['businessStatus']);
-        $('#business_type').val(business['businessType']['text']);
-
-        // owner info
-        $('#firstName').val(business['owner']['firstName']);
-        $('#lastName').val(business['owner']['lastName']);
-        $('#email').val(business['owner']['email']);
-        $('#username').val(business['owner']['username']);
-
-        // services info
-        var $el = $("#predefined_services_admin_panel");
-        $el.empty();
-        $.each(business['services'], function (index, obj) {
-            $el.append(
-                '<div class=\"form-outline mb-4\">' +
-                '    <div class="row">' +
-                '        <div class="col-md-6">\n' +
-                '            <input class="form-check-input" type="checkbox" checked value=\"' + obj.id + '\" id=\"' + obj.id + '\">\n' +
-                '            <label class="form-check-label" for=\"' + obj.id + '\">\n' +
-                obj['serviceType'].name +
-                '            </label>\n' +
-                '        </div>' +
-                '        <div class=\"form-outline col-md-2 d-grid\">' +
-                '            <input type=\"text\" id=\"' + obj.id + obj['serviceType'].name.replace(/\s/g, "") + "duration" + '\" class=\"form-control\" placeholder="time" value=\"' + obj.duration + '" />' +
-                '        </div>' +
-                '        <div class=\"form-outline col-md-2 d-grid\">' +
-                '            <input type=\"text\" id=\"' + obj.id + obj['serviceType'].name.replace(/\s/g, "") + "price" + '\" class=\"form-control\" placeholder="price" value=\"' + obj.price + '" />' +
-                '        </div>' +
-                '    </div>' +
-                '</div>');
-        });
-    });
+    getBusinessInfo(business);
+
+    getAppointments();
 
     $("#add_service").click(function () {
@@ -122,8 +79,8 @@
     $("#update_owner_button").click(function() {
         businesses = [];
-        console.log("Gjoko");
         business['owner']['firstName'] = $('#firstName').val();
         business['owner']['lastName'] = $('#lastName').val();
         business['owner']['email'] = $('#email').val();
+        business['owner']['phoneNumber'] = $('#phoneNumber').val();
         business['owner']['username'] = $('#username').val();
 
@@ -147,2 +104,98 @@
     event.preventDefault();
 });
+
+function cancelAppointment(appointmentId) {
+    if (confirm("Are you sure you want to cancel the appointment?")) {
+        $.ajax({
+            url: "http://localhost:8080/api/appointment/" + appointmentId,
+            type:"DELETE"
+        }).success(function (data) {
+            alert("Appointment successfully canceled.")
+            getAppointments();
+        }).error(function (error) {
+            alert("Something went wrong.");
+        });
+    }
+}
+
+function getAppointments() {
+    $.ajax({
+        url: "http://localhost:8080/api/appointment/future/me"
+    }).then(function (data) {
+        console.log(data);
+        var $el = $("#bookings-table-body");
+        $el.empty();
+        $.each(data, function (index, obj) {
+            var element =
+                '<tr>' +
+                '   <th scope="row">' + (parseInt(index) + 1) + '</th>' +
+                '   <td>' + obj['fullName'] + '</td>' +
+                '   <td>' + obj['email'] + '</td>' +
+                '   <td>' + obj['phoneNumber'] + '</td>' +
+                '   <td>' + obj['timePeriod'] + '</td>' +
+                '   <td>' + obj['serviceName'] + '</td>';
+
+            switch (obj['status']) {
+                case 'NEW':
+                    element += '   <td><button type="button" class="btn btn-danger" onclick="cancelAppointment(' + obj['appointmentId'] + ')">Cancel appointment</button></td>';
+                    break;
+                case 'CANCELLED_BY_CUSTOMER':
+                    element += '   <td><button type="button" class="btn btn-secondary" disabled>Cancelled by customer</button></td>';
+                    break;
+                case 'CANCELLED_BY_BUSINESS_OWNER':
+                    element += '   <td><button type="button" class="btn btn-secondary" disabled>Cancelled by business owner</button></td>';
+                    break;
+            }
+            element+='</tr>';
+
+            $el.append(element);
+        });
+    });
+}
+
+function getBusinessInfo(business) {
+    $.ajax({
+        url: "http://localhost:8080/api/business/me"
+    }).then(function (data) {
+        business = data;
+        var $header = $("#header");
+
+        // header
+        $header.text($header.text() + " " + business["owner"]['firstName'] + " " + business['owner']['lastName']);
+
+        // business info
+        if(business['businessStatus'] == 'NEW') {
+            $('#new_business_warning').removeAttr("hidden");
+        }
+        $('#business_status').val(business['businessStatus']);
+        $('#business_type').val(business['businessType']['text']);
+
+        // owner info
+        $('#firstName').val(business['owner']['firstName']);
+        $('#lastName').val(business['owner']['lastName']);
+        $('#phoneNumber').val(business['owner']['phoneNumber']);
+        $('#email').val(business['owner']['email']);
+        $('#username').val(business['owner']['username']);
+
+        // services info
+        var $el = $("#predefined_services_admin_panel");
+        $el.empty();
+        $.each(business['services'], function (index, obj) {
+            $el.append(
+                '<div class=\"form-outline mb-4\">' +
+                '    <div class="row">' +
+                '        <div class="col-md-6">\n' +
+                '            <input class="form-check-input" type="checkbox" checked value=\"' + obj.id + '\" id=\"' + obj.id + '\">\n' +
+                '            <label class="form-check-label" for=\"' + obj.id + '\">\n' + obj['serviceType'].name + '</label>\n' +
+                '        </div>' +
+                '        <div class=\"form-outline col-md-2 d-grid\">' +
+                '            <input type=\"text\" id=\"' + obj.id + obj['serviceType'].name.replace(/\s/g, "") + "duration" + '\" class=\"form-control\" placeholder="time" value=\"' + obj.duration + '" />' +
+                '        </div>' +
+                '        <div class=\"form-outline col-md-2 d-grid\">' +
+                '            <input type=\"text\" id=\"' + obj.id + obj['serviceType'].name.replace(/\s/g, "") + "price" + '\" class=\"form-control\" placeholder="price" value=\"' + obj.price + '" />' +
+                '        </div>' +
+                '    </div>' +
+                '</div>');
+        });
+    });
+}
Index: src/main/resources/static/js/customer_admin.js
===================================================================
--- src/main/resources/static/js/customer_admin.js	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/resources/static/js/customer_admin.js	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,256 @@
+$(document).ready(function() {
+
+    getCustomerInfo();
+
+    getFutureAppointments();
+
+    getPastAppointments();
+
+    getReviewsForCustomer();
+
+    $("#update_customer_button").click(function() {
+        var customerForSave = customer;
+        customerForSave['firstName'] = $('#firstName').val();
+        customerForSave['lastName'] = $('#lastName').val();
+        customerForSave['email'] = $('#email').val();
+        customerForSave['phoneNumber'] = $('#phoneNumber').val();
+        customerForSave['username'] = $('#username').val();
+
+        console.log(JSON.stringify(customerForSave));
+
+        $.ajax({
+            url: "http://localhost:8080/api/customer",
+            type:"PATCH",
+            data: JSON.stringify(customerForSave),
+            contentType:"application/json; charset=utf-8",
+            dataType: 'text',
+            success: function(succ){
+                alert( "Updates applied successfully" );
+            },
+            error: function(err) {
+                alert(err);
+            }
+        });
+    });
+
+
+    $("#post-review-button").click(function () {
+        var ratingRadios = document.getElementsByName('rating');
+        var rating = 0;
+        for (i = 0; i < ratingRadios.length; i++) {
+            if (ratingRadios[i].checked) {
+                rating = ratingRadios[i].value;
+            }
+        }
+        var id = $('.id-class').attr('id') ;
+        var message = $('#message-text').val();
+        postAppointmentReview(id, rating, message);
+    });
+
+    $('#reviewModal').on('show.bs.modal', function (event) {
+        // Button that triggered the modal
+        const button = event.relatedTarget
+        // Extract info from data-bs-* attributes
+        const recipientName = button.getAttribute('data-bs-fullName');
+        const recipientId = button.getAttribute('data-bs-id');
+
+        console.log(recipientName + ' ' + recipientId);
+        // If necessary, you could initiate an Ajax request here
+        // and then do the updating in a callback.
+
+        // Update the modal's content.
+        const modalTitle = reviewModal.querySelector('.modal-title');
+        const modalIdDiv = reviewModal.querySelector('.id-class');
+        const modalBodyInput = reviewModal.querySelector('.modal-body input');
+
+        modalTitle.textContent = `Review for ${recipientName}`;
+
+        modalIdDiv.id = recipientId;
+        modalBodyInput.value = recipientName;
+    })
+});
+
+function goToHomePage() {
+    window.location = "/homepage";
+}
+
+function getCustomerInfo() {
+    $.ajax({
+        url: "http://localhost:8080/api/customer/me"
+    }).success(function (customer) {
+        var $header = $("#fullName");
+        $header.text(customer['firstName'] + " " + customer['lastName']);
+
+        // customer info
+        $('#firstName').val(customer['firstName']);
+        $('#lastName').val(customer['lastName']);
+        $('#phoneNumber').val(customer['phoneNumber']);
+        $('#email').val(customer['email']);
+        $('#username').val(customer['username']);
+    }).error(function (error) {
+        console.log(JSON.stringify(error));
+    });
+}
+
+function getFutureAppointments(){
+    $.ajax({
+        url: "http://localhost:8080/api/appointment/future/me"
+    }).then(function (data) {
+        console.log(data);
+        var $el = $("#appointments-table-body");
+        $el.empty();
+        $.each(data, function (index, obj) {
+            var element =
+                '<tr>' +
+                '   <th scope="row">' + (parseInt(index) + 1) + '</th>' +
+                '   <td>' + obj['fullName'] + '</td>' +
+                '   <td>' + obj['email'] + '</td>' +
+                '   <td>' + obj['phoneNumber'] + '</td>' +
+                '   <td>' + obj['timePeriod'] + '</td>' +
+                '   <td>' + obj['serviceName'] + '</td>';
+
+            switch (obj['status']) {
+                case 'NEW':
+                    element += '   <td><button type="button" class="btn btn-danger" onclick="cancelAppointment(' + obj['appointmentId'] + ')">Cancel appointment</button></td>';
+                    break;
+                case 'CANCELLED_BY_CUSTOMER':
+                    element += '   <td><button type="button" class="btn btn-secondary" disabled>Cancelled by customer</button></td>';
+                    break;
+                case 'CANCELLED_BY_BUSINESS_OWNER':
+                    element += '   <td><button type="button" class="btn btn-secondary" disabled>Cancelled by business owner</button></td>';
+                    break;
+            }
+            element+='</tr>';
+
+            $el.append(element);
+        });
+    });
+}
+
+function getPastAppointments(){
+    $.ajax({
+        url: "http://localhost:8080/api/appointment/past/me"
+    }).then(function (data) {
+        var $el = $("#past-appointments-table-body");
+        $el.empty();
+        $.each(data, function (index, obj) {
+            var element=
+                '<tr>' +
+                '   <th scope="row">' + (parseInt(index) + 1) + '</th>' +
+                '   <td>' + obj['fullName'] + '</td>' +
+                '   <td>' + obj['email'] + '</td>' +
+                '   <td>' + obj['phoneNumber'] + '</td>' +
+                '   <td>' + obj['timePeriod'] + '</td>' +
+                '   <td>' + obj['serviceName'] + '</td>';
+
+            switch (obj['status']) {
+                case 'NEW':
+                    element += '<td><button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#reviewModal" data-bs-id="' + obj['appointmentId'] + '" data-bs-fullName="' + obj['fullName'] + '">Leave a review</button></td>';
+                    break;
+                case 'CANCELLED_BY_CUSTOMER':
+                    element += '   <td><button type="button" class="btn btn-secondary" disabled>Cancelled by you</button></td>';
+                    break;
+                case 'CANCELLED_BY_BUSINESS_OWNER':
+                    element += '   <td><button type="button" class="btn btn-secondary" disabled>Cancelled by business owner</button></td>';
+                    break;
+                case 'FINISHED_AND_REVIEWED_BY_USER':
+                    element += '   <td><button type="button" class="btn btn-success" disabled>Already reviewed</button></td>';
+                    break;
+            }
+            element+='</tr>';
+
+            $el.append(element);
+        });
+    });
+}
+function generateStars(number) {
+    return '☆'.repeat(number);
+}
+
+function generateBackgroundColor(number) {
+    var color = '';
+    switch (number) {
+        case 5:
+            color = 'text-bg-primary';
+            break;
+        case 4:
+            color = 'text-bg-success';
+            break;
+        case 3:
+            color = 'text-bg-secondary';
+            break;
+        case 2:
+            color = 'text-bg-warning';
+            break;
+        case 1:
+            color = 'text-bg-danger';
+            break;
+    }
+    return color;
+}
+
+function getReviewsForCustomer(){
+    $.ajax({
+        url: "http://localhost:8080/api/review/me"
+    }).then(function (data) {
+        console.log(data);
+        var $el = $("#my-reviews");
+        $el.empty();
+
+        $.each(data, function (index, obj) {
+            var element = '<div class="card m-3" style="max-width: 300px; padding: 0;">';
+            element +=  '<div class="card-header ' + generateBackgroundColor(obj['rating']) + '">' + generateStars(obj['rating']) + '</div>';
+            element += '<ul class="list-group list-group-flush">';
+            element += '<li class="list-group-item"><i><b>Business:</b></i> ' + obj['businessName'] + '</li>';
+            element += '<li class="list-group-item"><i><b>Service:</b></i> ' + obj['serviceName'] + '</li>';
+            element += '<li class="list-group-item"><i><b>Reviewer:</b></i> ' + obj['customerName'] + '</li>';
+            element += '<li class="list-group-item"><i><b>Comment:</b></i> ' + obj['comment'] + '</li>';
+            element += '<li class="list-group-item"><small class="text-body-secondary"><i>Created:</i> ' + obj['created'] + '</small></li>';
+            element += '</ul>';
+            element += '</div>';
+
+            $el.append(element);
+        });
+    });
+}
+
+function postAppointmentReview(appointmentId, rating, review) {
+    var appointment = {};
+    var ratingObj = {};
+    appointment['id'] = parseInt(appointmentId);
+    ratingObj['appointment'] = appointment;
+    ratingObj['rating'] = parseInt(rating);
+    ratingObj['comment'] = review;
+
+    $.ajax({
+        url: "http://localhost:8080/api/review",
+        type:"POST",
+        data: JSON.stringify(ratingObj),
+        contentType:"application/json; charset=utf-8",
+        dataType: 'text',
+        success: function(succ){
+            alert("Successful appointment!");
+            $('#reviewModal').modal('toggle');
+            getPastAppointments();
+        },
+        error: function(error) {
+            alert(error.responseText);
+            $('#reviewModal').modal('toggle');
+        }
+    });
+}
+
+function cancelAppointment(appointmentId) {
+    if (confirm("Are you sure you want to cancel the appointment?")) {
+        $.ajax({
+            url: "http://localhost:8080/api/appointment/" + appointmentId,
+            type:"DELETE"
+        }).success(function (data) {
+            alert("Appointment successfully canceled.")
+            getAppointments();
+        }).error(function (error) {
+            alert("Something went wrong.");
+        });
+    }
+}
+
Index: src/main/resources/static/js/fullcalendar.js
===================================================================
--- src/main/resources/static/js/fullcalendar.js	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/static/js/fullcalendar.js	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -332,6 +332,4 @@
 	/* View Rendering
 	-----------------------------------------------------------------------------*/
-	
-
 	function changeView(newViewName) {
 		if (!currentView || newViewName != currentView.name) {
@@ -480,5 +478,4 @@
 	// TODO: going forward, most of this stuff should be directly handled by the view
 
-
 	function refetchEvents() { // can be called as an API method
 		clearEvents();
@@ -538,10 +535,6 @@
 	}
 
-
-
 	/* Header Updating
 	-----------------------------------------------------------------------------*/
-
-
 	function updateTitle() {
 		header.updateTitle(currentView.title);
Index: src/main/resources/static/js/homepage.js
===================================================================
--- src/main/resources/static/js/homepage.js	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/static/js/homepage.js	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -3,4 +3,6 @@
     var businesses = {};
     var date = new Date();
+    var selectedServices = {};
+    var events = [];
     var d = date.getDate();
     var m = date.getMonth();
@@ -69,4 +71,5 @@
 
     $("#companyType").change(function () {
+        resetRatingCard();
         var selectedVal = $(this).find(':selected').val();
         var selectedObj = businessTypes[selectedVal - 1];
@@ -75,5 +78,4 @@
         }).then(function (data) {
             businesses = data;
-            console.log(data);
             var $el = $("#company");
             var $servicesEl = $("#service");
@@ -84,30 +86,123 @@
                 $el.append("<option value=" + obj.id + ">" + obj.companyName + "</option>");
             });
+
+            if (data && data.length === 1) {
+                selectedServices = data[0]["services"];
+                resetAndAppendServices(selectedServices);
+            }
         });
     });
 
     $("#company").change(function () {
+        resetRatingCard();
         var selectedVal = $(this).find(':selected').val();
         $.ajax({
             url: "http://localhost:8080/api/appointment/business/" + selectedVal
         }).then(function (data) {
-            console.log(data);
-            var $el = $("#service");
-            emptyDropdown($el);
-
-            var services = businesses.find(item => item.id == selectedVal)['services'];
-            console.log(services);
-
-            $.each(services, function (index, obj) {
-                $el.append("<option value=" + obj.id + ">" + obj.serviceType.name + "</option>");
-            });
-        });
-    });
-
+            // get already stored service from in-memory
+            selectedServices = businesses.find(item => item.id == selectedVal)['services'];
+            resetAndAppendServices(selectedServices);
+        });
+        $('#calendar').fullCalendar('refetchEvents');
+    });
+
+    $("#service").change(function () {
+        var selectedVal = $("#service").find(':selected').val();
+        var service = selectedServices.find(item => item.id == selectedVal);
+        setRatingCard(service['serviceType']['name'], service['rating'], service['reviewsCount'], selectedVal);
+    });
+
+    $("#startdatetime").change(function() {
+        var date = new Date(document.getElementById("startdatetime").valueAsDate);
+        var selectedVal = $("#service").find(':selected').val();
+        var minutes = selectedServices.find(item => item.id == selectedVal)['duration'];
+        date.setMinutes(date.getMinutes() + minutes);
+        document.getElementById("enddatetime").value = date.toISOString().slice(0, 16);
+    });
 
     /* initialize the calendar
     -----------------------------------------------------------------*/
-
-    var calendar =  $('#calendar').fullCalendar({
+    loadCalendar();
+
+    resetRatingCard();
+
+    $("#createAppointment").click(function() {
+        var appointment = {};
+        var business  = {};
+        business['id'] = parseInt($("#company").val());
+        appointment['service'] = {'id': parseInt($("#service").val()), 'business':business};
+        appointment['startTime'] = $("#startdatetime").val();
+
+        $.ajax({
+            url: "http://localhost:8080/api/appointment",
+            type:"POST",
+            data: JSON.stringify(appointment),
+            contentType:"application/json; charset=utf-8",
+            dataType: 'text',
+            success: function(succ){
+                alert("Successful appointment!");
+                var companyId = parseInt($("#company").find(':selected').val());
+                getAppointmentsByBusiness(companyId, events);
+                destroyCalendar();
+                loadCalendar(events);
+            },
+            error: function(error) {
+                alert(error.responseText);
+            }
+        });
+    });
+
+});
+
+document.getElementById("login").addEventListener("click", function(event){
+   window.location = "/login";
+});
+
+function resetAndAppendServices(services) {
+    var $el = $("#service");
+    emptyDropdown($el);
+
+    $.each(services, function (index, obj) {
+        $el.append("<option value=" + obj.id + ">" + obj.serviceType.name + "</option>");
+    });
+}
+
+function emptyDropdown(element) {
+    var defaultOption = element.children().get(0);
+    element.children().remove();
+    element.append(defaultOption);
+}
+
+/*loadCalendar([{'title': 'Gjoko', 'start': '2023-09-01 01:00:00', 'end': '2023-09-01 01:30:00', 'allDay':false, 'color': 'blue'}])*/
+
+function getAppointmentsByBusiness(businessId, events) {
+    $.ajax({
+        url: "http://localhost:8080/api/appointment/business/" + businessId,
+        type:"GET",
+        contentType:"application/json; charset=utf-8",
+        dataType: 'text',
+        success: function(data){
+            // reset events
+            events = [];
+            console.log(data);
+           $.each(JSON.parse(data), function (index, object) {
+               var event = {}
+               event['title'] = object['title'];
+               event['start'] = object['startTime'].replace('T', ' ');
+               event['end'] = object['endTime'].replace('T', ' ');
+               event['allDay'] = false;
+               event['color'] = 'blue';
+               events.push(event);
+           });
+
+        },
+        error: function(error) {
+            console.log(error.responseText);
+        }
+    });
+}
+
+function loadCalendar(events) {
+    $('#calendar').fullCalendar({
         header: {
             left: 'title',
@@ -178,82 +273,111 @@
 
         },
-
-        events: [
-            {
-                title: 'All Day Event',
-                start: new Date(y, m, 1)
-            },
-            {
-                id: 999,
-                title: 'Repeating Event',
-                start: new Date(y, m, d-3, 16, 0),
-                allDay: false,
-                className: 'info'
-            },
-            {
-                id: 999,
-                title: 'Repeating Event',
-                start: new Date(y, m, d+4, 16, 0),
-                allDay: false,
-                className: 'info'
-            },
-            {
-                title: 'Meeting',
-                start: new Date(y, m, d, 10, 30),
-                allDay: false,
-                className: 'info'
-            },
-            {
-                title: 'Lunch',
-                start: new Date(y, m, d, 12, 0),
-                end: new Date(y, m, d, 14, 0),
-                allDay: false,
-                className: 'info'
-            },
-            {
-                title: 'Birthday Party',
-                start: new Date(y, m, d+1, 19, 0),
-                end: new Date(y, m, d+1, 22, 30),
-                allDay: false,
-            },
-            {
-                title: 'Click for Google',
-                start: new Date(y, m, 28),
-                end: new Date(y, m, 29),
-                url: 'http://google.com/',
-                className: 'success'
-            }
-        ],
-    });
-
-    $("#createAppointment").click(function() {
-        var appointment = {};
-        appointment['business'] = {'id': parseInt($("#company").val())};
-        appointment['service'] = {'id': parseInt($("#service").val())};
-        appointment['startTime'] = $("#startdatetime").val();
-
-        $.ajax({
-            url: "http://localhost:8080/api/appointment",
-            type:"POST",
-            data: JSON.stringify(appointment),
-            contentType:"application/json; charset=utf-8",
-            dataType: 'text',
-            success: function(succ){
-                console.log('success');
-            },
-            error: function(err) {
-                console.log(JSON.stringify(err));
-            }
-        });
-    })
-
-});
-
-document.getElementById("login").addEventListener("click", function(event){
-   window.location = "/login";
-});
-
-function emptyDropdown(element) {
-    var defaultOption = element.children().get(0);
-    element.children().remove().end().append(defaultOption);
-}
+        events: function (start, end, callback) {
+            var selectedVal = $("#company").find(':selected').val();
+            if(!isNaN(parseInt(selectedVal))) {
+                $.ajax({
+                    url: "http://localhost:8080/api/appointment/business/" + selectedVal,
+                    type:"GET",
+                    contentType:"application/json; charset=utf-8",
+                    dataType: 'text',
+                    success: function(data){
+                        // reset events
+                        var events = [];
+                        $.each(JSON.parse(data), function (index, object) {
+                            var event = {}
+                            event['title'] = object['title'];
+                            event['start'] = object['startTime'].replace('T', ' ');
+                            event['end'] = object['endTime'].replace('T', ' ');
+                            event['allDay'] = false;
+                            event['color'] = '#3b71ca';
+                            event['textColor'] = '#FFFFFF';
+                            events.push(event);
+                        });
+                        // load events
+                        callback(events);
+                    },
+                    error: function(error) {
+                        console.log(error.responseText);
+                    }
+                });
+            }
+        },
+    });
+}
+
+function goToProfile() {
+    window.location = "/customer_admin";
+}
+
+function destroyCalendar() {
+    $('#calendar').fullCalendar('destroy');
+}
+
+function resetRatingCard() {
+    $('#rating-service-name').empty();
+    $('#rating-value').empty();
+    $('#rating-count').empty();
+    $('#reviews-li').remove();
+}
+
+function setRatingCard(name, value, count, serviceId) {
+    $('#rating-service-name').text(name);
+    $('#rating-value').text(value);
+    $('#rating-count').text(count);
+    $('#reviews-li').remove();
+    getReviewsForService(serviceId);
+
+}
+
+function getReviewsForService(serviceId){
+    $.ajax({
+        url: "http://localhost:8080/api/review/" + serviceId
+    }).then(function (data) {
+        var $el = $("#reviewsModalBody");
+        $('#reviews-ul').append($('<td class="form-outline mb-4"><button type="button" id="reviews-li" class="btn btn-primary btn-block" data-bs-toggle="modal" data-bs-target="#showReviewsModal">Checkout reviews</button></td>'));
+        $el.empty();
+
+        $.each(data, function (index, obj) {
+            var element = '<div class="card m-3" style="max-width: 300px; padding: 0;">';
+            element +=  '<div class="card-header" style="' + generateHeaderStyle(obj['rating']) + '">' + generateStars(obj['rating']) + '</div>';
+            element += '<ul class="list-group list-group-flush">';
+            element += '<li class="list-group-item"><i><b>Business:</b></i> ' + obj['businessName'] + '</li>';
+            element += '<li class="list-group-item"><i><b>Service:</b></i> ' + obj['serviceName'] + '</li>';
+            element += '<li class="list-group-item"><i><b>Reviewer:</b></i> ' + obj['customerName'] + '</li>';
+            element += '<li class="list-group-item"><i><b>Comment:</b></i> ' + obj['comment'] + '</li>';
+            element += '<li class="list-group-item"><small class="text-body-secondary"><i>Created:</i> ' + obj['created'] + '</small></li>';
+            element += '</ul>';
+            element += '</div>';
+
+            $el.append(element);
+        });
+    });
+}
+
+function generateStars(number) {
+    return '☆'.repeat(number);
+}
+
+function generateHeaderStyle(number) {
+    var style = '';
+    switch (number) {
+        case 5:
+            style = 'background-color: RGBA(13,110,253,var(--bs-bg-opacity,1)) !important; ';
+            break;
+        case 4:
+            style = 'background-color: RGBA(25,135,84,var(--bs-bg-opacity,1)) !important; ';
+            break;
+        case 3:
+            style = 'background-color: RGBA(108,117,125,var(--bs-bg-opacity,1)) !important; ';
+            break;
+        case 2:
+            style = 'background-color: RGBA(255,193,7,var(--bs-bg-opacity,1)) !important; ';
+            break;
+        case 1:
+            style = 'background-color: RGBA(220,53,69,var(--bs-bg-opacity,1)) !important; ';
+            break;
+    }
+
+    style += ' color: #fff !important;'
+    return style;
+}
Index: src/main/resources/static/js/register_business.js
===================================================================
--- src/main/resources/static/js/register_business.js	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/static/js/register_business.js	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -75,4 +75,5 @@
         ownerObj['username'] = $('#username').val();
         ownerObj['password'] = $('#password').val();
+        ownerObj['phoneNumber'] = $('#phoneNumber').val();
         businessObj['owner'] = ownerObj;
 
@@ -102,4 +103,5 @@
         });
         businessObj['services'] = servicesObj;
+        console.log(businessObj);
         $.ajax({
             url: "http://localhost:8080/api/business",
@@ -108,5 +110,5 @@
             contentType:"application/json; charset=utf-8",
             dataType: 'text',
-            success: function(succ){
+            success: function(success){
                 alert( "Well done! You have finished the registration process. " +
                     "Please check periodically to see if the company has been approved." );
Index: src/main/resources/templates/business_admin.html
===================================================================
--- src/main/resources/templates/business_admin.html	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/templates/business_admin.html	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -15,5 +15,5 @@
 <header class="p-3 mb-3 border-bottom">
     <div class="row">
-        <div class="col-md-10 mb-10" >
+        <div class="col-md-10 mb-10">
             <span id="header">
                 Welcome back
@@ -30,7 +30,12 @@
 <nav>
     <div class="nav nav-tabs" id="nav-tab" role="tablist">
-        <a class="nav-link active" id="nav-business-tab" data-bs-toggle="tab" href="#nav-business" role="tab" aria-controls="nav-business" aria-selected="true">Business Info</a>
-        <a class="nav-link" id="nav-owner-tab" data-bs-toggle="tab" href="#nav-owner" role="tab" aria-controls="nav-owner" aria-selected="false">Owner Info</a>
-        <a class="nav-link" id="nav-services-tab" data-bs-toggle="tab" href="#nav-services" role="tab" aria-controls="nav-services" aria-selected="false">Services Info</a>
+        <a class="nav-link active" id="nav-business-tab" data-bs-toggle="tab" href="#nav-business" role="tab"
+           aria-controls="nav-business" aria-selected="true">Business Info</a>
+        <a class="nav-link" id="nav-owner-tab" data-bs-toggle="tab" href="#nav-owner" role="tab"
+           aria-controls="nav-owner" aria-selected="false">Owner Info</a>
+        <a class="nav-link" id="nav-services-tab" data-bs-toggle="tab" href="#nav-services" role="tab"
+           aria-controls="nav-services" aria-selected="false">Services Info</a>
+        <a class="nav-link" id="nav-appointments-tab" data-bs-toggle="tab" href="#nav-appointments" role="tab"
+           aria-controls="nav-appointments" aria-selected="false">Appointments</a>
     </div>
 </nav>
@@ -41,5 +46,6 @@
                 <label for="business_status">Business status</label>
                 <input type="text" id="business_status" disabled class="form-control" aria-label="business_status"/>
-                <p id="new_business_warning" style="color:darkorange;" hidden>Please wait for the admin to approve your business.</p>
+                <p id="new_business_warning" style="color:darkorange;" hidden>Please wait for the admin to approve your
+                    business.</p>
             </div>
 
@@ -70,4 +76,11 @@
                         </div>
                     </div>
+                    <!-- Phone number input -->
+                    <div class="form-outline mb-4">
+                        <label for="phoneNumber">Phone number</label>
+                        <input type="number" id="phoneNumber" class="form-control" placeholder="Phone number"
+                               aria-label="phoneNumber"/>
+                    </div>
+
                     <!-- Email input -->
                     <div class="form-outline mb-4">
@@ -76,5 +89,5 @@
                                aria-label="Email"/>
                     </div>
-                    <!-- Email input -->
+                    <!-- Username input -->
                     <div class="form-outline mb-4">
                         <label for="username">Username</label>
@@ -97,5 +110,5 @@
                             <input type="text" id="input_service" class="form-control"
                                    placeholder="Services"
-                                   aria-label="services" />
+                                   aria-label="services"/>
                         </div>
                     </div>
@@ -111,4 +124,27 @@
         </div>
     </div>
+    <div class="tab-pane fade" id="nav-appointments" role="tabpanel" aria-labelledby="nav-appointments-tab">
+        <div class="form-outline col-lg-12 row">
+            <div class="form-outline col-lg-12">
+                <div class="table-responsive">
+                    <table class="table">
+                        <thead>
+                        <tr>
+                            <th scope="col">#</th>
+                            <th scope="col">Full name</th>
+                            <th scope="col">Email</th>
+                            <th scope="col">Phone number</th>
+                            <th scope="col">Time period</th>
+                            <th scope="col">Service</th>
+                            <th scope="col">Action</th>
+                        </tr>
+                        </thead>
+                        <tbody id="bookings-table-body">
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
 </div>
 <script src='js/jquery-1.10.2.js' type="text/javascript"></script>
Index: src/main/resources/templates/customer_admin.html
===================================================================
--- src/main/resources/templates/customer_admin.html	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
+++ src/main/resources/templates/customer_admin.html	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
+    <title>Profile settings page</title>
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
+          integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
+
+    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
+    <link href="css/headers.css" rel="stylesheet" />
+    <link href="css/customer_admin.css" rel="stylesheet"/>
+</head>
+<body>
+<!-- Navbar start -->
+<header class="p-3 mb-3 border-bottom">
+    <div class="row" style="justify-content: space-between;">
+        <div class="col-md-10 mb-10">
+            <h4>Profile settings for <span id="fullName"></span></h4>
+        </div>
+        <div class="col-md-1 mb-1">
+            <button id="profile" class="btn btn-primary btn-block" onclick="goToHomePage()">
+                Homepage
+            </button>
+        </div>
+        <div class="col-md-1 mb-1">
+            <button id="logout" class="btn btn-primary btn-block">
+                Logout
+            </button>
+        </div>
+    </div>
+</header>
+
+<nav>
+    <div class="nav nav-tabs" id="nav-customer-tab" role="tablist">
+        <a class="nav-link active" id="nav-profile-tab" data-bs-toggle="tab" href="#nav-profile" role="tab"
+           aria-controls="nav-profile" aria-selected="true">Profile Info</a>
+        <a class="nav-link" id="nav-appointment-tab" data-bs-toggle="tab" href="#nav-appointment" role="tab"
+           aria-controls="nav-appointment" aria-selected="false">Upcoming Appointments</a>
+        <a class="nav-link" id="nav-past-appointment-tab" data-bs-toggle="tab" href="#nav-past-appointment" role="tab"
+           aria-controls="nav-review" aria-selected="false">Past appointments</a>
+        <a class="nav-link" id="nav-review-tab" data-bs-toggle="tab" href="#nav-review" role="tab"
+           aria-controls="nav-review" aria-selected="false">My reviews</a>
+    </div>
+</nav>
+<div class="tab-content" id="nav-customer-tabContent">
+    <div class="tab-pane fade show active" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">
+        <div class="col-lg-6 mb-5 mb-lg-0">
+            <div class="card-body py-5 px-md-5">
+                <form>
+                    <div class="row">
+                        <div class="col-md-6 mb-4">
+                            <div class="form-outline">
+                                <label for="firstName">First name</label>
+                                <input type="text" id="firstName" class="form-control" placeholder="First name"
+                                       aria-label="Firstname"/>
+                            </div>
+                        </div>
+                        <div class="col-md-6 mb-4">
+                            <div class="form-outline">
+                                <label for="lastName">Last name</label>
+                                <input type="text" id="lastName" class="form-control" placeholder="Last name"
+                                       aria-label="lastname"/>
+                            </div>
+                        </div>
+                    </div>
+                    <!-- Phone number input -->
+                    <div class="form-outline mb-4">
+                        <label for="phoneNumber">Phone number</label>
+                        <input type="number" id="phoneNumber" class="form-control" placeholder="Phone number"
+                               aria-label="phoneNumber"/>
+                    </div>
+
+                    <!-- Email input -->
+                    <div class="form-outline mb-4">
+                        <label for="email">Email</label>
+                        <input type="email" id="email" class="form-control" placeholder="Email"
+                               aria-label="Email"/>
+                    </div>
+                    <!-- Username input -->
+                    <div class="form-outline mb-4">
+                        <label for="username">Username</label>
+                        <input type="text" id="username" class="form-control" placeholder="Username"
+                               aria-label="username"/>
+                    </div>
+                    <button type="button" class="btn btn-primary btn-block mb-4" id="update_customer_button">
+                        Update profile
+                    </button>
+                </form>
+            </div>
+        </div>
+    </div>
+    <div class="tab-pane fade" id="nav-appointment" role="tabpanel" aria-labelledby="nav-appointment-tab">
+        <div class="form-outline col-lg-12 row">
+            <div class="form-outline col-lg-12">
+                <div class="table-responsive">
+                    <table class="table">
+                        <thead>
+                        <tr>
+                            <th scope="col">#</th>
+                            <th scope="col">Full name</th>
+                            <th scope="col">Email</th>
+                            <th scope="col">Phone number</th>
+                            <th scope="col">Time period</th>
+                            <th scope="col">Service</th>
+                            <th scope="col">Action</th>
+                        </tr>
+                        </thead>
+                        <tbody id="appointments-table-body">
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="tab-pane fade" id="nav-past-appointment" role="tabpanel" aria-labelledby="nav-past-appointment-tab">
+        <div class="form-outline col-lg-12 row">
+            <div class="form-outline col-lg-12">
+                <div class="table-responsive">
+                    <table class="table">
+                        <thead>
+                        <tr>
+                            <th scope="col">#</th>
+                            <th scope="col">Full name</th>
+                            <th scope="col">Email</th>
+                            <th scope="col">Phone number</th>
+                            <th scope="col">Time period</th>
+                            <th scope="col">Service</th>
+                            <th scope="col">Action</th>
+                        </tr>
+                        </thead>
+                        <tbody id="past-appointments-table-body">
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+        <div class="modal fade" id="reviewModal" tabindex="-1" aria-labelledby="reviewModalLabel" aria-hidden="true">
+            <div class="modal-dialog">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h1 class="modal-title fs-5" id="reviewModalLabel">New message</h1>
+                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                    </div>
+                    <div class="modal-body">
+                        <form>
+                            <div class="mb-3">
+                                <label for="recipient-name" class="col-form-label">Recipient:</label>
+                                <input type="text" class="form-control" id="recipient-name" />
+                            </div>
+                            <div class="id-class"></div>
+                            <div class="rating">
+                                <input type="radio" name="rating" value="5" id="5">
+                                <label for="5">☆</label>
+                                <input type="radio" name="rating" value="4" id="4">
+                                <label for="4">☆</label>
+                                <input type="radio" name="rating" value="3" id="3">
+                                <label for="3">☆</label>
+                                <input type="radio" name="rating" value="2" id="2">
+                                <label for="2">☆</label>
+                                <input type="radio" name="rating" value="1" id="1">
+                                <label for="1">☆</label>
+                            </div>
+                            <div class="mb-3">
+                                <label for="message-text" class="col-form-label">Message:</label>
+                                <textarea class="form-control" id="message-text"></textarea>
+                            </div>
+                        </form>
+                    </div>
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
+                        <button type="button" class="btn btn-primary" id="post-review-button">Leave a review</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="tab-pane fade" id="nav-review" role="tabpanel" aria-labelledby="nav-review-tab">
+        <div id="my-reviews" class="form-outline col-lg-12 row">
+            <div class="card m-3" style="max-width: 300px; padding: 0;">
+                <div class="card-header text-bg-success">
+                    ☆☆☆☆☆
+                </div>
+                <ul class="list-group list-group-flush">
+                    <li class="list-group-item"><i>Business:</i> Businessname<</li>
+                    <li class="list-group-item"><i>Service:</i> Customer Name</li>
+                    <li class="list-group-item"><i>Reviewer:</i> Customer Name</li>
+                    <li class="list-group-item"><i>Comment:</i> Lorem ipsum diem amet</li>
+                    <li class="list-group-item"><small class="text-body-secondary">Created on 29-11-2023T11:32:23</small></li>
+                </ul>
+            </div>
+        </div>
+    </div>
+</div>
+
+
+<script src='js/jquery-1.10.2.js' type="text/javascript"></script>
+<script src="js/customer_admin.js" type="text/javascript"></script>
+<script src="js/bootstrap.bundle.min.js"></script>
+<script src="js/logout.js" type="text/javascript"></script>
+</body>
+</html>
Index: src/main/resources/templates/homepage.html
===================================================================
--- src/main/resources/templates/homepage.html	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/templates/homepage.html	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -16,5 +16,5 @@
     <link href='css/fullcalendar.print.css' rel='stylesheet' media='print'/>
     <link href="css/homepage.css" rel="stylesheet"/>
-    <link href="css/headers.css" rel="stylesheet">
+    <link href="css/headers.css" rel="stylesheet" />
 </head>
 <body>
@@ -27,5 +27,5 @@
         </div>
         <div class="col-md-2 mb-2">
-            <button id="profile" class="btn btn-primary btn-block">
+            <button id="profile" class="btn btn-primary btn-block" onclick="goToProfile()">
                 Profile
             </button>
@@ -70,5 +70,9 @@
         <div class="form-outline mb-4">
             <label for="startdatetime">Start:</label>
-            <input type="datetime-local" id="startdatetime" name="startdatetime">
+            <input type="datetime-local" id="startdatetime" name="startdatetime" style="float:right;">
+        </div>
+        <div class="form-outline mb-4">
+            <label for="enddatetime">End:</label>
+            <input type="datetime-local" id="enddatetime" name="enddatetime" style="float:right;">
         </div>
         <div class="form-outline mb-4">
@@ -78,5 +82,40 @@
         </div>
     </div>
+    <div class="card form-outline mb-4" style="margin-top:30px;" >
+        <ul class="list-group list-group-flush" id="reviews-ul">
+            <li class="list-group-item"><b>service name: </b><span id="rating-service-name"></span></li>
+            <li class="list-group-item"><b>rating </b><span id="rating-value"></span></li>
+            <li class="list-group-item" style="margin-bottom:10px;"><b>reviews count </b><span id="rating-count"></span></li>
+            <li class="list-group-item" id="reviews-li"><a href="#/reviews_page">Checkout reviews</a></li>
+        </ul>
+    </div>
 
+</div>
+<div class="modal fade" id="showReviewsModal" tabindex="-1" aria-labelledby="showReviewsModalLabel" aria-hidden="true">
+    <div class="modal-dialog modal-xl modal-dialog-scrollable modal-dialog-centered" >
+        <div class="modal-content">
+            <div class="modal-header">
+                <h1>Reviews</h1>
+                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+            </div>
+            <div class="modal-body form-outline col-lg-12 row" id="reviewsModalBody" style="--mdb-gutter-x: 0 !important; background-color: #f0f0f0 !important;">
+                <div class="card m-3" style="max-width: 300px; padding: 0;">
+                    <div class="card-header text-bg-success">
+                        ☆☆☆☆☆
+                    </div>
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item"><i>Business:</i> Businessname<</li>
+                        <li class="list-group-item"><i>Service:</i> Customer Name</li>
+                        <li class="list-group-item"><i>Reviewer:</i> Customer Name</li>
+                        <li class="list-group-item"><i>Comment:</i> Lorem ipsum diem amet</li>
+                        <li class="list-group-item"><small class="text-body-secondary">Created on 29-11-2023T11:32:23</small></li>
+                    </ul>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
+            </div>
+        </div>
+    </div>
 </div>
 <script src='js/jquery-1.10.2.js' type="text/javascript"></script>
Index: src/main/resources/templates/register_business.html
===================================================================
--- src/main/resources/templates/register_business.html	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/templates/register_business.html	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -44,4 +44,10 @@
                                     </div>
                                 </div>
+                            </div>
+
+                            <!-- Phone number input -->
+                            <div class="form-outline mb-4">
+                                <input type="number" id="phoneNumber" class="form-control" placeholder="Phone number"
+                                       aria-label="phonenumber"/>
                             </div>
 
Index: src/main/resources/templates/register_customer.html
===================================================================
--- src/main/resources/templates/register_customer.html	(revision 950fa0d7a6b439770e79c885947e9b7d6058b424)
+++ src/main/resources/templates/register_customer.html	(revision 77205be723c8b2ffd1ee5618a05e1d12b1cee44a)
@@ -67,4 +67,10 @@
                             </div>
 
+                            <!-- Phone number input -->
+                            <div class="form-outline mb-4">
+                                <input type="number" id="phoneNumber" class="form-control" required th:field="*{phoneNumber}" />
+                                <label class="form-label" for="phoneNumber">Phone Number</label>
+                            </div>
+
                             <!-- Email input -->
                             <div class="form-outline mb-4">
