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:
ghassanandClaude Sonnet 4.6 committed 2026-05-13 13:24:32 +03:00
1 parent d44490dfe0
commit 0b2e95ea65
161 files changed
+27496 -7250

No files matched your search

+78 -53
View File
@@ -8,7 +8,28 @@
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding: 12px 24px;
margin: -24px -24px 20px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
position: sticky;
top: 56px;
z-index: 90;
}
.playlist-title {
font-size: 20px;
font-weight: 500;
margin: 0;
}
@media (max-width: 768px) {
.playlist-header {
position: relative !important;
top: auto !important;
margin: -16px -16px 16px !important;
padding: 12px 16px !important;
}
}
.playlist-card {
@@ -85,24 +106,6 @@
gap: 16px;
}
.create-playlist-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 24px;
background: var(--brand-red);
color: white;
border: none;
border-radius: 20px;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.create-playlist-btn:hover {
background: #cc1a1a;
}
.empty-state {
text-align: center;
@@ -126,12 +129,6 @@
}
@media (max-width: 576px) {
.playlist-header {
flex-direction: column;
gap: 16px;
align-items: flex-start;
}
.playlist-grid {
grid-template-columns: 1fr;
}
@@ -150,8 +147,8 @@
@section('content')
<div class="playlist-header">
<h1 style="font-size: 24px; font-weight: 500;">My Playlists</h1>
<button class="create-playlist-btn" onclick="openCreatePlaylistModal()">
<h1 class="playlist-title">My Playlists</h1>
<button class="action-btn action-btn-primary" onclick="openCreatePlaylistModal()">
<i class="bi bi-plus-lg"></i> New Playlist
</button>
</div>
@@ -161,7 +158,7 @@
<i class="bi bi-collection-play empty-icon"></i>
<h2 class="empty-title">No playlists yet</h2>
<p class="empty-text">Create your first playlist to organize your favorite videos.</p>
<button class="create-playlist-btn" onclick="openCreatePlaylistModal()">
<button class="action-btn action-btn-primary" onclick="openCreatePlaylistModal()">
<i class="bi bi-plus-lg"></i> Create Playlist
</button>
</div>
@@ -250,33 +247,41 @@
onblur="this.style.borderColor = '#3f3f3f';"></textarea>
</div>
<!-- Privacy -->
<!-- Visibility -->
<div class="form-group" style="margin-bottom: 24px;">
<label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px 12px; border-radius: 8px; transition: background 0.2s;"
onmouseover="this.style.background='#3f3f3f'"
onmouseout="this.style.background='transparent'">
<input type="checkbox" name="visibility" value="public"
style="width: 20px; height: 20px; accent-color: #e61e1e; cursor: pointer;">
<div style="flex: 1;">
<span style="color: #fff; font-size: 14px; font-weight: 500;">Make playlist public</span>
<div style="color: #aaa; font-size: 12px; margin-top: 2px;">Anyone can search for and view this playlist</div>
</div>
</label>
<label style="display: block; margin-bottom: 10px; font-weight: 500; font-size: 14px; color: #fff;">Visibility</label>
<div style="display: flex; flex-direction: column; gap: 8px;">
<label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px 12px; border-radius: 8px; border: 1px solid #3f3f3f; transition: border-color 0.2s; background: #1a1a1a;">
<input type="radio" name="visibility" value="public" style="accent-color: #e61e1e; cursor: pointer; width: 16px; height: 16px;">
<div>
<div style="display: flex; align-items: center; gap: 6px; color: #fff; font-size: 14px; font-weight: 500;"><i class="bi bi-globe"></i> Public</div>
<div style="color: #aaa; font-size: 12px; margin-top: 2px;">Anyone can search for and view this playlist</div>
</div>
</label>
<label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px 12px; border-radius: 8px; border: 1px solid #3f3f3f; transition: border-color 0.2s; background: #1a1a1a;">
<input type="radio" name="visibility" value="unlisted" style="accent-color: #e61e1e; cursor: pointer; width: 16px; height: 16px;">
<div>
<div style="display: flex; align-items: center; gap: 6px; color: #fff; font-size: 14px; font-weight: 500;"><i class="bi bi-link-45deg"></i> Unlisted</div>
<div style="color: #aaa; font-size: 12px; margin-top: 2px;">Anyone with the link can view, but it won't appear in search</div>
</div>
</label>
<label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px 12px; border-radius: 8px; border: 1px solid #3f3f3f; transition: border-color 0.2s; background: #1a1a1a;">
<input type="radio" name="visibility" value="private" checked style="accent-color: #e61e1e; cursor: pointer; width: 16px; height: 16px;">
<div>
<div style="display: flex; align-items: center; gap: 6px; color: #fff; font-size: 14px; font-weight: 500;"><i class="bi bi-lock-fill"></i> Private</div>
<div style="color: #aaa; font-size: 12px; margin-top: 2px;">Only you can view this playlist</div>
</div>
</label>
</div>
</div>
<!-- Actions -->
<div style="display: flex; gap: 12px; justify-content: flex-end;">
<button type="button" onclick="closeCreatePlaylistModal()"
style="padding: 10px 20px; background: #3f3f3f; color: #fff; border: none; border-radius: 20px; cursor: pointer; font-size: 14px; font-weight: 500; transition: background 0.2s;"
onmouseover="this.style.background='#555'"
onmouseout="this.style.background='#3f3f3f'">
<button type="button" onclick="closeCreatePlaylistModal()" class="action-btn">
Cancel
</button>
<button type="submit"
style="padding: 10px 24px; background: #e61e1e; color: #fff; border: none; border-radius: 20px; cursor: pointer; font-size: 14px; font-weight: 500; transition: background 0.2s;"
onmouseover="this.style.background='#cc1a1a'"
onmouseout="this.style.background='#e61e1e'">
Create
<button type="submit" class="action-btn action-btn-primary">
<i class="bi bi-plus-lg"></i> <span>Create</span>
</button>
</div>
</form>
@@ -316,7 +321,11 @@ const thumbnailImg = document.getElementById('playlist-thumbnail-img');
// Click to upload
thumbnailDropzone.addEventListener('click', function(e) {
if (e.target.closest('button')) return;
thumbnailInput.click();
if (typeof window.openCropperModal_thumb_pl_create === 'function') {
window.openCropperModal_thumb_pl_create();
} else {
thumbnailInput.click();
}
});
// File input change
@@ -343,15 +352,21 @@ thumbnailDropzone.addEventListener('drop', function(e) {
this.style.background = '#1a1a1a';
if (e.dataTransfer.files.length) {
thumbnailInput.files = e.dataTransfer.files;
handleThumbnailSelect(thumbnailInput);
const droppedFile = e.dataTransfer.files[0];
if (typeof window.tcPreload_thumb_pl_create === 'function') {
window.tcPreload_thumb_pl_create(droppedFile);
window.openCropperModal_thumb_pl_create();
} else {
thumbnailInput.files = e.dataTransfer.files;
handleThumbnailSelect(thumbnailInput);
}
}
});
function handleThumbnailSelect(input) {
if (input.files && input.files[0]) {
const file = input.files[0];
const maxSize = 5 * 1024 * 1024; // 5MB
const maxSize = 20 * 1024 * 1024; // 20MB
// Validate file type
const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
@@ -369,7 +384,7 @@ function handleThumbnailSelect(input) {
}
if (file.size > maxSize) {
showPlaylistToast('File size exceeds 5MB limit. Please select a smaller image.', 'error');
showPlaylistToast('File size exceeds 20MB limit. Please select a smaller image.', 'error');
return;
}
@@ -508,6 +523,16 @@ function showPlaylistToast(message, type = 'success') {
}, 3000);
}
</script>
<x-image-cropper
id="thumb_pl_create"
:width="448"
:height="252"
shape="square"
target-input="playlist-thumbnail-input"
preview-img="playlist-thumbnail-img"
output-width="1280"
title="Crop Playlist Thumbnail"
/>
@endsection
<style>