Scaffold full project: Vue 3/Inertia frontend, Docker infra, domain model

- Frontend: Vue 3 + Inertia.js + Pinia + Tailwind CSS with layout and dashboard page
- Infrastructure: Docker Compose with nginx, PHP-FPM, PostgreSQL+pgvector, Redis, Python AI service
- Database: 22 migrations covering all domain entities (projects, phases, commitments, decisions, documents, handover, audit)
- Models: 23 Eloquent models with relationships, casts, and 14 string-backed enums
- AI service: FastAPI scaffold with health, chat, summarize, and search endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-04-01 12:49:20 +02:00
parent 46a1279cd6
commit b71b274361
81 changed files with 4700 additions and 39 deletions

View File

@@ -0,0 +1,23 @@
<?php
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::table('users', function (Blueprint $table) {
$table->string('functie')->nullable()->after('name');
$table->string('afdeling')->nullable()->after('functie');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['functie', 'afdeling']);
});
}
};

View File

@@ -0,0 +1,31 @@
<?php
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('roles', function (Blueprint $table) {
$table->id();
$table->string('naam');
$table->string('beschrijving')->nullable();
$table->json('permissies')->nullable();
$table->timestamps();
});
Schema::create('role_user', function (Blueprint $table) {
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
$table->primary(['user_id', 'role_id']);
});
}
public function down(): void
{
Schema::dropIfExists('role_user');
Schema::dropIfExists('roles');
}
};

View File

@@ -0,0 +1,27 @@
<?php
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('themas', function (Blueprint $table) {
$table->id();
$table->string('naam');
$table->text('beschrijving');
$table->enum('prioriteit', ['laag', 'midden', 'hoog'])->default('midden');
$table->date('periode_start')->nullable();
$table->date('periode_eind')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
public function down(): void
{
Schema::dropIfExists('themas');
}
};

View File

@@ -0,0 +1,27 @@
<?php
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('speerpunten', function (Blueprint $table) {
$table->id();
$table->foreignId('thema_id')->constrained('themas')->restrictOnDelete();
$table->string('naam');
$table->text('beschrijving');
$table->foreignId('eigenaar_id')->constrained('users')->restrictOnDelete();
$table->enum('status', ['concept', 'actief', 'afgerond'])->default('concept');
$table->timestamps();
$table->softDeletes();
});
}
public function down(): void
{
Schema::dropIfExists('speerpunten');
}
};

View File

@@ -0,0 +1,52 @@
<?php
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('projects', function (Blueprint $table) {
$table->id();
$table->foreignId('speerpunt_id')->nullable()->constrained('speerpunten')->nullOnDelete();
$table->string('naam');
$table->text('beschrijving');
$table->foreignId('eigenaar_id')->constrained('users')->restrictOnDelete();
$table->enum('status', [
'signaal',
'verkenning',
'concept',
'experiment',
'pilot',
'besluitvorming',
'overdracht_bouwen',
'overdracht_beheer',
'evaluatie',
'geparkeerd',
'gestopt',
'afgerond',
])->default('signaal');
$table->enum('prioriteit', ['laag', 'midden', 'hoog'])->default('midden');
$table->date('startdatum')->nullable();
$table->date('streef_einddatum')->nullable();
$table->timestamps();
$table->softDeletes();
});
Schema::create('project_user', function (Blueprint $table) {
$table->foreignId('project_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->enum('rol', ['eigenaar', 'lid', 'reviewer', 'stakeholder'])->default('lid');
$table->timestamps();
$table->primary(['project_id', 'user_id']);
});
}
public function down(): void
{
Schema::dropIfExists('project_user');
Schema::dropIfExists('projects');
}
};

View File

@@ -0,0 +1,37 @@
<?php
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('fases', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
$table->enum('type', [
'signaal',
'verkenning',
'concept',
'experiment',
'pilot',
'besluitvorming',
'overdracht_bouwen',
'overdracht_beheer',
'evaluatie',
]);
$table->enum('status', ['open', 'actief', 'afgerond'])->default('open');
$table->date('startdatum')->nullable();
$table->date('einddatum')->nullable();
$table->text('opmerkingen')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('fases');
}
};

View File

@@ -0,0 +1,27 @@
<?php
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('risicos', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
$table->text('beschrijving');
$table->enum('impact', ['laag', 'midden', 'hoog'])->default('midden');
$table->enum('kans', ['laag', 'midden', 'hoog'])->default('midden');
$table->text('mitigatie')->nullable();
$table->foreignId('eigenaar_id')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('risicos');
}
};

View File

@@ -0,0 +1,26 @@
<?php
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('afhankelijkheden', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
$table->foreignId('afhankelijk_van_project_id')->constrained('projects')->restrictOnDelete();
$table->string('type');
$table->text('beschrijving')->nullable();
$table->enum('status', ['open', 'opgelost'])->default('open');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('afhankelijkheden');
}
};

View File

@@ -0,0 +1,28 @@
<?php
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('besluiten', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
$table->string('titel');
$table->text('beschrijving');
$table->date('datum');
$table->string('type');
$table->enum('status', ['concept', 'voorgelegd', 'goedgekeurd', 'afgewezen'])->default('concept');
$table->text('onderbouwing')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('besluiten');
}
};

View File

@@ -0,0 +1,28 @@
<?php
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('commitments', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id')->nullable()->constrained('projects')->nullOnDelete();
$table->foreignId('besluit_id')->nullable()->constrained('besluiten')->nullOnDelete();
$table->text('beschrijving');
$table->foreignId('eigenaar_id')->constrained('users')->restrictOnDelete();
$table->date('deadline');
$table->enum('status', ['open', 'in_uitvoering', 'afgerond', 'verlopen'])->default('open');
$table->string('bron')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('commitments');
}
};

View File

@@ -0,0 +1,27 @@
<?php
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('acties', function (Blueprint $table) {
$table->id();
$table->foreignId('commitment_id')->constrained('commitments')->cascadeOnDelete();
$table->text('beschrijving');
$table->foreignId('eigenaar_id')->constrained('users')->restrictOnDelete();
$table->date('deadline')->nullable();
$table->enum('status', ['open', 'in_uitvoering', 'afgerond'])->default('open');
$table->enum('prioriteit', ['laag', 'midden', 'hoog'])->default('midden');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('acties');
}
};

View File

@@ -0,0 +1,26 @@
<?php
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('budgets', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
$table->decimal('bedrag', 12, 2);
$table->string('type');
$table->string('periode')->nullable();
$table->enum('status', ['aangevraagd', 'toegekend', 'uitgeput'])->default('aangevraagd');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('budgets');
}
};

View File

@@ -0,0 +1,26 @@
<?php
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('bestedingen', function (Blueprint $table) {
$table->id();
$table->foreignId('budget_id')->constrained('budgets')->cascadeOnDelete();
$table->decimal('bedrag', 12, 2);
$table->text('beschrijving');
$table->date('datum');
$table->string('categorie')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('bestedingen');
}
};

View File

@@ -0,0 +1,30 @@
<?php
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('documents', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id')->nullable()->constrained('projects')->nullOnDelete();
$table->foreignId('fase_id')->nullable()->constrained('fases')->nullOnDelete();
$table->string('titel');
$table->string('type');
$table->text('inhoud')->nullable();
$table->string('bestandspad')->nullable();
$table->integer('versie')->default(1);
$table->foreignId('auteur_id')->constrained('users')->restrictOnDelete();
$table->timestamps();
$table->softDeletes();
});
}
public function down(): void
{
Schema::dropIfExists('documents');
}
};

View File

@@ -0,0 +1,30 @@
<?php
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('tags', function (Blueprint $table) {
$table->id();
$table->string('naam');
$table->string('categorie')->nullable();
$table->timestamps();
});
Schema::create('document_tag', function (Blueprint $table) {
$table->foreignId('document_id')->constrained()->cascadeOnDelete();
$table->foreignId('tag_id')->constrained()->cascadeOnDelete();
$table->primary(['document_id', 'tag_id']);
});
}
public function down(): void
{
Schema::dropIfExists('document_tag');
Schema::dropIfExists('tags');
}
};

View File

@@ -0,0 +1,32 @@
<?php
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('kennis_artikelen', function (Blueprint $table) {
$table->id();
$table->string('titel');
$table->text('inhoud');
$table->foreignId('auteur_id')->constrained('users')->restrictOnDelete();
$table->timestamps();
$table->softDeletes();
});
Schema::create('kennis_artikel_tag', function (Blueprint $table) {
$table->foreignId('kennis_artikel_id')->constrained('kennis_artikelen')->cascadeOnDelete();
$table->foreignId('tag_id')->constrained()->cascadeOnDelete();
$table->primary(['kennis_artikel_id', 'tag_id']);
});
}
public function down(): void
{
Schema::dropIfExists('kennis_artikel_tag');
Schema::dropIfExists('kennis_artikelen');
}
};

View File

@@ -0,0 +1,32 @@
<?php
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('lessons_learned', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id')->nullable()->constrained('projects')->nullOnDelete();
$table->foreignId('fase_id')->nullable()->constrained('fases')->nullOnDelete();
$table->string('titel');
$table->text('inhoud');
$table->timestamps();
});
Schema::create('lesson_learned_tag', function (Blueprint $table) {
$table->foreignId('lesson_learned_id')->constrained('lessons_learned')->cascadeOnDelete();
$table->foreignId('tag_id')->constrained()->cascadeOnDelete();
$table->primary(['lesson_learned_id', 'tag_id']);
});
}
public function down(): void
{
Schema::dropIfExists('lesson_learned_tag');
Schema::dropIfExists('lessons_learned');
}
};

View File

@@ -0,0 +1,26 @@
<?php
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('overdrachtsplannen', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
$table->enum('type', ['naar_bouwen', 'naar_beheer']);
$table->enum('status', ['concept', 'in_uitvoering', 'afgerond'])->default('concept');
$table->foreignId('eigenaar_rnd_id')->constrained('users')->restrictOnDelete();
$table->foreignId('eigenaar_ontvanger_id')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('overdrachtsplannen');
}
};

View File

@@ -0,0 +1,25 @@
<?php
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('criteria', function (Blueprint $table) {
$table->id();
$table->foreignId('overdrachtsplan_id')->constrained('overdrachtsplannen')->cascadeOnDelete();
$table->text('beschrijving');
$table->enum('status', ['open', 'voldaan', 'niet_voldaan'])->default('open');
$table->text('verificatie')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('criteria');
}
};

View File

@@ -0,0 +1,26 @@
<?php
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('acceptaties', function (Blueprint $table) {
$table->id();
$table->foreignId('overdrachtsplan_id')->constrained('overdrachtsplannen')->cascadeOnDelete();
$table->date('datum');
$table->foreignId('door_id')->constrained('users')->restrictOnDelete();
$table->text('opmerkingen')->nullable();
$table->enum('status', ['geaccepteerd', 'afgewezen', 'voorwaardelijk']);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('acceptaties');
}
};

View File

@@ -0,0 +1,27 @@
<?php
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('roadmap_items', function (Blueprint $table) {
$table->id();
$table->foreignId('thema_id')->constrained('themas')->cascadeOnDelete();
$table->string('titel');
$table->date('start');
$table->date('eind');
$table->string('type');
$table->string('status');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('roadmap_items');
}
};

View File

@@ -0,0 +1,26 @@
<?php
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('audit_logs', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete();
$table->string('action');
$table->string('entity_type');
$table->unsignedBigInteger('entity_id')->nullable();
$table->json('payload')->nullable();
$table->timestamp('created_at')->useCurrent();
});
}
public function down(): void
{
Schema::dropIfExists('audit_logs');
}
};