You've already forked Epicnabbo-Catalogus-Updated-Daily
🆙 Added fixed cms
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@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";
|
||||
|
||||
@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 md:border-b-4 md:border-transparent md:hover:border-b-[#eeb425];
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
@apply block py-2 px-4 font-semibold hover:bg-gray-100 text-gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
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;
|
||||
}
|
||||
+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'],
|
||||
// });
|
||||
@@ -0,0 +1 @@
|
||||
import "./bootstrap";
|
||||
@@ -0,0 +1,335 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@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";
|
||||
|
||||
@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 md:border-b-4 md:border-transparent md:hover:border-b-[#eeb425];
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
@apply block py-2 px-4 font-semibold hover:bg-gray-100 text-gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
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;
|
||||
}
|
||||
|
||||
.badge-drawer-button {
|
||||
background-image: linear-gradient(to bottom, #f2f2f3 51%, #d9d8d8 49%);
|
||||
box-shadow: inset 0 0 0px 2px #d9d9d9;
|
||||
padding: 10px;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to bottom, #ffffff 50%, #EBEBEB 50%);
|
||||
box-shadow: inset 0 0 0px 2px #ffffff;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to bottom, #171717 50%, #111111 50%);
|
||||
box-shadow: inset 0 0 0px 2px #363636;
|
||||
}
|
||||
|
||||
.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%;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50%;
|
||||
background-color: #fff;
|
||||
opacity: .1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
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;",
|
||||
""
|
||||
);
|
||||
@@ -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'],
|
||||
// });
|
||||
@@ -0,0 +1,147 @@
|
||||
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 };
|
||||
@@ -0,0 +1,17 @@
|
||||
import Swiper from 'swiper';
|
||||
import { Autoplay, Pagination } from 'swiper/modules';
|
||||
|
||||
export default () => ({
|
||||
init() {
|
||||
const swiper = new Swiper('.swiper', {
|
||||
modules: [Autoplay, Pagination],
|
||||
autoplay: {
|
||||
delay: 3000,
|
||||
},
|
||||
pagination: {
|
||||
el: '.swiper-pagination',
|
||||
},
|
||||
// andere opties...
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -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 };
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
const defaultTheme = require("tailwindcss/defaultTheme");
|
||||
|
||||
module.exports = {
|
||||
darkMode: "class",
|
||||
content: [
|
||||
"./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php",
|
||||
"./storage/framework/views/*.php",
|
||||
"./resources/**/*.blade.php",
|
||||
"./resources/**/*.js",
|
||||
],
|
||||
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ["Nunito", ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
variants: {
|
||||
extend: {
|
||||
opacity: ["disabled"],
|
||||
},
|
||||
},
|
||||
|
||||
plugins: [
|
||||
require("@tailwindcss/forms"),
|
||||
require("@tailwindcss/typography"),
|
||||
],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -0,0 +1,141 @@
|
||||
<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 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>
|
||||
</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-gray-900">
|
||||
<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>
|
||||
@@ -0,0 +1,36 @@
|
||||
<x-guest-layout>
|
||||
<div class="flex min-h-screen flex-col items-center bg-gray-100 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-gray-600">
|
||||
{{ __('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-none focus:ring 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-gray-600 underline hover:text-gray-900">
|
||||
{{ __('Log Out') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-guest-layout>
|
||||
@@ -0,0 +1,46 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Banned'))
|
||||
<div class="col-span-12 flex justify-center dark:text-gray-100">
|
||||
<div class="space-y-4 lg:w-1/2">
|
||||
<div class="w-full rounded-md bg-red-500 p-2 text-center text-white">
|
||||
{{ __('It seems like you are banned off :hotel', ['hotel' => setting('hotel_name')]) }}
|
||||
</div>
|
||||
|
||||
<div class="rounded-md p-2 shadow bg-white dark:bg-gray-800">
|
||||
<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="{{ 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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,106 @@
|
||||
<!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.scss', 'resources/themes/' . setting('theme') . '/js/app.js'], 'build')
|
||||
</head>
|
||||
|
||||
<body class="overflow-hidden" id="nitro-client">
|
||||
<div class="absolute top-4 left-4 z-10 flex gap-x-2">
|
||||
<a data-turbolinks="false" href="{{ route('me.show') }}">
|
||||
<x-client.client-button>
|
||||
<x-icons.home />
|
||||
</x-client.client-button>
|
||||
</a>
|
||||
|
||||
<div onclick="reloadClient()">
|
||||
<x-client.client-button>
|
||||
<x-icons.reload />
|
||||
</x-client.client-button>
|
||||
</div>
|
||||
|
||||
<div onclick="toggleFullscreen()">
|
||||
<x-client.client-button>
|
||||
<x-icons.fullscreen />
|
||||
</x-client.client-button>
|
||||
</div>
|
||||
|
||||
<x-client.client-button classes="flex items-center justify-center gap-x-1">
|
||||
<x-icons.user />
|
||||
|
||||
<span id="online-count"></span>
|
||||
</x-client.client-button>
|
||||
</div>
|
||||
<iframe id="nitro" src="{{ sprintf('%s/index.html?sso=%s', setting('nitro_path'), $sso) }}"
|
||||
class="absolute top-0 left-0 m-0 h-full w-full overflow-hidden border-none p-0"></iframe>
|
||||
|
||||
{{-- Show disconnected message on client if the user has been disconnected --}}
|
||||
<div id="disconnected" class="h-screen w-full">
|
||||
<div class="absolute h-full w-full bg-black bg-opacity-50"></div>
|
||||
|
||||
<div class="relative flex h-full w-full flex-col items-center justify-center gap-4">
|
||||
<h2 class="text-2xl text-white">
|
||||
{{ __('Whoops! It seems like you have been disconnected...') }}
|
||||
</h2>
|
||||
|
||||
<div class="flex gap-x-4">
|
||||
<button
|
||||
class="py-2 px-4 text-white rounded bg-[#eeb425] hover:bg-[#e3aa1e] border-2 border-[#cf9d15] transition ease-in-out"
|
||||
onclick="reloadClient()">
|
||||
{{ __('Reload client') }}
|
||||
</button>
|
||||
|
||||
<a href="{{ route('me.show') }}">
|
||||
<x-form.secondary-button>
|
||||
{{ __('Back to website') }}
|
||||
</x-form.secondary-button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleFullscreen() {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
document.documentElement.requestFullscreen();
|
||||
}
|
||||
|
||||
function reloadClient() {
|
||||
window.location.href = window.location;
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
function getOnlineUserCount() {
|
||||
fetch('{{ route('api.online-count') }}')
|
||||
.then(function(response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function(response) {
|
||||
document.getElementById('online-count').innerHTML = response.data.onlineCount;
|
||||
});
|
||||
}
|
||||
|
||||
getOnlineUserCount();
|
||||
|
||||
setInterval(function() {
|
||||
getOnlineUserCount();
|
||||
}, 15000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="{{ asset('assets/js/atom.js') }}"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -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 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 drop-shadow">
|
||||
<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 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 text-white"
|
||||
style="background: url(/storage/{{ $article->image }}) center; background-size: cover;">
|
||||
<div class="absolute h-full w-full bg-black bg-opacity-50"></div>
|
||||
|
||||
<p class="relative w-full truncate text-center text-xl font-semibold lg:text-2xl xl:text-3xl">
|
||||
{{ $article->title }}</p>
|
||||
<p class="relative w-full truncate text-center">{{ $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-[#eeb425] 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->sortByDesc('created_at') as $comment)
|
||||
<div
|
||||
class="relative w-full rounded bg-[#f5f5f5] dark:bg-gray-700 p-4 h-[90px] overflow-hidden flex items-center shadow">
|
||||
<a href="{{ route('profile.show', $comment->user) }}"
|
||||
class="absolute top-2 left-1 drop-shadow">
|
||||
<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>
|
||||
+76
@@ -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-yellow-400 cursor-pointer bg-[#eeb425] 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-sm 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
|
||||
@@ -0,0 +1,25 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Photos'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<x-content.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.content-card>
|
||||
|
||||
{{ $photos->links() }}
|
||||
</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>
|
||||
@@ -0,0 +1,67 @@
|
||||
<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 classes="bg-red-200" name="username" value="{{ auth()->user()->username }}"
|
||||
:readonly="true" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-form.label for="username" 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-[#eeb425] w-full min-h-[180px]"></textarea>
|
||||
</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 field out all the fields to apply for :position. Remember when applying for a position here at :hotel you must be fully transparent and honest. If found out the information provided is false or incorrect you might risk losing your position if hired.', ['position' => $position->permission->rank_name, 'hotel' => setting('hotel_name')]) }}
|
||||
</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,67 @@
|
||||
<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)
|
||||
<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()->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,76 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Staff'))
|
||||
|
||||
<div class="col-span-12 lg:col-span-9 lg:w-[96%]">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
@foreach ($employees as $employee)
|
||||
<x-content.staff-content-section :badge="$employee->badge" :color="$employee->staff_color">
|
||||
<x-slot:title>
|
||||
{{ $employee->rank_name }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ $employee->job_description }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
@foreach ($employee->users as $staff)
|
||||
<x-community.staff-card :user="$staff" />
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if (count($employee->users) === 0)
|
||||
<div class="text-center dark:text-gray-400">
|
||||
{{ __('We currently have no staff in this position') }}
|
||||
</div>
|
||||
@endif
|
||||
</x-content.staff-content-section>
|
||||
@endforeach
|
||||
</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>
|
||||
{{ __(':hotel staff', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('About the :hotel staff', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<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>
|
||||
</x-content.content-card>
|
||||
|
||||
<x-content.content-card icon="chat-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Apply for staff') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('How to join the staff team', ['hotel' => setting('hotel_name')]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm space-y-4 dark:text-gray-200">
|
||||
<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>
|
||||
|
||||
<p>
|
||||
{!! __(
|
||||
'You can occasionally also look at the :startTag Staff application page :endTag which will show you all of our current open positions.',
|
||||
['startTag' => '<a href="/community/staff-applications" class="underline">', 'endTag' => '</a>'],
|
||||
) !!}
|
||||
</p>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,31 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Staff'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
@foreach ($employees as $employee)
|
||||
<x-content.staff-content-section :badge="$employee->badge" :color="$employee->staff_color">
|
||||
<x-slot:title>
|
||||
{{ $employee->rank_name }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ $employee?->job_description }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
@foreach ($employee->users as $staff)
|
||||
<x-community.staff-card :user="$staff" />
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if (count($employee->users) === 0)
|
||||
<div class="text-center dark:text-gray-400">
|
||||
{{ __('We currently have no staff in this position') }}
|
||||
</div>
|
||||
@endif
|
||||
</x-content.staff-content-section>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,44 @@
|
||||
@props(['article', 'forSlider' => false])
|
||||
|
||||
<div @class([
|
||||
'h-[210px] dark:bg-gray-900 rounded w-full bg-white shadow relative overflow-hidden transition ease-in-out duration-200',
|
||||
'hover:scale-[101%]' => !$forSlider,
|
||||
'swiper-slide group' => $forSlider,
|
||||
]) @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-4 px-4">
|
||||
<p @class([
|
||||
'font-semibold text-lg truncate dark:text-gray-200',
|
||||
'group-hover:text-[#e9b124] transition duration-200' => $forSlider,
|
||||
])>
|
||||
{{ $article->title }}
|
||||
</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') }}{{ $article->user?->look }}&headonly=1" alt="">
|
||||
</div>
|
||||
|
||||
<p class="mt-4 font-semibold dark:text-gray-400">
|
||||
{{ $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,50 @@
|
||||
<x-slot name="title">
|
||||
<h2 class="text-2xl font-semibold">{{ __('Hello!') }}</h2>
|
||||
<p class="dark:text-gray-400">
|
||||
{{ __('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 text-gray-700 dark:text-gray-400">
|
||||
<a href="{{ route('forgot.password.get') }}" class="hover:underline" x-on:click="open = false">
|
||||
{{ __('Did you forget your password?') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-center text-sm font-semibold text-gray-700 dark:text-gray-400">
|
||||
<a href="{{ route('register') }}" class="hover:underline" x-on:click="open = false">
|
||||
{{ __('Dont have an account? Join now!') }}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,6 @@
|
||||
@props(['classes' => ''])
|
||||
|
||||
<div
|
||||
class="text-white rounded bg-[#eeb425] hover:bg-[#e3aa1e] border-[2px] border-[#cf9d15] transition ease-in-out text-center rounded py-1 px-2 text-sm text-white cursor-pointer {{ $classes }}">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
@props(['user'])
|
||||
|
||||
<div class="relative h-24 w-full overflow-hidden rounded border bg-white dark:border-gray-900 dark:bg-gray-700 md:mt-0">
|
||||
<div class="absolute top-1 right-1 rounded bg-white px-2 text-sm font-semibold dark:bg-gray-900 dark:text-gray-300">
|
||||
{{ $user->permission->rank_name }}
|
||||
</div>
|
||||
|
||||
<div class="h-[65%] w-full staff-bg"
|
||||
style="background: rgba(0, 0, 0, 0.5) url({{ asset(sprintf('assets/images/%s', $user->permission->staff_background)) }});">
|
||||
</div>
|
||||
|
||||
<div class="absolute top-4 left-1 drop-shadow">
|
||||
<a href="{{ route('profile.show', $user->username) }}">
|
||||
<img style="image-rendering: pixelated;" class="transition duration-300 ease-in-out hover:scale-105"
|
||||
src="{{ setting('avatar_imager') }}{{ $user->look }}&direction=2&head_direction=3&gesture=sml&action=wav"
|
||||
alt="">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p class="text-2xl font-semibold ml-[70px] text-white -mt-[35px]">
|
||||
{{ $user->username }}
|
||||
</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 truncate">
|
||||
{{ Str::limit($user->motto, 20) }}
|
||||
</p>
|
||||
|
||||
<div
|
||||
class="min-w-[15px] max-w-[15px] min-h-[15px] max-h-[15px] rounded-full mt-2 flex items-start {{ $user->online ? 'bg-green-600' : 'bg-red-600' }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
@props(['icon' => '', 'classes' => ''])
|
||||
|
||||
<div class="w-full flex flex-col gap-y-4 rounded overflow-hidden bg-white pb-3 dark:bg-gray-800 shadow {{ $classes }}">
|
||||
<div class="flex gap-x-2 border-b bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-900">
|
||||
@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 text-black dark:text-gray-300">{{ $title }}</p>
|
||||
|
||||
@if(isset($underTitle))
|
||||
<p class="dark:text-gray-500">{{ $underTitle }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="h-full flex flex-col px-3">
|
||||
{{ $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 bg-white pb-3 dark:bg-gray-800 shadow {{ $classes }}">
|
||||
<div class="flex gap-x-2 border-b bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-900">
|
||||
@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 text-black dark:text-gray-300">{{ $title }}</p>
|
||||
|
||||
@if(isset($underTitle))
|
||||
<p class="dark:text-gray-500">{{ $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 bg-white pb-3 shadow dark:bg-gray-800">
|
||||
<div class="flex gap-x-2 border-b bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-900">
|
||||
<div class="max-w-[50px] max-h-[50px] min-w-[50px] min-h-[50px] 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 text-black dark:text-gray-300">{{ $title }}</p>
|
||||
|
||||
@if(isset($underTitle))
|
||||
<p class="dark:text-gray-500">{{ $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 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>
|
||||
@@ -0,0 +1,53 @@
|
||||
<footer
|
||||
class="mt-auto flex h-14 w-full flex-col items-center justify-center bg-gray-100 text-sm text-gray-400 dark:bg-gray-900 md:flex-row md:justify-center md:px-8"
|
||||
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,6 @@
|
||||
@props(['classes' => '', 'type' => 'submit'])
|
||||
|
||||
<button type="{{ $type }}"
|
||||
class="{{ $classes }} w-full rounded bg-red-500 hover:bg-red-600 text-white p-2 border-2 border-red-400 transition ease-in-out duration-150 font-semibold">
|
||||
{{ $slot }}
|
||||
</button>
|
||||
@@ -0,0 +1,14 @@
|
||||
@props(['errorBag' => '', 'classes' => '', 'name', 'type' => 'text', 'value' => '', 'placeholder' => '', 'required' => true, 'autofocus' => false, 'readonly' => false])
|
||||
|
||||
<input
|
||||
class="{{ $classes }} focus:ring-0 border-4 border-gray-200 rounded dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 focus:border-[#eeb425] w-full @error($name, $errorBag) border-red-600 ring-red-500 @enderror"
|
||||
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>
|
||||
|
||||
@error($name, $errorBag)
|
||||
<p class="mt-1 text-xs italic text-red-500">
|
||||
{{ $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,6 @@
|
||||
@props(['classes' => '', 'type' => 'submit'])
|
||||
|
||||
<button type="{{ $type }}"
|
||||
class="{{ $classes }} w-full rounded bg-[#eeb425] text-white p-2 border-2 border-yellow-400 transition ease-in-out duration-200 hover:bg-[#d49f1c] font-semibold">
|
||||
{{ $slot }}
|
||||
</button>
|
||||
@@ -0,0 +1,6 @@
|
||||
@props(['classes' => '', 'type' => 'submit'])
|
||||
|
||||
<button type="{{ $type }}"
|
||||
class="{{ $classes }} 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">
|
||||
{{ $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: 218 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: 363 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: 541 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: 300 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: 377 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: 354 B |
@@ -0,0 +1,49 @@
|
||||
@props([
|
||||
'title',
|
||||
'icon',
|
||||
'data',
|
||||
'valueKey',
|
||||
'valueType',
|
||||
'relationship' => null,
|
||||
'formatValue' => null,
|
||||
])
|
||||
|
||||
<div class="rounded bg-white p-2 shadow dark:bg-gray-900">
|
||||
<div class="flex justify-center gap-x-1 text-center font-semibold text-gray-700 dark:text-gray-300">
|
||||
<div class="flex items-center">
|
||||
<img src="{{ asset('/assets/images/icons/' . $icon) }}" alt="{{ $title }}" class="w-4" style="image-rendering: pixelated;">
|
||||
</div>
|
||||
{{ __($title) }}
|
||||
</div>
|
||||
<hr class="dark:border-gray-500">
|
||||
|
||||
<div class="mt-4 flex flex-col gap-y-3">
|
||||
@foreach ($data as $index => $entry)
|
||||
<div class="p-3 rounded bg-gray-100 flex gap-x-2 items-center h-[70px] overflow-hidden dark:bg-gray-800">
|
||||
<div @class([
|
||||
'w-10 h-10 rounded-full bg-gray-300 flex items-center justify-center',
|
||||
'leaderboard-first' => $index + 1 == 1,
|
||||
'leaderboard-second' => $index + 1 == 2,
|
||||
'leaderboard-third' => $index + 1 == 3,
|
||||
])>
|
||||
{{ $index + 1 }}
|
||||
</div>
|
||||
|
||||
<img @class([
|
||||
'mt-8' => !Str::contains(setting('avatar_imager'), 'www.habbo.com'),
|
||||
])
|
||||
src="{{ setting('avatar_imager') }}{{ $relationship ? $entry->{$relationship}?->look : $entry->look }}&size=b&head_direction=2&gesture=sml&headonly=1"
|
||||
alt="" />
|
||||
|
||||
<div class="flex flex-col">
|
||||
<p class="font-bold text-gray-700 dark:text-gray-100">
|
||||
{{ $relationship ? $entry->{$relationship}?->username : $entry->username }}
|
||||
</p>
|
||||
<p class="text-gray-600 dark:text-gray-300">
|
||||
{{ $formatValue ? $formatValue($entry->{$valueKey}) : $entry->{$valueKey} }} {{ $valueType }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</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,30 @@
|
||||
<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 bg-opacity-50"></div>
|
||||
|
||||
<div x-on:click.stop x-trap.noscroll.inert="open"
|
||||
class="relative w-full rounded bg-white px-6 py-6 text-black shadow-md dark:bg-gray-900 dark:text-gray-200 md:w-1/2 lg:w-1/3 lg:px-8 xl:w-1/4/2 xl:w-1/4">
|
||||
<button type="button" x-on:click="open = false"
|
||||
class="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-800 dark:hover:text-white">
|
||||
<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 dark:text-gray-200 dark:hover:bg-gray-700', $classes])>
|
||||
{{ $slot }}
|
||||
</a>
|
||||
@@ -0,0 +1,70 @@
|
||||
@props(['icon', 'routeGroup' => '', 'classes' => '', 'childClasses' => 'min-w-[150px]', 'uppercase' => false])
|
||||
|
||||
<div
|
||||
x-data="{
|
||||
open: false,
|
||||
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-id="['dropdown-button']"
|
||||
@class([
|
||||
'relative h-auto md:h-[60px] text-[14px] font-semibold text-gray-700 md:border-b-4 md:border-transparent md:hover:border-b-[#eeb425] transition duration-200 ease-in-out dark:text-gray-200 z-5',
|
||||
$classes,
|
||||
'md:border-b-4 md:border-b-[#eeb425]' => 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 md:gap-2 h-full',
|
||||
'uppercase' => $uppercase,
|
||||
])"
|
||||
>
|
||||
@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 text-gray-400" 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-transition.origin.top.left
|
||||
x-on:click.outside="close($refs.button)"
|
||||
:id="$id('dropdown-button')"
|
||||
style="display: none;"
|
||||
@class(['absolute left-0 mt-2 rounded bg-white dark:bg-gray-800 shadow whitespace-nowrap overflow-hidden z-10', $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 (DB::table('website_languages')->get() 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,11 @@
|
||||
<button data-collapse-toggle="mobile-menu" type="button"
|
||||
class="absolute right-4 top-4 z-10 hover:text-gray-900 dark:hover:text-white dark:text-white md:hidden"
|
||||
aria-controls="mobile-menu-2" 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>
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<div class="relative hidden flex 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">
|
||||
@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 dark:text-gray-200 {{ request()->routeIs('welcome') ? 'md:border-b-4 md:border-b-[#eeb425]' : '' }}">
|
||||
<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('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 dark:text-gray-200 {{ request()->routeIs('leaderboard.*') ? 'md:border-b-4 md:border-b-[#eeb425]' : '' }}">
|
||||
<i class="navigation-icon leaderboards mr-1 hidden lg:inline-flex"></i>
|
||||
{{ __('Leaderboards') }}
|
||||
</a>
|
||||
|
||||
<a href="{{ route('values.index') }}"
|
||||
class="nav-item dark:text-gray-200 {{ request()->routeIs('values.*') ? 'md:border-b-4 md:border-b-[#eeb425]' : '' }}">
|
||||
<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 dark:text-gray-200 {{ request()->routeIs('shop.*') ? 'md:border-b-4 md:border-b-[#eeb425]' : '' }}">
|
||||
<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="{{ setting('discord_invitation_link') }}" target="_blank" class="nav-item dark:text-gray-200">
|
||||
{{ __('Discord') }}
|
||||
</a>
|
||||
|
||||
<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,36 @@
|
||||
<button id="dropdownNavbarLink" data-dropdown-toggle="dropdownNavbarUser"
|
||||
class="ml-4 flex items-center dark:text-gray-200">
|
||||
<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 block hidden w-44 bg-white py-2 shadow dark:bg-gray-800">
|
||||
<a href="{{ route('settings.account.show') }}"
|
||||
class="block px-4 py-2 font-semibold hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-gray-200">
|
||||
{{ __('User settings') }}
|
||||
</a>
|
||||
|
||||
@auth
|
||||
<a href="{{ route('logout') }}"
|
||||
class="block px-4 py-2 font-semibold hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-gray-200"
|
||||
onclick="event.preventDefault();
|
||||
document.getElementById('logout-form').submit();">
|
||||
{{ __('Logout') }}
|
||||
</a>
|
||||
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="hidden">
|
||||
@csrf
|
||||
</form>
|
||||
@endauth
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
@props(['photos'])
|
||||
|
||||
<div class="grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-4">
|
||||
@foreach ($photos as $photo)
|
||||
<a href="{{ $photo->url }}" data-fancybox="gallery" class="cursor-pointer">
|
||||
<div class="rounded border-2 dark:border-gray-600 h-[280px] relative object-fill overflow-hidden">
|
||||
<img class="h-full w-full object-cover object-center" src="{{ $photo->url }}" alt="">
|
||||
<div class="absolute bottom-3 left-4 flex items-center gap-x-3 rounded-full bg-white pr-3 dark:bg-gray-800">
|
||||
<div class="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-gray-100 dark:bg-gray-900">
|
||||
<img src="{{ setting('avatar_imager') }}{{ $photo->user->look ?? '' }}&direction=2&headonly=1&head_direction=2&gesture=sml" alt="">
|
||||
</div>
|
||||
|
||||
<p class="dark:text-white">
|
||||
{{ $photo->user->username ?? __('Unknown') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@@ -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 bg-yellow-400 rounded h-[35px] flex items-center mt-2">
|
||||
<div class="bg-yellow-500 rounded-l w-1/3 px-4 h-full flex items-center justify-center">
|
||||
<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,75 @@
|
||||
<div class="relative flex 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 bg-opacity-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="drop-shadow transition duration-300 ease-in-out hover:scale-105"
|
||||
src="{{ setting('cms_logo') }}" alt="Hotel logo">
|
||||
</a>
|
||||
|
||||
<div
|
||||
class="hidden md:flex items-center bg-white dark:bg-gray-900 dark:text-white px-4 rounded-md relative h-[50px]">
|
||||
<div class="absolute -left-1 h-6 w-6 rotate-45 bg-white dark:bg-gray-900"></div>
|
||||
|
||||
<span class="relative">
|
||||
{{ __(':online :hotel online', ['online' => DB::table('users')->where('online', '1')->count(),'hotel' => setting('hotel_name')]) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<flex class="flex gap-x-4">
|
||||
<a data-turbolinks="false" href="{{ route('nitro-client') }}">
|
||||
<button
|
||||
class="relative hidden rounded-full bg-white bg-opacity-90 px-6 py-2 text-lg font-semibold text-black transition duration-300 ease-in-out hover:bg-opacity-100 dark:bg-gray-900 dark:text-white md:block">
|
||||
{{ __('Nitro client') }}
|
||||
</button>
|
||||
</a>
|
||||
|
||||
@if (config('habbo.client.flash_enabled'))
|
||||
<a data-turbolinks="false" href="{{ route('flash-client') }}">
|
||||
<button
|
||||
class="relative hidden rounded-full bg-white bg-opacity-90 px-6 py-2 text-lg font-semibold text-black transition duration-300 ease-in-out hover:bg-opacity-100 dark:bg-gray-900 dark:text-white md:block">
|
||||
{{ __('Flash client') }}
|
||||
</button>
|
||||
</a>
|
||||
@endif
|
||||
</flex>
|
||||
</div>
|
||||
@endauth
|
||||
|
||||
@guest
|
||||
<x-modals.modal-wrapper>
|
||||
<div class="flex justify-center">
|
||||
<div class="text-white font-semibold flex-col md:w-[600px]">
|
||||
<p class="hidden text-center text-xl md:block">
|
||||
{{ __('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 border-white px-8 py-2 uppercase transition duration-200 ease-in-out hover:bg-white hover:text-black">
|
||||
{{ __('Login') }}
|
||||
</button>
|
||||
|
||||
<p class="text-sm uppercase text-opacity-80">{{ __('Or') }}</p>
|
||||
|
||||
<a data-turbolinks="false" href="{{ route('register') }}">
|
||||
<button
|
||||
class="uppercase bg-green-600 bg-opacity-80 px-8 py-2.5 rounded-full transition ease-in-out duration-200 hover:bg-opacity-100">
|
||||
{{ __('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="hidden gap-x-3 md:flex">
|
||||
<div class="h-[25px] w-[25px] rounded-full {{ $icon }} outline-offset-[3px]"></div>
|
||||
|
||||
<div class="dark:text-gray-400">
|
||||
<span class="font-semibold dark:text-white">
|
||||
{{ $currency }}
|
||||
</span>
|
||||
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,81 @@
|
||||
<div class="max-w-7xl min-h-[60px] px-4 md:flex md:items-center md:justify-between md:mx-auto">
|
||||
<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>
|
||||
|
||||
<div class="flex gap-x-3">
|
||||
@if(hasPermission('view_server_logs') || hasPermission('housekeeping_access') || hasPermission('generate_logo'))
|
||||
<x-navigation.dropdown classes="!text-red-700 !border-none">
|
||||
{{ __('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>
|
||||
@endif
|
||||
|
||||
@if (hasPermission('view_server_logs'))
|
||||
<x-navigation.dropdown-child route="/log-viewer" :turbolink="false" target="_blank">
|
||||
{{ __('Error logs') }}
|
||||
</x-navigation.dropdown-child>
|
||||
@endif
|
||||
|
||||
@if(hasPermission('housekeeping_access'))
|
||||
<a data-turbolinks="false" href="{{ setting('housekeeping_url') }}" target="_blank" class="dropdown-item dark:text-gray-200 dark:hover:bg-gray-700">
|
||||
{{ __('Housekeeping') }}
|
||||
</a>
|
||||
@endif
|
||||
</x-slot:children>
|
||||
</x-navigation.dropdown>
|
||||
@endif
|
||||
|
||||
<x-navigation.dropdown classes="!border-none">
|
||||
<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>
|
||||
|
||||
<x-slot:children>
|
||||
<x-navigation.dropdown-child :route="route('settings.account.show')">
|
||||
{{ __('User settings') }}
|
||||
</x-navigation.dropdown-child>
|
||||
|
||||
<button class="dropdown-item dark:text-gray-200 dark:hover:bg-gray-700 w-full text-left" @click.prevent="document.getElementById('logout-form').submit();">
|
||||
{{ __('Logout') }}
|
||||
</button>
|
||||
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="hidden">
|
||||
@csrf
|
||||
</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,21 @@
|
||||
|
||||
@props(['user'])
|
||||
|
||||
<div class="relative flex items-center justify-between overflow-hidden rounded px-10 me-backdrop"
|
||||
style="background: rgba(0, 0, 0, 0.3) url({{ setting('cms_me_backdrop') }});">
|
||||
<div>
|
||||
<a href="{{ route('profile.show', $user) }}"
|
||||
class="absolute -bottom-12 left-0 drop-shadow transition duration-300 ease-in-out hover:scale-105">
|
||||
<img style="image-rendering: pixelated;"
|
||||
src="{{ setting('avatar_imager') }}{{ $user->look }}&direction=2&head_direction=3&gesture=sml&action=wav&size=l"
|
||||
alt="">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a data-turbolinks="false" href="{{ route('nitro-client') }}">
|
||||
<button
|
||||
class="relative rounded-full bg-white bg-opacity-90 px-6 py-2 text-lg font-semibold text-black transition duration-300 ease-in-out hover:bg-opacity-100 dark:bg-gray-900 dark:text-white">
|
||||
{{ __('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 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-900">
|
||||
<p class="font-semibold text-black dark:text-white">{{ $title }}</p>
|
||||
</div>
|
||||
|
||||
<section class="rounded-b bg-white p-3 dark:bg-gray-800 dark:text-white">
|
||||
{{ $slot }}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<a data-turbolinks="false" href="{{ route('settings.account.show') }}"
|
||||
class="{{ request()->routeIs('settings.account.show') ? 'bg-[#eeb425] text-white' : 'bg-gray-100 dark:bg-gray-900' }} dark:text-gray-100 flex gap-x-2 justify-center items-center rounded p-2 md:p-6 text-center md:text-xl font-semibold transition duration-200 ease-in-out hover:bg-[#eeb425] hover:text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" 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>
|
||||
|
||||
{{ __('Account settings') }}
|
||||
</a>
|
||||
|
||||
<a data-turbolinks="false" href="{{ route('settings.password.show') }}"
|
||||
class="{{ request()->routeIs('settings.password.show') ? 'bg-[#eeb425] text-white' : 'bg-gray-100 dark:bg-gray-900' }} dark:text-gray-100 flex gap-x-2 justify-center rounded p-2 md:p-6 text-center md:text-xl font-semibold transition duration-200 ease-in-out hover:bg-[#eeb425] hover:text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" 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>
|
||||
|
||||
{{ __('Password settings') }}
|
||||
</a>
|
||||
|
||||
<a data-turbolinks="false" href="{{ route('settings.two-factor') }}"
|
||||
class="{{ request()->routeIs('settings.two-factor') ? 'bg-[#eeb425] text-white' : 'bg-gray-100 dark:bg-gray-900' }} dark:text-gray-100 flex gap-x-2 justify-center rounded p-2 md:p-6 text-center md:text-xl font-semibold transition duration-200 ease-in-out hover:bg-[#eeb425] hover:text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
||||
class="h-6 w-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M7.864 4.243A7.5 7.5 0 0119.5 10.5c0 2.92-.556 5.709-1.568 8.268M5.742 6.364A7.465 7.465 0 004.5 10.5a7.464 7.464 0 01-1.15 3.993m1.989 3.559A11.209 11.209 0 008.25 10.5a3.75 3.75 0 117.5 0c0 .527-.021 1.049-.064 1.565M12 10.5a14.94 14.94 0 01-3.6 9.75m6.633-4.596a18.666 18.666 0 01-2.485 5.33" />
|
||||
</svg>
|
||||
|
||||
{{ __('Two factor') }}
|
||||
</a>
|
||||
|
||||
<a data-turbolinks="false" href="{{ route('settings.session-logs') }}"
|
||||
class="{{ request()->routeIs('settings.session-logs') ? 'bg-[#eeb425] text-white' : 'bg-gray-100 dark:bg-gray-900' }} dark:text-gray-100 flex gap-x-2 justify-center rounded p-2 md:p-6 text-center md:text-xl font-semibold transition duration-200 ease-in-out hover:bg-[#eeb425] hover:text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<rect x="5" y="3" width="14" height="18" rx="2" />
|
||||
<line x1="9" y1="7" x2="15" y2="7" />
|
||||
<line x1="9" y1="11" x2="15" y2="11" />
|
||||
<line x1="9" y1="15" x2="13" y2="15" />
|
||||
</svg>
|
||||
|
||||
{{ __('Session logs') }}
|
||||
</a>
|
||||
@@ -0,0 +1,639 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Badge Generator'))
|
||||
|
||||
<script>
|
||||
const translations = {
|
||||
buy_confirmation: @json(__('badge_purchase_confirmation', ['cost' => $cost, 'currency' => $currencyType])),
|
||||
purchase_success: @json(__('badge_purchase_success', ['currency' => ucfirst($currencyType)])),
|
||||
purchase_error_insufficient: @json(__('badge_purchase_error_insufficient', ['currency' => $currencyType])),
|
||||
purchase_error_general: @json(__('badge_purchase_error_general')),
|
||||
missing_fields: @json(__('Please fill in the badge name, description, and draw something on the canvas.')),
|
||||
invalid_content: @json(__('Badge name and description cannot contain URLs.')),
|
||||
invalid_file_type: @json(__('Only PNG and GIF files are allowed.'))
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="col-span-12 flex flex-col lg:grid grid-cols-4 gap-4" x-data="badgeDrawer({ cost: {{ $cost }}, currencyType: '{{ $currencyType }}' })">
|
||||
<x-content.content-card icon="hotel-icon" classes="border dark:border-gray-900 col-span-3">
|
||||
<x-slot:title>
|
||||
{{ __('Badge Drawer') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Draw your very own badge') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm dark:text-gray-200">
|
||||
@if ($folderError)
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
|
||||
<strong class="font-bold">Error:</strong>
|
||||
<span class="block sm:inline">{{ $errorMessage }}</span>
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-col md:flex-row flex-wrap gap-4 justify-between">
|
||||
<button @click="toggleCopyMode" :aria-pressed="copyMode" aria-label="{{ __('Toggle copy color mode') }}" class="flex items-center justify-center font-medium gap-2 flex-1 border-2 border-black rounded badge-drawer-button dark:text-gray-100">
|
||||
{{ __('Copy color:') }}
|
||||
<i class="fa-solid fa-eye-dropper"></i>
|
||||
</button>
|
||||
<button @click="toggleEraseMode" :aria-pressed="eraseMode" aria-label="{{ __('Toggle erase mode') }}" class="flex items-center justify-center font-medium gap-2 flex-1 border-2 border-black rounded badge-drawer-button dark:text-gray-100">
|
||||
{{ __('Erase mode:') }}
|
||||
<i class="fa-solid fa-eraser"></i>
|
||||
</button>
|
||||
<button @click="$refs.fileInput.click()" aria-label="{{ __('Import a picture for your badge') }}" class="flex items-center justify-center font-medium gap-2 flex-1 border-2 border-black rounded badge-drawer-button dark:text-gray-100">
|
||||
{{ __('Import Picture:') }}
|
||||
<i class="fa-solid fa-file-import"></i>
|
||||
</button>
|
||||
<button @click="showGrid = !showGrid" :aria-pressed="showGrid" aria-label="{{ __('Toggle grid visibility') }}" class="flex items-center justify-center font-medium gap-2 flex-1 border-2 border-black rounded badge-drawer-button dark:text-gray-100">
|
||||
{{ __('Show Grid:') }}
|
||||
<i class="fa-solid fa-table-cells"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="file" accept="image/png,image/gif" x-ref="fileInput" style="display:none;" @change="importImage($event)">
|
||||
<div class="flex flex-col md:flex-row gap-4 md:gap-8 items-start">
|
||||
<div class="checkerboard w-full max-w-[640px] mx-auto aspect-square relative">
|
||||
<div id="guide" x-ref="guide"></div>
|
||||
<canvas width="40" height="40" x-ref="canvas" class="w-full h-full border border-gray-300 dark:border-gray-700" style="image-rendering: pixelated; background: transparent; role="img" aria-label="{{ __('Badge drawing area') }}"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row flex-wrap gap-4 justify-between">
|
||||
<div class="flex flex-col gap-2 flex-1">
|
||||
<p class="font-semibold mb-1 dark:text-gray-100">{{ __('Choose Color') }}</p>
|
||||
<div class="flex items-center gap-2 w-full">
|
||||
<div @click="$refs.colorInput.click()" class="w-12 h-12 !p-0 border-2 border-black rounded badge-drawer-button cursor-pointer flex items-center justify-center relative">
|
||||
<i class="fa-solid fa-fill-drip fa-lg text-black"></i>
|
||||
<input type="color" id="colorInput" x-ref="colorInput" x-model="color" class="absolute opacity-0 w-full h-full cursor-pointer top-0 left-0" @input="color = $event.target.value" >
|
||||
</div>
|
||||
<div :style="'background-color: ' + color" class="!w-full !max-w-none !h-12 badge-drawer-palette"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2 flex-1">
|
||||
<p class="font-semibold mb-1 dark:text-gray-100">{{ __('Recent color') }}</p>
|
||||
<div class="flex items-center flex-wrap gap-x-2 mx-auto w-full gap-y-2">
|
||||
<template x-for="col in recentColors" :key="col">
|
||||
<div @click="color = col; eraseMode = false" :style="'background-color: ' + col" class="badge-drawer-palette"></div>
|
||||
</template>
|
||||
<template x-for="i in (12 - recentColors.length)" :key="'empty-' + i">
|
||||
<div class="badge-drawer-palette"></div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2 flex-1">
|
||||
<p class="font-semibold mb-1 dark:text-gray-100">{{ __('Palette') }}</p>
|
||||
<div class="flex items-center flex-wrap gap-x-2 mx-auto w-full gap-y-2">
|
||||
<template x-for="col in colors">
|
||||
<div @click="color = col; eraseMode = false" :style="'background-color: ' + col" class="badge-drawer-palette"></div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row gap-4 justify-between">
|
||||
<button type="button" @click="clearBoard" class="w-full rounded bg-red-600 hover:bg-red-700 text-white p-2 border-2 border-red-500 transition ease-in-out duration-150 font-semibold">{{ __('Clear All') }}</button>
|
||||
<button type="button" @click="generateCanvas('download')" class="w-full rounded bg-[#eeb425] text-white p-2 border-2 border-yellow-400 transition ease-in-out duration-200 hover:bg-[#d49f1c] font-semibold"> {{ __('Download badge') }} </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
<x-content.content-card icon="hotel-icon" classes="border dark:border-gray-900 col-span-1 h-max">
|
||||
<x-slot:title>
|
||||
{{ __('Badge Drawer Details') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('My Badge Details') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm">
|
||||
<div class="flex flex-col gap-3">
|
||||
<h3 class="font-semibold dark:text-gray-100">{{ __('Preview') }}</h3>
|
||||
<div id="avatarbox" class="mx-auto">
|
||||
<div class="username"
|
||||
style="font-size: 12px;margin-top: 13px;margin-left: 30px;color: #FFF;">
|
||||
{{ auth()->user()->username}}
|
||||
</div>
|
||||
<div class="avatara"
|
||||
style="float: left;background: url('{{ setting('avatar_imager') }}{{ auth()->user()->look}}&direction=4&head_direction=3') no-repeat;width: 60px;height: 120px;margin-left: 15px;margin-top: 10px;">
|
||||
</div>
|
||||
<div class="preview" style='float: left;margin-left: 15px;margin-top: 7px;'>
|
||||
<canvas width="40" height="40" x-ref="previewCanvas" style="image-rendering: pixelated; background: transparent; role="img" aria-label="Badge preview"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="badgeName" class="font-semibold dark:text-gray-100">{{ __('Badge Name:') }}</label>
|
||||
<input type="text" id="badgeName" x-model="badgeName" maxlength="24" class="mt-1 focus:ring-0 border-4 border-gray-200 rounded dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 focus:border-[#eeb425] w-full">
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<label for="badgeDescription" class="font-semibold mb-4 dark:text-gray-100">{{ __('Badge Description:') }}</label>
|
||||
<input type="text" id="badgeDescription" x-model="badgeDescription" maxlength="255" class="mt-1 focus:ring-0 border-4 border-gray-200 rounded dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 focus:border-[#eeb425] w-full">
|
||||
</div>
|
||||
<button type="button" x-text="buttonText" @click="buyBadge" class="w-full rounded text-white p-2 border-2 border-green-500 transition ease-in-out duration-150 font-semibold bg-green-600 hover:bg-green-700" :class="isValid ? 'cursor-pointer' : 'cursor-not-allowed'" :disabled="!isValid || {{ $folderError ? 'true' : 'false' }}"></button>
|
||||
</div>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" integrity="sha512-DxV+EoADOkOygM4IR9yXP8Sb2qwgidEmeqAEmDKIOfPRQZOWbXCzLC6vjbZyy0vPisbH2SyW27+ddLVCN+OMzQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<script src="{{ asset('js/gif/gif.js') }}"></script>
|
||||
|
||||
<script>
|
||||
function badgeDrawer({ cost, currencyType }) {
|
||||
return {
|
||||
cost: cost,
|
||||
currencyType: currencyType,
|
||||
color: '#000000',
|
||||
recentColors: [],
|
||||
eraseMode: false,
|
||||
copyMode: false,
|
||||
showGrid: true,
|
||||
isDrawing: false,
|
||||
cellSideCount: 40,
|
||||
colorHistory: {},
|
||||
badgeName: '',
|
||||
badgeDescription: '',
|
||||
lastBuyTime: null,
|
||||
canBuy: true,
|
||||
buttonText: `{{ __('Buy Badge') }} (${cost} ${currencyType.charAt(0).toUpperCase() + currencyType.slice(1)})`,
|
||||
drawingContext: null,
|
||||
previewContext: null,
|
||||
guide: null,
|
||||
canvas: null,
|
||||
previewCanvas: null,
|
||||
colors: [
|
||||
'#000000', '#FFFFFF', '#808080', '#C0C0C0', '#FF0000', '#800000',
|
||||
'#FFFF00', '#808000', '#00FF00', '#008000', '#00FFFF', '#008080',
|
||||
'#0000FF', '#000080', '#FF00FF', '#800080', '#FF4500', '#FFA500',
|
||||
'#FFD700', '#F0E68C', '#90EE90', '#98FB98', '#AFEEEE', '#ADD8E6',
|
||||
'#87CEFA', '#6495ED', '#DDA0DD', '#EE82EE', '#A52A2A', '#D2691E',
|
||||
'#CD853F', '#F4A460', '#FFC0CB', '#9370DB', '#228B22', '#20B2AA'
|
||||
],
|
||||
|
||||
get isValid() {
|
||||
return this.badgeName.trim().length > 0 && this.badgeName.trim().length <= 24 && this.badgeDescription.trim().length > 0 && this.badgeDescription.trim().length <= 255 && Object.keys(this.colorHistory).length > 0 && !this.badgeName.match(/https?:\/\/|www\./i) && !this.badgeDescription.match(/https?:\/\/|www\./i) && this.canBuy;
|
||||
},
|
||||
|
||||
init() {
|
||||
this.canvas = this.$refs.canvas;
|
||||
this.previewCanvas = this.$refs.previewCanvas;
|
||||
this.guide = this.$refs.guide;
|
||||
this.drawingContext = this.canvas.getContext('2d');
|
||||
this.previewContext = this.previewCanvas.getContext('2d');
|
||||
|
||||
// Get actual rendered size of the canvas
|
||||
const canvasWidth = this.canvas.clientWidth;
|
||||
const cellSize = canvasWidth / this.cellSideCount;
|
||||
|
||||
// Setup the guide with dynamic grid lines using gradients (no borders, no child divs)
|
||||
this.guide.style.width = `${canvasWidth}px`;
|
||||
this.guide.style.height = `${canvasWidth}px`;
|
||||
|
||||
// Clear any existing children (no longer needed)
|
||||
this.guide.innerHTML = '';
|
||||
|
||||
// Determine border color based on dark mode
|
||||
const isDark = document.documentElement.classList.contains('dark');
|
||||
const borderColor = isDark ? '#fff' : '#4b5563'; // gray-700
|
||||
|
||||
// Set grid lines as background gradients
|
||||
this.guide.style.backgroundImage = `
|
||||
repeating-linear-gradient(to bottom, ${borderColor} 0px 1px, transparent 1px ${cellSize}px),
|
||||
repeating-linear-gradient(to right, ${borderColor} 0px 1px, transparent 1px ${cellSize}px)
|
||||
`;
|
||||
|
||||
// Watch for grid toggle (use block instead of grid)
|
||||
this.$watch('showGrid', (value) => {
|
||||
this.guide.style.display = value ? 'block' : 'none';
|
||||
});
|
||||
|
||||
// Make checkerboard dynamic and aligned to cellSize
|
||||
const checkerboardEl = this.$el.querySelector('.checkerboard');
|
||||
let bgColor, checkColor;
|
||||
if (isDark) {
|
||||
bgColor = '#1a1a1a';
|
||||
checkColor = '#2a2a2a';
|
||||
} else {
|
||||
bgColor = '#fff';
|
||||
checkColor = '#eee';
|
||||
}
|
||||
const checkUnit = cellSize / 2; // Half cell for finer checks (2x2 per cell)
|
||||
checkerboardEl.style.backgroundColor = bgColor;
|
||||
checkerboardEl.style.backgroundImage = `
|
||||
linear-gradient(45deg, ${checkColor} 25%, transparent 25%),
|
||||
linear-gradient(-45deg, ${checkColor} 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, ${checkColor} 75%),
|
||||
linear-gradient(-45deg, transparent 75%, ${checkColor} 75%)
|
||||
`;
|
||||
checkerboardEl.style.backgroundSize = `${checkUnit * 2}px ${checkUnit * 2}px`;
|
||||
checkerboardEl.style.backgroundPosition = `0 0, 0 ${checkUnit}px, ${checkUnit}px -${checkUnit}px, -${checkUnit}px 0px`;
|
||||
|
||||
// Watch for erase/copy toggle
|
||||
this.$watch('eraseMode', (value) => {
|
||||
if (value) this.copyMode = false;
|
||||
});
|
||||
this.$watch('copyMode', (value) => {
|
||||
if (value) this.eraseMode = false;
|
||||
});
|
||||
|
||||
// Mouse events
|
||||
this.canvas.addEventListener('mousedown', this.handleMousedown.bind(this));
|
||||
this.canvas.addEventListener('mousemove', this.handleMousemove.bind(this));
|
||||
this.canvas.addEventListener('mouseup', () => { this.isDrawing = false; });
|
||||
this.canvas.addEventListener('mouseleave', () => { this.isDrawing = false; });
|
||||
|
||||
// Touch events
|
||||
this.canvas.addEventListener('touchstart', this.handleTouchstart.bind(this));
|
||||
this.canvas.addEventListener('touchmove', this.handleTouchmove.bind(this));
|
||||
this.canvas.addEventListener('touchend', () => { this.isDrawing = false; });
|
||||
this.canvas.addEventListener('touchcancel', () => { this.isDrawing = false; });
|
||||
|
||||
// Initial preview
|
||||
this.updatePreview();
|
||||
},
|
||||
|
||||
toggleCopyMode() {
|
||||
this.copyMode = !this.copyMode;
|
||||
if (this.copyMode) {
|
||||
this.eraseMode = false;
|
||||
}
|
||||
},
|
||||
|
||||
toggleEraseMode() {
|
||||
this.eraseMode = !this.eraseMode;
|
||||
if (this.eraseMode) {
|
||||
this.copyMode = false;
|
||||
}
|
||||
},
|
||||
|
||||
handleMousedown(e) {
|
||||
if (e.button !== 0) return;
|
||||
|
||||
const canvasBoundingRect = this.canvas.getBoundingClientRect();
|
||||
const scaleX = this.canvas.width / canvasBoundingRect.width;
|
||||
const scaleY = this.canvas.height / canvasBoundingRect.height;
|
||||
const x = (e.clientX - canvasBoundingRect.left) * scaleX;
|
||||
const y = (e.clientY - canvasBoundingRect.top) * scaleY;
|
||||
const cellX = Math.floor(x);
|
||||
const cellY = Math.floor(y);
|
||||
const currentColor = this.colorHistory[`${cellX}_${cellY}`];
|
||||
|
||||
if (this.copyMode) {
|
||||
if (currentColor) {
|
||||
this.color = currentColor;
|
||||
this.copyMode = false;
|
||||
}
|
||||
} else if (e.ctrlKey) {
|
||||
if (currentColor) {
|
||||
this.color = currentColor;
|
||||
}
|
||||
} else {
|
||||
this.paintCell(cellX, cellY);
|
||||
this.isDrawing = true;
|
||||
}
|
||||
},
|
||||
|
||||
handleMousemove(e) {
|
||||
if (!this.isDrawing) return;
|
||||
|
||||
const canvasBoundingRect = this.canvas.getBoundingClientRect();
|
||||
const scaleX = this.canvas.width / canvasBoundingRect.width;
|
||||
const scaleY = this.canvas.height / canvasBoundingRect.height;
|
||||
const x = (e.clientX - canvasBoundingRect.left) * scaleX;
|
||||
const y = (e.clientY - canvasBoundingRect.top) * scaleY;
|
||||
const cellX = Math.floor(x);
|
||||
const cellY = Math.floor(y);
|
||||
|
||||
this.paintCell(cellX, cellY);
|
||||
},
|
||||
|
||||
handleTouchstart(e) {
|
||||
e.preventDefault();
|
||||
const touch = e.touches[0];
|
||||
const canvasBoundingRect = this.canvas.getBoundingClientRect();
|
||||
const scaleX = this.canvas.width / canvasBoundingRect.width;
|
||||
const scaleY = this.canvas.height / canvasBoundingRect.height;
|
||||
const x = (touch.clientX - canvasBoundingRect.left) * scaleX;
|
||||
const y = (touch.clientY - canvasBoundingRect.top) * scaleY;
|
||||
const cellX = Math.floor(x);
|
||||
const cellY = Math.floor(y);
|
||||
const currentColor = this.colorHistory[`${cellX}_${cellY}`];
|
||||
|
||||
if (this.copyMode) {
|
||||
if (currentColor) {
|
||||
this.color = currentColor;
|
||||
this.copyMode = false;
|
||||
}
|
||||
} else {
|
||||
this.paintCell(cellX, cellY);
|
||||
this.isDrawing = true;
|
||||
}
|
||||
},
|
||||
|
||||
handleTouchmove(e) {
|
||||
e.preventDefault();
|
||||
if (!this.isDrawing) return;
|
||||
const touch = e.touches[0];
|
||||
const canvasBoundingRect = this.canvas.getBoundingClientRect();
|
||||
const scaleX = this.canvas.width / canvasBoundingRect.width;
|
||||
const scaleY = this.canvas.height / canvasBoundingRect.height;
|
||||
const x = (touch.clientX - canvasBoundingRect.left) * scaleX;
|
||||
const y = (touch.clientY - canvasBoundingRect.top) * scaleY;
|
||||
const cellX = Math.floor(x);
|
||||
const cellY = Math.floor(y);
|
||||
|
||||
this.paintCell(cellX, cellY);
|
||||
},
|
||||
|
||||
importImage(e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
if (!['image/png', 'image/gif'].includes(file.type)) {
|
||||
alert(translations.invalid_file_type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('Import image and overwrite the canvas?')) {
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
const img = new Image();
|
||||
img.onerror = () => {
|
||||
alert('Invalid image format. Please upload a valid PNG or GIF file.');
|
||||
e.target.value = '';
|
||||
};
|
||||
img.onload = () => {
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
tempCanvas.width = this.cellSideCount;
|
||||
tempCanvas.height = this.cellSideCount;
|
||||
const tempContext = tempCanvas.getContext('2d');
|
||||
tempContext.drawImage(img, 0, 0, this.cellSideCount, this.cellSideCount);
|
||||
|
||||
// Binarize alpha to avoid semi-transparent pixels
|
||||
const imageData = tempContext.getImageData(0, 0, this.cellSideCount, this.cellSideCount);
|
||||
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||
const a = imageData.data[i + 3];
|
||||
if (a < 128) {
|
||||
imageData.data[i + 3] = 0;
|
||||
} else {
|
||||
imageData.data[i + 3] = 255;
|
||||
}
|
||||
}
|
||||
tempContext.putImageData(imageData, 0, 0);
|
||||
|
||||
// Clear the board
|
||||
this.drawingContext.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.colorHistory = {};
|
||||
|
||||
// Draw the resized image
|
||||
this.drawingContext.drawImage(tempCanvas, 0, 0);
|
||||
|
||||
// Update colorHistory
|
||||
const importedImageData = this.drawingContext.getImageData(0, 0, this.cellSideCount, this.cellSideCount);
|
||||
for (let y = 0; y < this.cellSideCount; y++) {
|
||||
for (let x = 0; x < this.cellSideCount; x++) {
|
||||
const index = (y * this.cellSideCount + x) * 4;
|
||||
const r = importedImageData.data[index];
|
||||
const g = importedImageData.data[index + 1];
|
||||
const b = importedImageData.data[index + 2];
|
||||
const a = importedImageData.data[index + 3];
|
||||
if (a > 0) {
|
||||
const hexColor = this.rgbToHex(r, g, b);
|
||||
this.colorHistory[`${x}_${y}`] = hexColor;
|
||||
// Add to recentColors
|
||||
if (!this.recentColors.includes(hexColor)) {
|
||||
this.recentColors.unshift(hexColor);
|
||||
if (this.recentColors.length > 12) {
|
||||
this.recentColors = this.recentColors.slice(0, 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.updatePreview();
|
||||
};
|
||||
img.src = event.target.result;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
},
|
||||
|
||||
rgbToHex(r, g, b) {
|
||||
r = Math.round(r);
|
||||
g = Math.round(g);
|
||||
b = Math.round(b);
|
||||
return '#' + ((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1).toUpperCase();
|
||||
},
|
||||
|
||||
paintCell(cellX, cellY) {
|
||||
if (this.eraseMode) {
|
||||
this.drawingContext.clearRect(cellX, cellY, 1, 1);
|
||||
delete this.colorHistory[`${cellX}_${cellY}`];
|
||||
} else {
|
||||
this.drawingContext.fillStyle = this.color;
|
||||
this.drawingContext.fillRect(cellX, cellY, 1, 1);
|
||||
this.colorHistory[`${cellX}_${cellY}`] = this.color;
|
||||
// Add to recentColors only when painting
|
||||
if (!this.recentColors.includes(this.color)) {
|
||||
this.recentColors.unshift(this.color);
|
||||
if (this.recentColors.length > 12) {
|
||||
this.recentColors = this.recentColors.slice(0, 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.updatePreview();
|
||||
},
|
||||
|
||||
clearBoard() {
|
||||
if (!confirm('Clear the entire board?')) return;
|
||||
this.drawingContext.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.colorHistory = {};
|
||||
this.recentColors = []; // Clear recent colors as well
|
||||
this.updatePreview();
|
||||
},
|
||||
|
||||
updatePreview() {
|
||||
this.previewContext.clearRect(0, 0, this.previewCanvas.width, this.previewCanvas.height);
|
||||
this.previewContext.drawImage(this.canvas, 0, 0);
|
||||
},
|
||||
|
||||
generateGifBlob() {
|
||||
return new Promise((resolve) => {
|
||||
// Get image data to find used colors
|
||||
const imageData = this.drawingContext.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||||
let usedColors = new Set();
|
||||
for (let y = 0; y < this.canvas.height; y++) {
|
||||
for (let x = 0; x < this.canvas.width; x++) {
|
||||
const idx = (y * this.canvas.width + x) * 4;
|
||||
if (imageData.data[idx + 3] > 0) { // Only consider opaque pixels
|
||||
const r = imageData.data[idx];
|
||||
const g = imageData.data[idx + 1];
|
||||
const b = imageData.data[idx + 2];
|
||||
usedColors.add((r << 16) | (g << 8) | b); // Store as integer for efficiency
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find an unused color for transparency placeholder, starting from 0xFF00FF
|
||||
let transColor = 0xFF00FF;
|
||||
while (usedColors.has(transColor)) {
|
||||
transColor = (transColor + 1) % 0x1000000; // Increment and wrap around (unlikely to loop much)
|
||||
}
|
||||
const transHex = '#' + transColor.toString(16).padStart(6, '0').toUpperCase();
|
||||
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
tempCanvas.width = this.canvas.width;
|
||||
tempCanvas.height = this.canvas.height;
|
||||
const tempContext = tempCanvas.getContext('2d');
|
||||
tempContext.fillStyle = transHex; // Use the dynamic placeholder
|
||||
tempContext.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
|
||||
tempContext.drawImage(this.canvas, 0, 0);
|
||||
|
||||
const gif = new GIF({
|
||||
workers: 2,
|
||||
quality: 10,
|
||||
workerScript: '{{ asset('js/gif/gif.worker.js') }}', // Local worker script
|
||||
width: this.canvas.width,
|
||||
height: this.canvas.height,
|
||||
transparent: transColor // Use the dynamic integer value
|
||||
});
|
||||
|
||||
gif.addFrame(tempCanvas);
|
||||
|
||||
gif.on('finished', (blob) => {
|
||||
resolve(blob);
|
||||
});
|
||||
|
||||
gif.render();
|
||||
});
|
||||
},
|
||||
|
||||
async generateCanvas(action) {
|
||||
const blob = await this.generateGifBlob();
|
||||
|
||||
if (action === 'download') {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'badge.gif';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
},
|
||||
|
||||
async buyBadge() {
|
||||
if (Object.keys(this.colorHistory).length === 0 || !this.badgeName.trim() || !this.badgeDescription.trim()) {
|
||||
alert(translations.missing_fields);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.badgeName.match(/https?:\/\/|www\./i) || this.badgeDescription.match(/https?:\/\/|www\./i)) {
|
||||
alert(translations.invalid_content);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.canBuy || !confirm(translations.buy_confirmation)) return;
|
||||
|
||||
this.lastBuyTime = Date.now();
|
||||
this.canBuy = false;
|
||||
this.buttonText = `Cooldown (${Math.ceil((30000 - (Date.now() - this.lastBuyTime)) / 1000)} sec)`;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const remainingTime = Math.ceil((30000 - (Date.now() - this.lastBuyTime)) / 1000);
|
||||
if (remainingTime <= 0) {
|
||||
clearInterval(interval);
|
||||
this.canBuy = true;
|
||||
this.buttonText = `{{ __('Buy Badge') }} (${this.cost} ${this.currencyType.charAt(0).toUpperCase() + this.currencyType.slice(1)})`;
|
||||
} else {
|
||||
this.buttonText = `Cooldown (${remainingTime} sec)`;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
const blob = await this.generateGifBlob();
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
const base64data = reader.result;
|
||||
|
||||
fetch('{{ route('badge.buy') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify({ badge_data: base64data, badge_name: this.badgeName, badge_description: this.badgeDescription })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert(translations.purchase_success);
|
||||
} else {
|
||||
alert(data.message || translations.purchase_error_insufficient);
|
||||
clearInterval(interval); // Clear interval if buy fails
|
||||
this.canBuy = true;
|
||||
this.buttonText = `{{ __('Buy Badge') }} (${this.cost} ${this.currencyType.charAt(0).toUpperCase() + this.currencyType.slice(1)})`;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert(translations.purchase_error_general);
|
||||
clearInterval(interval); // Clear interval if buy fails
|
||||
this.canBuy = true;
|
||||
this.buttonText = `{{ __('Buy Badge') }} (${this.cost} ${this.currencyType.charAt(0).toUpperCase() + this.currencyType.slice(1)})`;
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
|
||||
// Ensure cooldown ends even if fetch fails or takes long
|
||||
setTimeout(() => {
|
||||
if (!this.canBuy) {
|
||||
clearInterval(interval);
|
||||
this.canBuy = true;
|
||||
this.buttonText = `{{ __('Buy Badge') }} (${this.cost} ${this.currencyType.charAt(0).toUpperCase() + this.currencyType.slice(1)})`;
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#canvas {
|
||||
cursor: pointer;
|
||||
}
|
||||
#guide {
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-repeat: repeat;
|
||||
}
|
||||
.checkerboard {
|
||||
}
|
||||
.dark .checkerboard {
|
||||
}
|
||||
input[type="color"] {
|
||||
position: absolute;
|
||||
top: auto;
|
||||
left: auto;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
#avatarbox {
|
||||
background: url('/assets/images/badgecreator/avatarbox.png');
|
||||
width: 199px;
|
||||
height: 180px;
|
||||
float: right;
|
||||
}
|
||||
.tint-red {
|
||||
filter: invert(21%) sepia(87%) saturate(4855%) hue-rotate(346deg) brightness(91%) contrast(92%);
|
||||
}
|
||||
</style>
|
||||
</x-app-layout>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,51 @@
|
||||
<x-app-layout>
|
||||
@push('title', auth()->user()->username)
|
||||
|
||||
<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 dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ $category->name }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="px-2 text-sm dark:text-gray-200">
|
||||
<img class="px-2" style="float: right !important;"
|
||||
src="{{ asset('/assets/images/help-center/' . $category->image_url) }}" alt="">
|
||||
{!! $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;"
|
||||
class="px-2 py-1 text-white font-semibold rounded transition hover:scale-105">
|
||||
{{ $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 dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ $category->name }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="px-2 text-sm dark:text-gray-200">
|
||||
{!! $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;"
|
||||
class="px-2 py-1 text-white font-semibold rounded transition hover:scale-105">
|
||||
{{ $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 dark:border-gray-900 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-gray-200 rounded dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 focus:border-[#eeb425] 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 dark:border-gray-900 dark:text-gray-100">
|
||||
<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-[#eeb425]">
|
||||
{{ 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 dark:border-gray-900 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-gray-200 rounded dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 focus:border-[#eeb425] 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 dark:border-gray-900 dark:text-gray-100">
|
||||
<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-[#eeb425]">
|
||||
{{ 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 dark:border-gray-900 dark:text-gray-100 col-span-12">
|
||||
<x-slot:title>
|
||||
{{ __('All tickets') }}
|
||||
</x-slot:title>
|
||||
|
||||
<div class="overflow-hidden overflow-x-auto rounded border border-gray-200 dark:border-gray-700">
|
||||
<table class="min-w-full text-sm divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-100 dark:bg-gray-800">
|
||||
<tr>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('Title') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('Author') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('Status') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('Actions') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse ($tickets as $ticket)
|
||||
<tr>
|
||||
<td class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-gray-300">
|
||||
{{ Str::limit($ticket->title, 80) }}
|
||||
</td>
|
||||
<td class="px-4 py-2 text-gray-700 dark:text-gray-300">
|
||||
{{ $ticket->user->username }}</td>
|
||||
<td class="px-4 py-2 text-gray-700 dark:text-gray-300">
|
||||
{{ $ticket->open ? 'Open' : 'Closed' }}
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-gray-300 flex gap-x-3">
|
||||
<a data-turbolinks="false" href="{{ route('help-center.ticket.show', $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="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) }}">
|
||||
<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">
|
||||
<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 text-gray-700 dark:text-gray-300"
|
||||
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 dark:border-gray-900 dark:text-gray-100 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 dark:border-gray-900 dark:text-gray-100">
|
||||
<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-[#eeb425]">
|
||||
{{ 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 dark:border-gray-900 dark:text-gray-100 border dark:border-gray-900 dark:text-gray-100 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->sortByDesc('created_at') as $reply)
|
||||
@if($reply->user_id === auth()->user()->id)
|
||||
<div class="w-full rounded bg-gray-200 dark:bg-gray-700">
|
||||
<div class="h-[50px] px-4 flex items-center justify-between border-b border-gray-300 dark:border-gray-800 relative overflow-hidden">
|
||||
<div class="flex">
|
||||
<small class="ml-14 text-gray-400">{{ $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-gray-400">{{ $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-gray-200 dark:bg-gray-700">
|
||||
<div class="h-[50px] px-4 flex items-center justify-between border-b border-gray-300 dark:border-gray-800 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-gray-400">{{ $reply->created_at->diffForHumans() }}</small>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex">
|
||||
<small class="mr-14 text-gray-400">{{ $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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,111 @@
|
||||
<!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') }}">
|
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">
|
||||
|
||||
<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') . '/css/app.scss', 'resources/themes/' . setting('theme') . '/js/app.js'], 'build')
|
||||
@stack('scripts')
|
||||
|
||||
{{-- Cloudflare Turnstile Fix voor Laravel 12 & PHP 8.5 --}}
|
||||
@if (setting('cloudflare_turnstile_enabled') == '1')
|
||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||
@endif
|
||||
</head>
|
||||
|
||||
<body class="flex min-h-screen flex-col site-bg dark:bg-gray-800">
|
||||
@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
|
||||
|
||||
<x-messages.flash-messages />
|
||||
|
||||
<div id="app" class="bg-gray-100 dark:bg-gray-900">
|
||||
{{-- Top header --}}
|
||||
@auth
|
||||
<x-top-header />
|
||||
@endauth
|
||||
|
||||
{{-- Site Header --}}
|
||||
<x-site-header />
|
||||
|
||||
{{-- Navigation --}}
|
||||
<nav class="relative bg-white shadow dark:bg-gray-900">
|
||||
<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') == '1')
|
||||
<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') == '1')
|
||||
@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>
|
||||
|
||||
@stack('javascript')
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
<!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.scss', 'resources/themes/' . setting('theme') . '/js/app.js'], 'build')
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app" class="font-sans text-gray-900 antialiased">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,14 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Leaderboard'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<div class="grid grid-cols-1 gap-5 md: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>
|
||||
@@ -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 dark:text-gray-200">
|
||||
<div x-data="logoGenerator()" class="mt-4">
|
||||
<div class="grid grid-cols-6 gap-3">
|
||||
<div x-bind:class="{'bg-gray-200 dark:bg-gray-900 ring-2 ring-emerald-700': fontType === 'atom'}" class="h-24 rounded border border-gray-300 dark:border-gray-700 p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-gray-200 dark:hover:bg-gray-900 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-gray-200 dark:bg-gray-900 ring-2 ring-emerald-700': fontType === 'sunrise'}" class="h-24 rounded border border-gray-300 dark:border-gray-700 p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-gray-200 dark:hover:bg-gray-900 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-gray-200 dark:bg-gray-900 ring-2 ring-emerald-700': fontType === 'marine'}" class="h-24 rounded border border-gray-300 dark:border-gray-700 p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-gray-200 dark:hover:bg-gray-900 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-gray-200 dark:bg-gray-900 ring-2 ring-emerald-700': fontType === 'danlie'}" class="h-24 rounded border border-gray-300 dark:border-gray-700 p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-gray-200 dark:hover:bg-gray-900 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-gray-200 dark:bg-gray-900 ring-2 ring-emerald-700': fontType === 'habton'}" class="h-24 rounded border border-gray-300 dark:border-gray-700 p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-gray-200 dark:hover:bg-gray-900 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-gray-200 dark:bg-gray-900 ring-2 ring-emerald-700': fontType === 'habton_capitalized'}" class="h-24 rounded border border-gray-300 dark:border-gray-700 p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-gray-200 dark:hover:bg-gray-900 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-gray-200 dark:bg-gray-900 ring-2 ring-emerald-700': fontType === 'habbo_modern'}" class="h-24 rounded border border-gray-300 dark:border-gray-700 p-2 flex gap-2 justify-center items-center transition duration-300 ease-in-out hover:bg-gray-200 dark:hover:bg-gray-900 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-gray-200 rounded dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 focus:border-[#eeb425] 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-[#eeb425] text-white p-2 border-2 border-yellow-400 transition ease-in-out duration-200 hover:bg-[#d49f1c] 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>
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
<!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.scss', 'resources/themes/' . setting('theme') . '/js/app.js'], 'build')
|
||||
</head>
|
||||
|
||||
<body class="h-screen overflow-hidden relative bg-[#233143]">
|
||||
<x-messages.flash-messages/>
|
||||
|
||||
<div class="w-full h-full flex">
|
||||
<div class="bg-[#111827] w-96 h-full py-10 hidden lg:flex lg:flex-col items-center gap-10 relative px-6">
|
||||
<div>
|
||||
<img src="{{ setting('cms_logo') }}" alt="">
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2 w-full relative z-10">
|
||||
@foreach($tasks as $task)
|
||||
<div
|
||||
class="relative h-[80px] w-full overflow-hidden bg-[#233143] py-2 pr-2 transition duration-150 ease-in-out hover:scale-[101%] text-gray-100">
|
||||
<div class="flex h-full w-full items-center gap-x-6">
|
||||
<div>
|
||||
<img
|
||||
class="drop-shadow-thin -mb-8 transition duration-300 ease-in-out"
|
||||
style="image-rendering: auto"
|
||||
src="{{ setting('avatar_imager') }}{{ $task->user->look }}&direction=3&head_direction=3&gesture=sml&action=wav&frame=0"
|
||||
alt=""/>
|
||||
</div>
|
||||
|
||||
<div class="flex h-full w-2/3 items-center break-words">
|
||||
{{ $task->task }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-2 right-2 flex justify-between w-full">
|
||||
<small class="pl-24">{{ __('By: :user', ['user' => $task->user?->username]) }}</small>
|
||||
@if($task->completed)
|
||||
<small
|
||||
class="flex gap-1 items-center">
|
||||
Status:
|
||||
<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 text-green-400">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
</small>
|
||||
@else
|
||||
<small
|
||||
class="flex gap-1 items-center">
|
||||
Status:
|
||||
<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"
|
||||
stroke-width="2">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</small>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
{{ $tasks->links() }}
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-0 right-0 hidden lg:block z-0">
|
||||
<img src="/assets/images/maintenance/fireman.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-4 lg:px-14 text-gray-100 h-full flex flex-col justify-center relative z-10">
|
||||
|
||||
<h2 class="text-4xl lg:text-5xl font-bold uppercase">Maintenance break!</h2>
|
||||
|
||||
<article class="mt-4 text-lg lg:text-xl max-w-[600px] text-wrap">
|
||||
<p>{!! setting('maintenance_message') !!}</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img class="absolute bottom-0 right-0 opacity-60 hidden lg:block z-0" 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="rounded-full bg-white bg-opacity-70 px-4 py-2 font-semibold text-black transition duration-200 ease-in-out hover:bg-opacity-100">
|
||||
{{ __('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>
|
||||
@@ -0,0 +1,115 @@
|
||||
<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">
|
||||
@if(isset($categories->name))
|
||||
<x-content.content-card :icon="$categories->badge">
|
||||
<x-slot:title>
|
||||
{{ $categories->name }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('All the :category rares', ['category' => $categories->name]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
@foreach($categories->furniture as $rare)
|
||||
<x-rares.rare-card :rare="$rare" />
|
||||
@endforeach
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
@else
|
||||
@forelse($categories as $category)
|
||||
<x-content.content-card :icon="$category->badge">
|
||||
<x-slot:title>
|
||||
{{ $category->name }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('All the :category rares', ['category' => $category->name]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
@foreach($category->furniture as $rare)
|
||||
<x-rares.rare-card :rare="$rare" />
|
||||
@endforeach
|
||||
</div>
|
||||
</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">
|
||||
{{ __('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]">
|
||||
<x-content.content-card icon="catalog-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Search') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Search for rares') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form action="{{ route('values.search') }}" method="POST" class="space-y-3">
|
||||
@csrf
|
||||
|
||||
<x-form.input classes="mb-3" name="search" placeholder="Search for a rare"/>
|
||||
|
||||
@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>
|
||||
{{ __('Search') }}
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
|
||||
<x-content.content-card icon="inventory-icon" classes="border dark:border-gray-900">
|
||||
<x-slot:title>
|
||||
{{ __('Rare categories') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Select a category below') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="px-2 text-sm dark:text-gray-200 space-y-2">
|
||||
<div class="rounded bg-gray-200 dark:bg-gray-700 py-2 px-4 transition duration-200 ease-in-out hover:scale-[102%]">
|
||||
<a href="{{ route('values.index') }}" class="block text">
|
||||
{{ __('All values') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@foreach($categoriesNav as $category)
|
||||
<div class="rounded bg-gray-200 dark:bg-gray-700 py-2 px-4 transition duration-200 ease-in-out hover:scale-[102%]">
|
||||
<a href="{{ route('values.category', $category->id) }}" class="block text">
|
||||
{{ $category->name }}
|
||||
</a>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -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="dark:text-white">
|
||||
<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-gray-50 p-3 dark:border-gray-700 dark:bg-gray-900">
|
||||
<p class="font-semibold text-black dark:text-white">
|
||||
{{ __('Room details') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section class="rounded-b bg-white p-3 dark:bg-gray-800 dark:text-white">
|
||||
<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-gray-200 dark:bg-gray-700 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] dark:text-gray-300">
|
||||
{{ $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>
|
||||
@@ -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-white">
|
||||
{{ __('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-gray-900">
|
||||
<x-slot:title>
|
||||
{{ $category->name }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ $category->description }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<ul class="rounded bg-gray-100 p-2 dark:bg-gray-700 dark:text-gray-300">
|
||||
@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>
|
||||
@@ -0,0 +1,149 @@
|
||||
<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 bg-[#f68b08] text-white rounded">
|
||||
{{ __('Please make sure to read our shop') }}
|
||||
<button class="text-white underline font-bold" 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 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 dark:text-gray-100 transition ease-in-out duration-150">
|
||||
<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 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 dark:text-gray-100 transition ease-in-out duration-150">
|
||||
<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 bg-gray-100 text-black dark:text-gray-100 dark:bg-gray-700">
|
||||
{{ __('Current balance: $:balance', ['balance' => auth()->user()->website_balance]) }}
|
||||
</div>
|
||||
|
||||
@if(config('paypal.live.client_id') && config('paypal.live.client_secret'))
|
||||
<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 bg-blue-600 hover:bg-blue-700 text-white p-2 border-2 border-blue-500 transition ease-in-out duration-150 font-semibold">
|
||||
{{ __('Donate') }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<p class="dark:text-gray-100 mt-4 text-xs">
|
||||
{{ __('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>
|
||||
@@ -0,0 +1,135 @@
|
||||
<x-app-layout>
|
||||
@push('title', auth()->user()->username)
|
||||
|
||||
<div class="col-span-12 space-y-3 md:col-span-9">
|
||||
<x-user.me-backdrop :user="$user" />
|
||||
|
||||
<div
|
||||
class="flex flex-col justify-between gap-3 rounded border bg-white p-1 shadow dark:border-gray-900 dark:bg-gray-800 lg:flex-row">
|
||||
<div
|
||||
class="py-2 px-2 relative flex justify-center items-center rounded text-sm font-semibold dark:text-gray-300 bg-[#e9b124] dark:border-gray-700">
|
||||
<div class="absolute bg-[#e9b124] w-6 h-6 -right-1 rotate-45 invisible lg:visible"></div>
|
||||
<img src="{{ asset('/assets/images/icons/online-friends.png') }}" alt="{{ __('Online Friends') }}"
|
||||
class="mr-2 mb-1 inline-flex" style="max-width: 24px; max-height: 24px">
|
||||
<span class="relative text-white h-100">{{ __('Online Friends') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="relative flex flex-1 items-center justify-center gap-2 pl-2 h-100 sm:justify-start">
|
||||
@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-10 w-10 rounded-full border-2 border-gray-300 bg-center bg-no-repeat dark:border-gray-900">
|
||||
</div>
|
||||
|
||||
<div data-popover id="friend-{{ $friend->username }}" 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-sm 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">
|
||||
{{ $friend->username }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto px-3 py-2" style="max-height: 200px">
|
||||
<b class="mr-1 font-bold">{{ __('Motto') }}:</b>{{ $friend->motto }}<br>
|
||||
<b
|
||||
class="mr-1 font-bold">{{ __('Online Since') }}
|
||||
:</b>{{ date(config('habbo.site.date_format'), $friend->last_online) }}
|
||||
</div>
|
||||
<div data-popper-arrow></div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-content.content-card icon="friends-icon" classes="border dark:border-gray-900">
|
||||
<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 dark:text-gray-200">
|
||||
{{ __('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')]) }}
|
||||
<br>
|
||||
|
||||
<small class="text-gray-400">
|
||||
{{ __('Boosting referrals by making own accounts will lead to removal of all progress, currency, inventory and a potential ban') }}
|
||||
</small>
|
||||
|
||||
<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-gray-400 p-2 text-white dark:bg-gray-900">
|
||||
{{ 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 md: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>
|
||||
@@ -0,0 +1,210 @@
|
||||
<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 text-white overflow-hidden">
|
||||
<img class="mt-14 drop-shadow 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">{{ __('My name is,') }}</h3>
|
||||
<h2 class="text-4xl">
|
||||
{{ $user->username }}
|
||||
</h2>
|
||||
|
||||
<h4 class="text-lg font-semibold italic">{{ $user->motto }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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($user->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 dark:bg-gray-900 bg-gray-200 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-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="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>
|
||||
@@ -0,0 +1,76 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Account settings'))
|
||||
|
||||
<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>
|
||||
{{ __('Account settings') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Manage your account settings') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form action="{{ route('settings.account.update') }}" method="POST" class="flex flex-col gap-y-4">
|
||||
@method('PUT')
|
||||
@csrf
|
||||
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<x-form.label for="mail">
|
||||
{{ __('E-mail') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Make sure to use an email that you remember, if you ever lose your password, your email will be required.') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="mail" type="email" value="{{ $user->mail }}" :autofocus="true" />
|
||||
</div>
|
||||
|
||||
@if ($user->settings?->allow_name_change)
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<x-form.label for="username">
|
||||
{{ __('Username') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Your username is what you and others will see in-game') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="username" value="{{ $user->username }}" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<x-form.label for="motto">
|
||||
{{ __('Motto') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Spice up your profile with a nice motto') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="motto" value="{{ $user->motto }}" />
|
||||
</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
|
||||
|
||||
<div class="flex w-full justify-start md:justify-end">
|
||||
<x-form.secondary-button classes="lg:w-1/4">
|
||||
{{ __('Update settings') }}
|
||||
</x-form.secondary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,74 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Password settings'))
|
||||
|
||||
<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>
|
||||
{{ __('Password settings') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Change your password by filling out the fields below') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<form action="{{ route('settings.password.update') }}" method="POST" class="flex flex-col gap-y-4">
|
||||
@method('PUT')
|
||||
@csrf
|
||||
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<x-form.label for="current_password">
|
||||
{{ __('Current password') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Enter your current password') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="current_password" type="password" :autofocus="true" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<x-form.label for="password">
|
||||
{{ __('New password') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Enter a new secure password. Do not forget to save it somewhere safe') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="password" type="password" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<x-form.label for="password_confirmation">
|
||||
{{ __('Confirm new password') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Please confirm your new password') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input name="password_confirmation" 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
|
||||
|
||||
<div class="flex w-full justify-start md:justify-end">
|
||||
<x-form.secondary-button classes="lg:w-1/4">
|
||||
{{ __('Update password') }}
|
||||
</x-form.secondary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,72 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Session logs'))
|
||||
|
||||
<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>
|
||||
{{ __('Session logs') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Keep an eye on all your active sessions') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="overflow-hidden overflow-x-auto rounded border border-gray-200 dark:border-gray-700">
|
||||
<table class="min-w-full text-sm divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-100 dark:bg-gray-800">
|
||||
<tr>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('IP') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('IP Current Device') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('Is Desktop') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('Platform') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('Browser') }}
|
||||
</th>
|
||||
<th class="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900 dark:text-white">
|
||||
{{ __('Last Activity') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse ($logs as $log)
|
||||
<tr>
|
||||
<td class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-gray-300">
|
||||
{{ $log->ip_address }}
|
||||
</td>
|
||||
<td class="px-4 py-2 text-gray-700 dark:text-gray-300">
|
||||
{{ $log->is_current_device ? 'true' : 'false' }}</td>
|
||||
<td class="px-4 py-2 text-gray-700 dark:text-gray-300">
|
||||
{{ $log->agent['is_desktop'] ? 'true' : 'false' }}</td>
|
||||
<td class="px-4 py-2 text-gray-700 dark:text-gray-300">{{ $log->agent['platform'] }}
|
||||
</td>
|
||||
<td class="px-4 py-2 text-gray-700 dark:text-gray-300">{{ $log->agent['browser'] }}</td>
|
||||
<td class="whitespace-nowrap px-4 py-2 text-gray-700 dark:text-gray-300">
|
||||
{{ $log->last_active }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td class="whitespace-nowrap px-4 py-2 text-center text-gray-700 dark:text-gray-300"
|
||||
colspan="3">
|
||||
{{ __('No session logs found') }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,107 @@
|
||||
<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>
|
||||
{{ __('Two factor authentication') }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Add an extra layer of security to your account by enabling two-factor authentication') }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<!-- 2FA enabled, we display the QR code : -->
|
||||
@if (auth()->user()->two_factor_confirmed)
|
||||
<form action="{{ route('user.two-factor.disable') }}" method="post">
|
||||
@csrf
|
||||
@method('delete')
|
||||
|
||||
<x-form.danger-button>
|
||||
{{ __('Disable 2FA') }}
|
||||
</x-form.danger-button>
|
||||
</form>
|
||||
|
||||
{{-- 2FA enabled but not yet confirmed, we show the QRcode and ask for confirmation --}}
|
||||
@elseif(auth()->user()->two_factor_secret)
|
||||
<p>{{ __('Validate your two-factor enabling by scanning the following QR-code and enter your auto-generated 2-factor code from your phone.') }}
|
||||
</p>
|
||||
|
||||
<div class="mt-4 flex flex-col items-center md:flex-row md:items-start md:justify-center">
|
||||
<div class="flex gap-x-8 rounded bg-gray-100 px-4 py-2">
|
||||
<span class="flex items-center">
|
||||
{!! auth()->user()->twoFactorQrCodeSvg() !!}
|
||||
</span>
|
||||
|
||||
<div>
|
||||
<strong>
|
||||
{{ __('Recovery codes:') }}
|
||||
</strong>
|
||||
|
||||
<ul>
|
||||
@foreach (auth()->user()->recoveryCodes() as $code)
|
||||
<li>{{ $code }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 flex justify-center text-xs italic text-gray-600">
|
||||
<div class="w-full lg:w-[480px]">
|
||||
{{ __('Please save your recovery codes somewhere safe! If you lose access to your 2FA codes, those recovery codes will be needed to regain access your account.') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('two-factor.verify') }}" method="POST" class="mt-8">
|
||||
@csrf
|
||||
|
||||
<x-form.label for="code">
|
||||
{{ __('Code') }}
|
||||
|
||||
<x-slot:info>
|
||||
{{ __('Please scan the QR-code above with your phone to retrieve your two-factor authentication code.') }}
|
||||
</x-slot:info>
|
||||
</x-form.label>
|
||||
|
||||
<x-form.input classes="mb-3" name="code" placeholder="{{ __('Code') }}" />
|
||||
|
||||
@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-4">
|
||||
{{ __('Verify 2FA') }}
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
@else
|
||||
<div class="flex flex-col items-end">
|
||||
<div class="flex w-full flex-col gap-y-3 dark:text-gray-100">
|
||||
<p>
|
||||
{{ __('Here at :hotel we take security very serious and therefore we offer you as a user a way to secure your beloved account even further, by allowing you to enable Googles 2-factor authentication!', ['hotel' => setting('hotel_name')]) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ __('2-factor authentication adds an extra layer of security to your account, making it physical impossible to access it without having access to your mobile phone as only your phone will contain the 2-factor authentication code which will be re-generated every 30 seconds automatically') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('user.two-factor.enable') }}" method="post" class="mt-8">
|
||||
@csrf
|
||||
<x-form.secondary-button>
|
||||
{{ __('Activate 2FA') }}
|
||||
</x-form.secondary-button>
|
||||
</form>
|
||||
</div>
|
||||
@endif
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,48 @@
|
||||
<x-app-layout>
|
||||
@push('title', __('Rare values'))
|
||||
|
||||
<div class="col-span-12">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<a href="{{ route('values.index') }}" class="dark:text-gray-100 underline flex gap-x-1 items-center">
|
||||
<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="M19.5 12h-15m0 0l6.75 6.75M4.5 12l6.75-6.75" />
|
||||
</svg>
|
||||
|
||||
{{ __('Go back to values') }}
|
||||
</a>
|
||||
<x-content.content-card icon="dragon.png">
|
||||
<x-slot:title>
|
||||
{{ $value->name }}
|
||||
</x-slot:title>
|
||||
|
||||
<x-slot:under-title>
|
||||
{{ __('Here is a list of all the owned :value`s', ['value' => $value->name]) }}
|
||||
</x-slot:under-title>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
|
||||
@foreach($items as $item)
|
||||
<div class="px-3 h-[100px] rounded bg-gray-200 dark:bg-gray-700 flex gap-4 items-center overflow-hidden">
|
||||
<div class="w-12 h-12 overflow-hidden rounded-full flex items-center justify-center bg-gray-300 dark:bg-gray-800">
|
||||
<img src="{{ sprintf('%s/%s', setting('avatar_imager'), $item['user']->look) }}&headonly=1" alt="">
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<p class="dark:text-gray-100">{{ $item['user']->username }}</p>
|
||||
|
||||
<div class="w-full bg-yellow-400 rounded h-[35px] flex items-center">
|
||||
<div class="bg-yellow-500 rounded-l px-2 h-full flex items-center justify-center">
|
||||
<img class="h-[18px] w-[28px]" src="{{ asset('assets/images/icons/amount.png') }}" alt="">
|
||||
</div>
|
||||
|
||||
<p class="w-full text-center truncate text-sm">
|
||||
{{ $item['item_count'] ?? 0 }} {{ __('owned') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-content.content-card>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,58 @@
|
||||
import { defineConfig } from "vite";
|
||||
import laravel from "laravel-vite-plugin";
|
||||
import path from "path";
|
||||
import tailwindcss from "tailwindcss";
|
||||
import postcssImport from "postcss-import";
|
||||
import postcssNesting from "postcss-nesting";
|
||||
import tailwindcssNesting from "tailwindcss/nesting";
|
||||
import autoprefixer from "autoprefixer";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
laravel({
|
||||
input: [
|
||||
path.resolve(__dirname, "css/app.scss"),
|
||||
path.resolve(__dirname, "js/app.js"),
|
||||
"resources/js/global.js",
|
||||
"resources/css/global.scss",
|
||||
],
|
||||
}),
|
||||
{
|
||||
name: "blade",
|
||||
handleHotUpdate({ file, server }) {
|
||||
if (file.endsWith(".blade.php")) {
|
||||
server.ws.send({
|
||||
type: "full-reload",
|
||||
path: "*",
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "js/app.js"),
|
||||
},
|
||||
},
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [
|
||||
postcssImport(),
|
||||
tailwindcssNesting(postcssNesting()),
|
||||
tailwindcss({
|
||||
config: path.resolve(__dirname, "tailwind.config.js"),
|
||||
}),
|
||||
autoprefixer(),
|
||||
],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
onwarn(warning, warn) {
|
||||
// Negeer INVALID_ANNOTATION warnings van flowbite
|
||||
if (warning.code === 'INVALID_ANNOTATION') return;
|
||||
warn(warning);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,325 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.dropdown-item {
|
||||
@apply w-full text-left px-2 py-1 transition duration-200 ease-in-out text-sm hover:bg-[#8770b2];
|
||||
}
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#app {
|
||||
min-height: calc(100vh - 3.5rem);
|
||||
}
|
||||
|
||||
img {
|
||||
-webkit-user-drag: none;
|
||||
-khtml-user-drag: none;
|
||||
-moz-user-drag: none;
|
||||
-o-user-drag: none;
|
||||
user-drag: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.site-bg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
background: url("/public/assets/images/dusk/background_image.png") no-repeat;
|
||||
background-size: cover;
|
||||
-webkit-mask-image: linear-gradient(to top, transparent, black);
|
||||
mask-image: linear-gradient(to top, transparent, black);
|
||||
background-color: #957cc3;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(46, 45, 45, 0.7); /* Black overlay with 60% opacity */
|
||||
}
|
||||
}
|
||||
|
||||
.nav-header {
|
||||
background-color: #171a23;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.icon {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
filter: grayscale(1);
|
||||
transition: filter 300ms ease-in-out;
|
||||
|
||||
}
|
||||
|
||||
a:hover .icon {
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.dropdown-parent:hover .icon {
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.dropdown-parent:active .icon {
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
a.active, div.active {
|
||||
color: #ac93da;
|
||||
|
||||
.dropdown-children {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.icon {
|
||||
filter: grayscale(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.photo-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sub-header {
|
||||
color: #FFF;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.custom-shadow {
|
||||
box-shadow: 0 7px 16px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.swiper-pagination {
|
||||
top: 3px;
|
||||
z-index: 15 !important;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.read-more-link {
|
||||
z-index: 99999; /* Adjust as needed */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.swiper-button-prev {
|
||||
z-index: 15 !important;
|
||||
}
|
||||
|
||||
.swiper-button-next {
|
||||
z-index: 15 !important;
|
||||
}
|
||||
|
||||
|
||||
.hotel-icon {
|
||||
background: url("/public/assets/images/icons/feeds.png") no-repeat center;
|
||||
}
|
||||
|
||||
.chat-icon {
|
||||
background: url("/public/assets/images/icons/chat.png") no-repeat center;
|
||||
}
|
||||
|
||||
.article-icon {
|
||||
background: url("/public/assets/images/icons/article.gif") no-repeat center;
|
||||
}
|
||||
|
||||
.lighthouse-icon {
|
||||
background: url("/public/assets/images/icons/lighthouse.png") no-repeat center;
|
||||
}
|
||||
|
||||
.currency-icon {
|
||||
background: url("/public/assets/images/dusk/store_icon.png") no-repeat center;
|
||||
}
|
||||
|
||||
.catalog-icon {
|
||||
background: url("/public/assets/images/icons/catalog.png") no-repeat center;
|
||||
}
|
||||
|
||||
.inventory-icon {
|
||||
background: url("/public/assets/images/icons/inventory.png") no-repeat center;
|
||||
}
|
||||
|
||||
.duo-chat-icon {
|
||||
background: 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;
|
||||
}
|
||||
|
||||
.swal2-toast {
|
||||
background-color: #333 !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.article-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background-size: cover;
|
||||
background-position: right;
|
||||
margin: 0 0 20px;
|
||||
transition: .3s;
|
||||
}
|
||||
|
||||
.leaderboard-position {
|
||||
color: transparent;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
|
||||
&.first {
|
||||
background-image: url("/public/assets/images/leaderboards/trophy-gold.png");
|
||||
}
|
||||
|
||||
&.second {
|
||||
background-image: url("/public/assets/images/leaderboards/trophy-silver.png");
|
||||
}
|
||||
|
||||
&.third {
|
||||
background-image: url("/public/assets/images/leaderboards/trophy-bronze.png");
|
||||
}
|
||||
}
|
||||
|
||||
.leaderboard-background {
|
||||
background-image: url("/public/assets/images/dusk/leaderboard_circle_image.png");
|
||||
}
|
||||
|
||||
.staff-bg {
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
/* Nitro disconnect overlay */
|
||||
#disconnected {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to bottom, #ffffff 50%, #EBEBEB 50%);
|
||||
box-shadow: inset 0 0 0px 2px #ffffff;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to bottom, #171717 50%, #111111 50%);
|
||||
box-shadow: inset 0 0 0px 2px #363636;
|
||||
}
|
||||
|
||||
.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%;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50%;
|
||||
background-color: #fff;
|
||||
opacity: .1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
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 Swiper, { Navigation, Pagination } from "swiper";
|
||||
import "swiper/css";
|
||||
import "swiper/css/navigation";
|
||||
import "swiper/css/pagination";
|
||||
|
||||
ArticleReactions.init();
|
||||
Alpine.plugin(Focus);
|
||||
Alpine.start();
|
||||
|
||||
Swiper.use([Navigation, Pagination]);
|
||||
|
||||
// Swiper Initialization
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const swiper = new Swiper(".swiper", {
|
||||
// Your Swiper options here
|
||||
navigation: {
|
||||
nextEl: ".swiper-button-next",
|
||||
prevEl: ".swiper-button-prev",
|
||||
},
|
||||
pagination: {
|
||||
el: ".swiper-pagination",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
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;",
|
||||
""
|
||||
);
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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_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'],
|
||||
// });
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user