- 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>
635 lines
31 KiB
PHP
635 lines
31 KiB
PHP
@extends('layouts.app')
|
||
|
||
@section('title', 'Profile | TAKEONE')
|
||
|
||
@section('extra_styles')
|
||
<style>
|
||
/* ── Profile Header ─────────────────────────────────────────────────────── */
|
||
.profile-header {
|
||
background: var(--bg-secondary);
|
||
border-radius: 16px;
|
||
overflow: hidden;
|
||
margin-bottom: 28px;
|
||
box-shadow: 0 4px 24px rgba(0,0,0,.25);
|
||
}
|
||
.profile-banner {
|
||
height: 160px;
|
||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 40%, #0f3460 70%, #533483 100%);
|
||
position: relative;
|
||
}
|
||
.profile-banner-edit {
|
||
position: absolute;
|
||
top: 14px; right: 16px;
|
||
display: flex; align-items: center; gap: 6px;
|
||
padding: 7px 14px;
|
||
background: rgba(0,0,0,.45);
|
||
backdrop-filter: blur(6px);
|
||
border: 1px solid rgba(255,255,255,.15);
|
||
border-radius: 20px;
|
||
color: #fff;
|
||
font-size: 13px; font-weight: 500; font-family: inherit;
|
||
cursor: pointer; transition: background .2s, border-color .2s;
|
||
text-decoration: none;
|
||
}
|
||
.profile-banner-edit:hover { background: rgba(230,30,30,.7); border-color: rgba(230,30,30,.6); color: #fff; }
|
||
.profile-banner-edit i { font-size: 13px; }
|
||
|
||
.profile-content {
|
||
padding: 0 28px 28px;
|
||
text-align: center;
|
||
}
|
||
.profile-avatar-wrap {
|
||
display: inline-block;
|
||
position: relative;
|
||
margin-top: -52px;
|
||
margin-bottom: 14px;
|
||
}
|
||
.profile-avatar {
|
||
width: 108px; height: 108px;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
border: 4px solid var(--bg-secondary);
|
||
box-shadow: 0 6px 28px rgba(0,0,0,.5);
|
||
display: block;
|
||
}
|
||
.profile-name {
|
||
font-size: 24px; font-weight: 700;
|
||
letter-spacing: -.3px;
|
||
margin: 0 0 4px;
|
||
color: var(--text-primary);
|
||
}
|
||
.profile-username {
|
||
font-size: 14px; color: var(--text-secondary);
|
||
margin: 0 0 12px;
|
||
}
|
||
.profile-bio {
|
||
font-size: 14px; line-height: 1.7;
|
||
color: var(--text-secondary);
|
||
max-width: 480px;
|
||
margin: 0 auto 16px;
|
||
}
|
||
.profile-meta {
|
||
display: flex; flex-wrap: wrap;
|
||
align-items: center; justify-content: center;
|
||
gap: 6px 10px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.profile-meta-item {
|
||
display: flex; align-items: center; gap: 5px;
|
||
font-size: 13px; color: var(--text-secondary);
|
||
background: rgba(255,255,255,.05);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 20px;
|
||
padding: 4px 10px;
|
||
}
|
||
.profile-meta-item i { font-size: 12px; }
|
||
.profile-meta-item a { color: inherit; text-decoration: none; }
|
||
.profile-meta-item a:hover { color: var(--text-primary); }
|
||
|
||
.profile-divider { height: 1px; background: var(--border-color); margin: 0 -28px 20px; }
|
||
|
||
.profile-stats {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
.profile-stat {
|
||
padding: 8px 24px;
|
||
text-align: center;
|
||
border-right: 1px solid var(--border-color);
|
||
text-decoration: none; color: inherit;
|
||
transition: background .15s;
|
||
border-radius: 8px;
|
||
}
|
||
.profile-stat:last-child { border-right: none; }
|
||
.profile-stat:hover { background: rgba(255,255,255,.04); }
|
||
.profile-stat-value { display: block; font-size: 20px; font-weight: 700; line-height: 1.2; }
|
||
.profile-stat-label { display: block; font-size: 12px; color: var(--text-secondary); margin-top: 2px; text-transform: uppercase; letter-spacing: .5px; }
|
||
|
||
.social-links { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; padding-top: 4px; }
|
||
.social-link {
|
||
display: flex; align-items: center; justify-content: center;
|
||
width: 36px; height: 36px;
|
||
border-radius: 10px;
|
||
background: rgba(255,255,255,.06);
|
||
border: 1px solid var(--border-color);
|
||
color: var(--text-secondary);
|
||
text-decoration: none;
|
||
font-size: 16px;
|
||
transition: all .2s;
|
||
}
|
||
.social-link:hover { transform: translateY(-2px); color: #fff; border-color: transparent; background: var(--brand-red); }
|
||
.social-link.twitter:hover { background: #1da1f2; }
|
||
.social-link.instagram:hover { background: linear-gradient(45deg,#f09433,#e6683c,#dc2743,#cc2366,#bc1888); }
|
||
.social-link.facebook:hover { background: #1877f2; }
|
||
.social-link.youtube:hover { background: #ff0000; }
|
||
.social-link.linkedin:hover { background: #0077b5; }
|
||
.social-link.tiktok:hover { background: #010101; }
|
||
.social-link.whatsapp:hover { background: #25d366; }
|
||
.social-link.googlemap:hover { background: #ea4335; }
|
||
|
||
/* ── Page cards ─────────────────────────────────────────────────────────── */
|
||
.form-card { background: var(--bg-secondary); border-radius: 12px; padding: 24px; margin-bottom: 24px; }
|
||
.form-title { font-size: 16px; font-weight: 600; margin-bottom: 20px; padding-bottom: 12px; border-bottom: 1px solid var(--border-color); display: flex; align-items: center; gap: 8px; }
|
||
.form-group { margin-bottom: 16px; }
|
||
.form-label { display: block; margin-bottom: 8px; font-weight: 500; font-size: 14px; }
|
||
.form-input, .form-textarea { width: 100%; background: #121212; border: 1px solid var(--border-color); border-radius: 8px; padding: 11px 14px; color: var(--text-primary); font-size: 14px; transition: border-color .2s; }
|
||
.form-input:focus, .form-textarea:focus { outline: none; border-color: var(--brand-red); }
|
||
.form-textarea { min-height: 90px; resize: vertical; }
|
||
.form-hint { font-size: 12px; color: var(--text-secondary); margin-top: 4px; }
|
||
.section-title { font-size: 18px; font-weight: 600; margin-bottom: 20px; display: flex; align-items: center; gap: 8px; }
|
||
|
||
/* ── Quick links sidebar ─────────────────────────────────────────────────── */
|
||
.quick-link {
|
||
display: flex; align-items: center; gap: 12px;
|
||
padding: 12px 14px; border-radius: 10px;
|
||
background: rgba(255,255,255,.03);
|
||
border: 1px solid var(--border-color);
|
||
color: var(--text-primary); text-decoration: none;
|
||
font-size: 14px; font-weight: 500;
|
||
transition: background .15s, border-color .15s, transform .15s;
|
||
margin-bottom: 8px;
|
||
}
|
||
.quick-link:last-child { margin-bottom: 0; }
|
||
.quick-link i { font-size: 16px; color: var(--text-secondary); width: 20px; text-align: center; transition: color .15s; }
|
||
.quick-link:hover { background: rgba(230,30,30,.08); border-color: rgba(230,30,30,.3); transform: translateX(3px); }
|
||
.quick-link:hover i { color: var(--brand-red); }
|
||
|
||
/* ── Videos ─────────────────────────────────────────────────────────────── */
|
||
.video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
|
||
.video-card { background: var(--bg-primary); border-radius: 10px; overflow: hidden; transition: transform .2s, box-shadow .2s; }
|
||
.video-card:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,.3); }
|
||
.video-thumbnail { position: relative; aspect-ratio: 16/9; background: #000; }
|
||
.video-thumbnail img { width: 100%; height: 100%; object-fit: cover; }
|
||
.video-duration { position: absolute; bottom: 6px; right: 6px; background: rgba(0,0,0,.8); color: #fff; padding: 2px 6px; border-radius: 4px; font-size: 11px; font-weight: 500; }
|
||
.video-info { padding: 12px; }
|
||
.video-title { font-size: 13px; font-weight: 500; margin-bottom: 6px; line-height: 1.4; }
|
||
.video-title a { color: var(--text-primary); text-decoration: none; }
|
||
.video-title a:hover { color: var(--brand-red); }
|
||
.video-meta { font-size: 12px; color: var(--text-secondary); }
|
||
.empty-state { text-align: center; padding: 40px 20px; color: var(--text-secondary); }
|
||
.empty-state i { font-size: 44px; display: block; margin-bottom: 12px; opacity: .4; }
|
||
.w-100 { width: 100%; }
|
||
.mt-4 { margin-top: 24px; }
|
||
.d-flex { display: flex; }
|
||
.flex-column { flex-direction: column; }
|
||
|
||
/* ── Edit Profile Modal ─────────────────────────────────────────────────── */
|
||
.ep-overlay {
|
||
display: none; position: fixed; inset: 0; z-index: 2000;
|
||
background: rgba(0,0,0,.75);
|
||
backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
|
||
align-items: center; justify-content: center;
|
||
padding: 20px 16px;
|
||
}
|
||
.ep-overlay.is-open { display: flex; }
|
||
.ep-dialog {
|
||
display: flex; flex-direction: column;
|
||
background: var(--bg-secondary, #1e1e1e);
|
||
border: 1px solid var(--border-color, #303030);
|
||
border-radius: 16px;
|
||
width: 100%; max-width: 600px;
|
||
max-height: min(88vh, 720px);
|
||
box-shadow: 0 32px 80px rgba(0,0,0,.7);
|
||
animation: epIn .2s ease;
|
||
}
|
||
@keyframes epIn {
|
||
from { opacity: 0; transform: scale(.97) translateY(-8px); }
|
||
to { opacity: 1; transform: scale(1) translateY(0); }
|
||
}
|
||
.ep-header {
|
||
flex-shrink: 0;
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 18px 20px 0;
|
||
}
|
||
.ep-header-title { font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 8px; }
|
||
.ep-close {
|
||
display: flex; align-items: center; justify-content: center;
|
||
width: 30px; height: 30px; border-radius: 50%;
|
||
border: none; background: transparent;
|
||
color: var(--text-secondary, #aaa); font-size: 16px; cursor: pointer;
|
||
transition: background .15s, color .15s;
|
||
}
|
||
.ep-close:hover { background: rgba(255,255,255,.1); color: var(--text-primary, #f1f1f1); }
|
||
.ep-tabs {
|
||
flex-shrink: 0;
|
||
display: flex; gap: 2px;
|
||
padding: 12px 20px 0;
|
||
border-bottom: 1px solid var(--border-color, #303030);
|
||
}
|
||
.ep-tab {
|
||
display: flex; align-items: center; gap: 6px;
|
||
padding: 8px 14px;
|
||
border: none; background: transparent;
|
||
color: var(--text-secondary, #aaa);
|
||
font-size: 13px; font-weight: 500; font-family: inherit;
|
||
cursor: pointer; border-radius: 8px 8px 0 0;
|
||
border-bottom: 2px solid transparent; margin-bottom: -1px;
|
||
transition: color .15s, border-color .15s, background .15s;
|
||
white-space: nowrap;
|
||
}
|
||
.ep-tab:hover { color: var(--text-primary, #f1f1f1); background: rgba(255,255,255,.05); }
|
||
.ep-tab.active { color: var(--brand-red, #e61e1e); border-bottom-color: var(--brand-red, #e61e1e); }
|
||
.ep-tab i { font-size: 13px; }
|
||
.ep-body {
|
||
flex: 1; min-height: 0;
|
||
overflow-y: auto;
|
||
padding: 22px 20px;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--border-color, #303030) transparent;
|
||
}
|
||
.ep-body::-webkit-scrollbar { width: 4px; }
|
||
.ep-body::-webkit-scrollbar-thumb { background: var(--border-color, #303030); border-radius: 2px; }
|
||
.ep-panel { display: none; }
|
||
.ep-panel.active { display: block; }
|
||
/* Form must be a flex child so it respects the dialog's max-height */
|
||
#epForm {
|
||
display: flex; flex-direction: column;
|
||
flex: 1; min-height: 0; overflow: hidden;
|
||
}
|
||
.ep-footer {
|
||
flex-shrink: 0;
|
||
padding: 14px 20px 18px;
|
||
border-top: 1px solid var(--border-color, #303030);
|
||
display: flex; justify-content: flex-end; gap: 8px;
|
||
}
|
||
.ep-2col { display: flex; gap: 12px; }
|
||
.ep-2col > .form-group { flex: 1; min-width: 0; margin-bottom: 0; }
|
||
|
||
/* Match dropdown button height to .form-input (padding: 11px 14px, no min-height) */
|
||
.ep-body .csd-btn { padding: 11px 14px; min-height: 0; }
|
||
.ep-body .dp-btn { padding: 11px 10px; min-height: 0; }
|
||
|
||
/* Avatar upload */
|
||
.avatar-upload-area { display: flex; flex-direction: column; align-items: center; gap: 20px; padding: 24px 0 8px; }
|
||
.avatar-preview-wrap { position: relative; width: 120px; height: 120px; }
|
||
.avatar-preview { width: 120px; height: 120px; border-radius: 50%; object-fit: cover; border: 3px solid var(--border-color, #303030); display: block; }
|
||
.avatar-edit-btn {
|
||
position: absolute; bottom: 2px; right: 2px;
|
||
width: 32px; height: 32px; border-radius: 50%;
|
||
background: var(--brand-red, #e61e1e);
|
||
border: 2px solid var(--bg-secondary, #1e1e1e);
|
||
color: #fff; font-size: 13px; cursor: pointer;
|
||
display: flex; align-items: center; justify-content: center;
|
||
transition: background .15s;
|
||
}
|
||
.avatar-edit-btn:hover { background: #c01717; }
|
||
.avatar-file-input { display: none; }
|
||
.avatar-upload-hint { font-size: 13px; color: var(--text-secondary, #aaa); text-align: center; line-height: 1.7; }
|
||
.avatar-upload-hint strong { color: var(--text-primary, #f1f1f1); }
|
||
|
||
/* Toast */
|
||
.ep-toast {
|
||
position: fixed; bottom: 80px; left: 50%;
|
||
transform: translateX(-50%) translateY(16px);
|
||
background: #22c55e; color: #fff;
|
||
font-size: 14px; font-weight: 500;
|
||
padding: 10px 20px; border-radius: 8px;
|
||
box-shadow: 0 8px 24px rgba(0,0,0,.4);
|
||
z-index: 3000; opacity: 0; pointer-events: none;
|
||
transition: opacity .25s, transform .25s;
|
||
white-space: nowrap;
|
||
}
|
||
.ep-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||
|
||
@media (max-width: 768px) {
|
||
.profile-banner { height: 120px; }
|
||
.profile-content { padding: 0 18px 22px; }
|
||
.profile-stat { padding: 8px 14px; }
|
||
.profile-stat-value { font-size: 18px; }
|
||
.ep-overlay { align-items: flex-end; padding: 0; }
|
||
.ep-dialog { border-radius: 16px 16px 0 0; max-width: 100%; max-height: 92vh; }
|
||
}
|
||
</style>
|
||
@endsection
|
||
|
||
@php
|
||
// If re-displaying after a validation error, rebuild from old() input
|
||
$oldLinks = old('slink');
|
||
if ($oldLinks) {
|
||
$socialExisting = collect($oldLinks)
|
||
->filter(fn($e) => !empty($e['platform']) && !empty($e['value']))
|
||
->map(fn($e) => ['platform' => $e['platform'], 'value' => $e['value'], 'visibility' => $e['visibility'] ?? 'public'])
|
||
->values()->all();
|
||
} else {
|
||
$socialExisting = $user->socialLinks()->orderBy('sort_order')->get()
|
||
->map(fn($l) => ['platform' => $l->platform, 'value' => $l->value, 'visibility' => $l->visibility])
|
||
->all();
|
||
}
|
||
@endphp
|
||
|
||
@section('content')
|
||
<div class="profile-header">
|
||
<div class="profile-banner">
|
||
<button type="button" class="profile-banner-edit" id="openEditProfile">
|
||
<i class="bi bi-pencil-square"></i> Edit Profile
|
||
</button>
|
||
</div>
|
||
<div class="profile-content">
|
||
<div class="profile-avatar-wrap">
|
||
@if($user->avatar)
|
||
<img src="{{ asset('storage/avatars/' . $user->avatar) }}" alt="{{ $user->name }}" class="profile-avatar" id="pageAvatar">
|
||
@else
|
||
<img src="https://i.pravatar.cc/150?u={{ $user->id }}" alt="{{ $user->name }}" class="profile-avatar" id="pageAvatar">
|
||
@endif
|
||
</div>
|
||
|
||
<h1 class="profile-name">{{ $user->name }}</h1>
|
||
@if($user->username)
|
||
<p class="profile-username">@{{ $user->username }}</p>
|
||
@endif
|
||
@if($user->bio)
|
||
<p class="profile-bio">{{ $user->bio }}</p>
|
||
@endif
|
||
|
||
<div class="profile-meta">
|
||
@if($user->location)
|
||
<div class="profile-meta-item"><i class="bi bi-geo-alt"></i><span>{{ $user->location }}</span></div>
|
||
@endif
|
||
@if($user->website)
|
||
<div class="profile-meta-item"><i class="bi bi-link-45deg"></i><a href="{{ $user->website_url }}" target="_blank">{{ Str::limit($user->website, 30) }}</a></div>
|
||
@endif
|
||
<div class="profile-meta-item"><i class="bi bi-calendar3"></i><span>Joined {{ $user->created_at->format('M Y') }}</span></div>
|
||
</div>
|
||
|
||
@if($user->hasSocialLinks())
|
||
@php
|
||
$socialIconMap = [
|
||
'twitter' => ['icon' => 'bi-twitter-x', 'class' => 'twitter', 'href' => fn($v) => "https://twitter.com/{$v}"],
|
||
'instagram' => ['icon' => 'bi-instagram', 'class' => 'instagram', 'href' => fn($v) => "https://instagram.com/{$v}"],
|
||
'facebook' => ['icon' => 'bi-facebook', 'class' => 'facebook', 'href' => fn($v) => "https://facebook.com/{$v}"],
|
||
'youtube' => ['icon' => 'bi-youtube', 'class' => 'youtube', 'href' => fn($v) => "https://youtube.com/@{$v}"],
|
||
'linkedin' => ['icon' => 'bi-linkedin', 'class' => 'linkedin', 'href' => fn($v) => "https://linkedin.com/in/{$v}"],
|
||
'tiktok' => ['icon' => 'bi-tiktok', 'class' => 'tiktok', 'href' => fn($v) => "https://tiktok.com/@{$v}"],
|
||
'whatsapp' => ['icon' => 'bi-whatsapp', 'class' => 'whatsapp', 'href' => fn($v) => "https://wa.me/" . preg_replace('/\D/', '', $v)],
|
||
'website' => ['icon' => 'bi-globe', 'class' => '', 'href' => fn($v) => preg_match('/^https?:\/\//', $v) ? $v : "https://{$v}"],
|
||
'google_location' => ['icon' => 'bi-geo-alt-fill', 'class' => 'googlemap', 'href' => fn($v) => $v],
|
||
'social_phone' => ['icon' => 'bi-telephone-fill', 'class' => '', 'href' => fn($v) => "tel:{$v}"],
|
||
'social_email' => ['icon' => 'bi-envelope-fill', 'class' => '', 'href' => fn($v) => "mailto:{$v}"],
|
||
];
|
||
@endphp
|
||
<div class="social-links">
|
||
@foreach($user->socialLinks as $link)
|
||
@if(isset($socialIconMap[$link->platform]))
|
||
@php $m = $socialIconMap[$link->platform]; @endphp
|
||
<a href="{{ $m['href']($link->value) }}"
|
||
target="{{ in_array($link->platform, ['social_phone','social_email']) ? '_self' : '_blank' }}"
|
||
class="social-link {{ $m['class'] }}"
|
||
title="{{ $link->value }}">
|
||
<i class="bi {{ $m['icon'] }}"></i>
|
||
</a>
|
||
@endif
|
||
@endforeach
|
||
</div>
|
||
@endif
|
||
|
||
<div class="profile-divider"></div>
|
||
|
||
<div class="profile-stats">
|
||
<a href="{{ route('channel', $user->channel) }}" class="profile-stat">
|
||
<span class="profile-stat-value">{{ $user->videos->count() }}</span>
|
||
<span class="profile-stat-label">Videos</span>
|
||
</a>
|
||
<div class="profile-stat">
|
||
<span class="profile-stat-value">{{ number_format($user->subscriber_count) }}</span>
|
||
<span class="profile-stat-label">Subscribers</span>
|
||
</div>
|
||
<div class="profile-stat">
|
||
<span class="profile-stat-value">{{ \DB::table('video_views')->whereIn('video_id', $user->videos->pluck('id'))->count() }}</span>
|
||
<span class="profile-stat-label">Views</span>
|
||
</div>
|
||
<div class="profile-stat">
|
||
<span class="profile-stat-value">{{ $user->likes->count() }}</span>
|
||
<span class="profile-stat-label">Likes</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-lg-8">
|
||
<div class="form-card">
|
||
<h2 class="section-title"><i class="bi bi-play-circle"></i> Recent Videos</h2>
|
||
@if($user->videos->count() > 0)
|
||
<div class="video-grid">
|
||
@foreach($user->videos->take(4) as $video)
|
||
<div class="video-card">
|
||
<a href="{{ route('videos.show', $video) }}">
|
||
<div class="video-thumbnail">
|
||
<img src="{{ $video->thumbnail ? asset('storage/thumbnails/' . $video->thumbnail) : 'https://i.ytimg.com/vi/default.jpg' }}" alt="{{ $video->title }}">
|
||
<span class="video-duration">{{ $video->duration ?? '0:00' }}</span>
|
||
</div>
|
||
</a>
|
||
<div class="video-info">
|
||
<h3 class="video-title"><a href="{{ route('videos.show', $video) }}">{{ Str::limit($video->title, 50) }}</a></h3>
|
||
<div class="video-meta"><span>{{ number_format($video->view_count ?? 0) }} views</span><span> • </span><span>{{ $video->created_at->diffForHumans() }}</span></div>
|
||
</div>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
@if($user->videos->count() > 4)
|
||
<div class="text-center mt-4"><a href="{{ route('channel', $user->channel) }}" class="action-btn">View All Videos <i class="bi bi-arrow-right"></i></a></div>
|
||
@endif
|
||
@else
|
||
<div class="empty-state"><i class="bi bi-camera-video"></i><p>No videos yet</p></div>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-lg-4">
|
||
<div class="form-card">
|
||
<h2 class="form-title"><i class="bi bi-link-45deg"></i> Quick Links</h2>
|
||
<a href="{{ route('channel', $user->channel) }}" class="quick-link"><i class="bi bi-play-btn"></i><span>View My Channel</span></a>
|
||
<a href="{{ route('settings') }}" class="quick-link"><i class="bi bi-gear"></i><span>Account Settings</span></a>
|
||
<a href="{{ route('history') }}" class="quick-link"><i class="bi bi-clock-history"></i><span>Watch History</span></a>
|
||
<a href="{{ route('liked') }}" class="quick-link"><i class="bi bi-heart"></i><span>Liked Videos</span></a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- ── Edit Profile Modal ───────────────────────────────────────────── --}}
|
||
<div class="ep-overlay" id="epOverlay" role="dialog" aria-modal="true" aria-labelledby="epTitle">
|
||
<div class="ep-dialog">
|
||
|
||
<div class="ep-header">
|
||
<span class="ep-header-title" id="epTitle"><i class="bi bi-pencil-square"></i> Edit Profile</span>
|
||
<button type="button" class="ep-close" id="closeEditProfile" aria-label="Close"><i class="bi bi-x-lg"></i></button>
|
||
</div>
|
||
|
||
<div class="ep-tabs">
|
||
<button type="button" class="ep-tab active" data-tab="info">
|
||
<i class="bi bi-person"></i> Profile
|
||
</button>
|
||
<button type="button" class="ep-tab" data-tab="picture">
|
||
<i class="bi bi-camera"></i> Picture
|
||
</button>
|
||
<button type="button" class="ep-tab" data-tab="social">
|
||
<i class="bi bi-share"></i> Social
|
||
</button>
|
||
</div>
|
||
|
||
<form method="POST" action="{{ route('profile.update') }}" enctype="multipart/form-data" id="epForm">
|
||
@csrf @method('PUT')
|
||
|
||
<div class="ep-body">
|
||
|
||
{{-- Tab: Profile Info --}}
|
||
<div class="ep-panel active" id="panel-info">
|
||
<div class="form-group"><label class="form-label">Name</label><input type="text" name="name" class="form-input" value="{{ old('name', $user->name) }}" required></div>
|
||
<div class="form-group"><label class="form-label">Bio</label><textarea name="bio" class="form-textarea" placeholder="Tell us about yourself...">{{ old('bio', $user->bio) }}</textarea></div>
|
||
|
||
<div class="ep-2col" style="margin-bottom:16px;">
|
||
<div class="form-group">
|
||
<x-date-picker
|
||
name="birthday"
|
||
label="Birthday"
|
||
value="{{ old('birthday', $user->birthday) }}"
|
||
:max-year="(int) date('Y')"
|
||
:min-year="1900"
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Gender</label>
|
||
<select name="gender" class="form-input" style="cursor:pointer;">
|
||
<option value="">Prefer not to say</option>
|
||
<option value="male" {{ old('gender', $user->gender) === 'male' ? 'selected' : '' }}>Male</option>
|
||
<option value="female" {{ old('gender', $user->gender) === 'female' ? 'selected' : '' }}>Female</option>
|
||
<option value="prefer_not_to_say" {{ old('gender', $user->gender) === 'prefer_not_to_say' ? 'selected' : '' }}>Prefer not to say</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ep-2col" style="margin-bottom:16px;">
|
||
<div class="form-group">
|
||
<x-country-select
|
||
name="nationality"
|
||
label="Nationality"
|
||
placeholder="Select nationality"
|
||
value="{{ old('nationality', $user->nationality) }}"
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<x-timezone-select
|
||
name="timezone"
|
||
label="Timezone"
|
||
placeholder="Select timezone"
|
||
value="{{ old('timezone', $user->timezone) }}"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
{{-- Tab: Picture --}}
|
||
<div class="ep-panel" id="panel-picture">
|
||
<div class="avatar-upload-area">
|
||
<div class="avatar-preview-wrap">
|
||
@if($user->avatar)
|
||
<img src="{{ asset('storage/avatars/' . $user->avatar) }}" class="avatar-preview" id="avatarPreview" alt="Avatar">
|
||
@else
|
||
<img src="https://i.pravatar.cc/150?u={{ $user->id }}" class="avatar-preview" id="avatarPreview" alt="Avatar">
|
||
@endif
|
||
<button type="button" class="avatar-edit-btn" id="avatarEditBtn" title="Change photo">
|
||
<i class="bi bi-camera-fill"></i>
|
||
</button>
|
||
</div>
|
||
<input type="file" name="avatar" id="avatarFileInput" class="avatar-file-input" accept="image/*">
|
||
<div class="avatar-upload-hint">
|
||
<strong>Click the camera icon</strong> to upload a new photo<br>
|
||
JPG, PNG or GIF · Max 5 MB<br>
|
||
<small style="color:var(--text-secondary)">Recommended: at least 200 × 200 px</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Tab: Social --}}
|
||
<div class="ep-panel" id="panel-social">
|
||
<x-social-links-editor :existing="$socialExisting" />
|
||
</div>
|
||
|
||
</div>{{-- .ep-body --}}
|
||
|
||
<div class="ep-footer">
|
||
<button type="button" class="action-btn" id="epCancelBtn"><span>Cancel</span></button>
|
||
<button type="submit" class="action-btn action-btn-primary"><i class="bi bi-check-lg"></i> <span>Save Changes</span></button>
|
||
</div>
|
||
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ep-toast" id="epToast"></div>
|
||
@endsection
|
||
|
||
@section('scripts')
|
||
<script>
|
||
(function () {
|
||
const overlay = document.getElementById('epOverlay');
|
||
const openBtn = document.getElementById('openEditProfile');
|
||
const closeBtn = document.getElementById('closeEditProfile');
|
||
const cancelBtn = document.getElementById('epCancelBtn');
|
||
const toast = document.getElementById('epToast');
|
||
|
||
// ── Modal open / close ───────────────────────────────────────────────
|
||
function openModal(tab) {
|
||
overlay.classList.add('is-open');
|
||
document.body.style.overflow = 'hidden';
|
||
if (tab) switchTab(tab);
|
||
}
|
||
function closeModal() {
|
||
overlay.classList.remove('is-open');
|
||
document.body.style.overflow = '';
|
||
}
|
||
|
||
openBtn.addEventListener('click', () => openModal('info'));
|
||
closeBtn.addEventListener('click', closeModal);
|
||
cancelBtn.addEventListener('click', closeModal);
|
||
overlay.addEventListener('click', e => { if (e.target === overlay) closeModal(); });
|
||
document.addEventListener('keydown', e => {
|
||
if (e.key === 'Escape' && overlay.classList.contains('is-open')) closeModal();
|
||
});
|
||
|
||
// ── Tabs ─────────────────────────────────────────────────────────────
|
||
function switchTab(name) {
|
||
document.querySelectorAll('.ep-tab').forEach(t => t.classList.toggle('active', t.dataset.tab === name));
|
||
document.querySelectorAll('.ep-panel').forEach(p => p.classList.toggle('active', p.id === 'panel-' + name));
|
||
}
|
||
document.querySelectorAll('.ep-tab').forEach(btn => {
|
||
btn.addEventListener('click', () => switchTab(btn.dataset.tab));
|
||
});
|
||
|
||
// ── Avatar preview ───────────────────────────────────────────────────
|
||
const avatarEditBtn = document.getElementById('avatarEditBtn');
|
||
const avatarFileInput = document.getElementById('avatarFileInput');
|
||
const avatarPreview = document.getElementById('avatarPreview');
|
||
const pageAvatar = document.getElementById('pageAvatar');
|
||
|
||
avatarEditBtn.addEventListener('click', () => avatarFileInput.click());
|
||
avatarFileInput.addEventListener('change', () => {
|
||
const file = avatarFileInput.files[0];
|
||
if (!file) return;
|
||
const reader = new FileReader();
|
||
reader.onload = e => {
|
||
avatarPreview.src = e.target.result;
|
||
if (pageAvatar) pageAvatar.src = e.target.result;
|
||
};
|
||
reader.readAsDataURL(file);
|
||
});
|
||
|
||
// ── Flash handling ───────────────────────────────────────────────────
|
||
@if(session('success'))
|
||
showToast(@json(session('success')), 'success');
|
||
@elseif($errors->any())
|
||
openModal('info');
|
||
@endif
|
||
|
||
function showToast(msg, type) {
|
||
toast.textContent = msg;
|
||
toast.style.background = type === 'success' ? '#22c55e' : '#e61e1e';
|
||
toast.classList.add('show');
|
||
setTimeout(() => toast.classList.remove('show'), 3500);
|
||
}
|
||
}());
|
||
</script>
|
||
@endsection
|