migrant-web/app/Http/Controllers/Admin/AdminExtraController.php
2026-06-10 16:14:59 +05:30

575 lines
22 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Illuminate\Support\Facades\DB;
class AdminExtraController extends Controller
{
/**
* Safety & Moderation Dashboard
*/
public function safety()
{
$reports = DB::table('moderation_reports')->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,
'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/Announcements/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.');
}
}