attributes->get('sponsor'); $sponsorId = $sponsor ? $sponsor->id : null; try { // Recent charity/update announcements for the dashboard preview $recentEvents = Announcement::where(function ($q) use ($sponsorId) { $q->where('status', 'approved'); if ($sponsorId) { $q->orWhere('sponsor_id', $sponsorId); } }) ->latest() ->limit(5) ->get() ->map(function ($event) { $content = $event->body; if (strpos($event->body, '{"type":"Charity"') === 0) { $decoded = json_decode($event->body, true); if ($decoded) { $content = $decoded['content'] ?? $event->body; } } return [ 'id' => $event->id, 'title' => $event->title, 'body' => $content, 'type' => strtolower($event->type), 'status' => $event->status ?? 'pending', 'created_at' => $event->created_at->toIso8601String(), 'time_ago' => $event->created_at->diffForHumans(), ]; }); return response()->json([ 'success' => true, 'data' => [ 'sponsor' => [ 'id' => $sponsor->id, 'full_name' => $sponsor->full_name, 'organization_name' => $sponsor->organization_name, 'mobile' => $sponsor->mobile, 'email' => $sponsor->email, 'city' => $sponsor->city, 'nationality' => $sponsor->nationality, 'is_verified' => $sponsor->is_verified, 'status' => $sponsor->status, 'license_file' => $sponsor->license_file ? asset($sponsor->license_file) : null, 'emirates_id_file' => $sponsor->emirates_id_file ? asset($sponsor->emirates_id_file) : null, 'license_expiry' => $sponsor->license_expiry ? $sponsor->license_expiry->toDateString() : null, 'license_number' => $sponsor->license_number, 'license' => $sponsor->license_data, 'validity' => $sponsor->license_expiry ? ($sponsor->license_expiry->isFuture() ? 'Valid' : 'Expired') : 'Pending Review', 'joined_at' => $sponsor->created_at->toIso8601String(), 'emirates_id' => [ 'emirates_id_number' => $sponsor->emirates_id, 'name' => $sponsor->emirates_id_name, 'date_of_birth' => $sponsor->emirates_id_dob, 'issue_date' => $sponsor->emirates_id_issue_date, 'expiry_date' => $sponsor->emirates_id_expiry, 'employer' => $sponsor->emirates_id_employer, 'issue_place' => $sponsor->emirates_id_issue_place, 'occupation' => $sponsor->emirates_id_occupation, 'nationality' => $sponsor->emirates_id_nationality, 'gender' => $sponsor->emirates_id_gender, ], ], 'recent_charity_events' => $recentEvents, 'total_events' => Announcement::where(function ($q) use ($sponsorId) { $q->where('status', 'approved'); if ($sponsorId) { $q->orWhere('sponsor_id', $sponsorId); } })->count(), 'employer_stats' => [ 'total' => \App\Models\User::where('role', 'employer')->count(), 'active' => \App\Models\User::where('role', 'employer')->where('subscription_status', 'active')->count(), ], 'worker_stats' => [ 'total' => \App\Models\Worker::count(), 'active' => \App\Models\Worker::where('status', 'active')->count(), ], ] ], 200); } catch (\Exception $e) { logger()->error('Sponsor Dashboard API Failure: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'An error occurred while loading your dashboard.', 'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error' ], 500); } } /** * GET /api/sponsors/charity-events * * Returns a paginated list of all charity/announcement events. */ public function getCharityEvents(Request $request) { try { $page = (int) $request->input('page', 1); $perPage = (int) $request->input('per_page', 15); $type = $request->input('type'); // optional filter: 'charity', 'update', etc. /** @var Sponsor $sponsor */ $sponsor = $request->attributes->get('sponsor'); $sponsorId = $sponsor ? $sponsor->id : null; $query = Announcement::with(['employer.employerProfile', 'sponsor']) ->where(function ($q) use ($sponsorId) { $q->where('status', 'approved'); if ($sponsorId) { $q->orWhere('sponsor_id', $sponsorId); } }) ->latest(); if ($type) { if (strtolower($type) === 'charity') { $query->whereIn('type', ['charity', 'Charity']); } else { $query->where('type', $type); } } $total = $query->count(); $offset = ($page - 1) * $perPage; $events = $query->skip($offset)->take($perPage)->get() ->map(function ($event) { $postedBy = 'System'; $organization = 'Migrant Support'; if ($event->sponsor_id) { $postedBy = $event->sponsor->full_name; $organization = $event->sponsor->organization_name; } elseif ($event->employer_id) { $postedBy = $event->employer->name; $organization = optional($event->employer)->employerProfile->company_name ?? 'Migrant Support'; } // Decode charity details if they exist in json $charityDetails = null; $content = $event->body; if (strpos($event->body, '{"type":"Charity"') === 0) { $decoded = json_decode($event->body, true); if ($decoded) { $charityDetails = $decoded; $content = $decoded['content'] ?? $event->body; } } return [ 'id' => $event->id, 'title' => $event->title, 'body' => $content, 'type' => strtolower($event->type), 'status' => $event->status ?? 'pending', 'remarks' => $event->remarks, 'posted_by' => $postedBy, 'organization' => $organization, 'created_at' => $event->created_at->toIso8601String(), 'time_ago' => $event->created_at->diffForHumans(), 'charity_details' => $charityDetails, ]; }); return response()->json([ 'success' => true, 'data' => [ 'events' => $events, 'pagination' => [ 'total' => $total, 'per_page' => $perPage, 'current_page' => $page, 'last_page' => max(1, (int) ceil($total / $perPage)), ], ] ], 200); } catch (\Exception $e) { logger()->error('Sponsor Charity Events API Failure: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'An error occurred while fetching charity events.', 'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error' ], 500); } } /** * POST /api/sponsors/charity-events * * Creates/Posts a new charity event for the authenticated sponsor. */ public function postCharityEvent(Request $request) { /** @var Sponsor $sponsor */ $sponsor = $request->attributes->get('sponsor'); if (!$sponsor) { return response()->json([ 'success' => false, 'message' => 'Unauthorized.' ], 401); } $validator = \Illuminate\Support\Facades\Validator::make($request->all(), [ 'title' => 'required|string|max:255', 'body' => 'required|string', 'type' => 'nullable|string|in:charity,info,warning,success', 'event_date' => 'required|string', 'start_time' => 'required|string', 'end_time' => 'required|string', 'provided_items' => 'required|string', 'location_details' => 'required|string', 'location_pin' => 'required|string|url', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation error.', 'errors' => $validator->errors() ], 422); } try { $eventTime = $request->start_time . ' - ' . $request->end_time; $bodyJson = json_encode([ 'type' => 'Charity', 'provided_items' => $request->provided_items, 'event_date' => $request->event_date, 'event_time' => $eventTime, 'location_details' => $request->location_details, 'location_pin' => $request->location_pin, 'content' => $request->body, ]); $event = Announcement::create([ 'title' => $request->title, 'body' => $bodyJson, 'type' => $request->type ?? 'charity', 'sponsor_id' => $sponsor->id, 'status' => 'pending', ]); // Decode charity details for the response representation $charityDetails = null; $content = $event->body; if (strpos($event->body, '{"type":"Charity"') === 0) { $decoded = json_decode($event->body, true); if ($decoded) { $charityDetails = $decoded; $content = $decoded['content'] ?? $event->body; } } return response()->json([ 'success' => true, 'message' => 'Charity event posted successfully.', 'data' => [ 'id' => $event->id, 'title' => $event->title, 'body' => $content, 'type' => strtolower($event->type), 'posted_by' => $sponsor->full_name, 'organization' => $sponsor->organization_name, 'created_at' => $event->created_at->toIso8601String(), 'charity_details' => $charityDetails, ] ], 201); } catch (\Exception $e) { logger()->error('Sponsor Post Charity Event API Failure: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'An error occurred while posting the charity event.', 'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error' ], 500); } } /** * GET /api/sponsors/profile * * Returns the authenticated sponsor's full profile. */ public function getProfile(Request $request) { /** @var Sponsor $sponsor */ $sponsor = $request->attributes->get('sponsor'); return response()->json([ 'success' => true, 'data' => [ 'sponsor' => [ 'id' => $sponsor->id, 'full_name' => $sponsor->full_name, 'organization_name' => $sponsor->organization_name, 'mobile' => $sponsor->mobile, 'email' => $sponsor->email, 'nationality' => $sponsor->nationality, 'city' => $sponsor->city, 'address' => $sponsor->address, 'country_code' => $sponsor->country_code, 'is_verified' => $sponsor->is_verified, 'status' => $sponsor->status, 'license_file' => $sponsor->license_file ? asset($sponsor->license_file) : null, 'emirates_id_file' => $sponsor->emirates_id_file ? asset($sponsor->emirates_id_file) : null, 'license_expiry' => $sponsor->license_expiry ? $sponsor->license_expiry->toDateString() : null, 'license_number' => $sponsor->license_number, 'license' => $sponsor->license_data, 'validity' => $sponsor->license_expiry ? ($sponsor->license_expiry->isFuture() ? 'Valid' : 'Expired') : 'Pending Review', 'joined_at' => $sponsor->created_at->toIso8601String(), 'emirates_id' => [ 'emirates_id_number' => $sponsor->emirates_id, 'name' => $sponsor->emirates_id_name, 'date_of_birth' => $sponsor->emirates_id_dob, 'issue_date' => $sponsor->emirates_id_issue_date, 'expiry_date' => $sponsor->emirates_id_expiry, 'employer' => $sponsor->emirates_id_employer, 'issue_place' => $sponsor->emirates_id_issue_place, 'occupation' => $sponsor->emirates_id_occupation, 'nationality' => $sponsor->emirates_id_nationality, 'gender' => $sponsor->emirates_id_gender, 'card_number' => $sponsor->emirates_id_card_number, 'country' => 'United Arab Emirates', ], ] ] ], 200); } /** * Build the standardized sponsor response array with nested license and emirates_id objects. */ private function buildSponsorResponse(Sponsor $sponsor): array { $licenseData = [ 'license_number' => $sponsor->license_data['license_no'] ?? $sponsor->license_data['license_number'] ?? null, 'organization_name' => $sponsor->organization_name, 'expiry_date' => $sponsor->license_expiry ? $sponsor->license_expiry->toDateString() : ($sponsor->license_data['expiry_date'] ?? null), 'document_type' => $sponsor->license_data['document_type'] ?? null, 'country' => $sponsor->license_data['country'] ?? null, 'authority' => $sponsor->license_data['authority'] ?? null, 'license_no' => $sponsor->license_data['license_no'] ?? null, 'company_name' => $sponsor->license_data['company_name'] ?? null, 'business_name' => $sponsor->license_data['business_name'] ?? null, 'license_category' => $sponsor->license_data['license_category'] ?? null, 'legal_type' => $sponsor->license_data['legal_type'] ?? null, 'issue_date' => $sponsor->license_data['issue_date'] ?? null, 'main_license_no' => $sponsor->license_data['main_license_no'] ?? null, 'register_no' => $sponsor->license_data['register_no'] ?? null, 'dcci_no' => $sponsor->license_data['dcci_no'] ?? null, 'members' => $sponsor->license_data['members'] ?? [], ]; $emiratesIdData = null; if ($sponsor->emirates_id) { $emiratesIdData = [ 'emirates_id_number' => $sponsor->emirates_id, 'name' => $sponsor->emirates_id_name, 'nationality' => $sponsor->emirates_id_nationality, 'date_of_birth' => $sponsor->emirates_id_dob, 'expiry_date' => $sponsor->emirates_id_expiry, 'issue_date' => $sponsor->emirates_id_issue_date, 'employer' => $sponsor->emirates_id_employer, 'issue_place' => $sponsor->emirates_id_issue_place, 'occupation' => $sponsor->emirates_id_occupation, 'card_number' => $sponsor->emirates_id_card_number, 'gender' => $sponsor->emirates_id_gender, 'country' => 'United Arab Emirates', ]; } return [ 'id' => $sponsor->id, 'full_name' => $sponsor->full_name, 'organization_name' => $sponsor->organization_name, 'mobile' => $sponsor->mobile, 'email' => $sponsor->email, 'nationality' => $sponsor->nationality, 'city' => $sponsor->city, 'address' => $sponsor->address, 'country_code' => $sponsor->country_code, 'is_verified' => $sponsor->is_verified, 'status' => $sponsor->status, 'license_file' => $sponsor->license_file ? asset($sponsor->license_file) : null, 'emirates_id_file' => $sponsor->emirates_id_file ? asset($sponsor->emirates_id_file) : null, 'license_expiry' => $sponsor->license_expiry ? $sponsor->license_expiry->toDateString() : null, 'validity' => $sponsor->license_expiry ? ($sponsor->license_expiry->isFuture() ? 'Valid' : 'Expired') : 'Pending Review', 'joined_at' => $sponsor->created_at->toIso8601String(), 'fcm_token' => $sponsor->fcm_token, 'license' => $licenseData, 'emirates_id' => $emiratesIdData, ]; } /** * Change sponsor's password. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse */ public function changePassword(Request $request) { /** @var Sponsor $sponsor */ $sponsor = $request->attributes->get('sponsor'); $validator = \Illuminate\Support\Facades\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 (!\Illuminate\Support\Facades\Hash::check($request->current_password, $sponsor->password)) { return response()->json([ 'success' => false, 'message' => 'Validation error.', 'errors' => [ 'current_password' => ['The current password is incorrect.'] ] ], 422); } try { $hashedPassword = \Illuminate\Support\Facades\Hash::make($request->new_password); // Update sponsor table $sponsor->update([ 'password' => $hashedPassword ]); // Sync with corresponding employer user record if found $matchingEmployer = \App\Models\User::where('email', $sponsor->email) ->where('role', 'employer') ->first(); if ($matchingEmployer) { $matchingEmployer->update([ 'password' => $hashedPassword ]); } return response()->json([ 'success' => true, 'message' => 'Password changed successfully.' ], 200); } catch (\Exception $e) { logger()->error('Mobile Sponsor 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); } } /** * POST /api/sponsors/profile/update * * Updates the authenticated sponsor's profile. */ public function updateProfile(Request $request) { /** @var Sponsor $sponsor */ $sponsor = $request->attributes->get('sponsor'); if (!$sponsor) { return response()->json([ 'success' => false, 'message' => 'Unauthorized.' ], 401); } // Find matching employer user using sponsor's original email $oldEmail = $sponsor->getOriginal('email') ?? $sponsor->email; $matchingUser = \App\Models\User::where('email', $oldEmail) ->where('role', 'employer') ->first(); $matchingUserId = $matchingUser ? $matchingUser->id : 'NULL'; $validator = \Illuminate\Support\Facades\Validator::make($request->all(), [ 'name' => 'required_without:full_name|nullable|string|max:255', 'full_name' => 'required_without:name|nullable|string|max:255', 'email' => 'required|email|max:255|unique:sponsors,email,' . $sponsor->id . '|unique:users,email,' . $matchingUserId, 'mobile' => 'required|string|max:255|unique:sponsors,mobile,' . $sponsor->id, 'organization_name' => 'required|string|max:255', 'city' => 'required|string|max:100', 'address' => 'required|string|max:255', 'nationality' => 'required|string|max:100', 'country_code' => 'required|string|max:10', 'fcm_token' => 'nullable|string|max:255', // Emirates ID — accepts nested object OR flat fields 'emirates_id' => 'nullable|array', 'emirates_id.emirates_id_number' => 'nullable|string|max:255', 'emirates_id.name' => 'nullable|string|max:255', 'emirates_id.nationality' => 'nullable|string|max:255', 'emirates_id.date_of_birth' => 'nullable|string|max:255', 'emirates_id.expiry_date' => 'nullable|string|max:255', 'emirates_id.issue_date' => 'nullable|string|max:255', 'emirates_id.employer' => 'nullable|string|max:255', 'emirates_id.issue_place' => 'nullable|string|max:255', 'emirates_id.occupation' => 'nullable|string|max:255', // Flat emirates ID fields (backward compatibility) 'id_number' => 'nullable|string|max:255', 'card_number' => 'nullable|string|max:255', 'date_of_birth' => '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', // License — accepts nested object OR flat fields 'license' => 'nullable|array', 'license.document_type' => 'nullable|string|max:255', 'license.country' => 'nullable|string|max:255', 'license.authority' => 'nullable|string|max:255', 'license.license_no' => 'nullable|string|max:255', 'license.company_name' => 'nullable|string|max:255', 'license.business_name' => 'nullable|string|max:255', 'license.license_category' => 'nullable|string|max:255', 'license.legal_type' => 'nullable|string|max:255', 'license.main_license_no' => 'nullable|string|max:255', 'license.register_no' => 'nullable|string|max:255', 'license.dcci_no' => 'nullable|string|max:255', 'license.members' => 'nullable|array', // Flat license fields (backward compatibility) 'document_type' => 'nullable|string|max:255', 'country' => 'nullable|string|max:255', 'authority' => 'nullable|string|max:255', 'license_no' => 'nullable|string|max:255', 'company_name' => 'nullable|string|max:255', 'business_name' => 'nullable|string|max:255', 'license_category' => 'nullable|string|max:255', 'legal_type' => 'nullable|string|max:255', 'main_license_no' => 'nullable|string|max:255', 'register_no' => 'nullable|string|max:255', 'dcci_no' => 'nullable|string|max:255', 'members' => 'nullable|array', ]); $validator->after(function ($validator) use ($request) { $nameFromEid = $request->input('emirates_id.name') ?? $request->input('full_name'); if ($request->has('full_name') && !$request->filled('full_name') && !$nameFromEid) { $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 { // Resolve nested emirates_id input into flat variables $eidInput = $request->input('emirates_id'); $eidNumber = null; $eidName = null; $eidDob = null; $eidExpiry = null; $eidIssueDate = null; $eidEmployer = null; $eidIssuePlace = null; $eidOccupation = null; $eidCardNumber = null; $eidGender = null; if (is_array($eidInput)) { $eidNumber = $eidInput['emirates_id_number'] ?? $eidInput['id_number'] ?? null; $eidName = $eidInput['name'] ?? null; $eidDob = $eidInput['date_of_birth'] ?? $eidInput['dob'] ?? null; $eidExpiry = $eidInput['expiry_date'] ?? null; $eidIssueDate = $eidInput['issue_date'] ?? null; $eidEmployer = $eidInput['employer'] ?? null; $eidIssuePlace = $eidInput['issue_place'] ?? $eidInput['issuing_place'] ?? null; $eidOccupation = $eidInput['occupation'] ?? null; $eidCardNumber = $eidInput['card_number'] ?? null; $eidGender = $eidInput['gender'] ?? null; } // Flat field fallbacks (backward compatibility) $eidNumber = $eidNumber ?? $request->input('id_number'); $eidName = $eidName ?? $request->input('full_name'); $eidDob = $eidDob ?? $request->input('date_of_birth'); $eidExpiry = $eidExpiry ?? $request->input('expiry_date'); $eidIssueDate = $eidIssueDate ?? $request->input('issue_date'); $eidEmployer = $eidEmployer ?? $request->input('employer'); $eidIssuePlace = $eidIssuePlace ?? $request->input('issuing_place'); $eidOccupation = $eidOccupation ?? $request->input('occupation'); $eidCardNumber = $eidCardNumber ?? $request->input('card_number'); $eidGender = $eidGender ?? $request->input('gender'); // Normalize dates $eidDob = $this->normaliseDate($eidDob); $eidExpiry = $this->normaliseDate($eidExpiry); $eidIssueDate = $this->normaliseDate($eidIssueDate); $nameToUse = $eidName ?? $request->name ?? $request->full_name; // Validate Emirates ID immutability — once set, it cannot be changed if ($eidNumber) { if ($sponsor->emirates_id && $sponsor->emirates_id !== $eidNumber) { return response()->json([ 'success' => false, 'message' => 'Emirates ID mismatch. You cannot change your registered Emirates ID number.', 'errors' => [ 'id_number' => ['Emirates ID mismatch.'] ] ], 422); } } // Sync with corresponding employer user and profile records if found if ($matchingUser) { $userData = [ 'name' => $nameToUse, 'email' => $request->email, ]; if ($request->has('fcm_token')) { $userData['fcm_token'] = $request->fcm_token; } $matchingUser->update($userData); $profile = $matchingUser->employerProfile; if (!$profile) { $profile = new \App\Models\EmployerProfile(['user_id' => $matchingUser->id]); } $profile->address = $request->address; $profile->phone = $request->mobile; if ($eidNumber) { $profile->emirates_id_number = $eidNumber; } if ($eidCardNumber) { $profile->emirates_id_card_number = $eidCardNumber; } if ($eidName) { $profile->emirates_id_name = $eidName; } if ($eidDob) { $profile->emirates_id_dob = $eidDob; } if ($request->has('nationality')) { $profile->nationality = $request->nationality; } if ($eidGender) { $profile->emirates_id_gender = $eidGender; } if ($eidIssueDate) { $profile->emirates_id_issue_date = $eidIssueDate; } if ($eidExpiry) { $profile->emirates_id_expiry = $eidExpiry; } if ($eidOccupation) { $profile->emirates_id_occupation = $eidOccupation; } if ($eidEmployer) { $profile->emirates_id_employer = $eidEmployer; } if ($eidIssuePlace) { $profile->emirates_id_issue_place = $eidIssuePlace; } $profile->save(); } // Update Sponsor profile $sponsorData = [ 'full_name' => $nameToUse, 'email' => $request->email, 'mobile' => $request->mobile, 'organization_name' => $request->organization_name, 'city' => $request->city, 'address' => $request->address, 'nationality' => $request->nationality, 'country_code' => $request->country_code, ]; if ($request->has('fcm_token')) { $sponsorData['fcm_token'] = $request->fcm_token; } if ($eidNumber) { $sponsorData['emirates_id'] = $eidNumber; } if ($eidCardNumber) { $sponsorData['emirates_id_card_number'] = $eidCardNumber; } if ($eidName) { $sponsorData['emirates_id_name'] = $eidName; } if ($eidDob) { $sponsorData['emirates_id_dob'] = $eidDob; } if ($eidGender) { $sponsorData['emirates_id_gender'] = $eidGender; } if ($eidIssueDate) { $sponsorData['emirates_id_issue_date'] = $eidIssueDate; } if ($eidExpiry) { $sponsorData['emirates_id_expiry'] = $eidExpiry; } if ($eidOccupation) { $sponsorData['emirates_id_occupation'] = $eidOccupation; } if ($eidEmployer) { $sponsorData['emirates_id_employer'] = $eidEmployer; } if ($eidIssuePlace) { $sponsorData['emirates_id_issue_place'] = $eidIssuePlace; } // Parse and merge license data — from nested license object or flat fields $currentLicenseData = is_array($sponsor->license_data) ? $sponsor->license_data : []; $licenseInput = $request->input('license'); $newLicenseData = []; if (is_array($licenseInput)) { foreach ([ 'document_type', 'country', 'authority', 'license_no', 'company_name', 'business_name', 'license_category', 'legal_type', 'issue_date', 'expiry_date', 'main_license_no', 'register_no', 'dcci_no', 'members' ] as $field) { if (isset($licenseInput[$field])) { $newLicenseData[$field] = $licenseInput[$field]; } } } // Flat field fallbacks (backward compatibility) foreach ([ 'document_type', 'country', 'authority', 'license_no', 'company_name', 'business_name', 'license_category', 'legal_type', 'issue_date', 'expiry_date', 'main_license_no', 'register_no', 'dcci_no', 'members' ] as $field) { if (!isset($newLicenseData[$field]) && $request->has($field)) { $newLicenseData[$field] = $request->input($field); } } if (!empty($newLicenseData)) { if (isset($newLicenseData['expiry_date'])) { $newLicenseData['expiry_date'] = $this->normaliseDate($newLicenseData['expiry_date']); $sponsorData['license_expiry'] = $newLicenseData['expiry_date']; } if (isset($newLicenseData['issue_date'])) { $newLicenseData['issue_date'] = $this->normaliseDate($newLicenseData['issue_date']); } $sponsorData['license_data'] = array_merge($currentLicenseData, $newLicenseData); } $sponsor->update($sponsorData); return response()->json([ 'success' => true, 'message' => 'Profile updated successfully.', 'data' => [ 'sponsor' => $this->buildSponsorResponse($sponsor) ] ], 200); } catch (\Exception $e) { logger()->error('Mobile Sponsor Update Profile Failure: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'An error occurred while updating profile.', '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; } } }