feat: install and configure Inertia.js with React

- Install inertia-laravel, @inertiajs/react, react, @vitejs/plugin-react
- Add HandleInertiaRequests middleware registered in web group
- Create Inertia root template (resources/views/app.blade.php)
- Add React entry point and page components (resources/js/)
- Add Inertia demo route (/inertia-test)
- HomeController reverted to Blade (index page stays original)
- Remove inertia-test2 test route
This commit is contained in:
root
2026-05-25 15:15:14 +02:00
parent 4ce68720bb
commit 943d5bfc38
59 changed files with 1382 additions and 293 deletions
+2
View File
@@ -17,6 +17,7 @@ use App\Http\Middleware\InjectPwaMeta;
use App\Http\Middleware\InstallationMiddleware;
use App\Http\Middleware\LocalizationMiddleware;
use App\Http\Middleware\LogViewerMiddleware;
use App\Http\Middleware\HandleInertiaRequests;
use App\Http\Middleware\MaintenanceMiddleware;
use App\Http\Middleware\PreventRequestsDuringMaintenance;
use App\Http\Middleware\RealClientIpMiddleware;
@@ -84,6 +85,7 @@ class Kernel extends HttpKernel
SubstituteBindings::class,
LocalizationMiddleware::class,
InstallationMiddleware::class,
HandleInertiaRequests::class,
],
'api' => [
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
protected $rootView = 'app';
public function version(Request $request): ?string
{
return parent::version($request);
}
public function share(Request $request): array
{
return [
...parent::share($request),
'avatarImager' => setting('avatar_imager'),
];
}
}
+1
View File
@@ -16,6 +16,7 @@
"filament/filament": "^5.0",
"flowframe/laravel-trend": "0.4.99",
"guzzlehttp/guzzle": "^7.2",
"inertiajs/inertia-laravel": "^3.1",
"laravel/fortify": "^1.16",
"laravel/framework": "^13.0",
"laravel/octane": "^2.17",
Generated
+74 -1
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9944b69232ce924d916aff32d3263e43",
"content-hash": "647e6549ddd7a48db6ef78503e9ce2be",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -2537,6 +2537,79 @@
],
"time": "2025-08-22T14:27:06+00:00"
},
{
"name": "inertiajs/inertia-laravel",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/inertiajs/inertia-laravel.git",
"reference": "f588ce4a5beb25d166f4e372bac0c7f46a4f52ac"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/f588ce4a5beb25d166f4e372bac0c7f46a4f52ac",
"reference": "f588ce4a5beb25d166f4e372bac0c7f46a4f52ac",
"shasum": ""
},
"require": {
"ext-json": "*",
"laravel/framework": "^11.0|^12.0|^13.0",
"php": "^8.2.0",
"symfony/console": "^7.0|^8.0"
},
"conflict": {
"laravel/boost": "<2.2.0"
},
"require-dev": {
"guzzlehttp/guzzle": "^7.2",
"larastan/larastan": "^3.0",
"laravel/pint": "^1.16",
"mockery/mockery": "^1.3.3",
"orchestra/testbench": "^9.2|^10.0|^11.0",
"phpunit/phpunit": "^11.5|^12.0",
"roave/security-advisories": "dev-master"
},
"suggest": {
"ext-pcntl": "Recommended when running the Inertia SSR server via the `inertia:start-ssr` artisan command."
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Inertia\\ServiceProvider"
]
}
},
"autoload": {
"files": [
"./helpers.php"
],
"psr-4": {
"Inertia\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan Reinink",
"email": "jonathan@reinink.ca",
"homepage": "https://reinink.ca"
}
],
"description": "The Laravel adapter for Inertia.js.",
"keywords": [
"inertia",
"laravel"
],
"support": {
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
"source": "https://github.com/inertiajs/inertia-laravel/tree/v3.1.0"
},
"time": "2026-04-30T15:30:29+00:00"
},
{
"name": "kirschbaum-development/eloquent-power-joins",
"version": "4.3.1",
+4
View File
@@ -52,7 +52,11 @@
"vite": "^8.0.3"
},
"dependencies": {
"@inertiajs/react": "^3.2.0",
"@vitejs/plugin-react": "^6.0.2",
"flowbite": "2.5.2",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"sass": "1.83.4",
"swiper": "^12.1.3"
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
:root{--swiper-navigation-size: 44px}.swiper-button-prev,.swiper-button-next{position:absolute;width:var(--swiper-navigation-size);height:var(--swiper-navigation-size);z-index:10;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--swiper-navigation-color, var(--swiper-theme-color));&.swiper-button-disabled{opacity:.35;cursor:auto;pointer-events:none}&.swiper-button-hidden{opacity:0;cursor:auto;pointer-events:none}.swiper-navigation-disabled &{display:none!important}::slotted(svg),svg{width:100%;height:100%;-o-object-fit:contain;object-fit:contain;transform-origin:center;fill:currentColor;pointer-events:none}}.swiper-button-lock{display:none}.swiper-button-prev,.swiper-button-next{top:var(--swiper-navigation-top-offset, 50%);margin-top:calc(0px - (var(--swiper-navigation-size) / 2))}.swiper-button-prev{left:var(--swiper-navigation-sides-offset, 4px);right:auto;::slotted(.swiper-navigation-icon),.swiper-navigation-icon{transform:rotate(180deg)}}.swiper-button-next{right:var(--swiper-navigation-sides-offset, 4px);left:auto}.swiper-horizontal{.swiper-button-prev,.swiper-button-next,~.swiper-button-prev,~.swiper-button-next{top:var(--swiper-navigation-top-offset, 50%);margin-top:calc(0px - (var(--swiper-navigation-size) / 2));margin-left:0}.swiper-button-prev,~.swiper-button-prev,&.swiper-rtl .swiper-button-next,&.swiper-rtl~.swiper-button-next{left:var(--swiper-navigation-sides-offset, 4px);right:auto}.swiper-button-next,~.swiper-button-next,&.swiper-rtl .swiper-button-prev,&.swiper-rtl~.swiper-button-prev{right:var(--swiper-navigation-sides-offset, 4px);left:auto}.swiper-button-prev,~.swiper-button-prev,&.swiper-rtl .swiper-button-next,&.swiper-rtl~.swiper-button-next{::slotted(.swiper-navigation-icon),.swiper-navigation-icon{transform:rotate(180deg)}}&.swiper-rtl .swiper-button-prev,&.swiper-rtl~.swiper-button-prev{::slotted(.swiper-navigation-icon),.swiper-navigation-icon{transform:rotate(0)}}}.swiper-vertical{.swiper-button-prev,.swiper-button-next,~.swiper-button-prev,~.swiper-button-next{left:var(--swiper-navigation-top-offset, 50%);right:auto;margin-left:calc(0px - (var(--swiper-navigation-size) / 2));margin-top:0}.swiper-button-prev,~.swiper-button-prev{top:var(--swiper-navigation-sides-offset, 4px);bottom:auto;::slotted(.swiper-navigation-icon),.swiper-navigation-icon{transform:rotate(-90deg)}}.swiper-button-next,~.swiper-button-next{bottom:var(--swiper-navigation-sides-offset, 4px);top:auto;::slotted(.swiper-navigation-icon),.swiper-navigation-icon{transform:rotate(90deg)}}}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
View File

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 260 B

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

View File

Before

Width:  |  Height:  |  Size: 678 B

After

Width:  |  Height:  |  Size: 678 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 381 B

+1
View File
@@ -0,0 +1 @@
var c=Object.create,_=Object.defineProperty,v=Object.getOwnPropertyDescriptor,O=Object.getOwnPropertyNames,b=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty,g=(e,r)=>()=>(r||e((r={exports:{}}).exports,r),r.exports),f=(e,r)=>{let t={};for(var n in e)_(t,n,{get:e[n],enumerable:!0});return r||_(t,Symbol.toStringTag,{value:"Module"}),t},P=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(var o=O(r),p=0,l=o.length,a;p<l;p++)a=o[p],!s.call(e,a)&&a!==t&&_(e,a,{get:(u=>r[u]).bind(null,a),enumerable:!(n=v(r,a))||n.enumerable});return e},i=(e,r,t)=>(t=e!=null?c(b(e)):{},P(r||!e||!e.__esModule?_(t,"default",{value:e,enumerable:!0}):t,e));export{f as n,i as r,g as t};
View File

Before

Width:  |  Height:  |  Size: 279 B

After

Width:  |  Height:  |  Size: 279 B

View File

Before

Width:  |  Height:  |  Size: 473 B

After

Width:  |  Height:  |  Size: 473 B

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 471 B

After

Width:  |  Height:  |  Size: 471 B

View File

Before

Width:  |  Height:  |  Size: 690 B

After

Width:  |  Height:  |  Size: 690 B

View File

Before

Width:  |  Height:  |  Size: 361 B

After

Width:  |  Height:  |  Size: 361 B

View File

Before

Width:  |  Height:  |  Size: 472 B

After

Width:  |  Height:  |  Size: 472 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 626 B

After

Width:  |  Height:  |  Size: 626 B

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 491 B

View File

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 477 B

View File

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 258 KiB

View File

Before

Width:  |  Height:  |  Size: 166 B

After

Width:  |  Height:  |  Size: 166 B

View File

Before

Width:  |  Height:  |  Size: 485 B

After

Width:  |  Height:  |  Size: 485 B

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
View File

Before

Width:  |  Height:  |  Size: 364 B

After

Width:  |  Height:  |  Size: 364 B

View File

Before

Width:  |  Height:  |  Size: 630 B

After

Width:  |  Height:  |  Size: 630 B

View File

Before

Width:  |  Height:  |  Size: 350 B

After

Width:  |  Height:  |  Size: 350 B

Executable → Regular
+75 -24
View File
@@ -1,7 +1,25 @@
{
"_axios-D3CjrHSW.js": {
"file": "assets/axios-D3CjrHSW.js",
"name": "axios"
"_axios-BgauIbGG.js": {
"file": "assets/axios-BgauIbGG.js",
"name": "axios",
"imports": [
"_chunk-BqhQeaEc.js"
]
},
"_chunk-BqhQeaEc.js": {
"file": "assets/chunk-BqhQeaEc.js",
"name": "chunk"
},
"_swiper-B7Yxk788.js": {
"file": "assets/swiper-B7Yxk788.js",
"name": "swiper",
"css": [
"assets/swiper-CrMA9oas.css"
]
},
"_swiper-CrMA9oas.css": {
"file": "assets/swiper-CrMA9oas.css",
"src": "_swiper-CrMA9oas.css"
},
"public/assets/images/background-dark.jpg": {
"file": "assets/background-dark-BfkMu3-0.jpg",
@@ -11,6 +29,18 @@
"file": "assets/background-light-CP7oKwVT.jpg",
"src": "public/assets/images/background-light.jpg"
},
"public/assets/images/dusk/background_image.png": {
"file": "assets/background_image-BH7pVpv1.png",
"src": "public/assets/images/dusk/background_image.png"
},
"public/assets/images/dusk/leaderboard_circle_image.png": {
"file": "assets/leaderboard_circle_image-BYkDVX69.png",
"src": "public/assets/images/dusk/leaderboard_circle_image.png"
},
"public/assets/images/dusk/store_icon.png": {
"file": "assets/store_icon-B52tsSKO.png",
"src": "public/assets/images/dusk/store_icon.png"
},
"public/assets/images/icons/article.gif": {
"file": "assets/article-CYhGsSKA.gif",
"src": "public/assets/images/icons/article.gif"
@@ -104,7 +134,7 @@
"src": "public/assets/images/profile/profile-bg.png"
},
"resources/css/global.css": {
"file": "assets/global-lUxz4Nll.css",
"file": "assets/global-DVSCrBhf.css",
"name": "global",
"names": [
"global.css"
@@ -140,30 +170,57 @@
]
},
"resources/js/global.js": {
"file": "assets/global-DM5efAYH.js",
"file": "assets/global-Bkbv5Qui.js",
"name": "global",
"src": "resources/js/global.js",
"isEntry": true,
"imports": [
"_axios-D3CjrHSW.js"
"_chunk-BqhQeaEc.js",
"_axios-BgauIbGG.js"
]
},
"resources/js/ssr.jsx": {
"file": "assets/ssr-dFzH1dUH.js",
"name": "ssr",
"src": "resources/js/ssr.jsx",
"isEntry": true,
"imports": [
"_chunk-BqhQeaEc.js"
]
},
"resources/themes/atom/css/app.css": {
"file": "assets/app-BGFeVnff.css",
"file": "assets/app-DrKvMbgn.css",
"src": "resources/themes/atom/css/app.css"
},
"resources/themes/atom/js/app.js": {
"file": "assets/app-BBe1NbvP.js",
"name": "app",
"src": "resources/themes/atom/js/app.js",
"isEntry": true,
"imports": [
"_chunk-BqhQeaEc.js",
"_swiper-B7Yxk788.js",
"_axios-BgauIbGG.js"
]
},
"resources/themes/dusk/css/app.css": {
"file": "assets/app-tY2AX6sE.css",
"name": "app",
"names": [
"app.css"
],
"src": "resources/themes/atom/css/app.css",
"src": "resources/themes/dusk/css/app.css",
"isEntry": true,
"css": [
"assets/app-DrKvMbgn.css"
],
"assets": [
"assets/camera-fu-bmGhB.png",
"assets/discord-rbcnEh-j.png",
"assets/background_image-BH7pVpv1.png",
"assets/feeds-BtHcJdHX.png",
"assets/chat-r5H1PnTg.png",
"assets/article-CYhGsSKA.gif",
"assets/lighthouse-BON6qnQ0.png",
"assets/currency-LKXzczCA.png",
"assets/store_icon-B52tsSKO.png",
"assets/catalog-D-956oDx.png",
"assets/inventory-BlHYLNGT.png",
"assets/due-chat-CeO4yxLu.png",
@@ -171,29 +228,23 @@
"assets/credits-Dpg5Nmby.png",
"assets/duckets-CaGJI1Oy.png",
"assets/diamonds-BtfqKoQu.png",
"assets/profile-bg-BWx4iuHa.png",
"assets/trophy-gold-bbKmpkii.png",
"assets/trophy-silver-bGfHJkQ_.png",
"assets/trophy-bronze-CgV5j1MU.png",
"assets/background-light-CP7oKwVT.jpg",
"assets/background-dark-BfkMu3-0.jpg",
"assets/shop-D3NfN6cF.png",
"assets/leaderboards-CGasq3cL.png",
"assets/rules--xzBmecz.gif",
"assets/home-DIMFC97Y.png",
"assets/community-Do_t1zw9.png"
"assets/leaderboard_circle_image-BYkDVX69.png"
]
},
"resources/themes/atom/js/app.js": {
"file": "assets/app-KZYZIZAW.js",
"resources/themes/dusk/js/app.js": {
"file": "assets/app-Dnwim5HE.js",
"name": "app",
"src": "resources/themes/atom/js/app.js",
"src": "resources/themes/dusk/js/app.js",
"isEntry": true,
"imports": [
"_axios-D3CjrHSW.js"
"_swiper-B7Yxk788.js",
"_axios-BgauIbGG.js"
],
"css": [
"assets/app-CeYfhhVD.css"
"assets/app-DU8Y3NnC.css"
]
}
}
+31
View File
@@ -0,0 +1,31 @@
import { Head } from '@inertiajs/react'
export default function Home({ auth, hotelName }) {
return (
<>
<Head title="Home" />
<div className="col-span-12">
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-sm p-8 text-center">
<h1 className="text-4xl font-bold mb-4" style={{ color: 'var(--color-text)' }}>
Welkom bij {hotelName}
</h1>
<p className="text-lg" style={{ color: 'var(--color-text-muted)' }}>
Dit is een Inertia.js pagina zelfde layout, zelfde Tailwind, zelfde stijlen.
</p>
<div className="mt-8 flex justify-center gap-4">
<a
href="/"
className="px-6 py-3 rounded-xl font-semibold transition-all duration-200 hover:scale-105"
style={{
backgroundColor: 'var(--color-primary)',
color: 'var(--button-text-color)',
}}
>
Naar huis
</a>
</div>
</div>
</div>
</>
)
}
+119
View File
@@ -0,0 +1,119 @@
import { Head, usePage } from '@inertiajs/react'
export default function Index({ articles, photos }) {
const { avatarImager } = usePage().props
return (
<>
<Head title="Welkom" />
<div className="col-span-12 space-y-14">
<div className="col-span-12">
<div className="flex w-full flex-col gap-y-4 overflow-hidden rounded-lg p-3">
<div className="flex gap-x-2">
<div className="hotel-icon relative flex min-h-[50px] min-w-[50px] max-w-[50px] max-h-[50px] items-center justify-center rounded-full" />
<div className="flex flex-col">
<p className="font-semibold text-black dark:text-gray-200">Laatste nieuws</p>
<p className="dark:text-gray-500">Blijf op de hoogte van het laatste hotel nieuws.</p>
</div>
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
{articles.length > 0 ? articles.map(article => (
<a key={article.id} href={`/community/article/${article.slug}`}
className="group relative block h-[210px] w-full overflow-hidden rounded shadow-sm transition-all duration-200 ease-in-out hover:scale-[101%]"
style={{ backgroundColor: 'var(--color-surface)' }}
onMouseEnter={e => {
const el = e.currentTarget.querySelector('.article-img-inner')
if (el) el.classList.add('article-image-slide')
}}
onMouseLeave={e => {
const el = e.currentTarget.querySelector('.article-img-inner')
if (el) el.classList.remove('article-image-slide')
}}>
<div className="article-img-inner h-[100px] w-full bg-cover bg-center transition-[background-position] duration-300"
style={{ backgroundImage: `url('/storage/${article.image}')`, backgroundPosition: '300px 220px' }}>
</div>
<div className="px-3 md:px-4">
<p className="truncate text-base font-semibold md:text-lg dark:text-gray-200">{article.title}</p>
<div className="flex items-center gap-x-2">
{article.user && (
<>
<div className="mt-2 flex h-8 w-8 items-center justify-center overflow-hidden rounded-full md:mt-3 md:h-10 md:w-10"
style={{ backgroundColor: 'var(--color-background)' }}>
<img src={`${avatarImager}${article.user.look}&headonly=1`}
alt="" className="h-full w-full object-cover" />
</div>
<p className="mt-2 text-sm font-semibold md:mt-4 md:text-base"
style={{ color: 'var(--color-text-muted)' }}>{article.user.username}</p>
</>
)}
</div>
</div>
</a>
)) : (
<>
{[1, 2, 3, 4].map(i => (
<div key={i}
className="h-[210px] w-full overflow-hidden rounded bg-white shadow-sm dark:bg-gray-900">
<div className="article-image"
style={{ background: "url('https://i.imgur.com/uGLDOUu.png')" }} />
<div className="mt-4 px-4">
<p className="truncate text-lg font-semibold dark:text-gray-200">Geen artikelen</p>
<div className="flex items-center gap-x-2">
<div className="mt-3 flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-gray-100 dark:bg-gray-800">
<img src={`${avatarImager}&headonly=1`} alt="" />
</div>
<p className="mt-4 font-semibold dark:text-gray-400">Epicnabbo</p>
</div>
</div>
</div>
))}
</>
)}
</div>
</div>
</div>
{photos.length > 0 && (
<div className="col-span-12">
<div className="flex w-full flex-col gap-y-4 overflow-hidden rounded-lg p-3">
<div className="flex gap-x-2">
<div className="camera-icon relative flex min-h-[50px] min-w-[50px] max-w-[50px] max-h-[50px] items-center justify-center rounded-full" />
<div className="flex flex-col">
<p className="font-semibold text-black dark:text-gray-200">Laatste foto's</p>
<p className="dark:text-gray-500">Bekijk de mooiste momenten vastgelegd door gebruikers.</p>
</div>
</div>
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-4 xl:grid-cols-5">
{photos.map(photo => (
<a key={photo.id} href={photo.url} data-fancybox="gallery" className="group block cursor-pointer">
<div className="relative overflow-hidden rounded-lg border border-gray-600 shadow-md transition-all duration-300 hover:border-[#eeb425]">
<div className="relative aspect-[4/3] overflow-hidden">
<img src={photo.url} alt={`Photo by ${photo.user?.username || 'Unknown'}`}
className="h-full w-full object-cover object-center transition-transform duration-300 group-hover:scale-110" />
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-transparent to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
</div>
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 to-transparent p-2">
<div className="flex items-center gap-2">
<div className="flex h-7 w-7 items-center justify-center overflow-hidden rounded-full border border-gray-500 bg-gray-700">
<img src={`${avatarImager}${photo.user?.look || ''}&direction=2&headonly=1&head_direction=2&gesture=sml`}
alt={photo.user?.username || 'Unknown'} className="h-full w-full object-cover" />
</div>
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-semibold text-white">{photo.user?.username || 'Unknown'}</p>
</div>
</div>
</div>
</div>
</a>
))}
</div>
</div>
</div>
)}
</div>
</>
)
}
+12
View File
@@ -0,0 +1,12 @@
import { createInertiaApp } from '@inertiajs/react'
import { createRoot } from 'react-dom/client'
createInertiaApp({
resolve: name => {
const pages = import.meta.glob('./pages/**/*.jsx', { eager: true })
return pages[`./pages/${name}.jsx`]
},
setup({ el, App, props }) {
createRoot(el).render(<App {...props} />)
},
})
+216
View File
@@ -0,0 +1,216 @@
<!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="csrf-token" content="{{ csrf_token() }}">
<title inertia>{{ setting('hotel_name') }}</title>
<link rel="icon" type="image/gif" sizes="18x17" href="{{ asset('assets/images/home_icon.gif') }}">
@php
$themeFont = setting('font_family', 'Nunito');
$googleFonts = ['Nunito', 'Inter', 'Poppins', 'Roboto', 'Playfair'];
@endphp
@if(in_array($themeFont, $googleFonts))
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family={{ $themeFont }}:wght@400;600;700&display=swap">
@else
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">
@endif
<link rel="stylesheet" href="{{ asset('assets/css/flowbite.min.css') }}" />
<script src="{{ asset('assets/js/popper.min.js') }}"></script>
<script src="{{ asset('assets/js/tippy-bundle.umd.min.js') }}"></script>
<link rel="stylesheet" href="{{ asset('assets/css/scale.min.css') }}"/>
@vite(['resources/js/ssr.jsx', 'resources/themes/' . setting('theme', 'atom') . '/css/app.css', 'resources/themes/' . setting('theme', 'atom') . '/js/app.js'], 'build')
<style>
:root {
--color-primary: {{ setting('color_primary', '#f59e0b') }};
--color-background: {{ setting('color_background', '#f8fafc') }};
--color-surface: {{ setting('color_surface', '#ffffff') }};
--color-dropdown: {{ setting('color_dropdown', '#ffffff') }};
--color-navbar: {{ setting('color_navbar', '#ffffff') }};
--color-navbar-text: {{ setting('color_navbar_text', '#1e293b') }};
--color-text: {{ setting('color_text', '#0f172a') }};
--color-text-muted: {{ setting('color_text_muted', '#64748b') }};
--color-accent: {{ setting('color_accent', '#10b981') }};
--button-color: {{ setting('button_primary_color', '#f59e0b') }};
--button-text-color: {{ setting('button_text_color', '#1e293b') }};
--button-secondary-color: {{ setting('button_secondary_color', '#22c55e') }};
--button-secondary-text-color: {{ setting('button_secondary_text_color', '#ffffff') }};
--button-secondary-hover-color: {{ setting('button_secondary_hover_color', '#16a34a') }};
--button-secondary-border-color: {{ setting('button_secondary_border_color', '#16a34a') }};
--button-danger-color: {{ setting('button_danger_color', '#ef4444') }};
--button-danger-text-color: {{ setting('button_danger_text_color', '#ffffff') }};
--button-danger-hover-color: {{ setting('button_danger_hover_color', '#dc2626') }};
--button-danger-border-color: {{ setting('button_danger_border_color', '#dc2626') }};
--button-outline-color: {{ setting('button_outline_color', '#eeb425') }};
--button-outline-text-color: {{ setting('button_outline_text_color', '#1a1a2e') }};
--button-outline-hover-color: {{ setting('button_outline_hover_color', '#cf9d15') }};
--border-radius: {{ setting('border_radius', '12') }}px;
--border-color: {{ setting('border_color', '#eeb425') }};
--font-family: '{{ setting('font_family', 'Nunito') }}', sans-serif;
--dropdown-radius: {{ setting('dropdown_style', 'rounded') === 'square' ? '0px' : (setting('dropdown_style', 'rounded') === 'pill' ? '20px' : '8px') }};
--dropdown-border: {{ setting('dropdown_border', '1') === '1' ? '1px solid var(--color-primary)' : 'none' }};
--dropdown-shadow: {{ setting('dropdown_shadow', 'medium') === 'none' ? 'none' : (setting('dropdown_shadow', 'medium') === 'small' ? '0 2px 8px rgba(0,0,0,0.15)' : (setting('dropdown_shadow', 'medium') === 'large' ? '0 8px 32px rgba(0,0,0,0.4)' : '0 4px 16px rgba(0,0,0,0.25)')) }};
/* Sizing */
--size-navigation-font: {{ setting('size_navigation_font', '14') }}px;
--size-navigation-padding: {{ setting('size_navigation_padding', '16') }}px;
--size-navigation-height: {{ setting('size_navigation_height', '60') }}px;
--size-heading-h1: {{ setting('size_heading_h1', '32') }}px;
--size-heading-h2: {{ setting('size_heading_h2', '24') }}px;
--size-heading-h3: {{ setting('size_heading_h3', '20') }}px;
--size-body-text: {{ setting('size_body_text', '16') }}px;
--size-small-text: {{ setting('size_small_text', '14') }}px;
--size-button-text: {{ setting('size_button_text', '14') }}px;
--size-card-padding: {{ setting('size_card_padding', '24') }}px;
--size-icon-small: {{ setting('size_icon_small', '16') }}px;
--size-icon-medium: {{ setting('size_icon_medium', '24') }}px;
--size-icon-large: {{ setting('size_icon_large', '32') }}px;
/* Avatar */
--avatar-border-radius: {{ setting('avatar_border_radius', '8') }}px;
--avatar-border-color: {{ setting('avatar_border_color', '#eeb425') }};
/* Badge */
--badge-background: {{ setting('badge_background', '#eeb425') }};
--badge-text-color: {{ setting('badge_text_color', '#000000') }};
--badge-border-radius: {{ setting('badge_border_radius', '4') }}px;
/* Card */
--card-border-width: {{ setting('card_border_width', '1') }}px;
--card-border-radius: {{ setting('card_border_radius', '12') }}px;
/* Link */
--link-color: {{ setting('link_color', '#eeb425') }};
--link-hover-color: {{ setting('link_hover_color', '#cf9d15') }};
/* Input */
--input-border-color: {{ setting('input_border_color', '#4b5563') }};
--input-text-color: {{ setting('input_text_color', '#ffffff') }};
--input-placeholder-color: {{ setting('input_placeholder_color', '#9ca3af') }};
--input-focus-color: {{ setting('input_focus_color', '#eeb425') }};
/* Shadow */
--shadow-color: {{ setting('shadow_color', '#000000') }};
--shadow-opacity: {{ setting('shadow_opacity', '20') }};
/* Spinner */
--spinner-color: {{ setting('spinner_color', '#eeb425') }};
/* Navbar */
--navbar-height: {{ setting('navbar_height', '64') }}px;
}
/* Button Effects */
@keyframes sparkle {
0%, 100% { transform: scale(0) rotate(0deg); opacity: 0; }
50% { transform: scale(1) rotate(180deg); opacity: 1; }
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-3px); }
}
@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 5px var(--color-primary), 0 0 10px var(--color-primary); }
50% { box-shadow: 0 0 15px var(--color-primary), 0 0 25px var(--color-primary); }
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
@keyframes shine {
0% { background-position: -200% center; }
100% { background-position: 200% center; }
}
</style>
@inertiaHead
<x-turnstile.scripts />
</head>
<body class="flex min-h-screen flex-col site-bg dark:bg-gray-800{{ setting('button_effects_enabled') ? ' btn-effects-active' : '' }}{{ setting('button_effects_enabled') && setting('button_effects_navigation', 'none') != 'none' ? ' btn-effect-nav-' . setting('button_effects_navigation') : '' }}{{ setting('button_effects_enabled') && setting('button_effects_buttons', 'none') != 'none' ? ' btn-effect-btn-' . setting('button_effects_buttons') : '' }}{{ setting('button_effects_enabled') && setting('button_effects_links', 'none') != 'none' ? ' btn-effect-link-' . setting('button_effects_links') : '' }}" style="{{ setting('button_effects_enabled') ? '--btn-effect-speed: ' . (setting('button_effects_speed') == 'slow' ? '3s' : (setting('button_effects_speed') == 'fast' ? '0.5s' : '1.5s')) : '' }}">
@if(config('habbo.site.debug_mode_enabled') && config('habbo.site.site_environment') === 'production')
<div class="w-full py-2 px-4 text-center rounded text-white bg-red-500">
{{ __('It seems like debug mode is enabled while being in production. It is heavily recommended too set APP_DEBUG in the .env file to false in production mode') }}
</div>
@endif
<x-messages.flash-messages />
<div id="inertia-app" style="background-color: var(--color-background)">
{{-- Top header (only for auth) --}}
@auth
<x-top-header />
@endauth
{{-- Site Header --}}
<x-site-header />
{{-- Navigation --}}
<nav class="relative bg-white shadow-sm dark:bg-gray-900" style="background-color: var(--color-navbar)">
<div class="max-w-7xl min-h-[60px] px-4 md:flex md:items-center md:justify-between md:mx-auto">
<x-navigation.navigation-menu />
<div class="hidden lg:flex items-center">
<x-navigation.theme-mode-switcher />
<x-navigation.language-selector>
<img src="/assets/images/icons/flags/{{ session()->has('locale') ? session()->get('locale') : config('habbo.site.default_language') }}.png" alt="">
</x-navigation.language-selector>
</div>
<x-navigation.mobile-menu />
</div>
</nav>
{{-- Content --}}
<main class="overflow-hidden site-bg">
<div class="mx-auto mt-10 grid max-w-7xl grid-cols-12 gap-x-3 gap-y-8 p-6 md:mt-0">
@inertia
</div>
</main>
</div>
<x-footer />
@if (setting('cloudflare_turnstile_enabled'))
<script>
document.addEventListener('DOMContentLoaded', function() {
const turnstileWidget = document.querySelector('#cf-turnstile-widget');
const theme = localStorage.getItem('theme');
if (turnstileWidget && theme) {
turnstileWidget.setAttribute('data-theme', theme);
}
});
</script>
@endif
@if (setting('cms_color_mode') === 'dark')
<script>
if (localStorage.getItem("theme") === null) {
document.documentElement.classList.add("dark");
localStorage.setItem("theme", 'dark');
}
</script>
@endif
@if (setting('google_recaptcha_enabled'))
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
@endif
<script defer src="{{ asset('assets/js/alpine-ui.js') }}"></script>
<script defer src="{{ asset('assets/js/alpine-focus.js') }}"></script>
<script src="{{ asset('assets/js/flowbite.min.js') }}"></script>
</body>
</html>
+7
View File
@@ -9,6 +9,13 @@ use App\Http\Controllers\Miscellaneous\MaintenanceController;
use App\Http\Controllers\User\BannedController;
use Illuminate\Support\Facades\Route;
// Inertia demo route
Route::get('/inertia-test', function () {
return inertia('Home', [
'hotelName' => setting('hotel_name', 'Epicnabbo'),
]);
})->name('inertia.test');
// Radio embed (public, no auth required)
Route::get('/radio/embed', [\App\Http\Controllers\Radio\EmbedController::class, 'show'])->name('radio.embed');
+4
View File
@@ -1,5 +1,6 @@
import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import react from "@vitejs/plugin-react";
import path from "path";
import tailwindcss from "@tailwindcss/postcss";
import autoprefixer from "autoprefixer";
@@ -10,6 +11,7 @@ export default defineConfig({
input: [
"resources/css/global.css",
"resources/js/global.js",
"resources/js/ssr.jsx",
"resources/themes/atom/css/app.css",
"resources/themes/atom/js/app.js",
"resources/themes/dusk/css/app.css",
@@ -17,6 +19,8 @@ export default defineConfig({
],
}),
react(),
{
name: "blade",
handleHotUpdate({ file, server }) {
+613 -185
View File
File diff suppressed because it is too large Load Diff