rejuvallife/app/Http/Controllers/Admin/Api/AppointmentController.php
2024-10-25 01:02:11 +05:00

644 lines
28 KiB
PHP

<?php
namespace App\Http\Controllers\Admin\Api;
use Agence104\LiveKit\VideoGrant;
use App\Http\Controllers\Controller;
use App\Models\Appointment;
use App\Models\Cart;
use App\Models\LabkitOrderItem;
use App\Models\Patient;
use App\Models\PatientRegActivity;
use App\Models\Setting;
use App\Models\Telemedpro;
use Carbon\Carbon;
use Carbon\CarbonTimeZone;
use DateTime;
use DateTimeZone;
use Error;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Yajra\DataTables\DataTables;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Validator;
use Agence104\LiveKit\AccessToken;
use Agence104\LiveKit\AccessTokenOptions;
use Agence104\LiveKit\RoomCreateOptions;
use Agence104\LiveKit\RoomServiceClient;
use Google\Protobuf\TwirpError;
use Illuminate\Auth\Access\AuthorizationException;
class AppointmentController extends Controller
{
protected $url;
protected $user;
public function __construct(UrlGenerator $url)
{
$this->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);
}
}
}