- Updated dropdown component styles and transitions for improved UX. - Refined checkbox, input, radio, select, and textarea components with consistent error messaging and styling. - Introduced a new card component for better layout options. - Added a stat card component for displaying metrics with trends. - Implemented a command palette for global search and navigation, accessible via Cmd/Ctrl+K. - Enhanced modal and navigation components with new styles and transitions. - Improved layout styles for better responsiveness and visual appeal.
416 lines
15 KiB
PHP
416 lines
15 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" @if(in_array(app()->getLocale(), config('app.rtl_locales'))) dir="rtl" @endif>
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
|
|
<title>
|
|
@if(theme('seo_title'))
|
|
{{ theme('seo_title') }}
|
|
@isset($title)
|
|
- {{ $title }}
|
|
@endisset
|
|
@else
|
|
{{ config('app.name', 'Paymenter') }}
|
|
@isset($title)
|
|
- {{ $title }}
|
|
@endisset
|
|
@endif
|
|
</title>
|
|
|
|
@if(theme('seo_description'))
|
|
<meta name="description" content="{{ theme('seo_description') }}">
|
|
<meta property="og:description" content="{{ theme('seo_description') }}">
|
|
@endif
|
|
|
|
@if(theme('seo_keywords'))
|
|
<meta name="keywords" content="{{ theme('seo_keywords') }}">
|
|
@endif
|
|
|
|
@if(theme('seo_author'))
|
|
<meta name="author" content="{{ theme('seo_author') }}">
|
|
@endif
|
|
|
|
@vite(['themes/' . config('settings.theme') . '/js/app.js', 'themes/' . config('settings.theme') . '/css/app.css'], config('settings.theme'))
|
|
@include('layouts.colors')
|
|
|
|
@if (config('settings.logo'))
|
|
<link rel="icon" href="{{ Storage::url(config('settings.logo')) }}" type="image/png">
|
|
@endif
|
|
|
|
<!-- Open Graph Meta Tags -->
|
|
@isset($title)
|
|
<meta content="{{ theme('seo_title') ? theme('seo_title') . ' - ' . $title : config('app.name', 'Paymenter') . ' - ' . $title }}" property="og:title">
|
|
<meta content="{{ theme('seo_title') ? theme('seo_title') . ' - ' . $title : config('app.name', 'Paymenter') . ' - ' . $title }}" name="title">
|
|
@else
|
|
<meta content="{{ theme('seo_title') ?: config('app.name', 'Paymenter') }}" property="og:title">
|
|
<meta content="{{ theme('seo_title') ?: config('app.name', 'Paymenter') }}" name="title">
|
|
@endisset
|
|
|
|
@isset($description)
|
|
<meta content="{{ $description }}" property="og:description">
|
|
<meta content="{{ $description }}" name="description">
|
|
@endisset
|
|
|
|
<!-- Open Graph Image -->
|
|
@if(theme('og_image'))
|
|
<meta content="{{ theme('og_image') }}" property="og:image">
|
|
<meta content="{{ theme('og_image') }}" name="image">
|
|
<meta content="{{ theme('og_image') }}" property="twitter:image">
|
|
@elseif(isset($image))
|
|
<meta content="{{ $image }}" property="og:image">
|
|
<meta content="{{ $image }}" name="image">
|
|
<meta content="{{ $image }}" property="twitter:image">
|
|
@endif
|
|
|
|
<!-- Additional Open Graph Tags -->
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:url" content="{{ url()->current() }}">
|
|
<meta property="og:site_name" content="{{ config('app.name', 'Paymenter') }}">
|
|
|
|
<!-- Twitter Card Tags -->
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="{{ theme('seo_title') ?: config('app.name', 'Paymenter') }}{{ isset($title) ? ' - ' . $title : '' }}">
|
|
@if(theme('seo_description'))
|
|
<meta name="twitter:description" content="{{ theme('seo_description') }}">
|
|
@endif
|
|
|
|
<meta name="theme-color" content="{{ theme('primary') }}">
|
|
<meta name="locale" content="{{ app()->getLocale() }}">
|
|
|
|
<meta name="format-detection" content="telephone=no">
|
|
<meta name="mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
|
|
|
{!! hook('head') !!}
|
|
|
|
<!-- Custom CSS from theme settings -->
|
|
@if(theme('custom_css'))
|
|
<style>
|
|
{!! theme('custom_css') !!}
|
|
</style>
|
|
@endif
|
|
|
|
<!-- Custom HTML Head Section -->
|
|
@if(theme('custom_head_html'))
|
|
{!! theme('custom_head_html') !!}
|
|
@endif
|
|
|
|
<style>
|
|
/* Critical animations that should load immediately */
|
|
.animate-pulse-optimized {
|
|
animation: pulse-optimized 4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
}
|
|
|
|
@keyframes pulse-optimized {
|
|
0%, 100% { opacity: 0.1; transform: scale(1); }
|
|
50% { opacity: 0.25; transform: scale(1.05); }
|
|
}
|
|
|
|
.low-performance * {
|
|
animation-duration: 0.1s !important;
|
|
transition-duration: 0.1s !important;
|
|
}
|
|
|
|
.low-performance .animate-pulse-optimized {
|
|
animation: none !important;
|
|
opacity: 0.1 !important;
|
|
}
|
|
|
|
.reveal-on-scroll {
|
|
transform: translateY(20px);
|
|
opacity: 0;
|
|
transition: transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94),
|
|
opacity 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
}
|
|
|
|
.reveal-on-scroll.revealed {
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
}
|
|
|
|
.btn-loading {
|
|
position: relative;
|
|
color: transparent !important;
|
|
}
|
|
|
|
.btn-loading::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 16px;
|
|
height: 16px;
|
|
border: 2px solid currentColor;
|
|
border-radius: 50%;
|
|
border-top-color: transparent;
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: translate(-50%, -50%) rotate(360deg); }
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.mobile-optimized * {
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
.mobile-optimized button,
|
|
.mobile-optimized a {
|
|
touch-action: manipulation;
|
|
}
|
|
}
|
|
|
|
/* Background Image Styles */
|
|
.background-image-layer {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: -2;
|
|
background-size: cover;
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
background-attachment: fixed;
|
|
}
|
|
|
|
.background-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: -1;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.background-image-layer {
|
|
background-attachment: scroll;
|
|
}
|
|
}
|
|
|
|
/* Custom Integration Styles */
|
|
.custom-chat-widget {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
z-index: 9999;
|
|
}
|
|
|
|
.custom-notification-bar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 9998;
|
|
padding: 10px;
|
|
background: var(--primary);
|
|
color: white;
|
|
text-align: center;
|
|
transform: translateY(-100%);
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.custom-notification-bar.show {
|
|
transform: translateY(0);
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body class="w-full bg-background text-base min-h-screen flex flex-col antialiased {{ theme('background_image_url') ? '' : 'grid-bg noise-overlay' }}"
|
|
x-cloak
|
|
x-data="{darkMode: $persist(window.matchMedia('(prefers-color-scheme: dark)').matches), scrolled: false}"
|
|
x-init="window.addEventListener('scroll', () => scrolled = window.scrollY > 20, { passive: true })"
|
|
:class="{'dark': darkMode, 'is-scrolled': scrolled}">
|
|
|
|
{!! hook('body') !!}
|
|
|
|
<!-- Custom HTML Body Top -->
|
|
@if(theme('custom_body_top_html'))
|
|
{!! theme('custom_body_top_html') !!}
|
|
@endif
|
|
|
|
@if(theme('background_image_url'))
|
|
<div class="background-image-layer"
|
|
style="background-image: url('{{ theme('background_image_url') }}');
|
|
opacity: {{ theme('background_image_opacity', 30) / 100 }};
|
|
filter: blur({{ theme('background_image_blur', 5) }}px);">
|
|
</div>
|
|
|
|
<div class="background-overlay"
|
|
style="opacity: {{ theme('background_overlay_opacity', 70) / 100 }};">
|
|
</div>
|
|
@endif
|
|
|
|
<x-navigation />
|
|
|
|
@if(!theme('background_image_url'))
|
|
{{-- Subtle ambient gradient accents (Volt-inspired). Kept minimal so
|
|
content readability stays crisp on both desktop and mobile. --}}
|
|
<div class="fixed inset-0 overflow-hidden pointer-events-none z-0" aria-hidden="true">
|
|
<div class="absolute top-[-10%] left-[-10%] w-[420px] h-[420px] bg-primary rounded-full filter blur-[110px] opacity-[0.10] sm:opacity-[0.14] animate-pulse-optimized"></div>
|
|
|
|
<div class="hidden md:block absolute bottom-[-15%] right-[-10%] w-[480px] h-[480px] bg-secondary rounded-full filter blur-[120px] opacity-[0.10] animate-pulse-optimized"
|
|
style="animation-delay: -2s;"></div>
|
|
|
|
<div class="hidden lg:block absolute top-[30%] right-[20%] w-[260px] h-[260px] bg-primary rounded-full filter blur-[90px] opacity-[0.06] animate-pulse-optimized"
|
|
style="animation-delay: -4s;"></div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="relative z-10 w-full flex flex-grow">
|
|
@if (isset($sidebar) && $sidebar)
|
|
<x-navigation.sidebar :title="$title ?? ''" />
|
|
@endif
|
|
|
|
<div class="{{ (isset($sidebar) && $sidebar) ? 'md:ml-64 rtl:ml-0 rtl:md:mr-64' : '' }} flex min-w-0 flex-col flex-grow overflow-y-auto overflow-x-hidden">
|
|
|
|
<main class="w-full mt-24 mx-auto min-w-0 max-w-full px-4 sm:px-6 md:px-16 lg:px-16 relative z-10 transition-opacity duration-150"
|
|
x-data="{ navigating: false }"
|
|
x-init="
|
|
document.addEventListener('livewire:navigate', () => { navigating = true });
|
|
document.addEventListener('livewire:navigated', () => { $nextTick(() => { navigating = false }) });
|
|
"
|
|
:class="{ 'opacity-60': navigating }">
|
|
{{ $slot }}
|
|
</main>
|
|
|
|
<x-notification />
|
|
|
|
{{-- Same width and horizontal padding as <main> so footer aligns with page containers --}}
|
|
<div class="w-full relative z-0 mx-auto mb-3 min-w-0 max-w-full px-4 py-4 sm:px-6 md:px-16 lg:px-16">
|
|
<x-navigation.footer />
|
|
</div>
|
|
</div>
|
|
<x-impersonating />
|
|
</div>
|
|
|
|
{{-- Global command palette (Cmd/Ctrl+K) --}}
|
|
<x-command-palette />
|
|
|
|
{!! hook('footer') !!}
|
|
|
|
<!-- Custom JavaScript from theme settings -->
|
|
@if(theme('custom_js'))
|
|
<script>
|
|
{!! theme('custom_js') !!}
|
|
</script>
|
|
@endif
|
|
|
|
<!-- Custom HTML Body Bottom -->
|
|
@if(theme('custom_body_bottom_html'))
|
|
{!! theme('custom_body_bottom_html') !!}
|
|
@endif
|
|
|
|
<script>
|
|
(function() {
|
|
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
|
const isLowEnd = connection && (connection.saveData || connection.effectiveType === 'slow-2g' || connection.effectiveType === '2g');
|
|
const isSlowDevice = navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 2;
|
|
|
|
if (isLowEnd || isSlowDevice) {
|
|
document.body.classList.add('low-performance');
|
|
}
|
|
if ('IntersectionObserver' in window) {
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('revealed');
|
|
}
|
|
});
|
|
}, {
|
|
threshold: 0.1,
|
|
rootMargin: '50px'
|
|
});
|
|
|
|
document.querySelectorAll('.reveal-on-scroll').forEach(el => {
|
|
observer.observe(el);
|
|
});
|
|
}
|
|
|
|
document.addEventListener('click', function(e) {
|
|
// Skip Livewire/Alpine handled buttons - they manage their own loading states
|
|
if (e.target.hasAttribute('wire:click') ||
|
|
e.target.hasAttribute('wire:submit') ||
|
|
e.target.hasAttribute('x-on:click') ||
|
|
e.target.hasAttribute('@click') ||
|
|
e.target.closest('[wire\\:submit]')) {
|
|
return;
|
|
}
|
|
|
|
if (e.target.matches('button[type="submit"], .btn-submit')) {
|
|
e.target.classList.add('btn-loading');
|
|
|
|
setTimeout(() => {
|
|
e.target.classList.remove('btn-loading');
|
|
}, 3000);
|
|
}
|
|
});
|
|
|
|
if (window.innerWidth <= 768) {
|
|
document.body.classList.add('mobile-optimized');
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
{{-- Tilt / spotlight directive + scroll-reveal stagger indexing --}}
|
|
<script>
|
|
(function() {
|
|
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
function bindTilt(el) {
|
|
if (el.__tiltBound) return;
|
|
el.__tiltBound = true;
|
|
if (prefersReduced) return;
|
|
let raf = 0;
|
|
el.addEventListener('pointermove', (e) => {
|
|
const r = el.getBoundingClientRect();
|
|
const x = (e.clientX - r.left) / r.width;
|
|
const y = (e.clientY - r.top) / r.height;
|
|
if (raf) cancelAnimationFrame(raf);
|
|
raf = requestAnimationFrame(() => {
|
|
el.style.setProperty('--tilt-x', x.toFixed(3));
|
|
el.style.setProperty('--tilt-y', y.toFixed(3));
|
|
const rx = ((y - 0.5) * -4).toFixed(2);
|
|
const ry = ((x - 0.5) * 4).toFixed(2);
|
|
el.style.transform = `perspective(800px) rotateX(${rx}deg) rotateY(${ry}deg)`;
|
|
});
|
|
});
|
|
el.addEventListener('pointerleave', () => {
|
|
el.style.transform = '';
|
|
el.style.setProperty('--tilt-x', '0.5');
|
|
el.style.setProperty('--tilt-y', '0.5');
|
|
});
|
|
}
|
|
|
|
function indexStaggerGroups() {
|
|
document.querySelectorAll('[data-reveal-stagger]').forEach((group) => {
|
|
const items = group.querySelectorAll('.reveal-on-scroll');
|
|
items.forEach((el, i) => el.style.setProperty('--reveal-idx', i));
|
|
});
|
|
}
|
|
|
|
function init() {
|
|
document.querySelectorAll('[data-tilt]').forEach(bindTilt);
|
|
indexStaggerGroups();
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
document.addEventListener('livewire:navigated', init);
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html> |