admin panel added and comments are working and likes are working
This commit is contained in:
1 parent
72e9439727
commit
a28023c29b
32 files changed
+5077
-2999
No files matched your search
@@ -0,0 +1,291 @@
|
||||
{{-- Video Type Icon (using Bootstrap Icons like video-card.blade.php) --}}
|
||||
@php
|
||||
$typeIcon = match($video->type) {
|
||||
'music' => 'bi-music-note',
|
||||
'match' => 'bi-trophy',
|
||||
default => 'bi-film',
|
||||
};
|
||||
@endphp
|
||||
|
||||
{{-- Video Title with Type Icon Inline --}}
|
||||
<h1 class="video-title" style="display: flex; align-items: center; gap: 10px; margin: 8px 0 6px;">
|
||||
<i class="bi {{ $typeIcon }}" style="color: #ef4444; margin-right: 6px;"></i>
|
||||
<span>{{ $video->title }}</span>
|
||||
</h1>
|
||||
|
||||
{{-- Channel Row with Actions Inline --}}
|
||||
<div class="channel-row" style="display: flex; align-items: center; justify-content: space-between; padding: 6px 0; flex-wrap: wrap; gap: 12px;">
|
||||
{{-- Left: Channel Info --}}
|
||||
<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: 36px; height: 36px;" alt="{{ $video->user->name }}">
|
||||
@else
|
||||
<div class="channel-avatar" style="width: 36px; height: 36px;"></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>
|
||||
|
||||
{{-- Subscribe Button --}}
|
||||
@auth
|
||||
@if(Auth::id() !== $video->user_id)
|
||||
<button class="subscribe-btn" style="background: white; color: black; border: none; padding: 8px 16px; border-radius: 18px; font-weight: 600; font-size: 14px; cursor: pointer; white-space: nowrap;">
|
||||
Subscribe
|
||||
</button>
|
||||
@endif
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="subscribe-btn" style="background: white; color: black; border: none; padding: 8px 16px; border-radius: 18px; font-weight: 600; font-size: 14px; text-decoration: none; white-space: nowrap; display: inline-block;">
|
||||
Subscribe
|
||||
</a>
|
||||
@endauth
|
||||
</div>
|
||||
|
||||
{{-- Right: Action Buttons (Like, Edit, Share) --}}
|
||||
<div class="video-actions">
|
||||
@auth
|
||||
{{-- Like Button with Icon and Count --}}
|
||||
<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="yt-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>
|
||||
|
||||
{{-- Edit Button - Only for video owner --}}
|
||||
@if(Auth::id() === $video->user_id)
|
||||
<button class="yt-action-btn" onclick="openEditVideoModal({{ $video->id }})">
|
||||
<i class="bi bi-pencil"></i> Edit
|
||||
</button>
|
||||
@endif
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="yt-action-btn">
|
||||
<i class="bi bi-hand-thumbs-up"></i> Like
|
||||
</a>
|
||||
@endauth
|
||||
|
||||
{{-- Share Button --}}
|
||||
@if($video->isShareable())
|
||||
<button class="yt-action-btn" onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
||||
<i class="bi bi-share"></i> Share
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Description Box (Expandable) --}}
|
||||
@if($video->description)
|
||||
@php
|
||||
// Parse markdown description
|
||||
$fullDescription = $video->description;
|
||||
$shortDescription = Str::limit($fullDescription, 200);
|
||||
$needsExpand = strlen($fullDescription) > 200;
|
||||
@endphp
|
||||
|
||||
<div class="video-description-box" style="background: var(--bg-secondary); border-radius: 12px; padding: 12px; margin-bottom: 16px;">
|
||||
{{-- Fixed Stats Row (views + date - cannot be manipulated) --}}
|
||||
<div class="description-stats" style="font-size: 14px; font-weight: 500; margin-bottom: 8px;">
|
||||
<span>{{ number_format($video->view_count) }} views</span>
|
||||
<span>•</span>
|
||||
<span>{{ $video->created_at->format('M d, Y') }}</span>
|
||||
</div>
|
||||
|
||||
{{-- Separator Line --}}
|
||||
<div style="border-bottom: 1px solid var(--border-color); margin: 8px 0;"></div>
|
||||
|
||||
{{-- Description Content --}}
|
||||
<div class="description-content" id="descriptionContent">
|
||||
@if($needsExpand)
|
||||
<div class="description-short" id="descShort">
|
||||
<span class="description-text">{!! Str::markdown($shortDescription) !!}</span>
|
||||
<span class="description-ellipsis" style="color: var(--text-secondary);">... </span>
|
||||
</div>
|
||||
<div class="description-full" id="descFull" style="display: none;">
|
||||
<span class="description-text">{!! Str::markdown($fullDescription) !!}</span>
|
||||
</div>
|
||||
<button onclick="toggleDescription()" id="descToggleBtn" style="background: none; border: none; color: var(--text-primary); font-weight: 600; font-size: 14px; cursor: pointer; padding: 0; margin-top: 4px;">
|
||||
Show more
|
||||
</button>
|
||||
@else
|
||||
<span class="description-text">{!! Str::markdown($fullDescription) !!}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleDescription() {
|
||||
const descShort = document.getElementById('descShort');
|
||||
const descFull = document.getElementById('descFull');
|
||||
const toggleBtn = document.getElementById('descToggleBtn');
|
||||
|
||||
if (descShort.style.display !== 'none') {
|
||||
descShort.style.display = 'none';
|
||||
descFull.style.display = 'block';
|
||||
toggleBtn.textContent = 'Show less';
|
||||
} else {
|
||||
descShort.style.display = 'block';
|
||||
descFull.style.display = 'none';
|
||||
toggleBtn.textContent = 'Show more';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.video-description-box .description-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.video-description-box .description-text p {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.video-description-box .description-text p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.video-description-box .description-text a {
|
||||
color: #3ea6ff;
|
||||
}
|
||||
.video-description-box .description-text code {
|
||||
background: rgba(255,255,255,0.1);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.video-description-box .description-text pre {
|
||||
background: rgba(255,255,255,0.1);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.video-description-box .description-text ul,
|
||||
.video-description-box .description-text ol {
|
||||
padding-left: 20px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.video-description-box .description-text strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
</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>
|
||||
|
||||
{{-- Comment Form --}}
|
||||
@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;">
|
||||
<textarea
|
||||
id="commentBody"
|
||||
class="form-control"
|
||||
placeholder="Add a comment... Use @ to mention someone"
|
||||
rows="3"
|
||||
style="background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 8px; padding: 12px; width: 100%; resize: none;"
|
||||
></textarea>
|
||||
<div style="display: flex; gap: 8px; margin-top: 8px; justify-content: flex-end;">
|
||||
<button type="button" class="yt-action-btn" onclick="document.getElementById('commentBody').value = ''">Cancel</button>
|
||||
<button type="button" class="yt-action-btn" style="background: var(--brand-red); color: white;" onclick="submitComment({{ $video->id }})">Comment</button>
|
||||
</div>
|
||||
</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
|
||||
|
||||
{{-- Comments List --}}
|
||||
<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>
|
||||
function submitComment(videoId) {
|
||||
const body = document.getElementById('commentBody').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())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('commentBody').value = '';
|
||||
loadComments(videoId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadComments(videoId) {
|
||||
fetch(`/videos/${videoId}/comments`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Reload page to show new comments
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function deleteComment(commentId) {
|
||||
if (!confirm('Are you sure you want to delete this comment?')) return;
|
||||
|
||||
fetch(`/comments/${commentId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Highlight @mentions in comments
|
||||
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>
|
||||
Reference in new issue
Block a user