You've already forked Atomcms-edit
Initial commit
This commit is contained in:
+82
@@ -0,0 +1,82 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', __('radio.become_dj') . ' - ' . config('app.name'))
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-white">
|
||||
<h2 class="text-2xl font-bold mb-4">{{ __('radio.become_dj') }}</h2>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="bg-green-500 text-white p-4 rounded mb-4">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($errors->any())
|
||||
<div class="bg-red-500 text-white p-4 rounded mb-4">
|
||||
<ul>
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(isset($hasPendingApplication) && $hasPendingApplication)
|
||||
<div class="bg-blue-500 text-white p-4 rounded">
|
||||
{{ __('radio.application_pending') }}
|
||||
</div>
|
||||
@else
|
||||
<form action="{{ route('radio.apply.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
<div>
|
||||
<label for="real_name" class="block text-sm font-medium text-gray-300">{{ __('radio.real_name') }}</label>
|
||||
<input type="text" name="real_name" id="real_name" required class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="age" class="block text-sm font-medium text-gray-300">{{ __('radio.age') }}</label>
|
||||
<input type="number" name="age" id="age" required min="13" max="99" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="discord_username" class="block text-sm font-medium text-gray-300">{{ __('radio.discord') }}</label>
|
||||
<input type="text" name="discord_username" id="discord_username" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="experience" class="block text-sm font-medium text-gray-300">{{ __('radio.experience') }}</label>
|
||||
<textarea name="experience" id="experience" rows="3" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="music_style" class="block text-sm font-medium text-gray-300">{{ __('radio.music_style') }}</label>
|
||||
<textarea name="music_style" id="music_style" rows="2" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="availability" class="block text-sm font-medium text-gray-300">{{ __('radio.availability') }}</label>
|
||||
<textarea name="availability" id="availability" required rows="2" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="motivation" class="block text-sm font-medium text-gray-300">{{ __('radio.motivation') }}</label>
|
||||
<textarea name="motivation" id="motivation" required rows="4" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded">
|
||||
{{ __('radio.send_application') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,45 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', __('radio.contests') . ' - ' . config('app.name'))
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-white">
|
||||
<h2 class="text-2xl font-bold mb-6">{{ __('radio.active_contests') }}</h2>
|
||||
|
||||
@if(session('error'))
|
||||
<div class="bg-red-500 text-white p-4 rounded mb-4">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($contests->isEmpty())
|
||||
<div class="bg-gray-700 p-6 rounded-lg text-center">
|
||||
<p class="text-gray-300">{{ __('radio.no_contests') }}</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach($contests as $contest)
|
||||
<div class="bg-gray-700 rounded-lg overflow-hidden shadow-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-xl font-bold mb-2">{{ $contest->title }}</h3>
|
||||
<p class="text-gray-300 mb-4 line-clamp-3">{{ $contest->description }}</p>
|
||||
|
||||
<div class="flex justify-between items-center text-sm text-gray-400 mb-4">
|
||||
<span>{{ __('radio.ends_in', ['time' => $contest->ends_at->diffForHumans()]) }}</span>
|
||||
<span>{{ $contest->entryCount() }} {{ __('radio.participants') }}</span>
|
||||
</div>
|
||||
|
||||
<a href="{{ route('radio.contests.show', $contest) }}" class="block w-full bg-indigo-600 hover:bg-indigo-700 text-center text-white font-bold py-2 px-4 rounded transition duration-200">
|
||||
{{ __('radio.view_participate') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', $contest->title . ' - ' . __('radio.contests') . ' - ' . config('app.name'))
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-white">
|
||||
<div class="mb-6">
|
||||
<a href="{{ route('radio.contests.index') }}" class="text-indigo-400 hover:text-indigo-300 flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
|
||||
{{ __('radio.back_overview') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h1 class="text-3xl font-bold mb-4">{{ $contest->title }}</h1>
|
||||
|
||||
<div class="prose prose-invert max-w-none mb-8">
|
||||
{!! nl2br(e($contest->description)) !!}
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-700 p-6 rounded-lg mb-8">
|
||||
<h3 class="text-xl font-semibold mb-4">{{ __('radio.information') }}</h3>
|
||||
<ul class="space-y-2 text-gray-300">
|
||||
<li><strong>{{ __('radio.start_date') }}:</strong> {{ $contest->starts_at->format('d-m-Y H:i') }}</li>
|
||||
<li><strong>{{ __('radio.end_date') }}:</strong> {{ $contest->ends_at->format('d-m-Y H:i') }}</li>
|
||||
<li><strong>{{ __('radio.participants') }}:</strong> {{ $contest->entryCount() }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-700 p-6 rounded-lg">
|
||||
<p class="text-center text-gray-300">{{ __('radio.participate_button') }} form placeholder.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,45 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', __('radio.giveaways') . ' - ' . config('app.name'))
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-white">
|
||||
<h2 class="text-2xl font-bold mb-6">{{ __('radio.active_giveaways') }}</h2>
|
||||
|
||||
@if(session('error'))
|
||||
<div class="bg-red-500 text-white p-4 rounded mb-4">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($giveaways->isEmpty())
|
||||
<div class="bg-gray-700 p-6 rounded-lg text-center">
|
||||
<p class="text-gray-300">{{ __('radio.no_giveaways') }}</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach($giveaways as $giveaway)
|
||||
<div class="bg-gray-700 rounded-lg overflow-hidden shadow-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-xl font-bold mb-2">{{ $giveaway->title }}</h3>
|
||||
<p class="text-gray-300 mb-4 line-clamp-3">{{ $giveaway->description }}</p>
|
||||
|
||||
<div class="flex justify-between items-center text-sm text-gray-400 mb-4">
|
||||
<span>{{ __('radio.ends_in', ['time' => $giveaway->ends_at->diffForHumans()]) }}</span>
|
||||
<span>{{ $giveaway->participantCount() }} {{ __('radio.participants') }}</span>
|
||||
</div>
|
||||
|
||||
<a href="{{ route('radio.giveaways.show', $giveaway) }}" class="block w-full bg-indigo-600 hover:bg-indigo-700 text-center text-white font-bold py-2 px-4 rounded transition duration-200">
|
||||
{{ __('radio.view_participate') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,38 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', $giveaway->title . ' - ' . __('radio.giveaways') . ' - ' . config('app.name'))
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-white">
|
||||
<div class="mb-6">
|
||||
<a href="{{ route('radio.giveaways.index') }}" class="text-indigo-400 hover:text-indigo-300 flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
|
||||
{{ __('radio.back_overview') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h1 class="text-3xl font-bold mb-4">{{ $giveaway->title }}</h1>
|
||||
|
||||
<div class="prose prose-invert max-w-none mb-8">
|
||||
{!! nl2br(e($giveaway->description)) !!}
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-700 p-6 rounded-lg mb-8">
|
||||
<h3 class="text-xl font-semibold mb-4">{{ __('radio.information') }}</h3>
|
||||
<ul class="space-y-2 text-gray-300">
|
||||
<li><strong>{{ __('radio.start_date') }}:</strong> {{ $giveaway->starts_at->format('d-m-Y H:i') }}</li>
|
||||
<li><strong>{{ __('radio.end_date') }}:</strong> {{ $giveaway->ends_at->format('d-m-Y H:i') }}</li>
|
||||
<li><strong>{{ __('radio.participants') }}:</strong> {{ $giveaway->participantCount() }}</li>
|
||||
<li><strong>{{ __('radio.prize_value') }}:</strong> {{ $giveaway->prize_value }} Credits</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-700 p-6 rounded-lg">
|
||||
<p class="text-center text-gray-300">{{ __('radio.participate_button') }} button placeholder.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', __('radio.music') . ' - ' . config('app.name'))
|
||||
|
||||
@push('styles')
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/inter@4.x/400-700.css">
|
||||
<style>
|
||||
:root {
|
||||
--radio-primary: {{ $primaryColor ?? '#eeb425' }};
|
||||
--radio-secondary: {{ $secondaryColor ?? '#1a1a2e' }};
|
||||
--radio-text: {{ $textColor ?? '#ffffff' }};
|
||||
--radio-accent: {{ $accentColor ?? '#ff6b6b' }};
|
||||
}
|
||||
|
||||
.radio-container {
|
||||
background: linear-gradient(135deg, var(--radio-secondary) 0%, #0f0f1a 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.radio-player {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.now-playing-card {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.animate-pulse-slow {
|
||||
animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
}
|
||||
|
||||
.live-badge {
|
||||
background: linear-gradient(90deg, #ef4444, #f97316);
|
||||
animation: livePulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes livePulse {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); }
|
||||
50% { box-shadow: 0 0 0 8px rgba(239, 68, 68, 0); }
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="radio-container text-white">
|
||||
@if(!$enabled)
|
||||
<!-- Radio Disabled State -->
|
||||
<div class="min-h-screen flex items-center justify-center p-6">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-6">
|
||||
<svg class="w-24 h-24 mx-auto text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold mb-4">{{ __('radio.offline') }}</h1>
|
||||
<p class="text-gray-400 mb-6">{{ __('radio.offline_message') }}</p>
|
||||
@if($offlineMessage)
|
||||
<div class="bg-gray-800 rounded-lg p-4">
|
||||
<p class="text-gray-300">{{ $offlineMessage }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<!-- Radio Player Section -->
|
||||
<div class="max-w-6xl mx-auto px-4 py-8">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold flex items-center gap-3">
|
||||
<span class="live-badge inline-flex items-center px-3 py-1 rounded-full text-xs font-bold">
|
||||
<span class="w-2 h-2 bg-white rounded-full mr-2"></span>
|
||||
{{ __('radio.live') }}
|
||||
</span>
|
||||
{{ __('radio.music') }}
|
||||
</h1>
|
||||
<p class="text-gray-400 mt-1">{{ __('radio.music_desc') }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="flex gap-6">
|
||||
@if($showListeners)
|
||||
<div class="stat-card rounded-lg px-4 py-2 flex items-center gap-2">
|
||||
<svg class="w-5 h-5 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
<span id="listenerCount" class="font-semibold">--</span>
|
||||
<span class="text-gray-400 text-sm">{{ __('radio.listeners') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
<a href="{{ route('radio.leaderboard') }}" class="stat-card rounded-lg px-4 py-2 flex items-center gap-2 hover:bg-white/10 transition">
|
||||
<svg class="w-5 h-5 text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
<span class="font-semibold">{{ __('radio.top_100') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Player -->
|
||||
<div class="radio-player rounded-2xl p-6 mb-8">
|
||||
<div class="grid md:grid-cols-3 gap-6 items-center">
|
||||
<!-- Album Art / DJ Avatar -->
|
||||
<div class="flex justify-center">
|
||||
<div id="djAvatar" class="w-32 h-32 rounded-full bg-gradient-to-br from-amber-400 to-amber-600 flex items-center justify-center text-4xl">
|
||||
@if($currentDJ && $currentDJ->user)
|
||||
<img src="{{ $currentDJ->user->avatar_url ?? asset('images/default-avatar.png') }}" alt="{{ $currentDJ->user->username }}" class="w-full h-full rounded-full object-cover">
|
||||
@else
|
||||
🎵
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Track Info -->
|
||||
<div class="text-center">
|
||||
<div id="nowPlayingInfo">
|
||||
<p class="text-gray-400 text-sm mb-1">{{ __('radio.now_playing') }}</p>
|
||||
<h2 id="trackTitle" class="text-xl font-bold mb-2">
|
||||
@if($nowPlaying && $nowPlaying['title'])
|
||||
{{ $nowPlaying['title'] }}
|
||||
@else
|
||||
--
|
||||
@endif
|
||||
</h2>
|
||||
@if($nowPlaying && $nowPlaying['artist'])
|
||||
<p id="trackArtist" class="text-gray-400">{{ $nowPlaying['artist'] }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- DJ Info -->
|
||||
@if($showCurrentDJ && $currentDJ && $currentDJ->user)
|
||||
<div class="mt-4 pt-4 border-t border-white/10">
|
||||
<p class="text-gray-400 text-sm mb-1">{{ __('radio.host') }}</p>
|
||||
<p class="font-semibold text-amber-400">{{ $currentDJ->user->username }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const player = document.getElementById('radioPlayer');
|
||||
const playBtn = document.getElementById('playPauseBtn');
|
||||
const playIcon = document.getElementById('playIcon');
|
||||
const pauseIcon = document.getElementById('pauseIcon');
|
||||
const volumeSlider = document.getElementById('volumeSlider');
|
||||
const listenerCount = document.getElementById('listenerCount');
|
||||
|
||||
// Play/Pause
|
||||
playBtn.addEventListener('click', function() {
|
||||
if (player.paused) {
|
||||
player.play().catch(function(e) {
|
||||
console.log('Autoplay blocked:', e);
|
||||
});
|
||||
playIcon.classList.add('hidden');
|
||||
pauseIcon.classList.remove('hidden');
|
||||
} else {
|
||||
player.pause();
|
||||
playIcon.classList.remove('hidden');
|
||||
pauseIcon.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Volume
|
||||
volumeSlider.addEventListener('input', function() {
|
||||
player.volume = this.value / 100;
|
||||
});
|
||||
|
||||
// Initial volume
|
||||
player.volume = volumeSlider.value / 100;
|
||||
|
||||
// Update listeners count
|
||||
function updateListeners() {
|
||||
fetch('/api/radio/listeners')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (listenerCount) {
|
||||
listenerCount.textContent = data.count.toLocaleString();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (listenerCount) listenerCount.textContent = '--';
|
||||
});
|
||||
}
|
||||
|
||||
// Update now playing
|
||||
function updateNowPlaying() {
|
||||
fetch('/api/radio/now-playing')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const titleEl = document.getElementById('trackTitle');
|
||||
const artistEl = document.getElementById('trackArtist');
|
||||
if (titleEl && data.title) titleEl.textContent = data.title;
|
||||
if (artistEl && data.artist) artistEl.textContent = data.artist;
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
// Auto-update listeners every 30 seconds
|
||||
updateListeners();
|
||||
setInterval(updateListeners, 30000);
|
||||
|
||||
// Auto-update now playing every 15 seconds
|
||||
updateNowPlaying();
|
||||
setInterval(updateNowPlaying, 15000);
|
||||
|
||||
// Load user points if logged in
|
||||
if (document.body.classList.contains('authenticated')) {
|
||||
loadUserPoints();
|
||||
setInterval(loadUserPoints, 60000); // Update every minute
|
||||
}
|
||||
});
|
||||
|
||||
function loadUserPoints() {
|
||||
fetch('/api/radio/points/user')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
const pointsCount = document.getElementById('userPointsCount');
|
||||
const userRank = document.getElementById('userRank');
|
||||
const pointsHistory = document.getElementById('pointsHistory');
|
||||
|
||||
if (pointsCount) pointsCount.textContent = data.points || 0;
|
||||
if (userRank) userRank.textContent = data.rank || '--';
|
||||
|
||||
if (pointsHistory && data.history && data.history.length > 0) {
|
||||
pointsHistory.innerHTML = data.history.map(item => `
|
||||
<div class="flex justify-between items-center py-1 border-b border-gray-700 text-xs">
|
||||
<span class="text-gray-300">${item.reason}</span>
|
||||
<span class="${item.points > 0 ? 'text-green-400' : 'text-red-400'}">
|
||||
${item.points > 0 ? '+' : ''}${item.points}
|
||||
</span>
|
||||
</div>
|
||||
`).join('');
|
||||
} else if (pointsHistory) {
|
||||
pointsHistory.innerHTML = '<p class="text-xs text-gray-500">{{ __("radio.no_history") }}</p>';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('User points not available:', error.message);
|
||||
// Silently fail if not authenticated or other error
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'Radio Leaderboard')
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<x-content.content-card icon="trophy" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Radio Leaderboard
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Wie zijn de meest toegewijde luisteraars?
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<!-- Period Navigation -->
|
||||
<div class="flex flex-wrap gap-2 mb-6">
|
||||
<a href="{{ route('radio.leaderboard', ['period' => 'all']) }}"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-300 transform hover:scale-105
|
||||
{{ $period === 'all'
|
||||
? 'bg-gradient-to-r from-yellow-400 to-yellow-500 text-gray-900 shadow-lg shadow-yellow-500/30'
|
||||
: 'bg-gray-700/50 text-gray-200 hover:bg-gray-600' }}">
|
||||
🌟 Alles
|
||||
</a>
|
||||
<a href="{{ route('radio.leaderboard', ['period' => 'weekly']) }}"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-300 transform hover:scale-105
|
||||
{{ $period === 'weekly'
|
||||
? 'bg-gradient-to-r from-yellow-400 to-yellow-500 text-gray-900 shadow-lg shadow-yellow-500/30'
|
||||
: 'bg-gray-700/50 text-gray-200 hover:bg-gray-600' }}">
|
||||
📅 Deze Week
|
||||
</a>
|
||||
<a href="{{ route('radio.leaderboard', ['period' => 'monthly']) }}"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-300 transform hover:scale-105
|
||||
{{ $period === 'monthly'
|
||||
? 'bg-gradient-to-r from-yellow-400 to-yellow-500 text-gray-900 shadow-lg shadow-yellow-500/30'
|
||||
: 'bg-gray-700/50 text-gray-200 hover:bg-gray-600' }}">
|
||||
📆 Deze Maand
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Top 3 Podium -->
|
||||
@if(!empty($users) && count($users) >= 3)
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||
<!-- 2nd Place -->
|
||||
<div class="order-1 md:order-1">
|
||||
<div class="bg-gradient-to-b from-gray-700 to-gray-800 rounded-xl p-6 text-center border border-gray-600/50 shadow-xl transform hover:scale-105 transition-all duration-300 relative overflow-hidden">
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-gray-500/10 to-transparent"></div>
|
||||
<div class="relative">
|
||||
<div class="absolute -top-6 left-1/2 -translate-x-1/2">
|
||||
<div class="w-16 h-16 rounded-full bg-gradient-to-br from-gray-300 to-gray-400 flex items-center justify-center text-2xl shadow-lg">
|
||||
🥈
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-8 mb-3">
|
||||
<img src="{{ $users[1]['avatar'] ?? 'https://ui-avatars.com/api/?name=' . urlencode($users[1]['username']) . '&size=128&background=6b7280&color=ffffff' }}"
|
||||
alt="{{ $users[1]['username'] }}"
|
||||
class="w-24 h-24 rounded-full mx-auto border-4 border-gray-500 shadow-lg">
|
||||
</div>
|
||||
<h4 class="font-bold text-xl text-white mb-1">{{ $users[1]['username'] }}</h4>
|
||||
<div class="inline-flex items-center gap-1 px-4 py-1.5 rounded-full bg-gray-600 text-gray-200 font-medium text-sm">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||||
</svg>
|
||||
{{ number_format($users[1]['points']) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 1st Place -->
|
||||
<div class="order-2">
|
||||
<div class="bg-gradient-to-b from-yellow-500/20 to-gray-800 rounded-xl p-6 text-center border-2 border-yellow-500/50 shadow-xl shadow-yellow-500/20 transform hover:scale-105 transition-all duration-300 relative overflow-hidden">
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-yellow-500/10 to-transparent"></div>
|
||||
<div class="absolute -top-8 left-1/2 -translate-x-1/2">
|
||||
<div class="w-20 h-20 rounded-full bg-gradient-to-br from-yellow-300 to-yellow-500 flex items-center justify-center text-3xl shadow-lg shadow-yellow-500/50 animate-pulse">
|
||||
🥇
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-10 mb-3">
|
||||
<img src="{{ $users[0]['avatar'] ?? 'https://ui-avatars.com/api/?name=' . urlencode($users[0]['username']) . '&size=128&background=eeb425&color=000000' }}"
|
||||
alt="{{ $users[0]['username'] }}"
|
||||
class="w-28 h-28 rounded-full mx-auto border-4 border-yellow-400 shadow-lg shadow-yellow-500/30">
|
||||
</div>
|
||||
<h4 class="font-bold text-xl text-yellow-400 mb-1">{{ $users[0]['username'] }}</h4>
|
||||
<div class="inline-flex items-center gap-1 px-4 py-1.5 rounded-full bg-yellow-500/20 text-yellow-400 font-medium text-sm">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||||
</svg>
|
||||
{{ number_format($users[0]['points']) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3rd Place -->
|
||||
<div class="order-3 md:order-3">
|
||||
<div class="bg-gradient-to-b from-orange-700/50 to-gray-800 rounded-xl p-6 text-center border border-orange-600/30 shadow-xl transform hover:scale-105 transition-all duration-300 relative overflow-hidden">
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-orange-500/10 to-transparent"></div>
|
||||
<div class="relative">
|
||||
<div class="absolute -top-6 left-1/2 -translate-x-1/2">
|
||||
<div class="w-14 h-14 rounded-full bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center text-xl shadow-lg">
|
||||
🥉
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 mb-3">
|
||||
<img src="{{ $users[2]['avatar'] ?? 'https://ui-avatars.com/api/?name=' . urlencode($users[2]['username']) . '&size=128&background=ea580c&color=ffffff' }}"
|
||||
alt="{{ $users[2]['username'] }}"
|
||||
class="w-20 h-20 rounded-full mx-auto border-4 border-orange-500 shadow-lg">
|
||||
</div>
|
||||
<h4 class="font-bold text-lg text-orange-400 mb-1">{{ $users[2]['username'] }}</h4>
|
||||
<div class="inline-flex items-center gap-1 px-4 py-1.5 rounded-full bg-orange-500/20 text-orange-400 font-medium text-sm">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||||
</svg>
|
||||
{{ number_format($users[2]['points']) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Leaderboard List -->
|
||||
<div class="bg-gray-800/50 backdrop-blur-sm rounded-xl overflow-hidden border border-gray-700/50 shadow-xl">
|
||||
<div class="px-6 py-4 border-b border-gray-700/50 bg-gradient-to-r from-gray-800/80 to-gray-800/40">
|
||||
<h4 class="font-bold text-white flex items-center gap-3">
|
||||
<span class="w-8 h-8 rounded-lg bg-[#eeb425]/20 flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-[#eeb425]" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/>
|
||||
</svg>
|
||||
</span>
|
||||
Alle Luisteraars
|
||||
<span class="ml-auto text-sm font-normal text-gray-400">({{ count($users) }})</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="max-h-[500px] overflow-y-auto custom-scrollbar">
|
||||
@forelse($users as $user)
|
||||
<div class="flex items-center gap-4 px-6 py-3 border-b border-gray-700/30 last:border-b-0 hover:bg-gray-700/30 transition-all duration-200 group">
|
||||
<!-- Rank -->
|
||||
<div class="w-8 flex-shrink-0">
|
||||
@if($user['rank'] <= 3)
|
||||
<div class="flex items-center justify-center w-8 h-8 rounded-full font-bold text-sm
|
||||
{{ $user['rank'] === 1 ? 'bg-gradient-to-br from-yellow-400 to-yellow-600 text-gray-900 shadow-lg shadow-yellow-500/30' :
|
||||
($user['rank'] === 2 ? 'bg-gradient-to-br from-gray-300 to-gray-500 text-gray-900' :
|
||||
'bg-gradient-to-br from-orange-400 to-orange-600 text-white') }}">
|
||||
{{ $user['rank'] }}
|
||||
</div>
|
||||
@else
|
||||
<div class="flex items-center justify-center w-8 h-8 rounded-full bg-gray-700/50 text-gray-400 text-sm font-medium">
|
||||
{{ $user['rank'] }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Avatar -->
|
||||
<div class="relative flex-shrink-0">
|
||||
<img src="{{ $user['avatar'] ?? 'https://ui-avatars.com/api/?name=' . urlencode($user['username']) . '&size=64&background=374151&color=eeb425' }}"
|
||||
alt="{{ $user['username'] }}"
|
||||
class="w-12 h-12 rounded-full border-2 border-gray-600 group-hover:border-[#eeb425] transition-colors duration-200">
|
||||
@if($user['rank'] <= 3)
|
||||
<div class="absolute -top-1 -right-1 w-5 h-5 rounded-full flex items-center justify-center text-xs
|
||||
{{ $user['rank'] === 1 ? 'bg-yellow-400' : ($user['rank'] === 2 ? 'bg-gray-300' : 'bg-orange-400') }}">
|
||||
{{ $user['rank'] }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Username -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-semibold text-white group-hover:text-[#eeb425] transition-colors duration-200 truncate">{{ $user['username'] }}</div>
|
||||
@if($user['rank'] <= 3)
|
||||
<div class="text-xs">
|
||||
<span class="{{ $user['rank'] === 1 ? 'text-yellow-400' : ($user['rank'] === 2 ? 'text-gray-300' : 'text-orange-400') }}">
|
||||
{{ $user['rank'] === 1 ? '👑 Kampioen' : ($user['rank'] === 2 ? '🥈 Runner-up' : '🥉 Top 3') }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Points -->
|
||||
<div class="flex items-center gap-3 flex-shrink-0">
|
||||
<div class="px-3 py-1.5 rounded-lg bg-gradient-to-r from-[#eeb425]/10 to-[#eeb425]/5 border border-[#eeb425]/20">
|
||||
<span class="text-[#eeb425] font-bold">{{ number_format($user['points']) }}</span>
|
||||
<span class="text-[#eeb425]/60 text-xs ml-1">pts</span>
|
||||
</div>
|
||||
@if($user['rank'] === 1)
|
||||
<span class="text-2xl">🏆</span>
|
||||
@elseif($user['rank'] <= 10)
|
||||
<span class="text-lg">⭐</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="text-center py-16 px-4">
|
||||
<div class="relative inline-block mb-4">
|
||||
<div class="w-24 h-24 rounded-full bg-gray-700/50 flex items-center justify-center text-5xl">
|
||||
🎵
|
||||
</div>
|
||||
<div class="absolute -bottom-2 -right-2 w-10 h-10 rounded-full bg-[#eeb425] flex items-center justify-center text-xl">
|
||||
🤔
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="text-xl font-bold text-white mb-2">{{ __('radio.no_listeners') }}</h4>
|
||||
<p class="text-gray-400 mb-6 max-w-sm mx-auto">{{ __('radio.listen_to_earn') }}</p>
|
||||
<a href="{{ route('radio.index') }}"
|
||||
class="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-[#eeb425] to-yellow-500 text-gray-900 rounded-xl font-semibold hover:from-yellow-400 hover:to-yellow-400 transition-all duration-300 transform hover:scale-105 shadow-lg shadow-yellow-500/30">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
||||
</svg>
|
||||
{{ __('radio.start_listening') }}
|
||||
</a>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
<x-content.content-card icon="info" classes="border dark:border-gray-900 bg-gradient-to-br from-gray-800/50 to-gray-900/50">
|
||||
<x-slot:title>
|
||||
{{ __('radio.leaderboard_info') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('radio.how_it_works') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p class="text-gray-300">{{ __('radio.points_auto') }}</p>
|
||||
<div class="space-y-3 text-xs">
|
||||
<div class="flex items-center gap-3 p-3 rounded-lg bg-green-500/10 border border-green-500/20">
|
||||
<span class="w-3 h-3 rounded-full bg-green-500 shadow-lg shadow-green-500/50"></span>
|
||||
<span class="text-green-400">{{ __('radio.points_per_minute') }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 p-3 rounded-lg bg-yellow-500/10 border border-yellow-500/20">
|
||||
<span class="w-3 h-3 rounded-full bg-yellow-500 shadow-lg shadow-yellow-500/50"></span>
|
||||
<span class="text-yellow-400">{{ __('radio.max_points_per_day') }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 p-3 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||
<span class="w-3 h-3 rounded-full bg-blue-500 shadow-lg shadow-blue-500/50"></span>
|
||||
<span class="text-blue-400">{{ __('radio.leaderboard_updated') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
|
||||
<x-content.content-card icon="trophy" classes="border dark:border-gray-900 bg-gradient-to-br from-[#eeb425]/10 to-transparent">
|
||||
<x-slot:title>
|
||||
{{ __('radio.quick_stats') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('radio.current_stand') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<div class="flex justify-between items-center p-3 rounded-lg bg-gray-700/30">
|
||||
<span class="text-gray-400">{{ __('radio.active_listeners') }}</span>
|
||||
<span class="font-bold text-[#eeb425] text-lg">{{ count($users) }}</span>
|
||||
</div>
|
||||
@if(!empty($users))
|
||||
<div class="flex justify-between items-center p-3 rounded-lg bg-yellow-500/10 border border-yellow-500/20">
|
||||
<span class="text-gray-400">{{ __('radio.current_leader') }}</span>
|
||||
<span class="font-bold text-yellow-400 flex items-center gap-2">
|
||||
👑 {{ $users[0]['username'] }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex justify-between items-center p-3 rounded-lg bg-gray-700/30">
|
||||
<span class="text-gray-400">{{ __('radio.current_period') }}</span>
|
||||
<span class="font-semibold text-white">
|
||||
{{ $period === 'weekly' ? __('radio.this_week') : ($period === 'monthly' ? __('radio.this_month') : __('radio.total')) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
|
||||
<!-- Top 3 Compact -->
|
||||
@if(!empty($users) && count($users) >= 3)
|
||||
<x-content.content-card icon="trophy" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Top 3
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
De beste luisteraars
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 space-y-3">
|
||||
@foreach(array_slice($users, 0, 3) as $index => $user)
|
||||
<div class="flex items-center gap-3 p-3 rounded-lg {{ $index === 0 ? 'bg-yellow-500/10 border border-yellow-500/20' : 'bg-gray-700/30' }}">
|
||||
<div class="w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm
|
||||
{{ $index === 0 ? 'bg-gradient-to-br from-yellow-400 to-yellow-600 text-gray-900' :
|
||||
($index === 1 ? 'bg-gradient-to-br from-gray-300 to-gray-500 text-gray-900' :
|
||||
'bg-gradient-to-br from-orange-400 to-orange-600 text-white') }}">
|
||||
{{ $index + 1 }}
|
||||
</div>
|
||||
<img src="{{ $user['avatar'] ?? 'https://ui-avatars.com/api/?name=' . urlencode($user['username']) . '&size=64&background=374151&color=eeb425' }}"
|
||||
alt="{{ $user['username'] }}"
|
||||
class="w-10 h-10 rounded-full border-2 border-gray-600">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-semibold text-white truncate">{{ $user['username'] }}</div>
|
||||
<div class="text-xs text-[#eeb425]">{{ number_format($user['points']) }} pts</div>
|
||||
</div>
|
||||
<span class="text-xl">
|
||||
{{ $index === 0 ? '🏆' : ($index === 1 ? '🥈' : '🥉') }}
|
||||
</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endif
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', __('radio.requests') . ' - ' . config('app.name'))
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-white">
|
||||
<h2 class="text-2xl font-bold mb-4">{{ __('radio.requests') }}</h2>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="bg-green-500 text-white p-4 rounded mb-4">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="bg-red-500 text-white p-4 rounded mb-4">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('radio.requests.store') }}" method="POST" class="mb-8">
|
||||
@csrf
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="artist" class="block text-sm font-medium text-gray-300">{{ __('radio.artist') }}</label>
|
||||
<input type="text" name="artist" id="artist" required class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white focus:border-indigo-500 focus:ring-indigo-500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="title" class="block text-sm font-medium text-gray-300">{{ __('radio.title') }}</label>
|
||||
<input type="text" name="title" id="title" required class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white focus:border-indigo-500 focus:ring-indigo-500">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<label for="message" class="block text-sm font-medium text-gray-300">{{ __('radio.message') }}</label>
|
||||
<input type="text" name="message" id="message" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white focus:border-indigo-500 focus:ring-indigo-500">
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded">
|
||||
{{ __('radio.submit_request') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if(isset($recentRequests) && count($recentRequests) > 0)
|
||||
<h3 class="text-xl font-bold mb-4">{{ __('radio.queue') }}</h3>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-gray-700 rounded-lg overflow-hidden">
|
||||
<thead class="bg-gray-600">
|
||||
<tr>
|
||||
<th class="py-3 px-4 text-left">{{ __('radio.artist') }}</th>
|
||||
<th class="py-3 px-4 text-left">{{ __('radio.title') }}</th>
|
||||
<th class="py-3 px-4 text-left">{{ __('radio.requested_by') }}</th>
|
||||
<th class="py-3 px-4 text-left">{{ __('radio.votes') }}</th>
|
||||
<th class="py-3 px-4 text-left">{{ __('radio.vote') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($recentRequests as $request)
|
||||
<tr class="border-b border-gray-600">
|
||||
<td class="py-3 px-4">{{ $request->artist }}</td>
|
||||
<td class="py-3 px-4">{{ $request->title }}</td>
|
||||
<td class="py-3 px-4">{{ $request->user->username ?? __('radio.unknown') }}</td>
|
||||
<td class="py-3 px-4">{{ $request->votes }}</td>
|
||||
<td class="py-3 px-4">
|
||||
<form action="{{ route('radio.requests.vote', $request) }}" method="POST">
|
||||
@csrf
|
||||
<button type="submit" class="text-indigo-400 hover:text-indigo-300">{{ __('radio.vote') }}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-gray-400">{{ __('radio.no_requests') }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', __('radio.timetable') . ' - ' . config('app.name'))
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-white">
|
||||
<h2 class="text-2xl font-bold mb-6">{{ __('radio.timetable') }}</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-7 gap-4">
|
||||
@php
|
||||
$days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
||||
@endphp
|
||||
|
||||
@foreach($days as $day)
|
||||
<div class="bg-gray-700 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-600 p-3 text-center font-bold">
|
||||
{{ __('radio.days.' . $day) }}
|
||||
</div>
|
||||
<div class="p-4 space-y-3">
|
||||
@if(isset($schedule[$day]) && count($schedule[$day]) > 0)
|
||||
@foreach($schedule[$day] as $slot)
|
||||
<div class="bg-gray-800 p-3 rounded border border-gray-600">
|
||||
<div class="text-sm text-indigo-400 font-semibold">
|
||||
{{ $slot->start_time->format('H:i') }} - {{ $slot->end_time->format('H:i') }}
|
||||
</div>
|
||||
<div class="font-bold">{{ $slot->user->username ?? __('radio.unknown') }}</div>
|
||||
<div class="text-xs text-gray-400 truncate">{{ $slot->show_name }}</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@else
|
||||
<div class="text-center text-gray-500 text-sm py-4">
|
||||
{{ __('radio.no_shows') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', __('radio.shouts') . ' - ' . config('app.name'))
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-white">
|
||||
<h2 class="text-2xl font-bold mb-4">{{ __('radio.shouts') }}</h2>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="bg-green-500 text-white p-4 rounded mb-4">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('radio.shouts.store') }}" method="POST" class="mb-8">
|
||||
@csrf
|
||||
<div>
|
||||
<label for="message" class="block text-sm font-medium text-gray-300">{{ __('radio.shout_message') }}</label>
|
||||
<textarea name="message" id="message" required rows="3" maxlength="280" placeholder="{{ __('radio.shout_placeholder') }}" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white focus:border-indigo-500 focus:ring-indigo-500"></textarea>
|
||||
<p class="text-xs text-gray-400 mt-1">{{ __('radio.max_chars') }}</p>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded">
|
||||
{{ __('radio.send_shout') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="shouts-container" class="space-y-4">
|
||||
{{-- Shouts will be loaded via API --}}
|
||||
<div class="text-center text-gray-400">{{ __('radio.loading') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function loadShouts() {
|
||||
fetch('/api/radio/shouts')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const container = document.getElementById('shouts-container');
|
||||
if (data.shouts && data.shouts.length > 0) {
|
||||
container.innerHTML = data.shouts.map(shout => `
|
||||
<div class="bg-gray-700 p-4 rounded-lg">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span class="font-bold text-indigo-400">${shout.username}</span>
|
||||
<span class="text-xs text-gray-400">${shout.created_at}</span>
|
||||
</div>
|
||||
<p class="text-gray-300">${shout.message}</p>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
container.innerHTML = "<p class='text-center text-gray-400'>{{ __('radio.no_shouts') }}</p>";
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
document.getElementById('shouts-container').innerHTML = "<p class='text-center text-red-400'>{{ __('radio.load_error') }}</p>";
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadShouts();
|
||||
setInterval(loadShouts, 30000); // Reload every 30 seconds
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
Reference in New Issue
Block a user