attributes->get('employer'); try { $employer->load('employerReviews.worker'); $profile = $employer->employerProfile; if (!$profile) { $profile = EmployerProfile::create([ 'user_id' => $employer->id, 'phone' => '+971 50 123 4567', 'address' => 'Dubai, UAE', 'verification_status' => 'approved', ]); } $employerProfile = [ 'name' => $employer->name, 'email' => $employer->email, 'phone' => $profile->phone, 'address' => $profile->address, 'country' => $profile->country, 'language' => $profile->language ?? 'English', 'notifications' => (bool)($profile->notifications ?? true), 'verification_status' => $profile->verification_status ?? 'approved', 'id_number' => $profile->emirates_id_number, 'card_number' => $profile->emirates_id_card_number, 'full_name' => $profile->emirates_id_name, 'gender' => $profile->emirates_id_gender, 'rating' => $employer->rating, 'reviews_count' => $employer->reviews_count, 'reviews' => $employer->employerReviews, 'emirates_id' => [ 'emirates_id_number' => $profile->emirates_id_number, 'name' => $profile->emirates_id_name, 'date_of_birth' => $profile->emirates_id_dob, 'issue_date' => $profile->emirates_id_issue_date, 'expiry_date' => $profile->emirates_id_expiry, 'employer' => $profile->emirates_id_employer, 'issue_place' => $profile->emirates_id_issue_place, 'occupation' => $profile->emirates_id_occupation, 'nationality' => $profile->nationality, 'gender' => $profile->emirates_id_gender, 'card_number' => $profile->emirates_id_card_number, 'country' => 'United Arab Emirates', ], ]; return response()->json([ 'success' => true, 'data' => [ 'profile' => $employerProfile ] ], 200); } catch (\Exception $e) { logger()->error('Mobile Employer Get Profile Failure: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'An error occurred while fetching the profile.', 'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error' ], 500); } } /** * Update the authenticated employer's profile details. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse */ public function updateProfile(Request $request) { /** @var User $employer */ $employer = $request->attributes->get('employer'); $sponsor = \App\Models\Sponsor::where('email', $employer->email)->first(); $validator = Validator::make($request->all(), [ 'name' => 'required|string|max:255', 'email' => [ 'required', 'string', 'email', 'max:255', 'unique:users,email,' . $employer->id, $sponsor ? 'unique:sponsors,email,' . $sponsor->id : 'unique:sponsors,email', ], 'phone' => [ 'required', 'string', 'max:255', 'unique:employer_profiles,phone,' . ($employer->employerProfile ? $employer->employerProfile->id : 'NULL'), $sponsor ? 'unique:sponsors,mobile,' . $sponsor->id : 'unique:sponsors,mobile', ], 'address' => 'required|string|max:255', 'notifications' => 'nullable|boolean', 'fcm_token' => 'nullable|string|max:255', // Emirates ID optional fields 'id_number' => 'nullable|string|max:255', 'card_number' => 'nullable|string|max:255', 'full_name' => 'nullable|string|max:255', 'date_of_birth' => 'nullable|string|max:255', 'nationality' => 'nullable|string|max:255', 'gender' => 'nullable|string|max:255', 'issue_date' => 'nullable|string|max:255', 'expiry_date' => 'nullable|string|max:255', 'occupation' => 'nullable|string|max:255', 'employer' => 'nullable|string|max:255', 'issuing_place' => 'nullable|string|max:255', ]); $validator->after(function ($validator) use ($request) { if ($request->has('full_name') && !$request->filled('full_name')) { $validator->errors()->add('full_name', 'Failed to extract name from the Emirates ID. Please upload a clearer document.'); } }); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation error.', 'errors' => $validator->errors() ], 422); } try { if ($request->filled('full_name')) { $request->merge(['name' => $request->full_name]); } // Validate Emirates ID immutability — once set, it cannot be changed if ($request->filled('id_number')) { $existingProfile = $employer->employerProfile; if ($existingProfile && $existingProfile->emirates_id_number && $existingProfile->emirates_id_number !== $request->id_number) { return response()->json([ 'success' => false, 'message' => 'Emirates ID mismatch. You cannot change your registered Emirates ID number.', 'errors' => [ 'id_number' => ['Emirates ID mismatch.'] ] ], 422); } } // Find old email before update to locate the correct sponsor record $oldEmail = $employer->getOriginal('email') ?? $employer->email; // Update user table $userData = [ 'name' => $request->name, 'email' => $request->email, ]; if ($request->has('fcm_token')) { $userData['fcm_token'] = $request->fcm_token; } $employer->update($userData); // Sync with corresponding sponsor record if found $matchingSponsor = \App\Models\Sponsor::where('email', $oldEmail)->first(); if ($matchingSponsor) { $sponsorData = [ 'full_name' => $request->name, 'email' => $request->email, 'mobile' => $request->phone, 'address' => $request->address, ]; if ($request->has('id_number')) { $sponsorData['emirates_id'] = $request->id_number; } if ($request->has('card_number')) { $sponsorData['emirates_id_card_number'] = $request->card_number; } if ($request->has('full_name')) { $sponsorData['emirates_id_name'] = $request->full_name; } if ($request->has('date_of_birth')) { $sponsorData['emirates_id_dob'] = $this->normaliseDate($request->date_of_birth); } if ($request->has('nationality')) { $sponsorData['nationality'] = $request->nationality; } if ($request->has('gender')) { $sponsorData['emirates_id_gender'] = $request->gender; } if ($request->has('issue_date')) { $sponsorData['emirates_id_issue_date'] = $this->normaliseDate($request->issue_date); } if ($request->has('expiry_date')) { $sponsorData['emirates_id_expiry'] = $this->normaliseDate($request->expiry_date); } if ($request->has('occupation')) { $sponsorData['emirates_id_occupation'] = $request->occupation; } if ($request->has('employer')) { $sponsorData['emirates_id_employer'] = $request->employer; } if ($request->has('issuing_place')) { $sponsorData['emirates_id_issue_place'] = $request->issuing_place; } $matchingSponsor->update($sponsorData); } // Update employer_profiles table $profile = $employer->employerProfile; if (!$profile) { $profile = new EmployerProfile(['user_id' => $employer->id]); } $profile->address = $request->address; $profile->phone = $request->phone; $profile->notifications = $request->has('notifications') ? $request->notifications : ($profile->notifications ?? true); if ($request->has('id_number')) { $profile->emirates_id_number = $request->id_number; } if ($request->has('card_number')) { $profile->emirates_id_card_number = $request->card_number; } if ($request->has('full_name')) { $profile->emirates_id_name = $request->full_name; } if ($request->has('date_of_birth')) { $profile->emirates_id_dob = $this->normaliseDate($request->date_of_birth); } if ($request->has('nationality')) { $profile->nationality = $request->nationality; } if ($request->has('gender')) { $profile->emirates_id_gender = $request->gender; } if ($request->has('issue_date')) { $profile->emirates_id_issue_date = $this->normaliseDate($request->issue_date); } if ($request->has('expiry_date')) { $profile->emirates_id_expiry = $this->normaliseDate($request->expiry_date); } if ($request->has('occupation')) { $profile->emirates_id_occupation = $request->occupation; } if ($request->has('employer')) { $profile->emirates_id_employer = $request->employer; } if ($request->has('issuing_place')) { $profile->emirates_id_issue_place = $request->issuing_place; } $profile->save(); $employer->load('employerReviews.worker'); $employerProfile = [ 'name' => $employer->name, 'email' => $employer->email, 'phone' => $profile->phone, 'address' => $profile->address, 'country' => $profile->country, 'language' => $profile->language, 'notifications' => (bool)$profile->notifications, 'verification_status' => $profile->verification_status ?? 'approved', 'id_number' => $profile->emirates_id_number, 'card_number' => $profile->emirates_id_card_number, 'full_name' => $profile->emirates_id_name, 'gender' => $profile->emirates_id_gender, 'rating' => $employer->rating, 'reviews_count' => $employer->reviews_count, 'reviews' => $employer->employerReviews, 'emirates_id' => [ 'emirates_id_number' => $profile->emirates_id_number, 'name' => $profile->emirates_id_name, 'date_of_birth' => $profile->emirates_id_dob, 'issue_date' => $profile->emirates_id_issue_date, 'expiry_date' => $profile->emirates_id_expiry, 'employer' => $profile->emirates_id_employer, 'issue_place' => $profile->emirates_id_issue_place, 'occupation' => $profile->emirates_id_occupation, 'nationality' => $profile->nationality, 'gender' => $profile->emirates_id_gender, 'card_number' => $profile->emirates_id_card_number, 'country' => 'United Arab Emirates', ], ]; return response()->json([ 'success' => true, 'message' => 'Profile updated successfully.', 'data' => [ 'profile' => $employerProfile ] ], 200); } catch (\Exception $e) { logger()->error('Mobile Employer Update Profile Failure: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'An error occurred while updating the profile.', 'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error' ], 500); } } /** * Get dashboard analytics and summary for employer. */ public function getDashboard(Request $request) { /** @var User $employer */ $employer = $request->attributes->get('employer'); try { $contactedWorkersCount = \App\Models\Conversation::where('employer_id', $employer->id)->count(); $totalHiredWorkers = \App\Models\JobOffer::where('employer_id', $employer->id)->where('status', 'accepted')->count() + \App\Models\JobApplication::whereHas('jobPost', function($q) use ($employer) { $q->where('employer_id', $employer->id); })->where('status', 'hired')->count(); $savedCandidates = \App\Models\Shortlist::where('employer_id', $employer->id)->count(); $totalWorkers = \App\Models\Worker::with('documents') ->where('status', '!=', 'Hired') ->where('status', '!=', 'hidden') ->get() ->filter(function ($w) { return !str_contains(strtolower($w->passport_status), 'pending'); }) ->count(); // Resolve plan $sub = \Illuminate\Support\Facades\DB::table('subscriptions') ->where('user_id', $employer->id) ->where('status', 'active') ->latest('id') ->first(); $planId = $sub ? $sub->plan_id : 'premium'; // Map raw subscription plan_id to database plan ID string $planMap = [ '1' => 'basic', '2' => 'premium', '3' => 'vip', 1 => 'basic', 2 => 'premium', 3 => 'vip', 'basic' => 'basic', 'premium' => 'premium', 'enterprise' => 'vip', 'vip' => 'vip', ]; $dbPlanId = $planMap[$planId] ?? 'premium'; $associatedPlan = \App\Models\Plan::find($dbPlanId); $planIdNumberMap = [ 'basic' => 1, 'premium' => 2, 'vip' => 3, ]; $currentPlan = [ 'plan_id' => $associatedPlan ? ($planIdNumberMap[$associatedPlan->id] ?? 2) : 2, 'name' => $associatedPlan ? $associatedPlan->name : (ucfirst($planId) . ' Pass'), 'status' => $sub ? $sub->status : 'active', 'starts_at' => $sub ? date('Y-m-d', strtotime($sub->starts_at)) : now()->subDays(6)->format('Y-m-d'), 'expires_at' => $sub ? date('Y-m-d', strtotime($sub->expires_at)) : now()->addDays(24)->format('Y-m-d'), 'max_contact_people' => $associatedPlan ? $associatedPlan->max_contact_people : null, 'unlimited_contacts' => $associatedPlan ? (bool)$associatedPlan->unlimited_contacts : false, 'enable_job_apply' => $associatedPlan ? (bool)$associatedPlan->enable_job_apply : false, 'features' => $associatedPlan ? $associatedPlan->features : [], 'price' => $associatedPlan ? (float)$associatedPlan->price : 0.0, 'duration' => $associatedPlan ? $associatedPlan->duration : 'Monthly', ]; // Recent Announcements / Events $dbAnnouncements = \App\Models\Announcement::where('status', 'approved')->latest()->limit(2)->get(); $recentAnnouncements = $dbAnnouncements->map(function ($ann) { $body = $ann->body; $eventDate = null; $eventTime = null; $locationDetails = null; $locationPin = null; $providedItems = null; if (strpos($ann->body, '{"type":"Charity"') === 0) { $decoded = json_decode($ann->body, true); if ($decoded) { $body = $decoded['content'] ?? $ann->body; $eventDate = $decoded['event_date'] ?? null; $eventTime = $decoded['event_time'] ?? null; $locationDetails = $decoded['location_details'] ?? null; $locationPin = $decoded['location_pin'] ?? null; $providedItems = $decoded['provided_items'] ?? null; } } else { $body = $ann->body; $eventDate = $ann->created_at->addDays(2)->format('Y-m-d'); $eventTime = '9:00 AM - 3:00 PM'; $locationDetails = 'Al Quoz Community Center, Dubai'; $locationPin = 'https://maps.google.com'; $providedItems = 'Free Medical Checks & Food Supplies'; } return [ 'id' => $ann->id, 'title' => $ann->title, 'body' => $body, 'is_charity' => true, 'event_date' => $eventDate, 'event_time' => $eventTime, 'location_details' => $locationDetails, 'location_pin' => $locationPin, 'provided_items' => $providedItems, 'created_at' => $ann->created_at->toISOString(), 'time_ago' => $ann->created_at->diffForHumans(), ]; }); return response()->json([ 'success' => true, 'data' => [ 'stats' => [ 'contacted_workers_count' => $contactedWorkersCount, 'total_hired_workers' => $totalHiredWorkers, 'saved_candidates' => $savedCandidates, 'total_workers' => $totalWorkers, ], 'current_plan' => $currentPlan, 'recent_announcements' => $recentAnnouncements, 'recent_events' => $recentAnnouncements, // alias for ease ] ], 200); } catch (\Exception $e) { logger()->error('Mobile Employer Get Dashboard Failure: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'An error occurred while fetching the dashboard statistics.', 'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error' ], 500); } } /** * Change employer's password. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse */ public function changePassword(Request $request) { /** @var User $employer */ $employer = $request->attributes->get('employer'); $validator = Validator::make($request->all(), [ 'current_password' => 'required|string', 'new_password' => 'required|string|min:8|confirmed', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation error.', 'errors' => $validator->errors() ], 422); } if (!Hash::check($request->current_password, $employer->password)) { return response()->json([ 'success' => false, 'message' => 'Validation error.', 'errors' => [ 'current_password' => ['The current password is incorrect.'] ] ], 422); } try { $hashedPassword = Hash::make($request->new_password); // Update user table $employer->update([ 'password' => $hashedPassword ]); // Sync with corresponding sponsor record if found $matchingSponsor = \App\Models\Sponsor::where('email', $employer->email)->first(); if ($matchingSponsor) { $matchingSponsor->update([ 'password' => $hashedPassword ]); } return response()->json([ 'success' => true, 'message' => 'Password changed successfully.' ], 200); } catch (\Exception $e) { logger()->error('Mobile Employer Change Password Failure: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'An error occurred while changing password.', 'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error' ], 500); } } /** * Normalise date format to Y-m-d. * E.g. "06/06/2025" -> "2025-06-06" */ private function normaliseDate(?string $raw): ?string { if (empty($raw)) { return null; } $raw = trim($raw); if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $raw)) { return $raw; } try { if (preg_match('/^(\d{1,2})[\/\- ]([A-Za-z0-9]+)[\/\- ](\d{4})$/', $raw, $m)) { $day = str_pad($m[1], 2, '0', STR_PAD_LEFT); $month = $m[2]; $year = $m[3]; if (is_numeric($month)) { $month = str_pad($month, 2, '0', STR_PAD_LEFT); return "{$year}-{$month}-{$day}"; } $ts = strtotime("{$day} {$month} {$year}"); return $ts ? date('Y-m-d', $ts) : $raw; } $dt = new \DateTime($raw); return $dt->format('Y-m-d'); } catch (\Exception $e) { return $raw; } } }