Add ViteHelper fallback for missing frontend assets
This commit is contained in:
14
README.md
14
README.md
@@ -208,6 +208,20 @@ If you prefer manual control:
|
|||||||
| `make setup-laravel` | Configure auth, API, middleware |
|
| `make setup-laravel` | Configure auth, API, middleware |
|
||||||
| `make setup-all` | Run both setup scripts |
|
| `make setup-all` | Run both setup scripts |
|
||||||
|
|
||||||
|
### Frontend Assets (Vite)
|
||||||
|
|
||||||
|
Build frontend CSS/JS assets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build assets for production
|
||||||
|
docker-compose run --rm node npm run build
|
||||||
|
|
||||||
|
# Or run Vite dev server (hot reload)
|
||||||
|
docker-compose --profile frontend up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** The template includes a resilient fallback - if assets aren't built, basic styling is provided and a development warning is shown. This prevents the `ViteManifestNotFoundException` error from breaking the app.
|
||||||
|
|
||||||
## Laravel Setup (Auth, API, Middleware)
|
## Laravel Setup (Auth, API, Middleware)
|
||||||
|
|
||||||
After installing Laravel, run the interactive setup:
|
After installing Laravel, run the interactive setup:
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ RUN pecl install redis && docker-php-ext-enable redis
|
|||||||
# Install Composer
|
# Install Composer
|
||||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
|
# Install Node.js for asset building
|
||||||
|
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||||
|
&& apt-get install -y nodejs \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Create system user to run Composer and Artisan commands
|
# Create system user to run Composer and Artisan commands
|
||||||
RUN useradd -G www-data,root -u 1000 -d /home/devuser devuser
|
RUN useradd -G www-data,root -u 1000 -d /home/devuser devuser
|
||||||
RUN mkdir -p /home/devuser/.composer && \
|
RUN mkdir -p /home/devuser/.composer && \
|
||||||
@@ -61,6 +66,13 @@ RUN chown -R devuser:www-data /var/www/html \
|
|||||||
&& chmod -R 775 /var/www/html/storage 2>/dev/null || true \
|
&& chmod -R 775 /var/www/html/storage 2>/dev/null || true \
|
||||||
&& chmod -R 775 /var/www/html/bootstrap/cache 2>/dev/null || true
|
&& chmod -R 775 /var/www/html/bootstrap/cache 2>/dev/null || true
|
||||||
|
|
||||||
|
# Build frontend assets (if package.json exists)
|
||||||
|
RUN if [ -f "package.json" ]; then \
|
||||||
|
npm ci --ignore-scripts && \
|
||||||
|
npm run build && \
|
||||||
|
rm -rf node_modules; \
|
||||||
|
fi
|
||||||
|
|
||||||
# Run post-install scripts
|
# Run post-install scripts
|
||||||
USER devuser
|
USER devuser
|
||||||
RUN composer run-script post-autoload-dump 2>/dev/null || true
|
RUN composer run-script post-autoload-dump 2>/dev/null || true
|
||||||
|
|||||||
88
src/app/Helpers/ViteHelper.php
Normal file
88
src/app/Helpers/ViteHelper.php
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
class ViteHelper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Check if the Vite manifest exists (assets have been built).
|
||||||
|
*/
|
||||||
|
public static function manifestExists(): bool
|
||||||
|
{
|
||||||
|
return file_exists(public_path('build/manifest.json'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get fallback CSS for when Vite assets are not built.
|
||||||
|
* This provides basic styling so the app remains usable.
|
||||||
|
*/
|
||||||
|
public static function fallbackStyles(): string
|
||||||
|
{
|
||||||
|
return <<<'CSS'
|
||||||
|
<style>
|
||||||
|
/* Fallback styles when Vite assets are not built */
|
||||||
|
*, *::before, *::after { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: ui-sans-serif, system-ui, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
background: #f3f4f6;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
.dark body { background: #111827; color: #f9fafb; }
|
||||||
|
.min-h-screen { min-height: 100vh; }
|
||||||
|
.bg-gray-100 { background: #f3f4f6; }
|
||||||
|
.bg-white { background: #fff; }
|
||||||
|
.shadow { box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
||||||
|
.max-w-7xl { max-width: 80rem; margin: 0 auto; }
|
||||||
|
.px-4 { padding-left: 1rem; padding-right: 1rem; }
|
||||||
|
.py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
|
||||||
|
.font-semibold { font-weight: 600; }
|
||||||
|
.text-xl { font-size: 1.25rem; }
|
||||||
|
.text-gray-800 { color: #1f2937; }
|
||||||
|
a { color: #3b82f6; text-decoration: none; }
|
||||||
|
a:hover { text-decoration: underline; }
|
||||||
|
.hidden { display: none; }
|
||||||
|
.flex { display: flex; }
|
||||||
|
.items-center { align-items: center; }
|
||||||
|
.justify-between { justify-content: space-between; }
|
||||||
|
.space-x-4 > * + * { margin-left: 1rem; }
|
||||||
|
nav { background: #fff; border-bottom: 1px solid #e5e7eb; padding: 1rem; }
|
||||||
|
.container { max-width: 80rem; margin: 0 auto; padding: 0 1rem; }
|
||||||
|
button, .btn {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: #3b82f6;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button:hover, .btn:hover { background: #2563eb; }
|
||||||
|
input, select, textarea {
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.alert { padding: 1rem; border-radius: 0.375rem; margin-bottom: 1rem; }
|
||||||
|
.alert-warning { background: #fef3c7; border: 1px solid #f59e0b; color: #92400e; }
|
||||||
|
</style>
|
||||||
|
CSS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a warning banner HTML for development.
|
||||||
|
*/
|
||||||
|
public static function devWarningBanner(): string
|
||||||
|
{
|
||||||
|
if (app()->environment('production')) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return <<<'HTML'
|
||||||
|
<div class="alert alert-warning" style="margin:1rem;padding:1rem;background:#fef3c7;border:1px solid #f59e0b;border-radius:0.5rem;color:#92400e;">
|
||||||
|
<strong>⚠️ Development Notice:</strong> Vite assets are not built.
|
||||||
|
Run <code style="background:#fde68a;padding:0.125rem 0.25rem;border-radius:0.25rem;">docker-compose run --rm node npm run build</code> to build assets.
|
||||||
|
</div>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/package-lock.json
generated
2
src/package-lock.json
generated
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "html",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|||||||
@@ -21,9 +21,16 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
@if(\App\Helpers\ViteHelper::manifestExists())
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
@else
|
||||||
|
{!! \App\Helpers\ViteHelper::fallbackStyles() !!}
|
||||||
|
@endif
|
||||||
</head>
|
</head>
|
||||||
<body class="font-sans antialiased">
|
<body class="font-sans antialiased">
|
||||||
|
@if(!\App\Helpers\ViteHelper::manifestExists())
|
||||||
|
{!! \App\Helpers\ViteHelper::devWarningBanner() !!}
|
||||||
|
@endif
|
||||||
<div class="min-h-screen bg-gray-100 dark:bg-gray-900">
|
<div class="min-h-screen bg-gray-100 dark:bg-gray-900">
|
||||||
@include('layouts.navigation')
|
@include('layouts.navigation')
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,16 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
@if(\App\Helpers\ViteHelper::manifestExists())
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
@else
|
||||||
|
{!! \App\Helpers\ViteHelper::fallbackStyles() !!}
|
||||||
|
@endif
|
||||||
</head>
|
</head>
|
||||||
<body class="font-sans text-gray-900 antialiased">
|
<body class="font-sans text-gray-900 antialiased">
|
||||||
|
@if(!\App\Helpers\ViteHelper::manifestExists())
|
||||||
|
{!! \App\Helpers\ViteHelper::devWarningBanner() !!}
|
||||||
|
@endif
|
||||||
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
|
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
|
||||||
<div>
|
<div>
|
||||||
<a href="/">
|
<a href="/">
|
||||||
|
|||||||
Reference in New Issue
Block a user