Initial commit: Pireon Admin — headless admin panel and public API

Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 17:55:27 +02:00
commit 3897113313
534 changed files with 64050 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
/*
* Translatable columns hold a JSON map of locale => value, e.g.
* {"es": "Servicios", "en": "Services", "ca": "Serveis"}.
* spatie/laravel-translatable casts these transparently.
*
* `jsonb` rather than `json`: Postgres cannot compare a `json`
* value for equality, so any `select distinct` touching one fails
* outright — and Filament's relationship selects emit exactly
* that. On MySQL it is still `json` and on SQLite still `text`,
* so nothing else changes.
*/
$table->jsonb('name');
$table->jsonb('slug');
$table->jsonb('description')->nullable();
$table->string('color', 7)->nullable();
$table->unsignedInteger('position')->default(0);
$table->boolean('is_visible')->default(true);
$table->timestamps();
$table->softDeletes();
$table->index('position');
});
Schema::create('pages', function (Blueprint $table) {
$table->id();
$table->foreignId('category_id')->nullable()
->constrained()->nullOnDelete();
$table->foreignId('author_id')->nullable()
->constrained('users')->nullOnDelete();
$table->jsonb('title');
// Slugs live inside JSON, so uniqueness cannot be a DB constraint.
// It is enforced per locale by App\Rules\UniqueTranslatedSlug.
$table->jsonb('slug');
$table->jsonb('excerpt')->nullable();
$table->jsonb('body')->nullable();
$table->jsonb('meta_title')->nullable();
$table->jsonb('meta_description')->nullable();
$table->string('status', 20)->default('draft');
$table->string('featured_image')->nullable();
$table->boolean('is_featured')->default(false);
$table->timestamp('published_at')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['status', 'published_at']);
});
}
public function down(): void
{
Schema::dropIfExists('pages');
Schema::dropIfExists('categories');
}
};