mateuszsudra 2 роки тому
батько
коміт
ea782ed906

+ 13 - 0
boat-reservation-logic/src/main/java/pl/sudra/controller/ReservationController.java

@@ -5,6 +5,9 @@ import org.springframework.web.bind.annotation.*;
 import pl.sudra.domain.Reservation;
 import pl.sudra.service.ReservationService;
 
+import java.sql.Date;
+import java.util.List;
+
 @RestController
 @CrossOrigin(origins = "http://localhost:1410")
 public class ReservationController {
@@ -31,4 +34,14 @@ public class ReservationController {
         System.out.println("Creating reservation");
         this.reservationService.addReservation(reservation);
     }
+
+    @RequestMapping(
+            value = "/findReservations",
+            method = RequestMethod.GET,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    public List<Reservation> findReservations(@RequestParam("boat_id") Long boat_id,
+                                              @RequestParam("date") Date date) {
+        System.out.println("Looking for reservations");
+        return this.reservationService.findReservations(boat_id, date);
+    }
 }

+ 39 - 26
boat-reservation-logic/src/main/java/pl/sudra/domain/Reservation.java

@@ -6,7 +6,8 @@ import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
 import jakarta.validation.constraints.NotNull;
 
-import java.util.Date;
+import java.sql.Date;
+
 
 @Entity
 public class Reservation {
@@ -14,22 +15,22 @@ public class Reservation {
     @GeneratedValue(strategy = GenerationType.AUTO)
     private Long id;
     @NotNull
-    private Long user_id;
+    private Long userId;
     @NotNull
-    private Long boat_id;
+    private Long boatId;
     @NotNull
     private Date date;
     @NotNull
-    private byte start_hour;
+    private byte startHour;
     @NotNull
-    private byte end_hour;
+    private byte endHour;
 
-    public Reservation(Long user_id, Long boat_id, Date date, byte start_hour, byte end_hour) {
-        this.user_id = user_id;
-        this.boat_id = boat_id;
+    public Reservation(Long userId, Long boatId, Date date, byte startHour, byte endHour) {
+        this.userId = userId;
+        this.boatId = boatId;
         this.date = date;
-        this.start_hour = start_hour;
-        this.end_hour = end_hour;
+        this.startHour = startHour;
+        this.endHour = endHour;
     }
 
     public Reservation() {
@@ -43,20 +44,20 @@ public class Reservation {
         this.id = id;
     }
 
-    public Long getUser_id() {
-        return user_id;
+    public Long getUserId() {
+        return userId;
     }
 
-    public void setUser_id(Long user_id) {
-        this.user_id = user_id;
+    public void setUserId(Long user_id) {
+        this.userId = user_id;
     }
 
-    public Long getBoat_id() {
-        return boat_id;
+    public Long getBoatId() {
+        return boatId;
     }
 
-    public void setBoat_id(Long boat_id) {
-        this.boat_id = boat_id;
+    public void setBoatId(Long boat_id) {
+        this.boatId = boat_id;
     }
 
     public Date getDate() {
@@ -67,19 +68,31 @@ public class Reservation {
         this.date = date;
     }
 
-    public byte getStart_hour() {
-        return start_hour;
+    public byte getStartHour() {
+        return startHour;
+    }
+
+    public void setStartHour(byte start_hour) {
+        this.startHour = start_hour;
     }
 
-    public void setStart_hour(byte start_hour) {
-        this.start_hour = start_hour;
+    public byte getEndHour() {
+        return endHour;
     }
 
-    public byte getEnd_hour() {
-        return end_hour;
+    public void setEndHour(byte end_hour) {
+        this.endHour = end_hour;
     }
 
-    public void setEnd_hour(byte end_hour) {
-        this.end_hour = end_hour;
+    @Override
+    public String toString() {
+        return "Reservation{" +
+                "id=" + id +
+                ", userId=" + userId +
+                ", boatId=" + boatId +
+                ", date=" + date +
+                ", startHour=" + startHour +
+                ", endHour=" + endHour +
+                '}';
     }
 }

+ 5 - 0
boat-reservation-logic/src/main/java/pl/sudra/repository/ReservationRepository.java

@@ -5,8 +5,13 @@ import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 import pl.sudra.domain.Reservation;
 
+import java.sql.Date;
+import java.util.List;
+
 @Transactional
 @Repository
 public interface ReservationRepository extends JpaRepository<Reservation, Long> {
     Reservation findReservationById(long id);
+
+    List<Reservation> findAllByBoatIdAndDate(long id, Date date);
 }

+ 3 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/ReservationService.java

@@ -2,6 +2,7 @@ package pl.sudra.service;
 
 import pl.sudra.domain.Reservation;
 
+import java.sql.Date;
 import java.util.List;
 
 public interface ReservationService {
@@ -17,4 +18,6 @@ public interface ReservationService {
     Reservation getReservation(long id);
 
     void generateReservations(int n);
+
+    List<Reservation> findReservations(long boat_id, Date date);
 }

+ 13 - 7
boat-reservation-logic/src/main/java/pl/sudra/service/ReservationServiceImpl.java

@@ -6,9 +6,9 @@ import org.springframework.transaction.annotation.Transactional;
 import pl.sudra.domain.Reservation;
 import pl.sudra.repository.ReservationRepository;
 
-import java.time.ZoneId;
+import java.time.LocalDate;
 import java.util.Calendar;
-import java.util.Date;
+import java.sql.Date;
 import java.util.List;
 import java.util.Random;
 
@@ -49,8 +49,8 @@ public class ReservationServiceImpl implements ReservationService {
 
     @Override
     public void generateReservations(int n) {
-        Date today = new Date();
-
+//        Date today = new Date();
+        LocalDate today = LocalDate.now();
         System.out.println("n: " + n);
 
         Random random = new Random();
@@ -61,18 +61,24 @@ public class ReservationServiceImpl implements ReservationService {
             byte start_hour = (byte) (random.nextInt(23 - 7) + 7);
             byte end_hour = (byte) (random.nextInt(24 - start_hour + 1) + start_hour + 1);
 
-            calendar.setTime(today);
-            calendar.add(Calendar.DAY_OF_YEAR, -1 + i);
+//            calendar.setTime(today);
+//            calendar.add(Calendar.DAY_OF_YEAR, -1 + i);
+//            System.out.println(calendar.getTime());
 
             this.reservationRepository.save(
                     new Reservation(
                             (long) i,
                             (long) (i % 2),
-                            calendar.getTime(),
+                            (java.sql.Date) Date.valueOf(today.plusDays(-1 + i)),
                             start_hour, end_hour
                     )
             );
         }
     }
+
+    @Override
+    public List<Reservation> findReservations(long boat_id, Date date) {
+        return this.reservationRepository.findAllByBoatIdAndDate(boat_id, date);
+    }
 }
 

+ 8 - 8
boat-reservation-view/src/app/domain/reservation.ts

@@ -1,17 +1,17 @@
 export class Reservation {
   id = ''
-  client_id = ''
-  boat_id = ''
+  clientId = ''
+  boatId = ''
   date = ''
-  start_hour = ''
-  end_hour = ''
+  startHour = ''
+  endHour = ''
 
   constructor(id: string, client_id: string, boat_id: string, date: string, start_hour: string, end_hour: string) {
     this.id = id;
-    this.client_id = client_id;
-    this.boat_id = boat_id;
+    this.clientId = client_id;
+    this.boatId = boat_id;
     this.date = date;
-    this.start_hour = start_hour;
-    this.end_hour = end_hour;
+    this.startHour = start_hour;
+    this.endHour = end_hour;
   }
 }

+ 8 - 2
boat-reservation-view/src/app/reservation-service/reservation.service.ts

@@ -1,4 +1,4 @@
-import { Injectable } from '@angular/core';
+import {Injectable} from '@angular/core';
 import {HttpClient, HttpHeaders} from "@angular/common/http";
 import {Observable} from "rxjs";
 import {Reservation} from "../domain/reservation";
@@ -17,7 +17,13 @@ export class ReservationService {
     return this.http.get<Reservation[]>(url);
   }
 
-  createReservarion(reservation_info: object): any {
+  findReservations(boat_id: number, date: string): Observable<Reservation[]> {
+    const url = 'http://localhost:2137/findReservations?boat_id=' + boat_id + '&date=' + date;
+
+    return this.http.get<Reservation[]>(url);
+  }
+
+  createReservation(reservation_info: object): any {
     const url = 'http://localhost:2137/createReservation';
 
     const headers = new HttpHeaders()

+ 44 - 19
boat-reservation-view/src/app/reservation-view/reservation-view.component.css

@@ -1,11 +1,11 @@
 form {
-  width: 300px;
+  width: 450px;
   margin: 0 auto;
+  text-align: center;
 }
 
 label {
   display: block;
-  margin-bottom: 8px;
 }
 
 input[type="text"],
@@ -18,8 +18,19 @@ textarea {
   border-radius: 4px;
 }
 
-input[type="submit"] {
+input[type="submit"].enabled {
   background-color: #4caf50;
+}
+
+input[type="submit"].disabled {
+  background-color: darkgrey;
+}
+
+input[type="submit"].disabled:hover {
+  cursor: not-allowed;
+}
+
+input[type="submit"] {
   color: #fff;
   padding: 10px 16px;
   border: none;
@@ -29,20 +40,19 @@ input[type="submit"] {
   align-self: center;
 }
 
-input[type="submit"]:hover {
+input[type="submit"].enabled:hover {
   background-color: #45a049;
 }
 
 #boat_label:after {
   content: " (name | capacity | cost/hour)";
-  color: gray;
+  color: darkslategray;
   font-size: 80%;
 }
 
 select {
   width: 100%;
   padding: 8px;
-  margin-bottom: 16px;
   border: 1px solid #ccc;
   border-radius: 4px;
 }
@@ -63,19 +73,18 @@ select:focus {
   display: inline-block;
 }
 
-#from_date::before, #to_date::before {
-  content: '\f073'; /* FontAwesome calendar icon */
-  font-family: "Font Awesome 5 Free";
-  font-weight: 900;
-  position: absolute;
-  right: 8px;
-  top: 50%;
-  transform: translateY(-50%);
-}
+/*#date::before {*/
+/*  content: '\f073'; !* FontAwesome calendar icon *!*/
+/*  font-family: "Font Awesome 5 Free";*/
+/*  font-weight: 900;*/
+/*  position: absolute;*/
+/*  right: 8px;*/
+/*  top: 50%;*/
+/*  transform: translateY(-50%);*/
+/*}*/
 
-input[id="from_date"], input[id="to_date"] {
+input[id="date"] {
   width: 100%;
-  padding-right: 24px; /* Make room for the calendar icon */
   appearance: none;
   -webkit-appearance: none;
   border: 1px solid #ccc;
@@ -86,7 +95,7 @@ input[id="from_date"], input[id="to_date"] {
 }
 
 /* Additional styling for the calendar icon */
-#from_date #to_date::-webkit-calendar-picker-indicator {
+#date::-webkit-calendar-picker-indicator {
   opacity: 0; /* Hide the default arrow icon in WebKit browsers */
   position: absolute;
   right: 0;
@@ -96,6 +105,22 @@ input[id="from_date"], input[id="to_date"] {
   cursor: pointer;
 }
 
-select[id="from_date"], select[id="to_date"] {
+select[id="date"] {
   width: fit-content;
 }
+
+#free {
+  background-color: lightgreen;
+}
+
+#taken {
+  background-color: indianred;
+}
+
+#selected {
+  background-color: cornflowerblue;
+}
+
+#conflict {
+  background-color: darkred;
+}

+ 98 - 63
boat-reservation-view/src/app/reservation-view/reservation-view.component.html

@@ -1,77 +1,112 @@
 <div style="text-align: center;">
   <h2>MAKE RESERVATION</h2>
 </div>
-<form (submit)="makeReservation()" #myForm="ngForm">
-  <label id="boat_label" for="boat" style="font-family: Courier New, monospace;">Boat:</label>
-  <select name="_boats" id="boat" [(ngModel)]="selectedBoat">
-    <option *ngFor="let boat of boats_labels">{{ boat.label.toString() }}</option>
+<form (submit)="makeReservation()" #myForm="ngForm" style="font-family: Courier New, monospace;">
+  <h3 style="text-align: center;margin-bottom: 0;">BOAT</h3>
+  <label id="boat_label" for="boat"
+         style="font-family: Courier New, monospace;text-align: center;margin-bottom: 5px;"></label>
+  <select name="_boats"
+          id="boat"
+          style="text-align: center; width: 280px"
+          (change)="onBoatSelected()">
+    <option *ngFor="let boat of boats">{{ boat.label.toString() }}</option>
   </select>
-  <br>
 
-  <h2 style="text-align: center">DATE</h2>
+  <h3 style="text-align: center">DATE</h3>
+  <p *ngIf="isDateFromPast()" style="color: #b70b0b">You can't select date from the past.</p>
   <div style="text-align: center">
     <input type="date"
-           id="from_date"
-           name="cost" [(ngModel)]="date" required
-           style="width: 80%">
+           id="date"
+           style="width: 130px"
+           [value]="date"
+           placeholder="dd-mm-yyyy"
+           (change)="onDateSelected()">
+    <br><br>
+    <div style="text-align: center; display: flex; justify-content: center; align-items: center;">
+      <table style="width: fit-content; height: fit-content;">
+        <tr>
+          <td style="border:1px #333333 solid; width: 20px; height: 20px"
+              *ngFor="let hour of hours" id="{{hour.class}}">{{hour.label}}</td>
+        </tr>
+        <tr>
+          <td style="height: 20px"><br></td>
+        </tr>
+        <tr style="font-size: 12px;text-align: left">
+          <td id="free" style="border:1px #333333 solid;width: 20px; height: 20px"></td>
+          <td colspan="3">Available</td>
+          <td id="taken" style="border:1px #333333 solid;width: 20px; height: 20px"></td>
+          <td colspan="3">Reserved</td>
+          <td id="selected" style="border:1px #333333 solid;width: 20px; height: 20px"></td>
+          <td colspan="3">Selected</td>
+          <td id="conflict" style="border:1px #333333 solid;width: 20px; height: 20px"></td>
+          <td colspan="3">Conflict</td>
+        </tr>
+      </table>
+    </div>
   </div>
 
-  <h2 style="text-align: center">TIME</h2>
-  <table style="width: 100%">
-    <tr>
-      <td style="width: 30%; text-align: center">
-        <label for="from_hour" style="font-family: Courier New, monospace;">START</label>
-      </td>
-      <td style="width: 70%;display:inline-block;">
-        <select id="from_hour" style="width:100%; box-sizing: border-box;" [(ngModel)]="formData.fromHour">
-          <option>7:00</option>
-          <option>8:00</option>
-          <option>9:00</option>
-          <option>10:00</option>
-          <option>11:00</option>
-          <option>12:00</option>
-          <option>13:00</option>
-          <option>14:00</option>
-          <option>15:00</option>
-          <option>16:00</option>
-          <option>17:00</option>
-          <option>18:00</option>
-          <option>19:00</option>
-          <option>20:00</option>
-          <option>21:00</option>
-          <option>22:00</option>
-        </select>
-      </td>
-    </tr>
-    <tr>
-      <td style="width: 30%; text-align: center">
-        <label for="to_hour" style="font-family: Courier New, monospace;">END</label>
-      </td>
-      <td style="width: 70%;display:inline-block;">
-        <select id="to_hour" style="width:100%; box-sizing: border-box;" [(ngModel)]="formData.toHour">
-          <option>8:00</option>
-          <option>9:00</option>
-          <option>10:00</option>
-          <option>11:00</option>
-          <option>12:00</option>
-          <option>13:00</option>
-          <option>14:00</option>
-          <option>15:00</option>
-          <option>16:00</option>
-          <option>17:00</option>
-          <option>18:00</option>
-          <option>19:00</option>
-          <option>20:00</option>
-          <option>21:00</option>
-          <option>22:00</option>
-          <option>23:00</option>
-        </select>
-      </td>
-    </tr>
-  </table>
+  <h3 style="text-align: center">TIME</h3>
+  <div style="display: flex; justify-content: center; align-items: center;">
+    <table style="width: 90%">
+      <tr>
+        <td style="width: 25%; text-align: right; padding-right: 10px;">
+          <label for="from_hour" style="font-family: Courier New, monospace;">START AT</label>
+        </td>
+        <td style="width: 25%; text-align: left">
+          <select id="from_hour" style="width: 70px; box-sizing: border-box;" (change)="onStartHourSelected()">
+            <option>7:00</option>
+            <option>8:00</option>
+            <option>9:00</option>
+            <option>10:00</option>
+            <option>11:00</option>
+            <option>12:00</option>
+            <option>13:00</option>
+            <option>14:00</option>
+            <option>15:00</option>
+            <option>16:00</option>
+            <option>17:00</option>
+            <option>18:00</option>
+            <option>19:00</option>
+            <option>20:00</option>
+            <option>21:00</option>
+            <option>22:00</option>
+          </select>
+        </td>
+        <td style="width: 25%; text-align: right; padding-right: 10px;">
+          <label for="to_hour" style="font-family: Courier New, monospace;">END AT</label>
+        </td>
+        <td style="width: 25%; text-align: left">
+          <select id="to_hour" style="width: 70px; box-sizing: border-box;" (change)="onEndHourSelected()">
+            <option>8:00</option>
+            <option>9:00</option>
+            <option>10:00</option>
+            <option>11:00</option>
+            <option>12:00</option>
+            <option>13:00</option>
+            <option>14:00</option>
+            <option>15:00</option>
+            <option>16:00</option>
+            <option>17:00</option>
+            <option>18:00</option>
+            <option>19:00</option>
+            <option>20:00</option>
+            <option>21:00</option>
+            <option>22:00</option>
+            <option>23:00</option>
+          </select>
+        </td>
+      </tr>
+    </table>
+  </div>
+  <p *ngIf="areHoursConflicting()" style="color: #b70b0b">Selected hours are conflicting with existing reservations.</p>
+  <p *ngIf="areHoursOrderWrong()" style="color: #b70b0b">The start hour should be before the end hour.</p>
+  <p *ngIf="isItTooLateToStart()" style="color: #b70b0b">It is too late to start this reservation.</p>
 
+  <br *ngIf="!(areHoursOrderWrong() || areHoursConflicting() || isItTooLateToStart())">
   <div style="text-align: center;">
-    <input type="submit" value="Submit">
+    <p *ngIf="isFormNotValid()" style="color: #b70b0b"><b>Fix the errors above to make reservation.</b></p>
+    <input type="submit" value="Submit" [disabled]="isFormNotValid()"
+           [ngClass]="{'disabled': isFormNotValid(), 'enabled': !isFormNotValid()}">
   </div>
   <br>
 </form>

+ 132 - 17
boat-reservation-view/src/app/reservation-view/reservation-view.component.ts

@@ -8,50 +8,165 @@ import {ReservationService} from "../reservation-service/reservation.service";
   styleUrls: ['./reservation-view.component.css']
 })
 export class ReservationViewComponent {
-
-
-  boats_labels: any[] = [];
-
-  formData: any = {};
+  boats: {
+    "id": string,
+    "label": string
+  }[] = [];
   boat: any;
   date: any;
   fromHour: any;
   toHour: any;
-  selectedBoat: string = '';
+  hours: { "label": number, "class": string }[] = [];
+
+  reservedHours: any[] = [];
 
 
   constructor(private boatService: BoatsService,
               private reservationService: ReservationService) {
+    const today = new Date();
+    this.date = today.toISOString().split('T')[0];
+    for (let i = 7; i < 23; i++) {
+      this.hours.push(
+        {
+          "label": i,
+          "class": "free"
+        }
+      )
+    }
+  }
+
+  isDateFromPast() {
+    return new Date(new Date().setHours(0, 0, 0)) >= new Date(
+      this.date.split("-")[0],
+      this.date.split("-")[1] - 1,
+      this.date.split("-")[2],
+      1
+    );
+  }
+
+  areHoursConflicting() {
+    return this.hours.some(h => h.class === "conflict")
+  }
+
+  areHoursOrderWrong() {
+    return this.fromHour >= this.toHour
+  }
+
+  isItTooLateToStart() {
+    return this.fromHour <= new Date().getHours() + 1
+  }
+
+  isFormNotValid(){
+    return this.areHoursOrderWrong() || this.areHoursConflicting() || this.isDateFromPast() || this.isItTooLateToStart()
   }
 
   makeReservation() {
     let reservation_info = {
-      "Boat": this.formData.boat,
-      "fromDate": this.date,
-      "fromHour": this.fromHour,
-      "toHour": this.toHour
+      "userId": 0,
+      "boatId": this.boat,
+      "date": this.date,
+      "startHour": this.fromHour,
+      "endHour": this.toHour
     }
     console.log("reservation_info: ", reservation_info)
-    console.log("boats_labels: ", this.boats_labels)
-    console.log("Form: ", this.selectedBoat)
-    console.log(this.date)
-    this.reservationService.createReservarion(reservation_info)
+    this.reservationService.createReservation(reservation_info)
+    this.updateSquares()
   }
 
   async ngOnInit() {
     const items = this.boatService.getBoats().subscribe(
       (response) => {
-        this.boats_labels = response.map(val => ({
+        this.boats = response.map(val => ({
           "id": val.id,
-          "label": val.name + "\t|\t" + val.capacity + "\t|\t" + parseFloat(val.cost).toFixed(2)
+          "label": val.name + " | " + val.capacity + " | " + parseFloat(val.cost).toFixed(2)
         }));
+        this.boat = this.boats[0].id
+        this.fromHour = 7
+        this.toHour = 8
+        this.updateSquares()
       },
       (error) => {
         // Handle any errors
         console.error(error);
       }
     );
-    console.log()
+  }
+
+  onBoatSelected() {
+    const element = document.getElementById('boat') as HTMLInputElement;
+    if (element) {
+      this.boat = this.boats.find(b => b.label == element.value)!.id
+    } else {
+      console.error('Element with ID "boat" not found.');
+    }
+    this.updateSquares()
+  }
+
+  onDateSelected() {
+    const element = document.getElementById('date') as HTMLInputElement;
+    if (element) {
+      this.date = element.value
+    } else {
+      console.error('Element with ID "date" not found.');
+    }
+    this.updateSquares()
+  }
+
+  onStartHourSelected() {
+    const element = document.getElementById('from_hour') as HTMLInputElement;
+    if (element) {
+      this.fromHour = parseInt(element.value.split(":")[0])
+    } else {
+      console.error('Element with ID "from_hour" not found.');
+    }
+    this.updateSquares()
+  }
+
+  onEndHourSelected() {
+    const element = document.getElementById('to_hour') as HTMLInputElement;
+    if (element) {
+      this.toHour = parseInt(element.value.split(":")[0])
+    } else {
+      console.error('Element with ID "to_hour" not found.');
+    }
+    this.updateSquares()
+  }
+
+  updateSquares() {
+    this.reservationService.findReservations(parseInt(this.boat), this.date).subscribe(
+      response => {
+        this.reservedHours = response.map(reservation =>
+          [reservation.startHour, reservation.endHour]
+        )
+        this.setHourSquaresByDatabase(this.reservedHours)
+        this.setHourSquaresBySelectedHours()
+      }
+    )
+  }
+
+  cleanSquares() {
+    this.hours.forEach(h => h.class = "free")
+  }
+
+  setHourSquaresByDatabase(reservedHours: number[][]) {
+    this.cleanSquares()
+    reservedHours.forEach(res => {
+      for (let i = res[0]; i < res[1]; i++) {
+        this.hours.find(h => h.label == i)!.class = "taken"
+      }
+    })
+  }
+
+  setHourSquaresBySelectedHours() {
+    let currentHour;
+    for (let i = this.fromHour; i < this.toHour; i++) {
+      currentHour = this.hours.find(h => h.label == i)
+      if (currentHour!.class == "taken") {
+        currentHour!.class = "conflict"
+        continue
+      }
+      currentHour!.class = "selected"
+    }
   }
 
 }

+ 1 - 1
boat-reservation-view/src/styles.css

@@ -27,7 +27,7 @@ html {
   text-decoration: none;
   color: #d1572d;
   cursor: inherit;
-  text-shadow: 2px 2px 10px rgba(0, 0, 0, 0.7);
+  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
 }
 
 @keyframes hover-in {