Initial commit

This commit is contained in:
root
2026-05-09 17:28:23 +02:00
commit 9d73f82529
5575 changed files with 281989 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
<?php
namespace App\Http\Controllers\Community;
use App\Http\Controllers\Controller;
use App\Models\Game\Player\UserCurrency;
use App\Models\Game\Player\UserSetting;
use App\Models\User;
use App\Services\Community\StaffService;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
class LeaderboardController extends Controller
{
/** @var array<int, int> */
protected array $staffIds = [];
public function __construct(private readonly StaffService $staffService)
{
$this->staffIds = $this->staffService->fetchEmployeeIds();
}
public function __invoke(): View
{
$topCredits = Cache::remember('leaderboard_credits', 60, fn () => User::query()
->whereNotIn('id', $this->staffIds)
->orderByDesc('credits')
->take(9)
->get(['id', 'username', 'look', 'credits']));
$duckets = Cache::remember('leaderboard_duckets', 60, fn () => UserCurrency::query()
->whereNotIn('user_id', $this->staffIds)
->where('type', 0)
->orderByDesc('amount')
->take(9)
->with(['user' => fn ($q) => $q->select('id', 'username', 'look')])
->get());
$diamonds = Cache::remember('leaderboard_diamonds', 60, fn () => UserCurrency::query()
->whereNotIn('user_id', $this->staffIds)
->where('type', 5)
->orderByDesc('amount')
->take(9)
->with(['user' => fn ($q) => $q->select('id', 'username', 'look')])
->get());
$mostOnline = Cache::remember('leaderboard_online', 60, fn () => $this->retrieveSettings('online_time'));
$respectsReceived = Cache::remember('leaderboard_respects', 60, fn () => $this->retrieveSettings('respects_received'));
$achievementScores = Cache::remember('leaderboard_achievements', 60, fn () => $this->retrieveSettings('achievement_score'));
return view('leaderboard', [
'credits' => $topCredits,
'duckets' => $duckets,
'diamonds' => $diamonds,
'mostOnline' => $mostOnline,
'respectsReceived' => $respectsReceived,
'achievementScores' => $achievementScores,
]);
}
private function retrieveSettings($column)
{
return UserSetting::query()
->select(['user_id', $column])
->whereNotIn('user_id', $this->staffIds)
->orderByDesc($column)
->take(9)
->with('user:id,username,look')
->get();
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers\Community;
use App\Http\Controllers\Controller;
use App\Services\Community\CameraService;
use Illuminate\View\View;
class PhotosController extends Controller
{
public function __construct(private readonly CameraService $cameraService) {}
public function __invoke(): View
{
return view('community.photos', [
'photos' => $this->cameraService->fetchPhotos(true),
]);
}
}
+608
View File
@@ -0,0 +1,608 @@
<?php
namespace App\Http\Controllers\Community;
use App\Http\Controllers\Controller;
use App\Models\Miscellaneous\WebsiteSetting;
use App\Models\RadioApplication;
use App\Models\RadioBanner;
use App\Models\RadioHistory;
use App\Models\RadioRank;
use App\Models\RadioSchedule;
use App\Models\RadioShout;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\View\View;
class RadioController extends Controller
{
private ?bool $azureCastDetected = null;
/** @var array<mixed> */
private array $settingsCache = [];
/**
* Get multiple settings at once (batched query)
*
* @param array<string> $keys
*
* @return array<string|null>
*/
private function getSettings(array $keys): array
{
$cacheKey = 'radio_settings_' . md5(implode(',', $keys));
return Cache::remember($cacheKey, 60, function () use ($keys): array {
/** @var Collection $collection */
$collection = WebsiteSetting::whereIn('key', $keys)
->pluck('value', 'key');
return $collection->all();
});
}
/**
* Get single setting with caching
*/
private function getSetting(string $key, mixed $default = null): mixed
{
if (! isset($this->settingsCache[$key])) {
$this->settingsCache[$key] = Cache::remember("setting_{$key}", 60, function () use ($key): mixed {
/** @var WebsiteSetting|null $setting */
$setting = WebsiteSetting::where('key', $key)->first();
return $setting?->value;
});
}
return $this->settingsCache[$key] ?? $default;
}
public function index(): View
{
// Batch load all radio settings at once
$settings = $this->getSettings([
'radio_enabled',
'radio_stream_url',
'radio_current_dj_id',
]);
if (! (bool) ($settings['radio_enabled'] ?? false)) {
return view('community.radio.disabled');
}
// Load all data with single queries each
$ranks = Cache::remember('radio_ranks', 300, fn () => RadioRank::all());
$banners = Cache::remember('radio_banners_active', 60, fn () => RadioBanner::with('user:id,username,look')
->active()
->ordered()
->take(10)
->get());
$todaySchedule = Cache::remember('radio_schedule_today', 60, fn () => RadioSchedule::with('user:id,username,look')
->active()
->today()
->orderBy('start_time')
->get());
$currentDJ = $this->getCurrentDJFromSchedule();
$streamUrl = $this->formatStreamUrl($settings['radio_stream_url'] ?? '');
// Cache stream status check (30 seconds)
$isOnline = Cache::remember('radio_stream_status', 30, fn () => $this->checkStreamOnline($streamUrl));
return view('community.radio.index', ['ranks' => $ranks, 'banners' => $banners, 'todaySchedule' => $todaySchedule, 'currentDJ' => $currentDJ, 'isOnline' => $isOnline, 'streamUrl' => $streamUrl]);
}
public function rooster(): View
{
$schedule = Cache::remember('radio_schedule_all', 300, fn () => RadioSchedule::with('user:id,username,look')
->active()
->ordered()
->get()
->groupBy('day'));
return view('community.radio.rooster', ['schedule' => $schedule]);
}
public function shouts(): RedirectResponse|View
{
if (! (bool) $this->getSetting('radio_shouts_enabled')) {
return redirect()->route('radio.index')->with('error', __('radio.shouts_disabled'));
}
return view('community.radio.shouts');
}
public function apply(): RedirectResponse|View
{
if (! (bool) $this->getSetting('radio_applications_enabled')) {
return redirect()->route('radio.index')->with('error', __('radio.applications_closed'));
}
$userId = auth()->id();
$hasPendingApplication = $userId && RadioApplication::where('user_id', $userId)
->where('status', 'pending')
->exists();
$ranks = Cache::remember('radio_ranks_accepting', 300, fn () => RadioRank::where('accepts_applications', true)->get());
return view('community.radio.apply', ['ranks' => $ranks, 'hasPendingApplication' => $hasPendingApplication]);
}
public function storeApplication(Request $request): RedirectResponse
{
if (! (bool) $this->getSetting('radio_applications_enabled')) {
return redirect()->route('radio.index')->with('error', __('radio.applications_closed'));
}
/** @var int|string $userId */
$userId = auth()->id();
$hasPendingApplication = RadioApplication::where('user_id', $userId)
->where('status', 'pending')
->exists();
if ($hasPendingApplication) {
return back()->withErrors([
'general' => __('radio.application_pending'),
])->withInput();
}
$validated = $request->validate([
'real_name' => ['required', 'string', 'max:255'],
'age' => ['required', 'integer', 'min:13', 'max:100'],
'availability' => ['required', 'string', 'min:10'],
'experience' => ['nullable', 'string'],
'motivation' => ['required', 'string', 'min:50'],
'music_style' => ['nullable', 'string', 'max:500'],
'discord_username' => ['nullable', 'string', 'max:255'],
'rank_id' => ['nullable', 'exists:radio_ranks,id'],
]);
// Check rank accepts applications (cached)
if ($validated['rank_id']) {
$rank = Cache::remember("radio_rank_{$validated['rank_id']}", 300, fn () => RadioRank::find($validated['rank_id']));
if (! $rank || ! $rank->accepts_applications) {
return back()->withErrors([
'rank_id' => __('radio.rank_not_accepting', ['rank' => $rank?->name ?? 'Deze rank']),
])->withInput();
}
}
RadioApplication::create([
'user_id' => $userId,
'real_name' => $validated['real_name'],
'age' => $validated['age'],
'availability' => $validated['availability'],
'experience' => $validated['experience'] ?? null,
'motivation' => $validated['motivation'],
'music_style' => $validated['music_style'] ?? null,
'discord_username' => $validated['discord_username'] ?? null,
'rank_id' => $validated['rank_id'] ?? null,
'status' => 'pending',
]);
return redirect()->route('radio.apply')->with('success', __('radio.application_sent'));
}
public function storeShout(Request $request): RedirectResponse
{
if (! (bool) $this->getSetting('radio_shouts_enabled')) {
return redirect()->route('radio.index')->with('error', __('radio.shouts_disabled'));
}
$validated = $request->validate([
'message' => ['required', 'string', 'max:280'],
]);
RadioShout::create([
'user_id' => auth()->id(),
'message' => $validated['message'],
]);
// Clear shouts cache
Cache::forget('radio_shouts_recent');
return redirect()->route('radio.shouts')->with('success', __('radio.shout_sent'));
}
public function nowPlaying(): JsonResponse
{
// Cache now playing for 10 seconds to reduce API calls
$nowPlaying = Cache::remember('radio_nowplaying', 10, fn () => $this->getNowPlaying());
return response()->json($nowPlaying);
}
public function listeners(): JsonResponse
{
// Cache listeners count for 30 seconds
$count = Cache::remember('radio_listeners', 30, fn () => $this->getListenersCount());
return response()->json(['count' => $count]);
}
public function currentDJ(): JsonResponse
{
$dj = $this->getCurrentDJFromSchedule();
return response()->json([
'dj' => $dj,
'is_live' => $dj !== null,
]);
}
private function getCurrentDJFromSchedule(): ?array
{
$manualDjId = $this->getSetting('radio_current_dj_id');
if (! empty($manualDjId)) {
$dj = Cache::remember("user_{$manualDjId}", 60, fn () => User::find($manualDjId));
if ($dj) {
return [
'username' => $dj->username,
'look' => $dj->look,
'show_name' => 'Live DJ',
'is_manual' => true,
];
}
}
$currentSlot = RadioSchedule::with('user:id,username,look')
->active()
->where('day', $this->getCurrentDay())
->whereTime('start_time', '<=', now()->format('H:i:s'))
->whereTime('end_time', '>=', now()->format('H:i:s'))
->first();
if ($currentSlot?->user) {
return [
'username' => $currentSlot->user->username,
'look' => $currentSlot->user->look,
'show_name' => $currentSlot->show_name,
'start_time' => $currentSlot->start_time->format('H:i'),
'end_time' => $currentSlot->end_time->format('H:i'),
'is_manual' => false,
];
}
return null;
}
private function getNowPlaying(): array
{
if (! (bool) $this->getSetting('radio_now_playing_enabled')) {
return ['enabled' => false, 'song' => null];
}
$apiUrl = $this->getSetting('radio_now_playing_api_url') ?: $this->getAzureCastApiUrl();
if (! $apiUrl) {
return ['enabled' => true, 'song' => null, 'artist' => null];
}
try {
$response = Http::timeout(5)->get($apiUrl);
if ($response->successful()) {
$data = $response->json();
if (isset($data['now_playing'])) {
$song = $data['now_playing']['song'] ?? $data['now_playing'];
return [
'enabled' => true,
'song' => $song['title'] ?? $song['text'] ?? null,
'artist' => $song['artist'] ?? null,
];
}
return [
'enabled' => true,
'song' => $data['song'] ?? $data['title'] ?? $data['now_playing'] ?? null,
'artist' => $data['artist'] ?? null,
];
}
} catch (\Exception) {
// Silent fail
}
return ['enabled' => true, 'song' => null, 'artist' => null];
}
private function getAzureCastApiUrl(): ?string
{
$baseUrl = $this->getSetting('radio_azurecast_base_url');
$stationId = $this->getSetting('radio_azurecast_station_id', '1');
if (! empty($baseUrl)) {
return rtrim((string) $baseUrl, '/') . '/api/nowplaying/' . $stationId;
}
$streamUrl = $this->getSetting('radio_stream_url');
if (! $streamUrl) {
return null;
}
$parsed = parse_url((string) $streamUrl);
if (! $parsed) {
return null;
}
$scheme = $parsed['scheme'] ?? 'https';
$host = $parsed['host'] ?? '';
return $scheme . '://' . $host . '/api/nowplaying/' . $stationId;
}
private function getListenersCount(): int
{
if (! (bool) $this->getSetting('radio_listeners_enabled')) {
return 0;
}
$apiUrl = $this->getSetting('radio_listeners_api_url') ?: $this->getAzureCastApiUrl();
if (! $apiUrl) {
return 0;
}
try {
$response = Http::timeout(5)->get($apiUrl);
if ($response->successful()) {
$data = $response->json();
return $data['listeners']['total']
?? $data['listeners']['current']
?? $data['listeners']
?? $data['count']
?? $data['total']
?? 0;
}
} catch (\Exception) {
// Silent fail
}
return 0;
}
private function getCurrentDay(): string
{
return strtolower(now()->format('l'));
}
private function formatStreamUrl(string $url): string
{
if ($url === '' || $url === '0') {
return $url;
}
$url = str_replace('http://', 'https://', $url);
if (preg_match('/^(https?:\/\/[^\/]+):(\d+)\/(.+)$/', $url, $matches)) {
$baseUrl = $matches[1];
$port = $matches[2];
$path = $matches[3];
if (in_array($port, ['8000', '8010', '8020', '8030', '8040', '8050'])) {
return $baseUrl . '/radio/' . $port . '/' . $path;
}
}
return $url;
}
private function checkStreamOnline(string $streamUrl): bool
{
if ($streamUrl === '' || $streamUrl === '0') {
return false;
}
try {
return Http::timeout(2)->withOptions(['verify' => false])->head($streamUrl)->successful();
} catch (\Exception) {
return false;
}
}
private function detectAzureCast(): bool
{
if ($this->azureCastDetected !== null) {
return $this->azureCastDetected;
}
$baseUrl = $this->getSetting('radio_azurecast_base_url');
if (! empty($baseUrl)) {
$this->azureCastDetected = true;
return true;
}
$streamUrl = $this->getSetting('radio_stream_url', '');
if (empty($streamUrl)) {
$this->azureCastDetected = false;
return false;
}
$parsed = parse_url((string) $streamUrl);
if (! $parsed) {
$this->azureCastDetected = false;
return false;
}
$scheme = $parsed['scheme'] ?? 'https';
$host = $parsed['host'] ?? '';
$testUrl = $scheme . '://' . $host . '/api/nowplaying';
try {
$response = Http::timeout(3)->get($testUrl);
if ($response->successful()) {
$data = $response->json();
if (is_array($data) && (isset($data[0]['station']) || isset($data['station']))) {
$this->azureCastDetected = true;
$stationId = $data[0]['station']['id'] ?? $data['station']['id'] ?? 1;
$detectedBaseUrl = $scheme . '://' . $host;
WebsiteSetting::updateOrCreate(
['key' => 'radio_azurecast_base_url'],
['value' => $detectedBaseUrl, 'comment' => 'Auto-detected AzureCast'],
);
WebsiteSetting::updateOrCreate(
['key' => 'radio_azurecast_station_id'],
['value' => $stationId, 'comment' => 'Auto-detected Station ID'],
);
WebsiteSetting::updateOrCreate(
['key' => 'radio_now_playing_enabled'],
['value' => '1', 'comment' => 'Auto-enabled'],
);
WebsiteSetting::updateOrCreate(
['key' => 'radio_listeners_enabled'],
['value' => '1', 'comment' => 'Auto-enabled'],
);
WebsiteSetting::updateOrCreate(
['key' => 'radio_show_current_dj'],
['value' => '1', 'comment' => 'Auto-enabled'],
);
WebsiteSetting::updateOrCreate(
['key' => 'radio_widget_enabled'],
['value' => '1', 'comment' => 'Auto-enabled'],
);
WebsiteSetting::updateOrCreate(
['key' => 'radio_widget_show_globally'],
['value' => '1', 'comment' => 'Auto-enabled'],
);
$this->settingsCache = [];
return true;
}
}
} catch (\Exception) {
// Not AzureCast
}
$this->azureCastDetected = false;
return false;
}
public function config(): JsonResponse
{
$settings = $this->getSettings([
'radio_enabled',
'radio_stream_url',
'radio_style',
'radio_now_playing_enabled',
'radio_listeners_enabled',
'radio_show_current_dj',
'radio_widget_enabled',
'radio_widget_show_globally',
'radio_widget_position',
]);
$streamUrl = $this->formatStreamUrl($settings['radio_stream_url'] ?? '');
$isAzurecast = $this->detectAzureCast();
return response()->json([
'enabled' => (bool) ($settings['radio_enabled'] ?? false),
'stream_url' => $streamUrl,
'style' => $settings['radio_style'] ?? 'dark',
'dj' => $this->getCurrentDJFromSchedule(),
'now_playing_enabled' => (bool) ($settings['radio_now_playing_enabled'] ?? false),
'listeners_enabled' => (bool) ($settings['radio_listeners_enabled'] ?? false),
'show_current_dj' => (bool) ($settings['radio_show_current_dj'] ?? false),
'widget_enabled' => (bool) ($settings['radio_widget_enabled'] ?? false),
'widget_show_globally' => (bool) ($settings['radio_widget_show_globally'] ?? false),
'widget_position' => $settings['radio_widget_position'] ?? 'bottom-right',
'is_azurecast' => $isAzurecast,
'azurecast_detected' => $isAzurecast,
]);
}
public function startSession(Request $request): JsonResponse
{
$userId = auth()->id();
$activeSession = RadioHistory::where('user_id', $userId)
->whereNull('ended_at')
->first();
if ($activeSession) {
return response()->json([
'error' => 'Je hebt al een actieve sessie',
'session_id' => $activeSession->id,
], 400);
}
$session = RadioHistory::create([
'user_id' => $userId,
'show_name' => $request->input('show_name'),
'started_at' => now(),
]);
return response()->json([
'message' => 'Sessie gestart',
'session_id' => $session->id,
]);
}
public function endSession(): JsonResponse
{
$userId = auth()->id();
$activeSession = RadioHistory::where('user_id', $userId)
->whereNull('ended_at')
->first();
if (! $activeSession) {
return response()->json([
'error' => 'Geen actieve sessie gevonden',
], 404);
}
$activeSession->endSession();
return response()->json([
'message' => 'Sessie beëindigd',
'duration' => $activeSession->duration,
]);
}
public function getShouts(): JsonResponse
{
if (! (bool) $this->getSetting('radio_shouts_enabled')) {
return response()->json([
'error' => 'Shouts zijn uitgeschakeld',
'shouts' => [],
], 403);
}
$shouts = Cache::remember('radio_shouts_recent', 30, fn () => RadioShout::with('user:id,username')
->orderBy('created_at', 'desc')
->take(50)
->get()
->map(fn ($shout) => [
'id' => $shout->id,
'username' => $shout->user?->username ?? 'Anoniem',
'message' => $shout->message,
'created_at' => $shout->created_at->diffForHumans(),
]));
return response()->json([
'shouts' => $shouts,
'total' => $shouts->count(),
]);
}
}
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Community;
use App\Http\Controllers\Controller;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class RadioLeaderboardController extends Controller
{
public function __invoke(Request $request)
{
$period = $request->get('period', 'all');
$users = match ($period) {
'weekly' => $this->getWeeklyLeaderboard(),
'monthly' => $this->getMonthlyLeaderboard(),
default => $this->getAllTimeLeaderboard(),
};
// Extract top 3 from already-fetched data
$topUserData = array_slice($users, 0, 3);
$topUserIds = array_column($topUserData, 'id');
$topUsers = User::whereIn('id', $topUserIds)
->get()
->keyBy('id');
return view('community.radio.leaderboard', ['users' => $users, 'topUsers' => $topUsers, 'period' => $period]);
}
private function getAllTimeLeaderboard(): array
{
return User::where('radio_points', '>', 0)
->orderBy('radio_points', 'desc')
->limit(100)
->get()
->map(fn ($user, $index) => [
'rank' => $index + 1,
'id' => $user->id,
'username' => $user->username,
'avatar' => $user->avatar ?? null,
'points' => $user->radio_points,
])
->toArray();
}
private function getWeeklyLeaderboard(): array
{
return $this->getPointsLeaderboard(now()->subWeek());
}
private function getMonthlyLeaderboard(): array
{
return $this->getPointsLeaderboard(now()->subMonth());
}
private function getPointsLeaderboard(Carbon $since): array
{
$points = DB::table('radio_listener_points')
->where('earned_at', '>=', $since)
->select('user_id', DB::raw('SUM(points) as total_points'))
->groupBy('user_id')
->orderBy('total_points', 'desc')
->limit(100)
->get();
$userIds = $points->pluck('user_id')->toArray();
$users = User::whereIn('id', $userIds)->get()->keyBy('id');
return $points->map(function ($item, $index) use ($users) {
$user = $users->get($item->user_id);
return [
'rank' => $index + 1,
'id' => $item->user_id,
'username' => $user?->username ?? 'Onbekend',
'avatar' => $user?->avatar ?? null,
'points' => (int) $item->total_points,
];
})->toArray();
}
}
@@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers\Community\Staff;
use App\Http\Controllers\Controller;
use App\Models\Community\Staff\WebsiteOpenPosition;
use App\Models\Community\Staff\WebsiteStaffApplications;
use Illuminate\Http\Request;
use Illuminate\View\View;
class StaffApplicationsController extends Controller
{
public function index(): View
{
$positions = WebsiteOpenPosition::query()
->where('position_kind', 'rank')
->whereNotNull('permission_id')
->with('permission')
->whereHas('permission')
->latest()
->get();
return view('community.staff-applications', ['positions' => $positions]);
}
public function show(WebsiteOpenPosition $position): View
{
abort_unless($position->position_kind === 'rank', 404);
$position->loadMissing('permission');
abort_unless($position->permission, 404);
return view('community.staff-apply', ['position' => $position]);
}
public function store(Request $request, WebsiteOpenPosition $position)
{
abort_unless($position->position_kind === 'rank', 404);
$validated = $request->validate([
'content' => ['required', 'string', 'min:10'],
]);
$user = $request->user();
if ($user->hasAppliedForPosition($position->permission_id)) {
return back()->withErrors([
'content' => __('You have already applied for this position.'),
])->withInput();
}
WebsiteStaffApplications::create([
'user_id' => $user->id,
'rank_id' => $position->permission_id,
'content' => $validated['content'],
]);
return redirect()
->route('staff-applications.index')
->with('status', __('Your application has been submitted!'));
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Community\Staff;
use App\Http\Controllers\Controller;
use App\Services\Community\StaffService;
use Illuminate\View\View;
class StaffController extends Controller
{
public function __construct(private readonly StaffService $staffService) {}
public function __invoke(): View
{
$employees = $this->staffService->fetchStaffPositions();
return view('community.staff', [
'employees' => $employees,
]);
}
}
@@ -0,0 +1,77 @@
<?php
namespace App\Http\Controllers\Community\Staff;
use App\Http\Controllers\Controller;
use App\Models\Community\Staff\WebsiteOpenPosition;
use App\Models\Community\Staff\WebsiteStaffApplications;
use Illuminate\Http\Request;
use Illuminate\View\View;
class WebsiteTeamApplicationsController extends Controller
{
public function index(): View
{
$positions = WebsiteOpenPosition::query()
->where('position_kind', 'team')
->whereNotNull('team_id')
->with('team')
->whereHas('team')
->latest()
->get();
$userAppStatuses = [];
if (auth()->check()) {
$teamIds = $positions->pluck('team_id')->filter()->unique()->all();
$userAppStatuses = WebsiteStaffApplications::query()
->where('user_id', auth()->id())
->whereIn('team_id', $teamIds)
->pluck('status', 'team_id')
->toArray();
}
return view('community.team-applications', [
'positions' => $positions,
'userAppStatuses' => $userAppStatuses,
]);
}
public function show(WebsiteOpenPosition $position): View
{
abort_unless($position->position_kind === 'team', 404);
$position->loadMissing('team');
return view('community.team-apply', [
'position' => $position,
]);
}
public function store(Request $request, WebsiteOpenPosition $position)
{
abort_unless($position->position_kind === 'team', 404);
$request->validate([
'content' => ['required', 'string', 'min:10'],
]);
$user = $request->user();
if ($user->hasAppliedForTeam($position->team_id)) {
return back()->withErrors([
'content' => __('You have already applied for this team.'),
]);
}
WebsiteStaffApplications::create([
'user_id' => $user->id,
'team_id' => $position->team_id,
'content' => $request->string('content'),
]);
return redirect()
->route('team-applications.index')
->with('status', __('Your application has been submitted!'));
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Community\Staff;
use App\Http\Controllers\Controller;
use App\Services\Community\TeamService;
use Illuminate\View\View;
class WebsiteTeamsController extends Controller
{
public function __construct(private readonly TeamService $teamService) {}
public function __invoke(): View
{
$employees = $this->teamService->fetchTeams();
return view('community.teams', [
'employees' => $employees,
]);
}
}
@@ -0,0 +1,108 @@
<?php
namespace App\Http\Controllers\Community;
use App\Http\Controllers\Controller;
use App\Http\Requests\RareSearchFormRequest;
use App\Models\Community\RareValue\WebsiteRareValue;
use App\Models\Community\RareValue\WebsiteRareValueCategory;
use App\Models\Game\Furniture\Item;
use App\Services\Community\RareValues\RareValueCategoriesService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
class WebsiteRareValuesController extends Controller
{
public function __construct(
private readonly RareValueCategoriesService $valueCategoriesService,
) {}
public function index(): View
{
return view('rare-values', [
'categories' => $this->valueCategoriesService->fetchCategoriesByPriority(),
'categoriesNav' => $this->valueCategoriesService->fetchAllCategories(),
'statistics' => $this->valueCategoriesService->getRareStatistics(),
]);
}
public function category(int $id, Request $request): View|RedirectResponse
{
$category = $this->valueCategoriesService->fetchCategoryById($id);
if (! $category instanceof WebsiteRareValueCategory) {
return redirect()->back()->withErrors([
'message' => __('The entered category does not exist'),
]);
}
// Get sort parameters
$sortBy = $request->get('sort', 'name');
$sortOrder = $request->get('order', 'asc');
// Validate sort parameters
$allowedSorts = ['name', 'credit_value', 'currency_value', 'created_at'];
$allowedOrders = ['asc', 'desc'];
if (! in_array($sortBy, $allowedSorts)) {
$sortBy = 'name';
}
if (! in_array($sortOrder, $allowedOrders)) {
$sortOrder = 'asc';
}
return view('rare-values', [
'category' => $category,
'categoriesNav' => $this->valueCategoriesService->fetchAllCategories(),
'statistics' => $this->valueCategoriesService->getRareStatistics(),
'sortBy' => $sortBy,
'sortOrder' => $sortOrder,
]);
}
public function search(RareSearchFormRequest $request): View|RedirectResponse
{
$searchTerm = $request->input('search');
$categories = $this->valueCategoriesService->searchCategories($searchTerm);
if ($categories->isEmpty()) {
return redirect()->back()->withErrors([
'message' => __('It seems like there were no rares matching your search input'),
]);
}
return view('rare-values', [
'categories' => $categories,
'categoriesNav' => $this->valueCategoriesService->fetchAllCategories(),
'statistics' => $this->valueCategoriesService->getRareStatistics(),
'searchTerm' => $searchTerm,
]);
}
public function value(WebsiteRareValue $value): View
{
$cacheKey = "rare_value_items_{$value->id}";
$items = Cache::remember($cacheKey, 300, fn () => Item::with(['user:id,username,look'])
->where('item_id', $value->item_id)
->get());
$itemsPerUser = $items->groupBy('user_id')->map(fn ($group) => [
'user' => $group->first()->user,
'item_count' => $group->count(),
]);
// Get total count
$totalItems = $items->count();
return view('value', [
'value' => $value,
'items' => $itemsPerUser,
'totalItems' => $totalItems,
]);
}
}