get()->map(function ($report) { return [ 'id' => $report->id, 'type' => $report->type, 'reported_user_name' => $report->reported_user_name, 'reported_user_role' => $report->reported_user_role, 'reported_user_avatar' => $report->reported_user_avatar, 'reported_by_name' => $report->reported_by_name, 'reported_by_role' => $report->reported_by_role, 'reported_by_avatar' => $report->reported_by_avatar, 'reason' => $report->reason, 'priority' => $report->priority, 'status' => $report->status, 'description' => $report->description, 'reported_at' => date('M d, Y h:i A', strtotime($report->reported_at)), ]; }); $rules = []; $moderationQueue = []; return Inertia::render('Admin/Safety/Index', [ 'reports' => $reports, 'rules' => $rules, 'moderation_queue' => $moderationQueue ]); } /** * Resolve a safety report */ public function resolveSafetyReport(Request $request, $id) { $request->validate([ 'action' => 'required|string', 'admin_notes' => 'nullable|string' ]); DB::table('moderation_reports') ->where('id', $id) ->update([ 'status' => 'Resolved', 'admin_notes' => $request->admin_notes ?: 'Resolved by administrator', 'updated_at' => now() ]); // Dynamically log event DB::table('audit_logs')->insert([ 'category' => 'admin_action', 'user' => 'SuperAdmin (support@marketplace.com)', 'action' => 'Resolved Safety Report ' . $id . ' with action: ' . strtoupper($request->action) . '. Notes: ' . ($request->admin_notes ?: 'none'), 'ip_address' => $request->ip() ?: '127.0.0.1', 'created_at' => now(), 'updated_at' => now() ]); return back()->with('success', "Safety Report {$id} has been resolved successfully with action: " . strtoupper($request->action)); } /** * Disputes Management Module */ public function disputes() { $tickets = DB::table('disputes')->get()->map(function ($dispute) { // Decode or initialize logs dynamically $logs = []; if ($dispute->chat_logs) { $logs = json_decode($dispute->chat_logs, true) ?: []; } else { $logs = [ ['sender' => $dispute->party_two_name, 'time' => '10:05 AM', 'message' => "Regarding the dispute on {$dispute->type}: we need to resolve this issue about {$dispute->reason} as soon as possible."], ['sender' => $dispute->party_one_name, 'time' => '10:12 AM', 'message' => 'I have already explained my side. Please check the contract agreement details.'], ['sender' => $dispute->party_two_name, 'time' => '10:14 AM', 'message' => 'That is not what we agreed upon. Let the admin mediator review the records.'] ]; // Save it back so it becomes persistent in DB immediately! DB::table('disputes')->where('id', $dispute->id)->update([ 'chat_logs' => json_encode($logs) ]); } return [ 'id' => $dispute->id, 'party_one_name' => $dispute->party_one_name, 'party_one_role' => $dispute->party_one_role, 'party_one_avatar' => $dispute->party_one_avatar, 'party_two_name' => $dispute->party_two_name, 'party_two_role' => $dispute->party_two_role, 'party_two_avatar' => $dispute->party_two_avatar, 'type' => $dispute->type, 'reason' => $dispute->reason, 'priority' => $dispute->priority, 'status' => $dispute->status, 'assigned_to' => $dispute->assigned_to, 'created_at' => date('M d, Y h:i A', strtotime($dispute->created_at)), 'chat_logs' => $logs, 'evidence' => [ ['type' => 'Contract', 'name' => 'hiring_agreement_signed.pdf', 'url' => '#'], ['type' => 'ID Proof', 'name' => 'emirates_id_verification.pdf', 'url' => '#'] ], 'admin_notes' => $dispute->admin_notes ?: 'Awaiting confirmation of contract terms under UAE law.' ]; }); return Inertia::render('Admin/Disputes/Index', [ 'tickets' => $tickets ]); } /** * Resolve a Dispute Ticket */ public function resolveDispute(Request $request, $id) { $request->validate([ 'resolution' => 'required|string', 'action_taken' => 'required|string', ]); $dispute = DB::table('disputes')->where('id', $id)->first(); if (!$dispute) { return back()->withErrors(['error' => 'Dispute not found']); } // Decode existing logs or set default $logs = []; if ($dispute->chat_logs) { $logs = json_decode($dispute->chat_logs, true) ?: []; } else { $logs = [ ['sender' => $dispute->party_two_name, 'time' => '10:05 AM', 'message' => "Regarding the dispute on {$dispute->type}: we need to resolve this issue about {$dispute->reason} as soon as possible."], ['sender' => $dispute->party_one_name, 'time' => '10:12 AM', 'message' => 'I have already explained my side. Please check the contract agreement details.'], ['sender' => $dispute->party_two_name, 'time' => '10:14 AM', 'message' => 'That is not what we agreed upon. Let the admin mediator review the records.'] ]; } // Add resolution details $logs[] = [ 'sender' => 'Mediator (Admin)', 'time' => date('h:i A'), 'message' => '⚠️ RESOLVED & CLOSED CASE: ' . $request->resolution ]; DB::table('disputes') ->where('id', $id) ->update([ 'status' => 'Resolved', 'admin_notes' => $dispute->admin_notes ? ($dispute->admin_notes . "\n• Resolution: " . $request->resolution) : ("• Resolution: " . $request->resolution), 'chat_logs' => json_encode($logs), 'updated_at' => now() ]); // Dynamically log event DB::table('audit_logs')->insert([ 'category' => 'admin_action', 'user' => 'SuperAdmin (support@marketplace.com)', 'action' => 'Resolved Dispute Ticket ' . $id . ' with resolution: ' . $request->resolution . ' (' . strtoupper($request->action_taken) . ')', 'ip_address' => $request->ip() ?: '127.0.0.1', 'created_at' => now(), 'updated_at' => now() ]); return back()->with('success', "Dispute ticket {$id} has been resolved successfully. Resolution: " . strtoupper($request->action_taken)); } /** * Add admin notes to a dispute */ public function addDisputeNote(Request $request, $id) { $request->validate([ 'note' => 'required|string' ]); // Retrieve existing dispute $dispute = DB::table('disputes')->where('id', $id)->first(); if (!$dispute) { return back()->withErrors(['error' => 'Dispute not found']); } // Decode existing logs or set default $logs = []; if ($dispute->chat_logs) { $logs = json_decode($dispute->chat_logs, true) ?: []; } else { $logs = [ ['sender' => $dispute->party_two_name, 'time' => '10:05 AM', 'message' => "Regarding the dispute on {$dispute->type}: we need to resolve this issue about {$dispute->reason} as soon as possible."], ['sender' => $dispute->party_one_name, 'time' => '10:12 AM', 'message' => 'I have already explained my side. Please check the contract agreement details.'], ['sender' => $dispute->party_two_name, 'time' => '10:14 AM', 'message' => 'That is not what we agreed upon. Let the admin mediator review the records.'] ]; } // Append new reply from mediator $logs[] = [ 'sender' => 'Mediator (Admin)', 'time' => date('h:i A'), 'message' => $request->note ]; // Update database DB::table('disputes') ->where('id', $id) ->update([ 'admin_notes' => $dispute->admin_notes ? ($dispute->admin_notes . "\n• " . $request->note) : ("• " . $request->note), 'chat_logs' => json_encode($logs), 'updated_at' => now() ]); // Dynamically log event DB::table('audit_logs')->insert([ 'category' => 'admin_action', 'user' => 'SuperAdmin (support@marketplace.com)', 'action' => 'Mediator posted a reply note to Dispute ticket ' . $id . ': "' . substr($request->note, 0, 100) . '..."', 'ip_address' => $request->ip() ?: '127.0.0.1', 'created_at' => now(), 'updated_at' => now() ]); return back()->with('success', "Mediator reply and logs successfully updated on Dispute ticket {$id}."); } /** * Notifications & Campaign Management */ public function notifications() { $campaigns = DB::table('audit_logs') ->where('category', 'campaign') ->orderBy('created_at', 'desc') ->get() ->map(function ($log) { $data = json_decode($log->action, true) ?: [ 'channel' => 'push', 'recipient_type' => 'All Active Workers', 'title' => 'Broadcast Alert', 'message' => $log->action ]; return [ 'id' => $log->id, 'channel' => $data['channel'] ?? 'push', 'recipient_type' => $data['recipient_type'] ?? 'All Active Workers', 'title' => $data['title'] ?? 'Broadcast Alert', 'message' => $data['message'] ?? '', 'sent_at' => date('Y-m-d H:i', strtotime($log->created_at)), ]; }); return Inertia::render('Admin/Notifications/Index', [ 'campaigns' => $campaigns, ]); } /** * Broadcast notification campaign */ public function broadcastNotification(Request $request) { $request->validate([ 'channel' => 'required|in:push,whatsapp,both', 'recipients' => 'required|string', 'title' => 'required|string|max:100', 'message' => 'required|string' ]); $campaignData = [ 'channel' => $request->channel, 'recipient_type' => $request->recipients, 'title' => $request->title, 'message' => $request->message, ]; DB::table('audit_logs')->insert([ 'category' => 'campaign', 'user' => auth()->user() ? auth()->user()->email : 'Admin', 'action' => json_encode($campaignData), 'ip_address' => $request->ip(), 'created_at' => now(), 'updated_at' => now(), ]); return back()->with('success', "Notification campaign broadcasted successfully to all {$request->recipients} users!"); } /** * Payments, Revenue Reports & Refund Control */ public function refundPayment(Request $request, $id) { $request->validate([ 'refund_reason' => 'required|string', 'amount' => 'required|numeric|min:1' ]); $payment = DB::table('payments')->where('id', $id)->first(); if ($payment) { DB::table('payments')->where('id', $id)->update([ 'status' => 'refunded', 'description' => $payment->description . " (Refunded: " . $request->refund_reason . ")", 'updated_at' => now() ]); DB::table('audit_logs')->insert([ 'category' => 'admin_action', 'user' => auth()->user() ? auth()->user()->email : 'Admin', 'action' => "Refunded payment #{$id} of amount AED {$request->amount}. Reason: {$request->refund_reason}", 'ip_address' => $request->ip() ?: '127.0.0.1', 'created_at' => now(), 'updated_at' => now() ]); } return back()->with('success', "Refund of AED {$request->amount} initiated successfully for transaction {$id}. Refund reason: {$request->refund_reason}"); } /** * Interactive Reports Hub */ public function analytics() { // 1. Worker Stats $workerStats = [ 'total' => DB::table('workers')->count(), 'verified' => DB::table('workers')->where('verified', 1)->count(), 'active' => DB::table('workers')->where('status', 'active')->count(), 'inactive' => DB::table('workers')->where('status', 'inactive')->count(), ]; // 2. Sponsor Stats $sponsorStats = [ 'total' => DB::table('sponsors')->count(), 'verified' => DB::table('sponsors')->where('is_verified', 1)->count(), 'active_subscribers' => DB::table('sponsors')->where('subscription_status', 'active')->count(), 'unpaid' => DB::table('sponsors')->where('payment_status', 'unpaid')->count(), ]; // 3. Dispute & Safety Stats $disputeStats = [ 'total' => DB::table('disputes')->count(), 'open' => DB::table('disputes')->where('status', 'Open')->count(), 'under_review' => DB::table('disputes')->where('status', 'Under Review')->count(), 'resolved' => DB::table('disputes')->where('status', 'Resolved')->count(), ]; $safetyStats = [ 'total' => DB::table('moderation_reports')->count(), 'pending' => DB::table('moderation_reports')->where('status', 'Pending')->count(), 'resolved' => DB::table('moderation_reports')->where('status', 'Resolved')->count(), ]; // 4. Financial Payments & Ledger $payments = DB::table('payments') ->leftJoin('users', 'payments.user_id', '=', 'users.id') ->select('payments.*', 'users.name as user_name', 'users.email as user_email') ->orderBy('payments.created_at', 'desc') ->get() ->map(function($payment) { return [ 'id' => $payment->id, 'user_name' => $payment->user_name ?: 'System Guest / Sponsor', 'user_email' => $payment->user_email ?: 'no-email@marketplace.com', 'amount' => (float)$payment->amount, 'currency' => $payment->currency, 'description' => $payment->description, 'status' => $payment->status, 'created_at' => date('M d, Y h:i A', strtotime($payment->created_at)), ]; }); $totalRevenue = DB::table('payments')->where('status', 'success')->sum('amount'); return Inertia::render('Admin/Analytics/Index', [ 'worker_stats' => $workerStats, 'sponsor_stats' => $sponsorStats, 'dispute_stats' => $disputeStats, 'safety_stats' => $safetyStats, 'payments' => $payments, 'total_revenue' => (float)$totalRevenue, ]); } /** * Global Database Searchable Audit Logs */ public function auditLogs(Request $request) { $searchTerm = $request->input('search', ''); $category = $request->input('category', 'all'); $query = DB::table('audit_logs'); if ($category !== 'all') { $query->where('category', $category); } if (!empty($searchTerm)) { $query->where(function($q) use ($searchTerm) { $q->where('user', 'like', '%' . $searchTerm . '%') ->orWhere('action', 'like', '%' . $searchTerm . '%') ->orWhere('id', 'like', '%' . $searchTerm . '%'); }); } $logs = $query->orderBy('created_at', 'desc') ->orderBy('id', 'desc') ->get() ->map(function ($log) { return [ 'id' => 'LOG-' . str_pad($log->id, 5, '0', STR_PAD_LEFT), 'category' => $log->category, 'user' => $log->user, 'action' => $log->action, 'ip_address' => $log->ip_address ?: 'Unknown', 'timestamp' => date('Y-m-d H:i', strtotime($log->created_at)), ]; }); return Inertia::render('Admin/AuditLogs/Index', [ 'logs' => $logs, 'search' => $searchTerm, 'category' => $category, ]); } /** * Charity Events & Drives management */ public function announcements() { $announcements = \App\Models\Announcement::with(['employer.employerProfile', 'sponsor'])->latest()->get()->map(function ($ann) { $postedBy = 'System'; $organization = 'Migrant Support'; if ($ann->sponsor_id) { $postedBy = $ann->sponsor->full_name; $organization = $ann->sponsor->organization_name; } elseif ($ann->employer_id) { $postedBy = $ann->employer->name; $organization = optional($ann->employer->employerProfile)->company_name ?? 'Employer'; } // Decode charity details if they exist in json $charityDetails = null; $content = $ann->body; if (strpos($ann->body, '{"type":"Charity"') === 0) { $decoded = json_decode($ann->body, true); if ($decoded) { $charityDetails = $decoded; $content = $decoded['content'] ?? $ann->body; } } else { // Fallback structured details if plain text existed previously $charityDetails = [ 'type' => 'Charity', 'provided_items' => 'Free Medical Checks & Food Supplies', 'event_date' => $ann->created_at->addDays(2)->format('Y-m-d'), 'event_time' => '9:00 AM - 3:00 PM', 'location_details' => 'Al Quoz Community Center, Dubai', 'location_pin' => 'https://maps.google.com', 'content' => $ann->body, ]; } return [ 'id' => $ann->id, 'title' => $ann->title, 'content' => $content, 'type' => $ann->type ?? 'Charity', 'posted_by' => $postedBy, 'organization' => $organization, 'posted_by_email' => $ann->sponsor ? $ann->sponsor->email : ($ann->employer ? $ann->employer->email : null), 'status' => $ann->status ?? 'pending', 'remarks' => $ann->remarks, 'created_at' => $ann->created_at ? $ann->created_at->format('M d, Y h:i A') : 'Just now', 'charityDetails' => $charityDetails, ]; }); return Inertia::render('Admin/Events/Index', [ 'initialAnnouncements' => $announcements, ]); } /** * Store admin charity event */ public function storeAnnouncement(Request $request) { $request->validate([ 'title' => 'required|string|max:255', 'content' => 'required|string', 'provided_items' => 'required|string', 'event_date' => 'required|string', 'event_time' => 'required|string', 'location_details' => 'required|string', 'location_pin' => 'required|string|url', ]); $body = json_encode([ 'type' => 'Charity', 'provided_items' => $request->provided_items, 'event_date' => $request->event_date, 'event_time' => $request->event_time, 'location_details' => $request->location_details, 'location_pin' => $request->location_pin, 'content' => $request->content, ]); \App\Models\Announcement::create([ 'title' => $request->title, 'body' => $body, 'type' => 'Charity', 'status' => 'approved', ]); return back()->with('success', 'Charity Event created successfully.'); } /** * Approve charity event */ public function approveAnnouncement(Request $request, $id) { $ann = \App\Models\Announcement::findOrFail($id); $ann->update(['status' => 'approved']); return back()->with('success', 'Charity Event approved successfully.'); } /** * Reject charity event */ public function rejectAnnouncement(Request $request, $id) { $request->validate([ 'remarks' => 'required|string|max:1000', ]); $ann = \App\Models\Announcement::findOrFail($id); $ann->update([ 'status' => 'rejected', 'remarks' => $request->remarks, ]); return back()->with('success', 'Charity Event rejected successfully.'); } /** * Delete charity event */ public function deleteAnnouncement(Request $request, $id) { $ann = \App\Models\Announcement::findOrFail($id); $ann->delete(); return back()->with('success', 'Charity Event deleted successfully.'); } }