ghassan 0b2e95ea65 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 <noreply@anthropic.com>
2026-05-13 13:24:32 +03:00

245 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('admin.layout')
@section('title', 'Users')
@section('content')
{{-- ── Page header ──────────────────────────────────────────────────── --}}
<div class="adm-page-header">
<h1 class="adm-page-title"><i class="bi bi-people"></i> Users</h1>
</div>
{{-- ── Alerts ───────────────────────────────────────────────────────── --}}
@if(session('success'))
<div class="adm-alert adm-alert-success">
<i class="bi bi-check-circle-fill"></i>
<span>{{ session('success') }}</span>
<button class="adm-alert-close"><i class="bi bi-x"></i></button>
</div>
@endif
@if(session('error'))
<div class="adm-alert adm-alert-error">
<i class="bi bi-exclamation-circle-fill"></i>
<span>{{ session('error') }}</span>
<button class="adm-alert-close"><i class="bi bi-x"></i></button>
</div>
@endif
{{-- ── Filter card ──────────────────────────────────────────────────── --}}
<div class="adm-card">
<div class="adm-card-body" style="padding:16px 20px;">
<form method="GET" action="{{ route('admin.users') }}" class="adm-filter-form">
<div class="adm-filter-search">
<i class="bi bi-search"></i>
<input type="text" name="search" class="adm-input"
placeholder="Search name or email…"
value="{{ request('search') }}" autocomplete="off">
</div>
<select name="role" class="adm-select">
<option value="">All Roles</option>
<option value="user" {{ request('role') === 'user' ? 'selected' : '' }}>User</option>
<option value="admin" {{ request('role') === 'admin' ? 'selected' : '' }}>Admin</option>
<option value="super_admin" {{ request('role') === 'super_admin' ? 'selected' : '' }}>Super Admin</option>
</select>
<select name="sort" class="adm-select">
<option value="latest" {{ request('sort', 'latest') === 'latest' ? 'selected' : '' }}>Newest first</option>
<option value="oldest" {{ request('sort') === 'oldest' ? 'selected' : '' }}>Oldest first</option>
<option value="name_asc" {{ request('sort') === 'name_asc' ? 'selected' : '' }}>Name AZ</option>
<option value="name_desc" {{ request('sort') === 'name_desc' ? 'selected' : '' }}>Name ZA</option>
</select>
<button type="submit" class="adm-btn adm-btn-primary">
<i class="bi bi-funnel"></i> Filter
</button>
@if(request()->hasAny(['search','role','sort']))
<a href="{{ route('admin.users') }}" class="adm-btn">
<i class="bi bi-x-lg"></i> Clear
</a>
@endif
</form>
</div>
</div>
{{-- ── Users table ──────────────────────────────────────────────────── --}}
<div class="adm-card">
<div class="adm-card-header">
<div class="adm-card-title">
<i class="bi bi-people"></i>
All Users
<span class="adm-badge adm-badge-user">{{ $users->total() ?? $users->count() }}</span>
</div>
</div>
<div class="adm-table-wrap">
<table class="adm-table">
<thead>
<tr>
<th>User</th>
<th>Role</th>
<th>Verified</th>
<th>Videos</th>
<th>Joined</th>
<th style="width:80px; text-align:right;">Actions</th>
</tr>
</thead>
<tbody>
@forelse($users as $user)
<tr>
{{-- User cell --}}
<td>
<div class="adm-user-cell">
<img src="{{ $user->avatar_url }}" alt="{{ $user->name }}">
<div>
<div style="display:flex;align-items:center;gap:4px;">
<span class="adm-user-cell-name">{{ $user->name }}</span>
@if($user->id === auth()->id())
<span class="adm-user-cell-you">you</span>
@endif
</div>
<div class="adm-user-cell-email">{{ $user->email }}</div>
</div>
</div>
</td>
{{-- Role --}}
<td>
@if($user->role === 'super_admin')
<span class="adm-badge adm-badge-superadmin"><i class="bi bi-shield-fill"></i> Super Admin</span>
@elseif($user->role === 'admin')
<span class="adm-badge adm-badge-admin"><i class="bi bi-person-badge"></i> Admin</span>
@else
<span class="adm-badge adm-badge-user"><i class="bi bi-person"></i> User</span>
@endif
</td>
{{-- Verified --}}
<td>
@if($user->email_verified_at)
<span class="adm-badge adm-badge-verified"><i class="bi bi-check-circle-fill"></i> Verified</span>
@else
<span class="adm-badge adm-badge-unverified"><i class="bi bi-clock"></i> Pending</span>
@endif
</td>
{{-- Videos --}}
<td>
<a href="{{ route('channel', $user->channel) }}" target="_blank"
class="text-dim" style="text-decoration:none; font-size:13px;">
{{ $user->videos->count() }}
<i class="bi bi-box-arrow-up-right" style="font-size:10px; opacity:.5; margin-left:2px;"></i>
</a>
</td>
{{-- Joined --}}
<td class="text-muted-sm">{{ $user->created_at->format('M d, Y') }}</td>
{{-- Actions --}}
<td>
<div class="adm-row-actions" style="justify-content:flex-end;">
@if(!$user->email_verified_at)
<form method="POST" action="{{ route('admin.users.verify', $user->id) }}" style="display:inline;">
@csrf
<button type="submit" class="adm-btn adm-btn-sm adm-btn-verify" title="Manually verify account">
<i class="bi bi-patch-check-fill"></i>
</button>
</form>
@endif
@if($user->id !== auth()->id() && !$user->isSuperAdmin())
<form method="POST" action="{{ route('admin.users.impersonate', $user->id) }}" style="display:inline;">
@csrf
<button type="submit" class="adm-btn adm-btn-sm adm-btn-impersonate" title="Impersonate user">
<i class="bi bi-person-fill-gear"></i>
</button>
</form>
@endif
<a href="{{ route('admin.users.edit', $user->id) }}"
class="adm-btn adm-btn-sm" title="Edit user">
<i class="bi bi-pencil"></i>
</a>
@if($user->id !== auth()->id())
<button type="button"
class="adm-btn adm-btn-sm adm-btn-danger"
title="Delete user"
onclick="openDeleteDialog({{ $user->id }}, '{{ addslashes($user->name) }}')">
<i class="bi bi-trash"></i>
</button>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6">
<div class="empty-state">
<i class="bi bi-people"></i>
<p>No users found</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{-- Pagination --}}
@if($users instanceof \Illuminate\Pagination\LengthAwarePaginator && $users->hasPages())
<div style="padding:16px 20px; border-top:1px solid var(--border);">
{{ $users->onEachSide(1)->links() }}
</div>
@endif
</div>
{{-- ── Delete confirmation dialog ───────────────────────────────────── --}}
<div class="adm-dialog-overlay" id="deleteDialog">
<div class="adm-dialog">
<div class="adm-dialog-header">
<div class="adm-dialog-title">
<i class="bi bi-exclamation-triangle-fill"></i>
Delete User
</div>
<button type="button" class="adm-btn adm-btn-sm" onclick="closeDeleteDialog()" style="border:none;background:none;color:var(--text-2);">
<i class="bi bi-x-lg"></i>
</button>
</div>
<div class="adm-dialog-body">
<p>You are about to permanently delete <strong id="dlgUserName"></strong>.</p>
<div class="adm-dialog-warning">
<i class="bi bi-info-circle-fill" style="flex-shrink:0;margin-top:1px;"></i>
All videos uploaded by this user will also be deleted. This cannot be undone.
</div>
</div>
<div class="adm-dialog-footer">
<button type="button" class="adm-btn" onclick="closeDeleteDialog()">Cancel</button>
<form id="deleteForm" method="POST">
@csrf @method('DELETE')
<button type="submit" class="adm-btn adm-btn-danger" style="height:36px;">
<i class="bi bi-trash"></i> Delete User
</button>
</form>
</div>
</div>
</div>
@endsection
@section('scripts')
<script>
function openDeleteDialog(userId, userName) {
document.getElementById('dlgUserName').textContent = userName;
document.getElementById('deleteForm').action = '/admin/users/' + userId;
document.getElementById('deleteDialog').classList.add('open');
}
function closeDeleteDialog() {
document.getElementById('deleteDialog').classList.remove('open');
}
document.getElementById('deleteDialog').addEventListener('click', function(e) {
if (e.target === this) closeDeleteDialog();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closeDeleteDialog();
});
</script>
@endsection