diff --git a/README.md b/README.md index 4df82fc..80163f2 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,20 @@ If you prefer manual control: | `make setup-laravel` | Configure auth, API, middleware | | `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) After installing Laravel, run the interactive setup: diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 3021266..148d631 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -38,6 +38,11 @@ RUN pecl install redis && docker-php-ext-enable redis # Install 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 RUN useradd -G www-data,root -u 1000 -d /home/devuser devuser 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/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 USER devuser RUN composer run-script post-autoload-dump 2>/dev/null || true diff --git a/src/app/Helpers/ViteHelper.php b/src/app/Helpers/ViteHelper.php new file mode 100644 index 0000000..b3ecf64 --- /dev/null +++ b/src/app/Helpers/ViteHelper.php @@ -0,0 +1,88 @@ + + /* 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; } + +CSS; + } + + /** + * Get a warning banner HTML for development. + */ + public static function devWarningBanner(): string + { + if (app()->environment('production')) { + return ''; + } + + return <<<'HTML' +
+ ⚠️ Development Notice: Vite assets are not built. + Run docker-compose run --rm node npm run build to build assets. +
+HTML; + } +} diff --git a/src/package-lock.json b/src/package-lock.json index dbf1580..1e7e231 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -1,5 +1,5 @@ { - "name": "app", + "name": "html", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/src/resources/views/layouts/app.blade.php b/src/resources/views/layouts/app.blade.php index d802b2d..4008063 100644 --- a/src/resources/views/layouts/app.blade.php +++ b/src/resources/views/layouts/app.blade.php @@ -21,9 +21,16 @@ - @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 + @if(!\App\Helpers\ViteHelper::manifestExists()) + {!! \App\Helpers\ViteHelper::devWarningBanner() !!} + @endif
@include('layouts.navigation') diff --git a/src/resources/views/layouts/guest.blade.php b/src/resources/views/layouts/guest.blade.php index 1b212aa..bbb76bd 100644 --- a/src/resources/views/layouts/guest.blade.php +++ b/src/resources/views/layouts/guest.blade.php @@ -21,9 +21,16 @@ - @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 + @if(!\App\Helpers\ViteHelper::manifestExists()) + {!! \App\Helpers\ViteHelper::devWarningBanner() !!} + @endif