瀏覽代碼

Availability calendar - TZ fixes

Vijayakrishnan 4 年之前
父節點
當前提交
62ef2d2a6a

+ 150 - 0
app/Helpers/TimeLine.php

@@ -0,0 +1,150 @@
+<?php
+
+namespace App\Helpers;
+
+use DateTime;
+
+class TimeLine {
+
+    public $start = null;
+    public $end = null;
+    public $available = [];
+    public $unavailable = [];
+
+    public $removeStart = null;
+    public $removeEnd = null;
+
+    public function __construct(DateTime $_start, DateTime $_end)
+    {
+        $this->start = $_start->getTimestamp();
+        $this->end = $_end->getTimestamp();
+    }
+
+    public function addAvailability(DateTime $_start, DateTime $_end) {
+
+        $this->available[] = new TimeSlot($_start, $_end);
+
+        // sort by start
+        usort($this->available, function ($item1, $item2) {
+            return $item1->start <=> $item2->start;
+        });
+
+        // compact (join adjacent overlapping slots)
+        // $this->vdump($this->available);
+        $this->normalize();
+        $this->vdump($this->available);
+    }
+
+    public function removeAvailability(DateTime $_start, DateTime $_end) {
+
+        $this->removeStart = $_start->getTimestamp();
+        $this->removeEnd = $_end->getTimestamp();
+
+        $this->cleanRemove();
+
+        $removeStart = null;
+        $removeSnd = null;
+
+        // sort by start
+        usort($this->available, function ($item1, $item2) {
+            return $item1->start <=> $item2->start;
+        });
+        $this->normalize();
+        $this->vdump($this->available);
+    }
+
+    private function cleanRemove() {
+
+        $allDone = true;
+
+        for ($i=0; $i<count($this->available); $i++) {
+
+            // removeStart at or after slot start
+            // removeEnd at or before slot end
+            // split slot into 2
+            if($this->removeStart >= $this->available[$i]->start &&
+                $this->removeEnd <= $this->available[$i]->end) {
+                $newSlot = new TimeSlot(new DateTime(), new DateTime());
+                $newSlot->start = $this->removeEnd;
+                $newSlot->end = $this->available[$i]->end;
+
+                $this->available[$i]->end = $this->removeStart;
+                array_splice($this->available, $i+1, 0, [$newSlot]);
+                $allDone = true;
+                break;
+            }
+
+            // removeStart at or after slot start
+            // removeStart at or before slot end
+            // update slot to end at removeStart
+            if($this->removeStart >= $this->available[$i]->start &&
+                $this->removeStart <= $this->available[$i]->end) {
+                $this->available[$i]->end = $this->removeStart;
+                $allDone = false;
+                break;
+            }
+
+            // process removeEnd
+
+            // removeEnd at or after slot start
+            // removeEnd at or before slot end
+            // update slot to start at removeEnd
+            if($this->removeEnd >= $this->available[$i]->start &&
+                $this->removeEnd <= $this->available[$i]->end) {
+                $this->available[$i]->start = $this->removeEnd;
+                $allDone = false;
+                break;
+            }
+
+        }
+
+        if(!$allDone) {
+            $this->normalize(); // recurse till clean
+        }
+    }
+
+    private function normalize() {
+
+        $allDone = true;
+
+        for ($i=0; $i<count($this->available)-1; $i++) {
+            if($this->available[$i]->end >= $this->available[$i+1]->start && // ends in the middle of next slot
+                $this->available[$i]->end <= $this->available[$i+1]->end)
+            {
+                // extend self & delete next
+                $this->available[$i]->end = $this->available[$i+1]->end;
+                array_splice($this->available, $i+1, 1);
+                $allDone = false;
+                break;
+            }
+
+            if($this->available[$i]->end >= $this->available[$i+1]->end) // ends after next slot end
+            {
+                // delete next
+                array_splice($this->available, $i+1, 1);
+                $allDone = false;
+                break;
+            }
+
+            if($this->available[$i]->start >= $this->available[$i]->end) // starts and ends at the same time!
+            {
+                // delete self
+                array_splice($this->available, $i, 1);
+                $allDone = false;
+                break;
+            }
+        }
+
+        if(!$allDone) {
+            $this->normalize(); // recurse till clean
+        }
+
+    }
+
+    public function vdump($_x) {
+//        echo "<pre>";
+//        print_r($_x);
+//        echo "</pre><hr>";
+    }
+
+}

+ 16 - 0
app/Helpers/TimeSlot.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Helpers;
+
+class TimeSlot {
+
+    public $start = null;
+    public $end = null;
+
+    public function __construct(\DateTime $_start, \DateTime $_end)
+    {
+        $this->start = $_start->getTimestamp();
+        $this->end = $_end->getTimestamp();
+    }
+
+}

+ 63 - 23
app/Http/Controllers/AppointmentController.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers;
 
+use App\Helpers\TimeLine;
 use App\Models\Appointment;
 use App\Models\BDTDevice;
 use App\Models\CareMonth;
@@ -108,6 +109,7 @@ class AppointmentController extends Controller
         $phpTZ = $this->appTZtoPHPTZ($timeZone);
         $startDate = new \DateTime($start, new \DateTimeZone($phpTZ));
         $endDate = new \DateTime($end, new \DateTimeZone($phpTZ));
+        $endDate->setTime(23,59,59);
         $period = new \DatePeriod($startDate, \DateInterval::createFromDateString('1 day'), $endDate);
         $days = [];
         foreach ($period as $day) {
@@ -119,45 +121,83 @@ class AppointmentController extends Controller
 
         foreach ($proIds as $proId) {
 
+            $proTimeLine = new TimeLine($startDate, $endDate);
+
             $pro = Pro::where('id', $proId)->first();
 
             $proGenAvail = $genAvail->filter(function ($record) use ($proId) {
                 return $record->pro_id == $proId;
             });
 
+            $proSpecAvail = $specAvail->filter(function ($record) use ($proId) {
+                return $record->pro_id == $proId;
+            });
+
+            $proSpecUnavail = $specUnavail->filter(function ($record) use ($proId) {
+                return $record->pro_id == $proId;
+            });
+
+            // general availability
             foreach ($days as $day) {
 
                 $proGenAvailForTheDay = $proGenAvail->filter(function ($record) use ($day) {
                     return $record->day_of_week === $day["day"];
                 });
+                foreach ($proGenAvailForTheDay as $ga) {
+
+                    $gaStart = new \DateTime($day["date"], new \DateTimeZone($this->appTZtoPHPTZ($ga->timezone)));
+                    $parts = explode(":", $ga->start_time);
+                    $gaStart->setTime(intval($parts[0]), intval($parts[1]), intval($parts[2]));
+                    $gaStart->setTimezone(new \DateTimeZone($phpTZ));
 
-                if(count($proGenAvailForTheDay)) {
-                    foreach ($proGenAvailForTheDay as $ga) {
-
-                        $gaStart = new \DateTime($day["date"], new \DateTimeZone($phpTZ));
-                        $parts = explode(":", $ga->start_time);
-                        $gaStart->setTime(intval($parts[0]), intval($parts[1]), intval($parts[2]));
-
-                        $gaEnd = new \DateTime($day["date"], new \DateTimeZone($phpTZ));
-                        $parts = explode(":", $ga->end_time);
-                        $gaEnd->setTime(intval($parts[0]), intval($parts[1]), intval($parts[2]));
-
-                        $events[] = [
-                            "type" => "availability",
-                            "title" => $pro->displayName(),
-                            "proId" => $pro->id,
-                            "proUid" => $pro->uid,
-                            "start" => $gaStart->format('Y-m-d H:i:s'),
-                            "end" => $gaEnd->format('Y-m-d H:i:s'),
-                            "editable" => false
-                        ];
-                    }
+                    $gaEnd = new \DateTime($day["date"], new \DateTimeZone($this->appTZtoPHPTZ($ga->timezone)));
+                    $parts = explode(":", $ga->end_time);
+                    $gaEnd->setTime(intval($parts[0]), intval($parts[1]), intval($parts[2]));
+                    $gaEnd->setTimezone(new \DateTimeZone($phpTZ));
+
+                    $proTimeLine->addAvailability($gaStart, $gaEnd);
                 }
 
-                // TODO: add spec avail
-                // TODO: subtract spec unavail
+            }
 
+            // specific availability
+            foreach ($proSpecAvail as $sa) {
+                $saStart = new \DateTime($sa->start_time, new \DateTimeZone($this->appTZtoPHPTZ($sa->timezone)));
+                $saStart->setTimezone(new \DateTimeZone($phpTZ));
+                $saEnd = new \DateTime($sa->end_time, new \DateTimeZone($this->appTZtoPHPTZ($sa->timezone)));
+                $saEnd->setTimezone(new \DateTimeZone($phpTZ));
+                $proTimeLine->addAvailability($saStart, $saEnd);
             }
+
+            // specific unavailability
+            foreach ($proSpecUnavail as $sua) {
+                $suaStart = new \DateTime($sua->start_time, new \DateTimeZone($this->appTZtoPHPTZ($sua->timezone)));
+                $suaStart->setTimezone(new \DateTimeZone($phpTZ));
+                $suaEnd = new \DateTime($sua->end_time, new \DateTimeZone($this->appTZtoPHPTZ($sua->timezone)));
+                $suaEnd->setTimezone(new \DateTimeZone($phpTZ));
+                $proTimeLine->removeAvailability($suaStart, $suaEnd);
+            }
+
+            foreach ($proTimeLine->available as $item) {
+
+                $eStart = new \DateTime('@' . $item->start);
+                $eStart->setTimezone(new \DateTimeZone($phpTZ));
+
+                $eEnd = new \DateTime('@' . $item->end);
+                $eEnd->setTimezone(new \DateTimeZone($phpTZ));
+
+                $events[] = [
+                    "type" => "availability",
+                    "title" => $pro->displayName(),
+                    "proId" => $pro->id,
+                    "proUid" => $pro->uid,
+                    "start" => $eStart->format('Y-m-d H:i:s'),
+                    "end" => $eEnd->format('Y-m-d H:i:s'),
+                    "editable" => false
+                ];
+
+            }
+
         }
 
         return json_encode($events);

+ 3 - 3
resources/views/app/patient/appointment-calendar.blade.php

@@ -68,8 +68,8 @@
                         class="form-control form-control-sm w-150"
                         v-model="eventTypes"
                         v-on:change="refreshEvents()">
-                    <option value="APPOINTMENTS" selected>Appointments</option>
-                    <option value="PRO_AVAILABILITY">Pro Availability</option>
+                    <option value="APPOINTMENTS">Appointments</option>
+                    <option value="PRO_AVAILABILITY" selected>Pro Availability</option>
                     <option value="BOTH">Both</option>
                 </select>
             </div>
@@ -346,7 +346,7 @@
                     el: '#calendarApp',
                     data: {
                         client: {!! json_encode($patient) !!},
-                        eventTypes: 'APPOINTMENTS',
+                        eventTypes: 'PRO_AVAILABILITY',
                         calendar: null,
                         proMeta: {!! json_encode($proMeta) !!},
                         proIds: ['{{ $pro->id }}'],