You've already forked Atomcms-edit
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:
Executable
+31
@@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Executable
+119
@@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Executable
+12
@@ -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} />)
|
||||
},
|
||||
})
|
||||
Executable
+216
@@ -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>
|
||||
Reference in New Issue
Block a user