latest update
This commit is contained in:
parent
3b09f4baed
commit
84fcbd84dc
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@ class MatchEventController extends Controller
|
||||
$request->validate([
|
||||
'round_number' => 'required|integer|min:1',
|
||||
'name' => 'nullable|string|max:50',
|
||||
'start_time_seconds' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
// Check if user owns the video
|
||||
@ -29,6 +30,7 @@ class MatchEventController extends Controller
|
||||
'video_id' => $video->id,
|
||||
'round_number' => $request->round_number,
|
||||
'name' => $request->name ?? 'ROUND '.$request->round_number,
|
||||
'start_time_seconds' => $request->start_time_seconds,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
@ -41,7 +43,9 @@ class MatchEventController extends Controller
|
||||
public function updateRound(Request $request, MatchRound $round)
|
||||
{
|
||||
$request->validate([
|
||||
'round_number' => 'sometimes|integer|min:1',
|
||||
'name' => 'required|string|max:50',
|
||||
'start_time_seconds' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
// Check if user owns the video
|
||||
@ -50,7 +54,9 @@ class MatchEventController extends Controller
|
||||
}
|
||||
|
||||
$round->update([
|
||||
'round_number' => $request->round_number ?? $round->round_number,
|
||||
'name' => $request->name,
|
||||
'start_time_seconds' => $request->start_time_seconds,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
@ -93,16 +99,25 @@ class MatchEventController extends Controller
|
||||
return response()->json(['success' => false, 'message' => 'Unauthorized'], 403);
|
||||
}
|
||||
|
||||
// Calculate scores
|
||||
$previousPoints = MatchPoint::where('video_id', $video->id)
|
||||
->where('match_round_id', $request->round_id)
|
||||
// Get ALL previous points in this round (ordered by timestamp)
|
||||
$previousPoints = MatchPoint::where('match_round_id', $request->round_id)
|
||||
->where('timestamp_seconds', '<', $request->timestamp_seconds)
|
||||
->orderBy('timestamp_seconds', 'desc')
|
||||
->first();
|
||||
->orderBy('timestamp_seconds', 'asc')
|
||||
->pluck('points', 'competitor')
|
||||
->toArray();
|
||||
|
||||
$scoreBlue = $previousPoints ? $previousPoints->score_blue : 0;
|
||||
$scoreRed = $previousPoints ? $previousPoints->score_red : 0;
|
||||
// Calculate cumulative scores by summing each point value
|
||||
$scoreBlue = 0;
|
||||
$scoreRed = 0;
|
||||
|
||||
if (isset($previousPoints['blue'])) {
|
||||
$scoreBlue += $previousPoints['blue'];
|
||||
}
|
||||
if (isset($previousPoints['red'])) {
|
||||
$scoreRed += $previousPoints['red'];
|
||||
}
|
||||
|
||||
// Add current point
|
||||
if ($request->competitor === 'blue') {
|
||||
$scoreBlue += $request->points;
|
||||
} else {
|
||||
|
||||
@ -18,7 +18,7 @@ class VideoController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth')->except(['index', 'show', 'search', 'stream']);
|
||||
$this->middleware('auth')->except(['index', 'show', 'search', 'stream', 'trending', 'shorts']);
|
||||
}
|
||||
|
||||
public function index()
|
||||
|
||||
@ -15,6 +15,11 @@ class MatchRound extends Model
|
||||
'video_id',
|
||||
'round_number',
|
||||
'name',
|
||||
'start_time_seconds',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'start_time_seconds' => 'integer',
|
||||
];
|
||||
|
||||
public function video(): BelongsTo
|
||||
|
||||
@ -57,7 +57,7 @@ return [
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
'asset_url' => env('ASSET_URL'),
|
||||
'asset_url' => env('APP_URL'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@ -70,7 +70,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
'timezone' => 'Asia/Bahrain',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
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::table('match_rounds', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('match_rounds', 'start_time_seconds')) {
|
||||
$table->integer('start_time_seconds')->nullable()->after('name');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('match_rounds', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('match_rounds', 'start_time_seconds')) {
|
||||
$table->dropColumn('start_time_seconds');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
66
resources/views/components/channel-row.blade.php
Normal file
66
resources/views/components/channel-row.blade.php
Normal file
@ -0,0 +1,66 @@
|
||||
<div class="channel-row">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<a href="{{ route('channel', $video->user_id) }}" class="channel-info text-decoration-none"
|
||||
style="color: inherit; display: flex; align-items: center; gap: 12px;">
|
||||
@if ($video->user)
|
||||
<img src="{{ $video->user->avatar_url }}" class="channel-avatar" alt="{{ $video->user->name }}">
|
||||
@else
|
||||
<div class="channel-avatar"></div>
|
||||
@endif
|
||||
<div>
|
||||
<div class="channel-name">{{ $video->user->name ?? 'Unknown' }}</div>
|
||||
<div class="channel-subs">{{ number_format($video->user->subscriber_count ?? 0) }} subscribers</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<x-video-actions :video="$video" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Channel Row */
|
||||
.channel-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.channel-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.channel-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.channel-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.channel-subs {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 576px) {
|
||||
.channel-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start !important;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.channel-info {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@props(['video'])
|
||||
200
resources/views/components/video-actions.blade.php
Normal file
200
resources/views/components/video-actions.blade.php
Normal file
@ -0,0 +1,200 @@
|
||||
@props(['video'])
|
||||
|
||||
<style>
|
||||
.action-btn {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 8px 14px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: var(--border-color);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.action-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.action-btn svg,
|
||||
.action-btn i {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-btn.comment-btn {
|
||||
background: var(--brand-red);
|
||||
color: white;
|
||||
border-color: var(--brand-red);
|
||||
}
|
||||
|
||||
.action-btn.liked {
|
||||
color: var(--brand-red) !important;
|
||||
}
|
||||
|
||||
.mobile-action-dropdown .dropdown-item.liked {
|
||||
color: var(--brand-red) !important;
|
||||
}
|
||||
|
||||
.mobile-action-dropdown {
|
||||
display: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mobile-action-dropdown .dropdown-menu {
|
||||
right: 0;
|
||||
left: auto;
|
||||
min-width: 200px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 10px;
|
||||
padding: 6px 0;
|
||||
z-index: 1200;
|
||||
}
|
||||
|
||||
.mobile-action-dropdown .dropdown-item {
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
padding: 8px 12px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.mobile-action-dropdown .dropdown-item:hover {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.video-actions>.desktop-action {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.mobile-action-dropdown {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="video-actions" style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap; overflow: visible;">
|
||||
@auth
|
||||
@if (Auth::id() === $video->user_id)
|
||||
<button class="action-btn desktop-action" onclick="openEditVideoModal({{ $video->id }})">
|
||||
<i class="bi bi-pencil"></i>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
@elseif (Auth::id() !== $video->user_id)
|
||||
<button class="action-btn desktop-action"><i class="bi bi-bell"></i><span>Subscribe</span></button>
|
||||
@endif
|
||||
@else
|
||||
<button onclick="window.location.href='{{ route('login') }}'" class="action-btn desktop-action"><i
|
||||
class="bi bi-bell"></i><span>Subscribe</span></button>
|
||||
@endauth
|
||||
|
||||
@auth
|
||||
<form method="POST"
|
||||
action="{{ $video->isLikedBy(Auth::user()) ? route('videos.unlike', $video->id) : route('videos.like', $video->id) }}"
|
||||
class="d-inline desktop-action">
|
||||
@csrf
|
||||
<button type="submit" class="action-btn {{ $video->isLikedBy(Auth::user()) ? 'liked' : '' }}">
|
||||
<i class="bi {{ $video->isLikedBy(Auth::user()) ? 'bi-hand-thumbs-up-fill' : 'bi-hand-thumbs-up' }}"></i>
|
||||
{{ $video->like_count > 0 ? number_format($video->like_count) : 'Like' }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<button onclick="window.location.href='{{ route('login') }}'" class="action-btn desktop-action">
|
||||
<i class="bi bi-hand-thumbs-up"></i>
|
||||
{{ $video->like_count > 0 ? number_format($video->like_count) : 'Like' }}
|
||||
</button>
|
||||
@endauth
|
||||
|
||||
@if ($video->isShareable())
|
||||
<button class="action-btn desktop-action"
|
||||
onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
||||
<i class="bi bi-share"></i> Share
|
||||
</button>
|
||||
@endif
|
||||
|
||||
<!-- Save to Playlist Button -->
|
||||
<button class="action-btn desktop-action" onclick="openAddToPlaylistModal({{ $video->id }})">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
|
||||
</svg>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
|
||||
<div class="dropdown mobile-action-dropdown">
|
||||
<button class="action-btn dropdown-toggle" type="button" id="dropdownMenuLinkMusic{{ $video->id }}"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-lightning-charge-fill"></i>
|
||||
<span>Action</span>
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuLinkMusic{{ $video->id }}">
|
||||
@auth
|
||||
@if (Auth::id() !== $video->user_id)
|
||||
<button type="button" class="dropdown-item">
|
||||
<i class="bi bi-bell"></i> Subscribe
|
||||
</button>
|
||||
@else
|
||||
<button type="button" class="dropdown-item" onclick="openEditVideoModal({{ $video->id }})">
|
||||
<i class="bi bi-pencil"></i> Edit
|
||||
</button>
|
||||
@endif
|
||||
@else
|
||||
<button class="dropdown-item" onclick="window.location.href='{{ route('login') }}'">
|
||||
<i class="bi bi-bell"></i> Subscribe
|
||||
</button>
|
||||
@endauth
|
||||
|
||||
@auth
|
||||
<form method="POST"
|
||||
action="{{ $video->isLikedBy(Auth::user()) ? route('videos.unlike', $video->id) : route('videos.like', $video->id) }}"
|
||||
class="d-inline w-100">
|
||||
@csrf
|
||||
<button type="submit" class="dropdown-item {{ $video->isLikedBy(Auth::user()) ? 'liked' : '' }}">
|
||||
<i
|
||||
class="bi {{ $video->isLikedBy(Auth::user()) ? 'bi-hand-thumbs-up-fill' : 'bi-hand-thumbs-up' }}"></i>
|
||||
{{ $video->like_count > 0 ? number_format($video->like_count) : 'Like' }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<button class="dropdown-item" onclick="window.location.href='{{ route('login') }}'">
|
||||
<i class="bi bi-hand-thumbs-up"></i>
|
||||
{{ $video->like_count > 0 ? number_format($video->like_count) : 'Like' }}
|
||||
</button>
|
||||
@endauth
|
||||
|
||||
@if ($video->isShareable())
|
||||
<button class="dropdown-item"
|
||||
onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
||||
<i class="bi bi-share"></i> Share
|
||||
</button>
|
||||
@endif
|
||||
|
||||
<button class="dropdown-item" onclick="openAddToPlaylistModal({{ $video->id }})">
|
||||
<i class="bi bi-bookmark"></i> Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
150
resources/views/components/video-comments.blade.php
Normal file
150
resources/views/components/video-comments.blade.php
Normal file
@ -0,0 +1,150 @@
|
||||
<div class="comments-section" style="margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--border-color);">
|
||||
<h3 style="font-size: 18px; font-weight: 600; margin-bottom: 16px;">
|
||||
Comments <span style="color: var(--text-secondary); font-weight: 400;">({{ $video->comment_count }})</span>
|
||||
</h3>
|
||||
|
||||
@auth
|
||||
<div class="comment-form" style="display: flex; gap: 12px; margin-bottom: 24px;">
|
||||
<img src="{{ Auth::user()->avatar_url }}" class="channel-avatar" style="width: 40px; height: 40px;"
|
||||
alt="{{ Auth::user()->name }}">
|
||||
<div style="flex: 1; display: flex; align-items: center; gap: 8px;">
|
||||
<textarea id="commentBody{{ $video->id }}" class="form-control"
|
||||
placeholder="Add a comment... Use @ to mention someone" rows="1"
|
||||
style="background: transparent; border: none; border-bottom: 2px solid var(--border-color); color: var(--text-primary); border-radius: 0; padding: 12px 0 8px 0; flex: 1; margin: 0; height: 40px; font-size: 14px; outline: none; overflow: hidden;"></textarea>
|
||||
<button type="button" class="action-btn comment-btn" onclick="submitComment({{ $video->id }})"
|
||||
style="flex-shrink: 0;">
|
||||
<span>Send</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div
|
||||
style="margin-bottom: 24px; padding: 16px; background: var(--bg-secondary); border-radius: 8px; text-align: center;">
|
||||
<a href="{{ route('login') }}" style="color: var(--brand-red);">Sign in</a> to comment
|
||||
</div>
|
||||
@endauth
|
||||
|
||||
<div id="commentsList{{ $video->id }}">
|
||||
@forelse($video->comments()->whereNull('parent_id')->with('user', 'replies.user')->latest()->get() as $comment)
|
||||
@include('videos.partials.comment', ['comment' => $comment])
|
||||
@empty
|
||||
<p style="color: var(--text-secondary); text-align: center; padding: 20px;">No comments yet. Be the first to
|
||||
comment!</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function submitComment(videoId) {
|
||||
const body = document.getElementById('commentBody' + videoId).value.trim();
|
||||
if (!body) return;
|
||||
|
||||
fetch(`/videos/${videoId}/comments`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body: body
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('Failed to post comment: ' + error);
|
||||
})
|
||||
.then(data => {
|
||||
if (data & amp; & amp; data.success) {
|
||||
document.getElementById('commentBody' + videoId).value = '';
|
||||
addCommentToList(data.comment, videoId);
|
||||
} else {
|
||||
alert('Failed to post comment');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addCommentToList(comment, videoId) {
|
||||
const commentsList = document.getElementById('commentsList' + videoId);
|
||||
const commentHtml = `
|
||||
<div class="comment-item" style="display: flex; gap: 12px; margin-bottom: 16px;" id="comment-${comment.id}">
|
||||
<img src="${comment.user.avatar_url}" class="channel-avatar" style="width: 36px; height: 36px; flex-shrink: 0;" alt="${comment.user.name}">
|
||||
<div style="flex: 1;">
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
|
||||
<span style="font-weight: 600; font-size: 14px;">${comment.user.name}</span>
|
||||
<span style="color: var(--text-secondary); font-size: 12px;">just now</span>
|
||||
</div>
|
||||
<div class="comment-body" style="font-size: 14px; line-height: 1.5; word-wrap: break-word;">
|
||||
${comment.body.replace(/@(\\\\w+)/g, '<span style="color: #3ea6ff; font-weight: 500;">@$1</span>')}
|
||||
</div>
|
||||
<div style="display: flex; gap: 12px; margin-top: 8px;">
|
||||
<button onclick="toggleReplyForm(${comment.id})" style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||
Reply
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
commentsList.insertAdjacentHTML('afterbegin', commentHtml);
|
||||
const commentCount = commentsList.closest('.comments-section').querySelector('h3 span');
|
||||
if (commentCount) {
|
||||
const count = parseInt(commentCount.textContent.match(/\\((\\d+)\\)/)?.[2] || 0) + 1;
|
||||
commentCount.textContent = `(${count})`;
|
||||
}
|
||||
}
|
||||
|
||||
function deleteComment(commentId) {
|
||||
if (confirm('Are you sure you want to delete this comment?')) {
|
||||
fetch(`/comments/${commentId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('comment-' + commentId).remove();
|
||||
// Update comment count for all comment sections (simplified)
|
||||
document.querySelectorAll('.comments-section h3 span').forEach(span => {
|
||||
const count = parseInt(span.textContent.match(/\\((\\d+)\\)/)?.[2] || 0) - 1;
|
||||
span.textContent = `(${Math.max(0, count)})`;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const commentTexts = document.querySelectorAll('.comment-body');
|
||||
commentTexts.forEach(text => {
|
||||
const html = text.innerHTML.replace(/@(\\w+)/g,
|
||||
'<span style="color: #3ea6ff; font-weight: 500;">@$1</span>');
|
||||
text.innerHTML = html;
|
||||
});
|
||||
});
|
||||
|
||||
function submitReply(videoId, parentId) {
|
||||
const textarea = document.querySelector(`#replyForm${parentId} textarea`);
|
||||
const body = textarea.value.trim();
|
||||
if (!body) return;
|
||||
|
||||
fetch(`/videos/${videoId}/comments`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body: body,
|
||||
parent_id: parentId
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@ -1,5 +1,6 @@
|
||||
<!-- Edit Video Modal - Cute Staged Pop-up -->
|
||||
<div class="modal fade" id="editVideoModal" tabindex="-1" aria-labelledby="editVideoModalLabel" aria-hidden="true" data-bs-backdrop="static">
|
||||
<div class="modal fade" id="editVideoModal" tabindex="-1" aria-labelledby="editVideoModalLabel" aria-hidden="true"
|
||||
data-bs-backdrop="static">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content edit-modal-content">
|
||||
<!-- Header -->
|
||||
@ -13,7 +14,8 @@
|
||||
<span class="edit-subtitle">Update your video</span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white" onclick="closeEditVideoModal()" aria-label="Close"></button>
|
||||
<button type="button" class="btn-close btn-close-white" onclick="closeEditVideoModal()"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
@ -58,7 +60,8 @@
|
||||
<i class="bi bi-image"></i> Current Thumbnail
|
||||
</label>
|
||||
<div class="current-thumbnail-wrapper" id="current-thumbnail-wrapper">
|
||||
<img id="current-thumbnail-preview" src="" alt="Current thumbnail" class="current-thumbnail-img">
|
||||
<img id="current-thumbnail-preview" src="" alt="Current thumbnail"
|
||||
class="current-thumbnail-img">
|
||||
<div class="thumbnail-placeholder" id="thumbnail-placeholder">
|
||||
<i class="bi bi-card-image"></i>
|
||||
<span>No thumbnail</span>
|
||||
@ -70,8 +73,7 @@
|
||||
<label class="form-label">
|
||||
<i class="bi bi-card-heading"></i> Title *
|
||||
</label>
|
||||
<input type="text" name="title" id="edit-video-title" required
|
||||
class="form-input"
|
||||
<input type="text" name="title" id="edit-video-title" required class="form-input"
|
||||
placeholder="Give your video a catchy title">
|
||||
</div>
|
||||
|
||||
@ -79,8 +81,7 @@
|
||||
<label class="form-label">
|
||||
<i class="bi bi-text-paragraph"></i> Description
|
||||
</label>
|
||||
<textarea name="description" id="edit-video-description" rows="3"
|
||||
class="form-textarea"
|
||||
<textarea name="description" id="edit-video-description" rows="3" class="form-textarea"
|
||||
placeholder="Tell viewers about your video"></textarea>
|
||||
</div>
|
||||
|
||||
@ -144,7 +145,8 @@
|
||||
<p class="filename" id="edit-thumbnail-filename"></p>
|
||||
<p id="edit-thumbnail-filesize"></p>
|
||||
</div>
|
||||
<button type="button" class="btn-remove-file" onclick="removeEditThumbnail(event)">
|
||||
<button type="button" class="btn-remove-file"
|
||||
onclick="removeEditThumbnail(event)">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -251,9 +253,11 @@
|
||||
transform: scale(0.9) translateY(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: scale(1.02) translateY(-5px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1) translateY(0);
|
||||
opacity: 1;
|
||||
@ -413,6 +417,7 @@
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
@ -494,7 +499,8 @@
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.form-input, .form-textarea {
|
||||
.form-input,
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
background: #121212;
|
||||
border: 1px solid #333;
|
||||
@ -505,13 +511,15 @@
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.form-input:focus, .form-textarea:focus {
|
||||
.form-input:focus,
|
||||
.form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: #e61e1e;
|
||||
box-shadow: 0 0 0 3px rgba(230, 30, 30, 0.15);
|
||||
}
|
||||
|
||||
.form-input::placeholder, .form-textarea::placeholder {
|
||||
.form-input::placeholder,
|
||||
.form-textarea::placeholder {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@ -613,8 +621,15 @@
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-8px); }
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone-title {
|
||||
@ -780,7 +795,9 @@
|
||||
border-top: 1px solid #2a2a2a;
|
||||
}
|
||||
|
||||
.btn-prev, .btn-next, .btn-save-changes {
|
||||
.btn-prev,
|
||||
.btn-next,
|
||||
.btn-save-changes {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@ -804,18 +821,21 @@
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-next, .btn-save-changes {
|
||||
.btn-next,
|
||||
.btn-save-changes {
|
||||
background: linear-gradient(135deg, #e61e1e 0%, #ff4757 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(230, 30, 30, 0.3);
|
||||
}
|
||||
|
||||
.btn-next:hover, .btn-save-changes:hover {
|
||||
.btn-next:hover,
|
||||
.btn-save-changes:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(230, 30, 30, 0.4);
|
||||
}
|
||||
|
||||
.btn-next:disabled, .btn-save-changes:disabled {
|
||||
.btn-next:disabled,
|
||||
.btn-save-changes:disabled {
|
||||
background: #444;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
@ -872,7 +892,9 @@
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.btn-prev, .btn-next, .btn-save-changes {
|
||||
.btn-prev,
|
||||
.btn-next,
|
||||
.btn-save-changes {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
@ -880,13 +902,13 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let currentEditStep = 1;
|
||||
const totalEditSteps = 3;
|
||||
let currentVideoId = null;
|
||||
window.currentEditStep = window.currentEditStep ?? 1;
|
||||
window.totalEditSteps = window.totalEditSteps ?? 3;
|
||||
window.currentVideoId = window.currentVideoId ?? null;
|
||||
|
||||
// Open modal and load video data
|
||||
function openEditVideoModal(videoId) {
|
||||
currentVideoId = videoId;
|
||||
window.currentVideoId = videoId;
|
||||
|
||||
// Fetch video data
|
||||
fetch(`/videos/${videoId}/edit`, {
|
||||
@ -944,7 +966,7 @@ function openEditVideoModal(videoId) {
|
||||
document.getElementById('edit-status-message').className = 'status-message-modal';
|
||||
|
||||
// Reset step to 1
|
||||
currentEditStep = 1;
|
||||
window.currentEditStep = 1;
|
||||
updateEditStepDisplay();
|
||||
|
||||
// Update form action
|
||||
@ -977,8 +999,10 @@ function closeEditVideoModal() {
|
||||
|
||||
modalEl.addEventListener('hidden.bs.modal', function() {
|
||||
modalEl.classList.remove('show');
|
||||
currentVideoId = null;
|
||||
}, { once: true });
|
||||
window.currentVideoId = null;
|
||||
}, {
|
||||
once: true
|
||||
});
|
||||
}
|
||||
|
||||
// Step Navigation
|
||||
@ -992,12 +1016,12 @@ function editNextStep(step) {
|
||||
}
|
||||
}
|
||||
|
||||
currentEditStep = step;
|
||||
window.currentEditStep = step;
|
||||
updateEditStepDisplay();
|
||||
}
|
||||
|
||||
function editPrevStep(step) {
|
||||
currentEditStep = step;
|
||||
window.currentEditStep = step;
|
||||
updateEditStepDisplay();
|
||||
}
|
||||
|
||||
@ -1007,16 +1031,16 @@ function updateEditStepDisplay() {
|
||||
const stepNum = index + 1;
|
||||
el.classList.remove('active', 'completed');
|
||||
|
||||
if (stepNum === currentEditStep) {
|
||||
if (stepNum === window.currentEditStep) {
|
||||
el.classList.add('active');
|
||||
} else if (stepNum < currentEditStep) {
|
||||
} else if (stepNum < window.currentEditStep) {
|
||||
el.classList.add('completed');
|
||||
}
|
||||
});
|
||||
|
||||
// Update step lines
|
||||
document.querySelectorAll('.step-line').forEach((el, index) => {
|
||||
if (index < currentEditStep - 1) {
|
||||
if (index < window.currentEditStep - 1) {
|
||||
el.classList.add('active');
|
||||
} else {
|
||||
el.classList.remove('active');
|
||||
@ -1026,7 +1050,7 @@ function updateEditStepDisplay() {
|
||||
// Update step content
|
||||
document.querySelectorAll('.edit-step-content').forEach(el => {
|
||||
el.classList.remove('active');
|
||||
if (parseInt(el.dataset.step) === currentEditStep) {
|
||||
if (parseInt(el.dataset.step) === window.currentEditStep) {
|
||||
el.classList.add('active');
|
||||
}
|
||||
});
|
||||
@ -1066,7 +1090,8 @@ function handleEditThumbnailSelect(input) {
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0];
|
||||
document.getElementById('edit-thumbnail-filename').textContent = file.name;
|
||||
document.getElementById('edit-thumbnail-filesize').textContent = (file.size / 1024 / 1024).toFixed(2) + ' MB';
|
||||
document.getElementById('edit-thumbnail-filesize').textContent = (file.size / 1024 / 1024).toFixed(2) +
|
||||
' MB';
|
||||
|
||||
// Show thumbnail preview
|
||||
const reader = new FileReader();
|
||||
@ -1120,7 +1145,7 @@ document.getElementById('edit-video-form-modal').addEventListener('submit', func
|
||||
submitBtn.innerHTML = '<i class="bi bi-arrow-repeat"></i> Saving...';
|
||||
document.getElementById('edit-status-message').className = 'status-message-modal';
|
||||
|
||||
fetch(`/videos/${currentVideoId}`, {
|
||||
fetch(`/videos/${window.currentVideoId}`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
@ -1137,8 +1162,10 @@ document.getElementById('edit-video-form-modal').addEventListener('submit', func
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('edit-status-message').innerHTML = '<i class="bi bi-check-circle-fill"></i> ' + data.message;
|
||||
document.getElementById('edit-status-message').className = 'status-message-modal success';
|
||||
document.getElementById('edit-status-message').innerHTML =
|
||||
'<i class="bi bi-check-circle-fill"></i> ' + data.message;
|
||||
document.getElementById('edit-status-message').className =
|
||||
'status-message-modal success';
|
||||
|
||||
// Close modal and reload page after short delay
|
||||
setTimeout(() => {
|
||||
@ -1151,7 +1178,8 @@ document.getElementById('edit-video-form-modal').addEventListener('submit', func
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
document.getElementById('edit-status-message').innerHTML = '<i class="bi bi-exclamation-circle-fill"></i> ' + error.message;
|
||||
document.getElementById('edit-status-message').innerHTML =
|
||||
'<i class="bi bi-exclamation-circle-fill"></i> ' + error.message;
|
||||
document.getElementById('edit-status-message').className = 'status-message-modal error';
|
||||
|
||||
submitBtn.disabled = false;
|
||||
|
||||
@ -1,20 +1,43 @@
|
||||
<div class="comment-item" style="display: flex; gap: 12px; margin-bottom: 16px;" id="comment-{{ $comment->id }}">
|
||||
<img src="{{ $comment->user->avatar_url }}" class="channel-avatar" style="width: 36px; height: 36px; flex-shrink: 0;" alt="{{ $comment->user->name }}">
|
||||
<img src="{{ $comment->user->avatar_url }}" class="channel-avatar" style="width: 36px; height: 36px; flex-shrink: 0;"
|
||||
alt="{{ $comment->user->name }}">
|
||||
<div style="flex: 1;">
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
|
||||
<span style="font-weight: 600; font-size: 14px;">{{ $comment->user->name }}</span>
|
||||
<span style="color: var(--text-secondary); font-size: 12px;">{{ $comment->created_at->diffForHumans() }}</span>
|
||||
<span
|
||||
style="color: var(--text-secondary); font-size: 12px;">{{ $comment->created_at->diffForHumans() }}</span>
|
||||
</div>
|
||||
<div class="comment-body" style="font-size: 14px; line-height: 1.5; word-wrap: break-word;">
|
||||
{{ $comment->body }}
|
||||
</div>
|
||||
@auth
|
||||
@if (Auth::id() === $comment->user_id)
|
||||
<div id="commentEditWrap{{ $comment->id }}" style="display:none; margin-top:8px;">
|
||||
<textarea id="commentEditInput{{ $comment->id }}" class="form-control" rows="3"
|
||||
style="background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 8px; padding: 8px; width: 100%; resize: vertical; font-size: 14px;">{{ $comment->body }}</textarea>
|
||||
<div style="display:flex; gap:8px; justify-content:flex-end; margin-top:8px;">
|
||||
<button type="button" class="yt-action-btn" style="font-size: 12px; padding: 6px 12px;"
|
||||
onclick="cancelEditComment({{ $comment->id }})">Cancel</button>
|
||||
<button type="button" class="yt-action-btn"
|
||||
style="background: var(--brand-red); color: white; font-size: 12px; padding: 6px 12px;"
|
||||
onclick="saveEditComment({{ $comment->id }})">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endauth
|
||||
<div style="display: flex; gap: 12px; margin-top: 8px;">
|
||||
@auth
|
||||
<button onclick="toggleReplyForm({{ $comment->id }})" style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||
<button onclick="toggleReplyForm({{ $comment->id }})"
|
||||
style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||
Reply
|
||||
</button>
|
||||
@if (Auth::id() === $comment->user_id)
|
||||
<button onclick="deleteComment({{ $comment->id }})" style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||
<button onclick="startEditComment({{ $comment->id }})"
|
||||
style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||
Edit
|
||||
</button>
|
||||
<button onclick="deleteComment({{ $comment->id }})"
|
||||
style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||
Delete
|
||||
</button>
|
||||
@endif
|
||||
@ -24,22 +47,22 @@
|
||||
<!-- Reply Form -->
|
||||
<div id="replyForm{{ $comment->id }}" style="display: none; margin-top: 12px;">
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<textarea
|
||||
class="form-control"
|
||||
placeholder="Write a reply..."
|
||||
rows="2"
|
||||
style="background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 8px; padding: 8px; width: 100%; resize: none; font-size: 14px;"
|
||||
></textarea>
|
||||
<textarea class="form-control" placeholder="Write a reply..." rows="2"
|
||||
style="background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 8px; padding: 8px; width: 100%; resize: none; font-size: 14px;"></textarea>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; margin-top: 8px; justify-content: flex-end;">
|
||||
<button type="button" class="yt-action-btn" style="font-size: 12px; padding: 6px 12px;" onclick="toggleReplyForm({{ $comment->id }})">Cancel</button>
|
||||
<button type="button" class="yt-action-btn" style="background: var(--brand-red); color: white; font-size: 12px; padding: 6px 12px;" onclick="submitReply({{ $video->id ?? $comment->video_id }}, {{ $comment->id }})">Reply</button>
|
||||
<button type="button" class="yt-action-btn" style="font-size: 12px; padding: 6px 12px;"
|
||||
onclick="toggleReplyForm({{ $comment->id }})">Cancel</button>
|
||||
<button type="button" class="yt-action-btn"
|
||||
style="background: var(--brand-red); color: white; font-size: 12px; padding: 6px 12px;"
|
||||
onclick="submitReply({{ $video->id ?? $comment->video_id }}, {{ $comment->id }})">Reply</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Replies -->
|
||||
@if ($comment->replies && $comment->replies->count() > 0)
|
||||
<div style="margin-left: 24px; margin-top: 12px; border-left: 2px solid var(--border-color); padding-left: 12px;">
|
||||
<div
|
||||
style="margin-left: 24px; margin-top: 12px; border-left: 2px solid var(--border-color); padding-left: 12px;">
|
||||
@foreach ($comment->replies as $reply)
|
||||
@include('videos.partials.comment', ['comment' => $reply, 'video' => $video ?? null])
|
||||
@endforeach
|
||||
@ -53,4 +76,60 @@ function toggleReplyForm(commentId) {
|
||||
const form = document.getElementById('replyForm' + commentId);
|
||||
form.style.display = form.style.display === 'none' ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function startEditComment(commentId) {
|
||||
const wrap = document.getElementById('commentEditWrap' + commentId);
|
||||
const body = document.querySelector('#comment-' + commentId + ' .comment-body');
|
||||
const input = document.getElementById('commentEditInput' + commentId);
|
||||
if (!wrap || !body || !input) return;
|
||||
|
||||
input.value = body.textContent.trim();
|
||||
wrap.style.display = 'block';
|
||||
body.style.display = 'none';
|
||||
}
|
||||
|
||||
function cancelEditComment(commentId) {
|
||||
const wrap = document.getElementById('commentEditWrap' + commentId);
|
||||
const body = document.querySelector('#comment-' + commentId + ' .comment-body');
|
||||
if (wrap) wrap.style.display = 'none';
|
||||
if (body) body.style.display = 'block';
|
||||
}
|
||||
|
||||
async function saveEditComment(commentId) {
|
||||
const input = document.getElementById('commentEditInput' + commentId);
|
||||
const bodyEl = document.querySelector('#comment-' + commentId + ' .comment-body');
|
||||
const wrap = document.getElementById('commentEditWrap' + commentId);
|
||||
if (!input || !bodyEl || !wrap) return;
|
||||
|
||||
const body = input.value.trim();
|
||||
if (!body) return;
|
||||
|
||||
try {
|
||||
const res = await fetch(`/comments/${commentId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body
|
||||
})
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
bodyEl.textContent = data.body || body;
|
||||
bodyEl.dataset.timeEnhanced = '';
|
||||
if (typeof enhanceCommentBodyWithTimeBadges === 'function') {
|
||||
enhanceCommentBodyWithTimeBadges(document.getElementById('comment-' + commentId));
|
||||
}
|
||||
wrap.style.display = 'none';
|
||||
bodyEl.style.display = 'block';
|
||||
} else {
|
||||
alert(data.error || 'Failed to update comment');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to update comment: ' + e.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
1116
resources/views/videos/types/backup/generic.blade.php
Normal file
1116
resources/views/videos/types/backup/generic.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
4184
resources/views/videos/types/backup/match.blade.php
Normal file
4184
resources/views/videos/types/backup/match.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
1069
resources/views/videos/types/backup/music.blade.php
Normal file
1069
resources/views/videos/types/backup/music.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -210,75 +210,6 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.video-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.yt-action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
border: none;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.yt-action-btn:hover {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
.action-btn,
|
||||
.comments-section .action-btn {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 8px 14px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.action-btn:hover,
|
||||
.comments-section .action-btn:hover {
|
||||
background: var(--border-color);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.action-btn:active,
|
||||
.comments-section .action-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.action-btn svg,
|
||||
.action-btn i,
|
||||
.comments-section .action-btn svg,
|
||||
.comments-section .action-btn i {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-btn.comment-btn {
|
||||
background: var(--brand-red);
|
||||
color: white;
|
||||
border-color: var(--brand-red);
|
||||
}
|
||||
|
||||
.yt-action-btn.liked {
|
||||
color: var(--brand-red);
|
||||
}
|
||||
|
||||
/* Channel Row */
|
||||
.channel-row {
|
||||
display: flex;
|
||||
@ -310,18 +241,6 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.subscribe-btn {
|
||||
background: white;
|
||||
color: black;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 18px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Description */
|
||||
.video-description {
|
||||
background: var(--bg-secondary);
|
||||
@ -546,76 +465,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Channel Row - All in one line -->
|
||||
<div class="channel-row"
|
||||
style="display: flex; align-items: center; justify-content: space-between; padding: 12px 0; flex-wrap: wrap; gap: 16px;">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<a href="{{ route('channel', $video->user_id) }}" class="channel-info text-decoration-none"
|
||||
style="color: inherit; display: flex; align-items: center; gap: 12px;">
|
||||
@if ($video->user)
|
||||
<img src="{{ $video->user->avatar_url }}" class="channel-avatar"
|
||||
style="width: 40px; height: 40px; border-radius: 50%;" alt="{{ $video->user->name }}">
|
||||
@else
|
||||
<div class="channel-avatar" style="width: 40px; height: 40px; border-radius: 50%;"></div>
|
||||
@endif
|
||||
<div>
|
||||
<div class="channel-name" style="font-weight: 600;">{{ $video->user->name ?? 'Unknown' }}</div>
|
||||
<div class="channel-subs" style="font-size: 12px; color: var(--text-secondary);">
|
||||
{{ number_format($video->user->subscriber_count ?? 0) }} subscribers</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="video-actions" style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
|
||||
@auth
|
||||
@if (Auth::id() !== $video->user_id)
|
||||
<button class="subscribe-btn">Subscribe</button>
|
||||
@else
|
||||
<button class="action-btn" onclick="openEditVideoModal({{ $video->id }})">
|
||||
<i class="bi bi-pencil"></i>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
@endif
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="subscribe-btn">Subscribe</a>
|
||||
@endauth
|
||||
|
||||
@auth
|
||||
<form method="POST"
|
||||
action="{{ $video->isLikedBy(Auth::user()) ? route('videos.unlike', $video->id) : route('videos.like', $video->id) }}"
|
||||
class="d-inline">
|
||||
@csrf
|
||||
<button type="submit" class="action-btn {{ $video->isLikedBy(Auth::user()) ? 'liked' : '' }}">
|
||||
<i
|
||||
class="bi {{ $video->isLikedBy(Auth::user()) ? 'bi-hand-thumbs-up-fill' : 'bi-hand-thumbs-up' }}"></i>
|
||||
{{ $video->like_count > 0 ? number_format($video->like_count) : 'Like' }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="yt-action-btn">
|
||||
<i class="bi bi-hand-thumbs-up"></i> Like
|
||||
</a>
|
||||
@endauth
|
||||
|
||||
@if ($video->isShareable())
|
||||
<button class="action-btn"
|
||||
onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
||||
<i class="bi bi-share"></i> Share
|
||||
</button>
|
||||
@endif
|
||||
|
||||
<!-- Save to Playlist Button -->
|
||||
<button class="action-btn" onclick="openAddToPlaylistModal({{ $video->id }})">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
|
||||
</svg>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<x-channel-row :video="$video" />
|
||||
|
||||
<!-- Description Box -->
|
||||
@if ($video->description)
|
||||
@ -700,16 +550,15 @@
|
||||
<img src="{{ Auth::user()->avatar_url }}" class="channel-avatar" style="width: 40px; height: 40px;"
|
||||
alt="{{ Auth::user()->name }}">
|
||||
<div style="flex: 1; display: flex; align-items: center; gap: 8px;">
|
||||
<textarea id="commentBody" class="form-control" placeholder="Add a comment... Use @ to mention someone"
|
||||
rows="1"
|
||||
<textarea id="commentBody" class="form-control" placeholder="Add a comment... Use @ to mention someone" rows="1"
|
||||
style="background: transparent; border: none; border-bottom: 2px solid var(--border-color); color: var(--text-primary); border-radius: 0; padding: 12px 0 8px 0; flex: 1; margin: 0; height: 40px; font-size: 14px; outline: none; overflow: hidden;"></textarea>
|
||||
<button type="button" class="action-btn"
|
||||
onclick="document.getElementById('commentBody').value = ''" style="flex-shrink: 0;">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
<button type="button" class="action-btn comment-btn"
|
||||
onclick="submitComment({{ $video->id }})" style="flex-shrink: 0;">
|
||||
<button type="button" class="action-btn comment-btn" onclick="submitComment({{ $video->id }})"
|
||||
style="flex-shrink: 0;">
|
||||
<i class="bi bi-chat-dots"></i>
|
||||
<span>Comment</span>
|
||||
</button>
|
||||
|
||||
3515
resources/views/videos/types/match.blade copy.php
Normal file
3515
resources/views/videos/types/match.blade copy.php
Normal file
File diff suppressed because it is too large
Load Diff
9
resources/views/videos/types/match.blade.backup.php
Normal file
9
resources/views/videos/types/match.blade.backup.php
Normal file
@ -0,0 +1,9 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', ($video->title ?? 'Match Video') . ' | ' . config('app.name'))
|
||||
|
||||
@section('extra_styles')
|
||||
<style>
|
||||
/* ===== Video Section ===== */
|
||||
.yt-video-section {
|
||||
flex: 1;
|
||||
min
|
||||
File diff suppressed because it is too large
Load Diff
3615
resources/views/videos/types/match.blade.php.bak_20260316_025638
Normal file
3615
resources/views/videos/types/match.blade.php.bak_20260316_025638
Normal file
File diff suppressed because it is too large
Load Diff
3682
resources/views/videos/types/match.blade.pre_coach_review_backup.php
Normal file
3682
resources/views/videos/types/match.blade.pre_coach_review_backup.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -210,81 +210,13 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.video-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.yt-action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
border: none;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.yt-action-btn:hover {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
.action-btn,
|
||||
.comments-section .action-btn {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 8px 14px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.action-btn:hover,
|
||||
.comments-section .action-btn:hover {
|
||||
background: var(--border-color);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.action-btn:active,
|
||||
.comments-section .action-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.action-btn svg,
|
||||
.action-btn i,
|
||||
.comments-section .action-btn svg,
|
||||
.comments-section .action-btn i {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-btn.comment-btn {
|
||||
background: var(--brand-red);
|
||||
color: white;
|
||||
border-color: var(--brand-red);
|
||||
}
|
||||
|
||||
.yt-action-btn.liked {
|
||||
color: var(--brand-red);
|
||||
}
|
||||
|
||||
/* Channel Row */
|
||||
.channel-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.channel-info {
|
||||
@ -310,18 +242,6 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.subscribe-btn {
|
||||
background: white;
|
||||
color: black;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 18px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Description */
|
||||
.video-description {
|
||||
background: var(--bg-secondary);
|
||||
@ -432,8 +352,16 @@
|
||||
|
||||
.video-actions {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
justify-content: flex-start;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.video-actions>.desktop-action {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.mobile-action-dropdown {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.yt-main {
|
||||
@ -546,76 +474,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Channel Row - All in one line -->
|
||||
<div class="channel-row"
|
||||
style="display: flex; align-items: center; justify-content: space-between; padding: 12px 0; flex-wrap: wrap; gap: 16px;">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<a href="{{ route('channel', $video->user_id) }}" class="channel-info text-decoration-none"
|
||||
style="color: inherit; display: flex; align-items: center; gap: 12px;">
|
||||
@if ($video->user)
|
||||
<img src="{{ $video->user->avatar_url }}" class="channel-avatar"
|
||||
style="width: 40px; height: 40px; border-radius: 50%;" alt="{{ $video->user->name }}">
|
||||
@else
|
||||
<div class="channel-avatar" style="width: 40px; height: 40px; border-radius: 50%;"></div>
|
||||
@endif
|
||||
<div>
|
||||
<div class="channel-name" style="font-weight: 600;">{{ $video->user->name ?? 'Unknown' }}</div>
|
||||
<div class="channel-subs" style="font-size: 12px; color: var(--text-secondary);">
|
||||
{{ number_format($video->user->subscriber_count ?? 0) }} subscribers</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="video-actions" style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
|
||||
@auth
|
||||
@if (Auth::id() !== $video->user_id)
|
||||
<button class="subscribe-btn">Subscribe</button>
|
||||
@else
|
||||
<button class="action-btn" onclick="openEditVideoModal({{ $video->id }})">
|
||||
<i class="bi bi-pencil"></i>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
@endif
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="subscribe-btn">Subscribe</a>
|
||||
@endauth
|
||||
|
||||
@auth
|
||||
<form method="POST"
|
||||
action="{{ $video->isLikedBy(Auth::user()) ? route('videos.unlike', $video->id) : route('videos.like', $video->id) }}"
|
||||
class="d-inline">
|
||||
@csrf
|
||||
<button type="submit" class="action-btn {{ $video->isLikedBy(Auth::user()) ? 'liked' : '' }}">
|
||||
<i
|
||||
class="bi {{ $video->isLikedBy(Auth::user()) ? 'bi-hand-thumbs-up-fill' : 'bi-hand-thumbs-up' }}"></i>
|
||||
{{ $video->like_count > 0 ? number_format($video->like_count) : 'Like' }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="yt-action-btn">
|
||||
<i class="bi bi-hand-thumbs-up"></i> Like
|
||||
</a>
|
||||
@endauth
|
||||
|
||||
@if ($video->isShareable())
|
||||
<button class="action-btn"
|
||||
onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
||||
<i class="bi bi-share"></i> Share
|
||||
</button>
|
||||
@endif
|
||||
|
||||
<!-- Save to Playlist Button -->
|
||||
<button class="action-btn" onclick="openAddToPlaylistModal({{ $video->id }})">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
|
||||
</svg>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<x-channel-row :video="$video" />
|
||||
|
||||
<!-- Description Box -->
|
||||
@if ($video->description)
|
||||
@ -687,49 +546,10 @@
|
||||
</style>
|
||||
@endif
|
||||
|
||||
<!-- Comment Section -->
|
||||
<div class="comments-section"
|
||||
style="margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--border-color);">
|
||||
<h3 style="font-size: 18px; font-weight: 600; margin-bottom: 16px;">
|
||||
Comments <span
|
||||
style="color: var(--text-secondary); font-weight: 400;">({{ $video->comment_count }})</span>
|
||||
</h3>
|
||||
<x-video-comments :video="$video" />
|
||||
|
||||
|
||||
@auth
|
||||
<div class="comment-form" style="display: flex; gap: 12px; margin-bottom: 24px;">
|
||||
<img src="{{ Auth::user()->avatar_url }}" class="channel-avatar" style="width: 40px; height: 40px;"
|
||||
alt="{{ Auth::user()->name }}">
|
||||
<div style="flex: 1; display: flex; align-items: center; gap: 8px;">
|
||||
<textarea id="commentBody" class="form-control" placeholder="Add a comment... Use @ to mention someone"
|
||||
rows="1"
|
||||
style="background: transparent; border: none; border-bottom: 2px solid var(--border-color); color: var(--text-primary); border-radius: 0; padding: 12px 0 8px 0; flex: 1; margin: 0; height: 40px; font-size: 14px; outline: none; overflow: hidden;"></textarea>
|
||||
<button type="button" class="action-btn"
|
||||
onclick="document.getElementById('commentBody').value = ''" style="flex-shrink: 0;">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
<button type="button" class="action-btn comment-btn"
|
||||
onclick="submitComment({{ $video->id }})" style="flex-shrink: 0;">
|
||||
<i class="bi bi-chat-dots"></i>
|
||||
<span>Comment</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div
|
||||
style="margin-bottom: 24px; padding: 16px; background: var(--bg-secondary); border-radius: 8px; text-align: center;">
|
||||
<a href="{{ route('login') }}" style="color: var(--brand-red);">Sign in</a> to comment
|
||||
</div>
|
||||
@endauth
|
||||
|
||||
<div id="commentsList">
|
||||
@forelse($video->comments()->whereNull('parent_id')->with('user', 'replies.user')->latest()->get() as $comment)
|
||||
@include('videos.partials.comment', ['comment' => $comment])
|
||||
@empty
|
||||
<p style="color: var(--text-secondary); text-align: center; padding: 20px;">No comments yet. Be the
|
||||
first to comment!</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@ -872,8 +692,7 @@
|
||||
alt="{{ $playlistVideo->title }}">
|
||||
@endif
|
||||
@if ($playlistVideo->duration)
|
||||
<span
|
||||
class="yt-video-duration">{{ gmdate('i:s', $playlistVideo->duration) }}</span>
|
||||
<span class="yt-video-duration">{{ gmdate('i:s', $playlistVideo->duration) }}</span>
|
||||
@endif
|
||||
@if ($playlistVideo->is_shorts)
|
||||
<span class="yt-shorts-badge"
|
||||
|
||||
@ -133,5 +133,3 @@ Route::delete('/points/{point}', [MatchEventController::class, 'destroyPoint'])-
|
||||
Route::post('/videos/{video}/reviews', [MatchEventController::class, 'storeReview'])->name('match.storeReview');
|
||||
Route::put('/reviews/{review}', [MatchEventController::class, 'updateReview'])->name('match.updateReview');
|
||||
Route::delete('/reviews/{review}', [MatchEventController::class, 'destroyReview'])->name('match.destroyReview');
|
||||
|
||||
// });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user