Visual header, body, and footer composition screen.

* Drag-and-drop section ordering with automatic saving.
* Add existing reusable documents to regions.
* Remove placements without deleting documents.
* Create independent page-specific copies.
* Shared-document usage warnings.
* Document creation, duplication, deletion protection and filtering.
* Draft and published document states.
* Document and placement scheduling.
* Authenticated homepage preview.
* Draft sections displayed in preview with a clear draft marker.
* Content Composer added to the administration sidebar.
* Public rendering now respects:
    * Publication status
    * Future publication dates
    * Expiry dates
    * Placement start and end dates
    * Enabled state
* Original Saasland homepage remains as the fallback when no composition exists.
This commit is contained in:
Mark Gregory
2026-07-27 08:39:20 +01:00
parent cb793fcc0b
commit 0c26d8db8f
7 changed files with 34 additions and 2 deletions
@@ -3,6 +3,7 @@ namespace Modules\Content\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\Content\Models\ContentDocument;
use Modules\Content\Models\ContentType;
use Modules\Content\Models\PageContent;
@@ -14,6 +15,19 @@ final class InstallSaaslandContent extends Command
public function handle(): int
{
$requiredTables = ['content_types', 'content_documents', 'page_content'];
$missingTables = array_values(array_filter(
$requiredTables,
static fn (string $table): bool => ! Schema::hasTable($table),
));
if ($missingTables !== []) {
$this->components->error('Content composition tables are missing: '.implode(', ', $missingTables).'.');
$this->components->info('Run php artisan migrate before installing the Saasland composition.');
return self::FAILURE;
}
DB::transaction(function (): void {
if ($this->option('force')) PageContent::query()->where('page_key','home')->delete();
$definitions = $this->definitions();
@@ -15,6 +15,7 @@ final class WelcomeThemeViewTest extends TestCase
{
$this->get('/')
->assertOk()
->assertViewIs('stylist-theme-saasland::home');
->assertViewIs('stylist-theme-saasland::home')
->assertSee('Transform Your Business With Our SaaS Solution');
}
}
@@ -13,6 +13,14 @@
@if($hasComposition)
{!! $contentRenderer->renderRegion($pageKey, 'body') !!}
@else
@include('stylist-theme-saasland::legacy-home-content')
@include('stylist-theme-saasland::legacy-home-content', [
'enabled' => fn (string $key, bool $default = true) => filter_var($themeSettings[$key] ?? $default, FILTER_VALIDATE_BOOL),
'heroTitle' => filled($themeSettings['hero_title'] ?? null)
? $themeSettings['hero_title']
: 'Transform Your Business With Our SaaS Solution',
'heroText' => filled($themeSettings['hero_description'] ?? null)
? $themeSettings['hero_description']
: 'Streamline operations, boost productivity, and improve customer satisfaction with one connected platform built for modern teams.',
])
@endif
@endsection
+3
View File
@@ -0,0 +1,3 @@
*
!data/
!.gitignore
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore