Add NAS file manager integration and all pending platform changes
- Installed p7h/nas-file-manager package via private VCS repo - Published config/nas-file-manager.php with super_admin middleware restriction - Added NAS env vars to .env.example - Created admin/nas-storage page with connection info panel and file browser widget - Added NAS Storage link to admin sidebar (super_admin only) - Added SuperAdminController@nasStorage method and admin.nas-storage route - Includes all accumulated branch changes: profile wall, 2FA, audit logs, settings panel, country/phone/timezone components, posts, slideshow, playlist shares, video downloads/shares, comment likes, notifications, social links, and more Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
d44490dfe0
commit
0b2e95ea65
161 files changed
+27496
-7250
No files matched your search
@@ -1,62 +1,220 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', isset($query) ? 'Search: ' . $query . ' | ' . config('app.name') : 'Video Gallery | ' . config('app.name'))
|
||||
@section('title', isset($query) ? 'Search: ' . $query . ' | ' . config('app.name') : config('app.name'))
|
||||
|
||||
@section('extra_styles')
|
||||
<style>
|
||||
/* Search info */
|
||||
.search-info {
|
||||
margin-bottom: 20px;
|
||||
padding: 16px;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 12px;
|
||||
}
|
||||
<style>
|
||||
/* ── Regular video grid ── */
|
||||
.yt-video-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
@media (max-width: 992px) { .yt-video-grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (max-width: 576px) { .yt-video-grid { grid-template-columns: 1fr; gap: 14px; } }
|
||||
|
||||
.search-info h2 {
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-info p {
|
||||
color: var(--text-secondary);
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
/* ── Playlist count badge (right strip on thumbnail) ── */
|
||||
.pl-count-badge {
|
||||
position: absolute; inset: 0 0 0 auto;
|
||||
width: 72px;
|
||||
background: rgba(0,0,0,.78);
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
gap: 4px; font-size: 12px; font-weight: 600; color: #fff;
|
||||
pointer-events: none;
|
||||
}
|
||||
.pl-count-badge i { font-size: 20px; }
|
||||
|
||||
/* Video Grid */
|
||||
.yt-video-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
/* ── Playlist type badge (top-left on thumbnail) ── */
|
||||
.feed-pl-type-badge {
|
||||
position: absolute; top: 8px; left: 8px;
|
||||
background: rgba(0,0,0,.75); color: #fff;
|
||||
font-size: 11px; font-weight: 700;
|
||||
padding: 3px 8px; border-radius: 4px;
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
text-transform: uppercase; letter-spacing: .4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) { .yt-video-grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (max-width: 576px) { .yt-video-grid { grid-template-columns: 1fr; } }
|
||||
/* ── Thumbnail orientation fix ── */
|
||||
.yt-video-card .yt-video-thumb img { object-fit: cover; }
|
||||
|
||||
/* Other styles unchanged */
|
||||
.yt-empty { text-align: center; padding: 80px 20px; }
|
||||
</style>
|
||||
/* ── Search result helpers ── */
|
||||
.search-info { margin-bottom: 20px; padding: 16px; background: var(--bg-secondary); border-radius: 12px; }
|
||||
.search-info h2 { font-size: 20px; margin: 0; }
|
||||
.search-info p { color: var(--text-secondary); margin: 8px 0 0; }
|
||||
.results-section-title {
|
||||
font-size: 15px; font-weight: 600; color: var(--text-secondary);
|
||||
text-transform: uppercase; letter-spacing: .05em;
|
||||
margin: 28px 0 14px;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.results-section-title::after { content: ''; flex: 1; height: 1px; background: var(--border-color); }
|
||||
|
||||
.yt-empty { text-align: center; padding: 80px 20px; }
|
||||
|
||||
</style>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@isset($query)
|
||||
<div class="search-info">
|
||||
<h2>Search results for "{{ $query }}"</h2>
|
||||
<p>{{ $videos->count() }} videos found</p>
|
||||
</div>
|
||||
@endisset
|
||||
|
||||
@if ($videos->isEmpty())
|
||||
{{-- ── Filter chip bar ── --}}
|
||||
@unless(isset($query))
|
||||
@php $activeFilter = request('filter', 'all'); @endphp
|
||||
<div class="yt-filter-bar">
|
||||
<a href="{{ route('videos.index') }}"
|
||||
class="yt-chip {{ $activeFilter === 'all' ? 'active' : '' }}">All</a>
|
||||
<a href="{{ route('videos.trending') }}"
|
||||
class="yt-chip {{ request()->is('trending') ? 'active' : '' }}">Trending</a>
|
||||
<a href="{{ route('videos.index', ['filter' => 'music']) }}"
|
||||
class="yt-chip {{ $activeFilter === 'music' ? 'active' : '' }}">Music</a>
|
||||
<a href="{{ route('videos.index', ['filter' => 'match']) }}"
|
||||
class="yt-chip {{ $activeFilter === 'match' ? 'active' : '' }}">Sports</a>
|
||||
<a href="{{ route('videos.shorts') }}"
|
||||
class="yt-chip {{ request()->is('shorts') ? 'active' : '' }}">Shorts</a>
|
||||
<a href="{{ route('videos.index', ['filter' => 'playlists']) }}"
|
||||
class="yt-chip {{ $activeFilter === 'playlists' ? 'active' : '' }}">Playlists</a>
|
||||
<a href="{{ route('videos.index', ['filter' => 'latest']) }}"
|
||||
class="yt-chip {{ $activeFilter === 'latest' ? 'active' : '' }}">New to You</a>
|
||||
</div>
|
||||
@endunless
|
||||
|
||||
{{-- ══════════════════════════════════════════════
|
||||
SEARCH RESULTS
|
||||
══════════════════════════════════════════════ --}}
|
||||
@isset($query)
|
||||
|
||||
@php
|
||||
$searchShorts = $shorts ?? collect();
|
||||
$searchPlaylists = $playlists ?? collect();
|
||||
$totalResults = $videos->count() + $searchShorts->count() + $searchPlaylists->count();
|
||||
@endphp
|
||||
|
||||
<div class="search-info">
|
||||
<h2>Search results for "{{ $query }}"</h2>
|
||||
<p>
|
||||
@if($videos->count()) {{ $videos->count() }} video{{ $videos->count() !== 1 ? 's' : '' }} @endif
|
||||
@if($searchShorts->count()) · {{ $searchShorts->count() }} short{{ $searchShorts->count() !== 1 ? 's' : '' }} @endif
|
||||
@if($searchPlaylists->count()) · {{ $searchPlaylists->count() }} playlist{{ $searchPlaylists->count() !== 1 ? 's' : '' }} @endif
|
||||
@if($totalResults === 0) No results found @endif
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@if($videos->count())
|
||||
<div class="results-section-title"><i class="bi bi-play-circle"></i> Videos</div>
|
||||
<div class="yt-video-grid">
|
||||
@foreach($videos as $video)
|
||||
@include('components.video-card', ['video' => $video])
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($searchShorts->count())
|
||||
<div class="results-section-title"><i class="bi bi-lightning-charge-fill"></i> Shorts</div>
|
||||
<div class="yt-video-grid">
|
||||
@foreach($searchShorts as $video)
|
||||
@include('components.video-card', ['video' => $video])
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($searchPlaylists->count())
|
||||
<div class="results-section-title"><i class="bi bi-collection-play"></i> Playlists</div>
|
||||
<div class="yt-video-grid">
|
||||
@foreach($searchPlaylists as $pl)
|
||||
@include('components.playlist-card', ['playlist' => $pl])
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($totalResults === 0)
|
||||
<div class="yt-empty">
|
||||
<i class="bi bi-search" style="font-size:56px;color:var(--text-secondary);display:block;margin-bottom:12px;"></i>
|
||||
<h2>No results for "{{ $query }}"</h2>
|
||||
<p style="color:var(--text-secondary);">Try different keywords or browse the gallery.</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@endisset
|
||||
|
||||
{{-- ══════════════════════════════════════════════
|
||||
PLAYLISTS-ONLY FILTER
|
||||
══════════════════════════════════════════════ --}}
|
||||
@if(!isset($query) && isset($filter) && $filter === 'playlists')
|
||||
@if(isset($playlists) && $playlists->count())
|
||||
<div class="yt-video-grid">
|
||||
@foreach($playlists as $pl)
|
||||
@include('components.playlist-card', ['playlist' => $pl])
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="yt-empty">
|
||||
<h2>No videos found</h2>
|
||||
<i class="bi bi-collection-play" style="font-size:56px;color:var(--text-secondary);display:block;margin-bottom:12px;"></i>
|
||||
<h2>No public playlists yet</h2>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- ══════════════════════════════════════════════
|
||||
HOME — MIXED FEED
|
||||
══════════════════════════════════════════════ --}}
|
||||
@elseif(!isset($query) && (!isset($filter) || $filter === 'all'))
|
||||
|
||||
@if(isset($feedItems) && $feedItems->count())
|
||||
<div class="yt-video-grid">
|
||||
@foreach($feedItems as $entry)
|
||||
@if($entry['kind'] === 'video')
|
||||
@include('components.video-card', ['video' => $entry['item']])
|
||||
@else
|
||||
@php $pl = $entry['item']; @endphp
|
||||
@include('components.playlist-card', ['playlist' => $pl, 'showTypeBadge' => true])
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="yt-empty">
|
||||
<h2>No content yet</h2>
|
||||
@auth
|
||||
<a href="{{ route('videos.create') }}" class="btn btn-primary">Upload First Video</a>
|
||||
<a href="{{ route('videos.create') }}" class="action-btn action-btn-primary" style="margin-top:12px;">Upload First Video</a>
|
||||
@endauth
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- ══════════════════════════════════════════════
|
||||
SINGLE-TYPE FILTERED VIEWS
|
||||
══════════════════════════════════════════════ --}}
|
||||
@elseif(!isset($query))
|
||||
|
||||
@if($videos->isEmpty())
|
||||
<div class="yt-empty">
|
||||
<i class="bi bi-camera-video" style="font-size:56px;color:var(--text-secondary);display:block;margin-bottom:12px;"></i>
|
||||
<h2>Nothing here yet</h2>
|
||||
</div>
|
||||
@else
|
||||
<div class="yt-video-grid">
|
||||
@foreach ($videos as $video)
|
||||
@foreach($videos as $video)
|
||||
@include('components.video-card', ['video' => $video])
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@endif
|
||||
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
(function () {
|
||||
function adjustPlThumb(img) {
|
||||
img.style.objectFit = img.naturalWidth < img.naturalHeight ? 'contain' : 'cover';
|
||||
}
|
||||
document.querySelectorAll('.yt-video-thumb img').forEach(function (img) {
|
||||
if (img.complete && img.naturalWidth) {
|
||||
adjustPlThumb(img);
|
||||
} else {
|
||||
img.addEventListener('load', function () { adjustPlThumb(img); });
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@endsection
|
||||
Reference in new issue
Block a user