mirror of
https://github.com/HDInnovations/UNIT3D-Community-Edition.git
synced 2026-04-23 03:34:22 -05:00
add: upload contests
Add a modular base set-up that allows adding more different event types. This one adds upload contests. More can be added the same way with minimal coding efforts. Implementing a completely automated system may not be feasible, as there are too many individual variations and nuances to account for.
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Notifications\NewUploadContestWinner;
|
||||
use App\Models\Torrent;
|
||||
use App\Models\UploadContest;
|
||||
use App\Models\UploadContestWinner;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
class AutoRewardUploadContestPrize extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'auto:reward_upload_contest_prize';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Automatically hands out rewards for upload contests';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @throws Exception|Throwable If there is an error during the execution of the command.
|
||||
*/
|
||||
final public function handle(): void
|
||||
{
|
||||
// Get all active upload contests
|
||||
$activeUploadContests = UploadContest::where('ends_at', '<', now()->toDateString())
|
||||
->where('awarded', '=', 0)
|
||||
->get();
|
||||
|
||||
foreach ($activeUploadContests as $activeUploadContest) {
|
||||
// Get the amount of competitors to reward based on prizes for the event
|
||||
$numRewards = $activeUploadContest->prizes->count();
|
||||
|
||||
// Get prizes ordered by position
|
||||
$rewards = $activeUploadContest->prizes->sortBy('position');
|
||||
|
||||
// Get top N competitors
|
||||
$winners = Torrent::query()
|
||||
->with('user.group')
|
||||
->where('anon', '=', false)
|
||||
->select(DB::raw('user_id, count(*) as uploads, max(created_at) as last_upload'))
|
||||
->where('created_at', '>=', $activeUploadContest->starts_at->startOfDay())
|
||||
->where('created_at', '<=', $activeUploadContest->ends_at->endOfDay())
|
||||
->groupBy('user_id')
|
||||
->orderByDesc('uploads')
|
||||
->orderBy('last_upload')
|
||||
->limit($numRewards)
|
||||
->get();
|
||||
|
||||
foreach ($winners as $i => $winner) {
|
||||
$rewardsByPosition = $rewards->where('position', $i + 1);
|
||||
|
||||
foreach ($rewardsByPosition as $reward) {
|
||||
// Reward prize
|
||||
if ($reward->type === 'bon') {
|
||||
$winner->user->increment('seedbonus', $reward->amount);
|
||||
}
|
||||
|
||||
if ($reward->type === 'fl_tokens') {
|
||||
$winner->user->increment('fl_tokens', $reward->amount);
|
||||
}
|
||||
}
|
||||
|
||||
// Persist the winners
|
||||
UploadContestWinner::create([
|
||||
'upload_contest_id' => $activeUploadContest->id,
|
||||
'user_id' => $winner->user->id,
|
||||
'place_number' => $i + 1,
|
||||
/** @phpstan-ignore property.notFound (Uploads is not part of the torrents table.) */
|
||||
'uploads' => $winner->uploads,
|
||||
]);
|
||||
|
||||
// Send notification
|
||||
$winner->user->notify(new NewUploadContestWinner($activeUploadContest));
|
||||
}
|
||||
|
||||
// Set upload contest as awarded
|
||||
$activeUploadContest->update([
|
||||
'awarded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->comment('Automated reward upload contest command complete');
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ use App\Console\Commands\AutoRemoveReseeds;
|
||||
use App\Console\Commands\AutoRemoveTimedTorrentBuffs;
|
||||
use App\Console\Commands\AutoResetUserFlushes;
|
||||
use App\Console\Commands\AutoRewardResurrection;
|
||||
use App\Console\Commands\AutoRewardUploadContestPrize;
|
||||
use App\Console\Commands\AutoSoftDeleteDisabledUsers;
|
||||
use App\Console\Commands\AutoSyncPeopleToMeilisearch;
|
||||
use App\Console\Commands\AutoSyncTorrentsToMeilisearch;
|
||||
@@ -109,6 +110,7 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->command(AutoSyncPeopleToMeilisearch::class)->daily();
|
||||
$schedule->command(AutoRemoveExpiredDonors::class)->daily();
|
||||
$schedule->command(AutoRemoveReseeds::class)->daily();
|
||||
$schedule->command(AutoRewardUploadContestPrize::class)->daily();
|
||||
// $schedule->command(AutoBanDisposableUsers::class)->weekends();
|
||||
$schedule->command(CleanupCommand::class)->daily();
|
||||
$schedule->command(BackupCommand::class, ['--only-db'])->daily();
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Staff;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Staff\StoreUploadContestRequest;
|
||||
use App\Http\Requests\Staff\UpdateUploadContestRequest;
|
||||
use App\Models\UploadContest;
|
||||
|
||||
class UploadContestController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
{
|
||||
return view('Staff.upload-contest.index', [
|
||||
'uploadContests' => UploadContest::all(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
{
|
||||
return view('Staff.upload-contest.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(StoreUploadContestRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
UploadContest::create([
|
||||
'active' => 0,
|
||||
'awarded' => 0,
|
||||
] + $request->validated());
|
||||
|
||||
return to_route('staff.upload_contests.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(UploadContest $uploadContest): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
{
|
||||
return view('Staff.upload-contest.edit', [
|
||||
'uploadContest' => $uploadContest->load('prizes'),
|
||||
'prizes' => $uploadContest->prizes->sortBy('position'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(UpdateUploadContestRequest $request, UploadContest $uploadContest): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$uploadContest->update($request->validated());
|
||||
|
||||
return to_route('staff.upload_contests.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(UploadContest $uploadContest): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$uploadContest->delete();
|
||||
|
||||
return to_route('staff.upload_contests.index');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Staff;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Staff\StoreUploadContestPrizeRequest;
|
||||
use App\Http\Requests\Staff\UpdateUploadContestPrizeRequest;
|
||||
use App\Models\UploadContest;
|
||||
use App\Models\UploadContestPrize;
|
||||
|
||||
class UploadContestPrizeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(StoreUploadContestPrizeRequest $request, UploadContest $uploadContest): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$uploadContest->prizes()->create($request->validated());
|
||||
|
||||
return to_route('staff.upload_contests.edit', [
|
||||
'uploadContest' => $uploadContest
|
||||
])
|
||||
->with('success', 'Prize added to upload contest.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(UpdateUploadContestPrizeRequest $request, UploadContest $uploadContest, UploadContestPrize $prize): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$prize->update($request->validated());
|
||||
|
||||
return to_route('staff.upload_contests.edit', [
|
||||
'uploadContest' => $uploadContest
|
||||
])
|
||||
->with('success', 'Prize updated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(UploadContest $uploadContest, UploadContestPrize $prize): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$prize->delete();
|
||||
|
||||
return to_route('staff.upload_contests.edit', [
|
||||
'uploadContest' => $uploadContest
|
||||
])
|
||||
->with('success', 'Prize removed from upload contest.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Torrent;
|
||||
use App\Models\UploadContest;
|
||||
use App\Models\UploadContestWinner;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UploadContestController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
{
|
||||
return view('upload-contest.index', [
|
||||
'uploadContests' => UploadContest::query()->orderBy('starts_at')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Request $request, UploadContest $uploadContest): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
{
|
||||
if (! $uploadContest->awarded) {
|
||||
// Fetch the live data
|
||||
$uploaders = Torrent::query()
|
||||
->with('user.group')
|
||||
->where('anon', '=', false)
|
||||
->select(DB::raw('user_id, count(*) as uploads, max(created_at) as last_upload'))
|
||||
->where('created_at', '>=', $uploadContest->starts_at->startOfDay())
|
||||
->where('created_at', '<=', $uploadContest->ends_at->endOfDay())
|
||||
->groupBy('user_id')
|
||||
->orderByDesc('uploads')
|
||||
->orderBy('last_upload')
|
||||
->take(25)
|
||||
->get();
|
||||
} else {
|
||||
// Fetch the persisted history winners
|
||||
$uploaders = UploadContestWinner::query()
|
||||
->with('user.group')
|
||||
->where('upload_contest_id', $uploadContest->id)
|
||||
->orderBy('place_number')
|
||||
->get();
|
||||
}
|
||||
|
||||
return view('upload-contest.show', [
|
||||
'uploadContest' => $uploadContest,
|
||||
'uploaders' => $uploaders,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Staff;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreUploadContestPrizeRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'type' => [
|
||||
'required',
|
||||
'string',
|
||||
'in:bon,fl_tokens',
|
||||
],
|
||||
'amount' => [
|
||||
'required',
|
||||
'integer',
|
||||
'min:0',
|
||||
],
|
||||
'position' => [
|
||||
'required',
|
||||
'integer',
|
||||
'min:1',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Staff;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreUploadContestRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:255',
|
||||
],
|
||||
'description' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:65535',
|
||||
],
|
||||
'icon' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:255',
|
||||
],
|
||||
'starts_at' => [
|
||||
'required',
|
||||
'date',
|
||||
],
|
||||
'ends_at' => [
|
||||
'required',
|
||||
'date',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Roardom <roardom@protonmail.com>
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Staff;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateUploadContestPrizeRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'type' => [
|
||||
'required',
|
||||
'string',
|
||||
'in:bon,fl_tokens',
|
||||
],
|
||||
'amount' => [
|
||||
'required',
|
||||
'integer',
|
||||
'min:0',
|
||||
],
|
||||
'position' => [
|
||||
'required',
|
||||
'integer',
|
||||
'min:0',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Staff;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateUploadContestRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:255',
|
||||
],
|
||||
'description' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:65535',
|
||||
],
|
||||
'icon' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:255',
|
||||
],
|
||||
'starts_at' => [
|
||||
'required',
|
||||
'date',
|
||||
],
|
||||
'ends_at' => [
|
||||
'required',
|
||||
'date',
|
||||
],
|
||||
'active' => [
|
||||
'required',
|
||||
'boolean',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Auditable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use AllowDynamicProperties;
|
||||
|
||||
/**
|
||||
* App\Models\UploadContest.
|
||||
*
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $description
|
||||
* @property string $icon
|
||||
* @property bool $active
|
||||
* @property bool $awarded
|
||||
* @property \Illuminate\Support\Carbon|null $starts_at
|
||||
* @property \Illuminate\Support\Carbon|null $ends_at
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
*/
|
||||
#[AllowDynamicProperties]
|
||||
final class UploadContest extends Model
|
||||
{
|
||||
use Auditable;
|
||||
|
||||
/** @use HasFactory<\Database\Factories\UploadContestFactory> */
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array{starts_at: 'datetime', ends_at: 'datetime', active: 'bool'}
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'starts_at' => 'datetime',
|
||||
'ends_at' => 'datetime',
|
||||
'active' => 'bool',
|
||||
'awarded' => 'bool',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the available prizes for the upload contest.
|
||||
*
|
||||
* @return HasMany<UploadContestPrize, $this>
|
||||
*/
|
||||
public function prizes(): HasMany
|
||||
{
|
||||
return $this->hasMany(UploadContestPrize::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the winners for the upload contest.
|
||||
*
|
||||
* @return HasMany<UploadContestWinner, $this>
|
||||
*/
|
||||
public function winners(): HasMany
|
||||
{
|
||||
return $this->hasMany(UploadContestWinner::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use AllowDynamicProperties;
|
||||
|
||||
/**
|
||||
* App\Models\UploadContestPrize.
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $upload_contest_id
|
||||
* @property string $type
|
||||
* @property int $amount
|
||||
* @property int $position
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
*/
|
||||
#[AllowDynamicProperties]
|
||||
final class UploadContestPrize extends Model
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UploadContestPrizeFactory> */
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* Get the UploadContest that owns the prize.
|
||||
*
|
||||
* @return BelongsTo<UploadContest, $this>
|
||||
*/
|
||||
public function contest(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(UploadContest::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use AllowDynamicProperties;
|
||||
|
||||
/**
|
||||
* App\Models\UploadContestPrize.
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $upload_contest_id
|
||||
* @property int $user_id
|
||||
* @property int $place_number
|
||||
* @property int $uploads
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
*/
|
||||
#[AllowDynamicProperties]
|
||||
final class UploadContestWinner extends Model
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UploadContestWinnerFactory> */
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* Get the UploadContest.
|
||||
*
|
||||
* @return BelongsTo<UploadContest, $this>
|
||||
*/
|
||||
public function contest(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(UploadContest::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user.
|
||||
*
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class)->withDefault([
|
||||
'username' => 'System',
|
||||
'id' => User::SYSTEM_USER_ID,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\UploadContest;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class NewUploadContestWinner extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* NewUploadContestWinner Constructor.
|
||||
*/
|
||||
public function __construct(public UploadContest $uploadContest)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return ['database'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the notification should be sent.
|
||||
*/
|
||||
public function shouldSend(User $notifiable): bool
|
||||
{
|
||||
return ! ($notifiable->notification?->block_notifications === 1)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [
|
||||
'title' => 'You have won an upload contest!',
|
||||
'body' => 'Your awards have been added to your account',
|
||||
'url' => route('upload_contests.show', ['uploadContest' => $this->uploadContest]),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use App\Models\Report;
|
||||
use App\Models\Scopes\ApprovedScope;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\Torrent;
|
||||
use App\Models\UploadContest;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class TopNavComposer
|
||||
@@ -53,6 +54,9 @@ class TopNavComposer
|
||||
->where('user_id', '=', $user->id),
|
||||
])
|
||||
->get(),
|
||||
'uploadContests' => UploadContest::query()
|
||||
->where('active', '=', true)
|
||||
->get(),
|
||||
'donationPercentage' => value(function (): int|string {
|
||||
$sum = Donation::query()
|
||||
->join('donation_packages', 'donations.package_id', '=', 'donation_packages.id')
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\UploadContest;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/** @extends Factory<UploadContest> */
|
||||
class UploadContestFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*/
|
||||
protected $model = UploadContest::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->sentence(3),
|
||||
'description' => $this->faker->paragraph(),
|
||||
'icon' => $this->faker->imageUrl(),
|
||||
'active' => $this->faker->boolean(),
|
||||
'awarded' => $this->faker->boolean(),
|
||||
'starts_at' => $this->faker->dateTimeBetween('-1 month', '+1 month'),
|
||||
'ends_at' => $this->faker->dateTimeBetween('+1 month', '+2 months'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author HDVinnie <hdinnovations@protonmail.com>
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\UploadContest;
|
||||
use App\Models\UploadContestPrize;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/** @extends Factory<UploadContestPrize> */
|
||||
class UploadContestPrizeFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*/
|
||||
protected $model = UploadContestPrize::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$types = ['fl_tokens', 'bon'];
|
||||
|
||||
return [
|
||||
'upload_contest_id' => UploadContest::factory(),
|
||||
'type' => $this->faker->randomElement($types),
|
||||
'amount' => $this->faker->numberBetween(1, 1000),
|
||||
'position' => $this->faker->numberBetween(1, 10),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\UploadContest;
|
||||
use App\Models\UploadContestWinner;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/** @extends Factory<UploadContestWinner> */
|
||||
class UploadContestWinnerFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*/
|
||||
protected $model = UploadContestWinner::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$types = ['fl_tokens', 'bon'];
|
||||
|
||||
return [
|
||||
'upload_contest_id' => UploadContest::factory(),
|
||||
'user_id' => User::factory(),
|
||||
'place_number' => $this->faker->numberBetween(1, 10),
|
||||
'uploads' => $this->faker->numberBetween(1, 100),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class () extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('upload_contests', function (Blueprint $table): void {
|
||||
$table->increments('id');
|
||||
$table->string('name');
|
||||
$table->text('description');
|
||||
$table->string('icon');
|
||||
$table->boolean('active');
|
||||
$table->boolean('awarded');
|
||||
$table->date('starts_at');
|
||||
$table->date('ends_at');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('upload_contest_prizes', function (Blueprint $table): void {
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('upload_contest_id');
|
||||
$table->string('type');
|
||||
$table->unsignedInteger('amount');
|
||||
$table->unsignedInteger('position');
|
||||
|
||||
$table->foreign('upload_contest_id')->references('id')->on('upload_contests');
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('upload_contest_winners', function (Blueprint $table): void {
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('upload_contest_id');
|
||||
$table->unsignedInteger('user_id');
|
||||
$table->unsignedInteger('place_number');
|
||||
$table->unsignedInteger('uploads');
|
||||
|
||||
$table->foreign('upload_contest_id')->references('id')->on('upload_contests');
|
||||
$table->foreign('user_id')->references('id')->on('users')->cascadeOnUpdate();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('upload_contest_winners');
|
||||
Schema::dropIfExists('upload_contest_prizes');
|
||||
Schema::dropIfExists('upload_contests');
|
||||
}
|
||||
};
|
||||
@@ -2246,6 +2246,57 @@ CREATE TABLE `unregistered_info_hashes` (
|
||||
CONSTRAINT `unregistered_info_hashes_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `upload_contest_prizes`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `upload_contest_prizes` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`upload_contest_id` int unsigned NOT NULL,
|
||||
`type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`amount` int unsigned NOT NULL,
|
||||
`position` int unsigned NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `upload_contest_prizes_upload_contest_id_foreign` (`upload_contest_id`),
|
||||
CONSTRAINT `upload_contest_prizes_upload_contest_id_foreign` FOREIGN KEY (`upload_contest_id`) REFERENCES `upload_contests` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `upload_contest_winners`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `upload_contest_winners` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`upload_contest_id` int unsigned NOT NULL,
|
||||
`user_id` int unsigned NOT NULL,
|
||||
`place_number` int unsigned NOT NULL,
|
||||
`uploads` int unsigned NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `upload_contest_winners_upload_contest_id_foreign` (`upload_contest_id`),
|
||||
KEY `upload_contest_winners_user_id_foreign` (`user_id`),
|
||||
CONSTRAINT `upload_contest_winners_upload_contest_id_foreign` FOREIGN KEY (`upload_contest_id`) REFERENCES `upload_contests` (`id`),
|
||||
CONSTRAINT `upload_contest_winners_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `upload_contests`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `upload_contests` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`description` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`icon` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`active` tinyint(1) NOT NULL,
|
||||
`awarded` tinyint(1) NOT NULL,
|
||||
`starts_at` date NOT NULL,
|
||||
`ends_at` date NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `user_audibles`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
@@ -3047,7 +3098,8 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (364,'2025_09_08_00
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (365,'2025_09_25_110038_alter_reports_create_assignee',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (366,'2025_11_08_094209_rename_warnings_torrent_to_torrent_id',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (367,'2025_11_18_080804_echoes_audibles_unique_keys',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (368,'2025_11_29_101934_update_events_rename_to_giveaways',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (369,'2026_01_06_231535_remove_unnecessary_bigints',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (370,'2026_01_07_040502_mark_columns_as_unsigned',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (371,'2026_01_09_015532_alter_table_reports_make_verdict_nullable',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (368,'2025_11_22_121612_create_upload_events_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (369,'2025_11_29_101934_update_events_rename_to_giveaways',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (370,'2026_01_06_231535_remove_unnecessary_bigints',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (371,'2026_01_07_040502_mark_columns_as_unsigned',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (372,'2026_01_09_015532_alter_table_reports_make_verdict_nullable',1);
|
||||
|
||||
@@ -34,6 +34,7 @@ return [
|
||||
'article' => 'Article',
|
||||
'ascending' => 'Ascending',
|
||||
'author' => 'Author',
|
||||
'awarded' => 'Awarded',
|
||||
'balance' => 'Balance',
|
||||
'blacklist' => 'Client blacklist',
|
||||
'bookmarked' => 'Bookmarked',
|
||||
@@ -58,6 +59,8 @@ return [
|
||||
'contact' => 'Contact',
|
||||
'contact-desc' => 'This contact request will be sent to the owner and will get back to you as soon as possible',
|
||||
'contact-header' => 'Hello',
|
||||
'contest' => 'Contest',
|
||||
'contests' => 'Contests',
|
||||
'create' => 'Create',
|
||||
'created_at' => 'Created at',
|
||||
'date' => 'Date',
|
||||
@@ -80,6 +83,7 @@ return [
|
||||
'email-whitelist' => 'Email whitelist',
|
||||
'email-list-notactive' => 'Email whitelist / blacklist system is not activated',
|
||||
'enable' => 'Enable',
|
||||
'ends-at' => 'Ends at',
|
||||
'enter' => 'Enter',
|
||||
'error' => 'Error',
|
||||
'everyday' => 'Everyday',
|
||||
@@ -196,6 +200,7 @@ return [
|
||||
'sponsor' => 'Become a sponsor',
|
||||
'staff' => 'Staff',
|
||||
'staff-tools' => 'Staff tools',
|
||||
'starts-at' => 'Starts at',
|
||||
'stats' => 'Stats',
|
||||
'status' => 'Status',
|
||||
'sticked' => 'Sticked',
|
||||
|
||||
@@ -168,6 +168,15 @@
|
||||
{{ __('event.giveaways') }}
|
||||
</a>
|
||||
</p>
|
||||
<p class="form__group form__group--horizontal">
|
||||
<a
|
||||
class="form__button form__button--text"
|
||||
href="{{ route('staff.upload_contests.index') }}"
|
||||
>
|
||||
<i class="{{ config('other.font-awesome') }} fa-calendar-star"></i>
|
||||
{{ __('common.upload') }} {{ __('common.contests') }}
|
||||
</a>
|
||||
</p>
|
||||
<p class="form__group form__group--horizontal">
|
||||
<a
|
||||
class="form__button form__button--text"
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
@extends('layout.with-main')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('staff.dashboard.index') }}" class="breadcrumb__link">
|
||||
{{ __('staff.staff-dashboard') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('staff.upload_contests.index') }}" class="breadcrumb__link">
|
||||
{{ __('common.upload') }} {{ __('common.contests') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumb--active">
|
||||
{{ __('common.new-adj') }}
|
||||
</li>
|
||||
@endsection
|
||||
|
||||
@section('page', 'page__staff-upload-contest--create')
|
||||
|
||||
@section('main')
|
||||
<section class="panelV2">
|
||||
<h2 class="panel__heading">{{ __('common.add') }} {{ __('common.contest') }}</h2>
|
||||
<form
|
||||
class="dialog__form"
|
||||
method="POST"
|
||||
action="{{ route('staff.upload_contests.store') }}"
|
||||
>
|
||||
@csrf
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="name"
|
||||
class="form__text"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
name="name"
|
||||
required
|
||||
/>
|
||||
<label class="form__label form__label--floating" for="name">
|
||||
{{ __('common.name') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<textarea
|
||||
id="description"
|
||||
class="form__textarea"
|
||||
name="description"
|
||||
required
|
||||
></textarea>
|
||||
<label class="form__label form__label--floating" for="description">
|
||||
{{ __('common.description') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="icon"
|
||||
class="form__text"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
name="icon"
|
||||
required
|
||||
/>
|
||||
<label class="form__label form__label--floating" for="icon">
|
||||
{{ __('common.icon') }}
|
||||
</label>
|
||||
</p>
|
||||
<div class="form__group--horizontal">
|
||||
<p class="form__group">
|
||||
<input id="starts_at" class="form__text" name="starts_at" type="date" />
|
||||
<label class="form__label form__label--floating" for="starts_at">
|
||||
{{ __('common.starts-at') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<input id="ends_at" class="form__text" name="ends_at" type="date" />
|
||||
<label class="form__label form__label--floating" for="ends_at">
|
||||
{{ __('common.ends-at') }}
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
<p class="form__group">
|
||||
<button class="form__button form__button--filled" wire:click="store">
|
||||
{{ __('common.save') }}
|
||||
</button>
|
||||
<button
|
||||
formmethod="dialog"
|
||||
formnovalidate
|
||||
class="form__button form__button--outlined"
|
||||
>
|
||||
{{ __('common.cancel') }}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
@endsection
|
||||
@@ -0,0 +1,365 @@
|
||||
@extends('layout.with-main')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('staff.dashboard.index') }}" class="breadcrumb__link">
|
||||
{{ __('staff.staff-dashboard') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('staff.upload_contests.index') }}" class="breadcrumb__link">
|
||||
{{ __('common.upload') }} {{ __('common.contests') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumbV2">
|
||||
{{ $uploadContest->name }}
|
||||
</li>
|
||||
<li class="breadcrumb--active">
|
||||
{{ __('common.edit') }}
|
||||
</li>
|
||||
@endsection
|
||||
|
||||
@section('page', 'page__staff-upload-contest--edit')
|
||||
|
||||
@section('main')
|
||||
<section class="panelV2">
|
||||
<h2 class="panel__heading">{{ __('common.edit') }} {{ __('common.contest') }}</h2>
|
||||
<form
|
||||
class="dialog__form"
|
||||
method="POST"
|
||||
action="{{ route('staff.upload_contests.update', ['uploadContest' => $uploadContest]) }}"
|
||||
>
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="name"
|
||||
class="form__text"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
name="name"
|
||||
required
|
||||
value="{{ $uploadContest->name }}"
|
||||
/>
|
||||
<label class="form__label form__label--floating" for="name">
|
||||
{{ __('common.name') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<textarea id="description" class="form__textarea" name="description" required>
|
||||
{{ $uploadContest->description }}</textarea
|
||||
>
|
||||
<label class="form__label form__label--floating" for="description">
|
||||
{{ __('common.description') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="icon"
|
||||
class="form__text"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
name="icon"
|
||||
required
|
||||
value="{{ $uploadContest->icon }}"
|
||||
/>
|
||||
<label class="form__label form__label--floating" for="icon">
|
||||
{{ __('common.icon') }}
|
||||
</label>
|
||||
</p>
|
||||
<div class="form__group--horizontal">
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="starts_at"
|
||||
class="form__text"
|
||||
name="starts_at"
|
||||
type="date"
|
||||
value="{{ $uploadContest->starts_at->format('Y-m-d') }}"
|
||||
required
|
||||
/>
|
||||
<label class="form__label form__label--floating" for="starts_at">
|
||||
{{ __('common.starts-at') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="ends_at"
|
||||
class="form__text"
|
||||
name="ends_at"
|
||||
type="date"
|
||||
value="{{ $uploadContest->ends_at->format('Y-m-d') }}"
|
||||
required
|
||||
/>
|
||||
<label class="form__label form__label--floating" for="ends_at">
|
||||
{{ __('common.ends-at') }}
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
<p class="form__group">
|
||||
<input type="hidden" name="active" value="0" />
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form__checkbox"
|
||||
id="active"
|
||||
name="active"
|
||||
value="1"
|
||||
@checked($uploadContest->active)
|
||||
/>
|
||||
<label class="form__label" for="active">{{ __('common.active') }}?</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<button class="form__button form__button--filled" wire:click="store">
|
||||
{{ __('common.save') }}
|
||||
</button>
|
||||
<button
|
||||
formmethod="dialog"
|
||||
formnovalidate
|
||||
class="form__button form__button--outlined"
|
||||
>
|
||||
{{ __('common.cancel') }}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
<section class="panelV2">
|
||||
<header class="panel__header">
|
||||
<h2 class="panel__heading">{{ __('contest.prizes') }}</h2>
|
||||
<div class="panel__actions">
|
||||
<div class="panel__action" x-data="dialog">
|
||||
<button class="form__button form__button--text" x-bind="showDialog">
|
||||
{{ __('common.add') }}
|
||||
</button>
|
||||
<dialog class="dialog" x-bind="dialogElement">
|
||||
<h3 class="dialog__heading">{{ __('event.add-prize') }}</h3>
|
||||
<form
|
||||
class="dialog__form"
|
||||
method="POST"
|
||||
action="{{ route('staff.upload_contests.prizes.store', ['uploadContest' => $uploadContest]) }}"
|
||||
x-bind="dialogForm"
|
||||
>
|
||||
@csrf
|
||||
<input
|
||||
type="hidden"
|
||||
name="contest_id"
|
||||
value="{{ $uploadContest->id }}"
|
||||
/>
|
||||
<p class="form__group">
|
||||
<select name="type" id="type" class="form__select" required>
|
||||
<option hidden disabled selected value=""></option>
|
||||
<option value="bon">{{ __('bon.bon') }}</option>
|
||||
<option value="fl_tokens">{{ __('common.fl_tokens') }}</option>
|
||||
</select>
|
||||
<label class="form__label form__label--floating" for="type">
|
||||
{{ __('common.type') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="amount"
|
||||
class="form__text"
|
||||
inputmode="numeric"
|
||||
name="amount"
|
||||
pattern="[0-9]*"
|
||||
placeholder=" "
|
||||
required
|
||||
type="text"
|
||||
/>
|
||||
<label class="form__label form__label--floating" for="amount">
|
||||
{{ __('common.amount') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="position"
|
||||
class="form__text"
|
||||
inputmode="numeric"
|
||||
name="position"
|
||||
pattern="[0-9]*"
|
||||
placeholder=" "
|
||||
required
|
||||
type="text"
|
||||
/>
|
||||
<label class="form__label form__label--floating" for="position">
|
||||
{{ __('common.position') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<button class="form__button form__button--filled">
|
||||
{{ __('common.add') }}
|
||||
</button>
|
||||
<button
|
||||
formmethod="dialog"
|
||||
formnovalidate
|
||||
class="form__button form__button--outlined"
|
||||
>
|
||||
{{ __('common.cancel') }}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</dialog>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="data-table-wrapper">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<th>{{ __('common.position') }}</th>
|
||||
<th>{{ __('common.type') }}</th>
|
||||
<th>{{ __('common.amount') }}</th>
|
||||
<th>{{ __('common.actions') }}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($prizes as $prize)
|
||||
<tr>
|
||||
<td>{{ $prize->position }}</td>
|
||||
<td>
|
||||
@switch($prize->type)
|
||||
@case('bon')
|
||||
{{ __('bon.bon') }}
|
||||
|
||||
@break
|
||||
@case('fl_tokens')
|
||||
{{ __('common.fl_tokens') }}
|
||||
|
||||
@break
|
||||
@endswitch
|
||||
</td>
|
||||
<td>{{ $prize->amount }}</td>
|
||||
<td>
|
||||
<menu class="data-table__actions">
|
||||
<li class="data-table__action" x-data="dialog">
|
||||
<button
|
||||
class="form__button form__button--text"
|
||||
x-bind="showDialog"
|
||||
>
|
||||
{{ __('common.edit') }}
|
||||
</button>
|
||||
<dialog class="dialog" x-bind="dialogElement">
|
||||
<h3 class="dialog__heading">
|
||||
{{ __('event.edit-prize') }}
|
||||
</h3>
|
||||
<form
|
||||
class="dialog__form"
|
||||
method="POST"
|
||||
action="{{ route('staff.upload_contests.prizes.update', ['uploadContest' => $uploadContest, 'prize' => $prize]) }}"
|
||||
x-bind="dialogForm"
|
||||
>
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<input
|
||||
type="hidden"
|
||||
name="contest_id"
|
||||
value="{{ $uploadContest->id }}"
|
||||
/>
|
||||
<p class="form__group">
|
||||
<select
|
||||
name="type"
|
||||
id="type"
|
||||
class="form__select"
|
||||
required
|
||||
>
|
||||
<option
|
||||
value="bon"
|
||||
@selected($uploadContest->type === 'bon')
|
||||
>
|
||||
{{ __('bon.bon') }}
|
||||
</option>
|
||||
<option
|
||||
value="fl_tokens"
|
||||
@selected($uploadContest->type === 'fl_tokens')
|
||||
>
|
||||
{{ __('common.fl_tokens') }}
|
||||
</option>
|
||||
</select>
|
||||
<label
|
||||
class="form__label form__label--floating"
|
||||
for="type"
|
||||
>
|
||||
{{ __('common.type') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="amount"
|
||||
class="form__text"
|
||||
inputmode="numeric"
|
||||
name="amount"
|
||||
pattern="[0-9]*"
|
||||
placeholder=" "
|
||||
required
|
||||
type="text"
|
||||
value="{{ $prize->amount }}"
|
||||
/>
|
||||
<label
|
||||
class="form__label form__label--floating"
|
||||
for="min"
|
||||
>
|
||||
{{ __('common.amount') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<input
|
||||
id="position"
|
||||
class="form__text"
|
||||
inputmode="numeric"
|
||||
name="position"
|
||||
pattern="[0-9.]*"
|
||||
placeholder=" "
|
||||
required
|
||||
type="text"
|
||||
value="{{ $prize->position }}"
|
||||
/>
|
||||
<label
|
||||
class="form__label form__label--floating"
|
||||
for="position"
|
||||
>
|
||||
{{ __('common.position') }}
|
||||
</label>
|
||||
</p>
|
||||
<p class="form__group">
|
||||
<button
|
||||
class="form__button form__button--filled"
|
||||
>
|
||||
{{ __('common.edit') }}
|
||||
</button>
|
||||
<button
|
||||
formmethod="dialog"
|
||||
formnovalidate
|
||||
class="form__button form__button--outlined"
|
||||
>
|
||||
{{ __('common.cancel') }}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</dialog>
|
||||
</li>
|
||||
<li class="data-table__action">
|
||||
<form
|
||||
action="{{ route('staff.upload_contests.prizes.destroy', ['uploadContest' => $uploadContest, 'prize' => $prize]) }}"
|
||||
method="POST"
|
||||
x-data="confirmation"
|
||||
>
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button
|
||||
x-on:click.prevent="confirmAction"
|
||||
class="form__button form__button--text"
|
||||
data-b64-deletion-message="{{ base64_encode('Are you sure you want to remove this prize (Type: ' . $prize->type . ', Amount: ' . $prize->amount . ') from this contest (.' . $uploadContest->name . ')?') }}"
|
||||
>
|
||||
{{ __('common.delete') }}
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</menu>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="4">{{ __('event.no-prizes') }}</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
@endsection
|
||||
@@ -0,0 +1,124 @@
|
||||
@extends('layout.with-main')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('staff.dashboard.index') }}" class="breadcrumb__link">
|
||||
{{ __('staff.staff-dashboard') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumb--active">{{ __('common.upload') }} {{ __('common.contests') }}</li>
|
||||
@endsection
|
||||
|
||||
@section('page', 'page__staff-upload-contest--index')
|
||||
|
||||
@section('main')
|
||||
<section class="panelV2">
|
||||
<header class="panel__header">
|
||||
<h2 class="panel__heading">{{ __('common.upload') }} {{ __('common.contests') }}</h2>
|
||||
<div class="panel__actions">
|
||||
<div class="panel__action">
|
||||
<a
|
||||
class="form__button form__button--text"
|
||||
href="{{ route('staff.upload_contests.create') }}"
|
||||
>
|
||||
{{ __('common.add') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="data-table-wrapper">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('common.name') }}</th>
|
||||
<th>{{ __('common.starts-at') }}</th>
|
||||
<th>{{ __('common.ends-at') }}</th>
|
||||
<th>{{ __('common.active') }}</th>
|
||||
<th>{{ __('common.awarded') }}</th>
|
||||
<th>{{ __('common.actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($uploadContests as $uploadContest)
|
||||
<tr>
|
||||
<td>
|
||||
<a
|
||||
href="{{ route('staff.upload_contests.edit', ['uploadContest' => $uploadContest]) }}"
|
||||
>
|
||||
{{ $uploadContest->name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<time
|
||||
datetime="{{ $uploadContest->starts_at }}"
|
||||
title="{{ $uploadContest->starts_at }}"
|
||||
>
|
||||
{{ $uploadContest->starts_at->format('Y-m-d') }}
|
||||
</time>
|
||||
</td>
|
||||
<td>
|
||||
<time
|
||||
datetime="{{ $uploadContest->ends_at }}"
|
||||
title="{{ $uploadContest->ends_at }}"
|
||||
>
|
||||
{{ $uploadContest->ends_at->format('Y-m-d') }}
|
||||
</time>
|
||||
</td>
|
||||
<td>
|
||||
@if ($uploadContest->active)
|
||||
<i
|
||||
class="{{ config('other.font-awesome') }} fa-check text-green"
|
||||
></i>
|
||||
@else
|
||||
<i
|
||||
class="{{ config('other.font-awesome') }} fa-times text-red"
|
||||
></i>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($uploadContest->awarded)
|
||||
<i
|
||||
class="{{ config('other.font-awesome') }} fa-check text-green"
|
||||
></i>
|
||||
@else
|
||||
<i
|
||||
class="{{ config('other.font-awesome') }} fa-times text-red"
|
||||
></i>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<menu class="data-table__actions">
|
||||
<li class="data-table__action">
|
||||
<a
|
||||
href="{{ route('staff.upload_contests.edit', ['uploadContest' => $uploadContest]) }}"
|
||||
class="form__button form__button--text"
|
||||
>
|
||||
{{ __('common.edit') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="data-table__action">
|
||||
<form
|
||||
action="{{ route('staff.upload_contests.destroy', ['uploadContest' => $uploadContest]) }}"
|
||||
method="POST"
|
||||
x-data="confirmation"
|
||||
>
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button
|
||||
x-on:click.prevent="confirmAction"
|
||||
data-b64-deletion-message="{{ base64_encode('Are you sure you want to delete this upload contest: ' . $uploadContest->name . '?') }}"
|
||||
class="form__button form__button--text"
|
||||
>
|
||||
{{ __('common.delete') }}
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</menu>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
@endsection
|
||||
@@ -183,6 +183,19 @@
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
@foreach ($uploadContests as $uploadContest)
|
||||
<li>
|
||||
<a
|
||||
href="{{ route('upload_contests.show', ['uploadContest' => $uploadContest]) }}"
|
||||
>
|
||||
<i
|
||||
class="{{ config('other.font-awesome') }} {{ $uploadContest->icon }}"
|
||||
></i>
|
||||
{{ $uploadContest->name }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
<li>
|
||||
<a href="{{ route('subtitles.index') }}">
|
||||
<i class="{{ config('other.font-awesome') }} fa-closed-captioning"></i>
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
@extends('layout.with-main')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb--active">{{ __('common.upload') }} {{ __('common.contests') }}</li>
|
||||
@endsection
|
||||
|
||||
@section('page', 'page__upload-contest--index')
|
||||
|
||||
@section('main')
|
||||
<section class="panelV2">
|
||||
<h2 class="panel__heading">{{ __('common.upload') }} {{ __('common.contests') }}</h2>
|
||||
<div class="data-table-wrapper">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('common.name') }}</th>
|
||||
<th>{{ __('common.starts-at') }}</th>
|
||||
<th>{{ __('common.ends-at') }}</th>
|
||||
<th>{{ __('common.active') }}</th>
|
||||
<th>{{ __('common.awarded') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($uploadContests as $uploadContest)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ route('events.show', ['event' => $uploadContest]) }}">
|
||||
{{ $uploadContest->name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<time
|
||||
datetime="{{ $uploadContest->starts_at }}"
|
||||
title="{{ $uploadContest->starts_at }}"
|
||||
>
|
||||
{{ $uploadContest->starts_at->startOfDay() }}
|
||||
</time>
|
||||
</td>
|
||||
<td>
|
||||
<time
|
||||
datetime="{{ $uploadContest->ends_at }}"
|
||||
title="{{ $uploadContest->ends_at }}"
|
||||
>
|
||||
{{ $uploadContest->ends_at->endOfDay() }}
|
||||
</time>
|
||||
</td>
|
||||
<td>
|
||||
@if ($uploadContest->active)
|
||||
<i
|
||||
class="{{ config('other.font-awesome') }} fa-check text-green"
|
||||
></i>
|
||||
@else
|
||||
<i
|
||||
class="{{ config('other.font-awesome') }} fa-times text-red"
|
||||
></i>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($uploadContest->awarded)
|
||||
<i
|
||||
class="{{ config('other.font-awesome') }} fa-check text-green"
|
||||
></i>
|
||||
@else
|
||||
<i
|
||||
class="{{ config('other.font-awesome') }} fa-times text-red"
|
||||
></i>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
@endsection
|
||||
@@ -0,0 +1,88 @@
|
||||
@extends('layout.with-main-and-sidebar')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('upload_contests.index') }}" class="breadcrumb__link">
|
||||
{{ __('common.upload') }} {{ __('common.contests') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumb--active">
|
||||
{{ $uploadContest->name }}
|
||||
</li>
|
||||
@endsection
|
||||
|
||||
@section('page', 'page__upload-contest--show')
|
||||
|
||||
@section('main')
|
||||
<section class="panelV2">
|
||||
<h2 class="panel__heading">{{ $uploadContest->name }}</h2>
|
||||
<div class="data-table-wrapper">
|
||||
<div class="data-table-wrapper">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>{{ __('common.user') }}</th>
|
||||
<th>
|
||||
{{ __('torrent.uploaded') }} (Non-{{ __('common.anonymous') }})
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($uploaders as $user)
|
||||
<tr>
|
||||
<td>{{ $loop->iteration }}</td>
|
||||
<td>
|
||||
<x-user-tag
|
||||
:user="$user->user"
|
||||
:anon="$user->user->privacy?->private_profile"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{{ $user->uploads }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@endsection
|
||||
|
||||
@section('sidebar')
|
||||
<section class="panelV2">
|
||||
<h2 class="panel__heading">{{ __('common.info') }}</h2>
|
||||
<dl class="key-value">
|
||||
<div class="key-value__group">
|
||||
<dt>{{ __('common.starts-at') }}</dt>
|
||||
<dd>
|
||||
<time datetime="{{ $uploadContest->starts_at->startOfDay() }}">
|
||||
{{ $uploadContest->starts_at->startOfDay() }}
|
||||
</time>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="key-value__group">
|
||||
<dt>{{ __('common.ends-at') }}</dt>
|
||||
<dd>
|
||||
<time datetime="{{ $uploadContest->ends_at->endOfDay() }}">
|
||||
{{ $uploadContest->ends_at->endOfDay() }}
|
||||
</time>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="key-value__group">
|
||||
<dt>{{ __('common.awarded') }}</dt>
|
||||
<dd>
|
||||
@if ($uploadContest->awarded)
|
||||
<i class="{{ config('other.font-awesome') }} fa-check text-green"></i>
|
||||
@else
|
||||
<i class="{{ config('other.font-awesome') }} fa-times text-red"></i>
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<div class="panel__body">
|
||||
{{ $uploadContest->description }}
|
||||
</div>
|
||||
</section>
|
||||
@endsection
|
||||
@@ -125,6 +125,14 @@ Route::middleware('language')->group(function (): void {
|
||||
});
|
||||
});
|
||||
|
||||
// Upload Contests
|
||||
Route::prefix('upload-contests')->name('upload_contests.')->group(function (): void {
|
||||
Route::get('/', [App\Http\Controllers\UploadContestController::class, 'index'])->name('index');
|
||||
Route::prefix('{uploadContest}')->group(function (): void {
|
||||
Route::get('/', [App\Http\Controllers\UploadContestController::class, 'show'])->name('show');
|
||||
});
|
||||
});
|
||||
|
||||
// RSS System
|
||||
Route::prefix('rss')->name('rss.')->group(function (): void {
|
||||
Route::get('/', [App\Http\Controllers\RssController::class, 'index'])->name('index');
|
||||
@@ -1070,6 +1078,25 @@ Route::middleware('language')->group(function (): void {
|
||||
Route::get('/', [App\Http\Controllers\Staff\UnregisteredInfoHashController::class, 'index'])->name('index');
|
||||
});
|
||||
|
||||
// Upload Contests
|
||||
Route::prefix('upload-contests')->name('upload_contests.')->group(function (): void {
|
||||
Route::get('/', [App\Http\Controllers\Staff\UploadContestController::class, 'index'])->name('index');
|
||||
Route::get('/create', [App\Http\Controllers\Staff\UploadContestController::class, 'create'])->name('create');
|
||||
Route::post('/', [App\Http\Controllers\Staff\UploadContestController::class, 'store'])->name('store');
|
||||
Route::prefix('{uploadContest}')->group(function (): void {
|
||||
Route::get('/edit', [App\Http\Controllers\Staff\UploadContestController::class, 'edit'])->name('edit');
|
||||
Route::patch('/', [App\Http\Controllers\Staff\UploadContestController::class, 'update'])->name('update');
|
||||
Route::delete('/', [App\Http\Controllers\Staff\UploadContestController::class, 'destroy'])->name('destroy');
|
||||
|
||||
// Prizes
|
||||
Route::prefix('prizes')->name('prizes.')->group(function (): void {
|
||||
Route::post('/', [App\Http\Controllers\Staff\UploadContestPrizeController::class, 'store'])->name('store');
|
||||
Route::patch('/{prize}', [App\Http\Controllers\Staff\UploadContestPrizeController::class, 'update'])->name('update');
|
||||
Route::delete('/{prize}', [App\Http\Controllers\Staff\UploadContestPrizeController::class, 'destroy'])->name('destroy');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// User Staff Notes
|
||||
Route::prefix('notes')->name('notes.')->group(function (): void {
|
||||
Route::get('/', [App\Http\Controllers\Staff\NoteController::class, 'index'])->name('index');
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
use App\Models\UploadContest;
|
||||
use App\Models\Group;
|
||||
use App\Models\User;
|
||||
|
||||
test('index upload contests returns an ok response', function (): void {
|
||||
$group = Group::factory()->create([
|
||||
'is_modo' => true,
|
||||
]);
|
||||
$user = User::factory()->create([
|
||||
'group_id' => $group->id,
|
||||
]);
|
||||
|
||||
$uploadContests = UploadContest::factory()->times(3)->create();
|
||||
|
||||
$response = $this->actingAs($user)->get(route('staff.upload_contests.index'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertViewIs('Staff.upload-contest.index');
|
||||
$response->assertViewHas('uploadContests');
|
||||
});
|
||||
|
||||
test('store a new upload contest returns an ok response', function (): void {
|
||||
$group = Group::factory()->create([
|
||||
'is_modo' => true,
|
||||
]);
|
||||
$user = User::factory()->create([
|
||||
'group_id' => $group->id,
|
||||
]);
|
||||
|
||||
$data = [
|
||||
'name' => 'Test Upload Contest',
|
||||
'description' => 'This is a test upload contest.',
|
||||
'icon' => 'fa-gamepad',
|
||||
'starts_at' => now()->subDays(7)->format('Y-m-d'),
|
||||
'ends_at' => now()->addDays(14)->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$response = $this->actingAs($user)->post(route('staff.upload_contests.store'), $data);
|
||||
|
||||
$response->assertRedirect(route('staff.upload_contests.index'));
|
||||
$this->assertDatabaseHas('upload_contests', $data);
|
||||
});
|
||||
|
||||
test('update an upload contest returns an ok response', function (): void {
|
||||
$group = Group::factory()->create([
|
||||
'is_modo' => true,
|
||||
]);
|
||||
$user = User::factory()->create([
|
||||
'group_id' => $group->id,
|
||||
]);
|
||||
|
||||
$uploadContest = UploadContest::factory()->create();
|
||||
|
||||
$data = [
|
||||
'name' => 'Updated Test Upload Contest',
|
||||
'description' => 'This is an updated test upload contest.',
|
||||
'icon' => 'fa-gamepad',
|
||||
'active' => 1,
|
||||
'starts_at' => now()->subDays(7)->format('Y-m-d'),
|
||||
'ends_at' => now()->addDays(14)->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$response = $this->actingAs($user)->patch(route('staff.upload_contests.update', ['uploadContest' => $uploadContest]), $data);
|
||||
|
||||
$response->assertRedirect(route('staff.upload_contests.index'));
|
||||
$this->assertDatabaseHas('upload_contests', $data);
|
||||
});
|
||||
|
||||
test('destroy an upload contest returns an ok response', function (): void {
|
||||
$group = Group::factory()->create([
|
||||
'is_modo' => true,
|
||||
]);
|
||||
$user = User::factory()->create([
|
||||
'group_id' => $group->id,
|
||||
]);
|
||||
|
||||
$uploadContest = UploadContest::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->delete(route('staff.upload_contests.destroy', $uploadContest));
|
||||
$response->assertRedirect(route('staff.upload_contests.index'));
|
||||
$this->assertDatabaseMissing('upload_contests', ['id' => $uploadContest->id]);
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
use App\Models\UploadContest;
|
||||
use App\Models\Torrent;
|
||||
use App\Models\User;
|
||||
|
||||
test('show an upload contest returns an ok response', function (): void {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$uploader1 = User::factory()->create();
|
||||
$uploader2 = User::factory()->create();
|
||||
$uploader3 = User::factory()->create();
|
||||
|
||||
$torrentsUploader1 = Torrent::factory()->times(3)->create([
|
||||
'user_id' => $uploader1->id,
|
||||
'anon' => false,
|
||||
'created_at' => now(),
|
||||
]);
|
||||
$torrentsUploader2 = Torrent::factory()->times(6)->create([
|
||||
'user_id' => $uploader2->id,
|
||||
'anon' => false,
|
||||
'created_at' => now(),
|
||||
]);
|
||||
$torrentsUploader3 = Torrent::factory()->times(1)->create([
|
||||
'user_id' => $uploader3->id,
|
||||
'anon' => false,
|
||||
'created_at' => now(),
|
||||
]);
|
||||
|
||||
$uploadContest = UploadContest::factory()->create([
|
||||
'active' => true,
|
||||
'awarded' => false,
|
||||
'starts_at' => now()->subDays(2)->format('Y-m-d'),
|
||||
'ends_at' => now()->addDays(2)->format('Y-m-d'),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->get(route('upload_contests.show', $uploadContest));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertViewIs('upload-contest.show');
|
||||
$response->assertViewHas('uploadContest', $uploadContest);
|
||||
|
||||
$uploaders = $response->viewData('uploaders');
|
||||
$this->assertCount(3, $uploaders);
|
||||
$this->assertEquals($uploader2->id, $uploaders[0]->user_id);
|
||||
$this->assertEquals($uploader1->id, $uploaders[1]->user_id);
|
||||
$this->assertEquals($uploader3->id, $uploaders[2]->user_id);
|
||||
});
|
||||
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Obi-wana
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
use App\Console\Commands\AutoRewardUploadContestPrize;
|
||||
use App\Models\UploadContest;
|
||||
use App\Models\Torrent;
|
||||
use App\Models\User;
|
||||
|
||||
/**
|
||||
* @see AutoRewardUploadContestPrize
|
||||
*/
|
||||
it('runs successfully', function (): void {
|
||||
$this->artisan(AutoRewardUploadContestPrize::class)
|
||||
->assertExitCode(0)
|
||||
->run();
|
||||
});
|
||||
|
||||
it('rewards the top competitors in active upload contests', function (): void {
|
||||
$uploader1 = User::factory()->create([
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 0,
|
||||
]);
|
||||
$uploader2 = User::factory()->create([
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 0,
|
||||
]);
|
||||
$uploader3 = User::factory()->create([
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 0,
|
||||
]);
|
||||
|
||||
// Uploads for each user
|
||||
$torrentsUploader1 = Torrent::factory()->times(3)->create([
|
||||
'user_id' => $uploader1->id,
|
||||
'anon' => false,
|
||||
'created_at' => now()->subDays(3),
|
||||
]);
|
||||
$torrentsUploader2 = Torrent::factory()->times(6)->create([
|
||||
'user_id' => $uploader2->id,
|
||||
'anon' => false,
|
||||
'created_at' => now()->subDays(3),
|
||||
]);
|
||||
$torrentsUploader3 = Torrent::factory()->times(1)->create([
|
||||
'user_id' => $uploader3->id,
|
||||
'anon' => false,
|
||||
'created_at' => now()->subDays(3),
|
||||
]);
|
||||
|
||||
// Contest
|
||||
$uploadContest = UploadContest::factory()->create([
|
||||
'active' => true,
|
||||
'awarded' => false,
|
||||
'starts_at' => now()->subDays(7)->format('Y-m-d'),
|
||||
'ends_at' => now()->subDays(1)->format('Y-m-d'),
|
||||
]);
|
||||
|
||||
// Prizes for 1st place
|
||||
$uploadContest->prizes()->create([
|
||||
'position' => 1,
|
||||
'amount' => 100,
|
||||
'type' => 'bon',
|
||||
]);
|
||||
$uploadContest->prizes()->create([
|
||||
'position' => 1,
|
||||
'amount' => 10,
|
||||
'type' => 'fl_tokens',
|
||||
]);
|
||||
// Prizes for 2nd place
|
||||
$uploadContest->prizes()->create([
|
||||
'position' => 2,
|
||||
'amount' => 5,
|
||||
'type' => 'fl_tokens',
|
||||
]);
|
||||
|
||||
// Run command
|
||||
$this->artisan(AutoRewardUploadContestPrize::class)->assertExitCode(0);
|
||||
|
||||
// Assert
|
||||
$this->assertDatabaseHas('upload_contests', [
|
||||
'id' => $uploadContest->id,
|
||||
'awarded' => 1,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('upload_contest_winners', [
|
||||
'upload_contest_id' => $uploadContest->id,
|
||||
'user_id' => $uploader2->id,
|
||||
'place_number' => 1,
|
||||
'uploads' => 6,
|
||||
]);
|
||||
$this->assertDatabaseHas('upload_contest_winners', [
|
||||
'upload_contest_id' => $uploadContest->id,
|
||||
'user_id' => $uploader1->id,
|
||||
'place_number' => 2,
|
||||
'uploads' => 3,
|
||||
]);
|
||||
$this->assertDatabaseHas('upload_contest_winners', [
|
||||
'upload_contest_id' => $uploadContest->id,
|
||||
'user_id' => $uploader3->id,
|
||||
'place_number' => 3,
|
||||
'uploads' => 1,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $uploader1->id,
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 5,
|
||||
]);
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $uploader2->id,
|
||||
'seedbonus' => 100,
|
||||
'fl_tokens' => 10,
|
||||
]);
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $uploader3->id,
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 0,
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles ties between competitors in active upload contests', function (): void {
|
||||
$uploader1 = User::factory()->create([
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 0,
|
||||
]);
|
||||
$uploader2 = User::factory()->create([
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 0,
|
||||
]);
|
||||
$uploader3 = User::factory()->create([
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 0,
|
||||
]);
|
||||
$uploader4 = User::factory()->create([
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 0,
|
||||
]);
|
||||
|
||||
// Uploads for each user
|
||||
$torrentsUploader1 = Torrent::factory()->times(6)->create([
|
||||
'user_id' => $uploader1->id,
|
||||
'anon' => false,
|
||||
'created_at' => now()->subDays(3),
|
||||
]);
|
||||
$torrentsUploader2 = Torrent::factory()->times(6)->create([
|
||||
'user_id' => $uploader2->id,
|
||||
'anon' => false,
|
||||
'created_at' => now()->subDays(4),
|
||||
]);
|
||||
$torrentsUploader3 = Torrent::factory()->times(1)->create([
|
||||
'user_id' => $uploader3->id,
|
||||
'anon' => false,
|
||||
'created_at' => now()->subDays(4),
|
||||
]);
|
||||
$torrentsUploader4 = Torrent::factory()->times(1)->create([
|
||||
'user_id' => $uploader4->id,
|
||||
'anon' => false,
|
||||
'created_at' => now()->subDays(3),
|
||||
]);
|
||||
|
||||
// Contest
|
||||
$uploadContest = UploadContest::factory()->create([
|
||||
'active' => true,
|
||||
'awarded' => false,
|
||||
'starts_at' => now()->subDays(7)->format('Y-m-d'),
|
||||
'ends_at' => now()->subDays(1)->format('Y-m-d'),
|
||||
]);
|
||||
|
||||
// Prizes for 1st place
|
||||
$uploadContest->prizes()->create([
|
||||
'position' => 1,
|
||||
'amount' => 100,
|
||||
'type' => 'bon',
|
||||
]);
|
||||
$uploadContest->prizes()->create([
|
||||
'position' => 1,
|
||||
'amount' => 10,
|
||||
'type' => 'fl_tokens',
|
||||
]);
|
||||
// Prizes for 2nd place
|
||||
$uploadContest->prizes()->create([
|
||||
'position' => 2,
|
||||
'amount' => 5,
|
||||
'type' => 'fl_tokens',
|
||||
]);
|
||||
// Prizes for 3rd place
|
||||
$uploadContest->prizes()->create([
|
||||
'position' => 3,
|
||||
'amount' => 1,
|
||||
'type' => 'fl_tokens',
|
||||
]);
|
||||
|
||||
// Run command
|
||||
$this->artisan(AutoRewardUploadContestPrize::class)->assertExitCode(0);
|
||||
|
||||
// Assert
|
||||
$this->assertDatabaseHas('upload_contests', [
|
||||
'id' => $uploadContest->id,
|
||||
'awarded' => 1,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('upload_contest_winners', [
|
||||
'upload_contest_id' => $uploadContest->id,
|
||||
'user_id' => $uploader2->id,
|
||||
'place_number' => 1,
|
||||
'uploads' => 6,
|
||||
]);
|
||||
$this->assertDatabaseHas('upload_contest_winners', [
|
||||
'upload_contest_id' => $uploadContest->id,
|
||||
'user_id' => $uploader1->id,
|
||||
'place_number' => 2,
|
||||
'uploads' => 6,
|
||||
]);
|
||||
$this->assertDatabaseHas('upload_contest_winners', [
|
||||
'upload_contest_id' => $uploadContest->id,
|
||||
'user_id' => $uploader3->id,
|
||||
'place_number' => 3,
|
||||
'uploads' => 1,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $uploader1->id,
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 5,
|
||||
]);
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $uploader2->id,
|
||||
'seedbonus' => 100,
|
||||
'fl_tokens' => 10,
|
||||
]);
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $uploader3->id,
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 1,
|
||||
]);
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $uploader4->id,
|
||||
'seedbonus' => 0,
|
||||
'fl_tokens' => 0,
|
||||
]);
|
||||
});
|
||||
Reference in New Issue
Block a user