244 lines
12 KiB
PHP
244 lines
12 KiB
PHP
@extends('admin.layout')
|
|
|
|
@section('title', 'Video Management')
|
|
@section('page_title', 'Video Management')
|
|
|
|
@section('content')
|
|
<!-- Alerts -->
|
|
@if(session('success'))
|
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
|
{{ session('success') }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
@endif
|
|
|
|
@if(session('error'))
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
{{ session('error') }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Search & Filters -->
|
|
<div class="admin-card">
|
|
<form method="GET" action="{{ route('admin.videos') }}" class="filter-form">
|
|
<div class="form-group">
|
|
<label for="search">Search</label>
|
|
<input type="text" name="search" id="search" class="form-control" placeholder="Search by title or description..." value="{{ request('search') }}">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="status">Status</label>
|
|
<select name="status" id="status" class="form-select">
|
|
<option value="">All Status</option>
|
|
<option value="ready" {{ request('status') == 'ready' ? 'selected' : '' }}>Ready</option>
|
|
<option value="processing" {{ request('status') == 'processing' ? 'selected' : '' }}>Processing</option>
|
|
<option value="pending" {{ request('status') == 'pending' ? 'selected' : '' }}>Pending</option>
|
|
<option value="failed" {{ request('status') == 'failed' ? 'selected' : '' }}>Failed</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="visibility">Visibility</label>
|
|
<select name="visibility" id="visibility" class="form-select">
|
|
<option value="">All Visibility</option>
|
|
<option value="public" {{ request('visibility') == 'public' ? 'selected' : '' }}>Public</option>
|
|
<option value="unlisted" {{ request('visibility') == 'unlisted' ? 'selected' : '' }}>Unlisted</option>
|
|
<option value="private" {{ request('visibility') == 'private' ? 'selected' : '' }}>Private</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="type">Type</label>
|
|
<select name="type" id="type" class="form-select">
|
|
<option value="">All Types</option>
|
|
<option value="generic" {{ request('type') == 'generic' ? 'selected' : '' }}>Generic</option>
|
|
<option value="music" {{ request('type') == 'music' ? 'selected' : '' }}>Music</option>
|
|
<option value="match" {{ request('type') == 'match' ? 'selected' : '' }}>Match</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="sort">Sort By</label>
|
|
<select name="sort" id="sort" class="form-select">
|
|
<option value="latest" {{ request('sort') == 'latest' ? 'selected' : '' }}>Latest First</option>
|
|
<option value="oldest" {{ request('sort') == 'oldest' ? 'selected' : '' }}>Oldest First</option>
|
|
<option value="title_asc" {{ request('sort') == 'title_asc' ? 'selected' : '' }}>Title (A-Z)</option>
|
|
<option value="title_desc" {{ request('sort') == 'title_desc' ? 'selected' : '' }}>Title (Z-A)</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label> </label>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="bi bi-search"></i> Filter
|
|
</button>
|
|
<a href="{{ route('admin.videos') }}" class="btn btn-outline-light">
|
|
<i class="bi bi-x-circle"></i> Clear
|
|
</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Videos Table -->
|
|
<div class="admin-card">
|
|
<div class="admin-card-header">
|
|
<h5 class="admin-card-title">All Videos ({{ $videos->total() }})</h5>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="admin-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Video</th>
|
|
<th>Owner</th>
|
|
<th>Status</th>
|
|
<th>Visibility</th>
|
|
<th>Type</th>
|
|
<th>Views</th>
|
|
<th>Likes</th>
|
|
<th>Uploaded</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($videos as $video)
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center gap-2">
|
|
@if($video->thumbnail)
|
|
<img src="{{ asset('storage/thumbnails/' . $video->thumbnail) }}" alt="{{ $video->title }}" style="width: 80px; height: 50px; object-fit: cover; border-radius: 4px;">
|
|
@else
|
|
<div style="width: 80px; height: 50px; background: #333; border-radius: 4px; display: flex; align-items: center; justify-content: center;">
|
|
<i class="bi bi-play-circle text-secondary"></i>
|
|
</div>
|
|
@endif
|
|
<div style="max-width: 200px;">
|
|
<div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 500;">{{ $video->title }}</div>
|
|
<small class="text-secondary">{{ Str::limit($video->description, 50) }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<a href="{{ route('channel', $video->user->id) }}" target="_blank" class="text-decoration-none">
|
|
{{ $video->user->name }}
|
|
</a>
|
|
</td>
|
|
<td>
|
|
@switch($video->status)
|
|
@case('ready')
|
|
<span class="badge-status badge-ready">Ready</span>
|
|
@break
|
|
@case('processing')
|
|
<span class="badge-status badge-processing">Processing</span>
|
|
@break
|
|
@case('pending')
|
|
<span class="badge-status badge-pending">Pending</span>
|
|
@break
|
|
@case('failed')
|
|
<span class="badge-status badge-failed">Failed</span>
|
|
@break
|
|
@endswitch
|
|
</td>
|
|
<td>
|
|
@switch($video->visibility)
|
|
@case('public')
|
|
<span class="badge-status badge-public">Public</span>
|
|
@break
|
|
@case('unlisted')
|
|
<span class="badge-status badge-unlisted">Unlisted</span>
|
|
@break
|
|
@case('private')
|
|
<span class="badge-status badge-private">Private</span>
|
|
@break
|
|
@endswitch
|
|
</td>
|
|
<td>
|
|
<span class="text-capitalize">{{ $video->type }}</span>
|
|
</td>
|
|
<td>{{ number_format(\DB::table('video_views')->where('video_id', $video->id)->count()) }}</td>
|
|
<td>{{ number_format(\DB::table('video_likes')->where('video_id', $video->id)->count()) }}</td>
|
|
<td>{{ $video->created_at->format('M d, Y') }}</td>
|
|
<td>
|
|
<div class="dropdown">
|
|
<button class="btn btn-sm btn-outline-light dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="bi bi-gear"></i>
|
|
</button>
|
|
<ul class="dropdown-menu dropdown-menu-dark">
|
|
<li>
|
|
<a class="dropdown-item" href="{{ route('videos.show', $video->id) }}" target="_blank">
|
|
<i class="bi bi-play-circle"></i> View
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="dropdown-item" href="{{ route('admin.videos.edit', $video->id) }}">
|
|
<i class="bi bi-pencil"></i> Edit
|
|
</a>
|
|
</li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li>
|
|
<button class="dropdown-item text-danger" onclick="confirmDeleteVideo({{ $video->id }}, '{{ $video->title }}')">
|
|
<i class="bi bi-trash"></i> Delete
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="9" class="text-center text-secondary py-4">
|
|
No videos found
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<div class="d-flex justify-content-center">
|
|
{{ $videos->links() }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete Video Modal -->
|
|
<div class="modal fade" id="deleteVideoModal" tabindex="-1" aria-labelledby="deleteVideoModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content" style="background: #1a1a1a; border: 1px solid #3f3f3f; border-radius: 12px;">
|
|
<div class="modal-header" style="border-bottom: 1px solid #3f3f3f; padding: 20px 24px;">
|
|
<h5 class="modal-title" id="deleteVideoModalLabel" style="color: #fff; font-weight: 600;">
|
|
<i class="bi bi-exclamation-triangle-fill text-danger me-2"></i>
|
|
Delete Video
|
|
</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body" style="padding: 24px;">
|
|
<p>Are you sure you want to delete this video? This action cannot be undone.</p>
|
|
<p><strong>Video:</strong> <span id="deleteVideoTitle"></span></p>
|
|
<div class="alert alert-warning">
|
|
<i class="bi bi-info-circle me-2"></i>
|
|
This will also delete all likes and views associated with this video.
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer" style="border-top: 1px solid #3f3f3f; padding: 16px 24px;">
|
|
<button type="button" class="btn btn-outline-light" data-bs-dismiss="modal">Cancel</button>
|
|
<form id="deleteVideoForm" method="POST">
|
|
@csrf
|
|
@method('DELETE')
|
|
<button type="submit" class="btn btn-danger">Delete Video</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@endsection
|
|
|
|
@section('scripts')
|
|
<script>
|
|
function confirmDeleteVideo(videoId, videoTitle) {
|
|
document.getElementById('deleteVideoTitle').textContent = videoTitle;
|
|
document.getElementById('deleteVideoForm').action = '/admin/videos/' + videoId;
|
|
|
|
const modal = new bootstrap.Modal(document.getElementById('deleteVideoModal'));
|
|
modal.show();
|
|
}
|
|
</script>
|
|
@endsection
|