Changeset 77205be


Ignore:
Timestamp:
12/26/23 18:50:43 (11 months ago)
Author:
gjoko kostadinov <gjokokostadinov@…>
Branches:
master
Children:
1413ee2
Parents:
950fa0d
Message:

Add entire code

Files:
22 added
2 deleted
57 edited
4 moved

Legend:

Unmodified
Added
Removed
  • mvnw.cmd

    • Property mode changed from 100644 to 100755
  • pom.xml

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    66        <groupId>org.springframework.boot</groupId>
    77        <artifactId>spring-boot-starter-parent</artifactId>
    8         <version>2.7.4</version>
     8        <version>2.7.6</version>
    99        <relativePath/> <!-- lookup parent from repository -->
    1010    </parent>
     
    1717    <properties>
    1818        <java.version>17</java.version>
     19        <org.modelmapper.version>3.2.0</org.modelmapper.version>
    1920    </properties>
    2021    <dependencies>
     
    2627            <groupId>org.springframework.boot</groupId>
    2728            <artifactId>spring-boot-starter-jdbc</artifactId>
     29        </dependency>
     30        <dependency>
     31            <groupId>org.modelmapper</groupId>
     32            <artifactId>modelmapper</artifactId>
     33            <version>${org.modelmapper.version}</version>
    2834        </dependency>
    2935        <dependency>
  • src/main/java/edu/gjoko/schedlr/SchedlrApplication.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/ServletInitializer.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/config/AppAuthenticationEntryPoint.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/config/AppAuthenticationFailureHandler.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/config/AppConfig.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    1515    }
    1616
    17     @Bean
    18     public AuthenticationSuccessHandler appAuthenticationSuccessHandler() {
    19         return new AppAuthenticationSuccessHandler();
    20     }
    2117
    2218    @Bean
  • src/main/java/edu/gjoko/schedlr/config/AppFilter.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    6464                            break;
    6565                        case "CUSTOMER":
    66                             page = "/homepage";
     66                            if ("/customer_admin".equals(httpServletRequest.getRequestURI())) {
     67                                page = "/customer_admin";
     68                            } else {
     69                                page = "/homepage";
     70                            }
    6771                            break;
    6872                        case "BUSINESS_OWNER":
  • src/main/java/edu/gjoko/schedlr/config/AppSecurityConfig.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    3232    private final BCryptPasswordEncoder passwordEncoder;
    3333
    34     private final AuthenticationSuccessHandler authenticationSuccessHandler;
    35 
    3634    private final AuthenticationFailureHandler authenticationFailureHandler;
    3735
     
    5755                .loginPage("/login")
    5856                .loginProcessingUrl("/login")
    59                 .successHandler(authenticationSuccessHandler)
    6057                .failureHandler(authenticationFailureHandler)
    61                 .defaultSuccessUrl("/homepage")
     58                .defaultSuccessUrl("/login")
    6259                .and()
    6360                .logout(logout -> logout
  • src/main/java/edu/gjoko/schedlr/config/MvcConfig.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    1616        registry.addViewController("/admin").setViewName("admin");
    1717        registry.addViewController("/business_admin").setViewName("business_admin");
     18        registry.addViewController("/customer_admin").setViewName("customer_admin");
    1819    }
    1920}
  • src/main/java/edu/gjoko/schedlr/controllers/AdministrationController.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    88
    99import javax.servlet.http.HttpServletRequest;
    10 import java.security.Principal;
    1110
    1211@Controller
    13 public class AdminController {
     12public class AdministrationController {
    1413
    1514    @GetMapping(path = "/admin")
    16     public String getAdminPageTemplate(Model model, HttpServletRequest request) {
     15    public String getAdminPageTemplate(Model model) {
    1716        return "admin";
    1817    }
    1918
    2019    @GetMapping(path = "/business_admin")
    21     public String getBusinessAdminPageTemplate(Model model, HttpServletRequest request) {
    22         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    23         String currentPrincipalName = authentication.getName();
     20    public String getBusinessAdminPageTemplate(Model model) {
     21        return "business_admin";
     22    }
    2423
    25         return "business_admin";
     24    @GetMapping(path = "/customer_admin")
     25    public String getCustomerAdminPageTemplate(Model model) {
     26        return "customer_admin";
    2627    }
    2728
  • src/main/java/edu/gjoko/schedlr/controllers/HomePageController.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/controllers/LoginController.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/controllers/RegisterController.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/controllers/rest/BusinessApi.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    22
    33import edu.gjoko.schedlr.entity.Business;
    4 import edu.gjoko.schedlr.entity.BusinessType;
    54import edu.gjoko.schedlr.services.BusinessService;
    65import lombok.AllArgsConstructor;
     
    1312@RequestMapping("api/business")
    1413@AllArgsConstructor
    15 public class BusinessController {
     14public class BusinessApi {
    1615
    17     final BusinessService businessService;
     16    private final BusinessService businessService;
    1817
    1918    @PostMapping
    20     public void getBusinessTypes(@RequestBody Business business) {
     19    public void saveBusiness(@RequestBody Business business) {
    2120        businessService.saveBusiness(business);
    2221    }
     
    4039    @GetMapping(path = "/{businessTypeId}")
    4140    public List<Business> getBusinessesByBusinessType(@PathVariable("businessTypeId") Long id) {
    42         BusinessType businessType = new BusinessType();
    43         businessType.setId(id);
    44         return businessService.findByBusinessTypeAndActiveStatus(businessType);
     41        return businessService.findByBusinessTypeAndActiveStatus(id);
    4542    }
    4643}
  • src/main/java/edu/gjoko/schedlr/controllers/rest/NomenclatureApi.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    1313@RequestMapping("api/nomenclatures")
    1414@AllArgsConstructor
    15 public class NomenclatureController {
     15public class NomenclatureApi {
    1616
    1717    private final NomenclaturesService nomenclaturesService;
  • src/main/java/edu/gjoko/schedlr/controllers/rest/UserApi.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    11package edu.gjoko.schedlr.controllers.rest;
    22
    3 import edu.gjoko.schedlr.entity.Business;
    43import lombok.AllArgsConstructor;
    54import org.springframework.web.bind.annotation.GetMapping;
     
    87
    98import javax.servlet.http.HttpServletRequest;
    10 import java.util.List;
    119
    1210@RestController
    1311@RequestMapping("api/user")
    1412@AllArgsConstructor
    15 public class UserController {
     13public class UserApi {
    1614
    1715    @GetMapping(path = "/me")
  • src/main/java/edu/gjoko/schedlr/entity/Appointment.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    11package edu.gjoko.schedlr.entity;
    22
    3 import lombok.AllArgsConstructor;
    4 import lombok.Getter;
    5 import lombok.NoArgsConstructor;
    6 import lombok.Setter;
     3import com.fasterxml.jackson.annotation.JsonBackReference;
     4import lombok.*;
    75import org.springframework.data.annotation.LastModifiedDate;
    86import org.springframework.data.jpa.domain.support.AuditingEntityListener;
     
    1412@EntityListeners(AuditingEntityListener.class)
    1513@Table(name = "appointment")
    16 @Getter
    17 @Setter
     14@Data
    1815@NoArgsConstructor
    1916@AllArgsConstructor
     
    3027    private LocalDateTime endTime;
    3128
    32     @OneToOne
    33     @JoinColumn(name = "customer_id", referencedColumnName = "id")
     29    @ManyToOne
     30    @JoinColumn(name = "stakeholder_id")
     31    @JsonBackReference(value = "customerAppointments")
    3432    private Stakeholder customer;
    3533
    3634    @ManyToOne
    37     @JoinColumn(name = "business_id")
    38     private Business business;
     35    @JoinColumn(name = "service_id")
     36    @JsonBackReference(value = "serviceAppointments")
     37    private Service service;
    3938
    40     @ManyToOne
    41     @JoinColumn(name = "service_id")
    42     private Service service;
     39    @Column(name = "appointment_status", length = 32, columnDefinition = "varchar(32) default 'NEW'")
     40    @Enumerated(EnumType.STRING)
     41    private AppointmentStatus appointmentStatus = AppointmentStatus.NEW;
    4342
    4443    @Column(name = "created")
     
    4948    @LastModifiedDate
    5049    private LocalDateTime modified;
     50
     51    public String getTimePeriod() {
     52        return startTime + " - " + endTime;
     53    }
     54
    5155}
  • src/main/java/edu/gjoko/schedlr/entity/Business.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    3737    private BusinessType businessType;
    3838
    39     @ManyToOne()
     39    @OneToOne(cascade = CascadeType.PERSIST)
    4040    @JoinColumn(name = "owner_id", referencedColumnName = "id", nullable = false)
    4141    @JsonProperty("owner")
     
    4343
    4444    @OneToMany(mappedBy = "business", cascade = CascadeType.PERSIST)
    45     @JsonManagedReference
     45    @JsonManagedReference(value = "services")
    4646    private List<Service> services;
    4747
  • src/main/java/edu/gjoko/schedlr/entity/BusinessStatus.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/entity/BusinessType.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/entity/Service.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    33import com.fasterxml.jackson.annotation.JsonBackReference;
    44import com.fasterxml.jackson.annotation.JsonIgnore;
     5import com.fasterxml.jackson.annotation.JsonManagedReference;
    56import lombok.AllArgsConstructor;
    67import lombok.Getter;
     
    1314import javax.persistence.*;
    1415import java.time.LocalDateTime;
     16import java.util.List;
    1517
    1618@Entity
     
    3335    private Integer price;
    3436
    35     @OneToOne(cascade = CascadeType.PERSIST)
     37    @Column(name = "cumulated_rating")
     38    private Float rating = 0.0f;
     39
     40    @Column(name = "reviews_count")
     41    private Integer reviewsCount = 0;
     42
     43    @OneToOne(cascade = CascadeType.MERGE)
    3644    @JoinColumn(name = "service_type_id", referencedColumnName = "id")
    3745    private ServiceType serviceType;
     
    3947    @ManyToOne
    4048    @JoinColumn(name = "business_id")
    41     @JsonBackReference
     49    @JsonBackReference(value = "services")
    4250    private Business business;
     51
     52    @OneToMany(mappedBy="service")
     53    @JsonManagedReference(value = "serviceAppointments")
     54    private List<Appointment> appointments;
    4355
    4456    @Column(name = "created")
  • src/main/java/edu/gjoko/schedlr/entity/ServiceType.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/entity/Stakeholder.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    22
    33import com.fasterxml.jackson.annotation.JsonIgnore;
     4import com.fasterxml.jackson.annotation.JsonManagedReference;
    45import lombok.AllArgsConstructor;
    56import lombok.Getter;
     
    1213import javax.persistence.*;
    1314import java.time.LocalDateTime;
     15import java.util.List;
    1416
    1517@Entity
     
    3941    private String email;
    4042
     43    @Column(name = "phone_number")
     44    private String phoneNumber;
     45
    4146    @Column(name = "username")
    4247    private String username;
     
    4449    @Column(name = "password")
    4550    private String password;
     51
     52    @OneToMany(mappedBy = "customer")
     53    @JsonManagedReference(value = "customerAppointments")
     54    private List<Appointment> appointments;
    4655
    4756    @Column(name = "created")
     
    5463    @JsonIgnore
    5564    private LocalDateTime modified;
     65
     66    public String getFullName() {
     67        return firstName + " " + lastName;
     68    }
    5669}
  • src/main/java/edu/gjoko/schedlr/entity/StakeholderType.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/exceptions/BlockingTimeException.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    33public class BlockingTimeException extends RuntimeException {
    44
    5     public static final String MESSAGE = "The selected dates are overlapping with another appointment";
     5    public static final String MESSAGE = "Error! The selected dates are overlapping with another appointment.";
    66    public BlockingTimeException() {
    77        super(MESSAGE);
  • src/main/java/edu/gjoko/schedlr/repositories/AppointmentRepository.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    22
    33import edu.gjoko.schedlr.entity.Appointment;
    4 import edu.gjoko.schedlr.entity.Business;
    54import edu.gjoko.schedlr.entity.Stakeholder;
    65import org.springframework.data.jpa.repository.JpaRepository;
     
    109import java.time.LocalDateTime;
    1110import java.util.List;
     11import java.util.Optional;
    1212
    1313@Repository
    1414public interface AppointmentRepository extends JpaRepository<Appointment, Long> {
    1515
    16     List<Appointment> getAppointmentsByBusiness(Business business);
     16    @Query(value = "select ap from Appointment as ap " +
     17            "where ap.service.business.id = :businessId " +
     18            "and ap.appointmentStatus = 'NEW'")
     19    List<Appointment> getActiveAppointmentsByBusiness(Long businessId);
    1720
    18     List<Appointment> getAppointmentsByCustomer(Stakeholder customer);
     21    @Query( value = "select ap from Appointment as ap " +
     22            "where ap.service.business.id = :businessId " +
     23            "and (" +
     24            "(ap.startTime between :startDate and :endDate) " +
     25            "or (ap.endTime between :startDate and :endDate) " +
     26            "or (:startDate = ap.startTime and ap.startTime = :endDate)" +
     27            "or (:startDate > ap.startTime and ap.endTime > :endDate)" +
     28            ")")
     29    List<Appointment> findBlockingAppointments(Long businessId, LocalDateTime startDate, LocalDateTime endDate);
    1930
    20     List<Appointment> findAppointmentsByBusinessAndStartTimeBetweenOrEndTimeBetween(Business business, LocalDateTime startTime, LocalDateTime endTime, LocalDateTime startTime1, LocalDateTime endTime1);
     31    @Query(value = "select ap from Appointment  as ap " +
     32            "where ap.service.business.owner.id = :businessOwnerId " +
     33            "and ap.startTime > :now ")
     34    List<Appointment> findFutureAppointmentsByBusinessOwnerId(Long businessOwnerId, LocalDateTime now);
    2135
    22     @Query(value = "select a from Appointment a " +
    23             "where business_id = :businessId " +
    24             "and (" +
    25             "(start_time between :startDate and :endDate) " +
    26             "or (end_time between :startDate and :endDate) " +
    27             "or (:startDate <= start_time and end_time >= :endDate)" +
    28             ")", nativeQuery = true)
    29     List<Appointment> findBlockingAppointments(Long businessId, LocalDateTime startDate, LocalDateTime endDate);
     36    @Query(value = "select ap from Appointment  as ap " +
     37            "where ap.customer.id = :customerId " +
     38            "and ap.startTime < :now")
     39    List<Appointment> findPastAppointmentsByCustomerId(Long customerId, LocalDateTime now);
     40
     41    @Query(value = "select ap from Appointment  as ap " +
     42            "where ap.service.business.owner.id = :businessOwnerId " +
     43            "and ap.startTime < :now ")
     44    List<Appointment> findPastAppointmentsByBusinessOwnerId(Long businessOwnerId, LocalDateTime now);
     45
     46    @Query(value = "select ap from Appointment  as ap " +
     47            "where ap.customer.id = :customerId " +
     48            "and ap.startTime > :now ")
     49    List<Appointment> findFutureAppointmentsByCustomerId(Long customerId, LocalDateTime now);
     50
     51    Optional<Appointment> findAppointmentByIdAndCustomer_Id(Long id, Long customerId);
     52
     53    Optional<Appointment> findAppointmentByIdAndService_Business_Owner_Id(Long id, Long ownerId);
    3054}
  • src/main/java/edu/gjoko/schedlr/repositories/BusinessRepository.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    1717
    1818    List<Business> findBusinessesByBusinessTypeAndBusinessStatus(BusinessType businessType, BusinessStatus businessStatus);
     19
     20    List<Business> findBusinessesByBusinessStatusAndBusinessType_Id(BusinessStatus businessStatus, Long id);
    1921}
  • src/main/java/edu/gjoko/schedlr/repositories/BusinessTypeRepository.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/repositories/ServiceRepository.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/repositories/ServiceTypeRepository.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/repositories/StakeholderRepository.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/services/AppointmentsService.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    11package edu.gjoko.schedlr.services;
    22
    3 import edu.gjoko.schedlr.entity.Appointment;
    4 import edu.gjoko.schedlr.entity.Business;
    5 import edu.gjoko.schedlr.entity.Stakeholder;
     3import edu.gjoko.schedlr.dto.AppointmentInfoDto;
     4import edu.gjoko.schedlr.entity.*;
    65import edu.gjoko.schedlr.exceptions.BlockingTimeException;
     6import edu.gjoko.schedlr.mappers.AppointmentInfoDtoBusinessMapper;
     7import edu.gjoko.schedlr.mappers.AppointmentInfoDtoCustomerMapper;
    78import edu.gjoko.schedlr.repositories.AppointmentRepository;
    89import edu.gjoko.schedlr.repositories.ServiceRepository;
    9 import edu.gjoko.schedlr.repositories.ServiceTypeRepository;
     10import edu.gjoko.schedlr.repositories.StakeholderRepository;
    1011import lombok.AllArgsConstructor;
    1112import org.springframework.stereotype.Service;
    1213
     14import javax.persistence.EntityNotFoundException;
     15import javax.security.sasl.AuthenticationException;
     16import java.time.LocalDateTime;
     17import java.util.ArrayList;
    1318import java.util.List;
     19import java.util.Optional;
    1420
    1521@Service
     
    1824
    1925    private final AppointmentRepository appointmentRepository;
     26    private final StakeholderRepository stakeholderRepository;
     27    private final ServiceRepository serviceRepository;
     28    private final AppointmentInfoDtoBusinessMapper appointmentInfoDtoBusinessMapper;
     29    private final AppointmentInfoDtoCustomerMapper appointmentInfoDtoCustomerMapper;
    2030
    21     private final ServiceRepository serviceRepository;
    2231
    2332    public void saveAppointment(Appointment appointment) {
    2433        var service = serviceRepository.findById(appointment.getService().getId()).get();
    25         appointment.setEndTime(appointment.getStartTime().plusMinutes(service.getDuration()));
     34
     35        // remove 1 minute in order to prevent overlapping
     36        appointment.setEndTime(appointment.getStartTime().plusMinutes(service.getDuration() - 1));
    2637        List<Appointment> blockingAppointments = appointmentRepository.
    2738                findBlockingAppointments(
    28                         appointment.getBusiness().getId(), appointment.getStartTime(), appointment.getEndTime());
     39                        appointment.getService().getBusiness().getId(), appointment.getStartTime(), appointment.getEndTime());
    2940
    3041        // check to see if there are blocking exceptions
     
    3546    }
    3647
    37     public List<Appointment> getAppointmentsByBusiness(Business business) {
    38         return appointmentRepository.getAppointmentsByBusiness(business);
     48    public List<AppointmentInfoDto> getFutureAppointmentInfoList(Long stakeholderId) {
     49        StakeholderType type = stakeholderRepository.findById(stakeholderId).get().getStakeholderType();
     50        List<AppointmentInfoDto> appointmentInfoDtoList = new ArrayList<>();
     51
     52        switch (type) {
     53            case BUSINESS_OWNER -> appointmentInfoDtoList = appointmentRepository.findFutureAppointmentsByBusinessOwnerId(stakeholderId, LocalDateTime.now())
     54                    .stream()
     55                    .map(appointmentInfoDtoBusinessMapper::appointmentToAppointmentInfoDto)
     56                    .toList();
     57            case CUSTOMER -> appointmentInfoDtoList = appointmentRepository.findFutureAppointmentsByCustomerId(stakeholderId, LocalDateTime.now())
     58                    .stream()
     59                    .map(appointmentInfoDtoCustomerMapper::appointmentToAppointmentInfoDto)
     60                    .toList();
     61        }
     62
     63        return appointmentInfoDtoList;
    3964    }
    4065
    41     private List<Appointment> getAppointmentsByCustomer(Stakeholder stakeholder) {
    42         return appointmentRepository.getAppointmentsByCustomer(stakeholder);
     66    public List<AppointmentInfoDto> getPastAppointmentInfoList(Long stakeholderId) {
     67        StakeholderType type = stakeholderRepository.findById(stakeholderId).get().getStakeholderType();
     68        List<Appointment> appointmentInfoDtoList = new ArrayList<>();
     69
     70        switch (type) {
     71            case BUSINESS_OWNER -> appointmentInfoDtoList = appointmentRepository.findPastAppointmentsByBusinessOwnerId(stakeholderId, LocalDateTime.now());
     72            case CUSTOMER -> appointmentInfoDtoList = appointmentRepository.findPastAppointmentsByCustomerId(stakeholderId, LocalDateTime.now());
     73        }
     74
     75        return appointmentInfoDtoList
     76                .stream()
     77                .map(appointmentInfoDtoBusinessMapper::appointmentToAppointmentInfoDto)
     78                .toList();
     79
     80    }
     81
     82    public List<Appointment> getActiveAppointmentsByBusiness(Long businessId) {
     83        return appointmentRepository.getActiveAppointmentsByBusiness(businessId);
     84    }
     85
     86    public void deleteAppointment(Long appointmentId, Long stakeholderId) throws AuthenticationException {
     87        Optional<Stakeholder> stakeholderOptional = stakeholderRepository.findById(stakeholderId);
     88        if (stakeholderOptional.isPresent()) {
     89            Optional<Appointment> optional = appointmentRepository.findAppointmentByIdAndCustomer_Id(appointmentId, stakeholderId);
     90            if (optional.isEmpty()) {
     91                optional = appointmentRepository.findAppointmentByIdAndService_Business_Owner_Id(appointmentId, stakeholderId);
     92                if (optional.isEmpty()) {
     93                    throw new EntityNotFoundException("No appointment was found for the give stakeholder and appointment id.");
     94                } else {
     95                    softDeleteAppointment(optional.get(), AppointmentStatus.CANCELLED_BY_BUSINESS_OWNER);
     96                }
     97            } else {
     98                softDeleteAppointment(optional.get(), AppointmentStatus.CANCELLED_BY_CUSTOMER);
     99            }
     100        } else {
     101            throw new AuthenticationException();
     102        }
     103    }
     104
     105    public void softDeleteAppointment(Appointment appointment, AppointmentStatus appointmentStatus) {
     106        appointment.setAppointmentStatus(appointmentStatus);
     107        appointmentRepository.save(appointment);
    43108    }
    44109}
  • src/main/java/edu/gjoko/schedlr/services/BusinessService.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    22
    33import edu.gjoko.schedlr.entity.*;
    4 import edu.gjoko.schedlr.repositories.BusinessRepository;
    5 import edu.gjoko.schedlr.repositories.ServiceRepository;
    6 import edu.gjoko.schedlr.repositories.ServiceTypeRepository;
    7 import edu.gjoko.schedlr.repositories.StakeholderRepository;
     4import edu.gjoko.schedlr.repositories.*;
    85import lombok.AllArgsConstructor;
    96import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    107import org.springframework.stereotype.Service;
     8import org.springframework.util.CollectionUtils;
    119
    1210import java.util.List;
     
    2220    private final ServiceTypeRepository serviceTypeRepository;
    2321    private final ServiceRepository serviceRepository;
     22    private final StakeholderService stakeholderService;
    2423
    2524    private final StakeholderRepository stakeholderRepository;
     
    5453                .stream()
    5554                .forEach(business -> {
    56                     stakeholderRepository.save(business.getOwner());
    57                     serviceRepository.saveAll(business.getServices());
    58                     businessRepository.save(business);
     55                    stakeholderService.saveOrUpdateStakeholder(business.getOwner());
     56                    saveOrUpdateServices(business.getServices());
     57                    saveOrUpdateBusiness(business);
    5958                });
    6059    }
     
    6665    }
    6766
    68     public List<Business> findByBusinessTypeAndActiveStatus(BusinessType businessType) {
    69         return businessRepository.findBusinessesByBusinessTypeAndBusinessStatus(businessType, ACTIVE);
     67    public List<Business> findByBusinessTypeAndActiveStatus(Long businessTypeId) {
     68        return businessRepository.findBusinessesByBusinessStatusAndBusinessType_Id(ACTIVE, businessTypeId);
     69    }
     70
     71
     72
     73    private void saveOrUpdateBusiness(Business business) {
     74        if (business.getId() != null) {
     75            var foundBusinessEntity = businessRepository.findById(business.getId());
     76            business.setCreated(foundBusinessEntity.get().getCreated());
     77        }
     78        businessRepository.save(business);
     79    }
     80
     81    private void saveOrUpdateServices(List<edu.gjoko.schedlr.entity.Service> serviceList) {
     82        if (!CollectionUtils.isEmpty(serviceList)) {
     83            serviceList.forEach( service -> {
     84                if (service.getId() != null) {
     85                    var found = serviceRepository.findById(service.getId());
     86                    service.setCreated(found.get().getCreated());
     87                }
     88            });
     89            serviceRepository.saveAll(serviceList);
     90        }
    7091    }
    7192}
  • src/main/java/edu/gjoko/schedlr/services/NomenclaturesService.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/services/PostgresUserDetailsService.java

    • Property mode changed from 100644 to 100755
  • src/main/java/edu/gjoko/schedlr/services/StakeholderService.java

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    66import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    77import org.springframework.stereotype.Service;
     8
     9import java.util.Optional;
    810
    911@Service
     
    2830        return stakeholderRepository.findById(id).get();
    2931    }
     32
     33    public void saveOrUpdateStakeholder(Stakeholder stakeholder) {
     34        if (stakeholder.getId() != null) {
     35            var found = stakeholderRepository.findById(stakeholder.getId()).get();
     36            found.setFirstName(stakeholder.getFirstName());
     37            found.setLastName(stakeholder.getLastName());
     38            found.setPhoneNumber(stakeholder.getPhoneNumber());
     39            found.setEmail(stakeholder.getEmail());
     40            found.setUsername(stakeholder.getUsername());
     41            stakeholderRepository.save(found);
     42        }
     43    }
    3044}
  • src/main/resources/application.properties

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    1 spring.datasource.url=jdbc:postgresql://localhost:5433/schedlr
    2 spring.datasource.username=postgres
    3 spring.datasource.password=silic0n
     1spring.datasource.url=jdbc:postgresql://localhost:5432/schedlr
     2spring.datasource.username=gjoko
     3spring.datasource.password=gjoko
    44
    55spring.sql.init.mode=never
  • src/main/resources/data.sql

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    1 insert into stakeholder (id, created, email, first_name, last_name, modified, password, stakeholder_type, username)
    2 values (nextval('hibernate_sequence'), current_timestamp, 'admin@schedlr.com', 'admin', 'admin', current_timestamp, '$2a$10$DJyjt.Vj/U8MEuYX1PXL9ukQSMwXHVdhre3POlTqpYzNxHB5gu/MW','ADMIN', 'admin');
    3 
    4 insert into stakeholder (id, created, email, first_name, last_name, modified, password, stakeholder_type, username)
    5 values (nextval('hibernate_sequence'), current_timestamp, 'user1@schedlr.com', 'gjoko', 'kostadinov', current_timestamp, '$2a$10$Zc28AcCpAgxB.e67UMF/2.FgchjH9QWB7z8nP0TdkrFneV4IHPXji','CUSTOMER', 'user');
    6 
    71insert into business_type (id, created, modified, name)
    82values (1, current_timestamp, current_timestamp, 'tailor'),
     
    2115       (nextval('hibernate_sequence'), current_timestamp, current_timestamp, 'dress shortening', 1),
    2216       (nextval('hibernate_sequence'), current_timestamp, current_timestamp, 'holes fixing', 1);
     17
     18insert into stakeholder (id, created, phone_number, email, first_name, last_name, modified, password, stakeholder_type, username)
     19values (nextval('hibernate_sequence'), current_timestamp, '075658123', 'admin@schedlr.com', 'admin', 'admin', current_timestamp, '$2a$10$DJyjt.Vj/U8MEuYX1PXL9ukQSMwXHVdhre3POlTqpYzNxHB5gu/MW','ADMIN', 'admin'),
     20        (nextval('hibernate_sequence'), current_timestamp, '071658125', 'user1@schedlr.com', 'gjoko', 'kostadinov', current_timestamp, '$2a$10$Zc28AcCpAgxB.e67UMF/2.FgchjH9QWB7z8nP0TdkrFneV4IHPXji','CUSTOMER', 'user'),
     21        (11111, current_timestamp, '078658523',  'c@c.com', 'Kliment', 'Nedelkovski', current_timestamp, '$2a$10$OratLSMKNsqd7Re.Md3I4.jrRHtTQNaLz/wUKo.I98..GufnD48uG','CUSTOMER', 'kliment123'),
     22        (10000, current_timestamp, '071218123', 'q@c.com', 'Stefan', 'Petrovski', current_timestamp, '$2a$10$x10O8eLp1r1wHwxDGvyo5.TBL216p8h797JJjQYEfFTbj/1bnCMki','BUSINESS_OWNER', 'stefan123');
     23
     24insert into business (id, business_status, company_name, created, modified, business_type_id, owner_id)
     25values (10001, 'ACTIVE', 'krojach stefan', current_timestamp, current_timestamp, 1, 10000);
     26
     27insert into service (id, created, modified, duration, price, cumulated_rating, reviews_count, business_id, service_type_id)
     28values (10002, current_timestamp, current_timestamp, 60, 400, 0, 0, 10001, 6),
     29       (10003, current_timestamp, current_timestamp, 30, 200, 0, 0, 10001, 7);
     30
     31insert into appointment (id, created, modified, start_time, end_time, stakeholder_id, service_id)
     32values
     33    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '10:00:00', current_date + time '10:59:00', 11111, 10002),
     34    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '11:00:00', current_date + time '11:29:00', 11111, 10003),
     35    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '13:00:00', current_date + time '13:59:00', 11111, 10002),
     36    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '14:00:00', current_date + time '14:29:00', 11111, 10003),
     37    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + time '14:30:00', current_date + time '14:59:00', 11111, 10003),
     38    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 1 + time '11:30:00', current_date + 1 + time '11:59:00', 11111, 10003),
     39    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 1 + time '13:30:00', current_date + 1 + time '14:29:00', 11111, 10002),
     40    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 1 + time '15:00:00', current_date + 1 + time '15:29:00', 11111, 10003),
     41    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 1 + time '14:30:00', current_date + 1 + time '14:59:00', 11111, 10003),
     42    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 1 + time '09:00:00', current_date - 1 + time '09:29:00', 11111, 10003),
     43    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 1 + time '10:00:00', current_date - 1 + time '10:59:00', 11111, 10002),
     44    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 1 + time '12:00:00', current_date - 1 + time '12:29:00', 11111, 10003),
     45    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 1 + time '12:40:00', current_date - 1 + time '12:09:00', 11111, 10003),
     46    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 2 + time '10:30:00', current_date - 2 + time '10:59:00', 11111, 10003),
     47    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 2 + time '12:10:00', current_date - 2 + time '13:09:00', 11111, 10002),
     48    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 2 + time '15:10:00', current_date - 2 + time '15:39:00', 11111, 10003),
     49    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 2 + time '14:15:00', current_date - 2 + time '14:44:00', 11111, 10003),
     50    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 3 + time '11:10:00', current_date - 3 + time '11:39:00', 11111, 10003),
     51    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 3 + time '14:30:00', current_date - 3 + time '14:59:00', 11111, 10003),
     52    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date - 3 + time '08:00:00', current_date - 3 + time '08:29:00', 11111, 10003),
     53    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 2 + time '10:10:00', current_date + 2 + time '10:29:00', 11111, 10003),
     54    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 2 + time '11:15:00', current_date + 2 + time '11:44:00', 11111, 10003),
     55    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 2 + time '14:30:00', current_date + 2 + time '14:59:00', 11111, 10003),
     56    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 3 + time '11:14:00', current_date + 3 + time '11:44:00', 11111, 10003),
     57    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 3 + time '13:25:00', current_date + 3 + time '13:54:00', 11111, 10003),
     58    (nextval('hibernate_sequence'), current_timestamp, current_timestamp, current_date + 3 + time '14:00:00', current_date + 3 + time '14:29:00', 11111, 10003);
  • src/main/resources/static/css/admin.css

    • Property mode changed from 100644 to 100755
  • src/main/resources/static/css/business_admin.css

    • Property mode changed from 100644 to 100755
  • src/main/resources/static/css/headers.css

    • Property mode changed from 100644 to 100755
  • src/main/resources/static/css/homepage.css

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    7474.card {
    7575    padding: 10px;
    76    
    7776}
  • src/main/resources/static/css/login.css

    • Property mode changed from 100644 to 100755
  • src/main/resources/static/css/register.css

    • Property mode changed from 100644 to 100755
  • src/main/resources/static/js/admin.js

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    11$(document).ready(function() {
    2     var companies = {};
     2    let companies = [];
    33
    4     $.ajax({
    5        url: "http://localhost:8080/api/business"
    6     }).then(function (data) {
     4    getCompanies().then(function (data) {
    75        companies = data;
    8         var $el = $("#table_body");
    9 
    10         $.each(data, function (index, obj) {
    11             if(obj.businessStatus == "NEW" || obj.businessStatus == "DEACTIVATED") {
    12                 $el.append("<tr>\n" +
    13                     "            <th scope=\"row\">" + obj.id + "</th>\n" +
    14                     "            <td>" + obj.companyName + "</td>\n" +
    15                     "            <td>" + obj.owner.firstName + " " + obj.owner.lastName + "</td>\n" +
    16                     "            <td><input class=\"form-check-input\" type=\"checkbox\" value=\"" + obj.id +"\"></td>\n" +
    17                     "        </tr>")
    18             }
    19             if (obj.businessStatus == "ACTIVE") {
    20                 $el.append("<tr>\n" +
    21                     "            <th scope=\"row\">" + obj.id + "</th>\n" +
    22                     "            <td>" + obj.companyName + "</td>\n" +
    23                     "            <td>" + obj.owner.firstName + " " + obj.owner.lastName + "</td>\n" +
    24                     "            <td><input class=\"form-check-input\" type=\"checkbox\" checked value=\"" + obj.id +"\"></td>\n" +
    25                     "        </tr>")
    26             }
    27         });
    286    });
    297
    308    $('#save_button').click(function () {
     9        var companiesToSave = [];
    3110        $.each($('#table_body tr'), function(index, row) {
    32             if(companies[index].businessStatus == "NEW" &&  $($($(row).children() [3]).children()[0]).is(':checked')) {
    33                 companies[index]['businessStatus'] = 'ACTIVE';
     11            if((companies[index].businessStatus === "NEW" || companies[index].businessStatus === "DEACTIVATED") &&  $($($(row).children() [3]).children()[0]).is(':checked')) {
     12                let cloneCompany = { ...companies[index] }
     13                cloneCompany['businessStatus'] = 'ACTIVE';
     14                companiesToSave.push(cloneCompany);
    3415            }
    35             if(companies[index].businessStatus == "ACTIVE" && ! $($($(row).children() [3]).children()[0]).is(':checked')) {
    36                 companies[index]['businessStatus'] = 'DEACTIVATED';
     16            if(companies[index].businessStatus === "ACTIVE" && !$($($(row).children() [3]).children()[0]).is(':checked')) {
     17                let cloneCompany = { ...companies[index] }
     18                cloneCompany['businessStatus'] = 'DEACTIVATED';
     19                companiesToSave.push(cloneCompany);
    3720            }
    3821        });
    39         console.log(JSON.stringify(companies));
     22        console.log(JSON.stringify(companiesToSave));
     23
     24
    4025        $.ajax({
    4126            url: "http://localhost:8080/api/business",
    4227            type:"PATCH",
    43             data: JSON.stringify(companies),
     28            data: JSON.stringify(companiesToSave),
    4429            contentType:"application/json; charset=utf-8",
    4530            dataType: 'text',
    4631            success: function(succ){
     32                getCompanies().then(function (data) {
     33                    companies = data;
     34                });
    4735                alert( "Updates applied successfully" );
    4836            },
     
    5341        event.preventDefault();
    5442    });
     43
     44    function getCompanies() {
     45        return $.ajax({
     46            url: "http://localhost:8080/api/business"
     47        }).then(function (data) {
     48            var $el = $("#table_body");
     49            $("#new_table tbody").html("");
     50            $.each(data, function (index, obj) {
     51                if(obj.businessStatus == "NEW" || obj.businessStatus == "DEACTIVATED") {
     52                    $el.append("<tr>\n" +
     53                        "            <th scope=\"row\">" + obj.id + "</th>\n" +
     54                        "            <td>" + obj.companyName + "</td>\n" +
     55                        "            <td>" + obj.owner.firstName + " " + obj.owner.lastName + "</td>\n" +
     56                        "            <td><input class=\"form-check-input\" type=\"checkbox\" value=\"" + obj.id +"\"></td>\n" +
     57                        "        </tr>")
     58                }
     59                if (obj.businessStatus == "ACTIVE") {
     60                    $el.append("<tr>\n" +
     61                        "            <th scope=\"row\">" + obj.id + "</th>\n" +
     62                        "            <td>" + obj.companyName + "</td>\n" +
     63                        "            <td>" + obj.owner.firstName + " " + obj.owner.lastName + "</td>\n" +
     64                        "            <td><input class=\"form-check-input\" type=\"checkbox\" checked value=\"" + obj.id +"\"></td>\n" +
     65                        "        </tr>")
     66                }
     67            });
     68            return data;
     69        });
     70    }
    5571});
  • src/main/resources/static/js/bootstrap.bundle.min.js

    • Property mode changed from 100644 to 100755
  • src/main/resources/static/js/bootstrap.bundle.min.js.map

    • Property mode changed from 100644 to 100755
  • src/main/resources/static/js/business_admin.js

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    11$(document).ready(function() {
    2 
    32    var business = {};
    43
    5     $.ajax({
    6         url: "http://localhost:8080/api/business/me"
    7     }).then(function (data) {
    8         business = data;
    9         var $header = $("#header");
    10 
    11         // header
    12         $header.text($header.text() + " " + business["owner"]['firstName'] + " " + business['owner']['lastName']);
    13 
    14         // business info
    15         if(business['businessStatus'] == 'NEW') {
    16             $('#new_business_warning').removeAttr("hidden");
    17         }
    18         $('#business_status').val(business['businessStatus']);
    19         $('#business_type').val(business['businessType']['text']);
    20 
    21         // owner info
    22         $('#firstName').val(business['owner']['firstName']);
    23         $('#lastName').val(business['owner']['lastName']);
    24         $('#email').val(business['owner']['email']);
    25         $('#username').val(business['owner']['username']);
    26 
    27         // services info
    28         var $el = $("#predefined_services_admin_panel");
    29         $el.empty();
    30         $.each(business['services'], function (index, obj) {
    31             $el.append(
    32                 '<div class=\"form-outline mb-4\">' +
    33                 '    <div class="row">' +
    34                 '        <div class="col-md-6">\n' +
    35                 '            <input class="form-check-input" type="checkbox" checked value=\"' + obj.id + '\" id=\"' + obj.id + '\">\n' +
    36                 '            <label class="form-check-label" for=\"' + obj.id + '\">\n' +
    37                 obj['serviceType'].name +
    38                 '            </label>\n' +
    39                 '        </div>' +
    40                 '        <div class=\"form-outline col-md-2 d-grid\">' +
    41                 '            <input type=\"text\" id=\"' + obj.id + obj['serviceType'].name.replace(/\s/g, "") + "duration" + '\" class=\"form-control\" placeholder="time" value=\"' + obj.duration + '" />' +
    42                 '        </div>' +
    43                 '        <div class=\"form-outline col-md-2 d-grid\">' +
    44                 '            <input type=\"text\" id=\"' + obj.id + obj['serviceType'].name.replace(/\s/g, "") + "price" + '\" class=\"form-control\" placeholder="price" value=\"' + obj.price + '" />' +
    45                 '        </div>' +
    46                 '    </div>' +
    47                 '</div>');
    48         });
    49     });
     4    getBusinessInfo(business);
     5
     6    getAppointments();
    507
    518    $("#add_service").click(function () {
     
    12279    $("#update_owner_button").click(function() {
    12380        businesses = [];
    124         console.log("Gjoko");
    12581        business['owner']['firstName'] = $('#firstName').val();
    12682        business['owner']['lastName'] = $('#lastName').val();
    12783        business['owner']['email'] = $('#email').val();
     84        business['owner']['phoneNumber'] = $('#phoneNumber').val();
    12885        business['owner']['username'] = $('#username').val();
    12986
     
    147104    event.preventDefault();
    148105});
     106
     107function cancelAppointment(appointmentId) {
     108    if (confirm("Are you sure you want to cancel the appointment?")) {
     109        $.ajax({
     110            url: "http://localhost:8080/api/appointment/" + appointmentId,
     111            type:"DELETE"
     112        }).success(function (data) {
     113            alert("Appointment successfully canceled.")
     114            getAppointments();
     115        }).error(function (error) {
     116            alert("Something went wrong.");
     117        });
     118    }
     119}
     120
     121function getAppointments() {
     122    $.ajax({
     123        url: "http://localhost:8080/api/appointment/future/me"
     124    }).then(function (data) {
     125        console.log(data);
     126        var $el = $("#bookings-table-body");
     127        $el.empty();
     128        $.each(data, function (index, obj) {
     129            var element =
     130                '<tr>' +
     131                '   <th scope="row">' + (parseInt(index) + 1) + '</th>' +
     132                '   <td>' + obj['fullName'] + '</td>' +
     133                '   <td>' + obj['email'] + '</td>' +
     134                '   <td>' + obj['phoneNumber'] + '</td>' +
     135                '   <td>' + obj['timePeriod'] + '</td>' +
     136                '   <td>' + obj['serviceName'] + '</td>';
     137
     138            switch (obj['status']) {
     139                case 'NEW':
     140                    element += '   <td><button type="button" class="btn btn-danger" onclick="cancelAppointment(' + obj['appointmentId'] + ')">Cancel appointment</button></td>';
     141                    break;
     142                case 'CANCELLED_BY_CUSTOMER':
     143                    element += '   <td><button type="button" class="btn btn-secondary" disabled>Cancelled by customer</button></td>';
     144                    break;
     145                case 'CANCELLED_BY_BUSINESS_OWNER':
     146                    element += '   <td><button type="button" class="btn btn-secondary" disabled>Cancelled by business owner</button></td>';
     147                    break;
     148            }
     149            element+='</tr>';
     150
     151            $el.append(element);
     152        });
     153    });
     154}
     155
     156function getBusinessInfo(business) {
     157    $.ajax({
     158        url: "http://localhost:8080/api/business/me"
     159    }).then(function (data) {
     160        business = data;
     161        var $header = $("#header");
     162
     163        // header
     164        $header.text($header.text() + " " + business["owner"]['firstName'] + " " + business['owner']['lastName']);
     165
     166        // business info
     167        if(business['businessStatus'] == 'NEW') {
     168            $('#new_business_warning').removeAttr("hidden");
     169        }
     170        $('#business_status').val(business['businessStatus']);
     171        $('#business_type').val(business['businessType']['text']);
     172
     173        // owner info
     174        $('#firstName').val(business['owner']['firstName']);
     175        $('#lastName').val(business['owner']['lastName']);
     176        $('#phoneNumber').val(business['owner']['phoneNumber']);
     177        $('#email').val(business['owner']['email']);
     178        $('#username').val(business['owner']['username']);
     179
     180        // services info
     181        var $el = $("#predefined_services_admin_panel");
     182        $el.empty();
     183        $.each(business['services'], function (index, obj) {
     184            $el.append(
     185                '<div class=\"form-outline mb-4\">' +
     186                '    <div class="row">' +
     187                '        <div class="col-md-6">\n' +
     188                '            <input class="form-check-input" type="checkbox" checked value=\"' + obj.id + '\" id=\"' + obj.id + '\">\n' +
     189                '            <label class="form-check-label" for=\"' + obj.id + '\">\n' + obj['serviceType'].name + '</label>\n' +
     190                '        </div>' +
     191                '        <div class=\"form-outline col-md-2 d-grid\">' +
     192                '            <input type=\"text\" id=\"' + obj.id + obj['serviceType'].name.replace(/\s/g, "") + "duration" + '\" class=\"form-control\" placeholder="time" value=\"' + obj.duration + '" />' +
     193                '        </div>' +
     194                '        <div class=\"form-outline col-md-2 d-grid\">' +
     195                '            <input type=\"text\" id=\"' + obj.id + obj['serviceType'].name.replace(/\s/g, "") + "price" + '\" class=\"form-control\" placeholder="price" value=\"' + obj.price + '" />' +
     196                '        </div>' +
     197                '    </div>' +
     198                '</div>');
     199        });
     200    });
     201}
  • src/main/resources/static/js/fullcalendar.js

    r950fa0d r77205be  
    332332        /* View Rendering
    333333        -----------------------------------------------------------------------------*/
    334        
    335 
    336334        function changeView(newViewName) {
    337335                if (!currentView || newViewName != currentView.name) {
     
    480478        // TODO: going forward, most of this stuff should be directly handled by the view
    481479
    482 
    483480        function refetchEvents() { // can be called as an API method
    484481                clearEvents();
     
    538535        }
    539536
    540 
    541 
    542537        /* Header Updating
    543538        -----------------------------------------------------------------------------*/
    544 
    545 
    546539        function updateTitle() {
    547540                header.updateTitle(currentView.title);
  • src/main/resources/static/js/homepage.js

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    33    var businesses = {};
    44    var date = new Date();
     5    var selectedServices = {};
     6    var events = [];
    57    var d = date.getDate();
    68    var m = date.getMonth();
     
    6971
    7072    $("#companyType").change(function () {
     73        resetRatingCard();
    7174        var selectedVal = $(this).find(':selected').val();
    7275        var selectedObj = businessTypes[selectedVal - 1];
     
    7578        }).then(function (data) {
    7679            businesses = data;
    77             console.log(data);
    7880            var $el = $("#company");
    7981            var $servicesEl = $("#service");
     
    8486                $el.append("<option value=" + obj.id + ">" + obj.companyName + "</option>");
    8587            });
     88
     89            if (data && data.length === 1) {
     90                selectedServices = data[0]["services"];
     91                resetAndAppendServices(selectedServices);
     92            }
    8693        });
    8794    });
    8895
    8996    $("#company").change(function () {
     97        resetRatingCard();
    9098        var selectedVal = $(this).find(':selected').val();
    9199        $.ajax({
    92100            url: "http://localhost:8080/api/appointment/business/" + selectedVal
    93101        }).then(function (data) {
    94             console.log(data);
    95             var $el = $("#service");
    96             emptyDropdown($el);
    97 
    98             var services = businesses.find(item => item.id == selectedVal)['services'];
    99             console.log(services);
    100 
    101             $.each(services, function (index, obj) {
    102                 $el.append("<option value=" + obj.id + ">" + obj.serviceType.name + "</option>");
    103             });
    104         });
    105     });
    106 
     102            // get already stored service from in-memory
     103            selectedServices = businesses.find(item => item.id == selectedVal)['services'];
     104            resetAndAppendServices(selectedServices);
     105        });
     106        $('#calendar').fullCalendar('refetchEvents');
     107    });
     108
     109    $("#service").change(function () {
     110        var selectedVal = $("#service").find(':selected').val();
     111        var service = selectedServices.find(item => item.id == selectedVal);
     112        setRatingCard(service['serviceType']['name'], service['rating'], service['reviewsCount'], selectedVal);
     113    });
     114
     115    $("#startdatetime").change(function() {
     116        var date = new Date(document.getElementById("startdatetime").valueAsDate);
     117        var selectedVal = $("#service").find(':selected').val();
     118        var minutes = selectedServices.find(item => item.id == selectedVal)['duration'];
     119        date.setMinutes(date.getMinutes() + minutes);
     120        document.getElementById("enddatetime").value = date.toISOString().slice(0, 16);
     121    });
    107122
    108123    /* initialize the calendar
    109124    -----------------------------------------------------------------*/
    110 
    111     var calendar =  $('#calendar').fullCalendar({
     125    loadCalendar();
     126
     127    resetRatingCard();
     128
     129    $("#createAppointment").click(function() {
     130        var appointment = {};
     131        var business  = {};
     132        business['id'] = parseInt($("#company").val());
     133        appointment['service'] = {'id': parseInt($("#service").val()), 'business':business};
     134        appointment['startTime'] = $("#startdatetime").val();
     135
     136        $.ajax({
     137            url: "http://localhost:8080/api/appointment",
     138            type:"POST",
     139            data: JSON.stringify(appointment),
     140            contentType:"application/json; charset=utf-8",
     141            dataType: 'text',
     142            success: function(succ){
     143                alert("Successful appointment!");
     144                var companyId = parseInt($("#company").find(':selected').val());
     145                getAppointmentsByBusiness(companyId, events);
     146                destroyCalendar();
     147                loadCalendar(events);
     148            },
     149            error: function(error) {
     150                alert(error.responseText);
     151            }
     152        });
     153    });
     154
     155});
     156
     157document.getElementById("login").addEventListener("click", function(event){
     158   window.location = "/login";
     159});
     160
     161function resetAndAppendServices(services) {
     162    var $el = $("#service");
     163    emptyDropdown($el);
     164
     165    $.each(services, function (index, obj) {
     166        $el.append("<option value=" + obj.id + ">" + obj.serviceType.name + "</option>");
     167    });
     168}
     169
     170function emptyDropdown(element) {
     171    var defaultOption = element.children().get(0);
     172    element.children().remove();
     173    element.append(defaultOption);
     174}
     175
     176/*loadCalendar([{'title': 'Gjoko', 'start': '2023-09-01 01:00:00', 'end': '2023-09-01 01:30:00', 'allDay':false, 'color': 'blue'}])*/
     177
     178function getAppointmentsByBusiness(businessId, events) {
     179    $.ajax({
     180        url: "http://localhost:8080/api/appointment/business/" + businessId,
     181        type:"GET",
     182        contentType:"application/json; charset=utf-8",
     183        dataType: 'text',
     184        success: function(data){
     185            // reset events
     186            events = [];
     187            console.log(data);
     188           $.each(JSON.parse(data), function (index, object) {
     189               var event = {}
     190               event['title'] = object['title'];
     191               event['start'] = object['startTime'].replace('T', ' ');
     192               event['end'] = object['endTime'].replace('T', ' ');
     193               event['allDay'] = false;
     194               event['color'] = 'blue';
     195               events.push(event);
     196           });
     197
     198        },
     199        error: function(error) {
     200            console.log(error.responseText);
     201        }
     202    });
     203}
     204
     205function loadCalendar(events) {
     206    $('#calendar').fullCalendar({
    112207        header: {
    113208            left: 'title',
     
    178273
    179274        },
    180 
    181         events: [
    182             {
    183                 title: 'All Day Event',
    184                 start: new Date(y, m, 1)
    185             },
    186             {
    187                 id: 999,
    188                 title: 'Repeating Event',
    189                 start: new Date(y, m, d-3, 16, 0),
    190                 allDay: false,
    191                 className: 'info'
    192             },
    193             {
    194                 id: 999,
    195                 title: 'Repeating Event',
    196                 start: new Date(y, m, d+4, 16, 0),
    197                 allDay: false,
    198                 className: 'info'
    199             },
    200             {
    201                 title: 'Meeting',
    202                 start: new Date(y, m, d, 10, 30),
    203                 allDay: false,
    204                 className: 'info'
    205             },
    206             {
    207                 title: 'Lunch',
    208                 start: new Date(y, m, d, 12, 0),
    209                 end: new Date(y, m, d, 14, 0),
    210                 allDay: false,
    211                 className: 'info'
    212             },
    213             {
    214                 title: 'Birthday Party',
    215                 start: new Date(y, m, d+1, 19, 0),
    216                 end: new Date(y, m, d+1, 22, 30),
    217                 allDay: false,
    218             },
    219             {
    220                 title: 'Click for Google',
    221                 start: new Date(y, m, 28),
    222                 end: new Date(y, m, 29),
    223                 url: 'http://google.com/',
    224                 className: 'success'
    225             }
    226         ],
    227     });
    228 
    229     $("#createAppointment").click(function() {
    230         var appointment = {};
    231         appointment['business'] = {'id': parseInt($("#company").val())};
    232         appointment['service'] = {'id': parseInt($("#service").val())};
    233         appointment['startTime'] = $("#startdatetime").val();
    234 
    235         $.ajax({
    236             url: "http://localhost:8080/api/appointment",
    237             type:"POST",
    238             data: JSON.stringify(appointment),
    239             contentType:"application/json; charset=utf-8",
    240             dataType: 'text',
    241             success: function(succ){
    242                 console.log('success');
    243             },
    244             error: function(err) {
    245                 console.log(JSON.stringify(err));
    246             }
    247         });
    248     })
    249 
    250 });
    251 
    252 document.getElementById("login").addEventListener("click", function(event){
    253    window.location = "/login";
    254 });
    255 
    256 function emptyDropdown(element) {
    257     var defaultOption = element.children().get(0);
    258     element.children().remove().end().append(defaultOption);
    259 }
     275        events: function (start, end, callback) {
     276            var selectedVal = $("#company").find(':selected').val();
     277            if(!isNaN(parseInt(selectedVal))) {
     278                $.ajax({
     279                    url: "http://localhost:8080/api/appointment/business/" + selectedVal,
     280                    type:"GET",
     281                    contentType:"application/json; charset=utf-8",
     282                    dataType: 'text',
     283                    success: function(data){
     284                        // reset events
     285                        var events = [];
     286                        $.each(JSON.parse(data), function (index, object) {
     287                            var event = {}
     288                            event['title'] = object['title'];
     289                            event['start'] = object['startTime'].replace('T', ' ');
     290                            event['end'] = object['endTime'].replace('T', ' ');
     291                            event['allDay'] = false;
     292                            event['color'] = '#3b71ca';
     293                            event['textColor'] = '#FFFFFF';
     294                            events.push(event);
     295                        });
     296                        // load events
     297                        callback(events);
     298                    },
     299                    error: function(error) {
     300                        console.log(error.responseText);
     301                    }
     302                });
     303            }
     304        },
     305    });
     306}
     307
     308function goToProfile() {
     309    window.location = "/customer_admin";
     310}
     311
     312function destroyCalendar() {
     313    $('#calendar').fullCalendar('destroy');
     314}
     315
     316function resetRatingCard() {
     317    $('#rating-service-name').empty();
     318    $('#rating-value').empty();
     319    $('#rating-count').empty();
     320    $('#reviews-li').remove();
     321}
     322
     323function setRatingCard(name, value, count, serviceId) {
     324    $('#rating-service-name').text(name);
     325    $('#rating-value').text(value);
     326    $('#rating-count').text(count);
     327    $('#reviews-li').remove();
     328    getReviewsForService(serviceId);
     329
     330}
     331
     332function getReviewsForService(serviceId){
     333    $.ajax({
     334        url: "http://localhost:8080/api/review/" + serviceId
     335    }).then(function (data) {
     336        var $el = $("#reviewsModalBody");
     337        $('#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>'));
     338        $el.empty();
     339
     340        $.each(data, function (index, obj) {
     341            var element = '<div class="card m-3" style="max-width: 300px; padding: 0;">';
     342            element +=  '<div class="card-header" style="' + generateHeaderStyle(obj['rating']) + '">' + generateStars(obj['rating']) + '</div>';
     343            element += '<ul class="list-group list-group-flush">';
     344            element += '<li class="list-group-item"><i><b>Business:</b></i> ' + obj['businessName'] + '</li>';
     345            element += '<li class="list-group-item"><i><b>Service:</b></i> ' + obj['serviceName'] + '</li>';
     346            element += '<li class="list-group-item"><i><b>Reviewer:</b></i> ' + obj['customerName'] + '</li>';
     347            element += '<li class="list-group-item"><i><b>Comment:</b></i> ' + obj['comment'] + '</li>';
     348            element += '<li class="list-group-item"><small class="text-body-secondary"><i>Created:</i> ' + obj['created'] + '</small></li>';
     349            element += '</ul>';
     350            element += '</div>';
     351
     352            $el.append(element);
     353        });
     354    });
     355}
     356
     357function generateStars(number) {
     358    return '☆'.repeat(number);
     359}
     360
     361function generateHeaderStyle(number) {
     362    var style = '';
     363    switch (number) {
     364        case 5:
     365            style = 'background-color: RGBA(13,110,253,var(--bs-bg-opacity,1)) !important; ';
     366            break;
     367        case 4:
     368            style = 'background-color: RGBA(25,135,84,var(--bs-bg-opacity,1)) !important; ';
     369            break;
     370        case 3:
     371            style = 'background-color: RGBA(108,117,125,var(--bs-bg-opacity,1)) !important; ';
     372            break;
     373        case 2:
     374            style = 'background-color: RGBA(255,193,7,var(--bs-bg-opacity,1)) !important; ';
     375            break;
     376        case 1:
     377            style = 'background-color: RGBA(220,53,69,var(--bs-bg-opacity,1)) !important; ';
     378            break;
     379    }
     380
     381    style += ' color: #fff !important;'
     382    return style;
     383}
  • src/main/resources/static/js/login.js

    • Property mode changed from 100644 to 100755
  • src/main/resources/static/js/logout.js

    • Property mode changed from 100644 to 100755
  • src/main/resources/static/js/register_business.js

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    7575        ownerObj['username'] = $('#username').val();
    7676        ownerObj['password'] = $('#password').val();
     77        ownerObj['phoneNumber'] = $('#phoneNumber').val();
    7778        businessObj['owner'] = ownerObj;
    7879
     
    102103        });
    103104        businessObj['services'] = servicesObj;
     105        console.log(businessObj);
    104106        $.ajax({
    105107            url: "http://localhost:8080/api/business",
     
    108110            contentType:"application/json; charset=utf-8",
    109111            dataType: 'text',
    110             success: function(succ){
     112            success: function(success){
    111113                alert( "Well done! You have finished the registration process. " +
    112114                    "Please check periodically to see if the company has been approved." );
  • src/main/resources/templates/admin.html

    • Property mode changed from 100644 to 100755
  • src/main/resources/templates/business_admin.html

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    1515<header class="p-3 mb-3 border-bottom">
    1616    <div class="row">
    17         <div class="col-md-10 mb-10" >
     17        <div class="col-md-10 mb-10">
    1818            <span id="header">
    1919                Welcome back
     
    3030<nav>
    3131    <div class="nav nav-tabs" id="nav-tab" role="tablist">
    32         <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>
    33         <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>
    34         <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>
     32        <a class="nav-link active" id="nav-business-tab" data-bs-toggle="tab" href="#nav-business" role="tab"
     33           aria-controls="nav-business" aria-selected="true">Business Info</a>
     34        <a class="nav-link" id="nav-owner-tab" data-bs-toggle="tab" href="#nav-owner" role="tab"
     35           aria-controls="nav-owner" aria-selected="false">Owner Info</a>
     36        <a class="nav-link" id="nav-services-tab" data-bs-toggle="tab" href="#nav-services" role="tab"
     37           aria-controls="nav-services" aria-selected="false">Services Info</a>
     38        <a class="nav-link" id="nav-appointments-tab" data-bs-toggle="tab" href="#nav-appointments" role="tab"
     39           aria-controls="nav-appointments" aria-selected="false">Appointments</a>
    3540    </div>
    3641</nav>
     
    4146                <label for="business_status">Business status</label>
    4247                <input type="text" id="business_status" disabled class="form-control" aria-label="business_status"/>
    43                 <p id="new_business_warning" style="color:darkorange;" hidden>Please wait for the admin to approve your business.</p>
     48                <p id="new_business_warning" style="color:darkorange;" hidden>Please wait for the admin to approve your
     49                    business.</p>
    4450            </div>
    4551
     
    7076                        </div>
    7177                    </div>
     78                    <!-- Phone number input -->
     79                    <div class="form-outline mb-4">
     80                        <label for="phoneNumber">Phone number</label>
     81                        <input type="number" id="phoneNumber" class="form-control" placeholder="Phone number"
     82                               aria-label="phoneNumber"/>
     83                    </div>
     84
    7285                    <!-- Email input -->
    7386                    <div class="form-outline mb-4">
     
    7689                               aria-label="Email"/>
    7790                    </div>
    78                     <!-- Email input -->
     91                    <!-- Username input -->
    7992                    <div class="form-outline mb-4">
    8093                        <label for="username">Username</label>
     
    97110                            <input type="text" id="input_service" class="form-control"
    98111                                   placeholder="Services"
    99                                    aria-label="services" />
     112                                   aria-label="services"/>
    100113                        </div>
    101114                    </div>
     
    111124        </div>
    112125    </div>
     126    <div class="tab-pane fade" id="nav-appointments" role="tabpanel" aria-labelledby="nav-appointments-tab">
     127        <div class="form-outline col-lg-12 row">
     128            <div class="form-outline col-lg-12">
     129                <div class="table-responsive">
     130                    <table class="table">
     131                        <thead>
     132                        <tr>
     133                            <th scope="col">#</th>
     134                            <th scope="col">Full name</th>
     135                            <th scope="col">Email</th>
     136                            <th scope="col">Phone number</th>
     137                            <th scope="col">Time period</th>
     138                            <th scope="col">Service</th>
     139                            <th scope="col">Action</th>
     140                        </tr>
     141                        </thead>
     142                        <tbody id="bookings-table-body">
     143                        </tbody>
     144                    </table>
     145                </div>
     146            </div>
     147        </div>
     148    </div>
    113149</div>
    114150<script src='js/jquery-1.10.2.js' type="text/javascript"></script>
  • src/main/resources/templates/homepage.html

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    1616    <link href='css/fullcalendar.print.css' rel='stylesheet' media='print'/>
    1717    <link href="css/homepage.css" rel="stylesheet"/>
    18     <link href="css/headers.css" rel="stylesheet">
     18    <link href="css/headers.css" rel="stylesheet" />
    1919</head>
    2020<body>
     
    2727        </div>
    2828        <div class="col-md-2 mb-2">
    29             <button id="profile" class="btn btn-primary btn-block">
     29            <button id="profile" class="btn btn-primary btn-block" onclick="goToProfile()">
    3030                Profile
    3131            </button>
     
    7070        <div class="form-outline mb-4">
    7171            <label for="startdatetime">Start:</label>
    72             <input type="datetime-local" id="startdatetime" name="startdatetime">
     72            <input type="datetime-local" id="startdatetime" name="startdatetime" style="float:right;">
     73        </div>
     74        <div class="form-outline mb-4">
     75            <label for="enddatetime">End:</label>
     76            <input type="datetime-local" id="enddatetime" name="enddatetime" style="float:right;">
    7377        </div>
    7478        <div class="form-outline mb-4">
     
    7882        </div>
    7983    </div>
     84    <div class="card form-outline mb-4" style="margin-top:30px;" >
     85        <ul class="list-group list-group-flush" id="reviews-ul">
     86            <li class="list-group-item"><b>service name: </b><span id="rating-service-name"></span></li>
     87            <li class="list-group-item"><b>rating </b><span id="rating-value"></span></li>
     88            <li class="list-group-item" style="margin-bottom:10px;"><b>reviews count </b><span id="rating-count"></span></li>
     89            <li class="list-group-item" id="reviews-li"><a href="#/reviews_page">Checkout reviews</a></li>
     90        </ul>
     91    </div>
    8092
     93</div>
     94<div class="modal fade" id="showReviewsModal" tabindex="-1" aria-labelledby="showReviewsModalLabel" aria-hidden="true">
     95    <div class="modal-dialog modal-xl modal-dialog-scrollable modal-dialog-centered" >
     96        <div class="modal-content">
     97            <div class="modal-header">
     98                <h1>Reviews</h1>
     99                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
     100            </div>
     101            <div class="modal-body form-outline col-lg-12 row" id="reviewsModalBody" style="--mdb-gutter-x: 0 !important; background-color: #f0f0f0 !important;">
     102                <div class="card m-3" style="max-width: 300px; padding: 0;">
     103                    <div class="card-header text-bg-success">
     104                        ☆☆☆☆☆
     105                    </div>
     106                    <ul class="list-group list-group-flush">
     107                        <li class="list-group-item"><i>Business:</i> Businessname<</li>
     108                        <li class="list-group-item"><i>Service:</i> Customer Name</li>
     109                        <li class="list-group-item"><i>Reviewer:</i> Customer Name</li>
     110                        <li class="list-group-item"><i>Comment:</i> Lorem ipsum diem amet</li>
     111                        <li class="list-group-item"><small class="text-body-secondary">Created on 29-11-2023T11:32:23</small></li>
     112                    </ul>
     113                </div>
     114            </div>
     115            <div class="modal-footer">
     116                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
     117            </div>
     118        </div>
     119    </div>
    81120</div>
    82121<script src='js/jquery-1.10.2.js' type="text/javascript"></script>
  • src/main/resources/templates/login.html

    • Property mode changed from 100644 to 100755
  • src/main/resources/templates/register_business.html

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    4444                                    </div>
    4545                                </div>
     46                            </div>
     47
     48                            <!-- Phone number input -->
     49                            <div class="form-outline mb-4">
     50                                <input type="number" id="phoneNumber" class="form-control" placeholder="Phone number"
     51                                       aria-label="phonenumber"/>
    4652                            </div>
    4753
  • src/main/resources/templates/register_customer.html

    • Property mode changed from 100644 to 100755
    r950fa0d r77205be  
    6767                            </div>
    6868
     69                            <!-- Phone number input -->
     70                            <div class="form-outline mb-4">
     71                                <input type="number" id="phoneNumber" class="form-control" required th:field="*{phoneNumber}" />
     72                                <label class="form-label" for="phoneNumber">Phone Number</label>
     73                            </div>
     74
    6975                            <!-- Email input -->
    7076                            <div class="form-outline mb-4">
  • src/test/java/edu/gjoko/schedlr/SchedlrApplicationTests.java

    • Property mode changed from 100644 to 100755
Note: See TracChangeset for help on using the changeset viewer.