Server : Apache System : Linux 145.162.205.92.host.secureserver.net 5.14.0-611.45.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Apr 1 05:56:53 EDT 2026 x86_64 User : tradze ( 1001) PHP Version : 8.1.34 Disable Function : NONE Directory : /home/tradze/www/app/Modules/Users/Http/Controllers/Api/ |
<?php
namespace App\Modules\Users\Http\Controllers\Api;
use App\Events\ApiResetBasketEvent;
use App\Http\Controllers\ApiController;
use App\Modules\Schedules\Models\BookingOrder;
use App\Modules\Schedules\Models\Order;
use App\Modules\Schedules\Repositories\BookingRepository;
use App\Modules\Schedules\Repositories\BookingClass;
use App\Modules\Services\Models\ServiceDuration;
use App\Modules\Services\Models\ServiceType;
use App\Modules\Users\Http\Requests\TherapistCreateClientRequest;
use App\Modules\Users\Models\UserLocation;
use App\Modules\Notifications\Facades\NotifRepository;
use App\Modules\Schedules\Models\BookingAttachment;
use App\Modules\Users\Models\UserProfile;
use App\Modules\Users\Repositories\ApiRepository;
use App\Modules\Vouchers\Models\Voucher;
use App\User;
use Spatie\Permission\Models\Role;
use Carbon\Carbon;
use Cmgmyr\Messenger\Models\Thread;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use App\Modules\Users\Models\UserServiceDuration;
use App\Modules\Users\Models\UserCoverageArea;
use App\Modules\Users\Models\UserDevice;
use App\Modules\Schedules\Repositories\BookingClass as RepositoriesBookingClass;
use App\Modules\Testimonials\Models\SalonReviews;
use App\Modules\Schedules\Models\DynamicPricing;
use App\Modules\Users\Models\BookRequest;
use GuzzleHttp\Client;
use Illuminate\Support\Str;
class ApiClient extends ApiController
{
public function get_booking_attachments(Request $request, $booking_id)
{
// Validate booking_id
$validator = Validator::make(
['booking_id' => $booking_id],
['booking_id' => 'required|exists:booking_orders,id']
);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => $validator->errors()->first()
], 422);
}
$bookingId = $request->booking_id;
// Fetch attachments
$attachments = BookingAttachment::where('booking_id', $bookingId)
->orderBy('id', 'desc')
->get();
// If no data
if ($attachments->isEmpty()) {
return response()->json([
'success' => true,
'message' => 'No attachments found',
'data' => []
]);
}
return response()->json([
'success' => true,
'message' => 'Attachments fetched successfully',
'data' => $attachments
]);
}
public function upload_booking_attachment(Request $request)
{
$validator = Validator::make($request->all(), [
'booking_id' => 'required|exists:booking_orders,id',
'files' => 'required|array',
'files.*' => 'file|mimes:jpg,jpeg,png,mp4,mov,avi|max:25600'
]);
if ($validator->fails()) {
return response()->json([
'status' => false,
'message' => $validator->errors()->first()
], 422);
}
$userId = auth()->id();
$bookingId = $request->booking_id;
// Count existing files
$existingImages = BookingAttachment::where('booking_id', $bookingId)
->where('file_type', 'IMAGE')
->count();
$existingVideos = BookingAttachment::where('booking_id', $bookingId)
->where('file_type', 'VIDEO')
->count();
$imageLimit = 4;
$videoLimit = 2;
$uploadedFiles = $request->file('files');
if (!$uploadedFiles) {
return response()->json(['message' => 'No files uploaded'], 400);
}
$folderName = $userId . '_' . $bookingId;
$storagePath = "public/bookingAttachments/" . $folderName;
$responseData = [];
foreach ($uploadedFiles as $file) {
$extension = strtolower($file->getClientOriginalExtension());
$fileType = in_array($extension, ['jpg', 'jpeg', 'png']) ? 'IMAGE' : 'VIDEO';
// Check limits
if ($fileType === 'IMAGE' && $existingImages >= $imageLimit) {
continue;
}
if ($fileType === 'VIDEO' && $existingVideos >= $videoLimit) {
continue;
}
$originalName = $file->getClientOriginalName();
$extension = $file->getClientOriginalExtension();
// unique filename (timestamp + microtime to avoid collision)
$fileName = time() . '_' . uniqid() . '.' . $extension;
// Store file
$path = $file->storeAs($storagePath, $fileName);
// Convert to public URL path
$publicPath = Storage::url($path); // gives /storage/bookingAttachments/...
// Save in DB
$attachment = BookingAttachment::create([
'booking_id' => $bookingId,
'file_type' => $fileType,
'file_path' => $publicPath,
'file_name' => $originalName,
]);
$responseData[] = $attachment;
// Increment counters
if ($fileType === 'IMAGE') {
$existingImages++;
} else {
$existingVideos++;
}
}
if (empty($responseData)) {
return response()->json([
'success' => false,
'message' => 'File upload limits reached. No files were uploaded.',
'data' => null
], 400);
}
return response()->json([
'success' => true,
'message' => 'Files uploaded successfully',
'data' => $responseData
]);
}
public function delete_booking_attachments(Request $request)
{
$validator = Validator::make($request->all(), [
'booking_id' => 'required|exists:booking_orders,id',
'attachment_ids' => 'nullable|array',
'attachment_ids.*' => 'exists:booking_attachments,id'
]);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => $validator->errors()->first()
], 422);
}
$bookingId = $request->booking_id;
$attachmentIds = $request->attachment_ids;
// Case 1: Delete selected attachments
if (!empty($attachmentIds)) {
$attachments = BookingAttachment::where('booking_id', $bookingId)
->whereIn('id', $attachmentIds)
->get();
if ($attachments->isEmpty()) {
return response()->json([
'success' => false,
'message' => 'No matching attachments found'
], 404);
}
} else {
// Case 2: Delete ALL attachments of booking
$attachments = BookingAttachment::where('booking_id', $bookingId)->get();
if ($attachments->isEmpty()) {
return response()->json([
'success' => false,
'message' => 'No attachments found for this booking'
], 404);
}
}
// Delete files + DB records
foreach ($attachments as $attachment) {
// Convert /storage/... → public/... for deletion
$filePath = str_replace('/storage/', 'public/', $attachment->file_path);
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
$attachment->delete();
}
return response()->json([
'success' => true,
'message' => 'Attachment(s) deleted successfully'
]);
}
public function askForBooking(Request $request)
{
try {
$socketBaseUrl = env('SOCKET_BASE_URL');
// Generate unique req_id using timestamp in milliseconds + random string
$timestampMs = round(microtime(true) * 1000); // current time in ms
$randomStr = substr(bin2hex(random_bytes(4)), 0, 8); // 8-char random string
$reqId = $timestampMs . '_' . $randomStr;
$expiry = Carbon::now('Europe/London')->addSeconds(60);
$booking = Session::get('booking');;
$data['booking'] = $booking;
$data['to'] = $request->thID;
$data['clickedFrom'] = $request->clickedFrom;
$data['from'] = Auth::user()->id;
$data['customer_name'] = Auth::user()->name;
$data['customer_avatar'] = Auth::user()->profile['image_url'];
$data['request_exp_time'] = [
'date' => $expiry->format('Y-m-d H:i:s.u'),
'timezone_type' => 3,
'timezone' => $expiry->getTimezone()->getName(),
];
$data['hour'] = isset($request->hour) ? $request->hour : $booking['hour'];
$data['req_id'] = $reqId;
if ($data['hour'] == '00:00') {
$data['hour'] = date('H:i');
}
$booking['request_exp_time'] = $data['request_exp_time'];
//add push notification for booking
$notif_message = 'You have a booking request from ' . $data['customer_name'] . '.';
$notif_users = $data['to'];
$notiData['booking'] = $booking;
$notiData['reciever'] = $request->thID;
$notiData['clickedFrom'] = $request->clickedFrom;
$notiData['sender'] = Auth::user()->id;
$notiData['customer_name'] = Auth::user()->name;
$notiData['customer_avatar'] = Auth::user()->profile['image_url'];
$notiData['request_exp_time'] = Carbon::now()->addSeconds(60);
$notiData['hour'] = isset($request->hour) ? $request->hour : $booking['hour'];
$notiData['req_id'] = $reqId;
NotifRepository::add($notif_users, $notif_message, 'booking-request', 'Booking Request', $notiData);
$client = new Client();
$response = $client->post($socketBaseUrl . '/booking/ask-for-booking', [
'json' => $data
]);
$notiData = [
'req_id' => $reqId,
'service_provider' => $request->thID, // Assuming therapist or provider ID
'customer_id' => Auth::user()->id,
'booking' => json_encode($booking),
'customer_name' => Auth::user()->name,
'customer_avatar' => Auth::user()->profile['image_url'] ?? null,
'hour' => $request->hour ?? $booking['hour'] ?? null,
'date' => $booking['date'],
'req_expire' => Carbon::now()->addSeconds(60),
'address' => $booking['address'] ?? $booking['postalcode'] ?? '',
'status' => 0 //Pending
];
// Create record in DB
$newRequest = BookRequest::create($notiData);
return response()->json([
'success' => true,
'message' => 'Request sent!',
'data' => $booking,
'req_id' => $reqId
], 200);
} catch (\Throwable $th) {
return response()->json([
'success' => false,
'message' => $th->getMessage()
], 500);
}
}
public function sendPushNoti(Request $request)
{
try {
$notif_message = $request->message;
$sendTo = $request->userId;
$notificationHead = $request->notificationHead;
$notiData['message'] = $notif_message;
NotifRepository::add($sendTo, $notif_message, $notificationHead, $notificationHead, $notiData);
return response()->json([
'success' => true,
'message' => 'Notification sent successfully'
]);
} catch (\Throwable $th) {
return response()->json([
'success' => false,
'message' => 'Failed to send notification',
'error' => $th->getMessage()
], 500);
}
}
public function deviceLogout(Request $request)
{
try {
$device_uuid = $request->device_uuid;
if (!$device_uuid) {
return response()->json([
'success' => false,
'message' => 'Device UUID is required'
], 400);
}
$device = UserDevice::where('uuid', $device_uuid)->first();
if ($device) {
$device->delete();
return response()->json([
'success' => true,
'message' => 'Device logged out successfully'
]);
} else {
return response()->json([
'success' => false,
'message' => 'Device not found'
], 404);
}
} catch (\Throwable $th) {
return response()->json([
'success' => false,
'message' => 'Failed to log out device',
'error' => $th->getMessage()
], 500);
}
}
/**
* send OTP on mobile number
*/
public function sendOTPtoMobile(Request $request)
{
// Validate the input
$validator = Validator::make($request->all(), [
'mobile_number' => 'required', // Adjust digits as per your format
'country_code' => 'required', // Adjust digits as per your format
]);
if ($validator->fails()) {
return response([
'success' => false,
'message' => 'Validation failed',
'errors' => $validator->errors(),
], 422);
}
try {
$mobile_number = $request->mobile_number;
$country_code = $request->country_code;
$otp = rand(1000, 9999);
$userProfile = UserProfile::where(['mobile_number' => $mobile_number, 'country_code' => $country_code])->first();
if (!$userProfile) {
return response([
'success' => false,
'message' => 'Phone no. not found',
], 404);
}
$userDetail = User::where('id', $userProfile->user_id)->first();
if ($userDetail) {
$userDetail->otp = $otp;
$userDetail->save();
}
$sendTo = $country_code . $mobile_number;
$message = "Tradze \n" . "Your 2-step verification code is " . $otp;
$this->sendSMS($sendTo, $message);
return response([
'success' => true,
'message' => 'OTP sent successfully.',
'data' => null,
], 200);
} catch (\Exception $e) {
return response([
'success' => false,
'message' => 'Failed to send OTP.',
'error' => $e->getMessage(),
], 500);
}
}
public function verifyOTP(Request $request)
{
// Validate the input
$validator = Validator::make($request->all(), [
'otp' => 'required',
'device_push_token' => 'required',
'email' => 'sometimes|email',
'mobile_number' => 'sometimes|required_without:email',
'country_code' => 'required_with:mobile_number',
]);
if ($validator->fails()) {
return response([
'success' => false,
'message' => 'Validation failed',
'errors' => $validator->errors(),
], 422);
}
try {
$otp = $request->otp;
if ($request->has('email') && !empty($request->get('email'))) {
$userDetail = User::where('email', $request->get('email'))->first();
} else {
$mobile_number = $request->mobile_number;
$country_code = $request->country_code;
$user = UserProfile::where(['mobile_number' => $mobile_number, 'country_code' => $country_code])->first();
$userDetail = User::where('id', $user->user_id)->first();
}
if ($userDetail) {
if ($userDetail->otp == $otp) {
$userDetail->otp = null;
$userDetail->save();
// OTP is valid fetch main user
$mainUser = $userDetail;
if (!$mainUser) {
return response(['success' => false, 'message' => 'User not found'], 404);
}
// ✅ Generate token
$token = Str::random(60);
$mainUser->api_token = $token;
$mainUser->save();
// ✅ IMPORTANT: set user for current request (api guard)
Auth::guard('api')->setUser($mainUser);
// Login the user manually
// Auth::login($mainUser);
// // Generate API token if not exists
// if (!$mainUser->api_token) {
// $mainUser->api_token = Str::random(60);
// $mainUser->save();
// }
// (Optional) Save device info
if ($request->device_uuid) {
UserDevice::updateOrCreate(
['uuid' => $request->device_uuid],
[
'push_token' => $request->device_push_token,
'os' => $request->device_os,
'os_version' => $request->device_os_version,
'brand' => $request->device_brand,
'model' => $request->device_model,
'user_agent' => $request->user_agent,
'user_id' => $mainUser->id,
]
);
}
// Compose user data like in your original code
$profile = $mainUser->profile;
$postcode = $address = null;
$cardInfo = $mainUser->braintree_id ? [
'payment_id' => $mainUser->braintree_id,
'paypal_email' => $mainUser->paypal_email,
'card_brand' => $mainUser->card_brand,
'card_last_four' => $mainUser->card_last_four
] : null;
$lastBooking = $mainUser->bookings()->latest()->first();
if ($lastBooking) {
$info = json_decode($lastBooking->orderInfo, true);
$postcode = !empty($info['postcode']) ? $info['postcode'] : '';
$address = $lastBooking->address;
}
$userData = [
'id' => $mainUser->id,
'api_token' => $mainUser->api_token,
'first_name' => $profile->first_name,
'last_name' => $profile->last_name,
'email' => $mainUser->email,
'mobile_number' => $profile->mobile_number,
'role' => $mainUser->roles()->first()->slug,
'image_avatar' => $mainUser->public_avatar_url,
'card_info' => $cardInfo,
'address' => $address,
'postcode' => $postcode,
];
return response([
'success' => true,
'data' => $userData,
'message' => 'Welcome ' . $profile->first_name . '!'
], 200);
} else {
return response([
'success' => false,
'message' => 'Invalid OTP.',
'data' => null,
], 400);
}
} else {
return response([
'success' => false,
'message' => 'User not found.',
'data' => null,
], 404);
}
} catch (\Exception $e) {
return response([
'success' => false,
'message' => 'Failed to verify OTP.',
'error' => $e->getMessage(),
], 500);
}
}
/**
* Send SMS using Twilio
*/
private function sendSMS($to, $message)
{
$sid = env('TWILIO_SID');
$token = env('TWILIO_AUTH_TOKEN');
$from = env('TWILIO_FROM');
$url = "https://api.twilio.com/2010-04-01/Accounts/{$sid}/Messages.json";
$data = [
'To' => $to,
'From' => $from,
'Body' => $message,
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "{$sid}:{$token}");
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 200 && $httpCode < 300) {
return true;
} else {
throw new \Exception("Twilio SMS failed. HTTP $httpCode. Response: $response");
}
}
/**
* Get therapist info
*/
public function getUserInfo()
{
$user = $this->user;
$data = [
'id' => $user->id,
'api_token' => $user->api_token,
'first_name' => $user->profile->first_name,
'last_name' => $user->profile->last_name,
'email' => $user->email,
'mobile_number' => $user->profile->mobile_number,
'role' => $user->roles()->first()->slug,
'image_avatar' => $user->public_avatar_url,
];
//return booking data
return response([
'success' => true,
'message' => null,
'data' => $data,
], 200);
}
public function updateAvatar(Request $request)
{
try {
$user = $this->user;
if (!$user) {
return response()->json(['success' => false, 'message' => 'User not authenticated'], 401);
}
$validator = Validator::make($request->all(), [
'avatar' => 'required|mimes:jpeg,jpg,png|max:2048',
]);
if ($validator->fails()) {
return response()->json(['success' => false, 'message' => $validator->errors()->first()], 400);
}
$file = $request->file('avatar');
$fileName = time() . '_' . $file->getClientOriginalName();
// Full path where the file will be stored
$destinationPath = public_path('images/avatar/');
// Create directory if it doesn't exist
if (!file_exists($destinationPath)) {
mkdir($destinationPath, 0755, true);
}
// Move the file into the unique folder
$file->move($destinationPath, $fileName);
$userProfile = UserProfile::where('user_id', $user->id)->first();
$userProfile->avatar = 'avatar/' . $fileName;
$userProfile->save();
return response()->json([
'success' => true,
'message' => 'Avatar updated!',
'data' => $userProfile,
], 200);
} catch (\Throwable $th) {
return response()->json(['success' => false, 'message' => $th->getMessage()], 400);
}
}
public function createClient(Request $request)
{
//start transaction
DB::beginTransaction();
$data = [
'name' => $request->first_name . ' ' . $request->last_name,
'email' => $request->email,
'password' => rand(0, 6),
];
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'trial_ends_at' => Carbon::now()->addYears(10),
]);
//create user profile
$profile = UserProfile::create([
'first_name' => $request->first_name,
'last_name' => $request->last_name,
'mobile_number' => $request->mobile_number,
'trial_ends_at' => Carbon::now()->addYears(10),
'user_id' => $user->id,
]);
//get customer role
$role = Role::where('slug', 'customer')->first();
//attach role
if ($role)
$role_status = $user->assignRole($role);
if ($user && $profile) {
//commit transaction
DB::commit();
$userDetail = [
'id' => $user->id,
'api_token' => $user->api_token,
'first_name' => $profile->first_name,
'last_name' => $profile->last_name,
'email' => $user->email,
'mobile_number' => $profile->mobile_number,
'role' => $user->roles()->first()->slug,
'image_avatar' => null,
];
//response
return response([
'success' => true,
'message' => null,
'data' => $userDetail,
], 200);
} else {
//rollback
DB::rollback();
return response([
'success' => true,
'message' => 'something went wrong! Please try again later.',
], 200);
} //end elseif
}
/**
* Get therapist past bookings
*/
public function getPastBookings(Request $request)
{
$thBookings = [];
$user = $this->user;
//get therapist bookings
$bookings = $user->bookings()
->select('*', DB::raw('CONCAT_WS(" ",date,hour) as bookingdate'))
->whereRaw('CONCAT_WS(" ",date,hour) < NOW()')
// ->whereHas('booking',function($query){
// return $query->where('is_active',1);
// })
->orderBy('created_at', 'desc')
->take(15)
->get();
//return empty response
if ($bookings->isEmpty())
return response([
'success' => true,
'message' => 'You have no bookings',
'items_no' => 0,
'items' => [],
], 200);
foreach ($bookings as $bo) {
$info = json_decode($bo->orderInfo, true);
if (isset($info['salon_id'])) {
$thBookings[] = $this->format_salon_booking_list($bo, $info);
} else {
$thBookings[] = $this->format_booking_list($bo, $info);
}
// $thBookings[] = $this->format_booking_list($bo,$info);
} //endforeach
$responseData = [
'success' => true,
'message' => null,
'items_no' => collect($thBookings)->count(),
'items' => $thBookings
];
//return bookings
return response($responseData, 200);
}
// get future bookings
public function getFutureBookings()
{
$thBookings = [];
$user = $this->user;
//get therapist bookings
$bookings = $user->bookings()
->select('*', DB::raw('CONCAT_WS(" ",date,hour) as bookingdate'))
->whereRaw(' TIMESTAMPADD(MINUTE,duration_min,CONCAT_WS(" ",date,hour)) >= TIMESTAMP(DATE_SUB(NOW(),INTERVAL 1 HOUR))')
// ->whereRaw("date >= DATE(NOW()) AND TIME(ADDTIME(TIME(hour),'0 1:0:0')) > TIME(NOW())")
// ->whereRaw('ADDTIME(ADDTIME(hour,STR_TO_DATE(CONCAT(FLOOR(duration_min/60),\':\',MOD(duration_min,60)),"%h:%i")),"0:15") > TIME(NOW())')
->where('is_active', 1)
// ->orderBy('date','asc')
// ->orderBy('hour','asc')
->orderBy('created_at', 'desc')
->get();
//return empty response
if ($bookings->isEmpty())
return response([
'success' => true,
'message' => 'You have no bookings',
'items_no' => 0,
'items' => [],
], 200);
foreach ($bookings as $key => $bo) {
$info = json_decode($bo->orderInfo, true);
if (isset($info['salon_id'])) {
$thBookings[] = $this->format_salon_booking_list($bo, $info);
} else {
// dd($info['salon_id']);
$thBookings[] = $this->format_booking_list($bo, $info);
}
} //endforeach
$responseData = [
'success' => true,
'message' => null,
'items_no' => collect($thBookings)->count(),
'items' => $thBookings
];
//return bookings
return response($responseData, 200);
}
/**
* Format Booking Cell
* @param $bo
*/
protected function format_booking_list($bo, $info)
{
$name = $info['therapist'][0] ?? '';
$avatar = $info['therapistImage'][0] ?? '';
if (isset($info['has_table'])) {
$has_table = $info['has_table'];
} else {
$has_table = false;
}
return [
'id' => $bo->id,
'user_id' => $bo->user_id,
// 'client' => $bo->user->name,
// 'client_avatar' => $bo->user->avatar_url,
'client' => $name,
'client_avatar' => $avatar,
'date' => $bo->date->format('d M Y'),
'from_to_hour' => Carbon::createFromFormat('H:i', $bo->hour)->format('h:i A') . ' - ' . Carbon::createFromFormat('H:i', $bo->hour)->addMinutes($bo->duration_min)->format('h:i A'),
'hour' => $bo->hour,
'duration' => $bo->duration,
'address' => $bo->address . ', ' . ($info['postcode'] ?? ''),
'locationGeo' => json_decode($bo->locationGeo, true),
'massageType' => $bo->massage_type,
'has_table' => $has_table,
'can_be_cancel' => $bo->time_left_in_seconds ? 1 : 0,
'can_be_delayed' => $bo->time_left_in_seconds ? 1 : 0,
'time_left_in_seconds' => $bo->time_left_in_seconds,
'is_salon_booking' => false,
];
}
protected function format_salon_booking_list($bo, $info)
{
// dd($info);
$name = isset($info['therapist']['name']) ? $info['therapist']['name'] : '';
$avatar = $info['therapist']['profile']['image_url'];
return [
'id' => $bo->id,
'user_id' => $bo->user_id,
'salon' => $info['salon'] ? $info['salon'] : '',
'therapist' => $info['therapist'] ? $info['therapist'] : '',
// 'client' => $bo->user->name,
// 'client_avatar' => $bo->user->avatar_url,
'client' => $name,
'client_avatar' => $avatar,
'date' => $bo->date->format('d M Y'),
'from_to_hour' => Carbon::createFromFormat('H:i', $bo->hour)->format('h:i A') . ' - ' . Carbon::createFromFormat('H:i', $bo->hour)->addMinutes($bo->duration_min)->format('h:i A'),
'hour' => $bo->hour,
'duration' => $bo->duration,
'address' => $bo->address,
'locationGeo' => json_decode($bo->locationGeo, true),
'massageType' => $bo->massage_type,
'can_be_cancel' => $bo->time_left_in_seconds ? 1 : 0,
'can_be_delayed' => $bo->time_left_in_seconds ? 1 : 0,
'time_left_in_seconds' => $bo->time_left_in_seconds,
'is_salon_booking' => true,
];
}
/**
* Booking details
* @param BookingOrder $id
*/
public function showBooking($id)
{
//default booking data
$bookingData = [];
$booking = BookingOrder::where('id', $id)->first();
// return $booking;
//return empty object if booking is not found
if (!$booking)
return response([
'success' => false,
'message' => 'The Booking is not found.',
'data' => $bookingData,
], 403);
$info = json_decode($booking->orderInfo, true);
$your_commision = $info['duration_commision']['therapist'] ?? '';
// dd($info);
if (isset($info['has_extension']) && array_key_exists('duration_commision', $info['extension'])) {
if (array_key_exists('commision_th', $info['extension']['duration_commision']))
$your_commision += @(float)$info['extension']['duration_commision']['commision_th'];
else
$your_commision += @(float)$info['extension']['duration_commision']['therapist'];
}
//calculate price net
$price_net = $info['price_net'] ?? '';
if (isset($info['has_extension']))
$price_net += @(float)$info['extension']['price'];
//thread message
$subject = $booking->treadsubject;
$thread = Thread::where('subject', $subject)->first();
$review = SalonReviews::where('booking_id', $booking->id)->first();
$therapists = [];
foreach ($booking->therapists as $th) {
$therapists[] = [
'id' => $th->id,
'name' => $th->name,
'phone' => $th->profile->mobile_number,
'email' => $th->email,
'avatar' => $th->avatar_url,
];
}
if (isset($info['has_table'])) {
if (isset($info['table_value'])) {
$has_table = $info['table_value'];
} else {
$has_table = 0;
}
} else {
$has_table = 0;
}
$bookingData = [
'id' => $booking->id,
'user_id' => $booking->user_id,
'client' => $booking->user->name,
'client_email' => $booking->user->email,
'client_phone' => $booking->user->profile->mobile_number,
'client_avatar' => $booking->user->avatar_url,
'therapists' => $therapists,
'therapist_main' => $therapists[0],
'date' => $booking->date->format('d M Y'),
'from_to_hour' => Carbon::createFromFormat('H:i', $booking->hour)->format('h:i A') . ' - ' . Carbon::createFromFormat('H:i', $booking->hour)->addMinutes($booking->duration_min)->format('h:i A'),
'hour' => $booking->hour,
'duration' => $booking->duration,
'message_name' => $booking->massage_type,
'address' => $booking->address . ', ' . ($info['postcode'] ?? ''),
'locationGeo' => json_decode($booking->locationGeo, true),
'payment_type' => $booking->card_trans_id ? 'Card' : 'Cash',
'paid_value' => number_format(ceil($booking->amount * 20) / 20, 2),
'vat_amount' => number_format(ceil($booking->order->vat_amount * 20) / 20, 2),
'service_price' => number_format(ceil($booking->order->service_price * 20) / 20, 2),
'your_commision' => round($your_commision, 2),
'details_product' => $booking->massage_type . ' ' . $booking->duration,
'details_product_price' => number_format(ceil($price_net * 20) / 20, 2),
'details_massage_table' => $has_table,
'details_travel_supplements' => $info['transport_cost'] ?? '',
'details_voucher_discount' => $info['has_voucher'] ?? 0,
'is_delayed' => isset($info['is_delayed']) ? $info['is_delayed'] : false,
// 'can_be_cancel' => $booking->time_left_in_seconds?1:0,
'can_be_cancel' => 1,
'cen_be_delayed' => $booking->time_left_in_seconds ? 1 : 0,
'thread_id' => $thread ? $thread->id : null,
'time_left_in_seconds' => $booking->time_left_in_seconds,
'can_be_safe_canceled' => (!$booking->status_canceled) ? 0 : 1,
'can_be_safe_canceled_message' => (!$booking->status_canceled) ? trans('users::booking.text_booking_non_refundable') : trans('users::booking.text_booking_refundable'),
'is_extended' => (!empty($info['extension'])) ? 1 : 0,
'can_be_tracked' => $booking->can_be_tracked,
'review' => $review ? $review : null
];
//return booking data
return response([
'success' => true,
'message' => null,
'data' => $bookingData,
], 200);
}
/**
* Booking Salon details
* @param BookingOrder $id
*/
public function showSalonBooking($id)
{
//default booking data
$bookingData = [];
$booking = BookingOrder::where('id', $id)->first();
// dd($booking);
//return empty object if booking is not found
if (!$booking)
return response([
'success' => false,
'message' => 'The Booking is not found.',
'data' => $bookingData,
], 403);
$info = json_decode($booking->orderInfo, true);
// $your_commision = $info['duration_commision']['therapist'];
// if (isset($info['has_extension']) && array_key_exists('duration_commision',$info['extension'])){
// if (array_key_exists('commision_th', $info['extension']['duration_commision']))
// $your_commision += @(float)$info['extension']['duration_commision']['commision_th'];
// else
// $your_commision += @(float)$info['extension']['duration_commision']['therapist'];
// }
//calculate price net
// $price_net = $info['price_net'];
// if (isset($info['has_extension']))
// $price_net += @(float)$info['extension']['price'];
//thread message
$subject = $booking->treadsubject;
$thread = Thread::where('subject', $subject)->first();
$therapists = [];
foreach ($booking->therapists as $th) {
$therapists[] = [
'id' => $th->id,
'name' => $th->name,
'phone' => $th->profile->mobile_number,
'email' => $th->email,
'avatar' => $th->avatar_url,
];
}
$bookingData = [
'id' => $booking->id,
'user_id' => $booking->user_id,
'client' => $booking->user->name,
'client_email' => $booking->user->email,
'client_phone' => $booking->user->profile->mobile_number,
'client_avatar' => $booking->user->avatar_url,
'therapists' => $info['therapist'] ? $info['therapist'] : '',
'salon' => $info['salon'] ? $info['salon'] : '',
// 'therapist_main' => $therapists[0],
'date' => $booking->date->format('d M Y'),
'from_to_hour' => Carbon::createFromFormat('H:i', $booking->hour)->format('h:i A') . ' - ' . Carbon::createFromFormat('H:i', $booking->hour)->addMinutes($booking->duration_min)->format('h:i A'),
'hour' => $booking->hour,
'duration' => $booking->duration,
'address' => $booking->address,
'locationGeo' => json_decode($booking->locationGeo, true),
'payment_type' => $booking->card_trans_id ? 'Card' : 'Cash',
'paid_value' => $booking->amount,
'details_product' => $booking->massage_type . ' ' . $booking->duration,
'details_product_price' => $booking->amount,
'can_be_cancel' => $booking->time_left_in_seconds ? 1 : 0,
'cen_be_delayed' => $booking->time_left_in_seconds ? 1 : 0,
'thread_id' => $thread ? $thread->id : null,
'time_left_in_seconds' => $booking->time_left_in_seconds,
'can_be_safe_canceled' => (!$booking->status_canceled) ? 0 : 1,
'can_be_safe_canceled_message' => (!$booking->status_canceled) ? trans('users::booking.text_booking_non_refundable') : trans('users::booking.text_booking_refundable'),
'can_be_tracked' => $booking->can_be_tracked,
];
//return booking data
return response([
'success' => true,
'message' => null,
'data' => $bookingData,
], 200);
}
/**
* Cancel Booking
* @param $id
*/
public function action_cancel($id)
{
$booking = BookingOrder::find($id);
//return empty object if booking is not found
if (!$booking)
return response([
'success' => false,
'message' => 'The Booking is not found.',
], 403);
//refund booking value
$repo = new BookingRepository();
$refund = $repo->cancel_order($booking);
//add push notification for booking
$notif_message = trans("schedules::booking.mobile_cancel_order", ['number' => $booking->id, 'date' => $booking->date_to_human, 'hour' => $booking->hour_to_human]);
$notif_users = $booking->user_id;
// $boInfo = json_decode($booking->orderInfo, true);
// $boTherapistsIds = $boInfo['therapistIds'];
NotifRepository::add($notif_users, $notif_message, 'booking', 'Booking Cancelled');
//return response
return response([
'success' => true,
'message' => "The booking has been successfully cancelled."
], 200);
}
/**
* Get Massage Type
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
*/
public function get_massage_types(Request $request)
{
$obj = ServiceType::selectRaw('*, IF(name = "optional", 1,0) as first_el')
->orderBy('first_el', 'desc')
->orderBy('order_no', 'asc')
->get();
$results = [];
foreach ($obj as $o) {
$results[] = [
'id' => $o->id,
'name' => $o->name,
'description' => html_entity_decode(strip_tags($o->body)),
'image' => url($o->image_url_file),
];
} //endforeach
$responseData = [
'success' => true,
'message' => null,
'items_no' => collect($results)->count(),
'items' => $results,
];
//return response
return response($responseData, 200);
}
/**
* Get duration massage options
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
*/
public function get_durations($id)
{
$treatment = ServiceType::where('id', $id)->get();
$this->data['treatment'] = $treatment;
foreach ($treatment as $treatment) {
if ($treatment->is_massage == 1) {
$obj = ServiceDuration::whereIn('is_massage_duration', [1])->get();
} else {
$obj = ServiceDuration::whereIn('sub_category_id', [$treatment->id])->get();
}
}
// dd($obj);
// $obj = ServiceDuration::where('is_extra','=',0)
// ->orderBy('is_default','desc')
// ->orderBy('duration','asc')
// ->get();
$results = [];
foreach ($obj as $o) {
$results[] = [
'id' => $o->id,
'name' => $o->name,
'duration' => $o->duration,
'price' => round($o->price, 2),
];
} //endforeach
$responseData = [
'success' => true,
'message' => null,
'items_no' => collect($results)->count(),
'items' => $results,
];
//return response
return response($responseData, 200);
}
/**
* Get list of vouchers
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
*/
public function get_vouchers(Request $request)
{
$user = $this->user;
$vouchers = Voucher::where('is_active', 1)
->where(function ($query) use ($user) {
$query->where(function ($q) use ($user) {
return $q->where('email', $user->email)
->orWhere('user_id', $user->id);
});
// $query->orWhere(function($query){
// return $query->where('only_with_service',0)
// ->where('only_with_user',0)
// ->where('only_with_email',0);
// });
$query->orderBy('isValid', 'desc');
return $query;
})
->get();
$results = [];
foreach ($vouchers as $o) {
$info = $value = '';
if (class_basename($o->parentable()->withTrashed()->first()) == "VoucherOffer") {
$info = trans('users::vouchers.form_remaining_no');
$value = $o->max_no_usage;
}
if (class_basename($o->parentable()->withTrashed()->first()) == "VoucherPackage") {
$info = trans('users::vouchers.form_remaining_value');
$value = "£" . round(number_format($o->value, 2), 2);
}
$results[] = [
'id' => $o->id,
'name' => $o->name,
'body' => $o->body,
'code' => $o->code,
'available_from' => Carbon::createFromFormat('Y-m-d H:i:s', $o->date_start)->format('d M Y'),
'available_until' => Carbon::createFromFormat('Y-m-d H:i:s', $o->date_end)->format('d M Y'),
'info' => $info,
'value' => $value,
'is_valid' => $o->isValid,
'status' => $o->status['message'],
];
} //endforeach
$collection = collect($results);
// $filtered = $collection->sortBy(function ($value, $key) {
// return $value['is_valid'];
// });
$filtered = $collection->sortByDesc('is_valid');
$filtered = $filtered->values()->all();
$responseData = [
'success' => true,
'message' => null,
'items_no' => collect($results)->count(),
'items' => $filtered,
];
//return response
return response($responseData, 200);
}
/**
* Get days for calendar
*
*/
public function get_calendar(Request $request)
{
$startDay = Carbon::now();
$noOfDays = 45;
$results = [];
$message = null;
for ($i = 0; $i <= $noOfDays; $i++) {
$day = Carbon::now()->addDays($i);
$results[] = [
'day' => $day->format('D'),
'dayNo' => $day->format('d'),
'date' => $day->format('Y-m-d'),
'month' => $day->format('M'),
];
} //endfor
//create response data
$responseData = [
'success' => true,
'message' => $message,
'items_no' => collect($results)->count(),
'items' => $results,
];
//return response
return response($responseData, 200);
}
/**
* Get hours
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
*/
public function available_hours(Request $request)
{
ini_set('serialize_precision', -1);
event(new ApiResetBasketEvent());
$results = [];
$message = null;
$booking = Session::get('booking');
$booking['postcode'] = $request->postcode;
$booking['date'] = $request->date;
$booking['duration'] = $request->duration;
if ($request->massage_type_id > 0)
$booking['massage'] = $request->massage_type_id;
Session::put('booking', $booking);
if (!$request->date)
return response([
'success' => false,
'message' => 'The date field is required',
], 403);
// Session::put('booking',$booking);
$repo = new BookingRepository();
$hours = $repo->all_hours($request->date);
//process hours: display only available hours
if (!collect($hours)->isEmpty()) {
$filteredHours = $hours->filter(function ($value, $key) {
return $value['available'] == 1;
});
$results = $filteredHours->toArray();
} //endif hours
$message = null;
if (!collect($results)->count()) {
$message = trans("schedules::booking.mobile_no_available_hours");
}
$priceHour = ServiceDuration::find($request->duration);
$dayKey = Carbon::parse($request->date)->dayOfWeekIso; // 1 = Monday, 7 = Sunday
$dynamicPrices = DynamicPricing::where('day_of_week', $dayKey)
->whereNotNull('time_start')
->whereNotNull('time_end')
->get();
$dynamicPrices = $dynamicPrices->sortByDesc(function ($dp) {
return $dp->time_start;
});
$defaultPrice = DynamicPricing::where('day_of_week', 0)
->select('price')
->first();
$defaultPriceValue = $defaultPrice ? $defaultPrice->price : 0;
foreach ($results as $key => &$slot) {
$slotTime = Carbon::createFromFormat('H:i', $key);
$price = $defaultPriceValue;
foreach ($dynamicPrices as $dp) {
$start = Carbon::createFromFormat('H:i:s', $dp->time_start);
$end = Carbon::createFromFormat('H:i:s', $dp->time_end);
if ($start < $end) {
// Normal case (09:00 → 18:00)
if ($slotTime->between($start, $end, true)) {
$price = (float) $dp->price;
break;
}
} else {
// Overnight case (18:00 → 09:00)
if ($slotTime >= $start || $slotTime <= $end) {
$price = (float) $dp->price;
break;
}
}
}
$slot['price'] = round($price, 2);
}
unset($slot);
// foreach ($results as $key => $hour)
// $results[$key]['price'] = number_format(ceil($priceHour->price * 20) / 20, 2);
$responseData = [
'success' => true,
'message' => $message,
'items_no' => collect($results)->count(),
'items' => array_values($results),
];
//return response
return response($responseData, 200);
}
/**
* Get available therapist
* @param Request $request
*/
public function available_therapists_now(Request $request)
{
\Event::fire(new ApiResetBasketEvent());
Session::forget('booking');
$data = [
'postcode' => $request->postcode,
'address' => $request->address,
'duration' => $request->duration,
'massage' => $request->massage_type_id,
'date' => Carbon::today()->format('Y-m-d'),
'selected_therapists' => [],
];
$results = [];
//set data into session
Session::put('booking', $data);
//create booking repository
$repo = new BookingRepository();
//get therapists
$therapists = $repo->therapistsNow();
// return $therapists;
$is_in_polygon = 0;
foreach ($therapists as $th) {
$services = [];
$breaks = array("<br />", "<br>", "<br/>");
foreach ($th->servicetypes as $srv) {
$body = strip_tags($srv->body);
$services[] = [
'name' => $srv->name,
'body' => $body,
'image' => url($srv->image_url_file),
];
}
$therapist_coverages = UserCoverageArea::where('user_id', $th->id)->get()->toArray();
$therapist_coverages_last = UserCoverageArea::where('user_id', $th->id)->orderBy('id', 'DESC')->first();
if (!empty($therapist_coverages_last->polygon_no)) {
for ($i = 1; $i <= $therapist_coverages_last->polygon_no; $i++) {
$therapist_coverages = UserCoverageArea::select('lng', 'lat')->where('user_id', $th->id)->where('polygon_no', $therapist_coverages_last->polygon_no)->get();
$polygon_lat_lng_Arr = array();
foreach ($therapist_coverages as $coverages) {
$longitude = $coverages->lng;
$latitude = $coverages->lat;
$polygon_lat_lng_Arr[] = $longitude . ' ' . $latitude;
}
$longitude_x = $request->longitude; // x-coordinate of the point to test
$latitude_y = $request->latitude; // y-coordinate of the point to test
$point = $longitude_x . ' ' . $latitude_y;
if (UserCoverageArea::pointInPolygon($point, $polygon_lat_lng_Arr)) {
$is_in_polygon++;
}
}
}
if (!empty($request->longitude) && $request->latitude) {
if ($is_in_polygon > 0) {
$is_in_polygon = 0;
$results[] = [
'id' => $th->id,
'name' => $th->name,
'email' => $th->email,
'phone' => $th->profile->mobile_number,
'hour' => $request->hour,
'avatar' => $th->avatar_url,
'transport' => $th->transport_mode,
'about' => strip_tags($th->profile->about),
'massage_types' => $services,
'therapist_data' => $th,
];
}
} else {
$results[] = [
'id' => $th->id,
'name' => $th->name,
'email' => $th->email,
'phone' => $th->profile->mobile_number,
'date' => $data['date'],
'hour' => $th->firstAvHour,
'avatar' => $th->avatar_url,
'transport' => $th->transport_mode,
'about' => strip_tags($th->profile->about),
'massage_types' => $services,
'therapist_data' => $th,
];
}
} //end foreach
//return data
$responseData = [
'success' => true,
'message' => (collect($results)->count()) ? '' : trans('schedules::booking.mobile_text_no_therapists_now_api'),
'items_no' => collect($results)->count(),
'items' => $results,
];
//return response
return response($responseData, 200);
}
/**
* Get available therapist
* @param Request $request
*/
public function getTherapistsNow(Request $request)
{
event(new ApiResetBasketEvent());
Session::forget('booking');
$data = [
'postcode' => $request->postcode,
'address' => $request->address,
'duration' => $request->duration,
'massage' => $request->massage_type_id,
'date' => Carbon::today()->format('Y-m-d'),
'selected_therapists' => [],
];
$results = [];
//set data into session
Session::put('booking', $data);
//create booking repository
$repo = new BookingRepository();
//get therapists
if ($request->testMode) {
$therapists = $repo->gettherapistsNowRepoTestMode();
} else {
$therapists = $repo->gettherapistsNowRepo();
// $therapists = $repo->therapistsnow();
}
$is_in_polygon = 0;
foreach ($therapists as $th) {
$services = [];
$breaks = array("<br />", "<br>", "<br/>");
foreach ($th->servicetypes as $srv) {
$body = strip_tags($srv->body);
$services[] = [
'name' => $srv->name,
'body' => $body,
'image' => url($srv->image_url_file),
];
}
$therapist_coverages = UserCoverageArea::where('user_id', $th->id)->get()->toArray();
$therapist_coverages_last = UserCoverageArea::where('user_id', $th->id)->orderBy('id', 'DESC')->first();
if (!empty($therapist_coverages_last->polygon_no)) {
for ($i = 1; $i <= $therapist_coverages_last->polygon_no; $i++) {
$therapist_coverages = UserCoverageArea::select('lng', 'lat')->where('user_id', $th->id)->where('polygon_no', $therapist_coverages_last->polygon_no)->get();
$polygon_lat_lng_Arr = array();
foreach ($therapist_coverages as $coverages) {
$longitude = $coverages->lng;
$latitude = $coverages->lat;
$polygon_lat_lng_Arr[] = $longitude . ' ' . $latitude;
}
$longitude_x = $request->longitude; // x-coordinate of the point to test
$latitude_y = $request->latitude; // y-coordinate of the point to test
$point = $longitude_x . ' ' . $latitude_y;
if (UserCoverageArea::pointInPolygon($point, $polygon_lat_lng_Arr)) {
$is_in_polygon++;
}
}
}
if (!empty($request->longitude) && $request->latitude) {
if ($is_in_polygon > 0) {
$is_in_polygon = 0;
$results[] = [
'id' => $th->id,
'name' => $th->name,
'email' => $th->email,
'phone' => $th->profile->mobile_number,
'hour' => $request->hour,
'avatar' => $th->avatar_url,
'transport' => $th->transport_mode,
'about' => strip_tags($th->profile->about),
'massage_types' => $services,
'therapist_data' => $th,
];
}
} else {
$results[] = [
'id' => $th->id,
'name' => $th->name,
'email' => $th->email,
'phone' => $th->profile->mobile_number,
'date' => $data['date'],
'hour' => $th->firstAvHour,
'avatar' => $th->avatar_url,
'transport' => $th->transport_mode,
'about' => strip_tags($th->profile->about),
'massage_types' => $services,
'therapist_data' => $th,
];
}
} //end foreach
// Pagination
$page = (int) $request->get('page', 1);
$perPage = (int) $request->get('per_page', 5);
$total = count($results);
$paginatedResults = array_slice($results, ($page - 1) * $perPage, $perPage);
//return data
$responseData = [
'success' => true,
'message' => ($total) ? '' : trans('schedules::booking.mobile_text_no_therapists_now_api'),
'items_no' => count($paginatedResults),
'items' => $paginatedResults,
'pagination' => [
'total' => $total,
'per_page' => $perPage,
'current_page' => $page,
'last_page' => $total > 0 ? ceil($total / $perPage) : 0,
'from' => $total > 0 ? ($page - 1) * $perPage + 1 : null,
'to' => min($page * $perPage, $total),
],
];
//return response
return response($responseData, 200);
}
/**
* Get available therapist
* @param Request $request
*/
public function available_therapists_later(Request $request)
{
// dd("here");
// \Event::fire(new ApiResetBasketEvent());
$data = [
'postcode' => $request->postcode,
'address' => $request->address,
'duration' => $request->duration,
'massage' => $request->massage_type_id,
'date' => $request->date,
'hour' => $request->hour,
'selected_therapists' => [],
// "therapists_opt" => "1_th",
// "therapists" => 1,
// "type" => "1 therapist",
];
$results = [];
//set data into session
Session::put('booking', $data);
// dd($data);
//create booking repository
// $repo = new BookingRepository();
$repo = new BookingClass();
//get therapists
$therapists = $repo->therapists();
// return $therapists;
// dd($therapists);
$is_in_polygon = 0;
// return $therapists;
// foreach($therapists as $th){
// $services = [];
// $breaks = array("<br />","<br>","<br/>");
// foreach($th->servicetypes as $srv){
// $body = strip_tags($srv->body);
// $services[] = [
// 'name' => $srv->name,
// 'body' => $body,
// 'image' => url($srv->image_url_file),
// ];
// }
// // return $services;
// $therapist_coverages_1 = UserCoverageArea::where('user_id',$th->id)->get();
// $therapist_coverages_last = UserCoverageArea::where('user_id',$th->id)->orderBy('id','DESC')->first();
// // print_r($therapist_coverages_last->polygon_no);
// // if(!empty($therapist_coverages_last->polygon_no)){
// // //for ($i = 1; $i <= $therapist_coverages_last->polygon_no; $i++) {
// // $therapist_coverages = UserCoverageArea::select('lng','lat')->where('user_id',$th->id)->where('polygon_no',$therapist_coverages_last->polygon_no)->get();
// // //print_r($therapist_coverages);
// // // $therapist_coverages_lat_arr = UserCoverageArea::select('lat')->where('user_id',$th->id)->where('polygon_no',$therapist_coverages_last->polygon_no)->lists('lat')->toArray();
// // $polygon_lat_lng_Arr = array();
// // foreach ($therapist_coverages as $coverages) {
// // $longitude = $coverages->lng;
// // $latitude = $coverages->lat;
// // $polygon_lat_lng_Arr[] = $longitude.' '.$latitude;
// // }
// // // echo "<pre>";
// // //print_r($polygon_lat_lng_Arr);
// // // $vertices_x = $therapist_coverages_lng_arr; // x-coordinates of the vertices of the polygon
// // // $vertices_y = $therapist_coverages_lat_arr; // y-coordinates of the vertices of the polygon
// // // $vertices_x = array(37.628134, 37.629867, 37.62324, 37.622424); // x-coordinates of the vertices of the polygon
// // // $vertices_y = array(-77.458334,-77.449021,-77.445416,-77.457819); // y-coordinates of the vertices of the polygon
// // // $vertices_y = array('16.162535162065726 50.127002064138765','16.162524341756303 50.12701783001947');
// // // echo "<pre>";
// // // print_r($vertices_y); die();
// // // $points_polygon = count($vertices_x); // number vertices
// // $longitude_x = $request->longitude; // x-coordinate of the point to test
// // $latitude_y = $request->latitude; // y-coordinate of the point to test
// // //// For testing. This point lies inside the test polygon.
// // // $longitude_x = 37.62850;
// // // $latitude_y = -77.4499;
// // $point = $longitude_x.' '.$latitude_y;
// // //$point = array($point_data);
// // //print_r($point);
// // //echo UserCoverageArea::pointInPolygon($point, $polygon_lat_lng_Arr);
// // if (UserCoverageArea::pointInPolygon($point, $polygon_lat_lng_Arr)){
// // $is_in_polygon++;
// // }
// // //}
// // }
// if(!empty($therapist_coverages_last->polygon_no) && !empty($therapist_coverages_1)){
// foreach($therapist_coverages_1 as $thp_cover){
// //for ($i = 0; $i <= $therapist_coverages_last->polygon_no; $i++) {
// $therapist_coverages = UserCoverageArea::select('lng','lat')->where('user_id',$th->id)->where('polygon_no',$thp_cover->polygon_no)->get();
// $polygon_lat_lng_Arr = array();
// foreach ($therapist_coverages as $coverages) {
// $longitude = $coverages->lng;
// $latitude = $coverages->lat;
// $polygon_lat_lng_Arr[] = $longitude.' '.$latitude;
// }
// $longitude_x = $request->longitude; // x-coordinate of the point to test
// $latitude_y = $request->latitude; // y-coordinate of the point to test
// //echo $point = $longitude_x.' '.$latitude_y;
// //$point = $latitude_y.' '.$longitude_x;
// $point = $longitude_x.' '.$latitude_y;
// //print_r($polygon_lat_lng_Arr);
// if (UserCoverageArea::pointInPolygon($point, $polygon_lat_lng_Arr)){
// $is_in_polygon++;
// }
// //echo $is_in_polygon;
// }
// }
// if(!empty($request->longitude) && $request->latitude){
// if($is_in_polygon > 0){
// $is_in_polygon = 0;
// $results[] = [
// 'id'=>$th->id,
// 'name'=>$th->name,
// 'email' => $th->email,
// 'phone' => $th->profile->mobile_number,
// 'hour' => $request->hour,
// 'avatar' => $th->avatar_url,
// 'transport' => $th->transport_mode,
// 'about' => strip_tags($th->profile->about),
// 'massage_types' => $services,
// ];
// }
// }else{
// $results[] = [
// 'id'=>$th->id,
// 'name'=>$th->name,
// 'email' => $th->email,
// 'phone' => $th->profile->mobile_number,
// 'hour' => $request->hour,
// 'avatar' => $th->avatar_url,
// 'transport' => $th->transport_mode,
// 'about' => strip_tags($th->profile->about),
// 'massage_types' => $services,
// ];
// }
// } //end foreach
//return data
// $responseData = [
// 'success'=>true,
// 'message'=>(collect($results)->count())?'':trans('schedules::booking.text_no_therapists'),
// 'items_no' => collect($results)->count(),
// 'items' => $results,
// ];
$responseData = [
'success' => true,
// 'message'=>(collect($results)->count())?'':trans('schedules::booking.text_no_therapists'),
// 'items_no' => collect($results)->count(),
'items' => $therapists,
];
//return response
return response($responseData, 200);
}
/**
* Get hours
* available_therapist_hours individually
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
*/
public function available_therapist_hours(Request $request)
{
// return "here";
$results = [];
if (!$request->date)
return response([
'success' => false,
'message' => 'The date field is required',
], 403);
Session::forget('booking');
$booking = Session::get('booking');
$booking['postcode'] = $request->postcode;
$booking['date'] = $request->date;
$booking['duration'] = $request->duration;
$booking['address'] = $request->address;
if ($request->massage_type_id > 0)
$booking['massage'] = $request->massage_type_id;
Session::put('booking', $booking);
$repo = new BookingRepository();
$hours = $repo->hours($request->date, [$request->therapist_id]);
// return response($hours,200);
//process hours: display only available hours
if (!collect($hours)->isEmpty()) {
$filteredHours = $hours->filter(function ($value, $key) {
return $value['available'] == 1;
});
$results = $filteredHours->toArray();
} //endif hours
$message = null;
if (!collect($results)->count()) {
$message = "No hours are available for this date.";
}
$bookingDuration = UserServiceDuration::where('services_duration_id', $request->duration)
->where('user_id', $request->therapist_id)
->first();
if (!$bookingDuration) {
$priceHour = ServiceDuration::find($request->duration);
} else {
$priceHour = $bookingDuration;
}
$dayKey = Carbon::parse($request->date)->dayOfWeekIso; // 1 = Monday, 7 = Sunday
$dynamicPrices = DynamicPricing::where('day_of_week', $dayKey)
->whereNotNull('time_start')
->whereNotNull('time_end')
->get();
$dynamicPrices = $dynamicPrices->sortByDesc(function ($dp) {
return $dp->time_start;
});
$defaultPrice = DynamicPricing::where('day_of_week', 0)
->select('price')
->first();
$defaultPriceValue = $defaultPrice ? (float) $defaultPrice->price : 0;
foreach ($results as $key => &$slot) {
// Convert hour (16:30) to Carbon time
$slotTime = Carbon::createFromFormat('H:i', $key);
// Default price
$price = $defaultPriceValue;
foreach ($dynamicPrices as $dp) {
$start = Carbon::createFromFormat('H:i:s', $dp->time_start);
$end = Carbon::createFromFormat('H:i:s', $dp->time_end);
if ($start < $end) {
// Normal case (09:00 → 18:00)
if ($slotTime->between($start, $end, true)) {
$price = (float) $dp->price;
break;
}
} else {
// Overnight case (18:00 → 09:00)
if ($slotTime >= $start || $slotTime <= $end) {
$price = (float) $dp->price;
break;
}
}
}
// Push price into result
$slot['price'] = $price;
}
unset($slot);
// foreach ($results as $key => $hour)
// $results[$key]['price'] = round($priceHour->price, 2);
$responseData = [
'success' => true,
'message' => $message,
'items_no' => collect($results)->count(),
'items' => array_values($results),
];
//return response
return response($responseData, 200);
}
public function get_therapist_last_position($id, Request $request)
{
$input = $request->all();
$booking = BookingOrder::find($id);
$eta = null;
if (!$booking)
return response([
'success' => false,
'message' => 'The Booking is not found.',
], 200);
$infoBooking = json_decode($booking->orderInfo, true);
$location = UserLocation::where('user_id', $request->therapist_id)->first();
// $location = UserLocation::where('user_id',1)->first();
$fromGeo = [
'lat' => $location->lat,
'lng' => $location->lng
];
if (!$fromGeo || !$booking['address'])
$eta = null;
//compose from coordinates
$from = "{$fromGeo['lat']},{$fromGeo['lng']}";
$address = "{$booking['address']}, London UK";
$response = \GoogleMaps::load('geocoding')
->setParam(['address' => $address])
->get('results');
if (empty($response['results'])) {
$to = $address;
} else {
$toGeo = $response['results'][0]['geometry']['location'];
$to = "{$toGeo['lat']},{$toGeo['lng']}";
}
$eta = $this->gTimeTravel($from, $to);
// if (!$eta || $eta > 60)
// return null;
//add booking buffer for reservation process: equivalent with the availability in the basket
// $eta+=15;
$eta += 2;
$eta = Carbon::now()->addMinutes($eta)->format('H:i');
if ($location)
return response([
'success' => true,
'message' => '',
'locationGeo' => [
'latitude' => $location->lat,
'longitude' => $location->lng,
'last_update' => $location->updated_at->format('l jS \\of F h:i:s A'),
'eta' => $eta,
],
], 200);
else
return response([
'success' => false,
'message' => "The therapist's location cannot be determined at this time. Please come back later.",
], 200);
return response([
'success' => true,
'message' => '',
'locationGeo' => [
'latitude' => $request->booking_latitude,
'longitude' => $request->booking_longitude,
'last_update' => Carbon::now()->format('l jS \\of F h:i:s A'),
'eta' => $eta,
],
], 200);
}
protected function gTimeTravel($from, $to, $mode = "DRIVING")
{
$params = [
'origin' => $from,
'destination' => $to,
'mode' => $mode,
'departure_time' => 'now',
];
$direction = \GoogleMaps::load('directions')
->setParam($params)
->get();
$time = json_decode($direction, true);
if ($time['status'] != "OK")
return null;
//get estimated time arrival in minutes
// $buffer = 5;
$buffer = 0;
$eta = ceil($time['routes'][0]['legs'][0]['duration']['value'] / 60) + $buffer;
//return eta
return $eta;
}
/**
* Get google location based on user postcode
* @param $postcode
* @return mixed
*/
protected function getGeoLocation($postcode)
{
$response = \GoogleMaps::load('geocoding')
->setParam(['address' => "{$postcode} London UK"])
->get('results');
$geoLocation = $response['results'][0];
//return geolocation
return $geoLocation;
}
public function deleteUserUsingId($id)
{
$user = User::find($id);
$data['user'] = $user;
// dd($user);
if ($user) {
User::where('id', $id)->delete();
Mail::send('users::admin.email_deleted_user_request', ['user' => $user], function ($m) use ($data) {
$m->from(env('MAIL_FROM'), env('APP_NAME'));
// $m->to('dee@zenlondon.co.uk', $data['user']->name);
$m->to('info@tradze.com', $data['user']->name);
$m->bcc(explode(',', env('MAIL_DELETE_USER_BCC')), env('MAIL_DELETE_USER_BCC_NAME'));
$m->subject(env('APP_NAME') . ' – User Deleted Permanently');
});
return response([
'success' => true,
'message' => 'User Deleted successfully',
], 200);
} else {
return response([
'success' => false,
'message' => 'Account not found! Please contact to the admin'
], 403);
}
return response([
'success' => false,
'message' => 'Something went wrong! Please contact to admin'
], 403);
}
public function countBookings(Request $request)
{
$user = User::find($request->userId);
$upcomingBookings = [];
// Upcoming Bookings
//get therapist bookings
$bookings = $user->bookings()
->select('*', DB::raw('CONCAT_WS(" ",date,hour) as bookingdate'))
->whereRaw(' TIMESTAMPADD(MINUTE,duration_min,CONCAT_WS(" ",date,hour)) >= TIMESTAMP(DATE_SUB(NOW(),INTERVAL 1 HOUR))')
->where('is_active', 1)
->orderBy('created_at', 'desc')
->get();
foreach ($bookings as $key => $bo) {
$info = json_decode($bo->orderInfo, true);
if (isset($info['salon_id'])) {
$upcomingBookings[] = $this->format_salon_booking_list($bo, $info);
} else {
$upcomingBookings[] = $this->format_booking_list($bo, $info);
}
} //endforeach
// Past Bookings
$pastBookings = [];
//get therapist bookings
$bookings = $user->bookings()
->select('*', DB::raw('CONCAT_WS(" ",date,hour) as bookingdate'))
->whereRaw('CONCAT_WS(" ",date,hour) < NOW()')
->orderBy('created_at', 'desc')
->take(15)
->get();
foreach ($bookings as $bo) {
$info = json_decode($bo->orderInfo, true);
if (isset($info['salon_id'])) {
$pastBookings[] = $this->format_salon_booking_list($bo, $info);
} else {
$pastBookings[] = $this->format_booking_list($bo, $info);
}
} //endforeach
$responseData = [
'success' => true,
'message' => "Upcoming and Past Bookings count",
'pastBookings' => collect($pastBookings)->count(),
'upcomingBookings' => collect($upcomingBookings)->count(),
];
//return bookings
return response($responseData, 200);
}
}