You've already forked Atomcms-edit
Initial commit
This commit is contained in:
Executable
+316
@@ -0,0 +1,316 @@
|
||||
@import "tailwindcss";
|
||||
@import "https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap";
|
||||
@plugin "@tailwindcss/forms";
|
||||
@plugin "@tailwindcss/typography";
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
--font-sans:
|
||||
"Nunito", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
"Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
||||
"Segoe UI Symbol", "Noto Color Emoji";
|
||||
--breakpoint-sm: 40rem;
|
||||
--breakpoint-md: 48rem;
|
||||
--breakpoint-lg: 64rem;
|
||||
--breakpoint-xl: 80rem;
|
||||
--breakpoint-2xl: 96rem;
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.nav-item {
|
||||
@apply flex h-auto md:h-[60px] items-center text-[14px] font-semibold uppercase transition duration-200 ease-in-out md:border-b-4! md:border-transparent! md:hover:border-b-[#eeb425]!;
|
||||
@apply text-gray-700 dark:text-gray-100;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
@apply block py-2 px-4 font-semibold transition duration-200;
|
||||
@apply text-gray-900 dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: poppins, sans-serif;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 100px;
|
||||
border: 4px solid transparent;
|
||||
background-clip: content-box;
|
||||
background-color: #2c3039;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #626565;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
color: #626565;
|
||||
}
|
||||
|
||||
::-ms-input-placeholder {
|
||||
color: #626565;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.drop-shadow {
|
||||
-webkit-filter: drop-shadow(2px 1px 0 #fff) drop-shadow(-2px 1px 0 #fff)
|
||||
drop-shadow(0 -2px 0 #fff);
|
||||
}
|
||||
|
||||
.drop-shadow-thin {
|
||||
-webkit-filter: drop-shadow(1px 1px 0 #fff) drop-shadow(-1px 1px 0 #fff)
|
||||
drop-shadow(0 -1px 0 #fff);
|
||||
}
|
||||
|
||||
.article-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background-position: 300px 220px !important;
|
||||
margin: 0 0 20px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.article-image-slide {
|
||||
background-position: 350px 220px !important;
|
||||
}
|
||||
|
||||
.recent-articles {
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
.camera-icon {
|
||||
background: #242c31 url("/public/assets/images/icons/camera.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.discord-icon {
|
||||
background: #25658d url("/public/assets/images/icons/discord.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
html.dark .camera-icon {
|
||||
background: #25658d url("/public/assets/images/icons/camera.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.hotel-icon {
|
||||
background: #f68b08 url("/public/assets/images/icons/feeds.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.chat-icon {
|
||||
background: #375571 url("/public/assets/images/icons/chat.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.article-icon {
|
||||
background: #536e5e url("/public/assets/images/icons/article.gif") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.lighthouse-icon {
|
||||
background: #8554a6 url("/public/assets/images/icons/lighthouse.png")
|
||||
no-repeat center;
|
||||
}
|
||||
|
||||
.currency-icon {
|
||||
background: #e3ad06 url("/public/assets/images/icons/currency.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.catalog-icon {
|
||||
background: rgba(141, 74, 183, 0.51)
|
||||
url("/public/assets/images/icons/catalog.png") no-repeat center;
|
||||
}
|
||||
|
||||
.inventory-icon {
|
||||
background: #232121 url("/public/assets/images/icons/inventory.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.duo-chat-icon {
|
||||
background: #eec980 url("/public/assets/images/icons/due-chat.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.friends-icon {
|
||||
background: #b17f85 url("/public/assets/images/icons/friends.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.nav-credit-icon {
|
||||
background: #e9b124 url("/public/assets/images/icons/currency/credits.png")
|
||||
no-repeat center;
|
||||
outline: 1px solid #b26d18;
|
||||
}
|
||||
|
||||
.nav-ducket-icon {
|
||||
background: #c44aac url("/public/assets/images/icons/currency/duckets.png")
|
||||
no-repeat center;
|
||||
outline: 1px solid #812378;
|
||||
}
|
||||
|
||||
.nav-diamond-icon {
|
||||
background: #caf1f3 url("/public/assets/images/icons/currency/diamonds.png")
|
||||
no-repeat center;
|
||||
outline: 1px solid #6caff4;
|
||||
}
|
||||
|
||||
.me-backdrop {
|
||||
height: 180px;
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
.staff-bg {
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
/* Nitro disconnect overlay */
|
||||
#disconnected {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.profile-bg {
|
||||
background: rgba(0, 0, 0, 0.5)
|
||||
url("/public/assets/images/profile/profile-bg.png");
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
.leaderboard-first {
|
||||
background: #f9d83e url("/public/assets/images/leaderboards/trophy-gold.png")
|
||||
no-repeat center;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.leaderboard-second {
|
||||
background: #b8c4d4
|
||||
url("/public/assets/images/leaderboards/trophy-silver.png") no-repeat center;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.leaderboard-third {
|
||||
background: #f1851b
|
||||
url("/public/assets/images/leaderboards/trophy-bronze.png") no-repeat center;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.site-bg {
|
||||
background: url("/public/assets/images/background-light.jpg") no-repeat fixed
|
||||
right bottom;
|
||||
}
|
||||
|
||||
.app.dark .site-bg {
|
||||
background: url("/public/assets/images/background-dark.jpg") no-repeat fixed
|
||||
right bottom;
|
||||
}
|
||||
|
||||
.app.dark .swal2-popup {
|
||||
background: rgb(17 24 39);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.turbolinks-progress-bar {
|
||||
height: 5px;
|
||||
background-color: #e9b124;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.navigation-icon {
|
||||
width: 20px;
|
||||
height: 25px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.navigation-icon.shop {
|
||||
background-image: url("/public/assets/images/icons/navigation/shop.png");
|
||||
}
|
||||
|
||||
.navigation-icon.leaderboards {
|
||||
background-image: url("/public/assets/images/icons/navigation/leaderboards.png");
|
||||
}
|
||||
|
||||
.navigation-icon.rules {
|
||||
background-image: url("/public/assets/images/icons/navigation/rules.gif");
|
||||
}
|
||||
|
||||
.navigation-icon.home {
|
||||
background-image: url("/public/assets/images/icons/navigation/home.png");
|
||||
}
|
||||
|
||||
.navigation-icon.community {
|
||||
background-image: url("/public/assets/images/icons/navigation/community.png");
|
||||
}
|
||||
|
||||
.swiper-slide {
|
||||
height: 215px !important;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullet-active,
|
||||
html.dark .swiper-pagination-bullet-active {
|
||||
background: #e9b124 !important;
|
||||
}
|
||||
|
||||
html.dark .swiper-pagination-bullet:not(.swiper-pagination-bullet-active) {
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
.prose-xl p:is([style*="text-align: center;"]) img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.prose-xl p:is([style*="text-align: left;"]) img {
|
||||
margin-left: 0;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.prose-xl p:is([style*="text-align: right;"]) img {
|
||||
margin-left: auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.prose-xl p {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.atom-align-left {
|
||||
float: left;
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.atom-align-right {
|
||||
float: right;
|
||||
margin: 0 0 10px 10px;
|
||||
}
|
||||
|
||||
.atom-align-center {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.atom-align-center > * {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#article-content a {
|
||||
color: #53b2f8;
|
||||
}
|
||||
|
||||
.cursor-grab {
|
||||
cursor: grab !important;
|
||||
}
|
||||
.cursor-grab:active {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import _ from "lodash";
|
||||
window._ = _;
|
||||
|
||||
/**
|
||||
* We'll load the axios HTTP library which allows us to easily issue requests
|
||||
* to our Laravel back-end. This library automatically handles sending the
|
||||
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
window.axios = axios;
|
||||
|
||||
window.axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
|
||||
|
||||
/**
|
||||
* Echo exposes an expressive API for subscribing to channels and listening
|
||||
* for events that are broadcast by Laravel. Echo and event broadcasting
|
||||
* allows your team to easily build robust real-time web applications.
|
||||
*/
|
||||
|
||||
// import Echo from 'laravel-echo';
|
||||
|
||||
// import Pusher from 'pusher-js';
|
||||
// window.Pusher = Pusher;
|
||||
|
||||
// window.Echo = new Echo({
|
||||
// broadcaster: 'pusher',
|
||||
// key: import.meta.env.VITE_PUSHER_APP_KEY,
|
||||
// wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
|
||||
// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
|
||||
// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
|
||||
// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
|
||||
// enabledTransports: ['ws', 'wss'],
|
||||
// });
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
import "./bootstrap";
|
||||
Executable
+402
@@ -0,0 +1,402 @@
|
||||
@import "tailwindcss";
|
||||
@import "https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap";
|
||||
@plugin "@tailwindcss/forms";
|
||||
@plugin "@tailwindcss/typography";
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
--font-sans:
|
||||
"Nunito", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
"Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
||||
"Segoe UI Symbol", "Noto Color Emoji";
|
||||
--breakpoint-sm: 40rem;
|
||||
--breakpoint-md: 48rem;
|
||||
--breakpoint-lg: 64rem;
|
||||
--breakpoint-xl: 80rem;
|
||||
--breakpoint-2xl: 96rem;
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.nav-item {
|
||||
@apply flex h-auto md:h-[60px] items-center text-[14px] font-semibold uppercase text-gray-700 transition duration-200 ease-in-out;
|
||||
color: var(--color-navbar-text);
|
||||
border-bottom: 4px solid transparent;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
border-bottom-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
@apply block py-2 px-4 font-semibold hover:bg-gray-100 text-gray-900;
|
||||
color: var(--color-text);
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 8px 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dropdown-item::before,
|
||||
.dropdown-item::after {
|
||||
display: none !important;
|
||||
content: none !important;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile dropdown - force center everything */
|
||||
@media (max-width: 768px) {
|
||||
.dropdown-panel {
|
||||
text-align: center !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
align-items: center !important;
|
||||
text-align: center !important;
|
||||
width: 100% !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: poppins, sans-serif;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 100px;
|
||||
border: 4px solid transparent;
|
||||
background-clip: content-box;
|
||||
background-color: #2c3039;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #626565;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
color: #626565;
|
||||
}
|
||||
|
||||
::-ms-input-placeholder {
|
||||
color: #626565;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: var(--input-placeholder-color, var(--color-text-muted));
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.article-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background-position: 300px 220px !important;
|
||||
margin: 0 0 20px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.article-image-slide {
|
||||
background-position: 350px 220px !important;
|
||||
}
|
||||
|
||||
.recent-articles {
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
.camera-icon {
|
||||
background: #242c31 url("/public/assets/images/icons/camera.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.discord-icon {
|
||||
background: #25658d url("/public/assets/images/icons/discord.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
html.dark .camera-icon {
|
||||
background: #25658d url("/public/assets/images/icons/camera.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.hotel-icon {
|
||||
background: #f68b08 url("/public/assets/images/icons/feeds.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.chat-icon {
|
||||
background: #375571 url("/public/assets/images/icons/chat.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.article-icon {
|
||||
background: #536e5e url("/public/assets/images/icons/article.gif") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.lighthouse-icon {
|
||||
background: #8554a6 url("/public/assets/images/icons/lighthouse.png")
|
||||
no-repeat center;
|
||||
}
|
||||
|
||||
.currency-icon {
|
||||
background: #e3ad06 url("/public/assets/images/icons/currency.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.catalog-icon {
|
||||
background: rgba(141, 74, 183, 0.51)
|
||||
url("/public/assets/images/icons/catalog.png") no-repeat center;
|
||||
}
|
||||
|
||||
.inventory-icon {
|
||||
background: #232121 url("/public/assets/images/icons/inventory.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.duo-chat-icon {
|
||||
background: #eec980 url("/public/assets/images/icons/due-chat.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.friends-icon {
|
||||
background: #b17f85 url("/public/assets/images/icons/friends.png") no-repeat
|
||||
center;
|
||||
}
|
||||
|
||||
.nav-credit-icon {
|
||||
background: #e9b124 url("/public/assets/images/icons/currency/credits.png")
|
||||
no-repeat center;
|
||||
outline: 1px solid #b26d18;
|
||||
}
|
||||
|
||||
.nav-ducket-icon {
|
||||
background: #c44aac url("/public/assets/images/icons/currency/duckets.png")
|
||||
no-repeat center;
|
||||
outline: 1px solid #812378;
|
||||
}
|
||||
|
||||
.nav-diamond-icon {
|
||||
background: #caf1f3 url("/public/assets/images/icons/currency/diamonds.png")
|
||||
no-repeat center;
|
||||
outline: 1px solid #6caff4;
|
||||
}
|
||||
|
||||
.me-backdrop {
|
||||
height: 180px;
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
.staff-bg {
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
#disconnected {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.profile-bg {
|
||||
background: rgba(0, 0, 0, 0.5)
|
||||
url("/public/assets/images/profile/profile-bg.png");
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
.leaderboard-first {
|
||||
background: #f9d83e url("/public/assets/images/leaderboards/trophy-gold.png")
|
||||
no-repeat center;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.leaderboard-second {
|
||||
background: #b8c4d4
|
||||
url("/public/assets/images/leaderboards/trophy-silver.png") no-repeat center;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.leaderboard-third {
|
||||
background: #f1851b
|
||||
url("/public/assets/images/leaderboards/trophy-bronze.png") no-repeat center;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.site-bg {
|
||||
background: url("/public/assets/images/background-light.jpg") no-repeat fixed
|
||||
right bottom;
|
||||
}
|
||||
|
||||
.app.dark .site-bg {
|
||||
background: url("/public/assets/images/background-dark.jpg") no-repeat fixed
|
||||
right bottom;
|
||||
}
|
||||
|
||||
.app.dark .swal2-popup {
|
||||
background: rgb(17 24 39);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.turbolinks-progress-bar {
|
||||
height: 5px;
|
||||
background-color: #e9b124;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.navigation-icon {
|
||||
width: 20px;
|
||||
height: 25px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.navigation-icon.shop {
|
||||
background-image: url("/public/assets/images/icons/navigation/shop.png");
|
||||
}
|
||||
|
||||
.navigation-icon.leaderboards {
|
||||
background-image: url("/public/assets/images/icons/navigation/leaderboards.png");
|
||||
}
|
||||
|
||||
.navigation-icon.rules {
|
||||
background-image: url("/public/assets/images/icons/navigation/rules.gif");
|
||||
}
|
||||
|
||||
.navigation-icon.home {
|
||||
background-image: url("/public/assets/images/icons/navigation/home.png");
|
||||
}
|
||||
|
||||
.navigation-icon.community {
|
||||
background-image: url("/public/assets/images/icons/navigation/community.png");
|
||||
}
|
||||
|
||||
.swiper-slide {
|
||||
height: 215px !important;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullet-active,
|
||||
html.dark .swiper-pagination-bullet-active {
|
||||
background: #e9b124 !important;
|
||||
}
|
||||
|
||||
html.dark .swiper-pagination-bullet:not(.swiper-pagination-bullet-active) {
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
.prose-xl p:is([style*="text-align: center;"]) img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.prose-xl p:is([style*="text-align: left;"]) img {
|
||||
margin-left: 0;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.prose-xl p:is([style*="text-align: right;"]) img {
|
||||
margin-left: auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.prose-xl p {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.atom-align-left {
|
||||
float: left;
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.atom-align-right {
|
||||
float: right;
|
||||
margin: 0 0 10px 10px;
|
||||
}
|
||||
|
||||
.atom-align-center {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.atom-align-center > * {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#article-content a {
|
||||
color: #53b2f8;
|
||||
}
|
||||
|
||||
.badge-drawer-button {
|
||||
background-image: linear-gradient(to bottom, #f2f2f3 51%, #d9d8d8 49%);
|
||||
box-shadow: inset 0 0 0px 2px #d9d9d9;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.badge-drawer-button:hover {
|
||||
background: linear-gradient(to bottom, #ffffff 50%, #ebebeb 50%);
|
||||
box-shadow: inset 0 0 0px 2px #ffffff;
|
||||
}
|
||||
|
||||
.badge-drawer-button .toggled {
|
||||
background: #f2f2f3;
|
||||
}
|
||||
|
||||
html.dark .badge-drawer-button {
|
||||
background-image: linear-gradient(to bottom, #141414 51%, #101010 49%);
|
||||
box-shadow: inset 0 0 0px 2px #242424;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
html.dark .badge-drawer-button:hover {
|
||||
background: linear-gradient(to bottom, #171717 50%, #111111 50%);
|
||||
box-shadow: inset 0 0 0px 2px #363636;
|
||||
}
|
||||
|
||||
html.dark .badge-drawer-button .toggled {
|
||||
background: #f2f2f3;
|
||||
}
|
||||
|
||||
.badge-drawer-palette {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
color: #000;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
border-radius: 0.25rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
display: flex;
|
||||
border-width: 3px;
|
||||
border-color: #b6bec5;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-width: 38px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.badge-drawer-palette::after {
|
||||
content: "";
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50%;
|
||||
background-color: #fff;
|
||||
opacity: 0.1;
|
||||
}
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
import "./bootstrap";
|
||||
import "./external/flowbite";
|
||||
|
||||
import "swiper/css";
|
||||
import "swiper/css/pagination";
|
||||
|
||||
import Alpine from "alpinejs";
|
||||
import Focus from "@alpinejs/focus";
|
||||
|
||||
import ArticleReactions from "./components/ArticleReactions.js";
|
||||
|
||||
import ThemeSwitcher from "./components/ThemeSwitcher.js";
|
||||
import AtomSliders from "./components/AtomSliders.js";
|
||||
|
||||
ThemeSwitcher.init();
|
||||
ArticleReactions.init();
|
||||
AtomSliders.init();
|
||||
Alpine.plugin(Focus);
|
||||
Alpine.start();
|
||||
|
||||
console.log(
|
||||
"%cAtom CMS%c\n\nAtom CMS is a CMS for made for the community to enjoy. You can join our wonderful community at https://discord.gg/rX3aShUHdg\n\n",
|
||||
"color: #14619c; -webkit-text-stroke: 2px black; font-size: 32px; font-weight: bold;",
|
||||
"",
|
||||
);
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* We'll load the axios HTTP library which allows us to easily issue requests
|
||||
* to our Laravel back-end. This library automatically handles sending the
|
||||
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import Turbolinks from "turbolinks";
|
||||
|
||||
window.axios = axios;
|
||||
window.axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
|
||||
|
||||
Turbolinks.start();
|
||||
|
||||
/**
|
||||
* Echo exposes an expressive API for subscribing to channels and listening
|
||||
* for events that are broadcast by Laravel. Echo and event broadcasting
|
||||
* allows your team to easily build robust real-time web applications.
|
||||
*/
|
||||
|
||||
// import Echo from 'laravel-echo';
|
||||
|
||||
// import Pusher from 'pusher-js';
|
||||
// window.Pusher = Pusher;
|
||||
|
||||
// window.Echo = new Echo({
|
||||
// broadcaster: 'pusher',
|
||||
// key: import.meta.env.VITE_PUSHER_APP_KEY,
|
||||
// wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_CLUSTER}.pusher.com`,
|
||||
// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
|
||||
// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
|
||||
// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
|
||||
// enabledTransports: ['ws', 'wss'],
|
||||
// });
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
import Alpine from "alpinejs";
|
||||
|
||||
const ArticleReactions = {
|
||||
init() {
|
||||
document.addEventListener("alpine:init", () => this.startComponent());
|
||||
},
|
||||
|
||||
startComponent() {
|
||||
Alpine.data(
|
||||
"reactions",
|
||||
(myReactions = [], articleReactions = [], url = "") => ({
|
||||
url,
|
||||
myReactions,
|
||||
articleReactions,
|
||||
allReactions: [],
|
||||
isAuthenticated: false,
|
||||
|
||||
init() {
|
||||
this.treatArticleReactions();
|
||||
this.allReactions = window.App.defaultReactions;
|
||||
this.isAuthenticated = window.App.isAuthenticated;
|
||||
|
||||
this.dispatchFlowbiteEvent();
|
||||
},
|
||||
|
||||
treatArticleReactions() {
|
||||
let articleReactions = this.articleReactions;
|
||||
|
||||
this.articleReactions = [];
|
||||
|
||||
Object.entries(articleReactions).forEach((reactionData) => {
|
||||
let reactionName = reactionData[0],
|
||||
reactions = Object.values(reactionData[1]);
|
||||
|
||||
this.articleReactions.push({
|
||||
id: this.generateVirtualReactionId(reactionName),
|
||||
name: reactionName,
|
||||
count: reactions.length,
|
||||
users: reactions.map((reaction) => reaction.user?.username ?? ""),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
toggleReaction(reaction) {
|
||||
if (!this.url.length || !this.isAuthenticated) return;
|
||||
|
||||
axios.post(this.url, { reaction }).then((response) => {
|
||||
if (!response.data.success) return;
|
||||
|
||||
if (!response.data.added) {
|
||||
this.removeReaction(reaction, response.data.username);
|
||||
return;
|
||||
}
|
||||
|
||||
this.addReaction(reaction, response.data.username);
|
||||
});
|
||||
},
|
||||
|
||||
addReaction(name, username) {
|
||||
this.myReactions.push(name);
|
||||
|
||||
let existingReaction = this.getReactionDataFromName(name);
|
||||
|
||||
if (existingReaction) {
|
||||
existingReaction.count++;
|
||||
existingReaction.users.push(username);
|
||||
return;
|
||||
}
|
||||
|
||||
this.articleReactions.push({
|
||||
id: this.generateVirtualReactionId(name),
|
||||
name,
|
||||
count: 1,
|
||||
users: [username],
|
||||
});
|
||||
|
||||
this.dispatchFlowbiteEvent();
|
||||
},
|
||||
|
||||
removeReaction(name, username) {
|
||||
this.myReactions.splice(this.myReactions.indexOf(name), 1);
|
||||
|
||||
let reactionData = this.getReactionDataFromName(name);
|
||||
|
||||
if (reactionData.count > 1) {
|
||||
reactionData.count--;
|
||||
reactionData.users.splice(reactionData.users.indexOf(username), 1);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.articleReactions.splice(
|
||||
this.articleReactions.indexOf(reactionData),
|
||||
1,
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
generateVirtualReactionId(name) {
|
||||
return name + Math.floor(Math.random() * 1000);
|
||||
},
|
||||
|
||||
canAddReactionFromModal(name) {
|
||||
return !this.userHasReaction(name) && !this.articleHasReaction(name);
|
||||
},
|
||||
|
||||
userHasReaction(reaction) {
|
||||
return this.myReactions.includes(reaction.name);
|
||||
},
|
||||
|
||||
articleHasReaction(name) {
|
||||
return typeof this.getReactionDataFromName(name) !== "undefined";
|
||||
},
|
||||
|
||||
getReactionDataFromName(name) {
|
||||
return this.articleReactions.find(
|
||||
(reaction) => reaction.name === name,
|
||||
);
|
||||
},
|
||||
|
||||
dispatchFlowbiteEvent() {
|
||||
this.$nextTick(() =>
|
||||
document.dispatchEvent(new CustomEvent("reactions:loaded")),
|
||||
);
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export { ArticleReactions as default };
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import Swiper from "swiper";
|
||||
|
||||
const AtomSliders = {
|
||||
init() {
|
||||
document.addEventListener("turbolinks:load", () => {
|
||||
this.initArticleSlider();
|
||||
});
|
||||
},
|
||||
|
||||
initArticleSlider() {
|
||||
if (!document.querySelector(".article-slider")) return;
|
||||
|
||||
new Swiper(".articles-slider", {
|
||||
modules: [],
|
||||
slidesPerView: 1,
|
||||
loop: true,
|
||||
autoplay: {
|
||||
delay: 5000,
|
||||
disableOnInteraction: false,
|
||||
pauseOnMouseEnter: true,
|
||||
},
|
||||
pagination: {
|
||||
el: ".swiper-pagination",
|
||||
clickable: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export { AtomSliders as default };
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
const ThemeSwitcher = {
|
||||
currentTheme: "light",
|
||||
|
||||
init() {
|
||||
if (
|
||||
localStorage.theme === "dark" ||
|
||||
(typeof window.matchMedia != "undefined" &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches &&
|
||||
localStorage.theme !== "light")
|
||||
) {
|
||||
this.toggleTheme();
|
||||
}
|
||||
|
||||
document.addEventListener("turbolinks:load", () => this.initButton());
|
||||
},
|
||||
|
||||
initButton() {
|
||||
let themeSwitcher = document.getElementById("theme-switcher");
|
||||
|
||||
themeSwitcher?.addEventListener("click", () => this.toggleTheme());
|
||||
},
|
||||
|
||||
toggleTheme() {
|
||||
if (this.currentTheme === "light") {
|
||||
this.currentTheme = "dark";
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
this.currentTheme = "light";
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
|
||||
localStorage.setItem("theme", this.currentTheme);
|
||||
},
|
||||
};
|
||||
|
||||
export { ThemeSwitcher as default };
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { Collapse } from "flowbite";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const mobileMenuButton = document.querySelector(
|
||||
'[data-collapse-toggle="mobile-menu"]',
|
||||
);
|
||||
const mobileMenu = document.getElementById("mobile-menu");
|
||||
|
||||
if (mobileMenuButton && mobileMenu) {
|
||||
const collapse = new Collapse(mobileMenu);
|
||||
|
||||
mobileMenuButton.addEventListener("click", function () {
|
||||
collapse.toggle();
|
||||
});
|
||||
}
|
||||
});
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Create account'))
|
||||
|
||||
<!-- Validation Errors -->
|
||||
<x-messages.flash-messages />
|
||||
|
||||
<div class="col-span-12">
|
||||
<div class="lg:px-[250px]">
|
||||
<x-content.content-card icon="hotel-icon" classes="flex flex-col">
|
||||
<x-slot:title>
|
||||
{{ __('Login to :hotel', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Login to :hotel and take part in the most wonderful online world!', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form method="POST" action="{{ route('login') }}">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<x-form.label for="username">
|
||||
{{ __('Username') }}
|
||||
</x-form.label>
|
||||
</div>
|
||||
|
||||
<x-form.input error-bag="register" name="username" type="text" value="{{ old('username') }}"
|
||||
placeholder="{{ __('Username') }}" :autofocus="true" />
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<x-form.label for="password">
|
||||
{{ __('Password') }}
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input error-bag="register" name="password" type="password"
|
||||
placeholder="{{ __('Password') }}" />
|
||||
</div>
|
||||
|
||||
@if (setting('google_recaptcha_enabled'))
|
||||
<div class="mt-4 g-recaptcha" data-sitekey="{{ config('habbo.site.recaptcha_site_key') }}">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (setting('cloudflare_turnstile_enabled'))
|
||||
<x-turnstile />
|
||||
@endif
|
||||
|
||||
<div class="mt-4">
|
||||
<x-form.primary-button>
|
||||
{{ __('Login') }}
|
||||
</x-form.primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,53 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Two factor'))
|
||||
|
||||
<div class="col-span-12 flex flex-col gap-y-3 md:col-span-3">
|
||||
<x-user.settings.settings-navigation />
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 flex flex-col gap-y-3 md:col-span-9">
|
||||
<x-content.content-card icon="hotel-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Confirm your password') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('You must confirm your password to continue') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form method="POST" action="/user/confirm-password">
|
||||
@csrf
|
||||
|
||||
<!-- Password -->
|
||||
<div class="flex flex-col gap-y-2 mb-3">
|
||||
<div>
|
||||
<x-form.label for="password">
|
||||
{{ __('Password') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('You must confirm your current password before being able to toggle 2FA.') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="password" type="password"
|
||||
placeholder="{{ __('Enter your current password') }}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (setting('google_recaptcha_enabled'))
|
||||
<div class="mt-4 g-recaptcha" data-sitekey="{{ config('habbo.site.recaptcha_site_key') }}"></div>
|
||||
@endif
|
||||
|
||||
@if (setting('cloudflare_turnstile_enabled'))
|
||||
<x-turnstile />
|
||||
@endif
|
||||
|
||||
<div class="mt-4">
|
||||
<x-form.primary-button>
|
||||
{{ __('Confirm password') }}
|
||||
</x-form.primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,40 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Forgot password'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<x-content.content-card icon="hotel-icon" classes="max-w-[640px] mx-auto">
|
||||
<x-slot:title>
|
||||
{{ __('Did you forget your password?') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form method="POST" action="{{ route('forgot.password.post') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Email Address -->
|
||||
<div>
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<x-form.label for="mail">
|
||||
{{ __('Email') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Enter your email') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
</div>
|
||||
|
||||
<x-form.input name="mail" type="email" placeholder="{{ __('Enter your email') }}"/>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<x-form.primary-button>
|
||||
{{ __('Email Password Reset Link') }}
|
||||
</x-form.primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,50 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Reset Password'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<x-content.content-card icon="hotel-icon" classes="max-w-[640px] mx-auto">
|
||||
<x-slot:title>
|
||||
{{ __('Reset Password') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Choose a new password for your Account.') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form method="POST" action="{{ route('reset.password.post', $token) }}">
|
||||
@csrf
|
||||
|
||||
<!-- Password -->
|
||||
<div class="bg-[#efefef] rounded-md p-3 flex flex-col gap-y-6 dark:bg-gray-900">
|
||||
<div>
|
||||
<x-form.label for="password">
|
||||
{{ __('Password') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Your password must contain atleast 8 characters. Make sure to use a unique & secure password.') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="password" type="password" placeholder="{{ __('Choose a secure password') }}"/>
|
||||
</div>
|
||||
<hr class="dark:border-gray-700">
|
||||
|
||||
<!-- Confirm Password -->
|
||||
<div>
|
||||
<x-form.label for="password_confirmation">
|
||||
{{ __('Repeat Password') }}
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="password_confirmation" type="password" placeholder="{{ __('Repeat your chosen password') }}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<x-form.primary-button>
|
||||
{{ __('Reset Password') }}
|
||||
</x-form.primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Create account'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<x-content.content-card icon="hotel-icon" classes="flex flex-col gap-y-8">
|
||||
<x-slot:title>
|
||||
{{ __('Create your account!') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Create a free account, and be a part of a fun online world!') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="flex w-full justify-between">
|
||||
<div class="w-full !lg:w-[420px]">
|
||||
<form method="POST" action="{{ route('register') }}">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<x-form.label for="username">
|
||||
{{ __('Username') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Your username is what you will have to use, when logging into :hotel. It is also what other users will know you as, so make sure you select a username that you like!', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
</div>
|
||||
|
||||
<x-form.input error-bag="register" name="username" type="text"
|
||||
value="{{ old('username') }}" placeholder="{{ __('Username') }}"
|
||||
:autofocus="true"/>
|
||||
</div>
|
||||
|
||||
<!-- Email Address -->
|
||||
<div class="mt-4">
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<x-form.label for="mail">
|
||||
{{ __('Email') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('You will need your email if you were to ever forget your password, so make sure it is something that you remember.') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
</div>
|
||||
|
||||
<x-form.input error-bag="register" name="mail" type="email"
|
||||
value="{{ old('mail') }}" placeholder="{{ __('Enter your email') }}"/>
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div class="mt-4 bg-[#efefef] rounded-md p-3 flex flex-col gap-y-6 dark:bg-gray-900">
|
||||
<div>
|
||||
<x-form.label for="password">
|
||||
{{ __('Password') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Your password must contain atleast 8 characters. Make sure to use a unique & secure password.') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input error-bag="register" name="password" type="password"
|
||||
placeholder="{{ __('Choose a secure password') }}"/>
|
||||
</div>
|
||||
<hr class="dark:border-gray-700">
|
||||
|
||||
<!-- Confirm Password -->
|
||||
<div>
|
||||
<x-form.label for="password_confirmation">
|
||||
{{ __('Repeat Password') }}
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input error-bag="register" name="password_confirmation" type="password"
|
||||
placeholder="{{ __('Repeat your chosen password') }}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (setting('requires_beta_code'))
|
||||
<div class="mt-4">
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<x-form.label for="mail">
|
||||
{{ __('Beta code') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Enter the beta code you have been provided with') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
</div>
|
||||
|
||||
<x-form.input error-bag="register" name="beta_code" type="text"
|
||||
value="{{ old('beta_code') }}"
|
||||
placeholder="{{ __('Enter your beta code') }}"/>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{{ __('Choose your avatar') }}
|
||||
<span class="block text-xs font-normal text-gray-500 mt-0.5">{{ __('Select a starting avatar for your character') }}</span>
|
||||
</label>
|
||||
|
||||
<div class="grid grid-cols-5 gap-2 mt-2">
|
||||
@php
|
||||
$femaleFigures = array_slice(array_filter(array_map('trim', explode(',', setting('register_female_figures', 'hr-100-45.hd-180-1.ch-215-62.lg-270-62.sh-290-62,hr-130-45.hd-180-2.ch-220-62.lg-270-62.sh-305-62,hr-140-45.hd-180-3.ch-225-62.lg-270-62.sh-310-62,hr-165-45.hd-180-1.ch-210-62.lg-270-62.sh-290-62,hr-190-45.hd-180-2.ch-215-62.lg-270-62.sh-305-62')))), 0, 5);
|
||||
$maleFigures = array_slice(array_filter(array_map('trim', explode(',', setting('register_male_figures', 'hr-100-61.hd-180-1.ch-210-66.lg-270-110.sh-305-62,hr-105-61.hd-190-2.ch-215-66.lg-280-110.sh-310-62,hr-110-61.hd-200-3.ch-220-66.lg-290-110.sh-320-62,hr-115-61.hd-185-1.ch-210-66.lg-275-110.sh-315-62,hr-120-61.hd-180-2.ch-205-66.lg-285-110.sh-305-62')))), 0, 5);
|
||||
$allFigures = array_merge($femaleFigures, $maleFigures);
|
||||
@endphp
|
||||
|
||||
@foreach($allFigures as $index => $figure)
|
||||
<label class="cursor-pointer relative group">
|
||||
<input type="radio" name="look" value="{{ $figure }}"
|
||||
class="peer sr-only" {{ $index === 0 ? 'checked' : '' }}>
|
||||
<div class="p-1 rounded-lg border-2 border-gray-200 dark:border-gray-700
|
||||
peer-checked:border-purple-500 peer-checked:bg-purple-50
|
||||
dark:peer-checked:bg-purple-900/20 hover:border-gray-300
|
||||
dark:hover:border-gray-600 transition-all duration-300
|
||||
peer-checked:scale-110 peer-checked:shadow-lg peer-checked:shadow-purple-500/30"
|
||||
onmouseover="this.querySelector('img').src='https://www.habbo.nl/habbo-imaging/avatarimage?figure={{ $figure }}&size=l&action=wav'"
|
||||
onmouseout="if(!this.previousElementSibling.checked) this.querySelector('img').src='https://www.habbo.nl/habbo-imaging/avatarimage?figure={{ $figure }}&size=l'">
|
||||
<img src="https://www.habbo.nl/habbo-imaging/avatarimage?figure={{ $figure }}&size=l"
|
||||
class="w-full h-auto transition-transform duration-300"
|
||||
alt="Avatar">
|
||||
</div>
|
||||
<span class="absolute -bottom-1 left-1/2 -translate-x-1/2 text-[8px]
|
||||
bg-gray-900 dark:bg-gray-700 text-white px-1 rounded
|
||||
opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
{{ $index < 5 ? 'Vrouw' : 'Man' }}
|
||||
</span>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 bg-[#efefef] rounded-md p-3 flex flex-col gap-y-1 dark:bg-gray-900">
|
||||
<div class="flex items-center gap-x-3">
|
||||
<input id="terms" type="checkbox" name="terms"
|
||||
class="mt-1 rounded ring-0 focus:ring-0">
|
||||
|
||||
<a href="{{ route('help-center.rules.index') }}" target="_blank"
|
||||
class="mt-1 text-sm font-semibold text-gray-700 hover:text-gray-900 hover:underline dark:hover:text-gray-300 dark:text-gray-500">
|
||||
{{ __('I accept the :hotel terms & rules.', ['hotel' => setting('hotel_name')]) }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@error('terms')
|
||||
<p class="mt-1 text-xs italic text-red-500">
|
||||
{{ $message }}
|
||||
</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Used to determine the refer --}}
|
||||
<input type="hidden" name="referral_code" value="{{ $referral_code }}">
|
||||
|
||||
@if (setting('google_recaptcha_enabled'))
|
||||
<div class="mt-4 g-recaptcha"
|
||||
data-sitekey="{{ config('habbo.site.recaptcha_site_key') }}"></div>
|
||||
@endif
|
||||
|
||||
@if (setting('cloudflare_turnstile_enabled'))
|
||||
<x-turnstile />
|
||||
@endif
|
||||
|
||||
<div class="mt-4">
|
||||
<x-form.primary-button>
|
||||
{{ __('Create account') }}
|
||||
</x-form.primary-button>
|
||||
</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 register 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>
|
||||
{{ __('Register 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.106z"/></svg>
|
||||
{{ __('Register 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>
|
||||
{{ __('Register with GitHub') }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="hidden md:block relative w-full">
|
||||
<img class="opacity-50 absolute -right-3 -bottom-3" src="{{ asset('/assets/images/hotel.png') }}"
|
||||
alt="">
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,57 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Two-Factor Authentication'))
|
||||
|
||||
<div class="col-span-12 flex flex-col gap-y-3 md:col-span-9 md:col-start-4">
|
||||
<x-content.content-card icon="hotel-icon" classes="border dark:border-[var(--color-text-muted)]">
|
||||
<x-slot:title>
|
||||
{{ __('Two-Factor Authentication') }}
|
||||
</x-slot:title>
|
||||
<x-slot:under-title>
|
||||
{{ __('Please enter your two-factor authentication code or one of your recovery codes.') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
@if (session('error'))
|
||||
<div class="alert alert-danger">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('two-factor.login') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Two-Factor Code -->
|
||||
<div>
|
||||
<x-form.label for="code">
|
||||
{{ __('Code') }}
|
||||
<x-slot:info>
|
||||
{{ __('Enter the two-factor authentication code from your authenticator app.') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
<x-form.input id="code" name="code" type="text" placeholder="{{ __('Code') }}" class="mb-3" />
|
||||
</div>
|
||||
|
||||
<!-- Recovery Code -->
|
||||
<div class="mt-4">
|
||||
<x-form.label for="recovery_code">
|
||||
{{ __('Recovery Code') }}
|
||||
<x-slot:info>
|
||||
{{ __('Enter one of your recovery codes if you cannot access your authenticator app.') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
<x-form.input id="recovery_code" name="recovery_code" type="text" placeholder="{{ __('Recovery Code') }}" class="mb-3" />
|
||||
</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.secondary-button type="submit" class="mt-4">
|
||||
{{ __('Verify') }}
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<x-guest-layout>
|
||||
<div class="flex min-h-screen flex-col items-center bg-[var(--color-surface)] pt-6 sm:justify-center sm:pt-0">
|
||||
<div class="mt-6 w-full overflow-hidden bg-white px-6 py-4 shadow-md sm:max-w-md sm:rounded-lg">
|
||||
<div class="mb-4 text-sm text-[var(--color-text-muted)]">
|
||||
{{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you did not receive the email, we will gladly send you another.') }}
|
||||
</div>
|
||||
|
||||
@if (session('status') == 'verification-link-sent')
|
||||
<div class="mb-4 text-sm font-medium text-green-600">
|
||||
{{ __('A new verification link has been sent to the email address you provided during registration.') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<form method="POST" action="{{ route('verification.send') }}">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="ml-4 inline-flex items-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-xs font-semibold uppercase tracking-widest text-white ring-gray-300 transition duration-150 ease-in-out hover:bg-gray-700 focus:border-gray-900 focus:outline-hidden focus:ring-3 active:bg-gray-900 disabled:opacity-25">
|
||||
{{ __('Resend Verification Email') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
|
||||
<button type="submit" class="text-sm text-[var(--color-text-muted)] underline hover:text-[var(--color-text)]">
|
||||
{{ __('Log Out') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-guest-layout>
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Banned'))
|
||||
<div class="col-span-12 flex justify-center text-[var(--color-text)]">
|
||||
<div class="space-y-4 lg:w-1/2">
|
||||
<div class="w-full rounded-md bg-red-500 p-2 text-center text-[var(--color-text)]">
|
||||
{{ __('It seems like you are banned off :hotel', ['hotel' => setting('hotel_name')]) }}
|
||||
</div>
|
||||
|
||||
<div class="rounded-md p-2 shadow-sm bg-white dark:bg-[var(--color-surface)]">
|
||||
<div class="flex justify-between">
|
||||
<div class="flex flex-col px-1">
|
||||
<div class="max-w-[380px]">
|
||||
<p>
|
||||
<strong>{{ __('Ban type:') }}</strong> {{ $ban->type }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>{{ __('Ban reason:') }}</strong> {{ $ban->ban_reason }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>{{ __('Ban expiration:') }}</strong> {{ date('Y/m/d', $ban->ban_expire) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 max-w-[380px]">
|
||||
<p class="mb-4">
|
||||
{{ __('If you believe this is a mistake, please reach out to one of our staff members through our Discord server!') }}
|
||||
</p>
|
||||
|
||||
<a href="{{ is_array(setting('discord_invitation_link')) ? '' : setting('discord_invitation_link') }}" target="_blank">
|
||||
<x-form.primary-button>
|
||||
{{ __('Join discord') }}
|
||||
</x-form.primary-button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mr-8 hidden items-center lg:flex">
|
||||
<img src="{{ asset('assets/images/angry_frank.png') }}" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>{{ setting('hotel_name') }} - {{ __('Client') }}</title>
|
||||
|
||||
<script src="{{ asset('assets/js/jquery-latest.js') }}" type="text/javascript"></script>
|
||||
<script src="{{ asset('assets/js/jquery-ui.js') }}" type="text/javascript"></script>
|
||||
<script src="{{ asset('assets/js/flashclient.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ asset('assets/js/swfobject.js') }}"></script>
|
||||
|
||||
<link rel="stylesheet" href="{{ asset('assets/css/clientnew.css') }}" type="text/css">
|
||||
<link rel="stylesheet" href="{{ asset('assets/css/no-flash.css') }}" type="text/css">
|
||||
|
||||
<script type="text/javascript">
|
||||
var flashvars = {
|
||||
"connection.info.host": "{{ config('habbo.flash.host') }}",
|
||||
"connection.info.port": "{{ config('habbo.flash.port') }}",
|
||||
"site.url": "",
|
||||
"url.prefix": "",
|
||||
"client.reload.url": "",
|
||||
"client.fatal.error.url": "",
|
||||
"client.connection.failed.url": "",
|
||||
"logout.url": "",
|
||||
"client.starting": "Please wait! Habbo is starting up.",
|
||||
"client.starting.revolving": "For science, you monster\/Loading funny message\u2026please wait.\/Would you like fries with that?\/Follow the yellow duck.\/Time is just an illusion.\/Are we there yet?!\/I like your t-shirt.\/Look left. Look right. Blink twice. Ta da!\/It\'s not you, it\'s me.\/Shhh! I\'m trying to think here.\/Loading pixel universe.",
|
||||
"client.notify.cross.domain": "1",
|
||||
"client.allow.cross.domain": "1",
|
||||
"flash.client.origin": "popup",
|
||||
"processlog.enabled": "0",
|
||||
"sso.ticket": "{{ $sso }}",
|
||||
"productdata.load.url": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.external_productdata')) }}",
|
||||
"furnidata.load.url": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.external_furnidata')) }}",
|
||||
"external.texts.txt": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.external_texts')) }}",
|
||||
"external.variables.txt": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.external_variables')) }}",
|
||||
"external.figurepartlist.txt": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.external_figuredata')) }}",
|
||||
"flash.dynamic.avatar.download.configuration": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.external_figuremap')) }}",
|
||||
"external.override.texts.txt": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.external_override_texts')) }}",
|
||||
"external.override.variables.txt": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.external_override_variables')) }}",
|
||||
"flash.client.url": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.production_folder')) }}/",
|
||||
};
|
||||
|
||||
window.FlashExternalInterface.disconnect = function() {
|
||||
window.location.href = "{{ route('me.show') }}";
|
||||
};
|
||||
|
||||
var params = {
|
||||
"base": "{{ sprintf('%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.production_folder')) }}/",
|
||||
"allowScriptAccess": "always",
|
||||
"menu": "false",
|
||||
"wmode": "opaque"
|
||||
};
|
||||
|
||||
swfobject.embedSWF(
|
||||
'{{ sprintf('%s/%s/%s/%s', config('habbo.site.site_url'), config('habbo.flash.swf_base_path'), config('habbo.flash.production_folder'), config('habbo.flash.habbo_swf')) }}',
|
||||
'client', '100%', '100%', '11.1.0', '{{ asset('assets/js/expressInstall.swf') }}', flashvars, params, null,
|
||||
null);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="client">
|
||||
<habbo-client-error>
|
||||
<div class="client-error__background-frank">
|
||||
<div class="client-error__text-contents">
|
||||
<h1 class="client-error__title">{{ __('You are nearly in Habbo!') }}</h1>
|
||||
<p>{{ __('Click the yellow Hotel button below, then click on run flash` when prompted to. See you in the Hotel!') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="client-error__hotel-button-div">
|
||||
<a href="https://www.adobe.com/go/getflashplayer" target="_blank" rel="noopener noreferrer"
|
||||
class="hotel-button">
|
||||
<span class="hotel-button__text">{{ __('Get flash') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</habbo-client-error>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+687
@@ -0,0 +1,687 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>{{ setting('hotel_name') }} - Nitro</title>
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Ubuntu+Condensed&display=swap" rel="stylesheet">
|
||||
|
||||
@vite(['resources/themes/' . setting('theme') . '/css/app.css', 'resources/themes/' . setting('theme') . '/js/app.js'], 'build')
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--toolbar__primary__color: {{ setting('toolbar_primary_color', setting('button_primary_color', '#eeb425')) }};
|
||||
--toolbar__hover__color: {{ setting('toolbar_hover_color', setting('button_hover_color', '#cf9d15')) }};
|
||||
--toolbar__border__color: {{ setting('toolbar_border_color', setting('button_border_color', '#cf9d15')) }};
|
||||
--toolbar__text__color: {{ setting('toolbar_text_color', setting('button_text_color', '#1a1a2e')) }};
|
||||
}
|
||||
.toolbar { --toolbar__primary__color: var(--toolbar__primary__color); --toolbar__hover__color: var(--toolbar__hover__color); --toolbar__border__color: var(--toolbar__border__color); --toolbar__text__color: var(--toolbar__text__color); }
|
||||
--color-primary: {{ setting('color_primary', '#eeb425') }};
|
||||
--color-background: {{ setting('color_background', '#1a1a2e') }};
|
||||
--button-color: {{ setting('button_primary_color', '#eeb425') }};
|
||||
--button-text-color: {{ setting('button_text_color', '#1a1a2e') }};
|
||||
--button-secondary-color: {{ setting('button_secondary_color', '#22c55e') }};
|
||||
--button-secondary-text-color: {{ setting('button_secondary_text_color', '#ffffff') }};
|
||||
--button-secondary-hover-color: {{ setting('button_secondary_hover_color', '#16a34a') }};
|
||||
--button-secondary-border-color: {{ setting('button_secondary_border_color', '#16a34a') }};
|
||||
--button-danger-color: {{ setting('button_danger_color', '#ef4444') }};
|
||||
--button-danger-text-color: {{ setting('button_danger_text_color', '#ffffff') }};
|
||||
--button-danger-hover-color: {{ setting('button_danger_hover_color', '#dc2626') }};
|
||||
--button-danger-border-color: {{ setting('button_danger_border_color', '#dc2626') }};
|
||||
--button-outline-color: {{ setting('button_outline_color', '#eeb425') }};
|
||||
--button-outline-text-color: {{ setting('button_outline_text_color', '#1a1a2e') }};
|
||||
--button-outline-hover-color: {{ setting('button_outline_hover_color', '#cf9d15') }};
|
||||
}
|
||||
</style>
|
||||
|
||||
@if(setting('button_enabled') == '1')
|
||||
<style>
|
||||
.cms-button {
|
||||
background-color: {{ setting('toolbar_primary_color', setting('button_primary_color', '#eeb425')) }} !important;
|
||||
background: {{ setting('toolbar_primary_color', setting('button_primary_color', '#eeb425')) }} !important;
|
||||
color: {{ setting('toolbar_text_color', setting('button_text_color', '#1a1a2e')) }} !important;
|
||||
border: {{ setting('button_border_width', '2') }}px solid {{ setting('toolbar_border_color', setting('button_border_color', '#cf9d15')) }} !important;
|
||||
border-radius: {{ setting('button_border_radius', '8') }}px !important;
|
||||
transition: {{ setting('button_transition', 'all') }} {{ setting('button_transition_duration', '300') }}ms !important;
|
||||
}
|
||||
.cms-button:hover {
|
||||
background-color: {{ setting('toolbar_hover_color', setting('button_hover_color', '#cf9d15')) }} !important;
|
||||
background: {{ setting('toolbar_hover_color', setting('button_hover_color', '#cf9d15')) }} !important;
|
||||
transform: scale({{ setting('button_hover_scale', '1.05') }});
|
||||
}
|
||||
.toolbar-btn.cms-button {
|
||||
padding: 8px !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: {{ setting('toolbar_primary_color', setting('button_primary_color', '#eeb425')) }} !important;
|
||||
background: {{ setting('toolbar_primary_color', setting('button_primary_color', '#eeb425')) }} !important;
|
||||
}
|
||||
.toolbar-btn.cms-button svg {
|
||||
stroke: {{ setting('toolbar_text_color', setting('button_text_color', '#1a1a2e')) }} !important;
|
||||
}
|
||||
.toolbar-btn.cms-button:hover {
|
||||
background-color: {{ setting('toolbar_hover_color', setting('button_hover_color', '#cf9d15')) }} !important;
|
||||
background: {{ setting('toolbar_hover_color', setting('button_hover_color', '#cf9d15')) }} !important;
|
||||
}
|
||||
</style>
|
||||
@endif
|
||||
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Toolbar Container */
|
||||
.toolbar {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Standard Button Style - ALL buttons same size */
|
||||
.toolbar-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 2px solid {{ setting('toolbar_border_color', setting('button_border_color', '#cf9d15')) }};
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, {{ setting('toolbar_primary_color', setting('button_primary_color', '#eeb425')) }} 0%, {{ setting('toolbar_hover_color', setting('button_hover_color', '#cf9d15')) }} 100%);
|
||||
color: {{ setting('toolbar_text_color', setting('button_text_color', '#1a1a2e')) }};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.toolbar-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
/* Radio Bar - Same height as buttons */
|
||||
.radio-container {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 2px solid {{ setting('toolbar_border_color', setting('button_border_color', '#cf9d15')) }};
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, {{ setting('toolbar_primary_color', setting('button_primary_color', '#eeb425')) }} 0%, {{ setting('toolbar_hover_color', setting('button_hover_color', '#cf9d15')) }} 100%);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
overflow: hidden;
|
||||
transition: width 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.radio-container.expanded {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.radio-toggle {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
min-width: 36px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: {{ setting('button_text_color', '#1a1a2e') }};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.radio-status-dot {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #10b981;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.radio-status-dot.live {
|
||||
background: #ef4444;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.radio-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 0 8px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.radio-container.expanded .radio-content {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.radio-divider {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: rgba(26, 26, 46, 0.3);
|
||||
}
|
||||
|
||||
.radio-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.radio-dj-avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(26, 26, 46, 0.3);
|
||||
background: rgba(26, 26, 46, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.radio-dj-avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.radio-dj-name {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: var(--button-text-color);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.radio-song {
|
||||
font-size: 9px;
|
||||
color: var(--button-text-color);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.radio-control-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: #1a1a2e;
|
||||
color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.radio-volume {
|
||||
width: 50px;
|
||||
height: 4px;
|
||||
background: rgba(26, 26, 46, 0.3);
|
||||
border-radius: 2px;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-volume::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: #1a1a2e;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-listeners {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
color: var(--button-text-color);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* DJ Avatar Popup */
|
||||
.dj-popup {
|
||||
position: fixed;
|
||||
bottom: 100px;
|
||||
right: 24px;
|
||||
background: linear-gradient(145deg, rgba(20, 20, 30, 0.98) 0%, rgba(10, 10, 20, 0.99) 100%);
|
||||
border: 2px solid var(--color-primary);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
|
||||
z-index: 10000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
min-width: 280px;
|
||||
transform: translateX(400px);
|
||||
opacity: 0;
|
||||
transition: all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
}
|
||||
|
||||
.dj-popup.show {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dj-popup-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid var(--color-primary);
|
||||
background: var(--color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32px;
|
||||
box-shadow: 0 8px 24px rgba(238, 180, 37, 0.4);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dj-popup-avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.dj-popup-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.dj-popup-label {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.dj-popup-name {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.dj-popup-show {
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.dj-popup-close {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.dj-popup-close:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dj-popup-indicator {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: -4px;
|
||||
right: -4px;
|
||||
bottom: -4px;
|
||||
border: 2px solid #10b981;
|
||||
border-radius: 18px;
|
||||
animation: djPopupPulse 2s infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes djPopupPulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.5; transform: scale(1.02); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="overflow-hidden" id="nitro-client">
|
||||
{{-- Toolbar --}}
|
||||
<div class="toolbar">
|
||||
{{-- Home Button --}}
|
||||
<a href="{{ route('me.show') }}" class="toolbar-btn cms-button" data-turbolinks="false" title="Home">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
|
||||
<polyline points="9 22 9 12 15 12 15 22"></polyline>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
{{-- Reload Button --}}
|
||||
<button class="toolbar-btn cms-button" onclick="location.reload()" title="Herladen">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="23 4 23 10 17 10"></polyline>
|
||||
<polyline points="1 20 1 14 7 14"></polyline>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{{-- Fullscreen Button --}}
|
||||
<button class="toolbar-btn cms-button" onclick="toggleFullscreen()" title="Fullscreen">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{{-- Online Count Button --}}
|
||||
<button class="toolbar-btn cms-button" title="Online gebruikers">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
<span id="onlineCount" style="margin-left: 4px; font-size: 11px; font-weight: 700;">0</span>
|
||||
</button>
|
||||
|
||||
|
||||
</button>
|
||||
|
||||
{{-- Radio Player --}}
|
||||
<div id="radioContainer" class="radio-container">
|
||||
<button class="radio-toggle" onclick="toggleRadioBar()" title="Radio">
|
||||
<div class="radio-status-dot" id="radioStatus"></div>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M9 18V5l12-2v13"></path>
|
||||
<circle cx="6" cy="18" r="3"></circle>
|
||||
<circle cx="18" cy="16" r="3"></circle>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="radio-content">
|
||||
<div class="radio-divider"></div>
|
||||
|
||||
<div class="radio-info">
|
||||
<div class="radio-dj-avatar" id="radioDjAvatar">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column;">
|
||||
<span class="radio-dj-name" id="radioDj">{{ __('radio.music') }}</span>
|
||||
<span class="radio-song" id="radioSong">{{ __('radio.loading') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="radio-divider"></div>
|
||||
|
||||
<button class="radio-control-btn" onclick="toggleRadioPlay()" title="Play/Pause">
|
||||
<svg id="playIcon" width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
|
||||
<polygon points="5 3 19 12 5 21 5 3"/>
|
||||
</svg>
|
||||
<svg id="pauseIcon" width="12" height="12" viewBox="0 0 24 24" fill="currentColor" class="hidden">
|
||||
<rect x="6" y="4" width="4" height="16"/>
|
||||
<rect x="14" y="4" width="4" height="16"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="radio-divider"></div>
|
||||
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#1a1a2e" stroke-width="2">
|
||||
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
|
||||
</svg>
|
||||
<input type="range" class="radio-volume" id="radioVolume" min="0" max="100" value="50" onchange="setRadioVolume(this.value)">
|
||||
|
||||
<div class="radio-divider"></div>
|
||||
|
||||
<div class="radio-listeners">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#1a1a2e" stroke-width="2">
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="9" cy="7" r="4"/>
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
||||
</svg>
|
||||
<span id="radioListeners">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Nitro Client --}}
|
||||
@php
|
||||
$nitroUrl = sprintf('%s/index.html?sso=%s', setting('nitro_path'), $sso);
|
||||
$toolbarParams = [];
|
||||
// Toolbar with fallback to button colors (don't URL encode - # is valid in URLs)
|
||||
$toolbarParams[] = 'toolbar_primary=' . (setting('toolbar_primary_color') ?: setting('button_primary_color', '#eeb425'));
|
||||
$toolbarParams[] = 'toolbar_hover=' . (setting('toolbar_hover_color') ?: setting('button_hover_color', '#cf9d15'));
|
||||
$toolbarParams[] = 'toolbar_border=' . (setting('toolbar_border_color') ?: setting('button_border_color', '#cf9d15'));
|
||||
$toolbarParams[] = 'toolbar_text=' . (setting('toolbar_text_color') ?: setting('button_text_color', '#1a1a2e'));
|
||||
if(count($toolbarParams)) $nitroUrl .= '&' . implode('&', $toolbarParams);
|
||||
@endphp
|
||||
<iframe id="nitro" src="{{ $nitroUrl }}"
|
||||
class="absolute top-0 left-0 m-0 h-full w-full overflow-hidden border-none p-0"></iframe>
|
||||
|
||||
{{-- Disconnected Message --}}
|
||||
<div id="disconnected" class="hidden" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 10000;">
|
||||
<div style="position: absolute; width: 100%; height: 100%; background: rgba(0,0,0,0.7);"></div>
|
||||
<div style="position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; gap: 16px;">
|
||||
<h2 style="font-size: 24px; color: white;">{{ __('Verbinding verbroken') }}</h2>
|
||||
<div style="display: flex; gap: 16px;">
|
||||
<button onclick="location.reload()" style="padding: 12px 24px; background: var(--color-primary); border: 2px solid var(--color-primary); border-radius: 6px; color: var(--button-text-color); font-weight: 700; cursor: pointer;">
|
||||
{{ __('Herladen') }}
|
||||
</button>
|
||||
<a href="{{ route('me.show') }}" style="padding: 12px 24px; background: transparent; border: 2px solid var(--color-primary); border-radius: 6px; color: var(--color-primary); text-decoration: none; font-weight: 700;">
|
||||
{{ __('Terug') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- DJ Avatar Popup --}}
|
||||
@if(setting('radio_show_dj_avatar_popup'))
|
||||
<div id="djPopup" class="dj-popup">
|
||||
<div class="dj-popup-indicator"></div>
|
||||
<button class="dj-popup-close" onclick="hideDjPopup()">✕</button>
|
||||
<div class="dj-popup-avatar" id="djPopupAvatar">
|
||||
🎵
|
||||
</div>
|
||||
<div class="dj-popup-info">
|
||||
<div class="dj-popup-label">Nu Live</div>
|
||||
<div class="dj-popup-name" id="djPopupName">DJ Name</div>
|
||||
<div class="dj-popup-show" id="djPopupShow">Show Name</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<audio id="radioAudio" preload="none"></audio>
|
||||
|
||||
<script>
|
||||
// Fullscreen
|
||||
function toggleFullscreen() {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
} else {
|
||||
document.documentElement.requestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
// Online count
|
||||
function updateOnlineCount() {
|
||||
fetch("{{ route('api.online-count') }}")
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById('onlineCount').textContent = data.data?.onlineCount || 0;
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
setInterval(updateOnlineCount, 15000);
|
||||
updateOnlineCount();
|
||||
|
||||
// Radio
|
||||
let radioPlaying = false;
|
||||
let radioStreamUrl = null;
|
||||
let radioExpanded = false;
|
||||
const radioAudio = document.getElementById('radioAudio');
|
||||
|
||||
function toggleRadioBar() {
|
||||
const container = document.getElementById('radioContainer');
|
||||
radioExpanded = !radioExpanded;
|
||||
|
||||
if (radioExpanded) {
|
||||
container.classList.add('expanded');
|
||||
if (!radioStreamUrl) loadRadio();
|
||||
} else {
|
||||
container.classList.remove('expanded');
|
||||
}
|
||||
}
|
||||
|
||||
function loadRadio() {
|
||||
fetch("{{ route('api.radio.config') }}")
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.enabled && data.stream_url) {
|
||||
radioStreamUrl = data.stream_url;
|
||||
radioAudio.src = radioStreamUrl;
|
||||
document.getElementById('radioSong').textContent = 'Gereed';
|
||||
|
||||
fetch("{{ route('api.settings.radio.auto-play') }}")
|
||||
.then(r => r.json())
|
||||
.then(settings => {
|
||||
if (settings.auto_play) toggleRadioPlay();
|
||||
});
|
||||
} else {
|
||||
document.getElementById('radioSong').textContent = 'Offline';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
document.getElementById('radioSong').textContent = 'Error';
|
||||
});
|
||||
}
|
||||
|
||||
function toggleRadioPlay() {
|
||||
if (!radioStreamUrl) {
|
||||
loadRadio();
|
||||
return;
|
||||
}
|
||||
|
||||
const playIcon = document.getElementById('playIcon');
|
||||
const pauseIcon = document.getElementById('pauseIcon');
|
||||
|
||||
if (radioPlaying) {
|
||||
radioAudio.pause();
|
||||
playIcon.classList.remove('hidden');
|
||||
pauseIcon.classList.add('hidden');
|
||||
radioPlaying = false;
|
||||
} else {
|
||||
radioAudio.volume = document.getElementById('radioVolume').value / 100;
|
||||
radioAudio.play()
|
||||
.then(() => {
|
||||
playIcon.classList.add('hidden');
|
||||
pauseIcon.classList.remove('hidden');
|
||||
radioPlaying = true;
|
||||
})
|
||||
.catch(() => {
|
||||
document.getElementById('radioSong').textContent = 'Fout';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setRadioVolume(val) {
|
||||
radioAudio.volume = val / 100;
|
||||
localStorage.setItem('radioVol', val);
|
||||
}
|
||||
|
||||
const savedVol = localStorage.getItem('radioVol');
|
||||
if (savedVol) {
|
||||
document.getElementById('radioVolume').value = savedVol;
|
||||
}
|
||||
|
||||
// Update radio info
|
||||
function updateRadioInfo() {
|
||||
if (!radioStreamUrl) return;
|
||||
|
||||
fetch("{{ route('api.radio.config') }}")
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const defaultLook = '{{ setting("radio_default_avatar_figure", "hr-893-45.hd-180-2.ch-210-62.lg-285-62.sh-295-62.ha-1012-62.wa-2007-0") }}';
|
||||
const defaultName = '{{ setting("radio_default_dj_name", "Frank") }}';
|
||||
const avatarUrl = "{{ setting('avatar_imager') }}" + (data.dj?.look || defaultLook) + '&headonly=1';
|
||||
|
||||
const avatarEl = document.getElementById('radioDjAvatar');
|
||||
if (data.dj?.look) {
|
||||
avatarEl.innerHTML = '<img src="' + avatarUrl + '" alt="DJ">';
|
||||
} else {
|
||||
avatarEl.innerHTML = '<img src="' + avatarUrl + '" alt="' + defaultName + '">';
|
||||
}
|
||||
|
||||
const dj = data.dj ? (typeof data.dj === 'object' ? data.dj.username : data.dj) : defaultName;
|
||||
document.getElementById('radioDj').textContent = dj;
|
||||
|
||||
const status = document.getElementById('radioStatus');
|
||||
if (data.dj) {
|
||||
status.classList.add('live');
|
||||
} else {
|
||||
status.classList.remove('live');
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
fetch("{{ route('api.radio.now-playing') }}")
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const song = data.song || data.title || (data.now_playing?.title) || 'Live';
|
||||
document.getElementById('radioSong').textContent = song;
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
fetch("{{ route('api.radio.listeners') }}")
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById('radioListeners').textContent = data.count || '0';
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
setInterval(updateRadioInfo, 10000);
|
||||
|
||||
// Init
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
loadRadio();
|
||||
updateRadioInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="{{ asset('assets/js/atom.js') }}"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
<x-app-layout>
|
||||
@push('title', $article->title)
|
||||
|
||||
<div class="col-span-12 rounded space-y-3 md:col-span-3">
|
||||
<div
|
||||
class="relative mt-6 h-24 w-full overflow-hidden rounded border bg-white shadow-sm dark:border-gray-900 dark:bg-gray-800 md:mt-0">
|
||||
<div
|
||||
class="absolute top-1 right-1 rounded bg-white px-2 text-sm font-semibold dark:bg-gray-700 dark:text-gray-100">
|
||||
{{ $article->user && !$article->user->hidden_staff ? $article->user->permission->rank_name ?? 'Member' : 'Member' }}
|
||||
</div>
|
||||
|
||||
<div class="h-[65%] w-full staff-bg"
|
||||
style="background: rgba(0, 0, 0, 0.5) url({{ asset(sprintf('assets/images/%s', $article->user ? $article->user->permission->staff_background ?? 'staff-bg.png' : 'staff-bg.png')) }});">
|
||||
</div>
|
||||
|
||||
<a href="{{ route('profile.show', $article->user ?? \App\Models\User::first()) }}" class="absolute top-4 left-1 ">
|
||||
<img style="image-rendering: pixelated;" class="transition duration-300 ease-in-out hover:scale-105"
|
||||
src="{{ setting('avatar_imager') }}{{ $article->user?->look }}&direction=2&head_direction=3&gesture=sml&action=wav"
|
||||
alt="">
|
||||
</a>
|
||||
|
||||
<p class="text-2xl font-semibold ml-[70px] text-white -mt-[35px] ">
|
||||
{{ $article->user->username ?? setting('hotel_name') }}
|
||||
</p>
|
||||
|
||||
<div class="flex w-full items-center justify-between px-4">
|
||||
<p class="ml-[57px] text-sm mt-[10px] font-semibold text-gray-500 ">
|
||||
{{ $article->user->motto ?? setting('start_motto') }}
|
||||
</p>
|
||||
|
||||
@if($article->user)
|
||||
<div class="w-4 h-4 rounded-full mt-2 {{ $article->user->online ? 'bg-green-600' : 'bg-red-600' }}">
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-content.content-card icon="article-icon" classes="border dark:bg-gray-800 dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Other articles') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Our most recent articles') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="flex flex-col gap-y-2">
|
||||
@forelse($otherArticles as $art)
|
||||
<a href="{{ route('article.show', $art->slug) }}"
|
||||
style="background: rgba(0, 0, 0, 0.5) url({{ $art->image }}) center;"
|
||||
class="w-full rounded h-12 bg-blue-200 transition ease-in-out duration-200 hover:scale-[103%] text-white flex justify-center items-center font-bold recent-articles">
|
||||
{{ Str::limit($art->title, 20) }}
|
||||
</a>
|
||||
@empty
|
||||
<p class="dark:text-gray-400">
|
||||
{{ __('There is currently no other articles') }}
|
||||
</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 space-y-4 md:col-span-9">
|
||||
<div
|
||||
class="relative flex flex-col gap-y-8 overflow-hidden rounded bg-white p-3 shadow-sm dark:bg-gray-800 dark:text-gray-300">
|
||||
<div class="relative flex h-24 flex-col items-center justify-center gap-y-1 overflow-hidden rounded px-2"
|
||||
style="background: url(/storage/{{ $article->image }}) center; background-size: cover; color: var(--color-text);">
|
||||
<div class="absolute h-full w-full bg-black/50"></div>
|
||||
|
||||
<p class="relative w-full truncate text-center text-xl font-semibold lg:text-2xl xl:text-3xl " style="color: white;">
|
||||
{{ $article->title }}</p>
|
||||
<p class="relative w-full truncate text-center " style="color: rgba(255,255,255,0.9);">{{ $article->short_story }}</p>
|
||||
</div>
|
||||
|
||||
<div class="px-2" id="article-content">
|
||||
{!! $article->full_story !!}
|
||||
</div>
|
||||
|
||||
<div class="w-full h-10 lg:h-1/2 py-1 flex gap-1 items-center justify-start flex-wrap">
|
||||
@forelse ($article->tags as $tag)
|
||||
<span @class([
|
||||
"text-xs font-medium rounded-lg px-2",
|
||||
"text-slate-600" => $tag->background_color,
|
||||
"text-white" => $tag->background_color
|
||||
]) style="background-color: {{ $tag->background_color }}">{{ $tag->name }}</span>
|
||||
@empty
|
||||
<span @class([
|
||||
"text-xs font-medium rounded-lg px-2",
|
||||
"text-slate-600",
|
||||
"text-white"
|
||||
]) style="background-color: #000000">{{ __('No tags found.') }}</span>
|
||||
</span>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@include('community.partials.article-reactions')
|
||||
</div>
|
||||
|
||||
@if ($article->can_comment)
|
||||
@if (auth()->check() && !$article->userHasReachedArticleCommentLimit())
|
||||
<x-content.content-card icon="hotel-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Post a comment') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Post a comment on the article, to let us know what you think about it') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm dark:text-gray-200">
|
||||
<form action="{{ route('article.comment.store', $article) }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<textarea name="comment"
|
||||
class="focus:ring-0 border-2 border-gray-200 rounded dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 focus:border-[var(--color-primary)] w-full min-h-[100px] max-h-[100px] @error('comment') border-red-600 ring-red-500 @enderror"></textarea>
|
||||
|
||||
<x-form.primary-button classes="mt-2">
|
||||
{{ __('Post comment') }}
|
||||
</x-form.primary-button>
|
||||
</form>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endif
|
||||
|
||||
<x-content.content-card icon="hotel-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Comments') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Below you will see all the comments, written on this article') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-1 dark:text-gray-200 space-y-[13px]">
|
||||
@foreach ($article->comments as $comment)
|
||||
<div
|
||||
class="relative w-full rounded bg-[#f5f5f5] dark:bg-gray-700 p-4 h-[90px] overflow-hidden flex items-center shadow-sm">
|
||||
<a href="{{ route('profile.show', $comment->user) }}"
|
||||
class="absolute top-2 left-1 ">
|
||||
<img style="image-rendering: pixelated;"
|
||||
class="transition duration-300 ease-in-out hover:scale-105"
|
||||
src="{{ setting('avatar_imager') }}{{ $comment->user->look }}&direction=2&head_direction=3&gesture=sml&action=wav"
|
||||
alt="">
|
||||
</a>
|
||||
|
||||
<div class="flex justify-between ml-[60px] w-full">
|
||||
<div class="text-sm">
|
||||
<a href="{{ route('profile.show', $comment->user) }}"
|
||||
class="font-semibold text-[#89cdf0] dark:text-blue-300 hover:underline">
|
||||
{{ $comment->user->username }}
|
||||
</a>
|
||||
|
||||
<p class="block dark:text-gray-200">
|
||||
{{ $comment->comment }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-x-2">
|
||||
<p class="text-xs dark:text-gray-200">
|
||||
{{ $comment->created_at->diffForHumans() }}
|
||||
</p>
|
||||
|
||||
@if ($comment->canBeDeleted())
|
||||
<form action="{{ route('article.comment.destroy', $comment) }}" method="POST"
|
||||
class="cursor-pointer transition duration-200 ease-in-out hover:scale-105">
|
||||
@method('DELETE')
|
||||
@csrf
|
||||
|
||||
<button type="submit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
||||
class="h-4 w-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endif
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,17 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Articles'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
||||
@forelse($articles as $article)
|
||||
<x-article-card :article="$article" />
|
||||
@empty
|
||||
<x-filler-article-card />
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
{{ $articles->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,76 @@
|
||||
<x-modals.modal-wrapper>
|
||||
<div class="select-none"
|
||||
x-data='reactions(@json($myReactions), @json($articleReactions), "{{ route('article.toggle-reaction', $article->slug) }}")'>
|
||||
<div class="mt-4 flex w-full flex-wrap gap-2 rounded-lg bg-gray-100 p-2 dark:bg-gray-900">
|
||||
<div x-show="isAuthenticated"
|
||||
class="px-2 hover:scale-110 transition-all font-semibold h-8 flex items-center justify-center border-2 text-xs border-[var(--color-primary)] cursor-pointer bg-[var(--color-primary)] text-white rounded-lg"
|
||||
x-on:click="open = true">
|
||||
{{ __('Add') }}
|
||||
</div>
|
||||
|
||||
<template x-for="articleReaction in articleReactions">
|
||||
<div>
|
||||
<div class="flex h-8 w-12 items-center justify-center gap-2 rounded-lg border-2 border-gray-300 text-sm font-bold dark:border-gray-800"
|
||||
:class="{
|
||||
'bg-gray-300 dark:bg-gray-800 dark:border-gray-700': userHasReaction(
|
||||
articleReaction),
|
||||
'cursor-pointer hover:bg-gray-200 hover:scale-110 transition-all dark:hover:bg-gray-700': isAuthenticated
|
||||
}"
|
||||
@click="toggleReaction(articleReaction.name)" :data-popover-target="articleReaction.id">
|
||||
<img :src="'/assets/images/icons/reactions/' + articleReaction.name + '.png'"
|
||||
:alt="articleReaction.name">
|
||||
<span x-text="articleReaction.count"></span>
|
||||
</div>
|
||||
|
||||
<div data-popover :id="articleReaction.id" role="tooltip"
|
||||
class="invisible absolute z-10 inline-block w-64 rounded-lg border border-gray-200 bg-white text-sm font-light text-gray-500 opacity-0 shadow-xs transition-opacity duration-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400">
|
||||
<div
|
||||
class="rounded-t-lg border-b border-gray-200 bg-gray-100 px-3 py-2 dark:border-gray-600 dark:bg-gray-700">
|
||||
<div
|
||||
class="flex w-full items-center justify-center font-semibold text-gray-900 dark:text-white">
|
||||
{{ __('Reactions with') }} <img
|
||||
:src="'/assets/images/icons/reactions/' + articleReaction.name + '.png'"
|
||||
class="ml-1" :alt="articleReaction.name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto px-3 py-2" style="max-height: 200px">
|
||||
<template x-for="user in articleReaction.users">
|
||||
<p class="w-full text-center" x-text="user"></p>
|
||||
</template>
|
||||
</div>
|
||||
<div data-popper-arrow></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div x-show="isAuthenticated">
|
||||
<x-modals.regular-modal>
|
||||
<x-slot name="title">
|
||||
<h2 class="text-2xl">
|
||||
{{ __('Insert Reaction') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="flex w-full flex-wrap justify-center gap-3 p-2">
|
||||
<template x-for="defaultReaction in allReactions">
|
||||
<div class="cursor-pointer rounded-lg border-2 border-gray-300 px-3 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 dark:hover:border-g dark:border-gray-800"
|
||||
x-show="canAddReactionFromModal(defaultReaction)" @click="toggleReaction(defaultReaction)">
|
||||
<img :src="'/assets/images/icons/reactions/' + defaultReaction + '.png'"
|
||||
:alt="defaultReaction">
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</x-modals.regular-modal>
|
||||
</div>
|
||||
</div>
|
||||
</x-modals.modal-wrapper>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
window.App = {
|
||||
defaultReactions: @json(config('habbo.reactions')),
|
||||
isAuthenticated: @json(auth()->check())
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Photos'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<div class="mb-6 p-6 rounded-xl border-2">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="w-14 h-14 rounded-full flex items-center justify-center shadow-lg" style="background-color: var(--color-primary);">
|
||||
<svg class="w-8 h-8" style="color: var(--button-text-color);" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold" style="color: var(--color-text)">{{ __('Photos') }}</h1>
|
||||
<p style="color: var(--color-text-muted)">Bekijk de beste momenten vastgelegd door gebruikers</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-content.content-card icon="camera-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Photo Gallery') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Klik op een foto om deze te vergroten') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<x-photos :photos="$photos" />
|
||||
</x-content.content-card>
|
||||
|
||||
<div class="mt-6">
|
||||
{{ $photos->links() }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('javascript')
|
||||
<script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@4.0/dist/fancybox.umd.js"></script>
|
||||
@endpush
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css" />
|
||||
</x-app-layout>
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'Word DJ')
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<x-content.content-card icon="star-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Word DJ
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Solliciteer als DJ bij {{ setting('hotel_name') }} Radio!
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
@if (session('success'))
|
||||
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (session('error'))
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
|
||||
<ul class="list-disc list-inside">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($hasPendingApplication)
|
||||
<div class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded mb-4">
|
||||
<p class="font-bold">Je hebt al een openstaande aanmelding!</p>
|
||||
<p>Je kunt pas een nieuwe aanmelding doen als je huidige aanmelding is beoordeeld.</p>
|
||||
</div>
|
||||
@else
|
||||
<p>Heb je passie voor muziek en wil je onze community entertainen? Solliciteer dan nu als DJ!</p>
|
||||
|
||||
<form action="{{ route('radio.apply.store') }}" method="POST" class="space-y-4">
|
||||
@csrf
|
||||
|
||||
<!-- Persoonlijke Gegevens -->
|
||||
<div class="bg-gray-100 dark:bg-gray-800 p-4 rounded">
|
||||
<h3 class="font-bold text-[var(--color-primary)] mb-3 text-lg">Persoonlijke Gegevens</h3>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="real_name" class="block text-sm font-medium dark:text-gray-300 mb-1">
|
||||
Echte Naam <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="real_name"
|
||||
name="real_name"
|
||||
required
|
||||
value="{{ old('real_name') }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||
placeholder="Je echte naam">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="age" class="block text-sm font-medium dark:text-gray-300 mb-1">
|
||||
Leeftijd <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
id="age"
|
||||
name="age"
|
||||
required
|
||||
min="13"
|
||||
max="100"
|
||||
value="{{ old('age') }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||
placeholder="Je leeftijd">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DJ Gegevens -->
|
||||
<div class="bg-gray-100 dark:bg-gray-800 p-4 rounded">
|
||||
<h3 class="font-bold text-[var(--color-primary)] mb-3 text-lg">DJ Gegevens</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="availability" class="block text-sm font-medium dark:text-gray-300 mb-1">
|
||||
Beschikbaarheid <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="availability"
|
||||
name="availability"
|
||||
rows="3"
|
||||
required
|
||||
minlength="10"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||
placeholder="Wanneer kan je draaien? (bijv. maandag 20:00-22:00, woensdag 19:00-21:00)">{{ old('availability') }}</textarea>
|
||||
<p class="text-xs text-gray-500 mt-1">Geef aan op welke dagen en tijden je beschikbaar bent om te draaien.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="experience" class="block text-sm font-medium dark:text-gray-300 mb-1">
|
||||
Ervaring
|
||||
</label>
|
||||
<textarea
|
||||
id="experience"
|
||||
name="experience"
|
||||
rows="3"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||
placeholder="Heb je ervaring als DJ? Zo ja, vertel erover...">{{ old('experience') }}</textarea>
|
||||
<p class="text-xs text-gray-500 mt-1">Heb je eerder als DJ gewerkt? Bijvoorbeeld op andere hotels of radio stations.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="motivation" class="block text-sm font-medium dark:text-gray-300 mb-1">
|
||||
Motivatie <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="motivation"
|
||||
name="motivation"
|
||||
rows="5"
|
||||
required
|
||||
minlength="50"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||
placeholder="Waarom wil je DJ worden bij ons? Vertel ons waarom we jou moeten kiezen...">{{ old('motivation') }}</textarea>
|
||||
<p class="text-xs text-gray-500 mt-1">Minimaal 50 karakters. Wees zo uitgebreid mogelijk!</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="music_style" class="block text-sm font-medium dark:text-gray-300 mb-1">
|
||||
Muziekstijl
|
||||
</label>
|
||||
<textarea
|
||||
id="music_style"
|
||||
name="music_style"
|
||||
rows="2"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||
placeholder="Welke muziekstijl draai je graag? (bijv. Pop, EDM, Hip-Hop, etc.)">{{ old('music_style') }}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="discord_username" class="block text-sm font-medium dark:text-gray-300 mb-1">
|
||||
Discord Gebruikersnaam
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="discord_username"
|
||||
name="discord_username"
|
||||
value="{{ old('discord_username') }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||
placeholder="bijv. gebruiker#1234">
|
||||
<p class="text-xs text-gray-500 mt-1">Voor contact over je aanmelding.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="rank_id" class="block text-sm font-medium dark:text-gray-300 mb-1">
|
||||
Gewenste DJ Rank
|
||||
</label>
|
||||
<select
|
||||
id="rank_id"
|
||||
name="rank_id"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] dark:bg-gray-700 dark:border-gray-600 dark:text-white">
|
||||
<option value="">-- Kies een rank (optioneel) --</option>
|
||||
@foreach ($ranks as $rank)
|
||||
<option value="{{ $rank->id }}" {{ old('rank_id') == $rank->id ? 'selected' : '' }}>
|
||||
{{ $rank->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button type="submit" class="flex-1 bg-[var(--color-primary)] hover:bg-[var(--color-primary)] text-white px-6 py-3 rounded transition font-bold">
|
||||
Verstuur Aanmelding
|
||||
</button>
|
||||
<a href="{{ route('radio.index') }}" class="bg-gray-300 hover:bg-gray-400 text-gray-800 px-6 py-3 rounded transition">
|
||||
Annuleren
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
<x-content.content-card icon="info-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
DJ Info
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Waarom word DJ?
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>Als DJ bij {{ setting('hotel_name') }} Radio krijg je de kans om je favoriete muziek te delen met de community.</p>
|
||||
|
||||
<div class="space-y-2 text-xs">
|
||||
<p><strong class="text-[var(--color-primary)]">Voordelen:</strong></p>
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
<li>Eigen radioshow</li>
|
||||
<li>Leuke community</li>
|
||||
<li>Ervaring opdoen</li>
|
||||
<li>Events draaien</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-gray-500">We zoeken naar gepassioneerde muziekliefhebbers!</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
|
||||
<x-content.content-card icon="musical-note-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Beschikbare Ranks
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
DJ Ranks
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-2 dark:text-gray-200">
|
||||
@forelse ($ranks as $rank)
|
||||
<div class="flex items-center gap-2 p-2 bg-gray-100 dark:bg-gray-800 rounded">
|
||||
@if ($rank->badge_code)
|
||||
<img src="{{ sprintf(setting('badge_imaging_path'), $rank->badge_code) }}" alt="" class="w-6 h-6">
|
||||
@endif
|
||||
<span>{{ $rank->name }}</span>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-gray-500">Momenteel geen ranks beschikbaar.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,54 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'Radio')
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<x-content.content-card icon="music-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ setting('hotel_name') }} Radio
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Radio is momenteel offline
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<div class="bg-gray-100 dark:bg-gray-800 rounded-lg p-8 text-center">
|
||||
<div class="text-6xl mb-4">📻</div>
|
||||
<h2 class="text-xl font-bold text-[var(--color-primary)] mb-2">Radio is Offline</h2>
|
||||
<p class="text-gray-500">Onze radio is momenteel niet beschikbaar.</p>
|
||||
<p class="text-sm text-gray-400 mt-2">Kom later terug om te luisteren naar onze geweldige DJ's!</p>
|
||||
|
||||
<div class="mt-6 space-y-2">
|
||||
<a href="{{ route('radio.rooster') }}" class="block text-[var(--color-primary)] hover:underline">
|
||||
Bekijk het rooster
|
||||
</a>
|
||||
@if(setting('radio_applications_enabled') == '1')
|
||||
<a href="{{ route('radio.apply') }}" class="block text-[var(--color-primary)] hover:underline">
|
||||
Word DJ
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
<x-content.content-card icon="info-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Radio Info
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Over onze radio
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>Onze radio is normaal gesproken 24/7 online met de beste muziek!</p>
|
||||
<p class="text-xs text-gray-500">De radio kan tijdelijk offline zijn voor onderhoud.</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'Radio')
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<x-content.content-card icon="music-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ setting('hotel_name') }} Radio
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Luister live naar onze DJ's!
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
@if($currentDJ && is_array($currentDJ) && isset($currentDJ['username']))
|
||||
@php
|
||||
$currentDJBanner = $banners->first(function($banner) use ($currentDJ) {
|
||||
return $banner->user && $banner->user->username === $currentDJ['username'];
|
||||
});
|
||||
@endphp
|
||||
@if($currentDJBanner)
|
||||
<div class="mb-4 overflow-hidden rounded-lg border-2 border-[var(--color-primary)] shadow-lg shadow-[var(--color-primary)]/20">
|
||||
<div class="relative">
|
||||
<img src="{{ $currentDJBanner->image_url }}" alt="{{ $currentDJBanner->title ?? 'DJ Banner' }}" class="w-full h-56 object-cover">
|
||||
<div class="absolute top-2 left-2">
|
||||
<span class="bg-red-500 text-white px-3 py-1 rounded-full text-sm font-bold animate-pulse">🔴 NU LIVE</span>
|
||||
</div>
|
||||
@if($currentDJBanner->title || $currentDJBanner->description)
|
||||
<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 via-black/50 to-transparent p-4 rounded-b-lg">
|
||||
@if($currentDJBanner->title)
|
||||
<p class="text-white font-bold text-lg ">{{ $currentDJBanner->title }}</p>
|
||||
@endif
|
||||
@if($currentDJBanner->description)
|
||||
<p class="text-white/80 text-sm ">{{ $currentDJBanner->description }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if($banners->count() > 0)
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-bold mb-3" style="color: var(--color-text)">🎧 DJ Banners</h3>
|
||||
<div class="overflow-hidden rounded-lg">
|
||||
<div id="banner-carousel" class="relative">
|
||||
@foreach($banners as $index => $banner)
|
||||
<div class="banner-slide {{ $index === 0 ? 'block' : 'hidden' }}" data-index="{{ $index }}">
|
||||
<div class="relative">
|
||||
<img src="{{ $banner->image_url }}" alt="{{ $banner->title ?? 'DJ Banner' }}" class="w-full h-48 object-cover rounded-lg">
|
||||
@if($banner->title)
|
||||
<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4 rounded-b-lg">
|
||||
<p class="text-white font-bold ">{{ $banner->title }}</p>
|
||||
@if($banner->user)
|
||||
<p class="text-[var(--color-primary)] text-xs mt-1 ">DJ: {{ $banner->user->username }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@if($banners->count() > 1)
|
||||
<div class="flex justify-center gap-2 mt-2">
|
||||
@foreach($banners as $index => $banner)
|
||||
<button class="banner-dot w-2 h-2 rounded-full bg-gray-400 transition-all duration-300 {{ $index === 0 ? 'bg-[var(--color-primary)] w-4' : '' }}" data-index="{{ $index }}"></button>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="bg-black/80 rounded-lg p-4">
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-3">
|
||||
@if($isOnline)
|
||||
<div class="w-3 h-3 rounded-full bg-red-500 animate-pulse"></div>
|
||||
<span class="text-[var(--color-primary)] font-bold">LIVE</span>
|
||||
@else
|
||||
<div class="w-3 h-3 rounded-full bg-gray-500"></div>
|
||||
<span class="text-gray-500 font-bold" style="color: var(--color-text-muted)">OFFLINE</span>
|
||||
@endif
|
||||
</div>
|
||||
@if($isOnline)
|
||||
<div class="flex items-center gap-1 text-xs text-gray-400" id="listeners-container">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3z"/>
|
||||
</svg>
|
||||
<span id="listeners-count">0</span> luisteraars
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($currentDJ && is_array($currentDJ) && isset($currentDJ['username']))
|
||||
<div class="mb-3 p-3 bg-white/10 rounded-lg">
|
||||
<p class="text-xs text-gray-400 mb-1">Nu aan het draaien:</p>
|
||||
<p class="text-[var(--color-primary)] font-bold text-lg">{{ $currentDJ['username'] }}</p>
|
||||
@if(isset($currentDJ['show_name']) && $currentDJ['show_name'])
|
||||
<p class="text-white text-sm ">{{ $currentDJ['show_name'] }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@else
|
||||
<p class="text-gray-400 text-sm mb-3" style="color: var(--color-text-muted)">Geen DJ actief op dit moment</p>
|
||||
@endif
|
||||
|
||||
@if($isOnline)
|
||||
<div id="now-playing-container" class="mb-3 p-2 bg-white/5 rounded">
|
||||
<p class="text-xs text-gray-400 mb-1">Nu aan het spelen:</p>
|
||||
<p id="now-playing-song" class="text-white font-medium truncate ">Laden...</p>
|
||||
<p id="now-playing-artist" class="text-gray-400 text-sm"></p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($isOnline)
|
||||
<div class="flex items-center gap-4">
|
||||
<button id="play-btn" class="bg-[var(--color-primary)] hover:bg-[var(--color-primary)] text-white px-6 py-2 rounded-lg transition flex items-center gap-2">
|
||||
<span id="play-icon">▶</span>
|
||||
<span id="play-text">Play</span>
|
||||
</button>
|
||||
<div class="flex-1 flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<input type="range" id="volume" min="0" max="100" value="50" class="flex-1 h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer">
|
||||
</div>
|
||||
</div>
|
||||
<audio id="radio-audio" preload="none"></audio>
|
||||
@else
|
||||
<div class="text-gray-400 text-sm flex-1 text-center py-2 bg-white/5 rounded">
|
||||
Radio is momenteel offline. We zijn zo terug!
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
|
||||
@if($todaySchedule->count() > 0)
|
||||
<x-content.content-card icon="calendar-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>Vandaag op Radio</x-slot:title>
|
||||
<x-slot:under-title>
|
||||
<a href="{{ route('radio.rooster') }}" class="text-[var(--color-primary)] hover:underline">Bekijk volledig rooster</a>
|
||||
</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-3 dark:text-gray-200">
|
||||
@foreach($todaySchedule as $slot)
|
||||
<div class="flex items-center gap-3 p-3 bg-gray-100 dark:bg-gray-800 rounded-lg {{ $currentDJ && is_array($currentDJ) && isset($currentDJ['username']) && ($currentDJ['username'] ?? '') === $slot->user->username ? 'ring-2 ring-[var(--color-primary)]' : '' }}">
|
||||
<div class="text-center min-w-[60px]">
|
||||
<p class="font-bold text-[var(--color-primary)]">{{ $slot->start_time->format('H:i') }}</p>
|
||||
<p class="text-xs text-gray-500">{{ $slot->end_time->format('H:i') }}</p>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="font-bold">{{ $slot->user->username }}</p>
|
||||
@if($slot->show_name)
|
||||
<p class="text-sm text-gray-400">{{ $slot->show_name }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@if($currentDJ && is_array($currentDJ) && isset($currentDJ['username']) && ($currentDJ['username'] ?? '') === $slot->user->username)
|
||||
<span class="px-2 py-1 bg-red-500 text-white text-xs rounded ">LIVE</span>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endif
|
||||
|
||||
@if($banners->count() > 0)
|
||||
<x-content.content-card icon="photo-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>Alle DJ Banners</x-slot:title>
|
||||
<x-slot:under-title>Ontdek onze DJ's</x-slot:under-title>
|
||||
<div class="px-2">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
@foreach($banners as $banner)
|
||||
<div class="group relative overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700 hover:border-[var(--color-primary)] transition-all duration-300">
|
||||
<img src="{{ $banner->image_url }}" alt="{{ $banner->title ?? 'DJ Banner' }}" class="w-full h-32 object-cover transition-transform duration-300 group-hover:scale-105">
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||
<div class="absolute bottom-0 left-0 right-0 p-3">
|
||||
@if($banner->title)
|
||||
<p class="text-white font-bold text-sm ">{{ $banner->title }}</p>
|
||||
@endif
|
||||
@if($banner->user)
|
||||
<p class="text-[var(--color-primary)] text-xs">DJ: {{ $banner->user->username }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
@if($currentDJ && is_array($currentDJ) && isset($currentDJ['username']))
|
||||
@php
|
||||
$currentDJBanner = $banners->first(function($banner) use ($currentDJ) {
|
||||
return $banner->user && $banner->user->username === $currentDJ['username'];
|
||||
});
|
||||
@endphp
|
||||
<x-content.content-card icon="user-icon" classes="border dark:border-gray-900 bg-gradient-to-br from-[var(--color-primary)]/10 to-transparent">
|
||||
<x-slot:title>Nu aan het Draaien</x-slot:title>
|
||||
<x-slot:under-title>Huidige DJ</x-slot:under-title>
|
||||
<div class="px-2 text-center">
|
||||
@if($currentDJBanner)
|
||||
<img src="{{ $currentDJBanner->image_url }}" alt="{{ $currentDJ['username'] }}" class="w-full h-24 object-cover rounded-lg mb-3">
|
||||
@endif
|
||||
<div class="inline-flex items-center gap-2 mb-2">
|
||||
<span class="w-2 h-2 bg-red-500 rounded-full animate-pulse"></span>
|
||||
<span class="text-red-500 font-bold text-sm">LIVE</span>
|
||||
</div>
|
||||
<p class="text-xl font-bold text-[var(--color-primary)]">{{ $currentDJ['username'] }}</p>
|
||||
@if(isset($currentDJ['show_name']) && $currentDJ['show_name'])
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">{{ $currentDJ['show_name'] }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endif
|
||||
|
||||
<x-content.content-card icon="info-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>Radio Info</x-slot:title>
|
||||
<x-slot:under-title>Over onze radio</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>Luister live naar de beste muziek 24/7! Onze DJ's spelen jouw favoriete tracks.</p>
|
||||
<div class="space-y-2">
|
||||
<a href="{{ route('radio.rooster') }}" class="block text-[var(--color-primary)] hover:underline">📅 Bekijk het rooster</a>
|
||||
@if(setting('radio_applications_enabled') == '1')
|
||||
<a href="{{ route('radio.apply') }}" class="block text-[var(--color-primary)] hover:underline">🎤 Word DJ</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
|
||||
@if($ranks->count() > 0)
|
||||
<x-content.content-card icon="star-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>DJ Ranks</x-slot:title>
|
||||
<x-slot:under-title>Onze DJ niveaus</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-2 dark:text-gray-200">
|
||||
@foreach($ranks as $rank)
|
||||
<div class="flex items-center gap-2 p-2 bg-gray-100 dark:bg-gray-800 rounded">
|
||||
@if($rank->badge_code)
|
||||
<img src="{{ sprintf(setting('badge_imaging_path'), $rank->badge_code) }}" alt="" class="w-6 h-6">
|
||||
@endif
|
||||
<span>{{ $rank->name }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let isPlaying = false;
|
||||
let currentBanner = 0;
|
||||
const banners = document.querySelectorAll('.banner-slide');
|
||||
const bannerDots = document.querySelectorAll('.banner-dot');
|
||||
|
||||
function updateBanner(index) {
|
||||
banners[currentBanner].classList.add('hidden');
|
||||
banners[currentBanner].classList.remove('block');
|
||||
if (bannerDots[currentBanner]) {
|
||||
bannerDots[currentBanner].classList.remove('bg-[var(--color-primary)]', 'w-4');
|
||||
bannerDots[currentBanner].classList.add('bg-gray-400');
|
||||
}
|
||||
|
||||
currentBanner = index;
|
||||
|
||||
banners[currentBanner].classList.remove('hidden');
|
||||
banners[currentBanner].classList.add('block');
|
||||
if (bannerDots[currentBanner]) {
|
||||
bannerDots[currentBanner].classList.remove('bg-gray-400');
|
||||
bannerDots[currentBanner].classList.add('bg-[var(--color-primary)]', 'w-4');
|
||||
}
|
||||
}
|
||||
|
||||
if (banners.length > 1) {
|
||||
setInterval(function() {
|
||||
updateBanner((currentBanner + 1) % banners.length);
|
||||
}, 5000);
|
||||
|
||||
bannerDots.forEach(function(dot, index) {
|
||||
dot.addEventListener('click', function() {
|
||||
updateBanner(index);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@if($isOnline)
|
||||
let radioStream = '';
|
||||
|
||||
fetch('/api/radio/config')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.enabled && data.stream_url) {
|
||||
radioStream = data.stream_url;
|
||||
var listenersContainer = document.getElementById('listeners-container');
|
||||
var nowPlayingContainer = document.getElementById('now-playing-container');
|
||||
if (listenersContainer) listenersContainer.style.display = 'flex';
|
||||
if (nowPlayingContainer) nowPlayingContainer.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
var playBtn = document.getElementById('play-btn');
|
||||
if (playBtn) {
|
||||
playBtn.addEventListener('click', function() {
|
||||
var audio = document.getElementById('radio-audio');
|
||||
var icon = document.getElementById('play-icon');
|
||||
var text = document.getElementById('play-text');
|
||||
|
||||
if (isPlaying) {
|
||||
audio.pause();
|
||||
icon.textContent = '▶';
|
||||
text.textContent = 'Play';
|
||||
isPlaying = false;
|
||||
} else {
|
||||
if (radioStream) {
|
||||
audio.src = radioStream;
|
||||
audio.play().catch(function(e) { console.error('Audio play failed:', e); });
|
||||
icon.textContent = '⏸';
|
||||
text.textContent = 'Pause';
|
||||
isPlaying = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var volumeSlider = document.getElementById('volume');
|
||||
if (volumeSlider) {
|
||||
volumeSlider.addEventListener('input', function(e) {
|
||||
var audio = document.getElementById('radio-audio');
|
||||
if (audio) audio.volume = e.target.value / 100;
|
||||
});
|
||||
}
|
||||
|
||||
function updateNowPlaying() {
|
||||
fetch('/api/radio/now-playing')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.enabled && data.song) {
|
||||
var songEl = document.getElementById('now-playing-song');
|
||||
var artistEl = document.getElementById('now-playing-artist');
|
||||
if (songEl) songEl.textContent = data.song;
|
||||
if (artistEl && data.artist) artistEl.textContent = data.artist;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateListeners() {
|
||||
fetch('/api/radio/listeners')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
var el = document.getElementById('listeners-count');
|
||||
if (el && data.count > 0) el.textContent = data.count;
|
||||
});
|
||||
}
|
||||
|
||||
setInterval(updateNowPlaying, 5000);
|
||||
setInterval(updateListeners, 5000);
|
||||
updateNowPlaying();
|
||||
updateListeners();
|
||||
@endif
|
||||
</script>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,275 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', $title ?? '')
|
||||
|
||||
@push('styles')
|
||||
@stack('styles')
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="radio-container text-white">
|
||||
@if(!$enabled)
|
||||
<!-- Radio Disabled State -->
|
||||
<div class="min-h-screen flex items-center justify-center p-6">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-6">
|
||||
<svg class="w-24 h-24 mx-auto text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold mb-4">Radio is Offline</h1>
|
||||
<p class="text-gray-400 mb-6">De radio is momenteel uitgeschakeld. Kom later terug!</p>
|
||||
@if($offlineMessage)
|
||||
<div class="bg-gray-800 rounded-lg p-4">
|
||||
<p class="text-gray-300">{{ $offlineMessage }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<!-- Radio Player Section -->
|
||||
<div class="max-w-6xl mx-auto px-4 py-8">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold flex items-center gap-3">
|
||||
<span class="live-badge inline-flex items-center px-3 py-1 rounded-full text-xs font-bold">
|
||||
<span class="w-2 h-2 bg-white rounded-full mr-2"></span>
|
||||
LIVE
|
||||
</span>
|
||||
Radio
|
||||
</h1>
|
||||
<p class="text-gray-400 mt-1">Luister nu naar de beste muziek</p>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="flex gap-6">
|
||||
@if($showListeners)
|
||||
<div class="stat-card rounded-lg px-4 py-2 flex items-center gap-2">
|
||||
<svg class="w-5 h-5 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
<span id="listenerCount" class="font-semibold">--</span>
|
||||
<span class="text-gray-400 text-sm">luisteraars</span>
|
||||
</div>
|
||||
@endif
|
||||
<a href="{{ route('radio.leaderboard') }}" class="stat-card rounded-lg px-4 py-2 flex items-center gap-2 hover:bg-white/10 transition">
|
||||
<svg class="w-5 h-5 text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
<span class="font-semibold">Top 100</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Player -->
|
||||
<div class="radio-player rounded-2xl p-6 mb-8">
|
||||
<div class="grid md:grid-cols-3 gap-6 items-center">
|
||||
<!-- Album Art / DJ Avatar -->
|
||||
<div class="flex justify-center">
|
||||
<div id="djAvatar" class="w-32 h-32 rounded-full bg-gradient-to-br from-amber-400 to-amber-600 flex items-center justify-center text-4xl">
|
||||
@if($currentDJ && $currentDJ->user)
|
||||
<img src="{{ $currentDJ->user->avatar_url ?? asset('images/default-avatar.png') }}" alt="{{ $currentDJ->user->username }}" class="w-full h-full rounded-full object-cover">
|
||||
@else
|
||||
🎵
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Track Info -->
|
||||
<div class="text-center">
|
||||
<div id="nowPlayingInfo">
|
||||
<p class="text-gray-400 text-sm mb-1">Nu draait</p>
|
||||
<h2 id="trackTitle" class="text-xl font-bold mb-2">
|
||||
@if($nowPlaying && $nowPlaying['title'])
|
||||
{{ $nowPlaying['title'] }}
|
||||
@else
|
||||
--
|
||||
@endif
|
||||
</h2>
|
||||
@if($nowPlaying && $nowPlaying['artist'])
|
||||
<p id="trackArtist" class="text-gray-400">{{ $nowPlaying['artist'] }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- DJ Info -->
|
||||
@if($showCurrentDJ && $currentDJ && $currentDJ->user)
|
||||
<div class="mt-4 pt-4 border-t border-white/10">
|
||||
<p class="text-gray-400 text-sm mb-1">Presentator</p>
|
||||
<p class="font-semibold text-amber-400">{{ $currentDJ->user->username }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Player Controls -->
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<!-- Audio Player -->
|
||||
<audio id="radioPlayer" preload="none">
|
||||
<source src="{{ $streamUrl }}" type="audio/mpeg">
|
||||
</audio>
|
||||
|
||||
<!-- Play/Pause Button -->
|
||||
<button id="playPauseBtn" class="w-20 h-20 rounded-full bg-amber-500 hover:bg-amber-400 flex items-center justify-center transition-all transform hover:scale-105 shadow-lg shadow-amber-500/30">
|
||||
<svg id="playIcon" class="w-10 h-10 ml-1" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
<svg id="pauseIcon" class="w-10 h-10 hidden" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Volume Control -->
|
||||
<div class="flex items-center gap-3 w-full max-w-xs">
|
||||
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z"/>
|
||||
</svg>
|
||||
<input type="range" id="volumeSlider" min="0" max="100" value="80" class="flex-1 h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Song History -->
|
||||
@if($showHistory && !empty($songHistory))
|
||||
<div class="now-playing-card rounded-xl p-6 mb-8">
|
||||
<h3 class="text-lg font-semibold mb-4">Recent Gedraaid</h3>
|
||||
<div class="space-y-3" id="songHistory">
|
||||
@foreach($songHistory as $song)
|
||||
<div class="flex items-center justify-between py-2 border-b border-white/5 last:border-0">
|
||||
<div>
|
||||
<p class="font-medium">{{ $song['title'] }}</p>
|
||||
@if($song['artist'])
|
||||
<p class="text-sm text-gray-400">{{ $song['artist'] }}</p>
|
||||
@endif
|
||||
</div>
|
||||
<span class="text-sm text-gray-500">{{ $song['time'] }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Schedule Preview -->
|
||||
@if($showSchedule && $todaySchedule->isNotEmpty())
|
||||
<div class="now-playing-card rounded-xl p-6">
|
||||
<h3 class="text-lg font-semibold mb-4">Vandaag op Radio</h3>
|
||||
<div class="space-y-3">
|
||||
@foreach($todaySchedule as $slot)
|
||||
<div class="flex items-center justify-between py-2 border-b border-white/5 last:border-0">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-amber-400 font-mono">{{ date('H:i', strtotime($slot->start_time)) }}</span>
|
||||
<span class="font-medium">{{ $slot->user->username ?? 'Onbekend' }}</span>
|
||||
</div>
|
||||
<span class="text-gray-400 text-sm">{{ $slot->description ?? $slot->name }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Social Links -->
|
||||
@if(!empty($socialLinks))
|
||||
<div class="flex justify-center gap-6 mt-8">
|
||||
@foreach($socialLinks as $platform => $url)
|
||||
@if($url)
|
||||
<a href="{{ $url }}" target="_blank" class="w-10 h-10 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center transition">
|
||||
@switch($platform)
|
||||
@case('facebook')
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
|
||||
@break
|
||||
@case('twitter')
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
|
||||
@break
|
||||
@case('instagram')
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
|
||||
@break
|
||||
@case('discord')
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><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-.028 14.09 14.09 0 0 0 1.226-1.994.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-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
|
||||
@break
|
||||
@case('youtube')
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
|
||||
@break
|
||||
@case('twitch')
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714z"/></svg>
|
||||
@break
|
||||
@endswitch
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const player = document.getElementById('radioPlayer');
|
||||
const playBtn = document.getElementById('playPauseBtn');
|
||||
const playIcon = document.getElementById('playIcon');
|
||||
const pauseIcon = document.getElementById('pauseIcon');
|
||||
const volumeSlider = document.getElementById('volumeSlider');
|
||||
const listenerCount = document.getElementById('listenerCount');
|
||||
|
||||
// Play/Pause
|
||||
playBtn.addEventListener('click', function() {
|
||||
if (player.paused) {
|
||||
player.play().catch(function(e) {
|
||||
console.log('Autoplay blocked:', e);
|
||||
});
|
||||
playIcon.classList.add('hidden');
|
||||
pauseIcon.classList.remove('hidden');
|
||||
} else {
|
||||
player.pause();
|
||||
playIcon.classList.remove('hidden');
|
||||
pauseIcon.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Volume
|
||||
volumeSlider.addEventListener('input', function() {
|
||||
player.volume = this.value / 100;
|
||||
});
|
||||
|
||||
// Initial volume
|
||||
player.volume = volumeSlider.value / 100;
|
||||
|
||||
// Update listeners count
|
||||
function updateListeners() {
|
||||
fetch('/api/radio/listeners')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (listenerCount) {
|
||||
listenerCount.textContent = data.count.toLocaleString();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (listenerCount) listenerCount.textContent = '--';
|
||||
});
|
||||
}
|
||||
|
||||
// Update now playing
|
||||
function updateNowPlaying() {
|
||||
fetch('/api/radio/now-playing')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const titleEl = document.getElementById('trackTitle');
|
||||
const artistEl = document.getElementById('trackArtist');
|
||||
if (titleEl && data.title) titleEl.textContent = data.title;
|
||||
if (artistEl && data.artist) artistEl.textContent = data.artist;
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
// Auto-update listeners every 30 seconds
|
||||
updateListeners();
|
||||
setInterval(updateListeners, 30000);
|
||||
|
||||
// Auto-update now playing every 15 seconds
|
||||
updateNowPlaying();
|
||||
setInterval(updateNowPlaying, 15000);
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
@@ -0,0 +1,128 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'DJ Rooster')
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<x-content.content-card icon="calendar-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
DJ Rooster
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Wanneer is de volgende show?
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-6 dark:text-gray-200">
|
||||
@php
|
||||
$days = [
|
||||
'monday' => 'Maandag',
|
||||
'tuesday' => 'Dinsdag',
|
||||
'wednesday' => 'Woensdag',
|
||||
'thursday' => 'Donderdag',
|
||||
'friday' => 'Vrijdag',
|
||||
'saturday' => 'Zaterdag',
|
||||
'sunday' => 'Zondag',
|
||||
];
|
||||
$currentDay = strtolower(now()->format('l'));
|
||||
@endphp
|
||||
|
||||
@foreach($days as $dayKey => $dayName)
|
||||
@if(isset($schedule[$dayKey]) && $schedule[$dayKey]->count() > 0)
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-4 last:border-0">
|
||||
<h3 class="text-lg font-bold text-[var(--color-primary)] mb-3 {{ $dayKey === $currentDay ? 'ring-2 ring-[var(--color-primary)] rounded-lg p-2' : '' }}">
|
||||
{{ $dayName }}
|
||||
@if($dayKey === $currentDay)
|
||||
<span class="text-xs bg-red-500 text-white px-2 py-1 rounded ml-2">Vandaag</span>
|
||||
@endif
|
||||
</h3>
|
||||
|
||||
<div class="space-y-2">
|
||||
@foreach($schedule[$dayKey] as $slot)
|
||||
<div class="flex items-center gap-4 p-3 bg-gray-100 dark:bg-gray-800 rounded-lg {{ $dayKey === $currentDay && now()->format('H:i') >= $slot->start_time->format('H:i') && now()->format('H:i') <= $slot->end_time->format('H:i') ? 'ring-2 ring-green-500' : '' }}">
|
||||
<div class="text-center min-w-[80px]">
|
||||
<p class="font-bold">{{ $slot->start_time->format('H:i') }}</p>
|
||||
<p class="text-xs text-gray-500">{{ $slot->end_time->format('H:i') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold">{{ $slot->user->username }}</span>
|
||||
@if($slot->show_name)
|
||||
<span class="text-sm text-gray-400">- {{ $slot->show_name }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@if($slot->description)
|
||||
<p class="text-xs text-gray-500 mt-1">{{ $slot->description }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($dayKey === $currentDay && now()->format('H:i') >= $slot->start_time->format('H:i') && now()->format('H:i') <= $slot->end_time->format('H:i'))
|
||||
<span class="px-2 py-1 bg-green-500 text-white text-xs rounded animate-pulse">NU LIVE</span>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
@if($schedule->isEmpty())
|
||||
<div class="text-center py-8">
|
||||
<p class="text-gray-500">Er zijn momenteel geen shows gepland.</p>
|
||||
<p class="text-sm text-gray-400 mt-2">Kom later terug voor updates!</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
<x-content.content-card icon="info-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Rooster Info
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Houd ons in de gaten!
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>Al onze DJ's staan klaar om je te entertainen met de beste muziek!</p>
|
||||
|
||||
<div class="space-y-2 text-xs">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-3 h-3 bg-green-500 rounded animate-pulse"></span>
|
||||
<span>NU LIVE</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-3 h-3 bg-[var(--color-primary)] rounded"></span>
|
||||
<span>Vandaag</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-gray-500 mt-4">Tijden kunnen wijzigen zonder voorafgaande kennisgeving.</p>
|
||||
|
||||
<a href="{{ route('radio.index') }}" class="block text-[var(--color-primary)] hover:underline mt-4">
|
||||
← Terug naar Radio
|
||||
</a>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
|
||||
<x-content.content-card icon="music-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Nu op Radio
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Luister mee!
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm dark:text-gray-200">
|
||||
<a href="{{ route('radio.index') }}" class="block bg-[var(--color-primary)] hover:bg-[var(--color-primary)] text-white text-center py-3 rounded-lg transition">
|
||||
▶ Luister Live
|
||||
</a>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,76 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'Stuur een Shout')
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<x-content.content-card icon="chat-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Stuur een Shout naar de DJ
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Stuur een bericht naar de DJ!
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
@if (auth()->check())
|
||||
@if (session('success'))
|
||||
<div class="bg-green-100 dark:bg-green-900/30 border border-green-400 dark:border-green-600 text-green-700 dark:text-green-300 px-4 py-3 rounded mb-4">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('radio.shouts.store') }}" method="POST" class="mb-4">
|
||||
@csrf
|
||||
<div>
|
||||
<label class="block text-sm font-medium dark:text-gray-300 mb-2">Jouw bericht (max 280 karakters)</label>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
name="message"
|
||||
required
|
||||
maxlength="280"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200"
|
||||
placeholder="Plaats je shout voor de DJ...">
|
||||
<button type="submit" class="bg-[var(--color-primary)] hover:bg-[var(--color-primary)] text-white px-4 py-2 rounded transition whitespace-nowrap">
|
||||
📤 Stuur
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 p-4 rounded-lg">
|
||||
<p class="text-blue-800 dark:text-blue-300">
|
||||
<span class="font-semibold">✅ Je shout is verstuurd!</span><br>
|
||||
De DJ kan je bericht lezen in het admin paneel. Bedankt voor je bericht!
|
||||
</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-gray-100 dark:bg-gray-800 p-3 rounded text-center">
|
||||
<p class="text-gray-500 mb-2">
|
||||
<a href="/login" class="text-[var(--color-primary)] hover:underline">Log in</a> om een shout te sturen
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
<x-content.content-card icon="info-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Shout Info
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
Hoe werkt het?
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>Stuur shouts die de DJ live kan lezen tijdens de show!</p>
|
||||
<p class="text-xs text-gray-500">Houd het vriendelijk en respectvol.</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,73 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Staff'))
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2">
|
||||
@forelse($positions as $position)
|
||||
@continue(!$position->permission) {{-- skip if the rank relation is missing --}}
|
||||
|
||||
<x-content.staff-content-section :badge="$position->permission->badge" :color="$position->permission->staff_color">
|
||||
<x-slot:title>
|
||||
{{ $position->permission->rank_name }}
|
||||
</x-slot:title>
|
||||
<x-slot:under-title>
|
||||
{{ $position->permission->job_description }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="text-center dark:text-gray-400">
|
||||
<div class="mb-4 text-sm">
|
||||
{!! $position->description !!}
|
||||
</div>
|
||||
<div class="mb-4 text-sm font-semibold">
|
||||
{{ __('Application Deadline :date', [
|
||||
'date' => $position->apply_to ? $position->apply_to->format('F j, Y, g:i A') : __('No deadline set')
|
||||
]) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between">
|
||||
@if (auth()->check() && auth()->user()->hasAppliedForPosition($position->permission->id))
|
||||
<x-form.danger-button>
|
||||
{{ __('You have already applied for :position', ['position' => $position->permission->rank_name]) }}
|
||||
</x-form.danger-button>
|
||||
@else
|
||||
<a href="{{ route('staff-applications.show', $position) }}" class="w-full">
|
||||
<x-form.secondary-button>
|
||||
{{ __('Apply for :position', ['position' => $position->permission->rank_name]) }}
|
||||
</x-form.secondary-button>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</x-content.staff-content-section>
|
||||
@empty
|
||||
<x-content.content-card icon="lighthouse-icon" classes="border dark:border-gray-900 col-span-full">
|
||||
<x-slot:title>
|
||||
{{ __('No positions open') }}
|
||||
</x-slot:title>
|
||||
<x-slot:under-title>
|
||||
{{ __('There is currently no positions open') }}
|
||||
</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>
|
||||
{{ __('Please come back at a later time to check if we have any positions open by then! Thank you for your interest.', ['hotel' => setting('hotel_name')]) }}
|
||||
</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
<x-content.content-card icon="chat-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Apply for :hotel staff', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:title>
|
||||
<x-slot:under-title>
|
||||
{{ __('Select position to get started', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>
|
||||
{{ __('Here at :hotel we open up for staff applications every now and then. Sometimes you will find this page empty other times it might be filled with positions, if you ever come across a position you feel you would fit perfectly into, then do not hesitate to apply for it.', ['hotel' => setting('hotel_name')]) }}
|
||||
</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,56 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Staff'))
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<x-content.staff-content-section :badge="$position->permission->badge" :color="$position->permission->staff_color">
|
||||
<x-slot:title>
|
||||
{{ __('You are applying for :position', ['position' => $position->permission->rank_name]) }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Please fill out the fields below to apply for :position', ['position' => $position->permission->rank_name]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form class="flex flex-col gap-y-3" action="{{ route('staff-applications.store', $position) }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<x-form.label for="username" disabled>{{ __('Username') }}</x-form.label>
|
||||
<x-form.input name="username" value="{{ auth()->user()->username }}" :readonly="true" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-form.label for="content" disabled>{{ __('About you') }}</x-form.label>
|
||||
<textarea name="content" class="focus:ring-0 border-4 border-gray-200 rounded dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 focus:border-[var(--color-primary)] w-full min-h-[180px]">{{ old('content') }}</textarea>
|
||||
@error('content')
|
||||
<p class="text-red-600 text-sm mt-1">{{ $message }}</p>
|
||||
@enderror
|
||||
</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>
|
||||
{{ __('Apply for :position', ['position' => $position->permission->rank_name]) }}
|
||||
</x-form.primary-button>
|
||||
</form>
|
||||
</x-content.staff-content-section>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
<x-content.content-card icon="hotel-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>{{ __('Applying for :position', ['position' => $position->permission->rank_name]) }}</x-slot:title>
|
||||
<x-slot:under-title>{{ __('Read before applying') }}</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>
|
||||
{{ __('Please fill out all the fields to apply for :position. Be honest and transparent. Providing incorrect information may lead to removal if hired.', ['position' => $position->permission->rank_name]) }}
|
||||
</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Staff'))
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 space-y-8">
|
||||
@foreach ($employees as $employee)
|
||||
@php
|
||||
$sectionColor = $employee->staff_color ?? '#eeb425';
|
||||
@endphp
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl border-2 overflow-hidden transition-all duration-300"
|
||||
style="border-color: {{ $sectionColor }}30; box-shadow: 0 8px 32px {{ $sectionColor }}10;">
|
||||
<div class="px-6 py-5 border-b flex items-center gap-4 transition-all duration-300"
|
||||
style="background: linear-gradient(135deg, {{ $sectionColor }}12 0%, transparent 100%); border-color: {{ $sectionColor }}30;">
|
||||
@if($employee->badge)
|
||||
<div class="relative">
|
||||
<img src="{{ asset(setting('badges_path') . $employee->badge . '.gif') }}"
|
||||
alt="{{ $employee->rank_name }}"
|
||||
class="w-14 h-14 -lg">
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-3">
|
||||
<h2 class="text-2xl font-extrabold tracking-tight"
|
||||
style="color: {{ $sectionColor }};">
|
||||
{{ $employee->rank_name }}
|
||||
</h2>
|
||||
</div>
|
||||
@if($employee->job_description)
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mt-0.5">{{ $employee->job_description }}</p>
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="px-4 py-1.5 rounded-full text-sm font-bold"
|
||||
style="background-color: {{ $sectionColor }}20; color: {{ $sectionColor }}; border: 1px solid {{ $sectionColor }}40;">
|
||||
{{ count($employee->users) }} {{ count($employee->users) == 1 ? __('member') : __('members') }}
|
||||
</span>
|
||||
<span class="text-4xl font-black tracking-tighter"
|
||||
style="color: {{ $sectionColor }}; text-shadow: 0 0 20px {{ $sectionColor }}60;">
|
||||
>>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
@if(count($employee->users) > 0)
|
||||
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-5">
|
||||
@foreach ($employee->users as $staff)
|
||||
<x-community.staff-card :user="$staff" />
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="text-center py-16">
|
||||
<div class="w-20 h-20 mx-auto mb-4 rounded-full bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
|
||||
<svg class="w-10 h-10 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-lg text-gray-500 dark:text-gray-400 font-medium">{{ __('No staff members in this position') }}</p>
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500 mt-1">{{ __('Check back later for updates') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 space-y-4">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||
<div class="absolute left-0 top-0 bottom-0 w-1.5 bg-gradient-to-b from-amber-400 to-amber-500"></div>
|
||||
<div class="p-5">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h3 class="text-lg font-bold" style="color: var(--color-text)">
|
||||
{{ setting('hotel_name') }} {{ __('Staff') }}
|
||||
</h3>
|
||||
<span style="color: var(--color-primary);" class="text-2xl font-black">>></span>
|
||||
</div>
|
||||
<p class="text-xs uppercase tracking-wider mb-4" style="color: var(--color-text-muted)">{{ __('About our team') }}</p>
|
||||
<div class="space-y-3 text-sm" style="color: var(--color-text)">
|
||||
<p>{{ __('The :hotel staff team is one big happy family, each staff member has a different role and duties to fulfill.', ['hotel' => setting('hotel_name')]) }}</p>
|
||||
<p>{{ __('Most of our team usually consists of players that have been around :hotel for quite a while, but this does not mean we only recruit old & known players, we recruit those who shine out to us!', ['hotel' => setting('hotel_name')]) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border overflow-hidden" style="background-color: var(--color-surface); border-color: var(--color-text-muted);">
|
||||
<div class="absolute left-0 top-0 bottom-0 w-1.5" style="background: linear-gradient(to bottom, var(--color-primary), var(--color-primary));"></div>
|
||||
<div class="p-5">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h3 class="text-lg font-bold" style="color: var(--color-text)">
|
||||
{{ __('Join the Team') }}
|
||||
</h3>
|
||||
<span style="color: var(--color-primary);" class="text-2xl font-black">>></span>
|
||||
</div>
|
||||
<p class="text-xs uppercase tracking-wider mb-4" style="color: var(--color-text-muted)">{{ __('Become a staff member') }}</p>
|
||||
<div class="space-y-3 text-sm" style="color: var(--color-text)">
|
||||
<p>{{ __('Every now and then staff applications may open up. Once they do we always make sure to post a news article explaining the process - So make sure you keep an eye out for those in you are interested in joining the :hotel staff team.', ['hotel' => setting('hotel_name')]) }}</p>
|
||||
<a href="/community/staff-applications" class="inline-flex items-center gap-2 mt-2 px-4 py-2 rounded-lg font-semibold text-sm transition-all hover:scale-105"
|
||||
style="background: var(--color-primary); color: var(--button-text-color);">
|
||||
{{ __('View Open Positions') }}
|
||||
<span class="text-lg">>></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,105 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Staff'))
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2">
|
||||
@forelse($positions as $position)
|
||||
@continue(!$position->team)
|
||||
@php
|
||||
$status = auth()->check()
|
||||
? ($userAppStatuses[$position->team->id] ?? null) // 'pending'|'approved'|'rejected'|null
|
||||
: null;
|
||||
|
||||
$statusLabel = $status ? ucfirst($status) : null;
|
||||
$statusColorClasses = match ($status) {
|
||||
'approved' => 'bg-green-100 text-green-800 dark:bg-green-900/40 dark:text-green-300 border-green-200 dark:border-green-800',
|
||||
'pending' => 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/40 dark:text-yellow-300 border-yellow-200 dark:border-yellow-800',
|
||||
'rejected' => 'bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300 border-red-200 dark:border-red-800',
|
||||
default => '',
|
||||
};
|
||||
@endphp
|
||||
|
||||
<x-content.staff-content-section :badge="$position->team->badge" :color="$position->team->staff_color">
|
||||
<x-slot:title>
|
||||
<span class="inline-flex items-center gap-2">
|
||||
{{ $position->team->rank_name }}
|
||||
|
||||
@if ($statusLabel)
|
||||
<span class="inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium {{ $statusColorClasses }}">
|
||||
{{ $statusLabel }}
|
||||
</span>
|
||||
@endif
|
||||
</span>
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ $position->team?->job_description }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="text-center dark:text-gray-400">
|
||||
<div class="mb-4 text-sm">
|
||||
{!! $position->description !!}
|
||||
</div>
|
||||
<div class="mb-4 text-sm font-semibold">
|
||||
{{ __('Application Deadline :date', ['date' => $position->apply_to ? $position->apply_to->format('F j, Y, g:i A') : __('No deadline set')]) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between">
|
||||
@auth
|
||||
@if ($status)
|
||||
{{-- Already applied: show a disabled button indicating status --}}
|
||||
<x-form.secondary-button class="w-full justify-center" disabled>
|
||||
@switch($status)
|
||||
@case('pending')
|
||||
{{ __('Your application is pending') }}
|
||||
@break
|
||||
@case('approved')
|
||||
{{ __('You have been approved') }}
|
||||
@break
|
||||
@case('rejected')
|
||||
{{ __('Your application was rejected') }}
|
||||
@break
|
||||
@default
|
||||
{{ __('Application submitted') }}
|
||||
@endswitch
|
||||
</x-form.secondary-button>
|
||||
@else
|
||||
{{-- No application yet: show Apply --}}
|
||||
<a href="{{ route('team-applications.show', $position) }}" class="w-full">
|
||||
<x-form.primary-button class="w-full justify-center">
|
||||
{{ __('Apply for :position', ['position' => $position->team->rank_name]) }}
|
||||
</x-form.primary-button>
|
||||
</a>
|
||||
@endif
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="w-full">
|
||||
<x-form.secondary-button class="w-full justify-center">
|
||||
{{ __('Login to apply') }}
|
||||
</x-form.secondary-button>
|
||||
</a>
|
||||
@endauth
|
||||
</div>
|
||||
</x-content.staff-content-section>
|
||||
@empty
|
||||
<x-content.content-card icon="lighthouse-icon" classes="border dark:border-gray-900 col-span-full">
|
||||
<x-slot:title>{{ __('No team positions open') }}</x-slot:title>
|
||||
<x-slot:under-title>{{ __('There are currently no open team positions') }}</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>{{ __('Please come back later to check for new openings. Thank you!') }}</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
<x-content.content-card icon="chat-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>{{ __('Apply for :hotel Team', ['hotel' => setting('hotel_name')]) }}</x-slot:title>
|
||||
<x-slot:under-title>{{ __('Select a team to get started') }}</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>{{ __('We open team applications periodically. If you see a team you fit, do not hesitate to apply!') }}</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,56 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Staff'))
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<x-content.staff-content-section :badge="$position->team->badge" :color="$position->team->staff_color">
|
||||
<x-slot:title>
|
||||
{{ __('You are applying for :position', ['position' => $position->team->rank_name]) }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Please fill out the fields below to apply for :position', ['position' => $position->team->rank_name]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form class="flex flex-col gap-y-3" action="{{ route('team-applications.store', $position) }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<x-form.label for="username" disabled>{{ __('Username') }}</x-form.label>
|
||||
<x-form.input classes="bg-gray-100" name="username" value="{{ auth()->user()->username }}" :readonly="true" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-form.label for="content" disabled>{{ __('About you') }}</x-form.label>
|
||||
<textarea name="content" class="focus:ring-0 border-4 border-gray-200 rounded dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 focus:border-[var(--color-primary)] w-full min-h-[180px]">{{ old('content') }}</textarea>
|
||||
@error('content')
|
||||
<span class="text-red-500 text-sm">{{ $message }}</span>
|
||||
@enderror
|
||||
</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>
|
||||
{{ __('Apply for :position', ['position' => $position->team->rank_name]) }}
|
||||
</x-form.primary-button>
|
||||
</form>
|
||||
</x-content.staff-content-section>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
<x-content.content-card icon="hotel-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>{{ __('Applying for :position', ['position' => $position->team->rank_name]) }}</x-slot:title>
|
||||
<x-slot:under-title>{{ __('Read before applying') }}</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>
|
||||
{{ __('Please fill out all the fields to apply for :position. Be honest and transparent. Providing incorrect information may lead to removal if hired.', ['position' => $position->team->rank_name]) }}
|
||||
</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Staff'))
|
||||
|
||||
<div
|
||||
x-data="{ q: '', hideEmpty: false }"
|
||||
class="col-span-12 space-y-4"
|
||||
>
|
||||
{{-- Toolbar --}}
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<h1 class="text-2xl font-semibold tracking-tight" style="color: var(--color-text)">{{ __('Teams') }}</h1>
|
||||
|
||||
<div class="flex w-full flex-col items-stretch gap-3 sm:w-auto sm:flex-row sm:items-center">
|
||||
<div class="relative sm:w-80">
|
||||
<input
|
||||
x-model="q"
|
||||
type="text"
|
||||
placeholder="{{ __('Search teams…') }}"
|
||||
class="w-full rounded-xl border px-4 py-2.5 pr-10 text-sm focus:outline-none focus:ring-0"
|
||||
style="background-color: var(--color-background); color: var(--color-text); border-color: var(--input-border-color);"
|
||||
/>
|
||||
<svg class="pointer-events-none absolute right-3 top-1/2 h-5 w-5 -translate-y-1/2 opacity-60"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="m21 21-4.35-4.35M10 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<label class="inline-flex items-center gap-2 text-sm" style="color: var(--color-text)">
|
||||
<input type="checkbox" x-model="hideEmpty"
|
||||
class="rounded border-gray-300 text-[var(--color-primary)] focus:ring-[var(--color-primary)]" />
|
||||
<span class="select-none">{{ __('Hide empty teams') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Teams --}}
|
||||
<div class="flex flex-col gap-y-4">
|
||||
@forelse ($employees as $employee)
|
||||
@php
|
||||
/** @var \Illuminate\Support\Collection $users */
|
||||
$users = $employee->users ?? collect();
|
||||
$memberCount = $users->count();
|
||||
$searchText = trim(($employee->rank_name ?? '') . ' ' . ($employee->job_description ?? ''));
|
||||
@endphp
|
||||
|
||||
<section
|
||||
x-data="{
|
||||
name: @js($searchText),
|
||||
hasMembers: {{ $memberCount > 0 ? 'true' : 'false' }}
|
||||
}"
|
||||
x-show="(name.toLowerCase().includes(q.toLowerCase())) && (!hideEmpty || hasMembers)"
|
||||
x-cloak
|
||||
>
|
||||
<x-content.staff-content-section
|
||||
:badge="$employee->badge"
|
||||
:color="$employee->staff_color"
|
||||
class="overflow-hidden rounded-2xl border border-gray-100 shadow-sm dark:border-gray-800"
|
||||
>
|
||||
{{-- Header --}}
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<x-slot:title>
|
||||
{{ $employee->rank_name }}
|
||||
</x-slot:title>
|
||||
<x-slot:under-title>
|
||||
{{ $employee?->job_description }}
|
||||
</x-slot:under-title>
|
||||
</div>
|
||||
|
||||
{{-- Member count chip --}}
|
||||
<span class="ml-4 shrink-0 rounded-full border px-3 py-1 text-xs font-medium" style="border-color: var(--color-text-muted); color: var(--color-text);">
|
||||
{{ $memberCount }} {{ \Illuminate\Support\Str::plural(__('member'), $memberCount) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{-- Members grid --}}
|
||||
<div class="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
@forelse ($users as $staff)
|
||||
<x-community.staff-card :user="$staff" />
|
||||
@empty
|
||||
<div class="col-span-full">
|
||||
<div class="rounded-xl border border-dashed p-6 text-center text-sm" style="border-color: var(--color-text-muted); color: var(--color-text-muted);">
|
||||
{{ __('We currently have no staff in this team') }}
|
||||
</div>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-content.staff-content-section>
|
||||
</section>
|
||||
@empty
|
||||
<x-content.content-card icon="lighthouse-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>{{ __('No teams found') }}</x-slot:title>
|
||||
<x-slot:under-title>{{ __('Please check back later.') }}</x-slot:under-title>
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<p>{{ __('There are no teams to display right now.') }}</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -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>
|
||||
+19
@@ -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>
|
||||
+22
@@ -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
@@ -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">
|
||||
© {{ 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>
|
||||
+11
@@ -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>
|
||||
+137
@@ -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>
|
||||
+5
@@ -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
@@ -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>
|
||||
+55
@@ -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>
|
||||
+1342
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
<h1>Forget Password Email</h1>
|
||||
|
||||
Click the link to reset your password:
|
||||
<a href="{{ route('reset.password.get', $token) }}">Reset Password</a>
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
@php
|
||||
$hotelName = setting('hotel_name', 'Laravel');
|
||||
@endphp
|
||||
|
||||
<x-app-layout>
|
||||
@push('title', auth()->user() ? auth()->user()->username : __('Help Center'))
|
||||
|
||||
<div class="col-span-12 flex flex-col lg:flex-row gap-4">
|
||||
<div class="flex flex-col gap-4 w-full lg:w-3/5">
|
||||
@foreach($categories->where('small_box', false) as $category)
|
||||
<x-content.content-card icon="duo-chat-icon" classes="border border-[var(--color-text-muted)]">
|
||||
<x-slot:title>
|
||||
{{ str_replace(':hotel', $hotelName, $category->name) }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="px-2 text-sm text-[var(--color-text)]">
|
||||
<img class="px-2" style="float: right !important;"
|
||||
src="{{ asset('/assets/images/help-center/' . $category->image_url) }}" alt="">
|
||||
{!! str_replace(':hotel', $hotelName, $category->content) !!}
|
||||
</div>
|
||||
|
||||
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 ml-2">
|
||||
<button
|
||||
style="background-color: {{ $category->button_color }}; border: {{ $category->button_border_color }} solid 2px; color: var(--button-text-color);"
|
||||
class="px-2 py-1 font-semibold rounded transition hover:scale-105">
|
||||
{{ str_replace(':hotel', $hotelName, $category->button_text) }}
|
||||
</button>
|
||||
</a>
|
||||
</x-content.content-card>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4 w-full lg:w-2/5">
|
||||
@foreach($categories->where('small_box', true) as $category)
|
||||
<x-content.content-card icon="duo-chat-icon" classes="border border-[var(--color-text-muted)]">
|
||||
<x-slot:title>
|
||||
{{ str_replace(':hotel', $hotelName, $category->name) }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="px-2 text-sm text-[var(--color-text)]">
|
||||
{!! str_replace(':hotel', $hotelName, $category->content) !!}
|
||||
</div>
|
||||
|
||||
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 ml-2">
|
||||
<button
|
||||
style="background-color: {{ $category->button_color }}; border: {{ $category->button_border_color }} solid 2px; color: var(--button-text-color);"
|
||||
class="px-2 py-1 font-semibold rounded transition hover:scale-105">
|
||||
{{ str_replace(':hotel', $hotelName, $category->button_text) }}
|
||||
</button>
|
||||
</a>
|
||||
</x-content.content-card>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,78 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'Create a ticket')
|
||||
|
||||
<x-content.content-card icon="chat-icon" classes="border border-[var(--color-text-muted)] col-span-12 lg:col-span-9">
|
||||
<x-slot:title>
|
||||
{{ __('Create a ticket') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Please describe your request below') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form action="{{ route('help-center.ticket.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<select name="category_id" id="category_id"
|
||||
class="focus:ring-0 border-4 border-[var(--color-text-muted)] rounded bg-[var(--color-surface)] border-[var(--color-text-muted)] text-[var(--color-text)] focus:border-[var(--color-primary)] w-full @error('category_id') border-red-600 ring-red-500 @enderror">
|
||||
@foreach($categories as $category)
|
||||
<option value="{{ $category->id }}">
|
||||
{{ $category->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<div class="mt-4 no-tailwind">
|
||||
<x-form.label for="password_confirmation">
|
||||
{{ __('Title') }}
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="title" type="text"
|
||||
placeholder="{{ __('Enter a title for your ticket') }}"/>
|
||||
</div>
|
||||
|
||||
<x-form.wysiwyg-editor/>
|
||||
|
||||
<x-form.secondary-button type="submit" classes="mt-4">
|
||||
{{ __('Submit ticket') }}
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3">
|
||||
<x-content.content-card icon="duo-chat-icon"
|
||||
classes="border border-[var(--color-text-muted)] text-[var(--color-text)]">
|
||||
<x-slot:title>
|
||||
{{ __('Open tickets') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Your current open tickets') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse($openTickets as $ticket)
|
||||
<div class="w-full rounded bg-[var(--color-background)] p-2">
|
||||
<div class="flex items-center gap-x-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M11.25 4.5l7.5 7.5-7.5 7.5m-6-15l7.5 7.5-7.5 7.5"/>
|
||||
</svg>
|
||||
|
||||
<a data-turbolinks="false" href="{{ route('help-center.ticket.show', $ticket) }}" class="hover:text-[var(--color-primary)]">
|
||||
{{ Str::limit($ticket->title, 20) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p>
|
||||
You currently have no open tickets.
|
||||
</p>
|
||||
|
||||
@endforelse
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,83 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'Create a ticket')
|
||||
|
||||
<x-content.content-card icon="chat-icon" classes="border border-[var(--color-text-muted)] col-span-12 lg:col-span-9">
|
||||
<x-slot:title>
|
||||
{{ __('Edit your ticket') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Please describe your request below') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form action="{{ route('help-center.ticket.update', $ticket) }}" method="POST">
|
||||
@method('PUT')
|
||||
@csrf
|
||||
|
||||
<select name="category_id" id="category_id"
|
||||
class="focus:ring-0 border-4 border-[var(--color-text-muted)] rounded bg-[var(--color-surface)] border-[var(--color-text-muted)] text-[var(--color-text)] focus:border-[var(--color-primary)] w-full @error('category_id') border-red-600 ring-red-500 @enderror">
|
||||
<option value="{{ $ticket->category_id }}" selected>
|
||||
{{ $ticket->category->name }}
|
||||
</option>
|
||||
|
||||
@foreach($categories as $category)
|
||||
<option value="{{ $category->id }}">
|
||||
{{ $category->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<div class="mt-4 no-tailwind">
|
||||
<x-form.label for="password_confirmation">
|
||||
{{ __('Title') }}
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="title" type="text" value="{{ $ticket->title }}"
|
||||
placeholder="{{ __('Enter a title for your ticket') }}"/>
|
||||
</div>
|
||||
|
||||
<x-form.wysiwyg-editor :content="$ticket->content"/>
|
||||
|
||||
<x-form.secondary-button type="submit" classes="mt-4">
|
||||
{{ __('Update ticket') }}
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3">
|
||||
<x-content.content-card icon="duo-chat-icon"
|
||||
classes="border border-[var(--color-text-muted)] text-[var(--color-text)]">
|
||||
<x-slot:title>
|
||||
{{ __('Open tickets') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Your current open tickets') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse($openTickets as $ticket)
|
||||
<div class="w-full rounded bg-[var(--color-background)] p-2">
|
||||
<div class="flex items-center gap-x-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M11.25 4.5l7.5 7.5-7.5 7.5m-6-15l7.5 7.5-7.5 7.5"/>
|
||||
</svg>
|
||||
|
||||
<a data-turbolinks="false" href="{{ route('help-center.ticket.show', $ticket) }}" class="hover:text-[var(--color-primary)]">
|
||||
{{ Str::limit($ticket->title, 20) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p>
|
||||
You currently have no open tickets.
|
||||
</p>
|
||||
|
||||
@endforelse
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,83 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'Create a ticket')
|
||||
|
||||
<x-content.content-card icon="chat-icon" classes="border col-span-12">
|
||||
<x-slot:title>
|
||||
{{ __('All tickets') }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="overflow-hidden overflow-x-auto rounded border" style="border-color: var(--color-text-muted);">
|
||||
<table class="min-w-full text-sm divide-y" style="border-color: var(--color-text-muted);">
|
||||
<thead style="background-color: var(--color-surface);">
|
||||
<tr>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium">
|
||||
{{ __('Title') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium">
|
||||
{{ __('Author') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium">
|
||||
{{ __('Status') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium">
|
||||
{{ __('Actions') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="divide-y" style="border-color: var(--color-text-muted); color: var(--color-text);">
|
||||
@forelse ($tickets as $ticket)
|
||||
<tr>
|
||||
<td class="whitespace-nowrap px-4 py-2 font-medium">
|
||||
{{ Str::limit($ticket->title, 80) }}
|
||||
</td>
|
||||
<td class="px-4 py-2">
|
||||
{{ $ticket->user->username }}</td>
|
||||
<td class="px-4 py-2">
|
||||
{{ $ticket->open ? 'Open' : 'Closed' }}
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-4 py-2 font-medium flex gap-x-3">
|
||||
<a data-turbolinks="false" href="{{ route('help-center.ticket.show', $ticket) }}" style="color: var(--link-color);">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a data-turbolinks="false" href="{{ route('help-center.ticket.edit', $ticket) }}" style="color: var(--link-color);">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
@if(hasPermission('delete_website_tickets'))
|
||||
<form action="{{ route('help-center.ticket.destroy', $ticket) }}" method="POST">
|
||||
@method('DELETE')
|
||||
@csrf
|
||||
|
||||
<button type="submit" style="color: #ef4444;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td class="whitespace-nowrap px-4 py-2 text-center"
|
||||
colspan="3">
|
||||
{{ __('No tickets available') }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
{{ $tickets->links() }}
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,185 @@
|
||||
<x-app-layout>
|
||||
@push('title', 'Create a ticket')
|
||||
|
||||
<x-content.content-card icon="chat-icon" classes="border border-[var(--color-text-muted)] text-[var(--color-text)] col-span-12 lg:col-span-9">
|
||||
<x-slot:title>
|
||||
<div class="flex gap-x-2">
|
||||
{{ $ticket->title }} [{{ $ticket->category->name }}]
|
||||
|
||||
@if($ticket->canManageTicket())
|
||||
<a data-turbolinks="false" href="{{ route('help-center.ticket.edit', $ticket) }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
|
||||
</svg>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</x-slot:title>
|
||||
|
||||
<div class="w-full flex gap-x-3">
|
||||
@if($ticket->isOpen())
|
||||
<form action="{{ route('help-center.ticket.toggle-status', $ticket) }}" method="POST" class="w-full">
|
||||
@method('PUT')
|
||||
@csrf
|
||||
|
||||
<x-form.secondary-button>
|
||||
Close
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
@else
|
||||
<form action="{{ route('help-center.ticket.toggle-status', $ticket) }}" method="POST" class="w-full">
|
||||
@method('PUT')
|
||||
@csrf
|
||||
|
||||
<x-form.primary-button>
|
||||
Re-open
|
||||
</x-form.primary-button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('help-center.ticket.destroy', $ticket) }}" method="POST" class="w-full">
|
||||
@method('DELETE')
|
||||
@csrf
|
||||
|
||||
<x-form.danger-button>
|
||||
Delete
|
||||
</x-form.danger-button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<article class="prose-xl mt-8" style="width: 100%;">
|
||||
{!! $ticket->content !!}
|
||||
</article>
|
||||
</x-content.content-card>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3">
|
||||
<x-content.content-card icon="duo-chat-icon"
|
||||
classes="border border-[var(--color-text-muted)] text-[var(--color-text)]">
|
||||
<x-slot:title>
|
||||
{{ __('Open tickets') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Your current open tickets') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse($openTickets as $ticket)
|
||||
<div class="w-full rounded bg-gray-200 p-2 dark:bg-gray-700">
|
||||
<div class="flex items-center gap-x-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M11.25 4.5l7.5 7.5-7.5 7.5m-6-15l7.5 7.5-7.5 7.5"/>
|
||||
</svg>
|
||||
|
||||
<a data-turbolinks="false" href="{{ route('help-center.ticket.show', $ticket) }}" class="hover:text-[var(--color-primary)]">
|
||||
{{ Str::limit($ticket->title, 20) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p>
|
||||
You currently have no open tickets.
|
||||
</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
|
||||
<x-content.content-card icon="duo-chat-icon"
|
||||
classes="border border-[var(--color-text-muted)] text-[var(--color-text)] border border-[var(--color-text-muted)] text-[var(--color-text)] col-span-12 lg:col-span-9 -mt-4">
|
||||
<x-slot:title>
|
||||
{{ __('Comments') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Please submit your reply below') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
@if($ticket->isOpen())
|
||||
<form action="{{ route('help-center.ticket.reply.store', $ticket) }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<x-form.wysiwyg-editor />
|
||||
|
||||
<x-form.secondary-button classes="mt-2">
|
||||
{{ __('Submit reply') }}
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<div class="flex flex-col gap-y-4 mt-4">
|
||||
@forelse($ticket->replies as $reply)
|
||||
@if($reply->user_id === auth()->user()->id)
|
||||
<div class="w-full rounded bg-[var(--color-background)]">
|
||||
<div class="h-[50px] px-4 flex items-center justify-between border-b border-[var(--color-text-muted)] relative overflow-hidden">
|
||||
<div class="flex">
|
||||
<small class="ml-14 text-[var(--color-text-muted)]">{{ $reply->user->username }}</small>
|
||||
<div class="absolute left-2 -bottom-10 flex gap-x-2">
|
||||
<img src="{{ setting('avatar_imager') }}/{{ $reply->user->look }}" alt="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-x-2">
|
||||
<small class="text-[var(--color-text-muted)]">{{ $reply->created_at->diffForHumans() }}</small>
|
||||
|
||||
@if($reply->user_id === Auth::id() || hasPermission('delete_website_ticket_replies'))
|
||||
<form action="{{ route('help-center.ticket.reply.destroy', $reply) }}" method="POST">
|
||||
@method('DELETE')
|
||||
@csrf
|
||||
|
||||
<button type="submit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
{!! $reply->content !!}
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="w-full rounded bg-[var(--color-background)]">
|
||||
<div class="h-[50px] px-4 flex items-center justify-between border-b border-[var(--color-text-muted)] relative overflow-hidden">
|
||||
<div class="flex gap-x-2">
|
||||
<form action="{{ route('help-center.ticket.reply.destroy', $reply) }}" method="POST">
|
||||
@method('DELETE')
|
||||
@csrf
|
||||
|
||||
<button type="submit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<small class="text-[var(--color-text-muted)]">{{ $reply->created_at->diffForHumans() }}</small>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex">
|
||||
<small class="mr-14 text-[var(--color-text-muted)]">{{ $reply->user->username }}</small>
|
||||
<div class="absolute right-2 -bottom-10 flex gap-x-2">
|
||||
<img class="scale-x-[-1]" src="{{ setting('avatar_imager') }}/{{ $reply->user->look }}" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
{!! $reply->content !!}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@empty
|
||||
<p>
|
||||
{{ __('There is currently no replies') }}
|
||||
</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</x-app-layout>
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Welcome to the best hotel on the web!'))
|
||||
|
||||
<div class="col-span-12 space-y-14">
|
||||
<div class="col-span-12">
|
||||
<x-content.guest-content-card icon="hotel-icon">
|
||||
<x-slot:title>
|
||||
{{ __('Latest news') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Keep up to date with the latest hotel gossip.') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
||||
@forelse($articles as $article)
|
||||
<x-article-card :article="$article" />
|
||||
@empty
|
||||
<x-filler-article-card />
|
||||
@endforelse
|
||||
</div>
|
||||
</x-content.guest-content-card>
|
||||
</div>
|
||||
|
||||
@if($photos)
|
||||
<div class="col-span-12">
|
||||
<x-content.guest-content-card icon="camera-icon">
|
||||
<x-slot:title>
|
||||
{{ __('Latest Photos') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Have a look at some of the great moments captured by users around the hotel.') }}
|
||||
</x-slot:under-title>
|
||||
<x-photos :photos="$photos" />
|
||||
</x-content.guest-content-card>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@push('javascript')
|
||||
<script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@4.0/dist/fancybox.umd.js"></script>
|
||||
@endpush
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css" />
|
||||
</x-app-layout>
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="app" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="turbolinks-cache-control" content="no-cache">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ setting('hotel_name') }} - @stack('title')</title>
|
||||
|
||||
<link rel="icon" type="image/gif" sizes="18x17" href="{{ asset('assets/images/home_icon.gif') }}">
|
||||
|
||||
<!-- Fonts -->
|
||||
@php
|
||||
$themeFont = setting('font_family', 'Nunito');
|
||||
$googleFonts = ['Nunito', 'Inter', 'Poppins', 'Roboto', 'Playfair'];
|
||||
@endphp
|
||||
@if(in_array($themeFont, $googleFonts))
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family={{ $themeFont }}:wght@400;600;700&display=swap">
|
||||
@else
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">
|
||||
@endif
|
||||
|
||||
<link rel="stylesheet" href="{{ asset('assets/css/flowbite.min.css') }}" />
|
||||
<script src="{{ asset('assets/js/popper.min.js') }}"></script>
|
||||
<script src="{{ asset('assets/js/tippy-bundle.umd.min.js') }}"></script>
|
||||
<link rel="stylesheet" href="{{ asset('assets/css/scale.min.css') }}"/>
|
||||
|
||||
@vite(['resources/themes/' . setting('theme', 'atom') . '/css/app.css', 'resources/themes/' . setting('theme', 'atom') . '/js/app.js'], 'build')
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--color-primary: {{ setting('color_primary', '#f59e0b') }};
|
||||
--color-background: {{ setting('color_background', '#f8fafc') }};
|
||||
--color-surface: {{ setting('color_surface', '#ffffff') }};
|
||||
--color-dropdown: {{ setting('color_dropdown', '#ffffff') }};
|
||||
--color-navbar: {{ setting('color_navbar', '#ffffff') }};
|
||||
--color-navbar-text: {{ setting('color_navbar_text', '#1e293b') }};
|
||||
--color-text: {{ setting('color_text', '#0f172a') }};
|
||||
--color-text-muted: {{ setting('color_text_muted', '#64748b') }};
|
||||
--color-accent: {{ setting('color_accent', '#10b981') }};
|
||||
--button-color: {{ setting('button_primary_color', '#f59e0b') }};
|
||||
--button-text-color: {{ setting('button_text_color', '#1e293b') }};
|
||||
--button-secondary-color: {{ setting('button_secondary_color', '#22c55e') }};
|
||||
--button-secondary-text-color: {{ setting('button_secondary_text_color', '#ffffff') }};
|
||||
--button-secondary-hover-color: {{ setting('button_secondary_hover_color', '#16a34a') }};
|
||||
--button-secondary-border-color: {{ setting('button_secondary_border_color', '#16a34a') }};
|
||||
--button-danger-color: {{ setting('button_danger_color', '#ef4444') }};
|
||||
--button-danger-text-color: {{ setting('button_danger_text_color', '#ffffff') }};
|
||||
--button-danger-hover-color: {{ setting('button_danger_hover_color', '#dc2626') }};
|
||||
--button-danger-border-color: {{ setting('button_danger_border_color', '#dc2626') }};
|
||||
--button-outline-color: {{ setting('button_outline_color', '#eeb425') }};
|
||||
--button-outline-text-color: {{ setting('button_outline_text_color', '#1a1a2e') }};
|
||||
--button-outline-hover-color: {{ setting('button_outline_hover_color', '#cf9d15') }};
|
||||
--border-radius: {{ setting('border_radius', '12') }}px;
|
||||
--border-color: {{ setting('border_color', '#eeb425') }};
|
||||
--font-family: '{{ setting('font_family', 'Nunito') }}', sans-serif;
|
||||
--dropdown-radius: {{ setting('dropdown_style', 'rounded') === 'square' ? '0px' : (setting('dropdown_style', 'rounded') === 'pill' ? '20px' : '8px') }};
|
||||
--dropdown-border: {{ setting('dropdown_border', '1') === '1' ? '1px solid var(--color-primary)' : 'none' }};
|
||||
--dropdown-shadow: {{ setting('dropdown_shadow', 'medium') === 'none' ? 'none' : (setting('dropdown_shadow', 'medium') === 'small' ? '0 2px 8px rgba(0,0,0,0.15)' : (setting('dropdown_shadow', 'medium') === 'large' ? '0 8px 32px rgba(0,0,0,0.4)' : '0 4px 16px rgba(0,0,0,0.25)')) }};
|
||||
|
||||
/* Sizing */
|
||||
--size-navigation-font: {{ setting('size_navigation_font', '14') }}px;
|
||||
--size-navigation-padding: {{ setting('size_navigation_padding', '16') }}px;
|
||||
--size-navigation-height: {{ setting('size_navigation_height', '60') }}px;
|
||||
--size-heading-h1: {{ setting('size_heading_h1', '32') }}px;
|
||||
--size-heading-h2: {{ setting('size_heading_h2', '24') }}px;
|
||||
--size-heading-h3: {{ setting('size_heading_h3', '20') }}px;
|
||||
--size-body-text: {{ setting('size_body_text', '16') }}px;
|
||||
--size-small-text: {{ setting('size_small_text', '14') }}px;
|
||||
--size-button-text: {{ setting('size_button_text', '14') }}px;
|
||||
--size-card-padding: {{ setting('size_card_padding', '24') }}px;
|
||||
--size-icon-small: {{ setting('size_icon_small', '16') }}px;
|
||||
--size-icon-medium: {{ setting('size_icon_medium', '24') }}px;
|
||||
--size-icon-large: {{ setting('size_icon_large', '32') }}px;
|
||||
|
||||
/* Avatar */
|
||||
--avatar-border-radius: {{ setting('avatar_border_radius', '8') }}px;
|
||||
--avatar-border-color: {{ setting('avatar_border_color', '#eeb425') }};
|
||||
|
||||
/* Badge */
|
||||
--badge-background: {{ setting('badge_background', '#eeb425') }};
|
||||
--badge-text-color: {{ setting('badge_text_color', '#000000') }};
|
||||
--badge-border-radius: {{ setting('badge_border_radius', '4') }}px;
|
||||
|
||||
/* Card */
|
||||
--card-border-width: {{ setting('card_border_width', '1') }}px;
|
||||
--card-border-radius: {{ setting('card_border_radius', '12') }}px;
|
||||
|
||||
/* Link */
|
||||
--link-color: {{ setting('link_color', '#eeb425') }};
|
||||
--link-hover-color: {{ setting('link_hover_color', '#cf9d15') }};
|
||||
|
||||
/* Input */
|
||||
--input-border-color: {{ setting('input_border_color', '#4b5563') }};
|
||||
--input-text-color: {{ setting('input_text_color', '#ffffff') }};
|
||||
--input-placeholder-color: {{ setting('input_placeholder_color', '#9ca3af') }};
|
||||
--input-focus-color: {{ setting('input_focus_color', '#eeb425') }};
|
||||
|
||||
/* Shadow */
|
||||
--shadow-color: {{ setting('shadow_color', '#000000') }};
|
||||
--shadow-opacity: {{ setting('shadow_opacity', '20') }};
|
||||
|
||||
/* Spinner */
|
||||
--spinner-color: {{ setting('spinner_color', '#eeb425') }};
|
||||
|
||||
/* Navbar */
|
||||
--navbar-height: {{ setting('navbar_height', '64') }}px;
|
||||
}
|
||||
|
||||
/* Button Effects */
|
||||
@keyframes sparkle {
|
||||
0%, 100% { transform: scale(0) rotate(0deg); opacity: 0; }
|
||||
50% { transform: scale(1) rotate(180deg); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-3px); }
|
||||
}
|
||||
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% { box-shadow: 0 0 5px var(--color-primary), 0 0 10px var(--color-primary); }
|
||||
50% { box-shadow: 0 0 15px var(--color-primary), 0 0 25px var(--color-primary); }
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-5px); }
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% { background-position: -200% center; }
|
||||
100% { background-position: 200% center; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@stack('scripts')
|
||||
<x-turnstile.scripts />
|
||||
</head>
|
||||
|
||||
<body class="flex min-h-screen flex-col site-bg dark:bg-gray-800{{ setting('button_effects_enabled') ? ' btn-effects-active' : '' }}{{ setting('button_effects_enabled') && setting('button_effects_navigation', 'none') != 'none' ? ' btn-effect-nav-' . setting('button_effects_navigation') : '' }}{{ setting('button_effects_enabled') && setting('button_effects_buttons', 'none') != 'none' ? ' btn-effect-btn-' . setting('button_effects_buttons') : '' }}{{ setting('button_effects_enabled') && setting('button_effects_links', 'none') != 'none' ? ' btn-effect-link-' . setting('button_effects_links') : '' }}" style="{{ setting('button_effects_enabled') ? '--btn-effect-speed: ' . (setting('button_effects_speed') == 'slow' ? '3s' : (setting('button_effects_speed') == 'fast' ? '0.5s' : '1.5s')) : '' }}">
|
||||
@if(config('habbo.site.debug_mode_enabled') && config('habbo.site.site_environment') === 'production')
|
||||
<div class="w-full py-2 px-4 text-center rounded text-white bg-red-500">
|
||||
{{ __('It seems like debug mode is enabled while being in production. It is heavily recommended too set APP_DEBUG in the .env file to false in production mode') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Validation Errors -->
|
||||
<x-messages.flash-messages />
|
||||
|
||||
<div id="app" style="background-color: var(--color-background)">
|
||||
{{-- Top header --}}
|
||||
@auth
|
||||
<x-top-header />
|
||||
@endauth
|
||||
|
||||
{{-- Site Header --}}
|
||||
<x-site-header />
|
||||
|
||||
{{-- Navigation --}}
|
||||
<nav class="relative bg-white shadow-sm dark:bg-gray-900" style="background-color: var(--color-navbar)">
|
||||
<div class="max-w-7xl min-h-[60px] px-4 md:flex md:items-center md:justify-between md:mx-auto">
|
||||
<x-navigation.navigation-menu />
|
||||
|
||||
<div class="hidden lg:flex items-center">
|
||||
<x-navigation.theme-mode-switcher />
|
||||
|
||||
<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>
|
||||
|
||||
<x-navigation.mobile-menu />
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{{-- Content --}}
|
||||
<main class="overflow-hidden site-bg">
|
||||
<div class="mx-auto mt-10 grid max-w-7xl grid-cols-12 gap-x-3 gap-y-8 p-6 md:mt-0">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<x-footer />
|
||||
|
||||
@if (setting('cloudflare_turnstile_enabled'))
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const turnstileWidget = document.querySelector('#cf-turnstile-widget');
|
||||
const theme = localStorage.getItem('theme');
|
||||
|
||||
if (turnstileWidget && theme) {
|
||||
turnstileWidget.setAttribute('data-theme', theme);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
|
||||
|
||||
@if (setting('cms_color_mode') === 'dark')
|
||||
<script>
|
||||
if (localStorage.getItem("theme") === null) {
|
||||
document.documentElement.classList.add("dark");
|
||||
localStorage.setItem("theme", 'dark');
|
||||
}
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@if (setting('google_recaptcha_enabled'))
|
||||
@push('javascript')
|
||||
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
||||
@endpush
|
||||
@endif
|
||||
|
||||
<script defer src="{{ asset('assets/js/alpine-ui.js') }}"></script>
|
||||
<script defer src="{{ asset('assets/js/alpine-focus.js') }}"></script>
|
||||
<script src="{{ asset('assets/js/flowbite.min.js') }}"></script>
|
||||
|
||||
@stack('javascript')
|
||||
</body>
|
||||
</html>
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ config('app.name', 'Laravel') }}</title>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">
|
||||
|
||||
@vite(['resources/themes/' . setting('theme') . '/css/app.css', 'resources/themes/' . setting('theme') . '/js/app.js'], 'build')
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--color-primary: {{ setting('color_primary', '#eeb425') }};
|
||||
--color-background: {{ setting('color_background', '#1a1a2e') }};
|
||||
--color-surface: {{ setting('color_surface', '#2d2d44') }};
|
||||
--color-dropdown: {{ setting('color_dropdown', '#2d2d44') }};
|
||||
--color-navbar: {{ setting('color_navbar', '#ffffff') }};
|
||||
--color-navbar-text: {{ setting('color_navbar_text', '#1a1a2e') }};
|
||||
--color-text: {{ setting('color_text', '#ffffff') }};
|
||||
--color-text-muted: {{ setting('color_text_muted', '#9ca3af') }};
|
||||
--color-accent: {{ setting('color_accent', '#10b981') }};
|
||||
--button-color: {{ setting('button_primary_color', '#eeb425') }};
|
||||
--button-text-color: {{ setting('button_text_color', '#1a1a2e') }};
|
||||
--button-secondary-color: {{ setting('button_secondary_color', '#22c55e') }};
|
||||
--button-secondary-text-color: {{ setting('button_secondary_text_color', '#ffffff') }};
|
||||
--button-secondary-hover-color: {{ setting('button_secondary_hover_color', '#16a34a') }};
|
||||
--button-secondary-border-color: {{ setting('button_secondary_border_color', '#16a34a') }};
|
||||
--button-danger-color: {{ setting('button_danger_color', '#ef4444') }};
|
||||
--button-danger-text-color: {{ setting('button_danger_text_color', '#ffffff') }};
|
||||
--button-danger-hover-color: {{ setting('button_danger_hover_color', '#dc2626') }};
|
||||
--button-danger-border-color: {{ setting('button_danger_border_color', '#dc2626') }};
|
||||
--button-outline-color: {{ setting('button_outline_color', '#eeb425') }};
|
||||
--button-outline-text-color: {{ setting('button_outline_text_color', '#1a1a2e') }};
|
||||
--button-outline-hover-color: {{ setting('button_outline_hover_color', '#cf9d15') }};
|
||||
--border-radius: {{ setting('border_radius', '12') }}px;
|
||||
--border-color: {{ setting('border_color', '#eeb425') }};
|
||||
--font-family: '{{ setting('font_family', 'Nunito') }}', sans-serif;
|
||||
--dropdown-radius: {{ setting('dropdown_style', 'rounded') === 'square' ? '0px' : (setting('dropdown_style', 'rounded') === 'pill' ? '20px' : '8px') }};
|
||||
--dropdown-border: {{ setting('dropdown_border', '1') === '1' ? '1px solid var(--color-primary)' : 'none' }};
|
||||
--dropdown-shadow: {{ setting('dropdown_shadow', 'medium') === 'none' ? 'none' : (setting('dropdown_shadow', 'medium') === 'small' ? '0 2px 8px rgba(0,0,0,0.15)' : (setting('dropdown_shadow', 'medium') === 'large' ? '0 8px 32px rgba(0,0,0,0.4)' : '0 4px 16px rgba(0,0,0,0.25)')) }};
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="{{ setting('button_effects_enabled') ? 'btn-effects-active' : '' }}{{ setting('button_effects_enabled') && setting('button_effects_navigation', 'none') != 'none' ? ' btn-effect-nav-' . setting('button_effects_navigation') : '' }}{{ setting('button_effects_enabled') && setting('button_effects_buttons', 'none') != 'none' ? ' btn-effect-btn-' . setting('button_effects_buttons') : '' }}{{ setting('button_effects_enabled') && setting('button_effects_links', 'none') != 'none' ? ' btn-effect-link-' . setting('button_effects_links') : '' }}" style="{{ setting('button_effects_enabled') ? '--btn-effect-speed: ' . (setting('button_effects_speed') == 'slow' ? '3s' : (setting('button_effects_speed') == 'fast' ? '0.5s' : '1.5s')) : '' }}">
|
||||
<div id="app" class="font-sans antialiased">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Leaderboard'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<!-- Header -->
|
||||
<div class="mb-6 p-6 rounded-xl border-2" style="background-color: var(--color-surface); border-color: var(--color-primary);">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="w-16 h-16 rounded-full flex items-center justify-center text-3xl shadow-lg" style="background-color: var(--color-background);">
|
||||
🏆
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold" style="color: var(--color-text)">Leaderboard</h1>
|
||||
<p style="color: var(--color-text-muted)">Bekijk de beste spelers van {{ setting('hotel_name') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<x-leaderboard-card title="{{ __('Top credits') }}" icon="credits.png" :data="$credits" valueKey="credits" valueType="Credits" />
|
||||
<x-leaderboard-card title="{{ __('Top duckets') }}" icon="duckets.png" :data="$duckets" relationship="user" valueKey="amount" valueType="Duckets" />
|
||||
<x-leaderboard-card title="{{ __('Top diamonds') }}" icon="diamond.png" :data="$diamonds" relationship="user" valueKey="amount" valueType="Diamonds" />
|
||||
<x-leaderboard-card title="{{ __('Hours online') }}" icon="clock.gif" :data="$mostOnline" relationship="user" valueKey="online_time" valueType="Hours online" :formatValue="fn($value) => round($value / 3600)" />
|
||||
<x-leaderboard-card title="{{ __('Respects received') }}" icon="heart.gif" :data="$respectsReceived" relationship="user" valueKey="respects_received" valueType="Respect received" />
|
||||
<x-leaderboard-card title="{{ __('Achievement score') }}" icon="star.gif" :data="$achievementScores" relationship="user" valueKey="achievement_score" valueType="Achievement points" />
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Generator'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<x-content.content-card icon="hotel-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
Logo generator
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Generate your very own logo') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm text-[var(--color-text)]">
|
||||
<div x-data="logoGenerator()" class="mt-4">
|
||||
<div class="grid grid-cols-6 gap-3">
|
||||
<div x-bind:class="{'bg-[var(--color-background)] ring-2 ring-emerald-700': fontType === 'atom'}" class="h-24 rounded border border-[var(--color-text-muted)] p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-[var(--color-background)] cursor-pointer" x-on:click="selectFont('atom')">
|
||||
<img src="{{ asset('/assets/images/logo-generator/atom/a.png') }}" alt="Letter a">
|
||||
<img src="{{ asset('/assets/images/logo-generator/atom/b.png') }}" alt="Letter b">
|
||||
<img src="{{ asset('/assets/images/logo-generator/atom/c.png') }}" alt="Letter c">
|
||||
</div>
|
||||
|
||||
<div x-bind:class="{'bg-[var(--color-background)] ring-2 ring-emerald-700': fontType === 'sunrise'}" class="h-24 rounded border border-[var(--color-text-muted)] p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-[var(--color-background)] cursor-pointer" x-on:click="selectFont('sunrise')">
|
||||
<img src="{{ asset('/assets/images/logo-generator/sunrise/a.png') }}" alt="Letter a">
|
||||
<img src="{{ asset('/assets/images/logo-generator/sunrise/b.png') }}" alt="Letter b">
|
||||
<img src="{{ asset('/assets/images/logo-generator/sunrise/c.png') }}" alt="Letter c">
|
||||
</div>
|
||||
|
||||
<div x-bind:class="{'bg-[var(--color-background)] ring-2 ring-emerald-700': fontType === 'marine'}" class="h-24 rounded border border-[var(--color-text-muted)] p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-[var(--color-background)] cursor-pointer" x-on:click="selectFont('marine')">
|
||||
<img src="{{ asset('/assets/images/logo-generator/marine/a.png') }}" alt="Letter a">
|
||||
<img src="{{ asset('/assets/images/logo-generator/marine/b.png') }}" alt="Letter b">
|
||||
<img src="{{ asset('/assets/images/logo-generator/marine/c.png') }}" alt="Letter c">
|
||||
</div>
|
||||
|
||||
<div x-bind:class="{'bg-[var(--color-background)] ring-2 ring-emerald-700': fontType === 'danlie'}" class="h-24 rounded border border-[var(--color-text-muted)] p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-[var(--color-background)] cursor-pointer" x-on:click="selectFont('danlie')">
|
||||
<img src="{{ asset('/assets/images/logo-generator/danlie/a.png') }}" alt="Letter a">
|
||||
<img src="{{ asset('/assets/images/logo-generator/danlie/b.png') }}" alt="Letter b">
|
||||
<img src="{{ asset('/assets/images/logo-generator/danlie/c.png') }}" alt="Letter c">
|
||||
</div>
|
||||
|
||||
<div x-bind:class="{'bg-[var(--color-background)] ring-2 ring-emerald-700': fontType === 'habton'}" class="h-24 rounded border border-[var(--color-text-muted)] p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-[var(--color-background)] cursor-pointer" x-on:click="selectFont('habton')">
|
||||
<img src="{{ asset('/assets/images/logo-generator/habton/a.png') }}" alt="Letter a">
|
||||
<img src="{{ asset('/assets/images/logo-generator/habton/b.png') }}" alt="Letter b">
|
||||
<img src="{{ asset('/assets/images/logo-generator/habton/c.png') }}" alt="Letter c">
|
||||
</div>
|
||||
|
||||
<div x-bind:class="{'bg-[var(--color-background)] ring-2 ring-emerald-700': fontType === 'habton_capitalized'}" class="h-24 rounded border border-[var(--color-text-muted)] p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-[var(--color-background)] cursor-pointer" x-on:click="selectFont('habton_capitalized')">
|
||||
<img src="{{ asset('/assets/images/logo-generator/habton_capitalized/a.png') }}" alt="Letter a">
|
||||
<img src="{{ asset('/assets/images/logo-generator/habton_capitalized/b.png') }}" alt="Letter b">
|
||||
<img src="{{ asset('/assets/images/logo-generator/habton_capitalized/c.png') }}" alt="Letter c">
|
||||
</div>
|
||||
|
||||
<div x-bind:class="{'bg-[var(--color-background)] ring-2 ring-emerald-700': fontType === 'habbo_modern'}" class="h-24 rounded border border-[var(--color-text-muted)] p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-[var(--color-background)] cursor-pointer" x-on:click="selectFont('habbo_modern')">
|
||||
<img src="{{ asset('/assets/images/logo-generator/habbo_modern/a.png') }}" alt="Letter a">
|
||||
<img src="{{ asset('/assets/images/logo-generator/habbo_modern/b.png') }}" alt="Letter b">
|
||||
<img src="{{ asset('/assets/images/logo-generator/habbo_modern/c.png') }}" alt="Letter c">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<label for="text" class="font-bold"> {{ __('Logo text') }} </label>
|
||||
<input x-model="text" class="mt-2 focus:ring-0 border-4 border-[var(--color-text-muted)] rounded bg-[var(--color-surface)] border-[var(--color-text-muted)] text-[var(--color-text)] focus:border-[var(--color-primary)] w-full" id="text" type="text" name="text" placeholder="Type here...">
|
||||
<div id="logoContainer" class="flex mt-4" :class="text !== '' ? 'mb-4' : ''" style="gap: 2px;" x-html="generateLogoHtml"></div>
|
||||
<div class="flex gap-4 justify-between">
|
||||
<button @click="generateCanvas('download')" class="w-full rounded bg-[var(--color-primary)] text-white p-2 border-2 border-yellow-400 transition ease-in-out duration-200 hover:bg-[var(--color-primary)] font-semibold"> {{ __('Download logo') }} </button>
|
||||
<button @click="generateCanvas('use')" 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"> {{ __('Use logo') }} </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
|
||||
{{-- TODO: Selfhost --}}
|
||||
<script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.3/html2canvas.min.js"></script>
|
||||
<script>
|
||||
function logoGenerator() {
|
||||
return {
|
||||
fontType: 'atom',
|
||||
text: '',
|
||||
get generateLogoHtml() {
|
||||
let sanitizedText = this.text.toLowerCase().replace(/[^a-z ]/g, '');
|
||||
let html = '';
|
||||
for (let i = 0; i < sanitizedText.length; i++) {
|
||||
let letter = sanitizedText[i];
|
||||
html += (letter === ' ') ? '<div style="width: 15px;"></div>' : `<img src="/assets/images/logo-generator/${this.fontType}/${letter}.png" alt="${letter}">`;
|
||||
}
|
||||
return html;
|
||||
},
|
||||
|
||||
async generateCanvas(action) {
|
||||
const logoContainer = document.querySelector('#logoContainer');
|
||||
const originalStyles = logoContainer.getAttribute('style');
|
||||
|
||||
let combinedWidth = 0;
|
||||
let maxHeight = 0;
|
||||
|
||||
logoContainer.childNodes.forEach(child => {
|
||||
const rect = child.getBoundingClientRect();
|
||||
combinedWidth += rect.width;
|
||||
if (rect.height > maxHeight) {
|
||||
maxHeight = rect.height;
|
||||
}
|
||||
});
|
||||
|
||||
combinedWidth += (logoContainer.childNodes.length - 1) * 2;
|
||||
logoContainer.style.display = 'flex';
|
||||
|
||||
setTimeout(async () => {
|
||||
const canvas = await html2canvas(logoContainer, {
|
||||
backgroundColor: null,
|
||||
width: combinedWidth,
|
||||
height: maxHeight
|
||||
});
|
||||
|
||||
logoContainer.setAttribute('style', originalStyles);
|
||||
|
||||
if (action === 'use') {
|
||||
this.uploadLogo(canvas);
|
||||
} else {
|
||||
this.downloadCanvasAsImage(canvas);
|
||||
}
|
||||
}, 100);
|
||||
},
|
||||
|
||||
async uploadLogo(canvas) {
|
||||
canvas.toBlob(async (blob) => {
|
||||
const formData = new FormData();
|
||||
const sanitizedFileName = `${this.text.replace(/[^a-zA-Z0-9]/g, '_')}_${Date.now()}.png`;
|
||||
formData.append('logo', blob, sanitizedFileName);
|
||||
|
||||
await fetch('{{ route('store.generated-logo') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': "{{ csrf_token() }}"
|
||||
},
|
||||
body: formData
|
||||
}).then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: data.message
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = "{{ url()->current() }}";
|
||||
}, 1000);
|
||||
} else {
|
||||
Toast.fire({
|
||||
icon: 'error',
|
||||
title: 'Something went wrong'
|
||||
})
|
||||
}
|
||||
});
|
||||
}, 'image/png');
|
||||
},
|
||||
|
||||
async downloadCanvasAsImage(canvas) {
|
||||
const a = document.createElement('a');
|
||||
const sanitizedFileName = this.text.replace(/[^a-zA-Z0-9]/g, '_');
|
||||
a.href = canvas.toDataURL("image/png", 1.0);
|
||||
a.download = `${sanitizedFileName}.png`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
},
|
||||
|
||||
selectFont(font) {
|
||||
this.fontType = font;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</x-app-layout>
|
||||
|
||||
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>{{ setting('hotel_name') }} - {{ __('Maintenance') }}</title>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/flowbite@1.5.1/dist/flowbite.min.css"/>
|
||||
<script src="https://unpkg.com/flowbite@1.5.1/dist/flowbite.js"></script>
|
||||
|
||||
@vite(['resources/themes/' . setting('theme') . '/css/app.css', 'resources/themes/' . setting('theme') . '/js/app.js'], 'build')
|
||||
|
||||
<style>
|
||||
.gradient-bg {
|
||||
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
|
||||
}
|
||||
.glass-effect {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.task-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% { box-shadow: 0 0 20px rgba(234, 179, 8, 0.3); }
|
||||
50% { box-shadow: 0 0 40px rgba(234, 179, 8, 0.6); }
|
||||
}
|
||||
.countdown-glow {
|
||||
animation: pulse-glow 2s ease-in-out infinite;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="h-screen overflow-hidden relative gradient-bg">
|
||||
<x-messages.flash-messages/>
|
||||
|
||||
<div class="w-full h-full flex">
|
||||
<!-- Left Sidebar - Tasks -->
|
||||
<div class="bg-[#0f172a]/80 backdrop-blur-xl w-[400px] h-full py-8 hidden lg:flex flex-col border-r border-white/10 relative overflow-hidden">
|
||||
<!-- Decorative elements -->
|
||||
<div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-yellow-400 via-yellow-500 to-yellow-600"></div>
|
||||
<div class="absolute -top-20 -right-20 w-40 h-40 bg-yellow-500/10 rounded-full blur-3xl"></div>
|
||||
|
||||
<!-- Logo -->
|
||||
<div class="px-8 mb-6">
|
||||
<img src="{{ setting('cms_logo') }}" alt="" class="h-16 w-auto">
|
||||
</div>
|
||||
|
||||
<!-- Stats Summary -->
|
||||
<div class="px-8 mb-6">
|
||||
<div class="glass-effect rounded-xl p-4">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<span class="text-[var(--color-text-muted)] text-sm uppercase tracking-wider">{{ __('Maintenance Progress') }}</span>
|
||||
@php
|
||||
$totalTasks = ($tasks instanceof \Illuminate\Pagination\LengthAwarePaginator || method_exists($tasks, 'total')) ? $tasks->total() : ($tasks->count() ?? 0);
|
||||
$completedTasks = $tasks->where('completed', true)->count();
|
||||
$progress = $totalTasks > 0 ? round(($completedTasks / $totalTasks) * 100) : 0;
|
||||
@endphp
|
||||
<span class="text-yellow-400 font-bold">{{ $progress }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-[var(--color-background)]/50 rounded-full h-2">
|
||||
<div class="bg-gradient-to-r from-yellow-400 to-yellow-500 h-2 rounded-full transition-all duration-500" style="width: {{ $progress }}%"></div>
|
||||
</div>
|
||||
<div class="flex justify-between mt-2 text-xs text-[var(--color-text-muted)]">
|
||||
<span>{{ $completedTasks }} {{ __('completed') }}</span>
|
||||
<span>{{ $totalTasks }} {{ __('total') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tasks List -->
|
||||
<div class="flex-1 overflow-y-auto px-8">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 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>
|
||||
<h3 class="text-[var(--color-text)] font-semibold">{{ __('Active Tasks') }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
@forelse($tasks as $task)
|
||||
<div class="task-card relative h-[90px] overflow-hidden bg-[#1e293b] rounded-xl p-4 transition-all duration-300 border border-white/5">
|
||||
<!-- Status indicator -->
|
||||
<div class="absolute top-3 right-3">
|
||||
@if($task->completed)
|
||||
<span class="flex items-center gap-1 text-xs text-green-400 bg-green-400/10 px-2 py-1 rounded-full">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
{{ __('Done') }}
|
||||
</span>
|
||||
@else
|
||||
<span class="flex items-center gap-1 text-xs text-yellow-400 bg-yellow-400/10 px-2 py-1 rounded-full">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 animate-spin" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
{{ __('In Progress') }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 h-full">
|
||||
<!-- User Avatar -->
|
||||
<div class="shrink-0">
|
||||
@if($task->user)
|
||||
<img
|
||||
class="w-12 h-12 rounded-lg -lg"
|
||||
style="image-rendering: auto"
|
||||
src="{{ setting('avatar_imager') }}{{ $task->user->look }}&direction=3&head_direction=3&gesture=sml&action=wav&frame=0"
|
||||
alt=""
|
||||
/>
|
||||
@else
|
||||
<div class="w-12 h-12 bg-[var(--color-background)] rounded-lg flex items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-[var(--color-text-muted)]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 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>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Task Info -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-[var(--color-text)] font-medium text-sm truncate">{{ $task->task }}</p>
|
||||
<p class="text-[var(--color-text-muted)] text-xs mt-1">
|
||||
{{ __('By') }}: <span class="text-yellow-400">{{ $task->user?->username ?? 'System' }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="text-center py-8 text-[var(--color-text-muted)]">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mx-auto mb-3 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<p>{{ __('No active maintenance tasks') }}</p>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@if($tasks->hasPages())
|
||||
<div class="mt-6">
|
||||
{{ $tasks->links() }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="px-8 py-4 border-t border-white/10">
|
||||
<div class="flex items-center justify-between text-xs text-[var(--color-text-muted)]">
|
||||
<span>{{ __('Last updated') }}: {{ now()->format('H:i') }}</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></span>
|
||||
<span>{{ __('System Online') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex-1 px-6 lg:px-16 text-[var(--color-text)] h-full flex flex-col justify-center relative z-10 overflow-y-auto">
|
||||
<!-- Background decoration -->
|
||||
<div class="absolute top-0 right-0 w-[600px] h-[600px] bg-yellow-500/5 rounded-full blur-3xl -z-10"></div>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="p-3 bg-yellow-500/20 rounded-xl">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 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" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-4xl lg:text-5xl font-bold text-[var(--color-text)]">{{ __('Maintenance Mode') }}</h2>
|
||||
<p class="text-[var(--color-text-muted)]">{{ setting('hotel_name') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$scheduledAt = setting('maintenance_scheduled_at');
|
||||
$duration = (int) setting('maintenance_duration_minutes', 30);
|
||||
$endTime = $scheduledAt ? \Carbon\Carbon::parse($scheduledAt)->addMinutes($duration) : null;
|
||||
$isScheduled = $endTime && $endTime->isFuture();
|
||||
@endphp
|
||||
|
||||
@if($isScheduled)
|
||||
<!-- Countdown Timer -->
|
||||
<div class="mb-8">
|
||||
<div class="glass-effect rounded-2xl p-6 max-w-md">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span class="text-[var(--color-text-muted)] text-sm uppercase tracking-wider">{{ __('Estimated Completion') }}</span>
|
||||
</div>
|
||||
|
||||
<div id="countdown" class="countdown-glow text-5xl font-mono font-bold text-[var(--color-text)] mb-2" data-end="{{ $endTime->timestamp }}">
|
||||
--:--:--
|
||||
</div>
|
||||
|
||||
<p class="text-[var(--color-text-muted)] text-sm">
|
||||
<span class="text-[var(--color-text-muted)]">{{ __('Date') }}:</span> {{ $endTime->format('d M Y') }}
|
||||
<span class="text-[var(--color-text-muted)] ml-2">{{ __('Time') }}:</span> {{ $endTime->format('H:i') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function updateCountdown() {
|
||||
const countdownEl = document.getElementById('countdown');
|
||||
if (!countdownEl) return;
|
||||
|
||||
const endTime = parseInt(countdownEl.dataset.end) * 1000;
|
||||
const now = Date.now();
|
||||
const diff = endTime - now;
|
||||
|
||||
if (diff <= 0) {
|
||||
countdownEl.textContent = '00:00:00';
|
||||
countdownEl.classList.remove('countdown-glow');
|
||||
return;
|
||||
}
|
||||
|
||||
const hours = Math.floor(diff / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
|
||||
|
||||
countdownEl.textContent =
|
||||
String(hours).padStart(2, '0') + ':' +
|
||||
String(minutes).padStart(2, '0') + ':' +
|
||||
String(seconds).padStart(2, '0');
|
||||
}
|
||||
|
||||
updateCountdown();
|
||||
setInterval(updateCountdown, 1000);
|
||||
</script>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Message -->
|
||||
<article class="glass-effect rounded-2xl p-8 max-w-2xl">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="p-2 bg-yellow-500/20 rounded-lg shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-[var(--color-text)] mb-2">{{ __('Important Information') }}</h3>
|
||||
<div class="prose prose-invert max-w-none text-[var(--color-text-muted)]">
|
||||
<p>{!! setting('maintenance_message') !!}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Additional Info -->
|
||||
<div class="mt-8 flex flex-wrap gap-4">
|
||||
@if(setting('discord_invitation_link'))
|
||||
<a href="{{ is_array(setting('discord_invitation_link')) ? '' : setting('discord_invitation_link') }}" target="_blank"
|
||||
class="glass-effect hover:bg-white/10 rounded-xl px-6 py-3 flex items-center gap-3 transition-all duration-200">
|
||||
<svg class="h-6 w-6 text-[#5865F2]" fill="currentColor" viewBox="0 0 24 24">
|
||||
<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-.028 14.09 14.09 0 0 0 1.226-1.994.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-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/>
|
||||
</svg>
|
||||
<span class="text-[var(--color-text)] font-medium">{{ __('Join our Discord') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if(setting('twitter_url'))
|
||||
<a href="{{ setting('twitter_url') }}" target="_blank"
|
||||
class="glass-effect hover:bg-white/10 rounded-xl px-6 py-3 flex items-center gap-3 transition-all duration-200">
|
||||
<svg class="h-6 w-6 text-[var(--color-text)]" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126h5.835z"/>
|
||||
</svg>
|
||||
<span class="text-[var(--color-text)] font-medium">{{ __('Follow us on X') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Mobile Stats (visible only on mobile) -->
|
||||
<div class="lg:hidden mt-8">
|
||||
<div class="glass-effect rounded-xl p-4">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<span class="text-[var(--color-text-muted)] text-sm">{{ __('Progress') }}</span>
|
||||
@php
|
||||
$totalTasks = ($tasks instanceof \Illuminate\Pagination\LengthAwarePaginator || method_exists($tasks, 'total')) ? $tasks->total() : ($tasks->count() ?? 0);
|
||||
$completedTasks = $tasks->where('completed', true)->count();
|
||||
$progress = $totalTasks > 0 ? round(($completedTasks / $totalTasks) * 100) : 0;
|
||||
@endphp
|
||||
<span class="text-yellow-400 font-bold">{{ $progress }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-[var(--color-background)]/50 rounded-full h-2">
|
||||
<div class="bg-yellow-500 h-2 rounded-full" style="width: {{ $progress }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Background Image -->
|
||||
<img class="absolute bottom-0 right-0 opacity-30 hidden lg:block z-0 pointer-events-none" src="/assets/images/maintenance/hotelview.png" alt="">
|
||||
|
||||
@guest
|
||||
<div class="absolute top-6 right-6 z-50">
|
||||
<x-modals.modal-wrapper>
|
||||
<button @click="open = !open"
|
||||
class="glass-effect hover:bg-white/20 px-6 py-3 rounded-xl font-semibold text-[var(--color-text)] transition duration-200 ease-in-out flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
{{ __('Staff Login') }}
|
||||
</button>
|
||||
|
||||
<x-modals.regular-modal x-model="show {{ session()->get('wrong-auth') }}">
|
||||
<x-auth.login-form/>
|
||||
</x-modals.regular-modal>
|
||||
</x-modals.modal-wrapper>
|
||||
</div>
|
||||
@endguest
|
||||
</body>
|
||||
|
||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" defer></script>
|
||||
|
||||
</html>
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Rare values'))
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
{{-- Statistics Card --}}
|
||||
@if(isset($statistics))
|
||||
<x-content.content-card icon="currency-icon">
|
||||
<x-slot:title>
|
||||
{{ __('Rare Statistics') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Overview of rare values on :hotel', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-center">
|
||||
<div class="p-3 rounded" style="background-color: var(--color-surface);">
|
||||
<div class="text-2xl font-bold" style="color: var(--color-primary)">{{ $statistics['total_categories'] }}</div>
|
||||
<div class="text-sm" style="color: var(--color-text-muted)">{{ __('Categories') }}</div>
|
||||
</div>
|
||||
<div class="p-3 rounded" style="background-color: var(--color-surface);">
|
||||
<div class="text-2xl font-bold" style="color: var(--color-primary)">{{ $statistics['total_rares'] }}</div>
|
||||
<div class="text-sm" style="color: var(--color-text-muted)">{{ __('Total Rares') }}</div>
|
||||
</div>
|
||||
<div class="p-3 rounded" style="background-color: var(--color-surface);">
|
||||
<div class="text-2xl font-bold" style="color: var(--color-primary)">{{ round($statistics['average_rares_per_category']) }}</div>
|
||||
<div class="text-sm" style="color: var(--color-text-muted)">{{ __('Avg per Category') }}</div>
|
||||
</div>
|
||||
<div class="p-3 rounded" style="background-color: var(--color-surface);">
|
||||
<div class="text-lg font-bold truncate" style="color: var(--color-primary)">{{ $statistics['most_valuable_category'] }}</div>
|
||||
<div class="text-sm" style="color: var(--color-text-muted)">{{ __('Top Category') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@endif
|
||||
|
||||
{{-- Search Results Info --}}
|
||||
@if(isset($searchTerm))
|
||||
<x-content.content-card icon="search-icon">
|
||||
<x-slot:title>
|
||||
{{ __('Search Results') }}
|
||||
</x-slot:title>
|
||||
<x-slot:under-title>
|
||||
{{ __('Showing results for: :term', ['term' => $searchTerm]) }}
|
||||
</x-slot:under-title>
|
||||
<p class="text-sm" style="color: var(--color-text-muted)">
|
||||
{{ __('Found :count categories', ['count' => $categories->count()]) }}
|
||||
</p>
|
||||
</x-content.content-card>
|
||||
@endif
|
||||
|
||||
@if(isset($category))
|
||||
{{-- Single Category View with Sorting --}}
|
||||
<x-content.content-card :icon="$category->badge">
|
||||
<x-slot:title>
|
||||
{{ $category->name }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('All :count rares in :category', ['count' => $category->furniture->count(), 'category' => $category->name]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
{{-- Sort Controls --}}
|
||||
<div class="mb-4 flex flex-wrap gap-2">
|
||||
<span class="text-sm self-center" style="color: var(--color-text-muted)">{{ __('Sort by:') }}</span>
|
||||
<a href="{{ route('values.category', ['id' => $category->id, 'sort' => 'name', 'order' => $sortBy === 'name' && $sortOrder === 'asc' ? 'desc' : 'asc']) }}"
|
||||
class="px-3 py-1 rounded text-sm" :style="$sortBy === 'name' ? 'background-color: var(--color-primary); color: var(--button-text-color)' : 'background-color: var(--color-surface); color: var(--color-text)'">
|
||||
{{ __('Name') }} {{ $sortBy === 'name' ? ($sortOrder === 'asc' ? '↑' : '↓') : '' }}
|
||||
</a>
|
||||
<a href="{{ route('values.category', ['id' => $category->id, 'sort' => 'credit_value', 'order' => $sortBy === 'credit_value' && $sortOrder === 'asc' ? 'desc' : 'asc']) }}"
|
||||
class="px-3 py-1 rounded text-sm" :style="$sortBy === 'credit_value' ? 'background-color: var(--color-primary); color: var(--button-text-color)' : 'background-color: var(--color-surface); color: var(--color-text)'">
|
||||
{{ __('Credits') }} {{ $sortBy === 'credit_value' ? ($sortOrder === 'asc' ? '↑' : '↓') : '' }}
|
||||
</a>
|
||||
<a href="{{ route('values.category', ['id' => $category->id, 'sort' => 'currency_value', 'order' => $sortBy === 'currency_value' && $sortOrder === 'asc' ? 'desc' : 'asc']) }}"
|
||||
class="px-3 py-1 rounded text-sm" :style="$sortBy === 'currency_value' ? 'background-color: var(--color-primary); color: var(--button-text-color)' : 'background-color: var(--color-surface); color: var(--color-text)'">
|
||||
{{ __('Currency') }} {{ $sortBy === 'currency_value' ? ($sortOrder === 'asc' ? '↑' : '↓') : '' }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
@foreach($category->furniture->sortBy($sortBy, SORT_REGULAR, $sortOrder === 'desc') as $rare)
|
||||
<x-rares.rare-card :rare="$rare" />
|
||||
@endforeach
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@elseif(isset($categories) && $categories instanceof \Illuminate\Database\Eloquent\Collection)
|
||||
{{-- All Categories View --}}
|
||||
@forelse($categories as $cat)
|
||||
<x-content.content-card :icon="$cat->badge">
|
||||
<x-slot:title>
|
||||
{{ $cat->name }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __(':count rares', ['count' => $cat->furniture->count()]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
@foreach($cat->furniture->take(6) as $rare)
|
||||
<x-rares.rare-card :rare="$rare" />
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if($cat->furniture->count() > 6)
|
||||
<div class="mt-4 text-center">
|
||||
<a href="{{ route('values.category', ['id' => $cat->id]) }}" class="text-primary hover:underline">
|
||||
{{ __('View all :count rares', ['count' => $cat->furniture->count()]) }} →
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
</x-content.content-card>
|
||||
@empty
|
||||
<x-content.content-card icon="currency-icon">
|
||||
<x-slot:title>
|
||||
{{ __('Rare values') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Get an overview of all of the rares on :hotel', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<p class="text-center py-8">
|
||||
{{ __('We currently have no rares listed here') }}
|
||||
</p>
|
||||
</x-content.content-card>
|
||||
@endforelse
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 lg:w-[110%] space-y-4 lg:-ml-[32px]">
|
||||
{{-- Enhanced Search --}}
|
||||
<x-content.content-card icon="catalog-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Search Rares') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Find rares quickly') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form action="{{ route('values.search') }}" method="POST" class="space-y-3">
|
||||
@csrf
|
||||
|
||||
<div class="relative">
|
||||
<x-form.input classes="mb-3 pr-10" name="search" placeholder="Search for a rare..." value="{{ $searchTerm ?? '' }}"/>
|
||||
@if(isset($searchTerm))
|
||||
<a href="{{ route('values.index') }}" class="absolute right-3 top-3 text-[var(--color-text-muted)] hover:text-[var(--color-text)]">
|
||||
✕
|
||||
</a>
|
||||
@endif
|
||||
</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.secondary-button class="w-full">
|
||||
{{ __('Search') }}
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
|
||||
{{-- Quick Stats --}}
|
||||
@if(isset($statistics))
|
||||
<div class="mt-4 pt-4 border-t border-gray-300 dark:border-gray-700">
|
||||
<p class="text-xs text-[var(--color-text-muted)]">
|
||||
{{ __('Searching through :total rares', ['total' => $statistics['total_rares']]) }}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
</x-content.content-card>
|
||||
|
||||
{{-- Categories Navigation --}}
|
||||
<x-content.content-card icon="inventory-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Categories') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __(':count categories', ['count' => $categoriesNav->count() ?? 0]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm text-[var(--color-text)] space-y-2 max-h-96 overflow-y-auto">
|
||||
<a href="{{ route('values.index') }}"
|
||||
class="block rounded py-2 px-4 transition duration-200 ease-in-out hover:scale-[102%] {{ !isset($category) && !isset($searchTerm) ? 'bg-[var(--color-primary)] text-[var(--button-text-color)]' : 'bg-[var(--color-background)]' }}">
|
||||
{{ __('All Rares') }}
|
||||
</a>
|
||||
|
||||
@foreach($categoriesNav ?? [] as $cat)
|
||||
<a href="{{ route('values.category', ['id' => $cat->id]) }}" class="block text-center text-sm transition hover:scale-[102%]" :style="'background-color: ' + (isset($category) && $category->id === $cat->id ? 'var(--color-primary)' : 'var(--color-surface)') + '; color: ' + (isset($category) && $category->id === $cat->id ? 'var(--button-text-color)' : 'var(--color-text)') + '; border-radius: 0.25rem; padding: 0.5rem 1rem;'" style="display: block; text-align: center; border-radius: 0.25rem; padding: 0.5rem 1rem;">
|
||||
{{ $cat->name }}
|
||||
<span class="float-right text-xs opacity-75">({{ $cat->furniture_count ?? $cat->furniture->count() }})</span>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<x-app-layout>
|
||||
@push('title', $room->name)
|
||||
|
||||
<div class="col-span-12">
|
||||
<div class="grid grid-cols-12 gap-y-4 gap-x-2 lg:gap-x-4">
|
||||
<div class="col-span-12 md:col-span-8 sm:flex gap-x-4">
|
||||
<div class="inline-block">
|
||||
<div class="relative">
|
||||
<img
|
||||
class="min-h-[110px] min-w-[110px] rounded"
|
||||
src="{{ setting('room_thumbnail_path') }}/{{ $room->id }}.png"
|
||||
alt="{{ $room->name }}"
|
||||
onerror="this.onerror=null;this.src='{{ asset('/assets/images/profile/room_placeholder.png') }}';"
|
||||
>
|
||||
|
||||
<div class="absolute bottom-1 left-1/2 transform -translate-x-1/2">
|
||||
<div class="{{ $room->users > 0 ? 'bg-[#00800B]' : 'bg-gray-400' }} px-1 py-[1px] font-semibold rounded flex gap-x-[3px] text-white items-center text-xs">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-[12px]" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{{ $room->users }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-[var(--color-text)]">
|
||||
<h5 class="text-xl font-bold">{{ $room->name }}</h5>
|
||||
<a class="flex items-center" href="/profile/{{ $room->owner->username }}">
|
||||
<img class="h-12" src="{{ setting('avatar_imager') }}{{ $room->owner->look }}&direction=2&headonly=1&head_direction=2&gesture=sml" alt="{{ $room->owner->username }}">
|
||||
<p>{{ $room->owner?->username ?? 'Unknown' }}</p>
|
||||
</a>
|
||||
<p class="leading-5">
|
||||
<span class="font-semibold">{{ __('Description:') }} </span>
|
||||
{{ $room->description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<div class="grid grid-cols-1 gap-y-2">
|
||||
<div class="shadow border dark:border-gray-900">
|
||||
<div class="flex gap-x-2 rounded-t border-b bg-[var(--color-surface)] p-3 border-[var(--color-text-muted)]">
|
||||
<p class="font-semibold text-[var(--color-text)]">
|
||||
{{ __('Room details') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section class="rounded-b bg-[var(--color-surface)] p-3 text-[var(--color-text)]">
|
||||
<p>
|
||||
<span class="font-semibold">{{ __('Max users:') }} </span>
|
||||
{{ $room->users_max }}
|
||||
</p>
|
||||
@if (strlen($room->tags) > 0)
|
||||
<p>
|
||||
<span class="font-semibold">Tags: </span>
|
||||
@foreach (explode(";", $room->tags) as $tag)
|
||||
@if (empty($tag) === false)
|
||||
<span class="rounded bg-[var(--color-background)] px-2">{{ $tag }}</span>
|
||||
@endif
|
||||
@endforeach
|
||||
</p>
|
||||
@endif
|
||||
</section>
|
||||
</div>
|
||||
@if ($room->guild !== null)
|
||||
<x-content.content-card icon="{{ $room->guild->badge }}-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('The room guild') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ $room->guild->name }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<p class="text-[14px] text-[var(--color-text-muted)]">
|
||||
{{ $room->guild->description }}
|
||||
</p>
|
||||
</x-content.content-card>
|
||||
<style>
|
||||
.{{ $room->guild->badge }}-icon {
|
||||
background: #f68b08 url("/client/flash/c_images/Badgeparts/generated/{{ $room->guild->badge }}.png") no-repeat center;
|
||||
}
|
||||
</style>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Rules'))
|
||||
|
||||
<div class="col-span-12 flex flex-col gap-y-3">
|
||||
<div class="mb-4 w-full rounded bg-red-600 p-4 text-[var(--color-text)]">
|
||||
{{ __('Rules and regulations are subject to change without notice. As a member of the :hotel community, you hereby agree to and understand the following terms and conditions above. Failure to comply with these rules and regulations will result in the necessary sanctions implemented upon your account. If you have any questions or concerns in regards to The :hotel Way, please do not hesitate to ask a member of the Hotel Staff.', ['hotel' => setting('hotel_name')]) }}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-y-6">
|
||||
@foreach ($categories as $category)
|
||||
<x-content.content-card icon="{{ $category->badge }}" classes="border dark:border-[var(--color-text-muted)]">
|
||||
<x-slot:title>
|
||||
{{ $category->name }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ $category->description }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<ul class="rounded bg-[var(--color-surface)] p-2 dark:bg-[var(--color-background)] dark:text-[var(--color-text-muted)]">
|
||||
@foreach ($category->rules as $rule)
|
||||
<li><strong>{{ $rule->paragraph }}.</strong> {{ $rule->rule }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</x-content.content-card>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Shop'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<x-modals.modal-wrapper>
|
||||
<div class="w-full py-2 px-4 text-center rounded" style="background-color: var(--color-primary); color: var(--button-text-color);">
|
||||
{{ __('Please make sure to read our shop') }}
|
||||
<button class="underline font-bold" style="color: var(--button-text-color);" x-on:click="open = true">{{ __('Terms & Conditions') }}</button>
|
||||
{{ __('before making a purchase') }}
|
||||
</div>
|
||||
|
||||
<x-modals.regular-modal>
|
||||
<x-slot name="title">
|
||||
<h2 class="text-2xl">
|
||||
{{ __('Shop Terms & Conditions') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="space-y-3 p-2">
|
||||
<p>
|
||||
{{ __('Here at :hotel Hotel we are accepting donations to keep the hotel up & running and as a thank you, you will in return receive in-game goods.', ['hotel' => setting('hotel_name')]) }}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col gap-y-2 !mt-6">
|
||||
<p class="font-semibold">{{ __('Why are donations important?') }}</p>
|
||||
|
||||
<p>{{ __('Donations are important, as it will help to pay our monthly bills needed to keep the hotel up & running, as well as adding new and exciting features for you and others to enjoy!') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-y-2 !mt-6">
|
||||
<p class="font-semibold">{{ __('Our terms') }}</p>
|
||||
|
||||
<p>{{ __('Once a donation has been made and received by us, it is non-refundable under any circumstances. The donated amount which is converted into website balance cannot be converted back into cash or other forms of money. By making a donation, you acknowledge and accept these terms and agree not to initiate a chargeback or dispute with your bank or card issuer.') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-y-2 !mt-6">
|
||||
<p class="font-semibold">{{ __('Notice') }}</p>
|
||||
|
||||
<p>{{ __('It is important to consider the consequences of our spending habits, especially when it comes to financial decisions. If you find yourself tempted to spend money you do not have, take a moment to reflect.') }}</p>
|
||||
<p>{{ __('Remember, your financial well-being is crucial, and making responsible choices is key. If you are facing difficulties in controlling your spending habits, do not hesitate to seek friendly and professional guidance. There are resources available that can provide valuable advice and support.') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</x-modals.regular-modal>
|
||||
</x-modals.modal-wrapper>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-3">
|
||||
<x-content.content-card icon="catalog-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Categories') }}
|
||||
</x-slot:title>
|
||||
|
||||
@foreach($categories as $category)
|
||||
<div class="space-y-2">
|
||||
<a href="{{ route('shop.index') }}"
|
||||
class="flex items-center gap-3 py-2 px-4 rounded font-semibold transition ease-in-out duration-150"
|
||||
style="background-color: var(--color-surface); color: var(--color-text);"
|
||||
onmouseover="this.style.backgroundColor='var(--color-primary)'; this.style.color='var(--button-text-color)'"
|
||||
onmouseout="this.style.backgroundColor='var(--color-surface)'; this.style.color='var(--color-text)'">
|
||||
<img class="max-h-[50px] max-w-[50px]" src="{{ asset('/assets/images/icons/navigation/shop.png') }}" alt="">
|
||||
|
||||
{{ __('All') }}
|
||||
</a>
|
||||
|
||||
@foreach($categories as $category)
|
||||
<a href="{{ route('shop.index', $category->slug) }}"
|
||||
class="flex items-center gap-3 py-2 px-4 rounded font-semibold transition ease-in-out duration-150"
|
||||
style="background-color: var(--color-surface); color: var(--color-text);"
|
||||
onmouseover="this.style.backgroundColor='var(--color-primary)'; this.style.color='var(--button-text-color)'"
|
||||
onmouseout="this.style.backgroundColor='var(--color-surface)'; this.style.color='var(--color-text)'">
|
||||
<img class="max-h-[50px] max-w-[50px]" src="{{ $category->icon }}" alt="">
|
||||
|
||||
{{ $category->name }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<div class="flex flex-col gap-y-2 dark:text-gray-300">
|
||||
@foreach ($articles as $article)
|
||||
<x-shop.packages :article="$article" />
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-start-2 md:row-auto col-span-12 flex flex-col gap-y-3 md:col-span-5 lg:col-span-4 xl:col-span-3">
|
||||
<x-content.content-card icon="currency-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Top up account') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Donate to :hotel', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="text-sm text-center py-2 px-4 rounded" style="background-color: var(--color-surface); color: var(--color-text);">
|
||||
{{ auth()->user() ? __('Current balance: $:balance', ['balance' => auth()->user()->website_balance]) : __('Current balance: Login required') }}
|
||||
</div>
|
||||
|
||||
@if(config('paypal.live.client_id') && config('paypal.live.client_secret') && config('paypal.live.client_id') !== 'test_client_id')
|
||||
<form action="{{ route('paypal.process-transaction') }}" method="GET" class="mt-3">
|
||||
@csrf
|
||||
|
||||
<x-form.input name="amount" type="number" value="0" placeholder="amount" />
|
||||
|
||||
<button type="submit" class="mt-2 w-full rounded p-2 border-2 transition ease-in-out duration-150 font-semibold"
|
||||
style="background-color: var(--color-primary); color: var(--button-text-color); border-color: var(--color-primary);">
|
||||
{{ __('Donate') }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<p class="mt-4 text-xs" style="color: var(--color-text);">
|
||||
{{ __('Please setup the paypal credentials to allow for top ups') }}
|
||||
</p>
|
||||
@endif
|
||||
</x-content.content-card>
|
||||
|
||||
|
||||
<x-content.content-card icon="catalog-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Voucher') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Use a voucher for free credit') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form action="{{ route('shop.use-voucher') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<x-form.input classes="mb-3" name="code" type="text" placeholder="Voucher" />
|
||||
|
||||
@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.secondary-button classes="mt-2">
|
||||
{{ __('Use voucher') }}
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
|
||||
@push('javascript')
|
||||
<script type="module">
|
||||
tippy('.user-badge');
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
<x-app-layout>
|
||||
@push('title', auth()->user()->username)
|
||||
|
||||
<div class="col-span-12 space-y-4 lg:col-span-9">
|
||||
<x-user.me-backdrop :user="$user" />
|
||||
|
||||
<div class="rounded-xl border-2 shadow-sm lg:flex lg:items-center lg:justify-between overflow-hidden"
|
||||
style="background-color: var(--color-surface); border-color: var(--border-color);">
|
||||
<div class="flex items-center gap-3 p-3" style="background-color: var(--color-primary);">
|
||||
<img src="{{ asset('/assets/images/icons/online-friends.png') }}" alt="{{ __('Online Friends') }}"
|
||||
class="w-6 h-6">
|
||||
<span class="text-sm font-semibold" style="color: var(--button-text-color)">{{ __('Online Friends') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="relative flex flex-wrap items-center justify-center gap-3 p-3">
|
||||
@foreach ($onlineFriends as $friend)
|
||||
<div data-popover-target="friend-{{ $friend->username }}"
|
||||
style="image-rendering: pixelated; background-image: url({{ setting('avatar_imager') }}{{ $friend->look }}&direction=2&head_direction=3&gesture=sml&action=wav&headonly=1&size=s)"
|
||||
class="inline-block h-12 w-12 rounded-full border-2 bg-center bg-no-repeat hover:border-[var(--border-color)] hover:scale-110 transition-all duration-300 cursor-pointer"
|
||||
style="border-color: var(--color-text-muted)">
|
||||
</div>
|
||||
|
||||
<div data-popover id="friend-{{ $friend->username }}" role="tooltip"
|
||||
class="invisible absolute z-10 inline-block w-64 rounded-lg border shadow-xl opacity-0 transition-opacity duration-300"
|
||||
style="background-color: var(--color-surface); border-color: var(--color-text-muted); color: var(--color-text);">
|
||||
<div class="rounded-t-lg border-b px-4 py-3" style="border-color: var(--color-text-muted); background-color: var(--color-surface);">
|
||||
<div class="flex items-center justify-between font-semibold" style="color: var(--color-text)">
|
||||
{{ $friend->username }}
|
||||
<span class="w-2 h-2 rounded-full bg-green-500"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 py-3 space-y-1">
|
||||
<div class="text-sm">
|
||||
<span class="font-medium" style="color: var(--color-text-muted)">{{ __('Motto') }}:</span>
|
||||
<span class="ml-1" style="color: var(--color-text)">{{ $friend->motto }}</span>
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<span class="font-medium" style="color: var(--color-text-muted)">{{ __('Online Since') }}:</span>
|
||||
<span class="ml-1" style="color: var(--color-text)">{{ date(config('habbo.site.date_format'), $friend->last_online) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div data-popper-arrow class="absolute h-2 w-2 rotate-45 border-l border-b" style="border-color: var(--color-text-muted); background-color: var(--color-surface);"></div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-content.content-card icon="friends-icon" classes="border-2 dark:border-gray-900" style="border-color: var(--border-color);">
|
||||
<x-slot:title>
|
||||
{{ sprintf(__('User Referrals (%s/%s)'), auth()->user()->referrals->referrals_total ?? 0, setting('referrals_needed')) }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Referral new users and be rewarded by in-game goods') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm text-[var(--color-text)] space-y-4">
|
||||
<p>
|
||||
{{ __('Here at :hotel we have added a referral system, allowing you to obtain a bonus for every :needed users that registers through your referral link will allow you to claim a reward of :amount diamonds!', ['hotel' => setting('hotel_name'), 'needed' => setting('referrals_needed'), 'amount' => setting('referral_reward_amount')]) }}
|
||||
</p>
|
||||
|
||||
<div class="bg-yellow-500/10 border border-yellow-500/30 rounded-lg p-3">
|
||||
<p class="text-yellow-400/80 text-xs">
|
||||
⚠️ {{ __('Boosting referrals by making own accounts will lead to removal of all progress, currency, inventory and a potential ban') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-12 gap-2">
|
||||
<x-form.input classes="col-span-12 md:col-span-10 text-sm" name="referral"
|
||||
value="{{ sprintf('%s/register/%s', config('habbo.site.site_url'), auth()->user()->referral_code) }}"
|
||||
:autofocus="false" :readonly="true" />
|
||||
|
||||
<div class="col-span-12 flex md:col-span-2" onclick="copyCode()">
|
||||
<x-form.secondary-button>
|
||||
{{ __('Copy code') }}
|
||||
</x-form.secondary-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (auth()->user()->referrals?->referrals_total >= (int) setting('referrals_needed'))
|
||||
<a href="{{ route('claim.referral-reward') }}" class="text-decoration-none">
|
||||
<x-form.secondary-button classes="mt-2">
|
||||
{{ __('Claim your referrals reward!') }}
|
||||
</x-form.secondary-button>
|
||||
</a>
|
||||
@else
|
||||
<button disabled class="mt-2 w-full rounded bg-[var(--color-text-muted)] p-3 text-[var(--color-text)] cursor-not-allowed opacity-60">
|
||||
{{ sprintf(__('You need to refer :needed more users, before being able to claim your reward', ['needed' =>auth()->user()->referralsNeeded() ?? 0]),auth()->user()->referrals->referrals_total ?? 0) }}
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 space-y-4 lg:col-span-3">
|
||||
<div class="relative w-full" style="height: 213px">
|
||||
<div class="relative swiper articles-slider">
|
||||
<div class="swiper-wrapper">
|
||||
@forelse ($articles as $article)
|
||||
<x-article-card :for-slider="true" :article="$article" />
|
||||
@empty
|
||||
<x-filler-article-card />
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-pagination" style="bottom: 0px !important; z-index: 0;"></div>
|
||||
</div>
|
||||
|
||||
<div class="!mt-3">
|
||||
<x-user.discord-widget />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('javascript')
|
||||
<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);
|
||||
}
|
||||
});
|
||||
|
||||
function copyCode() {
|
||||
let copyText = document.querySelector("#referral");
|
||||
copyText.select();
|
||||
document.execCommand("copy");
|
||||
|
||||
Toast.fire({
|
||||
icon: "success",
|
||||
title: '{{ __('Your referral code has been copied to your clipbord!') }}'
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
<x-app-layout>
|
||||
@push('title', $user->username)
|
||||
|
||||
<div class="col-span-12">
|
||||
<div class="grid grid-cols-1! gap-y-14">
|
||||
<div class="grid grid-cols-3! gap-x-8">
|
||||
<div
|
||||
class="col-span-3 md:col-span-1! h-[150px] lg:h-[220px] profile-bg rounded-lg relative flex gap-x-2 items-center overflow-hidden"
|
||||
style="color: var(--color-text)">
|
||||
<img class="mt-14 lg:mt-0" style="image-rendering: pixelated;"
|
||||
src="{{ setting('avatar_imager') }}{{ $user->look }}&direction=2&head_direction=3&gesture=sml&action=wav&size=l"
|
||||
alt="">
|
||||
|
||||
<div class="flex flex-col">
|
||||
<h3 class="text-xl font-semibold" style="color: var(--color-text-muted)">{{ __('My name is,') }}</h3>
|
||||
<h2 class="text-4xl">
|
||||
{{ $user->username }}
|
||||
</h2>
|
||||
|
||||
<h4 class="text-lg font-semibold italic" style="color: var(--color-text-muted)">{{ $user->motto }}</h4>
|
||||
@if(isset($showOnline) && $showOnline)
|
||||
<small class="text-xs mt-1 opacity-70">{{ __('Last online:') }} {{ $lastLogin ?? 'Unknown' }}</small>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(isset($showStats) && $showStats)
|
||||
<div class="mt-4 flex flex-wrap gap-4 p-3 rounded-lg" style="background: var(--color-surface)">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4" style="color: var(--color-primary)" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<span class="text-sm" style="color: var(--color-text-muted)">{{ __('Account:') }}</span>
|
||||
<span class="font-semibold" style="color: var(--color-primary)">{{ $accountAge ?? 'Unknown' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
<span class="text-sm" style="color: var(--color-text-muted)">{{ __('Friends:') }}</span>
|
||||
<span class="font-semibold text-blue-400">{{ $totalFriends ?? 0 }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"/>
|
||||
</svg>
|
||||
<span class="text-sm" style="color: var(--color-text-muted)">{{ __('Guilds:') }}</span>
|
||||
<span class="font-semibold text-purple-400">{{ $totalGuilds ?? 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div
|
||||
class="col-span-3! mt-4 grid w-full grid-cols-1 space-y-3 md:space-y-0 md:col-span-2! md:mt-0 md:grid-cols-3">
|
||||
<div
|
||||
class="rounded-lg md:rounded-none md:rounded-l-lg bg-[#f8ef2b] flex flex-col gap-y-2 items-center justify-center py-3 md:py-0">
|
||||
<img src="{{ asset('/assets/images/profile/credits.png') }}" alt="">
|
||||
|
||||
<h4 class="text-[#b16d18] font-semibold text-2xl">
|
||||
{{ $user->credits }}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-lg md:rounded-none bg-[#e99bdc] flex flex-col gap-y-2 items-center justify-center py-3 md:py-0">
|
||||
<img src="{{ asset('/assets/images/profile/duckets.png') }}" alt="">
|
||||
|
||||
<h4 class="text-[#812378] font-semibold text-2xl">
|
||||
{{ $user->currency('duckets') }}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-lg md:rounded-none md:rounded-r-lg! bg-[#82d6db] flex flex-col gap-y-2 items-center justify-center py-3 md:py-0">
|
||||
<img src="{{ asset('/assets/images/profile/diamonds.png') }}" alt="">
|
||||
|
||||
<h4 class="text-[#146867] font-semibold text-2xl">
|
||||
{{ $user->currency('diamonds') }}
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-14 grid-cols-1 md:grid-cols-2">
|
||||
<div class="col-span-1">
|
||||
<x-user.profile-info-card col-span="1">
|
||||
<x-slot:image>
|
||||
<img class="h-[140px] object-cover object-left" src="{{ asset('/assets/images/profile/badges.png') }}" alt="">
|
||||
</x-slot:image>
|
||||
|
||||
<x-slot:title>
|
||||
{{ __('Badges') }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="flex flex-wrap gap-2 justify-between">
|
||||
@forelse($badges as $badge)
|
||||
<div data-tippy-content="{{ $badge->badge_code }}"
|
||||
class="user-badge h-[70px] w-[70px] border-2 dark:border-gray-700 rounded-full flex items-center justify-center cursor-pointer">
|
||||
<img src="{{ setting('badges_path') }}/{{ $badge->badge_code }}.gif"
|
||||
class="max-h-[55px] max-w-[55px]" alt="">
|
||||
</div>
|
||||
@empty
|
||||
<div class="col-span-4">
|
||||
{{ __('It seems like :user has no badges.', ['user' => $user->username]) }}
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-user.profile-info-card>
|
||||
</div>
|
||||
|
||||
<div class="col-span-1">
|
||||
<x-user.profile-info-card col-span="1">
|
||||
<x-slot:image>
|
||||
<img class="h-[140px] object-cover object-left" src="{{ asset('/assets/images/profile/groups.png') }}" alt="">
|
||||
</x-slot:image>
|
||||
|
||||
<x-slot:title>
|
||||
{{ __('Groups') }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="flex flex-wrap gap-4 justify-between">
|
||||
@forelse($groups as $group)
|
||||
<div class="h-[70px] w-[70px] rounded-full border-2 dark:border-gray-700 overflow-hidden flex items-center justify-center p-1 rounded-md cursor-pointer friend"
|
||||
data-tippy-content="{{ $group->name ?? 'Unknown' }}">
|
||||
<img src="{{ setting('group_badge_path') }}/{{ $group->badge }}.png"
|
||||
alt="">
|
||||
</div>
|
||||
@empty
|
||||
<div class="w-full">
|
||||
{{ __('It seems like :user is not a member of any groups.', ['user' => $user->username]) }}
|
||||
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-user.profile-info-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-14 grid-cols-1 md:grid-cols-2">
|
||||
<div class="col-span-1">
|
||||
<x-user.profile-info-card col-span="1">
|
||||
<x-slot:image>
|
||||
<img class="h-[140px] object-cover object-left" src="{{ asset('/assets/images/profile/rooms.png') }}" alt="">
|
||||
</x-slot:image>
|
||||
|
||||
<x-slot:title>
|
||||
{{ __('Rooms') }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 space-x-1">
|
||||
@forelse($user->rooms as $room)
|
||||
<div
|
||||
class="flex flex-col gap-y-1 rounded-md bg-[var(--color-surface)] p-1 overflow-hidden">
|
||||
<div class="h-full inline-block">
|
||||
<div class="h-full relative border border-gray-500 dark:border-gray-700 rounded flex items-center justify-center flex-col">
|
||||
<img
|
||||
class="rounded mt-1 mb-1"
|
||||
src="{{ setting('room_thumbnail_path') }}/{{ $room->id }}.png"
|
||||
alt="{{ $room->name }}"
|
||||
onerror="this.onerror=null;this.src='{{ asset('/assets/images/profile/room_placeholder.png') }}';"
|
||||
>
|
||||
|
||||
<div class="absolute bottom-1 left-1/2 transform -translate-x-1/2">
|
||||
<div class="{{ $room->users > 0 ? 'bg-[#00800B]' : 'bg-[var(--color-text-muted)]' }} px-1 py-[1px] font-semibold rounded flex gap-x-[3px] text-[var(--color-text)] items-center text-xs">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-[12px]" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{{ $room->users }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between px-1">
|
||||
<p class="truncate">{{ Str::limit($room->name, 6) }}</p>
|
||||
|
||||
<a href="#">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5 text-cyan-300 hover:text-cyan-400"
|
||||
viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="col-span-4">
|
||||
{{ __('It seems like :user got no rooms.', ['user' => $user->username]) }}
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-user.profile-info-card>
|
||||
</div>
|
||||
|
||||
<div class="col-span-1">
|
||||
<x-user.profile-info-card col-span="1">
|
||||
<x-slot:image>
|
||||
<img class="h-[140px] object-cover object-left" src="{{ asset('/assets/images/profile/friends.png') }}" alt="">
|
||||
</x-slot:image>
|
||||
|
||||
<x-slot:title>
|
||||
{{ __('Friends') }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="grid grid-cols-4 gap-2 xl:grid-cols-6 xl:pl-3">
|
||||
@forelse($friends as $friend)
|
||||
<a href="{{ route('profile.show', $friend->user->username ?? 'SystemAccount') }}"
|
||||
class="h-[70px] w-[70px] rounded-full border-2 dark:border-gray-700 overflow-hidden flex items-center p-1 rounded-md cursor-pointer friend"
|
||||
data-tippy-content="{{ $friend->user->username ?? 'Unknown' }}">
|
||||
<img class="mt-6 transition duration-200 ease-in-out hover:scale-110 min-w-[64px] h-[110px] -ml-1"
|
||||
src="{{ setting('avatar_imager') }}{{ $friend->user?->look }}"
|
||||
alt="">
|
||||
</a>
|
||||
@empty
|
||||
<div class="col-span-6">
|
||||
{{ __('It seems like :user has no friends.', ['user' => $user->username]) }}
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-user.profile-info-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('javascript')
|
||||
<script type="module">
|
||||
tippy('.user-badge');
|
||||
tippy('.friend');
|
||||
tippy('.group');
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user