feature(manage-reported-deals): admin can approve or reject reports and remove content
This commit is contained in:
parent
c087126080
commit
6ede0d4548
74
app/Http/Controllers/Admin/ReportController.php
Normal file
74
app/Http/Controllers/Admin/ReportController.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Enums\ReportStatus;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Report;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ReportController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$reports = Report::query()
|
||||
->with(['user:id,name', 'deals:id,title'])
|
||||
->tap(fn ($q) => (new Report)->orderByStatus($q,
|
||||
[ReportStatus::Pending, ReportStatus::Rejected, ReportStatus::Resolved]))
|
||||
->get();
|
||||
|
||||
return view('dashboards.admin.reports.index')
|
||||
->with('reports', $reports);
|
||||
}
|
||||
|
||||
public function resolve(Report $report)
|
||||
{
|
||||
try {
|
||||
$report->status = ReportStatus::Resolved;
|
||||
$report->save();
|
||||
|
||||
return back()->with('success', 'Report resolved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
\Log::error('Error resolving report', [$report->id, $e->getMessage(), $e->getTraceAsString()]);
|
||||
|
||||
return back()->with('error', 'Something went wrong.');
|
||||
}
|
||||
}
|
||||
|
||||
public function reject(Report $report)
|
||||
{
|
||||
try {
|
||||
$report->status = ReportStatus::Rejected;
|
||||
$report->save();
|
||||
|
||||
return back()->with('success', 'Report Rejected successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
\Log::error('Error rejecting report', [$report->id, $e->getMessage(), $e->getTraceAsString()]);
|
||||
|
||||
return back()->with('error', 'Something went wrong.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the attached deal inactive and mark the report resolved.
|
||||
*/
|
||||
public function removeContent(Report $report)
|
||||
{
|
||||
try {
|
||||
DB::transaction(function () use ($report) {
|
||||
$deal = $report->deals()->first();
|
||||
$deal->active = false;
|
||||
$report->status = ReportStatus::Resolved;
|
||||
|
||||
$deal->save();
|
||||
$report->save();
|
||||
});
|
||||
|
||||
return back()->with('error', 'Deal has been deactivated.');
|
||||
} catch (\Throwable $e) {
|
||||
\Log::error('Error removing content of report', [$report->id, $e->getMessage(), $e->getTraceAsString()]);
|
||||
|
||||
return back()->with('error', 'Something went wrong.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,8 @@
|
||||
|
||||
use App\Enums\ReportStatus;
|
||||
use App\Enums\ReportType;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
@ -27,4 +29,15 @@ protected function casts(): array
|
||||
'status' => ReportStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
#[Scope]
|
||||
public function orderByStatus(Builder $query, array $statusOrder): Builder
|
||||
{
|
||||
$values = array_map(fn ($enum) => $enum->value, $statusOrder);
|
||||
|
||||
// Create placeholders for each value: FIELD(status, ?, ?, ?)
|
||||
$placeholders = implode(',', array_fill(0, count($values), '?'));
|
||||
|
||||
return $query->orderByRaw("FIELD(status, $placeholders)", $values);
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,8 +33,8 @@ class="flex flex-col p-4 pt-6 justify-between font-medium h-full w-full overflow
|
||||
<p class="sidebar-text transition-opacity duration-300 ease-in-out">Manage Brokers</p>
|
||||
</x-dashboard.broker.sidebar.item>
|
||||
|
||||
<x-dashboard.broker.sidebar.item :link="route('broker.deals.index')">
|
||||
<x-heroicon-o-document-text class="w-5 min-w-5"/>
|
||||
<x-dashboard.broker.sidebar.item :link="route('admin.reports.index')">
|
||||
<x-heroicon-o-exclamation-triangle class="w-5 min-w-5"/>
|
||||
<p class="sidebar-text transition-opacity duration-300 ease-in-out">Manage Reports</p>
|
||||
</x-dashboard.broker.sidebar.item>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@props(['deal_id' => '', 'deal_title' => '', 'isLiked' => false, 'isFavourite' => false, 'isInteractive' => true])
|
||||
<div class="action-toolbar" data-deal-title="{{$deal_title}}">
|
||||
<div class="action-toolbar flex items-center" data-deal-title="{{$deal_title}}">
|
||||
<x-dashboard.user.action-toolbar.like-button :is-liked="$isLiked" :is-interactive="$isInteractive" />
|
||||
<x-dashboard.user.action-toolbar.favorite-button :is-favourite="$isFavourite" :is-interactive="$isInteractive" />
|
||||
<x-dashboard.user.action-toolbar.report-button :id="$deal_id" :title="$deal_title" />
|
||||
|
||||
@ -16,8 +16,8 @@ class="deal-image h-full w-full object-cover rounded-lg border-none">
|
||||
<x-ui.button-sm data-is-loading="true" class="w-fit deal-category data-[is-loading=true]:h-6 data-[is-loading=true]:w-15" variant="neutral"/>
|
||||
<p data-is-loading="true" class="deal-title font-bold text-lg data-[is-loading=true]:h-3 "></p>
|
||||
<p data-is-loading="true" class="deal-description text-sm text-accent-600 wrap-break-word"></p>
|
||||
<div class="flex justify-between">
|
||||
<div >
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<x-dashboard.user.action-toolbar.like-button data-is-loading="true" :is-interactive="false"/>
|
||||
<x-dashboard.user.action-toolbar.favorite-button data-is-loading="true" :is-interactive="false"/>
|
||||
</div>
|
||||
|
||||
@ -2,9 +2,12 @@
|
||||
@php
|
||||
$variants = [
|
||||
'neutral' => 'bg-black text-white',
|
||||
'ghost' => 'bg-gray-200 text-gray-800'
|
||||
'ghost' => 'bg-gray-200 text-gray-800',
|
||||
'red' => 'bg-red-100 text-red-700',
|
||||
'green' => 'bg-green-100 text-green-700',
|
||||
'yellow' => 'bg-yellow-100 text-yellow-700',
|
||||
];
|
||||
@endphp
|
||||
<span class="text-xs {{$variants[$variant]}} px-3 py-1 rounded-lg">
|
||||
<p class="font-bold">{{$title}}</p>
|
||||
<span class="font-bold">{{$title}}</span>
|
||||
</span>
|
||||
|
||||
87
resources/views/dashboards/admin/deals/index.blade.php
Normal file
87
resources/views/dashboards/admin/deals/index.blade.php
Normal file
@ -0,0 +1,87 @@
|
||||
@php use App\Enums\ReportStatus; @endphp
|
||||
<x-dashboard.admin.layout title="Deals">
|
||||
<x-slot:heading>
|
||||
<x-dashboard.page-heading
|
||||
title="Manage Customers"
|
||||
description="Edit, Delete and Login as Customer"
|
||||
/>
|
||||
</x-slot:heading>
|
||||
<div class="flex items-center justify-center px-4 pb-4 pt-0 md:px-8 md:pb-8">
|
||||
<x-dashboard.card class="w-full">
|
||||
<h3 class="text-md font-bold mb-2">Reported deals</h3>
|
||||
<h4 class="text-sm font-medium mb-4 text-accent-600">Total Reports: {{$reports->count()}}</h4>
|
||||
<x-ui.table>
|
||||
<x-ui.table.head>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Customer</th>
|
||||
<th>Status</th>
|
||||
<th class="flex w-full justify-center items-center">
|
||||
Deal
|
||||
<x-heroicon-o-arrow-top-right-on-square class="w-4 ml-1"/>
|
||||
</th>
|
||||
</x-ui.table.head>
|
||||
|
||||
@forelse($reports as $report)
|
||||
<x-ui.table.row>
|
||||
<td class="text-sm font-medium px-4 text-center">{{ucfirst($report->type->value)}}</td>
|
||||
<td class="text-sm text-accent-600 px-4 text-center max-w-40">{{$report->description}}</td>
|
||||
<td class="text-sm px-4 text-center">{{$report->user->name}}</td>
|
||||
<td class="text-center px-4">
|
||||
<x-ui.badge
|
||||
:title="$report->status->value"
|
||||
:variant="match ($report->status){
|
||||
ReportStatus::Resolved => 'green',
|
||||
ReportStatus::Pending => 'yellow',
|
||||
ReportStatus::Rejected => 'red'
|
||||
}"
|
||||
/>
|
||||
</td>
|
||||
<td class="text-sm px-4 text-center truncate max-w-40">
|
||||
<a target="_blank" class="hover:underline"
|
||||
href="{{route('explore', ['show' => $report->deals()->first()->id])}}">
|
||||
{{$report->deals()->first()->title}}
|
||||
</a>
|
||||
</td>
|
||||
<x-slot:actions>
|
||||
<div class="flex items-center justify-center space-x-2 py-1 px-2">
|
||||
@if($report->status !== ReportStatus::Resolved)
|
||||
<form action="{{route('admin.reports.resolve', $report)}}"
|
||||
onsubmit="return confirm('Are you sure to mark this resolve ?')" method="post"
|
||||
class=" h-full items-center flex justify-center">
|
||||
@csrf
|
||||
<x-ui.button-sm tooltip="Mark resolved" variant="green">
|
||||
<x-heroicon-o-check class="w-4"/>
|
||||
</x-ui.button-sm>
|
||||
</form>
|
||||
@endif
|
||||
@if($report->status !== ReportStatus::Rejected)
|
||||
<form action="{{route('admin.reports.reject', $report)}}"
|
||||
onsubmit="return confirm('Are you sure to reject this ?')" method="post"
|
||||
class=" h-full items-center flex justify-center">
|
||||
@csrf
|
||||
<x-ui.button-sm tooltip="Reject" variant="red">
|
||||
<x-heroicon-o-x-mark class="w-4"/>
|
||||
</x-ui.button-sm>
|
||||
</form>
|
||||
@endif
|
||||
<form action="{{route('admin.reports.remove-content', $report)}}"
|
||||
onsubmit="return confirm('Are you sure to remove the deal ?')" method="post"
|
||||
class=" h-full items-center flex justify-center">
|
||||
@csrf
|
||||
<x-ui.button-sm tooltip="Remove Content" variant="ghost">
|
||||
<x-heroicon-o-exclamation-triangle class="w-4"/>
|
||||
</x-ui.button-sm>
|
||||
</form>
|
||||
</div>
|
||||
</x-slot:actions>
|
||||
</x-ui.table.row>
|
||||
@empty
|
||||
<x-ui.table.row>
|
||||
<td colspan="2" class="text-center text-sm text-accent-600 py-2">No Customer found</td>
|
||||
</x-ui.table.row>
|
||||
@endforelse
|
||||
</x-ui.table>
|
||||
</x-dashboard.card>
|
||||
</div>
|
||||
</x-dashboard.admin.layout>
|
||||
87
resources/views/dashboards/admin/reports/index.blade.php
Normal file
87
resources/views/dashboards/admin/reports/index.blade.php
Normal file
@ -0,0 +1,87 @@
|
||||
@php use App\Enums\ReportStatus; @endphp
|
||||
<x-dashboard.admin.layout title="Reports">
|
||||
<x-slot:heading>
|
||||
<x-dashboard.page-heading
|
||||
title="Manage Reports"
|
||||
description="Approve, Resolve or Remove content based on report."
|
||||
/>
|
||||
</x-slot:heading>
|
||||
<div class="flex items-center justify-center px-4 pb-4 pt-0 md:px-8 md:pb-8">
|
||||
<x-dashboard.card class="w-full">
|
||||
<h3 class="text-md font-bold mb-2">Reported deals</h3>
|
||||
<h4 class="text-sm font-medium mb-4 text-accent-600">Total Reports: {{$reports->count()}}</h4>
|
||||
<x-ui.table>
|
||||
<x-ui.table.head>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Customer</th>
|
||||
<th>Status</th>
|
||||
<th class="flex w-full justify-center items-center">
|
||||
Deal
|
||||
<x-heroicon-o-arrow-top-right-on-square class="w-4 ml-1"/>
|
||||
</th>
|
||||
</x-ui.table.head>
|
||||
|
||||
@forelse($reports as $report)
|
||||
<x-ui.table.row>
|
||||
<td class="text-sm font-medium px-4 text-center">{{ucfirst($report->type->value)}}</td>
|
||||
<td class="text-sm text-accent-600 px-4 text-center max-w-40">{{$report->description}}</td>
|
||||
<td class="text-sm px-4 text-center">{{$report->user->name}}</td>
|
||||
<td class="text-center px-4">
|
||||
<x-ui.badge
|
||||
:title="$report->status->value"
|
||||
:variant="match ($report->status){
|
||||
ReportStatus::Resolved => 'green',
|
||||
ReportStatus::Pending => 'yellow',
|
||||
ReportStatus::Rejected => 'red'
|
||||
}"
|
||||
/>
|
||||
</td>
|
||||
<td class="text-sm px-4 text-center truncate max-w-40">
|
||||
<a target="_blank" class="hover:underline"
|
||||
href="{{route('explore', ['show' => $report->deals()->first()->id])}}">
|
||||
{{$report->deals()->first()->title}}
|
||||
</a>
|
||||
</td>
|
||||
<x-slot:actions>
|
||||
<div class="flex items-center justify-center space-x-2 py-1 px-2">
|
||||
@if($report->status !== ReportStatus::Resolved)
|
||||
<form action="{{route('admin.reports.resolve', $report)}}"
|
||||
onsubmit="return confirm('Are you sure to mark this resolve ?')" method="post"
|
||||
class=" h-full items-center flex justify-center">
|
||||
@csrf
|
||||
<x-ui.button-sm tooltip="Mark resolved" variant="green">
|
||||
<x-heroicon-o-check class="w-4"/>
|
||||
</x-ui.button-sm>
|
||||
</form>
|
||||
@endif
|
||||
@if($report->status !== ReportStatus::Rejected)
|
||||
<form action="{{route('admin.reports.reject', $report)}}"
|
||||
onsubmit="return confirm('Are you sure to reject this ?')" method="post"
|
||||
class=" h-full items-center flex justify-center">
|
||||
@csrf
|
||||
<x-ui.button-sm tooltip="Reject" variant="red">
|
||||
<x-heroicon-o-x-mark class="w-4"/>
|
||||
</x-ui.button-sm>
|
||||
</form>
|
||||
@endif
|
||||
<form action="{{route('admin.reports.remove-content', $report)}}"
|
||||
onsubmit="return confirm('Are you sure to remove the deal ?')" method="post"
|
||||
class=" h-full items-center flex justify-center">
|
||||
@csrf
|
||||
<x-ui.button-sm tooltip="Remove Content" variant="ghost">
|
||||
<x-heroicon-o-exclamation-triangle class="w-4"/>
|
||||
</x-ui.button-sm>
|
||||
</form>
|
||||
</div>
|
||||
</x-slot:actions>
|
||||
</x-ui.table.row>
|
||||
@empty
|
||||
<x-ui.table.row>
|
||||
<td colspan="2" class="text-center text-sm text-accent-600 py-2">No Customer found</td>
|
||||
</x-ui.table.row>
|
||||
@endforelse
|
||||
</x-ui.table>
|
||||
</x-dashboard.card>
|
||||
</div>
|
||||
</x-dashboard.admin.layout>
|
||||
@ -4,6 +4,7 @@
|
||||
use App\Http\Controllers\Admin\AdminDashboardController;
|
||||
use App\Http\Controllers\Admin\BrokerController;
|
||||
use App\Http\Controllers\Admin\CustomerController;
|
||||
use App\Http\Controllers\Admin\ReportController;
|
||||
use App\Http\Middleware\HasRole;
|
||||
|
||||
Route::prefix('/admin')
|
||||
@ -15,4 +16,10 @@
|
||||
Route::resource('brokers', BrokerController::class)->except('show', 'create', 'store');
|
||||
|
||||
Route::post('/brokers/approve/{broker}', [BrokerController::class, 'approve'])->name('brokers.approve');
|
||||
|
||||
Route::get('/reports', [ReportController::class, 'index'])->name('reports.index');
|
||||
Route::post('/reports/resolve/{report}', [ReportController::class, 'resolve'])->name('reports.resolve');
|
||||
Route::post('/reports/reject/{report}', [ReportController::class, 'reject'])->name('reports.reject');
|
||||
Route::post('/reports/remove/{report}',
|
||||
[ReportController::class, 'removeContent'])->name('reports.remove-content');
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user