From 214d0ac269a3d67bf59437422a78f7bf3c36e692 Mon Sep 17 00:00:00 2001 From: roble Date: Sat, 25 Apr 2026 21:58:23 +0100 Subject: [PATCH 1/6] Refactor Blog Module: Update file names, adjust casing, and implement new structure - Changed version_file from 'module.json' to 'composer.json' in release workflow. - Updated module name from 'Blog' to 'blog' in test workflow. - Modified E2E test command to use lowercase 'blog'. - Removed BlogServiceProvider and RouteServiceProvider files. - Updated composer.json to include new providers and set module description and version. - Refactored BlogDatabaseSeeder to use a createPost method for post creation. - Added DatabaseSeeder for the Blog module. - Deleted module.json and created new data classes: AuthorData, CategoryData, PostData. - Created PostStatus enum and BlogPlugin for Filament integration. - Implemented CategoryResource and PostResource with respective pages and schemas. - Developed BlogController for handling blog-related requests. - Created models for Category and Post with necessary relationships and media handling. --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- CLAUDE.md | 2 +- Taskfile.yml | 4 +- app/Providers/BlogServiceProvider.php | 16 ------- app/Providers/RouteServiceProvider.php | 17 ------- composer.json | 13 ++++-- database/seeders/BlogDatabaseSeeder.php | 44 +++++++++---------- database/seeders/DatabaseSeeder.php | 13 ++++++ module.json | 15 ------- resources/js/types/page-props.d.ts | 2 +- routes/api.php | 4 +- routes/navigation.php | 2 +- routes/web.php | 8 ++-- {app => src}/Data/AuthorData.php | 0 {app => src}/Data/CategoryData.php | 0 {app => src}/Data/PostData.php | 0 {app => src}/Enums/PostStatus.php | 0 {app => src}/Filament/BlogPlugin.php | 0 .../Resources/Blog/CategoryResource.php | 0 .../Resources/Blog/Pages/CreateCategory.php | 0 .../Resources/Blog/Pages/CreatePost.php | 0 .../Resources/Blog/Pages/EditCategory.php | 0 .../Resources/Blog/Pages/EditPost.php | 0 .../Resources/Blog/Pages/ListCategories.php | 0 .../Resources/Blog/Pages/ListPosts.php | 0 .../Filament/Resources/Blog/PostResource.php | 0 .../Resources/Blog/Schemas/CategoryForm.php | 0 .../Resources/Blog/Schemas/PostForm.php | 0 .../Resources/Blog/Tables/CategoriesTable.php | 0 .../Resources/Blog/Tables/PostsTable.php | 0 .../Http/Controllers/BlogController.php | 0 {app => src}/Models/Category.php | 0 {app => src}/Models/Post.php | 0 src/Providers/BlogServiceProvider.php | 9 ++++ 35 files changed, 66 insertions(+), 87 deletions(-) delete mode 100644 app/Providers/BlogServiceProvider.php delete mode 100644 app/Providers/RouteServiceProvider.php create mode 100644 database/seeders/DatabaseSeeder.php delete mode 100644 module.json rename {app => src}/Data/AuthorData.php (100%) rename {app => src}/Data/CategoryData.php (100%) rename {app => src}/Data/PostData.php (100%) rename {app => src}/Enums/PostStatus.php (100%) rename {app => src}/Filament/BlogPlugin.php (100%) rename {app => src}/Filament/Resources/Blog/CategoryResource.php (100%) rename {app => src}/Filament/Resources/Blog/Pages/CreateCategory.php (100%) rename {app => src}/Filament/Resources/Blog/Pages/CreatePost.php (100%) rename {app => src}/Filament/Resources/Blog/Pages/EditCategory.php (100%) rename {app => src}/Filament/Resources/Blog/Pages/EditPost.php (100%) rename {app => src}/Filament/Resources/Blog/Pages/ListCategories.php (100%) rename {app => src}/Filament/Resources/Blog/Pages/ListPosts.php (100%) rename {app => src}/Filament/Resources/Blog/PostResource.php (100%) rename {app => src}/Filament/Resources/Blog/Schemas/CategoryForm.php (100%) rename {app => src}/Filament/Resources/Blog/Schemas/PostForm.php (100%) rename {app => src}/Filament/Resources/Blog/Tables/CategoriesTable.php (100%) rename {app => src}/Filament/Resources/Blog/Tables/PostsTable.php (100%) rename {app => src}/Http/Controllers/BlogController.php (100%) rename {app => src}/Models/Category.php (100%) rename {app => src}/Models/Post.php (100%) create mode 100644 src/Providers/BlogServiceProvider.php diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e6d0709..160d817 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: release: uses: saucebase-dev/saucebase/.github/workflows/semantic-release.yml@main with: - version_file: 'module.json' + version_file: 'composer.json' version_file_path: '.version' prerelease: false secrets: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd84ecd..d29720f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,5 +13,5 @@ jobs: test: uses: saucebase-dev/saucebase/.github/workflows/test-module.yml@main with: - module: Blog + module: blog dependencies: saucebase/auth diff --git a/CLAUDE.md b/CLAUDE.md index bc17df0..7a11c6a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,7 +57,7 @@ When adding a field visible on the frontend, update **all three** in sync: php -d memory_limit=2048M artisan test --testsuite=Modules --filter='^Modules\\Blog\\Tests' # E2E -npx playwright test --project="@Blog*" +npx playwright test --project="@blog*" ``` --- diff --git a/Taskfile.yml b/Taskfile.yml index f361133..fedd074 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -9,11 +9,11 @@ tasks: test:e2e: desc: Run E2E tests for Blog module - cmd: npx playwright test --project="@Blog*" {{.CLI_ARGS}} + cmd: npx playwright test --project="@blog*" {{.CLI_ARGS}} interactive: true # ── Code Generation ──────────────────────────────────────────── types:generate: desc: Generate TypeScript types from PHP DTOs and enums - cmd: php artisan module:generate-types Blog + cmd: php artisan module:generate-types blog diff --git a/app/Providers/BlogServiceProvider.php b/app/Providers/BlogServiceProvider.php deleted file mode 100644 index 17363c2..0000000 --- a/app/Providers/BlogServiceProvider.php +++ /dev/null @@ -1,16 +0,0 @@ -group(module_path('Blog', '/routes/web.php')); - Route::middleware('api') - ->group(module_path('Blog', '/routes/api.php')); - } -} diff --git a/composer.json b/composer.json index 48e9508..a1641c0 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,9 @@ { "name": "saucebase/blog", - "description": "", + "description": "Blog module", "type": "saucebase-module", + "license": "proprietary", + "version": "0.1.0", "authors": [ { "name": "Saucebase", @@ -10,13 +12,15 @@ ], "extra": { "laravel": { - "providers": [], + "providers": [ + "Modules\\Blog\\Providers\\BlogServiceProvider" + ], "aliases": {} } }, "autoload": { "psr-4": { - "Modules\\Blog\\": "app/", + "Modules\\Blog\\": "src/", "Modules\\Blog\\Database\\Factories\\": "database/factories/", "Modules\\Blog\\Database\\Seeders\\": "database/seeders/" } @@ -25,5 +29,6 @@ "psr-4": { "Modules\\Blog\\Tests\\": "tests/" } - } + }, + "minimum-stability": "stable" } diff --git a/database/seeders/BlogDatabaseSeeder.php b/database/seeders/BlogDatabaseSeeder.php index b565dc8..c35836f 100644 --- a/database/seeders/BlogDatabaseSeeder.php +++ b/database/seeders/BlogDatabaseSeeder.php @@ -20,7 +20,7 @@ public function run(): void $content = fn (string $file): string => file_get_contents(__DIR__.'/content/'.$file); - Post::create([ + $this->createPost([ 'title' => 'What Is Saucebase? The Modular Laravel Starter Kit', 'excerpt' => 'Every SaaS starts the same way: weeks of setup before you write a single line of real product code. Saucebase is the modular Laravel starter kit that skips that part.', 'content' => $content('post-1-what-is-saucebase.html'), @@ -28,9 +28,9 @@ public function run(): void 'published_at' => now()->subMonths(5), 'category_id' => $gettingStarted->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/what-is-saucebase.jpg')); - Post::create([ + $this->createPost([ 'title' => 'Stop Rebuilding the Same Boilerplate Every Project', 'excerpt' => "If you've launched more than one Laravel app, you know the feeling — two weeks gone before you write any real product code. There's a better way.", 'content' => $content('post-2-stop-rebuilding-boilerplate.html'), @@ -40,7 +40,7 @@ public function run(): void 'author_id' => $author?->id, ]); - Post::create([ + $this->createPost([ 'title' => 'The VILT Stack: Laravel, Vue, Inertia, and Tailwind Done Right', 'excerpt' => "Modern web dev has a tension between server productivity and rich interactivity. The VILT stack resolves it — and Saucebase wires all four pieces together so you don't have to.", 'content' => $content('post-3-vilt-stack.html'), @@ -48,9 +48,9 @@ public function run(): void 'published_at' => now()->subMonths(3), 'category_id' => $devExperience->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/tech-stack.jpg')); - $post4 = Post::create([ + $this->createPost([ 'title' => 'Your First Module: Scaffold and Ship in Under 10 Minutes', 'excerpt' => "One Artisan command scaffolds a fully structured module — service provider, controller, routes, Vue pages, migrations, factory, tests, and Vite config. Here's how it works.", 'content' => $content('post-4-your-first-module.html'), @@ -58,14 +58,9 @@ public function run(): void 'published_at' => now()->subMonths(2), 'category_id' => $featuresModules->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/add-your-saucebase.jpg')); - $image4 = public_path('images/blog/add-your-saucebase.jpg'); - if (file_exists($image4)) { - $post4->addMedia($image4)->preservingOriginal()->toMediaCollection('cover'); - } - - $post5 = Post::create([ + $this->createPost([ 'title' => 'Auth, Billing, and Privacy: The Three Modules Every SaaS Needs', 'excerpt' => "Three modules every SaaS needs — and Saucebase ships them already built. Here's what's included and why they matter.", 'content' => $content('post-5-auth-billing-privacy.html'), @@ -73,14 +68,9 @@ public function run(): void 'published_at' => now()->subMonth(), 'category_id' => $featuresModules->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/cookies-or-privacy.jpg')); - $image5 = public_path('images/blog/cookies-or-privacy.jpg'); - if (file_exists($image5)) { - $post5->addMedia($image5)->preservingOriginal()->toMediaCollection('cover'); - } - - $post6 = Post::create([ + $this->createPost([ 'title' => 'Copy-and-Own: The Philosophy Behind Saucebase Modules', 'excerpt' => "Most starter kits give you a locked box. Saucebase takes the opposite approach: when you install a module, the source code lands in your repo and it's yours to read, edit, and own.", 'content' => $content('post-6-copy-and-own.html'), @@ -88,11 +78,17 @@ public function run(): void 'published_at' => now()->subWeeks(2), 'category_id' => $devExperience->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/your-recipes.jpg')); + } - $image6 = public_path('images/blog/your-recipes.jpg'); - if (file_exists($image6)) { - $post6->addMedia($image6)->preservingOriginal()->toMediaCollection('cover'); + private function createPost(array $data, string $coverImage = ''): Post + { + $post = Post::create($data); + + if ($coverImage && file_exists($coverImage)) { + $post->addMedia($coverImage)->preservingOriginal()->toMediaCollection('cover'); } + + return $post; } } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..f29ab7b --- /dev/null +++ b/database/seeders/DatabaseSeeder.php @@ -0,0 +1,13 @@ +call(BlogDatabaseSeeder::class); + } +} diff --git a/module.json b/module.json deleted file mode 100644 index 33ba675..0000000 --- a/module.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "Blog", - "alias": "blog", - "description": "Blog module", - "author": "Saucebase", - "version": "v0.1.0", - "keywords": [ - "blog" - ], - "priority": 0, - "providers": [ - "Modules\\Blog\\Providers\\BlogServiceProvider" - ], - "files": [] -} diff --git a/resources/js/types/page-props.d.ts b/resources/js/types/page-props.d.ts index acfca86..01e84f4 100644 --- a/resources/js/types/page-props.d.ts +++ b/resources/js/types/page-props.d.ts @@ -2,7 +2,7 @@ // Augmentations here are auto-picked up when the module is enabled (no core edits needed). // See docs: architecture/module-system#typescript-page-props -// declare module '@inertiajs/vue3' { +// declare module '@inertiajs/core' { // interface PageProps { // // my_shared_prop?: MyType; // } diff --git a/routes/api.php b/routes/api.php index 899a436..beebb37 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,3 +1,5 @@ group(function (): void { + // No API routes for Blog module +}); diff --git a/routes/navigation.php b/routes/navigation.php index 0a29566..c0d1f95 100644 --- a/routes/navigation.php +++ b/routes/navigation.php @@ -13,7 +13,7 @@ | */ -Navigation::add('Blog', route('blog.index'), function (Section $section) { +Navigation::add('Blog', fn () => route('blog.index'), function (Section $section) { $section->attributes([ 'group' => 'landing', 'slug' => 'blog', diff --git a/routes/web.php b/routes/web.php index a23cc85..223c09b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,8 @@ use Illuminate\Support\Facades\Route; use Modules\Blog\Http\Controllers\BlogController; -Route::get('/blog', [BlogController::class, 'index'])->name('blog.index'); -Route::get('/blog/{category}/{slug}', [BlogController::class, 'show'])->name('blog.show.category'); -Route::get('/blog/{slug}', [BlogController::class, 'show'])->name('blog.show'); +Route::middleware('web')->group(function (): void { + Route::get('/blog', [BlogController::class, 'index'])->name('blog.index'); + Route::get('/blog/{category}/{slug}', [BlogController::class, 'show'])->name('blog.show.category'); + Route::get('/blog/{slug}', [BlogController::class, 'show'])->name('blog.show'); +}); diff --git a/app/Data/AuthorData.php b/src/Data/AuthorData.php similarity index 100% rename from app/Data/AuthorData.php rename to src/Data/AuthorData.php diff --git a/app/Data/CategoryData.php b/src/Data/CategoryData.php similarity index 100% rename from app/Data/CategoryData.php rename to src/Data/CategoryData.php diff --git a/app/Data/PostData.php b/src/Data/PostData.php similarity index 100% rename from app/Data/PostData.php rename to src/Data/PostData.php diff --git a/app/Enums/PostStatus.php b/src/Enums/PostStatus.php similarity index 100% rename from app/Enums/PostStatus.php rename to src/Enums/PostStatus.php diff --git a/app/Filament/BlogPlugin.php b/src/Filament/BlogPlugin.php similarity index 100% rename from app/Filament/BlogPlugin.php rename to src/Filament/BlogPlugin.php diff --git a/app/Filament/Resources/Blog/CategoryResource.php b/src/Filament/Resources/Blog/CategoryResource.php similarity index 100% rename from app/Filament/Resources/Blog/CategoryResource.php rename to src/Filament/Resources/Blog/CategoryResource.php diff --git a/app/Filament/Resources/Blog/Pages/CreateCategory.php b/src/Filament/Resources/Blog/Pages/CreateCategory.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/CreateCategory.php rename to src/Filament/Resources/Blog/Pages/CreateCategory.php diff --git a/app/Filament/Resources/Blog/Pages/CreatePost.php b/src/Filament/Resources/Blog/Pages/CreatePost.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/CreatePost.php rename to src/Filament/Resources/Blog/Pages/CreatePost.php diff --git a/app/Filament/Resources/Blog/Pages/EditCategory.php b/src/Filament/Resources/Blog/Pages/EditCategory.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/EditCategory.php rename to src/Filament/Resources/Blog/Pages/EditCategory.php diff --git a/app/Filament/Resources/Blog/Pages/EditPost.php b/src/Filament/Resources/Blog/Pages/EditPost.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/EditPost.php rename to src/Filament/Resources/Blog/Pages/EditPost.php diff --git a/app/Filament/Resources/Blog/Pages/ListCategories.php b/src/Filament/Resources/Blog/Pages/ListCategories.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/ListCategories.php rename to src/Filament/Resources/Blog/Pages/ListCategories.php diff --git a/app/Filament/Resources/Blog/Pages/ListPosts.php b/src/Filament/Resources/Blog/Pages/ListPosts.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/ListPosts.php rename to src/Filament/Resources/Blog/Pages/ListPosts.php diff --git a/app/Filament/Resources/Blog/PostResource.php b/src/Filament/Resources/Blog/PostResource.php similarity index 100% rename from app/Filament/Resources/Blog/PostResource.php rename to src/Filament/Resources/Blog/PostResource.php diff --git a/app/Filament/Resources/Blog/Schemas/CategoryForm.php b/src/Filament/Resources/Blog/Schemas/CategoryForm.php similarity index 100% rename from app/Filament/Resources/Blog/Schemas/CategoryForm.php rename to src/Filament/Resources/Blog/Schemas/CategoryForm.php diff --git a/app/Filament/Resources/Blog/Schemas/PostForm.php b/src/Filament/Resources/Blog/Schemas/PostForm.php similarity index 100% rename from app/Filament/Resources/Blog/Schemas/PostForm.php rename to src/Filament/Resources/Blog/Schemas/PostForm.php diff --git a/app/Filament/Resources/Blog/Tables/CategoriesTable.php b/src/Filament/Resources/Blog/Tables/CategoriesTable.php similarity index 100% rename from app/Filament/Resources/Blog/Tables/CategoriesTable.php rename to src/Filament/Resources/Blog/Tables/CategoriesTable.php diff --git a/app/Filament/Resources/Blog/Tables/PostsTable.php b/src/Filament/Resources/Blog/Tables/PostsTable.php similarity index 100% rename from app/Filament/Resources/Blog/Tables/PostsTable.php rename to src/Filament/Resources/Blog/Tables/PostsTable.php diff --git a/app/Http/Controllers/BlogController.php b/src/Http/Controllers/BlogController.php similarity index 100% rename from app/Http/Controllers/BlogController.php rename to src/Http/Controllers/BlogController.php diff --git a/app/Models/Category.php b/src/Models/Category.php similarity index 100% rename from app/Models/Category.php rename to src/Models/Category.php diff --git a/app/Models/Post.php b/src/Models/Post.php similarity index 100% rename from app/Models/Post.php rename to src/Models/Post.php diff --git a/src/Providers/BlogServiceProvider.php b/src/Providers/BlogServiceProvider.php new file mode 100644 index 0000000..c73e37a --- /dev/null +++ b/src/Providers/BlogServiceProvider.php @@ -0,0 +1,9 @@ + Date: Wed, 29 Apr 2026 21:36:58 +0100 Subject: [PATCH 2/6] feat(blog): add database seeding task for Blog module --- Taskfile.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Taskfile.yml b/Taskfile.yml index fedd074..46b1848 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -12,6 +12,12 @@ tasks: cmd: npx playwright test --project="@blog*" {{.CLI_ARGS}} interactive: true + # ── Database ────────────────────────────────────────────────── + + db:seed: + desc: Seed the Blog module database + cmd: php artisan modules:seed --module=blog + # ── Code Generation ──────────────────────────────────────────── types:generate: From 022797cdab48e534b211d0d00368f8cdb881a491 Mon Sep 17 00:00:00 2001 From: roble Date: Fri, 22 May 2026 21:33:30 +0100 Subject: [PATCH 3/6] feat(blog): restructure blog module by moving components and adding new pages --- resources/js/app.ts | 12 +----------- resources/js/vue/app.ts | 11 +++++++++++ .../js/{ => vue}/components/LatestPostsSection.vue | 0 resources/js/{ => vue}/components/PostCard.vue | 0 resources/js/{ => vue}/components/PostMeta.vue | 0 resources/js/{ => vue}/pages/Index.vue | 0 resources/js/{ => vue}/pages/Show.vue | 0 7 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 resources/js/vue/app.ts rename resources/js/{ => vue}/components/LatestPostsSection.vue (100%) rename resources/js/{ => vue}/components/PostCard.vue (100%) rename resources/js/{ => vue}/components/PostMeta.vue (100%) rename resources/js/{ => vue}/pages/Index.vue (100%) rename resources/js/{ => vue}/pages/Show.vue (100%) diff --git a/resources/js/app.ts b/resources/js/app.ts index 3aa2b51..3043f15 100644 --- a/resources/js/app.ts +++ b/resources/js/app.ts @@ -1,11 +1 @@ -import { registerIcon } from '@/lib/navigation'; - -import '../css/style.css'; - -import IconBlog from '~icons/heroicons/newspaper'; - -export function setup() { - registerIcon('blog', IconBlog); -} - -export function afterMount() {} +export * from './vue/app'; diff --git a/resources/js/vue/app.ts b/resources/js/vue/app.ts new file mode 100644 index 0000000..a55c0ff --- /dev/null +++ b/resources/js/vue/app.ts @@ -0,0 +1,11 @@ +import { registerIcon } from '@/lib/navigation'; + +import '@modules/blog/resources/css/style.css'; + +import IconBlog from '~icons/heroicons/newspaper'; + +export function setup() { + registerIcon('blog', IconBlog); +} + +export function afterMount() {} diff --git a/resources/js/components/LatestPostsSection.vue b/resources/js/vue/components/LatestPostsSection.vue similarity index 100% rename from resources/js/components/LatestPostsSection.vue rename to resources/js/vue/components/LatestPostsSection.vue diff --git a/resources/js/components/PostCard.vue b/resources/js/vue/components/PostCard.vue similarity index 100% rename from resources/js/components/PostCard.vue rename to resources/js/vue/components/PostCard.vue diff --git a/resources/js/components/PostMeta.vue b/resources/js/vue/components/PostMeta.vue similarity index 100% rename from resources/js/components/PostMeta.vue rename to resources/js/vue/components/PostMeta.vue diff --git a/resources/js/pages/Index.vue b/resources/js/vue/pages/Index.vue similarity index 100% rename from resources/js/pages/Index.vue rename to resources/js/vue/pages/Index.vue diff --git a/resources/js/pages/Show.vue b/resources/js/vue/pages/Show.vue similarity index 100% rename from resources/js/pages/Show.vue rename to resources/js/vue/pages/Show.vue From 8f2de4567c6fc0b434345705bffabffa0e465f38 Mon Sep 17 00:00:00 2001 From: roble Date: Wed, 27 May 2026 19:34:44 +0100 Subject: [PATCH 4/6] feat(blog): improve code readability by formatting template and component structure --- .../js/vue/components/LatestPostsSection.vue | 4 ++- resources/js/vue/components/PostCard.vue | 2 +- resources/js/vue/components/PostMeta.vue | 10 ++++-- resources/js/vue/pages/Index.vue | 10 ++++-- resources/js/vue/pages/Show.vue | 35 +++++++++++++++---- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/resources/js/vue/components/LatestPostsSection.vue b/resources/js/vue/components/LatestPostsSection.vue index d158573..2140ca9 100644 --- a/resources/js/vue/components/LatestPostsSection.vue +++ b/resources/js/vue/components/LatestPostsSection.vue @@ -11,7 +11,9 @@ defineProps<{
-

+

{{ $t('From the blog') }}

diff --git a/resources/js/vue/components/PostCard.vue b/resources/js/vue/components/PostCard.vue index 82e441b..9a32542 100644 --- a/resources/js/vue/components/PostCard.vue +++ b/resources/js/vue/components/PostCard.vue @@ -33,7 +33,7 @@ defineProps<{

-
+
{{ author.name.charAt(0).toUpperCase() }}
- {{ author.name }} - + {{ + author.name + }} +
diff --git a/resources/js/vue/pages/Index.vue b/resources/js/vue/pages/Index.vue index af62fd6..72f27d6 100644 --- a/resources/js/vue/pages/Index.vue +++ b/resources/js/vue/pages/Index.vue @@ -44,7 +44,11 @@ defineProps<{ v-else class="grid grid-cols-1 gap-8 sm:grid-cols-1 lg:grid-cols-2" > - +
@@ -55,14 +59,14 @@ defineProps<{ {{ $t('← Previous') }} {{ $t('Next →') }} diff --git a/resources/js/vue/pages/Show.vue b/resources/js/vue/pages/Show.vue index 5298865..13b32c3 100644 --- a/resources/js/vue/pages/Show.vue +++ b/resources/js/vue/pages/Show.vue @@ -33,10 +33,22 @@ const jsonLd = computed(() => ({ type="article" > - - + + - +
@@ -57,7 +69,10 @@ const jsonLd = computed(() => ({
- +
@@ -82,15 +97,21 @@ const jsonLd = computed(() => ({
-
+

{{ $t('You might also like') }}

-
- +
+
From 5f9d6953dbe56afbc0f7c7e75c88f19f045cd0b3 Mon Sep 17 00:00:00 2001 From: roble Date: Tue, 2 Jun 2026 20:02:17 +0100 Subject: [PATCH 5/6] feat(blog)!: update blog content and improve descriptions for clarity and accuracy --- database/seeders/BlogDatabaseSeeder.php | 4 ++-- .../seeders/content/post-1-what-is-saucebase.html | 4 ++-- database/seeders/content/post-3-vilt-stack.html | 10 +++++----- .../seeders/content/post-4-your-first-module.html | 11 ++++------- .../seeders/content/post-5-auth-billing-privacy.html | 2 -- database/seeders/content/post-6-copy-and-own.html | 2 +- 6 files changed, 14 insertions(+), 19 deletions(-) diff --git a/database/seeders/BlogDatabaseSeeder.php b/database/seeders/BlogDatabaseSeeder.php index c35836f..ee579b2 100644 --- a/database/seeders/BlogDatabaseSeeder.php +++ b/database/seeders/BlogDatabaseSeeder.php @@ -41,8 +41,8 @@ public function run(): void ]); $this->createPost([ - 'title' => 'The VILT Stack: Laravel, Vue, Inertia, and Tailwind Done Right', - 'excerpt' => "Modern web dev has a tension between server productivity and rich interactivity. The VILT stack resolves it — and Saucebase wires all four pieces together so you don't have to.", + 'title' => 'The Modern Laravel Stack: Inertia.js, Vue 3 or React, and Tailwind Done Right', + 'excerpt' => "Modern web dev has a tension between server productivity and rich interactivity. Laravel, Inertia.js, and Tailwind resolve it — and Saucebase wires all the pieces together so you don't have to.", 'content' => $content('post-3-vilt-stack.html'), 'status' => PostStatus::Published, 'published_at' => now()->subMonths(3), diff --git a/database/seeders/content/post-1-what-is-saucebase.html b/database/seeders/content/post-1-what-is-saucebase.html index caa1a7e..2680d10 100644 --- a/database/seeders/content/post-1-what-is-saucebase.html +++ b/database/seeders/content/post-1-what-is-saucebase.html @@ -2,9 +2,9 @@

Saucebase exists to solve that problem.

-

Saucebase is a modular Laravel SaaS starter kit built on the VILT stack: Vue 3, Inertia.js, Laravel 13, and Tailwind CSS 4. It ships with the infrastructure every SaaS needs and gets it out of your way so you can focus on building what makes your product unique.

+

Saucebase is a modular Laravel SaaS starter kit built on Laravel 13, Inertia.js, and Tailwind CSS 4 — with your choice of Vue 3 or React. It ships with the infrastructure every SaaS needs and gets it out of your way so you can focus on building what makes your product unique.

-

The core of Saucebase is its module system, powered by nwidart/laravel-modules. Features are packaged as self-contained modules — Auth, Billing, Settings, Blog — each with their own routes, controllers, models, migrations, and Vue pages. Enable what you need, disable what you don't.

+

The core of Saucebase is its module system, powered by internachi/modular. Features are packaged as self-contained modules — Auth, Billing, Settings, Blog — each with their own routes, controllers, models, migrations, and frontend pages. Modules are active when installed via Composer and live directly in your repository.

But here's what sets Saucebase apart: modules are copy-and-own. When you install a module, you own it. It lives in your repository. You can edit it, extend it, break it, and fix it. There are no locked vendor packages to work around, no version conflicts to manage. Just clean, readable Laravel code that does exactly what you'd write yourself — except already written.

diff --git a/database/seeders/content/post-3-vilt-stack.html b/database/seeders/content/post-3-vilt-stack.html index 3c91f42..ce98521 100644 --- a/database/seeders/content/post-3-vilt-stack.html +++ b/database/seeders/content/post-3-vilt-stack.html @@ -1,4 +1,4 @@ -

Modern web development has a tension at its core: server-side frameworks are productive but struggle with rich interactivity, while JavaScript SPAs offer great UX but introduce enormous complexity. The VILT stack resolves that tension elegantly — and Saucebase is built on it from the ground up.

+

Modern web development has a tension at its core: server-side frameworks are productive but struggle with rich interactivity, while JavaScript SPAs offer great UX but introduce enormous complexity. The Laravel + Inertia.js stack resolves that tension elegantly — and Saucebase is built on it from the ground up.

Laravel — the foundation

@@ -6,13 +6,13 @@

Laravel — the foundation

Inertia.js — the bridge

-

Version 3 replaces traditional Blade views with Vue components while keeping your server-side routing and controllers intact. No REST API, no GraphQL, no data-fetching layer to maintain. Your controller returns a component name and props. Inertia handles the rest.

+

Version 3 replaces traditional Blade views with Vue or React components while keeping your server-side routing and controllers intact. No REST API, no GraphQL, no data-fetching layer to maintain. Your controller returns a component name and props. Inertia handles the rest.

Version 3 brings deferred props, optimistic updates with automatic rollback, instant visits, and simplified SSR via the Vite plugin — all of which Saucebase uses by default.

-

Vue 3 — the UI layer

+

Vue 3 or React — the UI layer

-

Composition API, <script setup>, and TypeScript throughout. Pages live in resources/js/pages/, components in resources/js/components/. Types are auto-generated from your PHP enums and DTOs via php artisan module:generate-types — your backend and frontend stay in sync without manual work.

+

Saucebase ships with full support for both frameworks. Pick Vue 3 (Composition API, <script setup>) or React 19 (hooks, JSX) — the choice is made during setup and everything is wired up for you. TypeScript is used throughout either way, and types are auto-generated from your PHP enums and DTOs via php artisan module:generate-types — your backend and frontend stay in sync without manual work.

Tailwind CSS 4 — the styling layer

@@ -21,7 +21,7 @@

Tailwind CSS 4 — the styling layer

The full picture

    -
  • Vite 6.4 — HMR, SSR, and module-aware asset discovery
  • +
  • Vite — HMR, SSR, and module-aware asset discovery
  • Filament 5 — admin panel
  • Ziggy — named Laravel routes available in JavaScript
  • Playwright — end-to-end tests, auto-discovered per module
  • diff --git a/database/seeders/content/post-4-your-first-module.html b/database/seeders/content/post-4-your-first-module.html index ea8d4cd..177332e 100644 --- a/database/seeders/content/post-4-your-first-module.html +++ b/database/seeders/content/post-4-your-first-module.html @@ -6,13 +6,10 @@

    Enabling your module

    -

    After scaffolding, three more commands and your module is live:

    +

    After scaffolding, run your migrations and rebuild assets:

    -
    composer dump-autoload
    -php artisan module:enable ModuleName
    -php artisan module:migrate ModuleName --seed
    - -

    Then rebuild assets with npm run build or restart npm run dev.

    +
    php artisan migrate
    +npm run build

    What you get for free

    @@ -20,7 +17,7 @@

    What you get for free

  • Service provider registered and booted automatically
  • Routes loaded and scoped to the module
  • Filament plugin ready for your admin resources
  • -
  • Vue pages discoverable by Inertia out of the box
  • +
  • Vue or React pages discoverable by Inertia out of the box
  • TypeScript types generated via php artisan module:generate-types
  • Playwright config auto-discovered by module-loader.js
diff --git a/database/seeders/content/post-5-auth-billing-privacy.html b/database/seeders/content/post-5-auth-billing-privacy.html index f4dc440..cb3747a 100644 --- a/database/seeders/content/post-5-auth-billing-privacy.html +++ b/database/seeders/content/post-5-auth-billing-privacy.html @@ -8,7 +8,6 @@

Auth module

  • Registration, login, and logout
  • Email verification and password reset
  • Social login via Laravel Socialite
  • -
  • Two-factor authentication
  • Role-based access with Spatie Laravel Permission (admin and user seeded by default)
  • @@ -33,7 +32,6 @@

    Settings module

  • Profile editing and avatar upload
  • Email change with re-verification
  • Password management
  • -
  • Notification preferences
  • Privacy and compliance

    diff --git a/database/seeders/content/post-6-copy-and-own.html b/database/seeders/content/post-6-copy-and-own.html index aad6e08..5cd48d2 100644 --- a/database/seeders/content/post-6-copy-and-own.html +++ b/database/seeders/content/post-6-copy-and-own.html @@ -10,7 +10,7 @@

    What copy-and-own means

    The practical benefit

    -

    When a bug appears in your authentication flow, you look at modules/Auth/app/Http/Controllers/. When you want to add a field to the billing portal, you edit modules/Billing/resources/js/pages/. There's no mental overhead of figuring out which hook to use or which config key controls the behaviour you need. You find the code and change it.

    +

    When a bug appears in your authentication flow, you look at modules/auth/app/Http/Controllers/. When you want to add a field to the billing portal, you edit modules/billing/resources/js/pages/. There's no mental overhead of figuring out which hook to use or which config key controls the behaviour you need. You find the code and change it.

    The tradeoff

    From 8179942cc8b784c0fb46fa6df339c1cb6a4a5be7 Mon Sep 17 00:00:00 2001 From: roble Date: Tue, 2 Jun 2026 20:04:56 +0100 Subject: [PATCH 6/6] fix(license): update copyright year to 2026 --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index f9a509b..cf78a69 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Saucebase +Copyright (c) 2026 Saucebase Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal