Initial commit

This commit is contained in:
root
2026-05-09 17:28:23 +02:00
commit 9d73f82529
5575 changed files with 281989 additions and 0 deletions
@@ -0,0 +1,45 @@
@props(['article', 'forSlider' => false])
<div @class([
'h-[180px] md:h-[210px] rounded w-full shadow-sm relative overflow-hidden transition ease-in-out duration-200',
'hover:scale-[101%]' => !$forSlider,
'swiper-slide group' => $forSlider,
]) style="background-color: var(--color-surface)"
@if (!$forSlider)
onmouseover="slideImage({{ $article->id }})" onmouseleave="unslideImage({{ $article->id }})"
@endif>
<a href="{{ route('article.show', $article->slug) }}">
<div id="article-{{ $article->id }}" style="background: url('/storage/{{ $article->image }}');" class="article-image">
</div>
<div class="mt-3 md:mt-4 px-3 md:px-4">
<p @class([
'font-semibold text-base md:text-lg truncate',
'group-hover:text-[var(--color-primary)] transition duration-200' => $forSlider,
]) >
{{ $article->title }}
</p>
<div class="flex items-center gap-x-2">
<div
class="mt-2 md:mt-3 flex h-8 w-8 md:h-10 md:w-10 items-center justify-center overflow-hidden rounded-full" style="background-color: var(--color-background)">
<img src="{{ setting('avatar_imager') }}{{ $article->user?->look }}&headonly=1" alt="">
</div>
<p class="mt-2 md:mt-4 font-semibold text-sm md:text-base" style="color: var(--color-text-muted)">
{{ $article->user->username ?? setting('hotel_name') }}
</p>
</div>
</div>
</a>
</div>
<script>
function slideImage(articleId) {
document.getElementById('article-' + articleId).classList.add('article-image-slide');
}
function unslideImage(articleId) {
document.getElementById('article-' + articleId).classList.remove('article-image-slide');
}
</script>
@@ -0,0 +1,78 @@
<x-slot name="title">
<h2 class="text-2xl font-semibold" style="color: var(--color-text)">{{ __('Hello!') }}</h2>
<p style="color: var(--color-text-muted)">
{{ __('There is currently :online users online', ['online' => DB::table('users')->where('online', '1')->count()]) }}
</p>
</x-slot>
<form class="flex flex-col gap-y-3" action="{{ route('login') }}" method="POST">
@csrf
<div>
<x-form.label for="username">
{{ __('Username') }}
</x-form.label>
<x-form.input error-bag="login" name="username" value="{{ old('username') }}" placeholder="{{ __('Username') }}"
:autofocus="true" />
</div>
<div>
<x-form.label for="password">
{{ __('Password') }}
</x-form.label>
<x-form.input error-bag="login" name="password" placeholder="{{ __('Password') }}" type="password" />
</div>
@if (setting('google_recaptcha_enabled'))
<div class="g-recaptcha" data-sitekey="{{ config('habbo.site.recaptcha_site_key') }}"></div>
@endif
@if (setting('cloudflare_turnstile_enabled'))
<x-turnstile />
@endif
<x-form.primary-button>
{{ __('Login') }}
</x-form.primary-button>
<div class="text-center text-sm font-semibold" style="color: var(--color-text-muted)">
<a href="{{ route('forgot.password.get') }}" class="hover:underline" x-on:click="open = false" style="color: var(--link-color)">
{{ __('Did you forget your password?') }}
</a>
</div>
<div class="text-center text-sm font-semibold" style="color: var(--color-text-muted)">
<a href="{{ route('register') }}" class="hover:underline" x-on:click="open = false" style="color: var(--link-color)">
{{ __('Dont have an account? Join now!') }}
</a>
</div>
@if(setting('social_login_google_enabled') == '1' || setting('social_login_discord_enabled') == '1' || setting('social_login_github_enabled') == '1')
<div class="mt-4 pt-4 border-t" style="border-color: var(--border-color)">
<p class="text-center text-sm mb-3" style="color: var(--color-text-muted)">{{ __('Or login with') }}</p>
<div class="flex flex-col gap-2">
@if(setting('social_login_google_enabled') == '1')
<a href="{{ route('auth.google') }}" class="flex items-center justify-center gap-2 px-4 py-2 rounded-lg font-semibold" style="background: #4285F4; color: white">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
{{ __('Login with Google') }}
</a>
@endif
@if(setting('social_login_discord_enabled') == '1')
<a href="{{ route('auth.discord') }}" class="flex items-center justify-center gap-2 px-4 py-2 rounded-lg font-semibold" style="background: #5865F2; color: white">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.008.128c.354.183.684.404.97.645a.075.075 0 0 0 .04.106 18.558 18.558 0 0 0 6.956 1.041.077.077 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.008.128c.354.183.684.404.97.645a.076.076 0 0 0 .04.106 18.566 18.566 0 0 0 6.958 1.041.077.077 0 0 0 .084-.028c.463-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.008.128c.354.183.684.404.97.645a.076.076 0 0 0 .04.106z"/></svg>
{{ __('Login with Discord') }}
</a>
@endif
@if(setting('social_login_github_enabled') == '1')
<a href="{{ route('auth.github') }}" class="flex items-center justify-center gap-2 px-4 py-2 rounded-lg font-semibold" style="background: #333; color: white">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
{{ __('Login with GitHub') }}
</a>
@endif
</div>
</div>
@endif
</form>
@@ -0,0 +1,6 @@
@props(['classes' => ''])
<div
class="text-[var(--button-text-color)] rounded bg-[var(--button-color)] hover:opacity-90 border-[2px] border-[var(--button-color)] transition ease-in-out text-center rounded py-1 px-2 text-sm cursor-pointer {{ $classes }}">
{{ $slot }}
</div>
@@ -0,0 +1,90 @@
@props(['user'])
@php
$staffColor = $user->permission?->staff_color ?? '#eeb425';
$rankName = $user->permission?->rank_name ?? 'Staff';
@endphp
<style>
@keyframes sparkle {
0%, 100% { opacity: 0; transform: scale(0) rotate(0deg); }
50% { opacity: 1; transform: scale(1) rotate(180deg); }
}
@keyframes shimmer {
0% { background-position: -200% center; }
100% { background-position: 200% center; }
}
.sparkle-effect {
position: absolute;
width: 4px;
height: 4px;
background: white;
border-radius: 50%;
animation: sparkle 2s infinite;
}
.shimmer-text {
background: linear-gradient(90deg, {{ $staffColor }}, white, {{ $staffColor }});
background-size: 200% auto;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 3s linear infinite;
}
</style>
<div class="group relative bg-white dark:bg-gray-800 rounded-2xl overflow-hidden border-2 transition-all duration-300 hover:scale-[1.02] hover:shadow-2xl cursor-pointer"
style="border-color: {{ $staffColor }}40; box-shadow: 0 4px 15px {{ $staffColor }}10;">
<div class="absolute inset-0 bg-gradient-to-br from-white via-gray-50 to-gray-100 dark:from-gray-800 dark:via-gray-800 dark:to-gray-900 opacity-50"></div>
@php
$sparklePositions = [
['top' => '10%', 'left' => '15%', 'delay' => '0s'],
['top' => '20%', 'right' => '10%', 'delay' => '0.5s'],
['top' => '60%', 'left' => '5%', 'delay' => '1s'],
['top' => '70%', 'right' => '15%', 'delay' => '1.5s'],
];
@endphp
@foreach($sparklePositions as $pos)
<div class="sparkle-effect"
style="{{ $pos['top'] }}; {{ $pos['left'] ?? '' }}; {{ $pos['right'] ?? '' }}; animation-delay: {{ $pos['delay'] }}; box-shadow: 0 0 6px {{ $staffColor }};">
</div>
@endforeach
<div class="relative p-5 flex flex-col items-center text-center">
<div class="relative mb-3">
<div class="w-22 h-22 rounded-full overflow-hidden border-4 transition-all duration-300 group-hover:scale-110 group-hover:rotate-3"
style="border-color: {{ $staffColor }}; box-shadow: 0 0 15px {{ $staffColor }}50, inset 0 0 15px {{ $staffColor }}20;">
<img style="image-rendering: pixelated;"
src="{{ setting('avatar_imager') }}{{ $user->look }}&direction=2&head_direction=3&gesture=sml&action=wav&size=n"
alt="{{ $user->username }}"
class="w-full h-full object-cover">
</div>
@if($user->online)
<div class="absolute bottom-0 right-0 w-4 h-4 rounded-full border-2 border-white dark:border-gray-800 bg-green-500 animate-pulse"
style="box-shadow: 0 0 8px #22c55e;"></div>
@endif
</div>
<a href="{{ route('profile.show', $user->username) }}" class="block group/link">
<h3 class="text-lg font-extrabold truncate transition-all duration-300 group-hover/link:scale-105 shimmer-text">
{{ $user->username }}
</h3>
</a>
<div class="mt-1.5 px-3 py-0.5 rounded-full text-xs font-bold uppercase tracking-wide"
style="background-color: {{ $staffColor }}15; color: {{ $staffColor }}; border: 1px solid {{ $staffColor }}30;">
{{ $rankName }}
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-3 line-clamp-2 min-h-[2.5rem] px-2">
{{ $user->motto ?: __('No motto set') }}
</p>
<div class="mt-3 pt-3 border-t border-gray-100 dark:border-gray-700 w-full flex items-center justify-center gap-2 text-xs">
<span class="flex items-center gap-1.5 font-medium {{ $user->online ? 'text-green-500' : 'text-gray-400' }}">
<span class="w-2 h-2 rounded-full {{ $user->online ? 'bg-green-500 animate-pulse' : 'bg-gray-400' }}"></span>
{{ $user->online ? __('Online') : __('Offline') }}
</span>
</div>
</div>
</div>
@@ -0,0 +1,21 @@
@props(['icon' => '', 'classes' => ''])
<div class="w-full flex flex-col gap-y-4 rounded overflow-hidden pb-3 shadow-sm {{ $classes }}" style="background-color: var(--color-surface); color: var(--color-text);">
<div class="flex gap-x-2 border-b p-3" style="border-color: color-mix(in srgb, var(--color-primary) 30%, transparent)">
@if (empty($icon) === false)
<div class="max-w-[50px] max-h-[50px] min-w-[50px] min-h-[50px] rounded-full relative flex items-center justify-center {{ $icon }}"></div>
@endif
<div class="flex flex-col justify-center text-sm">
<p class="font-semibold" style="color: var(--color-text)">{{ $title }}</p>
@if(isset($underTitle))
<p style="color: var(--color-text-muted)">{{ $underTitle }}</p>
@endif
</div>
</div>
<section class="h-full flex flex-col px-3" style="color: var(--color-text)">
{{ $slot }}
</section>
</div>
@@ -0,0 +1,19 @@
@props(['icon', 'classes' => ''])
<div class="w-full flex flex-col gap-y-4 p-3 rounded-lg overflow-hidden {{ $classes }}">
<div class="flex gap-x-2">
<div
class="max-w-[50px] max-h-[50px] min-w-[50px] min-h-[50px] rounded-full {{ $icon }} relative flex items-center justify-center">
</div>
<div class="flex flex-col">
<p class="font-semibold text-black dark:text-gray-200">{{ $title }}</p>
@if(isset($underTitle))
<p class="dark:text-gray-500">{{ $underTitle }}</p>
@endif
</div>
</div>
{{ $slot }}
</div>
@@ -0,0 +1,22 @@
@props(['iconUrl' => '', 'color' => '', 'classes' => ''])
<div class="w-full flex flex-col gap-y-4 rounded overflow-hidden pb-3 shadow-sm {{ $classes }}" style="background-color: var(--color-surface)">
<div class="flex gap-x-2 border-b p-3" style="border-color: color-mix(in srgb, var(--color-primary) 30%, transparent)">
@if (!empty($iconUrl))
<div style="background-image: url({{ $iconUrl }}); background-color: {{ $color }}; background-repeat: no-repeat; background-position: center;" class="max-w-[50px] max-h-[50px] min-w-[50px] min-h-[50px] rounded-full relative flex items-center justify-center"></div>
@endif
<div class="flex flex-col justify-center text-sm">
<p class="font-semibold">{{ $title }}</p>
@if(isset($underTitle))
<p style="color: var(--color-text-muted)">{{ $underTitle }}</p>
@endif
</div>
</div>
<section class="h-full flex flex-col px-3">
{{ $slot }}
</section>
</div>
@@ -0,0 +1,22 @@
@props(['badge' => '', 'color' => '#327fa8'])
<div class="flex w-full flex-col gap-y-4 overflow-hidden rounded pb-3 shadow-sm" style="background-color: var(--color-surface)">
<div class="flex gap-x-2 border-b p-3" style="border-color: color-mix(in srgb, var(--color-primary) 30%, transparent)">
<div class="max-w-12.5 max-h-12.5 min-w-12.5 min-h-12.5 rounded-full relative flex items-center justify-center"
style="background-color: {{ $color }}">
<img src="{{ asset(sprintf('%s/%s.gif', setting('badges_path'), $badge)) }}" alt="">
</div>
<div class="flex flex-col justify-center text-sm">
<p class="font-semibold">{{ $title }}</p>
@if(isset($underTitle))
<p style="color: var(--color-text-muted)">{{ $underTitle }}</p>
@endif
</div>
</div>
<section class="px-3">
{{ $slot }}
</section>
</div>
@@ -0,0 +1,21 @@
<div class="h-[210px] dark:bg-gray-900 rounded w-full bg-white shadow-sm relative overflow-hidden transition ease-in-out duration-200">
<div style="background: url('https://i.imgur.com/uGLDOUu.png');" class="article-image">
</div>
<div class="mt-4 px-4">
<p class="font-semibold text-lg truncate dark:text-gray-200">
{{ __('No published articles') }}
</p>
<div class="flex items-center gap-x-2">
<div
class="mt-3 flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-gray-100 dark:bg-gray-800">
<img src="{{ setting('avatar_imager') }}&headonly=1" alt="">
</div>
<p class="mt-4 font-semibold dark:text-gray-400">
{{ setting('hotel_name') }}
</p>
</div>
</div>
</div>
+54
View File
@@ -0,0 +1,54 @@
<footer
class="mt-auto flex h-14 w-full flex-col items-center justify-center text-sm md:flex-row md:justify-center md:px-8"
style="background-color: var(--color-surface); color: var(--color-text-muted)"
onclick="showFooter()">
<div class="md:font-semibold text-[12px] md:text-[14px] cursor-pointer hover:underline">
&copy {{ date('Y') }} -
{{ __(':hotel is a not for profit educational project', ['hotel' => setting('hotel_name')]) }}
</div>
</footer>
<style>
.swal2-html-container {
max-height: 300px;
overflow-y: scroll;
}
</style>
<script>
function showFooter() {
const creator =
'<a class="text-blue-400 underline" href="https://devbest.com/threads/atom-cms-a-multi-theme-cms.93034/" target="_blank">Object</a>';
const credits = [
'<strong>Kasja</strong> Helping with design, ideas & GFX <br/>',
'<strong>Nicollas </strong> Dark mode, Turbolinks, Performance improvements, Article reactions, User sessions, Layout improvements & PT-BR translations <br/>',
'<strong>Dominic</strong> Performance improvements & User sessions <br/>',
'<strong>EntenKoeniq#0001</strong> Automatic language registration, rooms page, profile page tweaks & shop additions<br/>',
'<strong>MisterDeen</strong> Custom Discord widget, bugfixes & tweaks <br/>',
'<strong>Kani</strong> RCON base & Findretros API <br/>',
'<strong>Beny</strong> Findretros API Fixes & CF Fixes <br/>',
'<strong>Oliver</strong> Profile page additions & Finnish translations <br/>',
'<strong>Live</strong> French translations, bugfixes & tweaks <br/>',
'<strong>DamienJolly</strong> Bugfixes <br/>',
'<strong>Danbo</strong> Bugfixes <br/>',
'<strong>Diddy/Josh</strong> Code readability improvements <br/>',
'<strong>Damue & EntenKoeniq#0001</strong> German translations <br/>',
'<strong>Talion</strong> Turkish translations <br/>',
'<strong>CentralCee, Rille & Tuborgs</strong> Swedish translations <br/>',
'<strong>Yannick</strong> Netherlands translations <br/>',
'<strong>Gedomi</strong> Spanish translations <br/>',
'<strong>Lorenzune</strong> Italian translations <br/>',
'<strong>Twana & Zaruzet</strong> Norwegian translations <br/>',
'<strong>Plow</strong> French translations <br/>'
];
const content =
'{{ __('Thank you for playing :hotel. We have put a lot of effort into making the hotel what it is, and we truly appreciate you being here', ['hotel' => setting('hotel_name')]) }}' + '❤️';
const drivenBy = '{{ __(':hotel is driven by Atom CMS made by:', ['hotel' => setting('hotel_name')]) }}';
Swal.fire(
'<span class="text-[26px]">{{ setting('hotel_name') }}</span>',
`<span class="text-sm">${content}<br/><br/>${drivenBy} ${creator}<br/><br/><span class="flex flex-col space-y-2">{{ __('Credits:') }}<br/>${credits.join('')}</span></span>`,
'question'
)
}
</script>
@@ -0,0 +1,7 @@
@props(['classes' => '', 'type' => 'submit'])
<button type="{{ $type }}"
class="w-full rounded bg-[var(--button-danger-color)]! hover:bg-[var(--button-danger-hover-color)]! text-[var(--button-danger-text-color)]! border-2 border-[var(--button-danger-border-color)]! transition! ease-in-out! duration-150! font-semibold px-6! py-2! {{ $classes }}"
style="border-radius: var(--border-radius)">
{{ $slot }}
</button>
@@ -0,0 +1,17 @@
@props(['errorBag' => '', 'classes' => '', 'name', 'type' => 'text', 'value' => '', 'placeholder' => '', 'required' => true, 'autofocus' => false, 'readonly' => false])
<input
class="{{ $classes }} focus:ring-0 border-2 rounded w-full -sm @error($name, $errorBag) border-red-600 ring-red-500 @enderror"
style="background-color: var(--color-background); color: var(--color-text); border-color: var(--input-border-color, var(--color-text-muted)); --tw-ring-color: var(--color-primary); --tw-border-opacity: 1; border-color: var(--color-primary);"
id="{{ $name }}" type="{{ $type }}" name="{{ $name }}" value="{{ $value }}"
autocomplete="{{ $name }}" placeholder="{{ $placeholder }}" @if ($readonly) required @endif
@if ($autofocus) autofocus="{{ $name }}" @endif
@if ($readonly) readonly @endif
onfocus="this.style.borderColor='var(--color-primary)'"
onblur="this.style.borderColor='var(--input-border-color, var(--color-text-muted))'">
@error($name, $errorBag)
<p class="mt-1 text-xs italic" style="color: #ef4444;">
{{ $message }}
</p>
@enderror
@@ -0,0 +1,11 @@
@props(['for', 'info' => ''])
<div class="mb-2">
<label class="block font-semibold text-gray-700 dark:text-gray-200" for="{{ $for }}">
{{ $slot }}
</label>
<p class="text-gray-500 dark:text-gray-400 text-[14px]">
{{ $info }}
</p>
</div>
@@ -0,0 +1,7 @@
@props(['classes' => '', 'type' => 'submit'])
<button type="{{ $type }}"
class="w-full rounded bg-transparent! hover:bg-[var(--button-outline-hover-color)]! text-[var(--button-outline-text-color)]! border-2 border-[var(--button-outline-color)]! transition! ease-in-out! duration-150! font-semibold px-6! py-2! {{ $classes }}"
style="border-radius: var(--border-radius)">
{{ $slot }}
</button>
@@ -0,0 +1,7 @@
@props(['classes' => '', 'type' => 'submit'])
<button type="{{ $type }}"
class="w-full rounded bg-[var(--button-color)]! text-[var(--button-text-color)]! border-2 border-[var(--button-color)]! transition! ease-in-out! duration-200! hover:opacity-90! font-semibold px-6! py-2! {{ $classes }}"
style="border-radius: var(--border-radius)">
{{ $slot }}
</button>
@@ -0,0 +1,7 @@
@props(['classes' => '', 'type' => 'submit'])
<button type="{{ $type }}"
class="w-full rounded bg-[var(--button-secondary-color)]! hover:bg-[var(--button-secondary-hover-color)]! text-[var(--button-secondary-text-color)]! border-2 border-[var(--button-secondary-border-color)]! transition! ease-in-out! duration-150! font-semibold px-6! py-2! {{ $classes }}"
style="border-radius: var(--border-radius)">
{{ $slot }}
</button>
@@ -0,0 +1,20 @@
@props(['name', 'content' => null])
<div class="mt-3">
<textarea name="content" id="editor">
{{ $content ?? ''}}
</textarea>
</div>
<script src="https://cdn.tiny.cloud/1/{{ setting('tinymce_api_key') }}/tinymce/7/tinymce.min.js" referrerpolicy="origin"></script>
<script>
tinymce.init({
selector: 'textarea#editor',
plugins: 'lists image',
toolbar: 'undo redo | blocks| bold italic | bullist numlist checklist | code | table'
});
</script>
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"
stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />
</svg>

After

Width:  |  Height:  |  Size: 212 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="h-5 w-5">
<path stroke-linecap="round" stroke-linejoin="round"
d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
</svg>

After

Width:  |  Height:  |  Size: 350 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="h-5 w-5">
<path
d="M11.47 3.84a.75.75 0 011.06 0l8.69 8.69a.75.75 0 101.06-1.06l-8.689-8.69a2.25 2.25 0 00-3.182 0l-8.69 8.69a.75.75 0 001.061 1.06l8.69-8.69z" />
<path
d="M12 5.432l8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875 1.875H15a.75.75 0 01-.75-.75v-4.5a.75.75 0 00-.75-.75h-3a.75.75 0 00-.75.75V21a.75.75 0 01-.75.75H5.625a1.875 1.875 0 01-1.875-1.875v-6.198a2.29 2.29 0 00.091-.086L12 5.43z" />
</svg>

After

Width:  |  Height:  |  Size: 521 B

@@ -0,0 +1,4 @@
<svg {{ $attributes }} fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
</svg>

After

Width:  |  Height:  |  Size: 290 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="h-5 w-5">
<path stroke-linecap="round" stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
</svg>

After

Width:  |  Height:  |  Size: 364 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="h-4 w-4">
<path fill-rule="evenodd"
d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z"
clip-rule="evenodd" />
</svg>

After

Width:  |  Height:  |  Size: 337 B

@@ -0,0 +1,86 @@
@props([
'title',
'icon',
'data',
'valueKey',
'valueType',
'relationship' => null,
'formatValue' => null,
])
<div class="rounded-xl bg-[var(--color-surface)] p-3 md:p-4 shadow-xl border border-[var(--color-text-muted)]">
<div class="flex justify-center gap-x-3 text-center font-bold text-[var(--color-text)]">
<div class="flex items-center">
<img src="{{ asset('/assets/images/icons/' . $icon) }}" alt="{{ $title }}" class="w-5 h-5 md:w-6 md:h-6" style="image-rendering: pixelated;">
</div>
<span class="text-base md:text-lg">{{ __($title) }}</span>
</div>
<div class="mt-3 md:mt-4 flex flex-col gap-y-2 md:gap-y-3">
@forelse ($data as $index => $entry)
<div class="group relative p-3 md:p-4 rounded-xl bg-[var(--color-background)] flex gap-x-2 md:gap-x-3 items-center h-[70px] md:h-[80px] overflow-hidden border border-[var(--color-text-muted)] hover:border-[var(--color-primary)] hover:bg-[var(--color-primary)] transition-all duration-300">
<!-- Rank Badge -->
<div class="relative flex-shrink-0">
<div @class([
'w-10 h-10 md:w-12 md:h-12 rounded-full flex items-center justify-center font-bold text-base md:text-lg shadow-lg',
'bg-[var(--color-primary)] text-[var(--button-text-color)] ring-2 ring-[var(--color-primary)] ring-offset-2 ring-offset-[var(--color-surface)]' => $index + 1 == 1,
'bg-[var(--color-text-muted)] text-[var(--button-text-color)] ring-2 ring-[var(--color-text-muted)] ring-offset-2 ring-offset-[var(--color-surface)]' => $index + 1 == 2,
'bg-[var(--color-accent)] text-white ring-2 ring-[var(--color-accent)] ring-offset-2 ring-offset-[var(--color-surface)]' => $index + 1 == 3,
'bg-[var(--color-background)] text-[var(--color-text-muted)]' => $index + 1 > 3,
])>
{{ $index + 1 }}
</div>
@if($index + 1 <= 3)
<div class="absolute -top-1 -right-1 text-lg md:text-xl">
{{ $index + 1 == 1 ? '🥇' : ($index + 1 == 2 ? '🥈' : '🥉') }}
</div>
@endif
</div>
<!-- Avatar -->
<div class="relative flex-shrink-0">
<img @class([
'w-12 h-12 md:w-16 md:h-16 rounded-lg border-2 transition-all duration-300 group-hover:scale-110',
'border-[var(--color-primary)] shadow-lg shadow-[var(--color-primary)]/30' => $index + 1 == 1,
'border-[var(--color-text-muted)]' => $index + 1 == 2,
'border-[var(--color-accent)]' => $index + 1 == 3,
'border-[var(--color-text-muted)] group-hover:border-[var(--color-primary)]' => $index + 1 > 3,
])
src="{{ setting('avatar_imager') }}{{ $relationship ? $entry->{$relationship}?->look : $entry->look }}&size=b&head_direction=2&gesture=sml&headonly=1"
alt="" />
@if($index + 1 == 1)
<div class="absolute -bottom-1 -right-1 w-4 h-4 md:w-5 md:h-5 bg-[var(--color-primary)] rounded-full flex items-center justify-center text-xs shadow-lg">
👑
</div>
@endif
</div>
<!-- User Info -->
<div class="flex-1 min-w-0">
<p class="font-bold text-[var(--color-text)] truncate group-hover:text-[var(--color-primary)] transition-colors duration-300 text-sm md:text-base">
{{ $relationship ? $entry->{$relationship}?->username : $entry->username }}
</p>
<p @class([
'text-xs md:text-sm font-medium',
'text-[var(--color-primary)]' => $index + 1 == 1,
'text-[var(--color-text-muted)]' => $index + 1 > 1,
])>
{{ $formatValue ? $formatValue($entry->{$valueKey}) : $entry->{$valueKey} }} {{ $valueType }}
</p>
</div>
<!-- Trophy -->
@if($index + 1 == 1)
<div class="text-2xl md:text-3xl animate-pulse">🏆</div>
@elseif($index + 1 <= 3)
<div class="text-xl md:text-2xl"></div>
@endif
</div>
@empty
<div class="p-6 md:p-8 text-center text-[var(--color-text-muted)]">
<div class="text-3xl md:text-4xl mb-2">📊</div>
<p>No data available</p>
</div>
@endforelse
</div>
</div>
@@ -0,0 +1,55 @@
<script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
var Toast = Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 4000,
timerProgressBar: true,
didOpen: (toast) => {
toast.addEventListener('mouseenter', Swal.stopTimer)
toast.addEventListener('mouseleave', Swal.resumeTimer)
}
})
</script>
@if (session()->has('message'))
<script>
Toast.fire({
icon: 'error',
title: '{{ session()->get('message') }}'
})
</script>
@endif
@if ($errors->any())
@foreach ($errors->all() as $error)
<script>
Toast.fire({
icon: 'error',
title: '{{ $error }}'
})
</script>
@endforeach
@endif
@if ($errors->login)
@foreach ($errors->login->all() as $error)
<script>
Toast.fire({
icon: 'error',
title: '{{ $error }}'
})
</script>
@endforeach
@endif
@if (session()->has('success'))
<script>
Toast.fire({
icon: 'success',
title: '{{ session()->get('success') }}'
})
</script>
@endif
@@ -0,0 +1,5 @@
@props(['classes' => ''])
<div x-data="{ open: false }" class="relative {{ $classes }}">
{{ $slot }}
</div>
@@ -0,0 +1,34 @@
<div x-show="open" style="display: none" x-on:keydown.escape.prevent.stop="open = false" role="dialog" aria-modal="true"
x-id="['modal-title']" :aria-labelledby="$id('modal-title')" class="fixed inset-0 z-50 overflow-y-auto">
{{-- Overlay --}}
<div x-show="open" x-transition x-on:click="open = false"
class="relative flex min-h-screen items-center justify-center overflow-hidden p-4">
<div x-show="open" x-transition.opacity class="fixed inset-0 bg-black/50"></div>
<div x-on:click.stop x-trap.noscroll.inert="open"
class="relative w-full max-w-xl rounded px-6 py-6 shadow-md lg:max-w-2xl lg:px-8"
style="background-color: var(--color-surface); color: var(--color-text);">
<button type="button" x-on:click="open = false"
class="absolute top-3 right-2.5 rounded-lg p-1.5 ml-auto inline-flex items-center transition-colors"
style="background-color: transparent; color: var(--color-text-muted);"
onmouseover="this.style.backgroundColor='var(--color-text-muted)'; this.style.color='var(--color-background)'"
onmouseout="this.style.backgroundColor='transparent'; this.style.color='var(--color-text-muted)'">
<svg aria-hidden="true" class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"></path>
</svg>
<span class="sr-only">{{ __('Close modal') }}</span>
</button>
{{-- Title --}}
<div class="mb-2 flex flex-col items-center" :id="$id('modal-title')">
{{ $title }}
</div>
{{-- Content --}}
{{ $slot }}
</div>
</div>
</div>
@@ -0,0 +1,5 @@
@props(['route' => '', 'classes' => '', 'turbolink' => true, 'target' => '_self'])
<a @if(!$turbolink) data-turbolinks="false" @endif href="{{ $route }}" target="{{ $target }}" class="dropdown-item {{ $classes }}">
{{ $slot }}
</a>
@@ -0,0 +1,79 @@
@props(['icon', 'routeGroup' => '', 'classes' => '', 'childClasses' => 'min-w-[150px]', 'uppercase' => false])
<div
x-data="{
open: false,
isTouch: false,
init() {
this.isTouch = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)
},
toggle() {
if (this.open) {
return this.close()
}
this.$refs.button.focus()
this.open = true
},
close(focusAfter) {
if (! this.open) return
this.open = false
focusAfter && focusAfter.focus()
}
}"
x-on:keydown.escape.prevent.stop="close($refs.button)"
x-on:focusin.window="! $refs.panel.contains($event.target) && close()"
x-on:dropdown-close.window="close()"
x-on:close-menu.window="close()"
x-id="['dropdown-button']"
@class([
'relative h-[60px] px-3 text-sm font-semibold uppercase tracking-wide transition-all duration-200 ease-in-out border-b-4 border-transparent hover:border-b-[var(--color-primary)] z-50',
$classes,
'border-b-[var(--color-primary)]' => request()->is($routeGroup),
])"
>
<!-- Button -->
<button
x-ref="button"
x-on:click="toggle()"
:aria-expanded="open"
:aria-controls="$id('dropdown-button')"
type="button"
class="flex items-center gap-2 h-full text-sm font-medium"
>
@if(isset($icon))
<i class="hidden navigation-icon {{ $icon }} lg:inline-flex"></i>
@endif
{{ $slot }}
<!-- Heroicon: chevron-down -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 transition-transform duration-200" :class="open ? 'rotate-180' : ''" style="color: var(--color-text-muted)" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"/>
</svg>
</button>
<!-- Panel -->
<div
x-ref="panel"
x-show="open"
x-cloak
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 transform scale-100"
x-transition:leave-end="opacity-0 transform scale-95"
x-on:click.outside="close($refs.button)"
:id="$id('dropdown-button')"
style="display: none; background-color: var(--color-dropdown); border-radius: var(--dropdown-radius); border: var(--dropdown-border); box-shadow: var(--dropdown-shadow); color: var(--color-text);"
@class(['absolute left-0 mt-0 top-full overflow-hidden shadow-lg whitespace-nowrap z-50 w-full md:w-auto', $childClasses])
>
{{ $children }}
</div>
</div>
@@ -0,0 +1,11 @@
<x-navigation.dropdown classes="!border-none" childClasses="min-w-[50px] -ml-4">
{{ $slot }}
<x-slot:children>
@foreach (languages() as $lang)
<x-navigation.dropdown-child :route="route('language.select', $lang->country_code)" :turbolink="false">
<img src="/assets/images/icons/flags/{{ $lang->country_code }}.png" alt="{{ $lang->country_code }}">
</x-navigation.dropdown-child>
@endforeach
</x-slot:children>
</x-navigation.dropdown>
@@ -0,0 +1,12 @@
<button type="button"
onclick="const m=document.getElementById('mobile-menu');m.classList.toggle('hidden');this.setAttribute('aria-expanded',m.classList.contains('hidden')?'false':'true')"
class="absolute right-4 top-[50%] -translate-y-[50%] z-50 p-2 text-gray-700 dark:text-white md:hidden"
aria-controls="mobile-menu" aria-expanded="false">
<span class="sr-only">{{ __('Open main menu') }}</span>
<svg class="h-6 w-6" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clip-rule="evenodd"></path>
</svg>
</button>
@@ -0,0 +1,137 @@
<div class="relative hidden h-full w-full flex-col items-center gap-y-2 py-3 md:flex md:flex-row md:gap-x-8 md:gap-y-0 md:py-0" id="mobile-menu" style="color: var(--color-navbar-text)">
@auth
<x-navigation.dropdown icon="home" route-group="user*">
{{ auth()->user()->username }}
<x-slot:children>
<x-navigation.dropdown-child :route="route('me.show')">
{{ __('Home') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('draw-badge')">
{{ __('Badge Drawer') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('profile.show', auth()->user()->username)">
{{ __('My Profile') }}
</x-navigation.dropdown-child>
</x-slot:children>
</x-navigation.dropdown>
@else
<a href="{{ route('welcome') }}"
class="nav-item {{ request()->routeIs('welcome') ? 'border-b-2 border-[var(--color-primary)]' : '' }}">
<i class="mr-1 hidden navigation-icon home lg:inline-flex"></i>
{{ __('Home') }}
</a>
@endauth
@auth
<x-navigation.dropdown icon="community" route-group="community*" :uppercase="true">
{{ __('Community') }}
<x-slot:children>
<x-navigation.dropdown-child :route="route('article.index')">
{{ __('Articles') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('staff.index')">
{{ __('Staff') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('teams.index')">
{{ __('Teams') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('team-applications.index')">
{{ __('Team applications') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('staff-applications.index')">
{{ __('Staff applications') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('photos.index')">
{{ __('Photos') }}
</x-navigation.dropdown-child>
</x-slot:children>
</x-navigation.dropdown>
<a href="{{ route('leaderboard.index') }}"
class="nav-item {{ request()->routeIs('leaderboard.*') ? 'border-b-2 border-[var(--color-primary)]' : '' }}">
<i class="navigation-icon leaderboards mr-1 hidden lg:inline-flex"></i>
{{ __('Leaderboards') }}
</a>
<a href="{{ route('values.index') }}"
class="nav-item {{ request()->routeIs('values.*') ? 'border-b-2 border-[var(--color-primary)]' : '' }}">
<i class="navigation-icon leaderboards mr-1 hidden lg:inline-flex"></i>
{{ __('Rare values') }}
</a>
<a data-turbolinks="false" href="{{ route('shop.index') }}"
class="nav-item {{ request()->routeIs('shop.*') ? 'border-b-2 border-[var(--color-primary)]' : '' }}">
<i class="navigation-icon mr-1 hidden lg:inline-flex shop"></i>
{{ __('Shop') }}
</a>
@endauth
<x-navigation.dropdown icon="rules" route-group="help-center*" :uppercase="true">
{{ __('Assistance') }}
<x-slot:children>
@auth
<x-navigation.dropdown-child :route="route('help-center.index')">
{{ __('Help center') }}
</x-navigation.dropdown-child>
@if(hasPermission('manage_website_tickets'))
<x-navigation.dropdown-child :route="route('help-center.ticket.index')">
{{ __('Open tickets') }}
</x-navigation.dropdown-child>
@endif
@else
<x-navigation.dropdown-child :route="route('help-center.rules.index')">
{{ __('Rules') }}
</x-navigation.dropdown-child>
@endauth
</x-slot:children>
</x-navigation.dropdown>
<a href="{{ is_array(setting('discord_invitation_link')) ? '' : setting('discord_invitation_link') }}" target="_blank" class="nav-item">
{{ __('Discord') }}
</a>
<x-navigation.dropdown icon="music" route-group="radio*">
{{ __('Radio') }}
<x-slot:children>
<x-navigation.dropdown-child :route="route('radio.index')">
{{ __('Radio Home') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('radio.rooster')">
{{ __('DJ Rooster') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('radio.shouts')">
{{ __('Live Shouts') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('radio.apply')">
{{ __('Word DJ') }}
</x-navigation.dropdown-child>
<x-navigation.dropdown-child :route="route('radio.leaderboard')">
{{ __('Radio punten') }}
</x-navigation.dropdown-child>
</x-slot:children>
</x-navigation.dropdown>
<div class="w-full flex md:hidden gap-x-1 justify-center">
<x-navigation.language-selector>
<img src="/assets/images/icons/flags/{{ session()->has('locale') ? session()->get('locale') : config('habbo.site.default_language') }}.png"
alt="">
</x-navigation.language-selector>
</div>
</div>
@@ -0,0 +1,5 @@
<button id="theme-switcher"
type="button"
class="mr-3 cursor-pointer items-center justify-center rounded-lg bg-gray-200 p-1 shadow-inner dark:bg-gray-800 hidden md:flex">
<x-icons.moon class="h-5 w-5 text-gray-600 dark:text-white" />
</button>
@@ -0,0 +1,38 @@
<button id="dropdownNavbarLink" data-dropdown-toggle="dropdownNavbarUser"
class="ml-4 flex items-center">
<div class="h-10">
<img class="w-10"
src="{{ setting('avatar_imager') }}{{ auth()->user()->look }}&direction=2&headonly=1&head_direction=2&gesture=sml"
alt="">
</div>
<span>{{ auth()->user()->username }}</span>
<svg xmlns="http://www.w3.org/2000/svg" class="ml-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"
stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />
</svg>
</button>
<!-- Dropdown menu -->
<div id="dropdownNavbarUser" class="z-10 hidden w-44 py-2 overflow-hidden"
style="background-color: var(--color-dropdown); border-radius: var(--dropdown-radius); border: var(--dropdown-border); box-shadow: var(--dropdown-shadow); color: var(--color-text);">
<a href="{{ route('settings.account.show') }}"
class="dropdown-item ">
{{ __('User settings') }}
</a>
@auth
<form action="{{ route('logout') }}" method="POST" class="block">
@csrf
<button type="submit"
class="dropdown-item flex items-center gap-2 w-full text-left hover:!bg-red-500/20"
style="color: var(--button-danger-color)">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
{{ __('Logout') }}
</button>
</form>
@endauth
</div>
+34
View File
@@ -0,0 +1,34 @@
@props(['photos'])
<div class="grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-4 xl:grid-cols-5">
@forelse ($photos as $photo)
<a href="{{ $photo->url }}" data-fancybox="gallery" class="group cursor-pointer block">
<div class="relative rounded-lg overflow-hidden shadow-md border border-gray-600 hover:border-[#eeb425] transition-all duration-300">
<div class="aspect-[4/3] relative overflow-hidden">
<img class="h-full w-full object-cover object-center transition-transform duration-300 group-hover:scale-110"
src="{{ $photo->url }}"
alt="Photo by {{ $photo->user?->username ?? 'Unknown' }}">
<div class="absolute inset-0 bg-gradient-to-t from-black/70 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
</div>
<div class="absolute bottom-0 left-0 right-0 p-2 bg-gradient-to-t from-black/90 to-transparent">
<div class="flex items-center gap-2">
<div class="flex h-7 w-7 items-center justify-center overflow-hidden rounded-full bg-gray-700 border border-gray-500">
<img src="{{ setting('avatar_imager') }}{{ $photo->user?->look ?? '' }}&direction=2&headonly=1&head_direction=2&gesture=sml"
alt="{{ $photo->user?->username ?? 'Unknown' }}"
class="h-full w-full object-cover">
</div>
<div class="flex-1 min-w-0">
<p class="text-white text-sm font-semibold truncate ">{{ $photo->user?->username ?? __('Unknown') }}</p>
</div>
</div>
</div>
</div>
</a>
@empty
<div class="col-span-full text-center py-8">
<p class="text-gray-500 dark:text-gray-400">No photos available</p>
</div>
@endforelse
</div>
@@ -0,0 +1,235 @@
@props(['classes' => ''])
@php
$style = cache()->remember('radio_style_value', 300, fn() => \App\Models\Miscellaneous\WebsiteSetting::where('key', 'radio_style')->value('value')) ?? 'dark';
$bgColor = match($style) {
'light' => 'bg-white/90',
'blue' => 'bg-blue-900/90',
default => 'bg-black/90',
};
$textColor = match($style) {
'light' => 'text-gray-900',
default => 'text-white',
};
$accentColor = match($style) {
'light' => 'text-blue-600',
'blue' => 'text-yellow-400',
default => 'text-[var(--color-primary)]',
};
@endphp
<div class="fixed bottom-4 right-4 z-50 flex flex-col gap-2 {{ $classes }}">
<div id="radio-player" class="{{ $bgColor }} backdrop-blur-sm rounded-lg p-3 {{ $textColor }} shadow-lg w-72 sm:w-96">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div id="radio-status" class="w-3 h-3 rounded-full bg-green-500 animate-pulse"></div>
<div class="flex flex-col">
<span class="text-xs font-semibold {{ $accentColor }}">Radio</span>
<span id="current-dj" class="text-xs font-medium">Laden...</span>
</div>
</div>
<div class="flex items-center gap-2">
<button id="radio-toggle" onclick="toggleRadio()" class="{{ $accentColor }} hover:opacity-80 transition">
<span id="play-icon"></span>
<span id="pause-icon" class="hidden"></span>
</button>
<input type="range" id="volume-control" min="0" max="100" value="50" class="w-16 h-1 rounded cursor-pointer accent-{{ $accentColor }}" onchange="changeVolume(this.value)">
</div>
</div>
<div id="now-playing" class="mt-2 text-xs text-center py-1 px-2 bg-black/20 rounded">
<span id="song-title">Laden...</span>
</div>
<div class="mt-2 flex items-center justify-between text-xs">
<span id="listeners" class="flex items-center gap-1">
<span>👥</span>
<span id="listeners-count">--</span>
</span>
<button onclick="toggleShouts()" class="{{ $accentColor }} hover:underline">
🔊 Shouts
</button>
</div>
</div>
<div id="shouts-panel" class="hidden {{ $bgColor }} backdrop-blur-sm rounded-lg p-3 {{ $textColor }} shadow-lg w-80 sm:w-full max-h-64 overflow-y-auto">
<div class="flex justify-between items-center mb-2">
<span class="text-xs font-semibold {{ $accentColor }}">Live Shouts</span>
<button onclick="toggleShouts()" class="text-gray-400 hover:text-white text-xs">×</button>
</div>
<div id="shouts-list" class="space-y-2">
<p class="text-xs text-gray-400">Shouts laden...</p>
</div>
</div>
<input type="hidden" id="radio-stream-url" value="">
<audio id="radio-audio" preload="none"></audio>
</div>
<script>
let isPlaying = false;
let shoutsPanelOpen = false;
function toggleRadio() {
const audio = document.getElementById('radio-audio');
const playIcon = document.getElementById('play-icon');
const pauseIcon = document.getElementById('pause-icon');
const radioStatus = document.getElementById('radio-status');
if (isPlaying) {
audio.pause();
playIcon.classList.remove('hidden');
pauseIcon.classList.add('hidden');
radioStatus.classList.remove('bg-green-500', 'animate-pulse');
radioStatus.classList.add('bg-gray-500');
isPlaying = false;
} else {
fetch('/api/radio/config')
.then(r => r.json())
.then(config => {
if (config.stream_url) {
audio.src = config.stream_url;
audio.play().then(() => {
playIcon.classList.add('hidden');
pauseIcon.classList.remove('hidden');
radioStatus.classList.add('bg-green-500', 'animate-pulse');
radioStatus.classList.remove('bg-gray-500');
isPlaying = true;
}).catch(e => {
console.error('Audio play failed:', e);
alert('Kan radio niet afspelen. Controleer je browser instellingen en probeer het opnieuw.');
});
} else {
alert('Geen stream URL beschikbaar. Radio is mogelijk offline.');
}
})
.catch(e => {
console.error('Failed to fetch radio config:', e);
alert('Kan radio configuratie niet laden. Probeer het later opnieuw.');
});
}
}
function changeVolume(value) {
const audio = document.getElementById('radio-audio');
audio.volume = value / 100;
}
function toggleShouts() {
const panel = document.getElementById('shouts-panel');
shoutsPanelOpen = !shoutsPanelOpen;
panel.classList.toggle('hidden', !shoutsPanelOpen);
if (shoutsPanelOpen) {
loadShouts();
}
}
function loadShouts() {
fetch('/api/radio/shouts')
.then(response => {
if (!response.ok) {
throw new Error('Shouts niet beschikbaar');
}
return response.json();
})
.then(data => {
const shoutsList = document.getElementById('shouts-list');
if (data.error) {
shoutsList.innerHTML = `<p class="text-xs text-red-400">${data.error}</p>`;
} else if (data.shouts && data.shouts.length > 0) {
shoutsList.innerHTML = data.shouts.map(shout => `
<div class="text-xs border-b border-gray-700 pb-1 mb-1">
<span class="font-semibold ${shout.username === 'Anoniem' ? 'text-gray-400' : '{{ $accentColor }}'}">${shout.username}:</span>
<span class="text-gray-300 ml-1">${shout.message}</span>
<span class="text-gray-500 text-xs block">${shout.created_at}</span>
</div>
`).join('');
} else {
shoutsList.innerHTML = '<p class="text-xs text-gray-400">Nog geen shouts... Wees de eerste!</p>';
}
})
.catch(error => {
console.error('Shouts load error:', error);
document.getElementById('shouts-list').innerHTML = '<p class="text-xs text-red-400">Fout bij laden van shouts...</p>';
});
}
function getCurrentDJ() {
fetch('/api/radio/current-dj')
.then(response => response.json())
.then(data => {
const djElement = document.getElementById('current-dj');
if (data.error) {
djElement.textContent = 'Error: ' + data.error;
} else if (data.dj) {
const djName = (typeof data.dj === 'object' && data.dj !== null)
? (data.dj.username || data.dj.show_name || 'DJ')
: data.dj;
djElement.textContent = djName;
} else {
djElement.textContent = 'Geen DJ actief';
}
})
.catch(error => {
console.error('DJ info error:', error);
document.getElementById('current-dj').textContent = '--';
});
}
function getNowPlaying() {
fetch('/api/radio/now-playing')
.then(response => response.json())
.then(data => {
const songElement = document.getElementById('song-title');
if (data.error) {
songElement.textContent = 'Error: ' + data.error;
} else if (data.song) {
songElement.textContent = data.song + (data.artist ? ' - ' + data.artist : '');
} else if (data.artist && data.title) {
songElement.textContent = data.artist + ' - ' + data.title;
} else if (typeof data.now_playing === 'object' && data.now_playing !== null) {
const np = data.now_playing;
if (np.song) {
songElement.textContent = np.song + (np.artist ? ' - ' + np.artist : '');
} else if (np.artist && np.title) {
songElement.textContent = np.artist + ' - ' + np.title;
} else {
songElement.textContent = 'Live radio';
}
} else {
songElement.textContent = 'Live radio';
}
})
.catch(error => {
console.error('Now playing error:', error);
document.getElementById('song-title').textContent = 'Niet beschikbaar';
});
}
function getListeners() {
fetch('/api/radio/listeners')
.then(response => response.json())
.then(data => {
const countElement = document.getElementById('listeners-count');
if (data.error) {
countElement.textContent = 'Error';
} else {
countElement.textContent = data.count ? data.count.toLocaleString() : '0';
}
})
.catch(error => {
console.error('Listeners error:', error);
document.getElementById('listeners-count').textContent = '--';
});
}
setInterval(getListeners, 30000);
setInterval(getNowPlaying, 30000);
setInterval(getCurrentDJ, 30000);
if (shoutsPanelOpen) {
setInterval(loadShouts, 15000);
}
getCurrentDJ();
getNowPlaying();
getListeners();
</script>
@@ -0,0 +1,43 @@
@props(['rare'])
<div class="p-3 rounded bg-gray-200 dark:bg-gray-700 flex gap-x-6 gap-4 items-center overflow-hidden">
<div class="w-8 h-8">
<div class="w-10 h-10 overflow-hidden rounded-full flex items-center justify-center bg-gray-300 dark:bg-gray-800">
<img src="{{ sprintf('%s/%s', setting('furniture_icons_path'), $rare->furniture_icon) }}" alt="">
</div>
</div>
<div class="flex flex-col w-full">
<div class="font-bold text-gray-700 dark:text-gray-200 truncate flex items-center gap-x-[5px]">
@if($rare->item_id)
<a href="{{ route('values.value', $rare) }}" class="underline">
{{ strLimit($rare->name, 15) }}
</a>
@else
{{ strLimit($rare->name, 20) }}
@endif
@if($rare->isLimitedEdition())
<img class="w-4 h-4" src="{{ asset('/assets/images/icons/ltd.png') }}" alt="">
@endif
</div>
<div class="w-full rounded h-[35px] flex items-center mt-2" style="background-color: var(--color-primary); opacity: 0.8">
<div class="rounded-l w-1/3 px-4 h-full flex items-center justify-center" style="background-color: var(--color-primary)">
<img src="{{ asset('assets/images/icons/currency/credits.png') }}" alt="">
</div>
<p class="w-full text-center truncate">
{{ $rare->credit_value ?? 0 }} {{ __('credits') }}
</p>
</div>
<div class="w-full bg-gray-500 rounded h-[35px] flex items-center mt-1">
<div class="bg-gray-600 rounded-l w-1/3 px-4 h-full flex items-center justify-center">
<img src="{{ asset('/assets/images/icons/navigation/shop.png') }}" alt="">
</div>
<p class="w-full text-center truncate">
{{ $rare->currency_value ?? 0 }} {{ $rare->currency_type === 0 ? 'Duckets' : ($rare->currency_type === 5 ? 'Diamonds' : 'Other') }}
</p>
</div>
</div>
</div>
@@ -0,0 +1,130 @@
<x-content.shop-card icon-url="{{ $article->icon_url }}" color="{{ $article->color }}" classes="border dark:border-gray-900">
<x-slot:title>
{{ $article->name }}
</x-slot:title>
<x-slot:under-title>
{{ $article->info }}
</x-slot:under-title>
<div class="flex justify-between dark:text-white">
<div class="flex flex-col">
<p class="font-semibold">{{ __('You will receive:') }}</p>
<ul class="list-disc pl-4">
@if($article->features)
@foreach($article->features as $feature)
<li class="ml-3">
{{ $feature->content }}
</li>
@endforeach
@endif
@if ($article->credits)
<li class="ml-3">{{ number_format($article->credits, 0, '.', '.') }} credits</li>
@endif
@if ($article->duckets)
<li class="ml-3">{{ number_format($article->duckets, 0, '.', '.') }} duckets</li>
@endif
@if ($article->diamonds)
<li class="ml-3">{{ number_format($article->diamonds, 0, '.', '.') }} diamonds</li>
@endif
@if ($article->rank)
<li class="ml-3">
{{ $article->rank->rank_name }} rank
</li>
@endif
@if ($article->furniture)
@foreach ($article->furniItems() as $furni)
<li class="ml-3">
{{ collect(json_decode($article->furniture))->firstWhere('item_id', $furni->id)->amount }}
x {{ $furni->public_name }}
</li>
@endforeach
@endif
</ul>
</div>
<div class="flex flex-col gap-3">
@if (!empty($article->badges))
<div class="flex flex-col items-end">
Badge(s):
<div class="flex flex-col dark:text-white py-1.5 px-2 rounded bg-gray-200 dark:bg-gray-700">
<div class="flex gap-2 items-center">
@foreach (explode(';', $article->badges) as $badge)
<img data-tippy-content="1x {{ $badge }}" class="user-badge"
src="/client/flash/c_images/album1584/{{$badge}}.png" alt="{{ $badge }}"
style="image-rendering: auto;">
@endforeach
</div>
</div>
</div>
@endif
@if ($article->furniture)
<div class="flex flex-col items-end">
Furniture:
<div class="flex flex-col dark:text-white py-2 px-4 rounded bg-gray-200 dark:bg-gray-700">
<div class="flex gap-2 items-center">
@foreach ($article->furniItems() as $furni)
<div>
<img
data-tippy-content="{{ collect(json_decode($article->furniture))->firstWhere('item_id', $furni->id)->amount }}x {{ $furni->public_name }}"
class="user-badge" src="{{$furni->icon()}}" alt="{{ $furni->public_name }}">
</div>
@endforeach
</div>
</div>
</div>
@endif
</div>
</div>
<div class="pt-2 mt-auto flex gap-4">
@if($article->is_giftable)
<x-modals.modal-wrapper>
<div x-on:click="open = true">
<x-form.primary-button type="button" classes="px-10">
<x-icons.gift />
</x-form.primary-button>
</div>
<x-modals.regular-modal>
<x-slot name="title">
<h2 class="text-2xl">
{{ __('Gift :package', ['package' => $article->name]) }}
</h2>
</x-slot>
<div class="mt-4">
<form action="{{ route('shop.buy', $article) }}" method="POST" class="w-full">
@csrf
<x-form.input name="receiver" type="text" placeholder="Enter the name of the recipient you want to gift" classes="mb-2"/>
<button type="submit"
class="w-full rounded bg-green-600 hover:bg-green-700 text-white p-2 border-2 border-green-500 transition ease-in-out duration-150 font-semibold">
{{ __('Gift for $:cost', ['cost' => $article->price()]) }}
</button>
</form>
</div>
</x-modals.regular-modal>
</x-modals.modal-wrapper>
@endif
<form action="{{ route('shop.buy', $article) }}" method="POST" class="w-full">
@csrf
<button type="submit"
class="w-full rounded bg-green-600 hover:bg-green-700 text-white p-2 border-2 border-green-500 transition ease-in-out duration-150 font-semibold">
{{ __('Buy for $:cost', ['cost' => $article->price()]) }}
</button>
</form>
</div>
</x-content.shop-card>
@@ -0,0 +1,86 @@
<div class="relative flex min-h-[13rem] h-52 w-full items-center justify-center header-bg"
style="background: url({{ setting('cms_header') }});">
<div class="absolute h-full w-full bg-black/50"></div>
@auth
<div class="relative flex h-full w-full max-w-7xl items-center justify-center pr-10 md:justify-between">
<div class="flex items-center gap-x-4">
<a href="{{ route('me.show') }}" class="ml-7">
<img class=" transition duration-300 ease-in-out hover:scale-105"
src="{{ setting('cms_logo') }}" alt="Hotel logo">
</a>
<div
class="hidden md:flex items-center px-4 rounded-md relative h-[50px]"
style="background-color: var(--color-surface); color: var(--color-text)">
<div class="absolute -left-1 h-6 w-6 rotate-45" style="background-color: var(--color-surface)"></div>
<span class="relative ">
{{ __(':online :hotel online', ['online' => cache()->remember('online_user_count', 30, fn() => DB::table('users')->where('online', '1')->count()),'hotel' => setting('hotel_name')]) }}
</span>
</div>
</div>
<div class="flex flex-col gap-y-2 md:flex-row md:gap-x-4">
<a data-turbolinks="false" href="{{ route('nitro-client') }}">
<button
class="font-bold py-3 px-6 rounded-xl shadow-xl border-2 transition-all duration-300 hover:scale-110 hover:shadow-2xl text-base tracking-wide"
style="background-color: var(--button-color); color: var(--button-text-color); border-color: rgba(255,255,255,0.3);">
<span class="flex items-center gap-2">
<i class="fa-solid fa-gamepad"></i>
<span class="-md">{{ __('Nitro client') }}</span>
</span>
</button>
</a>
@if (config('habbo.client.flash_enabled'))
<a data-turbolinks="false" href="{{ route('flash-client') }}">
<button
class="font-bold py-3 px-6 rounded-xl shadow-xl border-2 transition-all duration-300 hover:scale-110 hover:shadow-2xl text-base tracking-wide"
style="background-color: var(--button-color); color: var(--button-text-color); border-color: rgba(255,255,255,0.3);">
<span class="flex items-center gap-2">
<i class="fa-solid fa-bolt"></i>
<span class="-md">{{ __('Flash client') }}</span>
</span>
</button>
</a>
@endif
</div>
</div>
@endauth
@guest
<x-modals.modal-wrapper>
<div class="flex justify-center">
<div class="font-semibold flex-col md:w-[600px] " style="color: var(--color-text)">
<p class="hidden text-center text-xl md:block " style="color: var(--color-text)">
{{ __('An online virtual world where you can create your own avatar, make friends, chat, create rooms and much more!') }}
</p>
<div class="flex flex-col items-center justify-center gap-x-6 gap-y-4 md:mt-6 md:flex-row md:gap-y-0">
<button type="button" x-on:click="open = true"
class="rounded-full border-2 px-8! py-2! uppercase transition! duration-200! ease-in-out!"
style="border-color: var(--color-primary); background-color: var(--color-primary); color: var(--button-text-color)">
{{ __('Login') }}
</button>
<p class="text-sm uppercase " style="color: var(--color-text-muted)">{{ __('Or') }}</p>
<a data-turbolinks="false" href="{{ route('register') }}">
<button
class="uppercase px-8! py-2.5! rounded-full transition! ease-in-out! duration-200!"
style="background-color: var(--color-accent); color: #ffffff;">
{{ __('Create an account') }}
</button>
</a>
</div>
</div>
</div>
<x-modals.regular-modal x-model="show {{ session()->get('wrong-auth') }}">
<x-auth.login-form />
</x-modals.regular-modal>
</x-modals.modal-wrapper>
@endguest
</div>
@@ -0,0 +1,13 @@
@props(['icon'])
<div class="flex gap-x-3 sm:flex-row sm:gap-x-2">
<div class="h-[25px] w-[25px] rounded-full {{ $icon }} outline-offset-[3px]"></div>
<div style="color: var(--color-text-muted)">
<span class="font-semibold" style="color: var(--color-text)">
{{ $currency }}
</span>
<span>{{ $slot }}</span>
</div>
</div>
@@ -0,0 +1,76 @@
<div class="max-w-7xl min-h-[60px] px-4 md:flex md:items-center md:justify-between md:mx-auto">
{{-- Linkerkant: Currencies --}}
<div class="flex gap-x-6">
<x-top-header-currency icon="nav-credit-icon">
<x-slot:currency>{{ auth()->user()->credits }}</x-slot:currency>
{{ __('Credits') }}
</x-top-header-currency>
<x-top-header-currency icon="nav-ducket-icon">
<x-slot:currency>{{ auth()->user()->currency('duckets') }}</x-slot:currency>
{{ __('Duckets') }}
</x-top-header-currency>
<x-top-header-currency icon="nav-diamond-icon">
<x-slot:currency>{{ auth()->user()->currency('diamonds') }}</x-slot:currency>
{{ __('Diamonds') }}
</x-top-header-currency>
</div>
{{-- Rechterkant: Admin & User Menu --}}
<div class="flex items-center gap-x-3">
{{-- 1. Administratie Dropdown --}}
@if(hasPermission('view_server_logs') || hasPermission('housekeeping_access') || hasPermission('generate_logo'))
<x-navigation.dropdown classes="!border-none" style="color: var(--color-text)">
{{ __('Administration') }}
<x-slot:children>
@if (hasPermission('generate_logo'))
<x-navigation.dropdown-child route="{{ route('logo-generator.index') }}" :turbolink="false" target="_blank">
{{ __('Logo generator') }}
</x-navigation.dropdown-child>
@if (hasPermission('view_server_logs'))
<x-navigation.dropdown-child route="/log-viewer" :turbolink="false" target="_blank">
{{ __('Error logs') }}
</x-navigation.dropdown-child>
@endif
@if(Auth::check())
<a data-turbolinks="false" href="{{ setting('housekeeping_url') }}" target="_blank" class="dropdown-item block px-4 py-2 text-sm" style="color: var(--color-text)">
{{ __('Housekeeping') }}
</a>
@endif
@endif
</x-slot:children>
</x-navigation.dropdown>
@endif
{{-- 2. Gebruiker Dropdown (Avatar + Logout) --}}
<x-navigation.dropdown classes="!border-none">
<div class="flex items-center">
<div @class([
'!bg-no-repeat !bg-center',
'w-[54px] h-[62px]' => Str::contains(setting('avatar_imager'), 'www.habbo.com'),
'w-[64px] h-[110px]' => !Str::contains(setting('avatar_imager'), 'www.habbo.com'),
])
style="background: url({{ setting('avatar_imager') }}{{ auth()->user()->look }}&direction=2&headonly=1&head_direction=2&gesture=sml)">
</div>
<span class="-ml-2">{{ auth()->user()->username }}</span>
</div>
<x-slot:children>
<x-navigation.dropdown-child :route="route('settings.account.show')">
{{ __('User settings') }}
</x-navigation.dropdown-child>
<form action="{{ route('logout') }}" method="POST" class="block">
@csrf
<button type="submit" class="dropdown-item w-full text-left hover:!bg-red-500/20" style="color: var(--button-danger-color);">
{{ __('Logout') }}
</button>
</form>
</x-slot:children>
</x-navigation.dropdown>
</div>
</div>
@@ -0,0 +1,121 @@
<x-content.content-card icon="discord-icon" classes="border dark:border-gray-900">
<x-slot:title>
{{ __('Discord') }}
</x-slot:title>
<x-slot:under-title>
<span id="guildName"></span>
</x-slot:under-title>
<div class="text-sm dark:text-gray-200">
<div id="guildUsers" class="h-[129px] overflow-auto"> </div>
<a id="guildInvite" target="blank">
<x-form.secondary-button classes="mt-3">
{{ __('Join server') }}
</x-form.secondary-button>
</a>
</div>
</x-content.content-card>
@push('javascript')
<script>
window.onload = DiscordApi();
function DiscordApi() {
let init = {
method: 'GET',
mode: 'cors',
cache: 'reload'
}
//gets discord widget json from url with in settings specifed id
fetch("https://discordapp.com/api/guilds/{{ setting('discord_widget_id') }}/widget.json", init).then(
function(res) {
//if there is a problem with discord or id sends an error message in console
if (res.status != 200) {
console.error("Discord widget cant connect to discord (" + res.status + ")");
return;
}
res.json().then(function(data) {
let users = data.members;
let guildName = data.name;
//sets the subtitle of the card to the guild name
document.getElementById('guildName').innerText = guildName;
//loops over every user in json array and display them in the widget
for (let i = 0; i < data.members.length; i++) {
let container = document.createElement('div')
let leftContainer = document.createElement('div')
let imgContainer = document.createElement('div')
let img = document.createElement('img')
let status = document.createElement('div')
let rightContainer = document.createElement('div')
let name = document.createElement('p')
let motto = document.createElement('p')
//sets styleing
container.classList.add('flex', 'items-center', 'gap-x-2')
leftContainer.classList.add('relative')
imgContainer.classList.add('h-9', 'w-9', 'bg-gray-100', 'dark:bg-gray-800',
'rounded-full', 'flex', 'items-center', 'justify-center', 'overflow-hidden')
status.classList.add('absolute', 'bottom-0', 'right-0', 'w-3', 'h-3',
'rounded-full', 'border-2', 'dark:border-gray-800')
name.classList.add('font-semibold')
motto.classList.add('dark:text-gray-400')
//sets styling for exceptions
if (i === 0) {
name.classList.add('mt-1')
}
if (i !== 0) {
imgContainer.classList.add('mt-1')
name.classList.add('mt-3')
}
if (users[i].status === 'online') {
status.style.backgroundColor = "#16a34a";
}
if (users[i].status === 'idle') {
status.style.backgroundColor = "#e9b124";
}
if (users[i].status === 'dnd') {
status.style.backgroundColor = "#9c0017";
}
//adds attributes to elements
img.setAttribute('src', data.members[i].avatar_url);
if (users[i].nick === undefined) {
name.innerText = users[i].username;
} else {
name.innerText = users[i].nick;
}
if (users[i].game !== undefined) {
motto.innerText = users[i].game.name;
}
//append all elements to each other
container.appendChild(leftContainer)
leftContainer.appendChild(imgContainer)
imgContainer.appendChild(img)
leftContainer.appendChild(status)
container.appendChild(rightContainer)
rightContainer.appendChild(name)
rightContainer.appendChild(motto)
document.getElementById('guildUsers').appendChild(container)
}
//Checks if join server link is null and removes btn form webpage
if (data.instant_invite === null) {
document.getElementById('guildInvite').remove()
document.getElementById('guildUsers').style.height = "176px"
} else {
//Gives the "Join server" button a href to the default selected channel in the server
//link is recived from widget json
document.getElementById('guildInvite').setAttribute('href', data.instant_invite)
}
})
});
}
</script>
@endpush
@@ -0,0 +1,22 @@
@props(['user'])
<div class="relative flex items-center justify-between overflow-hidden rounded px-10 me-backdrop border-2"
style="background: rgba(0, 0, 0, 0.3) url({{ setting('cms_me_backdrop') }}); border-color: var(--border-color);">
<div>
<a href="{{ route('profile.show', $user) }}"
class="absolute -bottom-12 left-0 transition duration-300 ease-in-out hover:scale-105 group">
<img style="image-rendering: pixelated;"
src="{{ setting('avatar_imager') }}{{ $user->look }}&direction=2&head_direction=3&gesture=sml&action=wav&size=l"
alt="{{ $user->username }}"
class="border-4 border-gray-800 group-hover:border-[var(--border-color)] transition-colors rounded-xl">
</a>
</div>
<a data-turbolinks="false" href="{{ route('nitro-client') }}">
<button
class="font-bold py-2 px-4 rounded-lg shadow-lg transition-all duration-300 hover:scale-105 hover:opacity-90"
style="background-color: var(--button-color); color: var(--button-text-color);">
{{ __('Go to :hotel', ['hotel' => setting('hotel_name')]) }}
</button>
</a>
</div>
@@ -0,0 +1,15 @@
@props(['colSpan'])
<div class="col-span-2 lg:col-span-{{ $colSpan }}">
{{ $image }}
<div class="shadow">
<div class="flex gap-x-2 rounded-t border-b p-3" style="background-color: var(--color-surface); border-color: var(--color-text-muted);">
<p class="font-semibold" style="color: var(--color-text)">{{ $title }}</p>
</div>
<section class="rounded-b p-3" style="background-color: var(--color-surface); color: var(--color-text);">
{{ $slot }}
</section>
</div>
</div>
@@ -0,0 +1,55 @@
<a data-turbolinks="false" href="{{ route('settings.account.show') }}"
class="{{ request()->routeIs('settings.account.show') ? 'bg-[var(--color-primary)]' : '' }} flex gap-x-2 justify-center items-center rounded p-2 md:p-4 text-center font-semibold transition duration-200 ease-in-out"
:style="request()->routeIs('settings.account.show') ? 'background-color: var(--color-primary); color: var(--button-text-color)' : 'background-color: var(--color-surface); color: var(--color-text)'"
onmouseover="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-primary)'; this.style.color='var(--button-text-color)'; }"
onmouseout="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-surface)'; this.style.color='var(--color-text)'; }">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="hidden md:inline">{{ __('Account') }}</span>
</a>
<a data-turbolinks="false" href="{{ route('settings.preferences.show') }}"
class="{{ request()->routeIs('settings.preferences.show') ? 'bg-[var(--color-primary)]' : '' }} flex gap-x-2 justify-center items-center rounded p-2 md:p-4 text-center font-semibold transition duration-200 ease-in-out"
:style="request()->routeIs('settings.preferences.show') ? 'background-color: var(--color-primary); color: var(--button-text-color)' : 'background-color: var(--color-surface); color: var(--color-text)'"
onmouseover="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-primary)'; this.style.color='var(--button-text-color)'; }"
onmouseout="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-surface)'; this.style.color='var(--color-text)'; }">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span class="hidden md:inline">{{ __('Preferences') }}</span>
</a>
<a data-turbolinks="false" href="{{ route('settings.password.show') }}"
class="{{ request()->routeIs('settings.password.show') ? 'bg-[var(--color-primary)]' : '' }} flex gap-x-2 justify-center items-center rounded p-2 md:p-4 text-center font-semibold transition duration-200 ease-in-out"
:style="request()->routeIs('settings.password.show') ? 'background-color: var(--color-primary); color: var(--button-text-color)' : 'background-color: var(--color-surface); color: var(--color-text)'"
onmouseover="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-primary)'; this.style.color='var(--button-text-color)'; }"
onmouseout="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-surface)'; this.style.color='var(--color-text)'; }">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
<span class="hidden md:inline">{{ __('Password') }}</span>
</a>
<a data-turbolinks="false" href="{{ route('settings.two-factor') }}"
class="{{ request()->routeIs('settings.two-factor') ? 'bg-[var(--color-primary)]' : '' }} flex gap-x-2 justify-center items-center rounded p-2 md:p-4 text-center font-semibold transition duration-200 ease-in-out"
:style="request()->routeIs('settings.two-factor') ? 'background-color: var(--color-primary); color: var(--button-text-color)' : 'background-color: var(--color-surface); color: var(--color-text)'"
onmouseover="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-primary)'; this.style.color='var(--button-text-color)'; }"
onmouseout="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-surface)'; this.style.color='var(--color-text)'; }">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
<span class="hidden md:inline">{{ __('2FA') }}</span>
</a>
<a data-turbolinks="false" href="{{ route('settings.session-logs') }}"
class="{{ request()->routeIs('settings.session-logs') ? 'bg-[var(--color-primary)]' : '' }} flex gap-x-2 justify-center items-center rounded p-2 md:p-4 text-center font-semibold transition duration-200 ease-in-out"
:style="request()->routeIs('settings.session-logs') ? 'background-color: var(--color-primary); color: var(--button-text-color)' : 'background-color: var(--color-surface); color: var(--color-text)'"
onmouseover="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-primary)'; this.style.color='var(--button-text-color)'; }"
onmouseout="if(!this.classList.contains('bg-[var(--color-primary)]')) { this.style.backgroundColor='var(--color-surface)'; this.style.color='var(--color-text)'; }">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
<span class="hidden md:inline">{{ __('Sessions') }}</span>
</a>