Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions app/Actions/Auction/CreateReportAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Otoszroto\Actions\Auction;

use Otoszroto\Models\Auction;
use Otoszroto\Models\Report;
use Otoszroto\Models\User;

class CreateReportAction
{
public function execute(User $user, Auction $auction, array $reportData): Report
{
$report = new Report($reportData);
$report->reporter->associate($user);
$report->auction->associate($auction);
$report->save();

return $report;
}
}
18 changes: 18 additions & 0 deletions app/Actions/Auction/ResolveReportAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Otoszroto\Actions\Auction;

use Otoszroto\Models\Report;

class ResolveReportAction
{
public function execute(Report $report): Report
{
$report->resolved_at = now();
$report->save();

return $report;
}
}
65 changes: 65 additions & 0 deletions app/Http/Controllers/Auction/ReportController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Otoszroto\Http\Controllers\Auction;

use Illuminate\Http\RedirectResponse;
use Inertia\Inertia;
use Inertia\Response;
use Otoszroto\Actions\Auction\CreateReportAction;
use Otoszroto\Actions\Auction\ResolveReportAction;
use Otoszroto\Helpers\SortHelper;
use Otoszroto\Http\Controllers\Controller;
use Otoszroto\Http\Requests\Auction\CreateReportRequest;
use Otoszroto\Http\Resources\ReportResource;
use Otoszroto\Models\Auction;
use Otoszroto\Models\Report;

class ReportController extends Controller
{
public function create(Auction $auction): Response
{
return Inertia::render("Auction/ReportAuction", ["auction" => $auction]);
}

public function store(CreateReportRequest $request, CreateReportAction $createReportAction, Auction $auction): RedirectResponse
{
$user = $request->user();
$this->authorize("report", $auction);
$validated = $request->validated();
$createReportAction->execute($user, $auction, $validated);

return redirect()->route("auctions.show", ["auction" => $auction])->with(["message" => "Aukcja została zgłoszona."]);
}

public function index(SortHelper $sorter): Response
{
$reports = Report::query()->with(["reporter", "auction"])->where("resolved_at", "=", null);

$perPage = (int)request()->query("per_page", 10);

$query = $sorter->sort($reports, ["id", "created_at"], []);
$query = $sorter->search($query, "id");

return Inertia::render("Auction/Reports", [
"reports" => ReportResource::collection($query->paginate($perPage)),
]);
}

public function show(Report $report): Response
{
$report->load(["reporter", "auction"]);

return Inertia::render("Auction/ShowReport", [
"report" => new ReportResource($report),
]);
}

public function resolve(ResolveReportAction $resolveReportAction, Report $report): RedirectResponse
{
$report = $resolveReportAction->execute($report);

return redirect()->route("reports.show", ["report" => $report])->with(["message" => "Zgłoszenie zostało rozwiązane."]);
}
}
33 changes: 33 additions & 0 deletions app/Http/Requests/Auction/CreateReportRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Otoszroto\Http\Requests\Auction;

use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;

class CreateReportRequest extends FormRequest
{
public function authorize(): bool
{
$user = $this->user();
$auction = $this->route("auction");

if ($user === null || $auction === null) {
return false;
}

return $user->can("report", $auction);
}

/**
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
"reason" => ["sometimes", "string", "max:255"],
];
}
}
3 changes: 3 additions & 0 deletions app/Http/Resources/AuctionResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public function toArray(Request $request): array
"auctionState" => $this->auction_state,
"createdAt" => $this->created_at,
"updatedAt" => $this->updated_at,
"wasReported" => auth()->check()
? $this->reports->contains("reporter_id", auth()->id())
: false,
];
}
}
27 changes: 27 additions & 0 deletions app/Http/Resources/ReportResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Otoszroto\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ReportResource extends JsonResource
{
/**
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
"id" => $this->id,
"reporter" => UserResource::make($this->whenLoaded("reporter")),
"auction" => AuctionResource::make($this->whenLoaded("auction")),
"reason" => $this->reason,
"resolvedAt" => optional($this->resolved_at)?->toDateTimeString(),
"createdAt" => optional($this->created_at)?->toDateTimeString(),
"updatedAt" => optional($this->updated_at)?->toDateTimeString(),
];
}
}
8 changes: 8 additions & 0 deletions app/Models/Auction.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,12 @@ public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}

/**
* @return HasMany<Report>
*/
public function reports()
{
return $this->hasMany(Report::class);
}
}
53 changes: 53 additions & 0 deletions app/Models/Report.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Otoszroto\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

/**
* @property int $id
* @property int $reporter_id
* @property int $auction_id
* @property string|null $reason
* @property Carbon|null $resolved_at
* @property Carbon $created_at
* @property Carbon $updated_at
* @property User $reporter
* @property Auction $auction
*/
class Report extends Model
{
use HasFactory;

public $timestamps = true;
protected $fillable = [
"reporter_id",
"auction_id",
"reason",
"resolved_at",
];
protected $casts = [
"resolved_at" => "datetime",
];

/**
* @return BelongsTo<User, $this>
*/
public function reporter(): BelongsTo
{
return $this->belongsTo(User::class, "reporter_id");
}

/**
* @return BelongsTo<Auction, $this>
*/
public function auction(): BelongsTo
{
return $this->belongsTo(Auction::class, "auction_id");
}
}
17 changes: 17 additions & 0 deletions app/Policies/AuctionPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,21 @@ public function update(User $user, Auction $auction): Response
? Response::allow()
: Response::deny();
}

public function report(User $user, Auction $auction): Response
{
$alreadyReported = $auction->reports()
->where("reporter_id", $user->id)
->exists();

if ($user->id === $auction->owner_id) {
return Response::deny("Nie można zgłosić własnej aukcji.");
}

if ($alreadyReported) {
return Response::deny("Aukcja została już przez Ciebie zgłoszona.");
}

return Response::allow();
}
}
26 changes: 26 additions & 0 deletions database/factories/ReportFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Otoszroto\Models\Auction;
use Otoszroto\Models\Report;
use Otoszroto\Models\User;

/**
* @extends Factory<Report>
*/
class ReportFactory extends Factory
{
public function definition(): array
{
return [
"reporter_id" => User::factory(),
"auction_id" => Auction::factory(),
"reason" => fake()->text(200),
"resolved_at" => null,
];
}
}
29 changes: 29 additions & 0 deletions database/migrations/2026_01_06_155512_create_reports_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Otoszroto\Models\Auction;
use Otoszroto\Models\User;

return new class() extends Migration {
public function up(): void
{
Schema::create("reports", function (Blueprint $table): void {
$table->id();
$table->foreignIdFor(User::class, "reporter_id")->constrained("users")->cascadeOnDelete();
$table->foreignIdFor(Auction::class, "auction_id")->constrained("auctions")->cascadeOnDelete();
$table->text("reason")->nullable();
$table->timestamp("resolved_at")->nullable();
$table->timestamps();
$table->unique(["reporter_id", "auction_id"]);
});
}

public function down(): void
{
Schema::dropIfExists("reports");
}
};
1 change: 1 addition & 0 deletions database/seeders/DatabaseSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public function run(): void
AuctionSeeder::class,
PermissionsSeeder::class,
RolesSeeder::class,
ReportSeeder::class,
]);
}
}
16 changes: 16 additions & 0 deletions database/seeders/ReportSeeder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Otoszroto\Models\Report;

class ReportSeeder extends Seeder
{
public function run(): void
{
Report::factory()->count(30)->create();
}
}
25 changes: 25 additions & 0 deletions resources/js/Pages/Auction/ReportAuction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import {Form} from "@inertiajs/react";
import { Auction } from "@/Types/auction";

type Props = {
errors: any;
auction: Auction;
}

export function ReportAuction({errors, auction}: Props) {
return (
<>
<h1 className={"text-lg font-semibold"}>Report auction</h1>
<Form
action={`/auctions/${auction.id}/report`}
method="POST"
>
<input type="text" placeholder="Reason" name="reason"/>
{errors?.reason && <p>{errors.reason}</p>}

<input type="submit" value="Zgłoś" />
</Form>
</>
);
}
22 changes: 22 additions & 0 deletions resources/js/Pages/Auction/ShowReport.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Title } from "@/Components/Title";
import { Report } from "@/Types/report";

type Props = {
report: Report,
}

export function ShowReport({report}: Props) {
return (
<div className='w-full p-4 mt-5 mx-auto md:px-10'>
<Title type='h2'>Szczegóły zgłoszenia</Title>

<div><strong>ID:</strong> {report.id}</div>
<div><strong>Reporter:</strong> {report.reporter?.email || "N/A"}</div>
<div><strong>Auction:</strong> {report.auction?.name || "N/A"}</div>
<div><strong>Reason:</strong> {report.reason || "Brak"}</div>
<div><strong>Resolved At:</strong> {report.resolvedAt || "Nie rozwiązano"}</div>
<div><strong>Created At:</strong> {report.createdAt}</div>
<div><strong>Updated At:</strong> {report.updatedAt}</div>
</div>
);
}
Loading