Add all files
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
{{--
|
||||
Credits Plus Enhanced Navigation Component
|
||||
|
||||
Created by: Exp (Discord: exp_yt)
|
||||
Company: ExpHost - https://www.exphost.net/
|
||||
|
||||
This code is the intellectual property of Exp and ExpHost.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
--}}
|
||||
<style>
|
||||
.text-flow-effect {
|
||||
background: linear-gradient(75deg,
|
||||
hsl(178, 46.40%, 11.00%) 0%,
|
||||
/* Tailwind slate-900 */
|
||||
hsl(211, 57.80%, 17.60%) 30%,
|
||||
hsl(220, 98%, 61%) 45%,
|
||||
/* Tailwind blue-500 */
|
||||
hsl(207, 53.00%, 50.80%) 55%,
|
||||
/* Tailwind fuchsia-700 */
|
||||
hsl(214, 82.50%, 31.40%) 70%,
|
||||
hsl(222, 47%, 11%) 100%);
|
||||
background-size: 400% auto;
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
animation: textFlowAnimation 8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes textFlowAnimation {
|
||||
to {
|
||||
background-position: 400% center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<nav
|
||||
class="w-full px-4 lg:px-8 bg-background-secondary border-b border-neutral h-auto flex flex-col lg:flex-row justify-between fixed top-0 z-20">
|
||||
<div x-data="{ slideOverOpen: false }"
|
||||
x-init="$watch('slideOverOpen', value => { document.documentElement.style.overflow = value ? 'hidden' : '' })"
|
||||
class="relative z-50 w-full h-auto">
|
||||
|
||||
<div class="flex flex-row items-center justify-between w-full h-16">
|
||||
|
||||
<!-- Logo and app name -->
|
||||
<div class="flex flex-row items-center">
|
||||
<a href="{{ route('home') }}" class="flex flex-row items-center h-10 gap-2" wire:navigate>
|
||||
@if (config('settings.logo'))
|
||||
<img src="{{ Storage::url(config('settings.logo')) }}" alt="{{ config('app.name') }}" class="h-10 w-auto inline-block">
|
||||
@endif
|
||||
<span class="text-xl font-bold leading-none flex items-center text-flow-effect">
|
||||
{{ config('app.name') }}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<!-- Desktop menu: show only lg and above -->
|
||||
<div class="hidden lg:flex flex-row ml-7">
|
||||
@foreach (\App\Classes\Navigation::getLinks() as $nav)
|
||||
@if (isset($nav['children']) && count($nav['children']) > 0)
|
||||
<div class="relative">
|
||||
<x-dropdown>
|
||||
<x-slot:trigger>
|
||||
<div class="flex flex-col">
|
||||
<span
|
||||
class="flex flex-row items-center p-3 text-sm font-semibold whitespace-nowrap text-base hover:text-base/80">
|
||||
{{ $nav['name'] }}
|
||||
</span>
|
||||
</div>
|
||||
</x-slot:trigger>
|
||||
|
||||
<x-slot:content>
|
||||
@foreach ($nav['children'] as $child)
|
||||
<x-navigation.link :href="route($child['route'], $child['params'] ?? null)"
|
||||
:spa="isset($child['spa']) ? $nav['spa'] : true">
|
||||
{{ $child['name'] }}
|
||||
</x-navigation.link>
|
||||
@endforeach
|
||||
</x-slot:content>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
@else
|
||||
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? null)"
|
||||
:spa="isset($nav['spa']) ? $nav['spa'] : true"
|
||||
class="flex items-center p-3">
|
||||
{{ $nav['name'] }}
|
||||
</x-navigation.link>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right-side controls -->
|
||||
<div class="flex flex-row items-center">
|
||||
|
||||
<!-- Desktop-only controls -->
|
||||
<div class="hidden lg:flex items-center mr-3 gap-3">
|
||||
<!--<x-dropdown>
|
||||
<x-slot:trigger>
|
||||
<span class="text-sm font-semibold">
|
||||
{{ strtoupper(app()->getLocale()) }} | {{ session('currency', config('settings.default_currency')) }}
|
||||
</span>
|
||||
</x-slot:trigger>
|
||||
<x-slot:content>
|
||||
<strong class="block p-2 text-xs font-semibold uppercase text-base/50"> Language </strong>
|
||||
<livewire:components.language-switch />
|
||||
<livewire:components.currency-switch />
|
||||
</x-slot:content>
|
||||
</x-dropdown>-->
|
||||
|
||||
@if(auth()->check() && isset($userCreditsDisplay))
|
||||
<a href="{{ $creditsUrl ?? '/account/credits' }}" wire:navigate
|
||||
class="text-sm text-base hover:text-primary transition-colors duration-200 px-3 py-2 whitespace-nowrap">
|
||||
Credits: {{ $userCreditsDisplay }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<x-theme-toggle />
|
||||
</div>
|
||||
|
||||
<!-- Cart (always visible) -->
|
||||
<livewire:components.cart />
|
||||
|
||||
<!-- Desktop account menu -->
|
||||
@if(auth()->check())
|
||||
<div class="hidden lg:flex">
|
||||
<x-dropdown>
|
||||
<x-slot:trigger>
|
||||
<img src="{{ auth()->user()->avatar }}"
|
||||
class="size-8 rounded-full border border-neutral bg-background" alt="avatar" />
|
||||
</x-slot:trigger>
|
||||
<x-slot:content>
|
||||
<div class="flex flex-col p-2">
|
||||
<span class="text-sm text-base break-words">{{ auth()->user()->name }}</span>
|
||||
<span class="text-sm text-base break-words">{{ auth()->user()->email }}</span>
|
||||
</div>
|
||||
@foreach (\App\Classes\Navigation::getAccountDropdownLinks() as $nav)
|
||||
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? null)"
|
||||
:spa="isset($nav['spa']) ? $nav['spa'] : true">
|
||||
{{ $nav['name'] }}
|
||||
</x-navigation.link>
|
||||
@endforeach
|
||||
<livewire:auth.logout />
|
||||
</x-slot:content>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
@else
|
||||
<div class="hidden lg:flex flex-row gap-3">
|
||||
<a href="{{ route('login') }}" wire:navigate>
|
||||
<x-button.secondary>{{ __('navigation.login') }}</x-button.secondary>
|
||||
</a>
|
||||
@if(!config('settings.registration_disabled', false))
|
||||
<a href="{{ route('register') }}" wire:navigate>
|
||||
<x-button.primary>{{ __('navigation.register') }}</x-button.primary>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Mobile menu toggle (below lg) -->
|
||||
<button @click="slideOverOpen = !slideOverOpen"
|
||||
class="relative w-10 h-10 flex lg:hidden items-center justify-center rounded-lg hover:bg-neutral transition"
|
||||
aria-label="Toggle Menu">
|
||||
<span x-show="!slideOverOpen" x-transition ... class="absolute inset-0 flex items-center justify-center">
|
||||
<x-ri-menu-fill class="size-5" />
|
||||
</span>
|
||||
<span x-show="slideOverOpen" x-transition ... class="absolute inset-0 flex items-center justify-center">
|
||||
<x-ri-close-fill class="size-5" />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile slide-over menu -->
|
||||
<template x-teleport="body">
|
||||
<div x-show="slideOverOpen" @keydown.window.escape="slideOverOpen=false" x-cloak
|
||||
class="fixed left-0 right-0 top-16 w-full z-[99]" style="height:calc(100dvh - 4rem);" aria-modal="true">
|
||||
<div x-show="slideOverOpen" @click.away="slideOverOpen = false"
|
||||
x-transition.opacity.duration.300ms
|
||||
class="absolute inset-0 bg-background-secondary/80 backdrop-blur-md border-t border-neutral/50 shadow-2xl overflow-y-auto flex flex-col rounded-t-2xl">
|
||||
<div class="flex flex-col h-full p-6">
|
||||
<div class="flex-1 min-h-0 overflow-y-auto">
|
||||
@include('creditsplus::components.navigation.sidebar-links')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -0,0 +1,263 @@
|
||||
{{--
|
||||
Credits Plus Enhanced Sidebar Links Component
|
||||
|
||||
Created by: Exp (Discord: exp_yt)
|
||||
Company: ExpHost - https://www.exphost.net/
|
||||
|
||||
This code is the intellectual property of Exp and ExpHost.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
--}}
|
||||
<div class="lg:px-4 lg:py-6 flex flex-col gap-3 h-full">
|
||||
<div class="flex flex-col gap-2">
|
||||
@foreach (\App\Classes\Navigation::getLinks() as $nav)
|
||||
@if (!empty($nav['children']))
|
||||
<div x-data="{ activeAccordion: {{ $nav['active'] ? 'true' : 'false' }} }"
|
||||
class="relative w-full overflow-hidden text-sm font-medium">
|
||||
<div class="cursor-pointer">
|
||||
<button @click="activeAccordion = !activeAccordion"
|
||||
class="flex items-center justify-between w-full p-3 text-sm font-semibold whitespace-nowrap rounded-lg
|
||||
hover:bg-primary/10 transition-colors duration-200 {{ $nav['active'] ? 'text-primary bg-primary/5' : 'text-color-base' }}">
|
||||
<div class="flex items-center gap-3">
|
||||
@isset($nav['icon'])
|
||||
<x-dynamic-component :component="$nav['icon']"
|
||||
class="size-5 {{ $nav['active'] ? 'text-primary' : 'text-color-muted' }}" />
|
||||
@endisset
|
||||
<span>{{ $nav['name'] }}</span>
|
||||
</div>
|
||||
<x-ri-arrow-down-s-line x-bind:class="{ 'rotate-180': activeAccordion }"
|
||||
class="size-5 text-color-muted transition-transform duration-300" />
|
||||
</button>
|
||||
<div x-show="activeAccordion" x-collapse x-cloak>
|
||||
<div class="py-2 pl-8 pr-4 border-l-2 border-primary/50 ml-4 opacity-90">
|
||||
@foreach ($nav['children'] as $child)
|
||||
<div class="flex items-center py-1">
|
||||
<x-navigation.link :href="route($child['route'], $child['params'] ?? [])"
|
||||
:spa="$child['spa'] ?? true"
|
||||
class="block w-full p-2 rounded-md transition-colors duration-200
|
||||
{{ $child['active'] ? 'text-primary font-bold bg-primary/10' : 'text-color-muted hover:bg-primary/5' }}">
|
||||
{{ $child['name'] }}
|
||||
</x-navigation.link>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? [])"
|
||||
:spa="$nav['spa'] ?? true"
|
||||
class="flex items-center gap-3 p-3 rounded-lg transition-colors duration-200 w-full
|
||||
{{ $nav['active'] ? 'bg-primary/10 text-primary font-semibold' : 'hover:bg-primary/5 text-color-base' }}">
|
||||
@isset($nav['icon'])
|
||||
<x-dynamic-component :component="$nav['icon']"
|
||||
class="size-5 {{ $nav['active'] ? 'text-primary' : 'text-color-muted' }}" />
|
||||
@endisset
|
||||
<span>{{ $nav['name'] }}</span>
|
||||
</x-navigation.link>
|
||||
@endif
|
||||
@isset($nav['separator'])
|
||||
<div class="h-px w-full bg-neutral/50 my-2"></div>
|
||||
@endisset
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- Separator between main navigation and dashboard navigation --}}
|
||||
@auth
|
||||
<div class="h-px w-full bg-neutral/50 my-2"></div>
|
||||
@endauth
|
||||
|
||||
{{-- Dashboard Navigation Section --}}
|
||||
<div class="flex flex-col gap-2 flex-grow">
|
||||
@foreach (\App\Classes\Navigation::getDashboardLinks() as $nav)
|
||||
@if (!empty($nav['children']))
|
||||
<div x-data="{ activeAccordion: {{ $nav['active'] ? 'true' : 'false' }} }"
|
||||
class="relative w-full overflow-hidden text-sm font-medium">
|
||||
<div class="cursor-pointer">
|
||||
<button @click="activeAccordion = !activeAccordion"
|
||||
class="flex items-center justify-between w-full p-3 text-sm font-semibold whitespace-nowrap rounded-lg
|
||||
hover:bg-primary/10 transition-colors duration-200 {{ $nav['active'] ? 'text-primary bg-primary/5' : 'text-color-base' }}">
|
||||
<div class="flex items-center gap-3">
|
||||
@isset($nav['icon'])
|
||||
<x-dynamic-component :component="$nav['icon']"
|
||||
class="size-5 {{ $nav['active'] ? 'text-primary' : 'text-color-muted' }}" />
|
||||
@endisset
|
||||
<span>{{ $nav['name'] }}</span>
|
||||
</div>
|
||||
<x-ri-arrow-down-s-line x-bind:class="{ 'rotate-180': activeAccordion }"
|
||||
class="size-5 text-color-muted transition-transform duration-300" />
|
||||
</button>
|
||||
<div x-show="activeAccordion" x-collapse x-cloak>
|
||||
<div class="py-2 pl-8 pr-4 border-l-2 border-primary/50 ml-4 opacity-90">
|
||||
@foreach ($nav['children'] as $child)
|
||||
@if ($child['condition'] ?? true)
|
||||
<div class="flex items-center py-1">
|
||||
<x-navigation.link :href="route($child['route'], $child['params'] ?? [])"
|
||||
:spa="$child['spa'] ?? true"
|
||||
class="block w-full p-2 rounded-md transition-colors duration-200
|
||||
{{ $child['active'] ? 'text-primary font-bold bg-primary/10' : 'text-color-muted hover:bg-primary/5' }}">
|
||||
{{ $child['name'] }}
|
||||
</x-navigation.link>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? [])"
|
||||
:spa="$nav['spa'] ?? true"
|
||||
class="flex items-center gap-3 p-3 rounded-lg transition-colors duration-200 w-full
|
||||
{{ $nav['active'] ? 'bg-primary/10 text-primary font-semibold' : 'hover:bg-primary/5 text-color-base' }}">
|
||||
@isset($nav['icon'])
|
||||
<x-dynamic-component :component="$nav['icon']"
|
||||
class="size-5 {{ $nav['active'] ? 'text-primary' : 'text-color-muted' }}" />
|
||||
@endisset
|
||||
<span>{{ $nav['name'] }}</span>
|
||||
</x-navigation.link>
|
||||
@endif
|
||||
@isset($nav['separator'])
|
||||
<div class="h-px w-full bg-neutral/50 my-2"></div>
|
||||
@endisset
|
||||
@endforeach
|
||||
|
||||
{{-- Language, Currency & Theme Toggle for Mobile --}}
|
||||
<div class="flex flex-row items-center mt-6 justify-between md:hidden border-t border-neutral/50 pt-4">
|
||||
<x-dropdown>
|
||||
<x-slot:trigger>
|
||||
<div class="flex flex-col items-start cursor-pointer hover:opacity-80 transition-opacity duration-200">
|
||||
<div class="flex items-center gap-2">
|
||||
<x-ri-global-line class="size-5 text-color-muted" />
|
||||
<span class="text-sm text-color-base font-semibold">
|
||||
{{ strtoupper(app()->getLocale()) }}
|
||||
<span class="text-color-muted/50 font-semibold mx-1">|</span>
|
||||
<span class="text-color-base">{{ session('currency', config('settings.default_currency')) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-xs text-color-muted mt-1">Language & Currency</span>
|
||||
</div>
|
||||
</x-slot:trigger>
|
||||
<x-slot:content>
|
||||
<strong class="block p-2 text-xs font-semibold uppercase text-color-muted">Language</strong>
|
||||
<livewire:components.language-switch />
|
||||
<strong class="block p-2 text-xs font-semibold uppercase text-color-muted mt-2">Currency</strong>
|
||||
<livewire:components.currency-switch />
|
||||
</x-slot:content>
|
||||
</x-dropdown>
|
||||
|
||||
<x-theme-toggle class="p-2 rounded-full hover:bg-primary/5 transition-colors duration-200" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(auth()->check())
|
||||
<div class="border-t border-neutral/50 pt-4 mt-auto">
|
||||
{{-- Credits Display for Mobile --}}
|
||||
@php
|
||||
try {
|
||||
$user = auth()->user();
|
||||
$credits = $user->credits()->with('currency')->get();
|
||||
|
||||
if ($credits->isEmpty()) {
|
||||
$currencyCode = session('currency', config('settings.default_currency', 'USD'));
|
||||
$currency = \App\Models\Currency::where('code', $currencyCode)->first();
|
||||
if ($currency) {
|
||||
$creditsDisplay = $currency->prefix . '0.00' . $currency->suffix;
|
||||
} else {
|
||||
$creditsDisplay = '0.00 ' . $currencyCode;
|
||||
}
|
||||
} else {
|
||||
$creditsByCurrency = $credits->groupBy('currency_code');
|
||||
$maxAmount = null;
|
||||
$maxCurrencyCode = null;
|
||||
$maxCurrency = null;
|
||||
|
||||
foreach ($creditsByCurrency as $currencyCode => $userCredits) {
|
||||
$totalAmount = $userCredits->sum('amount');
|
||||
if ($totalAmount <= 0) continue;
|
||||
if ($maxAmount === null || $totalAmount > $maxAmount) {
|
||||
$maxAmount = $totalAmount;
|
||||
$maxCurrencyCode = $currencyCode;
|
||||
$maxCurrency = $userCredits->first()->currency;
|
||||
}
|
||||
}
|
||||
|
||||
if ($maxAmount === null) {
|
||||
$currencyCode = session('currency', config('settings.default_currency', 'USD'));
|
||||
$currency = \App\Models\Currency::where('code', $currencyCode)->first();
|
||||
if ($currency) {
|
||||
$creditsDisplay = $currency->prefix . '0.00' . $currency->suffix;
|
||||
} else {
|
||||
$creditsDisplay = '0.00 ' . $currencyCode;
|
||||
}
|
||||
} else {
|
||||
if ($maxCurrency) {
|
||||
$creditsDisplay = $maxCurrency->prefix . number_format($maxAmount, 2) . $maxCurrency->suffix;
|
||||
} else {
|
||||
$creditsDisplay = number_format($maxAmount, 2) . ' ' . $maxCurrencyCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$creditsDisplay = '0.00';
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if(isset($creditsDisplay))
|
||||
<a href="{{ route('account.credits') }}" wire:navigate
|
||||
class="flex items-center justify-between p-3 mb-3 bg-gradient-to-r from-primary/10 to-secondary/10 hover:from-primary/20 hover:to-secondary/20 border border-primary/30 rounded-xl transition-all duration-200">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-primary/20 text-primary p-2 rounded-lg">
|
||||
<x-ri-copper-coin-line class="size-5" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs text-color-muted">Account Credits</span>
|
||||
<span class="text-sm font-bold text-primary">{{ $creditsDisplay }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<x-ri-arrow-right-line class="size-5 text-color-muted" />
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<div class="flex items-center gap-3 p-3 mb-3 bg-background-secondary/50 rounded-xl">
|
||||
<img src="{{ auth()->user()->avatar }}" class="size-10 rounded-full border-2 border-neutral bg-background shadow-lg" alt="User Avatar" />
|
||||
<div class="flex flex-col flex-1">
|
||||
<span class="text-sm font-semibold text-color-base">{{ auth()->user()->name }}</span>
|
||||
<span class="text-xs text-color-muted truncate">{{ auth()->user()->email }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Account dropdown links for mobile -->
|
||||
<div class="flex flex-col gap-1 mb-3">
|
||||
@foreach (\App\Classes\Navigation::getAccountDropdownLinks() as $nav)
|
||||
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? null)" :spa="isset($nav['spa']) ? $nav['spa'] : true"
|
||||
class="flex items-center gap-3 px-4 py-2.5 text-sm text-color-base hover:bg-background-secondary/50 rounded-lg transition-all duration-200">
|
||||
{{ $nav['name'] }}
|
||||
</x-navigation.link>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<livewire:auth.logout
|
||||
class="flex items-center gap-3 p-3 rounded-lg transition-colors duration-200 w-full text-color-base hover:bg-error/10 hover:text-error border border-neutral/50" />
|
||||
</div>
|
||||
@else
|
||||
<div class="border-t border-neutral/50 pt-4 mt-auto">
|
||||
<div class="flex flex-col gap-3">
|
||||
<a href="{{ route('login') }}" wire:navigate class="w-full">
|
||||
<button class="flex items-center justify-center gap-2 w-full px-4 py-3 text-sm font-semibold text-color-base border-2 border-neutral hover:border-primary hover:text-primary hover:bg-primary/5 rounded-xl transition-all duration-200">
|
||||
<x-ri-login-box-line class="size-5" />
|
||||
<span>{{ __('navigation.login') }}</span>
|
||||
</button>
|
||||
</a>
|
||||
@if(!config('settings.registration_disabled', false))
|
||||
<a href="{{ route('register') }}" wire:navigate class="w-full">
|
||||
<button class="flex items-center justify-center gap-2 w-full px-4 py-3 text-sm font-semibold text-white bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl transform hover:scale-[1.02]">
|
||||
<x-ri-user-add-line class="size-5" />
|
||||
<span>{{ __('navigation.register') }}</span>
|
||||
</button>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user