File: /home/u756937133/domains/swingersnest.com/public_html/staging/resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="scroll-smooth">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@php
$siteSettings = \App\Models\SiteSetting::getSettings();
$siteTitle = $siteSettings->site_title ?? config('app.name', 'SwingersNest');
$siteDescription = $siteSettings->site_description ?? config('app.meta_description', 'SwingersNest');
$siteKeywords = $siteSettings->site_keywords ?? '';
@endphp
<title>@yield('title', $siteTitle)</title>
<meta name="description" content="@yield('meta_description', $siteDescription)">
@if($siteKeywords)
<meta name="keywords" content="{{ $siteKeywords }}">
@endif
<meta name="csrf-token" content="{{ csrf_token() }}">
{{-- Resource Hints for Performance --}}
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
<link rel="dns-prefetch" href="https://rsms.me">
<link rel="dns-prefetch" href="https://ui-avatars.com">
{{-- Favicon & Icon --}}
<link rel="icon" type="image/svg+xml" href="{{ asset('assets/logonew.png') }}">
<!-- @if($siteSettings->site_favicon)
<link rel="icon" type="image/x-icon" href="{{ asset($siteSettings->site_favicon) }}">
@else
<link rel="icon" type="image/svg+xml" href="{{ asset('logonew.png') }}">
@endif -->
@if($siteSettings->site_icon)
<link rel="apple-touch-icon" href="{{ asset($siteSettings->site_icon) }}">
@elseif($siteSettings->site_favicon)
<link rel="apple-touch-icon" href="{{ asset($siteSettings->site_favicon) }}">
@else
<link rel="apple-touch-icon" href="{{ asset('favicon.svg') }}">
@endif
{{-- Open Graph Meta Tags --}}
@if($siteSettings->og_title || $siteSettings->og_description)
<meta property="og:type" content="{{ $siteSettings->og_type ?? 'website' }}">
<meta property="og:title" content="{{ $siteSettings->og_title ?? $siteTitle }}">
<meta property="og:description" content="{{ $siteSettings->og_description ?? $siteDescription }}">
@if($siteSettings->og_image)
<meta property="og:image" content="{{ url(asset($siteSettings->og_image)) }}">
@endif
@if($siteSettings->og_site_name)
<meta property="og:site_name" content="{{ $siteSettings->og_site_name }}">
@endif
@if($siteSettings->og_url)
<meta property="og:url" content="{{ $siteSettings->og_url }}">
@endif
@endif
{{-- Twitter Card Meta Tags --}}
@if($siteSettings->twitter_title || $siteSettings->twitter_description)
<meta name="twitter:card" content="{{ $siteSettings->twitter_card_type ?? 'summary_large_image' }}">
<meta name="twitter:title" content="{{ $siteSettings->twitter_title ?? $siteTitle }}">
<meta name="twitter:description" content="{{ $siteSettings->twitter_description ?? $siteDescription }}">
@if($siteSettings->twitter_image)
<meta name="twitter:image" content="{{ url(asset($siteSettings->twitter_image)) }}">
@elseif($siteSettings->og_image)
<meta name="twitter:image" content="{{ url(asset($siteSettings->og_image)) }}">
@endif
@endif
{{-- Header Scripts (GSC, Analytics, etc.) --}}
@if($siteSettings->header_scripts)
{!! $siteSettings->header_scripts !!}
@endif
{{-- Custom CSS --}}
@if($siteSettings->custom_css)
<style>
{!! $siteSettings->custom_css !!}
</style>
@endif
<!-- Remix Icons CDN with preload -->
<link
rel="preload"
href="https://cdn.jsdelivr.net/npm/remixicon@4.7.0/fonts/remixicon.css"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
>
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remixicon@4.7.0/fonts/remixicon.css"></noscript>
<!-- Google Fonts: Combined and optimized with font-display: swap -->
<link
rel="preload"
href="https://fonts.googleapis.com/css2?family=Arimo:wght@400;500;600;700&family=Leckerli+One&family=Grand+Hotel&display=swap"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
>
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Arimo:wght@400;500;600;700&family=Leckerli+One&family=Grand+Hotel&display=swap"></noscript>
<!-- <link rel="stylesheet" href="{{ asset('public/style.css') }}"> -->
@if(request()->routeIs('home') || request()->is('/'))
<!-- <script src="https://cdn.tailwindcss.com"></script> -->
<!-- //Hasseb don't use CDN , run NPM run dev its auto enable all tailwind classes -->
@endif
<style>
/* Ensure user dropdown hover works */
.group:hover #user-dropdown-menu,
#user-dropdown-menu:hover {
opacity: 1 !important;
visibility: visible !important;
}
</style>
<script>
// Initialize theme immediately to prevent flash of wrong theme
// Default to 'light' mode for new visitors - light mode is the default theme
(function() {
const storedTheme = localStorage.getItem('theme');
const theme = storedTheme || 'light';
if (theme === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
})();
// Global auth flag
window.isAuthenticated = @json(auth()->check());
</script>
<!-- Inter font - optimized with preload -->
<link
rel="preload"
href="https://rsms.me/inter/inter.css"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
>
<noscript><link rel="stylesheet" href="https://rsms.me/inter/inter.css"></noscript>
@vite(['resources/css/app.css', 'resources/js/app.js'])
@stack('head')
</head>
<body class="antialiased font-sans {{ request()->routeIs('messages.*') ? 'messages-page-no-scroll' : '' }}">
<script>
// Clean up any leftover body styles from previous pages
// The settings page was setting overflow: hidden and height: 100vh which shouldn't persist
document.addEventListener('DOMContentLoaded', function() {
// Small delay to check if modals need these styles
setTimeout(function() {
const hasOpenModal = document.querySelector('.fixed.inset-0.z-50:not(.hidden), [role="dialog"]:not([aria-hidden="true"])');
// Clear height: 100vh as it's almost never needed on body
if (document.body.style.height === '100vh' && !hasOpenModal) {
document.body.style.height = '';
}
}, 50);
});
</script>
<div class="min-h-screen pt-[68px] {{ request()->routeIs('messages.*') ? 'h-screen overflow-hidden flex flex-col' : '' }}">
@include('partials.header')
{{-- Profile Pending Approval Bar --}}
@auth
@if(!Auth::user()->is_active && !Auth::user()->is_admin)
<div class="w-full bg-red-600 text-white py-3 px-4 shadow-md">
<div class="container mx-auto flex items-center justify-between">
<div class="flex items-center gap-3">
<i class="ri-alert-line text-xl"></i>
<p class="font-semibold">
Your profile is pending admin approval. You will be able to access all features once your profile is approved.
</p>
</div>
</div>
</div>
@endif
@endauth
<main class="{{ request()->routeIs('messages.*') ? 'flex-1 min-h-0 min-w-0 overflow-hidden flex flex-col' : '' }}">
@hasSection('full-width')
@yield('full-width')
@else
<div class="px-4 sm:px-6 lg:px-8 py-6">
@yield('content')
</div>
@endif
</main>
@unless(request()->routeIs('messages.*'))
@include('partials.footer')
@endunless
</div>
{{-- Mobile Bottom Navigation Bar (authenticated users only) --}}
@include('partials.mobile-bottom-nav')
@include('components.toast')
@stack('modals')
@include('partials.auth-modals')
<!-- Complete Profile Modal -->
@auth
@php
$user = Auth::user();
$profile = $user->profile;
// Calculate Profile Completion Percentage (same logic as profile page)
$languages = $profile && $profile->languages
? (is_array($profile->languages) ? $profile->languages : json_decode($profile->languages, true) ?? [])
: [];
$preferences = $profile && $profile->preferences
? (is_array($profile->preferences) ? $profile->preferences : json_decode($profile->preferences, true) ?? [])
: [];
$isCouple = $profile && $profile->category === 'couple';
$coupleData = $profile && $profile->couple_data ? (is_array($profile->couple_data) ? $profile->couple_data : json_decode($profile->couple_data, true) ?? []) : [];
$profileFields = [
'category' => $profile && $profile->category ? 1 : 0,
'preferences' => $profile && $profile->preferences && !empty($preferences) ? 1 : 0,
'date_of_birth' => $profile && $profile->date_of_birth ? 1 : 0,
'sexuality' => $profile && $profile->sexuality ? 1 : 0,
'relationship_status' => $profile && $profile->relationship_status ? 1 : 0,
'relationship_orientation' => $profile && $profile->relationship_orientation ? 1 : 0,
'home_location' => $profile && $profile->home_location ? 1 : 0,
'country' => $profile && $profile->country ? 1 : 0,
'city' => $profile && $profile->city ? 1 : 0,
'languages' => !empty($languages) ? 1 : 0,
'bio' => $profile && $profile->bio ? 1 : 0,
'weight' => $profile && $profile->weight ? 1 : 0,
'height' => $profile && $profile->height ? 1 : 0,
'body_type' => $profile && $profile->body_type ? 1 : 0,
'eye_color' => $profile && $profile->eye_color ? 1 : 0,
'hair_color' => $profile && $profile->hair_color ? 1 : 0,
'profile_photo' => ($profile && $profile->profile_photo) || ($user->profile_image) ? 1 : 0,
];
// For couple profiles, check couple_data fields
if ($isCouple && !empty($coupleData)) {
$profileFields['date_of_birth'] = (!empty($coupleData['date_of_birth_her']) || !empty($coupleData['date_of_birth_him'])) ? 1 : $profileFields['date_of_birth'];
$profileFields['sexuality'] = (!empty($coupleData['sexuality_her']) || !empty($coupleData['sexuality_him'])) ? 1 : $profileFields['sexuality'];
}
$completedFields = array_sum($profileFields);
$totalFields = count($profileFields);
$profileCompletion = $totalFields > 0 ? round(($completedFields / $totalFields) * 100) : 0;
// Show modal ONLY if profile completion is less than 80%
// Modal will automatically stop showing once profile reaches 80% or more
// Don't show on auth pages or edit profile page
$excludedRoutes = ['account.profile.edit', 'login', 'register', 'password.request', 'password.reset'];
$isExcludedRoute = false;
foreach($excludedRoutes as $route) {
if(request()->routeIs($route)) {
$isExcludedRoute = true;
break;
}
}
// Modal shows when completion < 80%, hides automatically when >= 80%
$showCompleteProfileModal = $profileCompletion < 80 && !$isExcludedRoute;
@endphp
@if($showCompleteProfileModal)
<div id="complete-profile-modal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4 hidden">
<div class="bg-white dark:bg-black rounded-2xl shadow-2xl max-w-md w-full transform transition-all">
<!-- Modal Header -->
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Complete Your Profile</h3>
<button id="close-complete-profile-modal" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<i class="ri-close-line text-2xl"></i>
</button>
</div>
</div>
<!-- Modal Body -->
<div class="px-6 py-6">
<div class="text-center mb-6">
<div class="w-20 h-20 mx-auto mb-4 bg-gradient-to-br from-pink-100 to-pink-200 dark:from-pink-900/30 dark:to-pink-800/30 rounded-full flex items-center justify-center">
<i class="ri-user-settings-line text-4xl text-[#9810FA] dark:text-[#E60076] dark:text-[#9810FA] dark:text-[#E60076]"></i>
</div>
<h4 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Your profile is {{ $profileCompletion }}% complete
</h4>
<p class="text-sm text-gray-600 dark:text-gray-400">
Complete your profile to get better matches and connect with more members. Add your photos, preferences, and personal details to stand out!
</p>
<!-- Progress Bar -->
<div class="mt-4">
<div class="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div class="h-full bg-gradient-to-r from-[#9810FA] to-[#E60076] rounded-full transition-all duration-300" style="width: {{ $profileCompletion }}%"></div>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-2">{{ $completedFields }} of {{ $totalFields }} fields completed</p>
</div>
</div>
</div>
<!-- Modal Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex gap-3">
<button id="continue-browsing-btn" class="flex-1 px-4 py-2.5 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-xl font-semibold hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
Continue Browsing
</button>
<a href="{{ route('account.profile.edit') }}" class="flex-1 px-4 py-2.5 bg-gradient-to-r from-[#9810FA] to-[#E60076] text-white rounded-xl font-semibold hover:shadow-lg transition-all text-center">
Complete Profile
</a>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const modal = document.getElementById('complete-profile-modal');
const closeBtn = document.getElementById('close-complete-profile-modal');
const continueBtn = document.getElementById('continue-browsing-btn');
// Storage key for tracking when modal was last closed
const storageKey = 'complete-profile-modal-closed';
const hoursToWait = 24;
const millisecondsToWait = hoursToWait * 60 * 60 * 1000; // 24 hours in milliseconds
// Check if modal should be shown based on 24-hour rule
function shouldShowModal() {
const lastClosed = localStorage.getItem(storageKey);
if (!lastClosed) {
// Never been closed, show it
return true;
}
const lastClosedTime = parseInt(lastClosed, 10);
const currentTime = Date.now();
const timeDifference = currentTime - lastClosedTime;
// Show modal if 24 hours have passed
return timeDifference >= millisecondsToWait;
}
// Store the current timestamp when modal is closed
function markModalAsClosed() {
localStorage.setItem(storageKey, Date.now().toString());
}
// Show modal on page load if conditions are met
if (modal) {
if (shouldShowModal()) {
// Delay showing modal slightly for better UX
setTimeout(function() {
modal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
}, 1000);
} else {
// Don't show modal, but keep it in DOM (hidden)
modal.classList.add('hidden');
}
}
function closeModal() {
if (modal) {
modal.classList.add('hidden');
document.body.style.overflow = '';
// Mark modal as closed with current timestamp
markModalAsClosed();
}
}
if (closeBtn) {
closeBtn.addEventListener('click', closeModal);
}
if (continueBtn) {
continueBtn.addEventListener('click', closeModal);
}
// Close modal when clicking outside
if (modal) {
modal.addEventListener('click', function(e) {
if (e.target === modal) {
closeModal();
}
});
}
// Close modal on Escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && modal && !modal.classList.contains('hidden')) {
closeModal();
}
});
});
</script>
@endif
@endauth
{{-- Footer Scripts --}}
@if($siteSettings->footer_scripts)
{!! $siteSettings->footer_scripts !!}
@endif
{{-- Custom JavaScript --}}
@if($siteSettings->custom_js)
<script>
{!! $siteSettings->custom_js !!}
</script>
@endif
@stack('scripts')
<!-- Global Settings Toggle Script -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const settingsToggleBtn = document.getElementById('settings-toggle-btn');
if (settingsToggleBtn) {
// Remove any existing listeners
const newBtn = settingsToggleBtn.cloneNode(true);
settingsToggleBtn.parentNode.replaceChild(newBtn, settingsToggleBtn);
newBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
// Check if we're on the profile page (index or edit)
const isProfilePage = window.location.pathname.includes('/account/profile');
const isEditPage = window.location.pathname.includes('/account/profile/edit');
if (isProfilePage && !isEditPage) {
// If on profile index page, toggle sidebar
const sidebar = document.getElementById('settings-sidebar');
if (sidebar) {
const isOpen = sidebar.style.width !== '0px' && sidebar.style.width !== '0';
if (isOpen) {
// Close sidebar
sidebar.style.width = '0';
sidebar.style.minWidth = '0';
sidebar.style.overflow = 'hidden';
} else {
// Open sidebar
sidebar.style.width = '320px'; // w-80 = 320px
sidebar.style.minWidth = '320px';
sidebar.style.overflow = 'auto';
}
} else {
// Fallback: use global function if available
if (typeof window.toggleSettingsSidebar === 'function') {
window.toggleSettingsSidebar();
}
}
// Don't navigate, just toggle sidebar
return;
} else {
// If on edit page or not on profile page, navigate to profile page and open sidebar
window.location.href = '{{ route("account.profile") }}?settings=true';
}
});
}
});
// Show toast notifications for session messages
// Wait for app.js to load first
function showSessionToasts() {
if (typeof showToast === 'undefined') {
// Retry after a short delay if showToast is not yet available
setTimeout(showSessionToasts, 100);
return;
}
@if (session('success'))
showToast('{{ addslashes(session('success')) }}', 'success', 5000);
@endif
@if (session('coins_earned'))
showToast('You got {{ (int) session('coins_earned') }} SNC!', 'success', 5000);
@endif
@if (session('error'))
showToast('{{ addslashes(session('error')) }}', 'error', 5000);
@endif
@if ($errors->any())
@foreach ($errors->all() as $error)
showToast('{{ addslashes($error) }}', 'error', 5000);
@endforeach
@endif
}
// Run when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', showSessionToasts);
} else {
// DOM is already ready
showSessionToasts();
}
</script>
</body>
</html>