url = $url; $this->user = Auth::guard('admin')->user(); } public function getAppointmentList() { try { $this->authorizeForUser($this->user, 'list', new Appointment); $appointments = Appointment::select("patients.first_name", "patients.last_name", "telemed_pros.name as agent_name", "appointments.*") // Eager load the associated telemed pro ->leftJoin("telemed_pros", "telemed_pros.id", "appointments.telemed_pros_id") ->leftJoin("patients", "patients.id", "appointments.patient_id") /* ->orderBy('appointment_time', 'desc') */ // Optional: sort by appointment time ->get(); return response()->json($appointments, 200); } catch (AuthorizationException $e) { return response()->json(['error' => 'Failed to retrieve appointments'], 500); } } public function getMeetingHistory(Patient $patient, $filter = '12_months') { try { $this->authorizeForUser($this->user, 'meeting_history', new Appointment); $currentMonth = Carbon::now(); // Filter logic switch ($filter) { case 'current_month': $startDate = $currentMonth->copy()->startOfMonth(); break; case '1_month': $startDate = $currentMonth->copy()->subMonth()->startOfMonth(); break; case '2_months': $startDate = $currentMonth->copy()->subMonths(2)->startOfMonth(); break; case '3_months': $startDate = $currentMonth->copy()->subMonths(3)->startOfMonth(); break; case '6_months': $startDate = $currentMonth->copy()->subMonths(6)->startOfMonth(); break; default: // Default to 12 months $startDate = $currentMonth->copy()->subMonths(12)->startOfMonth(); } $endDate = $currentMonth->endOfMonth(); // Fetch patient names and appointment counts directly from the database $monthlyData = Appointment::select( 'patient_id', /* DB::raw('COUNT(*) as appointment_count'), */ 'appointment_time', 'appointment_date', 'start_time', 'end_time', 'duration', 'id' ) ->where("patient_id", $patient->id) ->whereNotNull("end_time") ->whereBetween('created_at', [$startDate, $endDate]) ->get(); $patients = []; foreach ($monthlyData as $dataPoint) { $patientName = $dataPoint->patient->first_name . " " . $dataPoint->patient->last_name; // Assuming 'name' is the field representing patient names /* $appointmentCount = $dataPoint->appointment_count; */ $start_time = $dataPoint->start_time; $end_time = $dataPoint->end_time; $duration = $dataPoint->duration; $appointment_time = $dataPoint->appointment_time; $appointment_date = $dataPoint->appointment_date; $id = $dataPoint->id; $patients[] = [ 'patient_name' => $patientName, 'appointment_time' => $appointment_time, 'appointment_date' => $appointment_date, /* 'appointment_count' => $appointmentCount, */ 'start_time' => $start_time, 'end_time' => $end_time, 'duration' => $duration, 'id' => $id, ]; } return response()->json([ 'patients' => $patients, ]); } catch (AuthorizationException $e) { return response()->json(['error' => 'Failed to retrieve appointments'], 500); } } public function getAppointmentByid($patient, $appointment, Request $request) { try { $this->authorizeForUser($this->user, 'list', new Appointment); // Assuming user can be either telemedPro or patient $data = Appointment::select('appointments.*', 'telemed_pros.name as agent_name') ->leftJoin('telemed_pros', 'appointments.telemed_pros_id', '=', 'telemed_pros.id') ->where('appointments.patient_id', $patient) ->where('appointments.id', $appointment) ->first(); // dd($data); return response()->json(['data' => $data]); } catch (AuthorizationException $e) { return response()->json(['error' => 'Failed to retrieve appointments'], 500); } } public function bookAppointment(Request $request) { try { $this->authorizeForUser($this->user, 'list', new Appointment); $validatedData = $request->validate([ /* 'telemed_pros_id' => 'required|exists:telemed_pros,id', */ 'patient_id' => 'required|exists:patients,id', 'appointment_time' => 'required|date_format:H:i:s', 'appointment_date' => 'required|date_format:Y-m-d', 'patient_name' => 'required', 'patient_email' => 'required', 'timezone' => 'required', ]); try { $tz = new DateTimeZone($validatedData['timezone']); $standardTz = $tz->getName(); } catch (Exception $e) { return response()->json([ 'message' => $e->getMessage() ], 400); } try { $timezoneMap = [ 'EST' => 'America/New_York', 'EDT' => 'America/New_York', 'CST' => 'America/Chicago', 'CDT' => 'America/Chicago', 'MST' => 'America/Denver', 'MDT' => 'America/Denver', 'PST' => 'America/Los_Angeles', 'PDT' => 'America/Los_Angeles', // Add more mappings as needed ]; $timezone = $validatedData['timezone']; if (array_key_exists($timezone, $timezoneMap)) { $timezone = $timezoneMap[$timezone]; } $appointmentDateTime = new DateTime( $validatedData['appointment_date'] . ' ' . $validatedData['appointment_time'], new DateTimeZone($timezone) ); $appointmentDateTime->setTimezone(new DateTimeZone('UTC')); $validatedData['appointment_time'] = $appointmentDateTime->format('H:i:s'); $validatedData['appointment_date'] = $appointmentDateTime->format('Y-m-d'); } catch (Exception $e) { return response()->json([ 'message' => $e->getMessage() ], 400); } $availableTelemedPros = Telemedpro::select("telemed_pros.id", "telemed_pros.name")/* ->where('is_busy', false) */ ->leftJoin('appointments', function ($join) use ($validatedData) { $join->on('telemed_pros.id', '=', 'appointments.telemed_pros_id') ->where('appointments.appointment_time', '=', $validatedData['appointment_time']) ->where('appointments.appointment_date', '=', $validatedData['appointment_date']); }) ->whereNull('appointments.id') ->first(); if (!$availableTelemedPros) return response()->json([ 'message' => 'Appointment time not available' ], 400); $existingAppointment = Appointment::where('telemed_pros_id', $availableTelemedPros->id) ->where('appointment_time', $validatedData['appointment_time']) ->where('appointment_date', $validatedData['appointment_date']) ->first(); if ($existingAppointment) { return response()->json([ 'message' => 'Appointment time not available' ], 400); } $validatedData['telemed_pros_id'] = $availableTelemedPros->id; $validatedData['status'] = 'pending'; // Create the appointment $appointment = Appointment::create($validatedData); $appointment_booking_tokens = $this->bookAppointmentApi($appointment, $availableTelemedPros); $appointment->agent_call_token = $appointment_booking_tokens['tokenAgent']; $appointment->patient_call_token = $appointment_booking_tokens['tokenPatient']; $appointment->save(); PatientRegActivity::create([ 'patient_id' => $validatedData['patient_id'], 'activity' => 'patient_appointment_booked' ]); $patient = $appointment->patient; $datetimeUtc = $appointment->appointment_date . ' ' . $appointment->appointment_time; $dateTimeUtc = Carbon::createFromFormat('Y-m-d H:i:s', $datetimeUtc, 'UTC'); $appointmentTimeZone = new CarbonTimeZone($appointment->timezone); $dateTimeInAppointmentTimeZone = $dateTimeUtc->setTimezone($appointmentTimeZone); $appointment->appointment_date = $appointmentDate = $dateTimeInAppointmentTimeZone->format('Y-m-d'); $appointment->appointment_time = $appointmentTime = $dateTimeInAppointmentTimeZone->format('H:i:s'); $setting = Setting::find(1); //event(new AppointmentBooked($appointment)); $cart = Cart::find($request->input("cart_id")); $cart->appointment_id = $appointment->id; $cart->save(); return response()->json([ 'message' => 'Appointment booked successfully', 'meeting_id' => $appointment->agent_call_token, 'appointment' => $appointment, 'appointment_time' => $validatedData['appointment_time'], 'appointment_date' => $validatedData['appointment_date'] ]); } catch (AuthorizationException $e) { return response()->json(['error' => $e->getMessage()], 500); } } public function editAppointment(Appointment $appointment, Request $request) { try { $this->authorizeForUser($this->user, 'list', new Appointment); $validatedData = $request->validate([ 'patient_id' => 'sometimes|exists:patients,id', 'appointment_time' => 'sometimes|date_format:H:i:s', 'appointment_date' => 'sometimes|date_format:Y-m-d', 'patient_name' => 'sometimes|string', 'patient_email' => 'sometimes|email', 'timezone' => 'sometimes|string', ]); if (isset($validatedData['timezone'])) { try { $tz = new DateTimeZone($validatedData['timezone']); $standardTz = $tz->getName(); } catch (Exception $e) { return response()->json([ 'message' => $e->getMessage() ], 400); } $timezoneMap = [ 'EST' => 'America/New_York', 'EDT' => 'America/New_York', 'CST' => 'America/Chicago', 'CDT' => 'America/Chicago', 'MST' => 'America/Denver', 'MDT' => 'America/Denver', 'PST' => 'America/Los_Angeles', 'PDT' => 'America/Los_Angeles', // Add more mappings as needed ]; $timezone = $validatedData['timezone']; if (array_key_exists($timezone, $timezoneMap)) { $timezone = $timezoneMap[$timezone]; } if (isset($validatedData['appointment_date']) && isset($validatedData['appointment_time'])) { try { $appointmentDateTime = new DateTime( $validatedData['appointment_date'] . ' ' . $validatedData['appointment_time'], new DateTimeZone($timezone) ); $appointmentDateTime->setTimezone(new DateTimeZone('UTC')); $validatedData['appointment_time'] = $appointmentDateTime->format('H:i:s'); $validatedData['appointment_date'] = $appointmentDateTime->format('Y-m-d'); } catch (Exception $e) { return response()->json([ 'message' => $e->getMessage() ], 400); } // Check if the new time slot is available $availableTelemedPros = Telemedpro::select("telemed_pros.id", "telemed_pros.name") ->leftJoin('appointments', function ($join) use ($validatedData, $appointment) { $join->on('telemed_pros.id', '=', 'appointments.telemed_pros_id') ->where('appointments.appointment_time', '=', $validatedData['appointment_time']) ->where('appointments.appointment_date', '=', $validatedData['appointment_date']) ->where('appointments.id', '!=', $appointment->id); // Exclude the current appointment }) ->whereNull('appointments.id') ->first(); if (!$availableTelemedPros) { return response()->json([ 'message' => 'New appointment time not available' ], 400); } // Update the telemed_pros_id if it's different if ($availableTelemedPros->id !== $appointment->telemed_pros_id) { $validatedData['telemed_pros_id'] = $availableTelemedPros->id; // Re-book the appointment with the new telemed pro $appointment_booking_tokens = $this->bookAppointmentApi($appointment, $availableTelemedPros); $validatedData['agent_call_token'] = $appointment_booking_tokens['tokenAgent']; $validatedData['patient_call_token'] = $appointment_booking_tokens['tokenPatient']; } } // Update the appointment $appointment->update($validatedData); // Update related cart if it exists $cart = Cart::where('appointment_id', $appointment->id)->first(); if ($cart) { $cart->appointment_id = $appointment->id; $cart->save(); } // Convert appointment time to the specified timezone for the response $datetimeUtc = $appointment->appointment_date . ' ' . $appointment->appointment_time; $dateTimeUtc = Carbon::createFromFormat('Y-m-d H:i:s', $datetimeUtc, 'UTC'); $appointmentTimeZone = new CarbonTimeZone($appointment->timezone); $dateTimeInAppointmentTimeZone = $dateTimeUtc->setTimezone($appointmentTimeZone); $appointmentDate = $dateTimeInAppointmentTimeZone->format('Y-m-d'); $appointmentTime = $dateTimeInAppointmentTimeZone->format('H:i:s'); return response()->json([ 'message' => 'Appointment updated successfully', 'meeting_id' => $appointment->agent_call_token, 'appointment' => $appointment, 'appointment_time' => $appointmentTime, 'appointment_date' => $appointmentDate ]); } } catch (AuthorizationException $e) { return response()->json(['error' => $e->getMessage()], 500); } } public function bookAppointmentApi($appointment, $availableTelemedPros) { try { $this->authorizeForUser($this->user, 'edit', new Appointment); $roomName = 'appointment-' . $appointment->id . "-" . uniqid(); $opts = (new RoomCreateOptions()) ->setName($roomName) ->setEmptyTimeout(10) ->setMaxParticipants(5); $host = "https://plugnmeet.codelfi.com"; $svc = new RoomServiceClient($host, config('app.LK_API_KEY'), config('app.LK_API_SECRET')); $room = $svc->createRoom($opts); $participantPatientName = "patient-" . uniqid() . $appointment->patient->first_name . " " . $appointment->patient->last_name; $tokenOptionsPatient = (new AccessTokenOptions()) ->setIdentity($participantPatientName); $videoGrantPatient = (new VideoGrant()) ->setRoomJoin() ->setRoomName($roomName); $tokenPatient = (new AccessToken(config('app.LK_API_KEY'), config('app.LK_API_SECRET'))) ->init($tokenOptionsPatient) ->setGrant($videoGrantPatient) ->toJwt(); $participantAgentName = "agent-" . uniqid() . $availableTelemedPros->name; $tokenOptionsAgent = (new AccessTokenOptions()) ->setIdentity($participantAgentName); $videoGrantAgent = (new VideoGrant()) ->setRoomJoin() ->setRoomName($roomName); $tokenAgent = (new AccessToken(config('app.LK_API_KEY'), config('app.LK_API_SECRET'))) ->init($tokenOptionsAgent) ->setGrant($videoGrantAgent) ->toJwt(); return [ 'tokenPatient' => $tokenPatient, 'tokenAgent' => $tokenAgent, ]; } catch (AuthorizationException | Error | Exception | TwirpError $e) { return response()->json(['error' => $e->getMessage()], 500); } } public function availableSlots($date) { try { $this->authorizeForUser($this->user, 'book_appointment', new Appointment); // Ensure date is in a valid format $date = Carbon::parse($date); $originalDate = Carbon::parse($date); // Generate all possible 30-minute slots between 9 AM and 4 PM $slots = collect(); $startTime = Carbon::parse($date)->subHours(24)->setTime(9, 0, 0); $endTime = Carbon::parse($date)->addHours(24)->setTime(16, 0, 0); while ($startTime < $endTime) { $slots->push($startTime->format('Y-m-d H:i:s')); $startTime->addMinutes(15); } /* $user = Patient::find($patient_id); */ // Filter out booked slots $bookedAppointments = Appointment::where('appointment_date', '>=', $date->format('Y-m-d')) ->where('appointment_date', '<', $date->addDay()->format('Y-m-d')) ->pluck('appointment_date'); $availableSlots = $slots->diff($bookedAppointments); $formattedSlots = $availableSlots->map(function ($slot) { $start = Carbon::parse($slot); $startTime = $start->format('Y-m-d H:i:s'); return $startTime; }); // Additional checking if slot is booked $formattedSlots = $formattedSlots->filter(function ($slot) use ($originalDate) { $time = Carbon::parse($slot); return !Appointment::where('appointment_time', $time->format('H:i:s')) ->where('appointment_date', $originalDate->format('Y-m-d')) ->exists(); }); return response()->json([ 'available_slots' => $formattedSlots->toArray() ]); } catch (AuthorizationException $e) { return response()->json(['error' => $e->getMessage()], 500); } } public function getItemByOrder($order_id) { try { $this->authorizeForUser($this->user, 'list', new Appointment); $labkits = LabkitOrderItem::leftJoin( 'lab_kit', 'labkit_order_items.lab_kit_id', 'lab_kit.id' ) ->leftJoin( 'items', 'items.id', 'labkit_order_items.item_id' ) ->leftJoin( 'plans_v1', 'plans_v1.id', 'items.plans_id' ) ->leftJoin( 'carts', 'carts.id', 'labkit_order_items.cart_id' ) ->where('carts.id', $order_id) ->select( 'labkit_order_items.id', 'labkit_order_items.status', 'labkit_order_items.result', 'lab_kit.name as lab_kit_name', 'plans_v1.id as product_id', 'plans_v1.title as product_name' ) ->get(); return response()->json([ 'order_item' => $labkits ]); } catch (AuthorizationException $e) { return response()->json(['error' => $e->getMessage()], 500); } } public function getAgentLastAppointment(Patient $patient, Request $request) { try { $this->authorizeForUser($this->user, 'list', new Appointment); $appointments = Appointment::select( "patients.first_name", "patients.last_name", "telemed_pros.name as agent_name", "appointments.*", "carts.shipping_address1", "carts.shipping_address2", "carts.id as order_id", "carts.shipping_city", "carts.shipping_state", "carts.shipping_zipcode", "carts.shipping_country" ) // Eager load the associated telemed pro ->leftJoin("telemed_pros", "telemed_pros.id", "appointments.telemed_pros_id") ->leftJoin("patients", "patients.id", "appointments.patient_id") ->leftJoin("carts", "carts.appointment_id", "appointments.id") ->where("appointments.patient_id", $patient->id) ->orderBy('appointments.created_at', 'desc') ->first(); $upcoming_appointments = Appointment::select( "patients.first_name", "patients.last_name", "telemed_pros.name as agent_name", "appointments.*", "carts.shipping_address1", "carts.shipping_address2", "carts.id as order_id", "carts.shipping_city", "carts.shipping_state", "carts.shipping_zipcode", "carts.shipping_country", "appointments.id as order_appointment_id" ) // Eager load the associated telemed pro ->leftJoin("telemed_pros", "telemed_pros.id", "appointments.telemed_pros_id") ->leftJoin("patients", "patients.id", "appointments.patient_id") ->leftJoin("carts", "carts.appointment_id", "appointments.id") //->where('appointments.appointment_date', '<', $appointments->appointment_date) ->where("appointments.patient_id", $patient->id) ->where("appointments.status", 'pending') ->whereNull("appointments.start_time") ->orderBy('appointments.created_at', 'desc') ->get(); if (!$appointments) return response()->json(['error' => 'No Record found.'], 500); $timezone = config('app.timezone'); if ($appointments->timezone) { $tz = new DateTimeZone($appointments->timezone); $standardTz = $tz->getName(); $appointmentDateTime = $appointmentCurrent = Carbon::parse($appointments->appointment_date . ' ' . $appointments->appointment_time)->shiftTimezone($standardTz); //$appointmentDateTime = $appointmentDateTime->shiftTimezone($timezone); $appointmentCurrent = Carbon::now($timezone); $diff = $appointmentDateTime->diff($appointmentCurrent); if ($diff->invert == 0) { // Appointment is in future, increment count $diff = $diff->format('0 days 0 hours 0 minutes 0 seconds'); } else $diff = $diff->format('%a days %h hours %i minutes %s seconds'); } else { $diff = ""; } $filePath = public_path("assets/profiles/{$patient->id}.png"); if ($patient->profile_picture) $patient->profile_picture = $this->url->to("storage/profile_pictures", $patient->profile_picture); else $patient->profile_picture = null; if (File::exists($filePath)) { $patient->url = "/assets/profiles/{$patient->id}.png"; } else { $patient->url = null; } foreach ($upcoming_appointments as $upcoming_appointment) { if ($upcoming_appointment->timezone) { $tz = new DateTimeZone($upcoming_appointment->timezone); $standardTz = $tz->getName(); $appointmentDateTime = $appointmentCurrent = Carbon::parse($upcoming_appointment->appointment_date . ' ' . $upcoming_appointment->appointment_time)->shiftTimezone($standardTz); //$appointmentDateTime = $appointmentDateTime->shiftTimezone($timezone); $appointmentCurrent = Carbon::now($timezone); $diff = $appointmentDateTime->diff($appointmentCurrent); if ($diff->invert == 0) { // Appointment is in future, increment count $diff = $diff->format('0 days 0 hours 0 minutes 0 seconds'); } else $diff = $diff->format('%a days %h hours %i minutes %s seconds'); } else { $diff = ""; } $upcoming_appointment->items_data = $this->getOrderItems($upcoming_appointment->order_id); $upcoming_appointment->time_diff = $diff; } return response()->json(['upcoming_appointments' => $upcoming_appointments, 'appointment' => $appointments, 'time_diff' => $diff, 'patient' => $patient, "items_data" => $this->getOrderItems($appointments->order_id)], 200); } catch (\Exception $e) { return response()->json(['error' => 'Failed to retrieve appointments'], 500); } } }