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

38
app/Models/Acceptatie.php Normal file
View File

@@ -0,0 +1,38 @@
<?php
namespace App\Models;
use App\Enums\AcceptatieStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Acceptatie extends Model
{
protected $table = 'acceptaties';
protected $fillable = [
'overdrachtsplan_id',
'datum',
'door_id',
'opmerkingen',
'status',
];
protected function casts(): array
{
return [
'status' => AcceptatieStatus::class,
'datum' => 'date',
];
}
public function overdrachtsplan(): BelongsTo
{
return $this->belongsTo(Overdrachtsplan::class);
}
public function door(): BelongsTo
{
return $this->belongsTo(User::class, 'door_id');
}
}

39
app/Models/Actie.php Normal file
View File

@@ -0,0 +1,39 @@
<?php
namespace App\Models;
use App\Enums\ActieStatus;
use App\Enums\Prioriteit;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Actie extends Model
{
protected $fillable = [
'commitment_id',
'beschrijving',
'eigenaar_id',
'deadline',
'status',
'prioriteit',
];
protected function casts(): array
{
return [
'status' => ActieStatus::class,
'prioriteit' => Prioriteit::class,
'deadline' => 'date',
];
}
public function commitment(): BelongsTo
{
return $this->belongsTo(Commitment::class);
}
public function eigenaar(): BelongsTo
{
return $this->belongsTo(User::class, 'eigenaar_id');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Afhankelijkheid extends Model
{
protected $fillable = [
'project_id',
'afhankelijk_van_project_id',
'type',
'beschrijving',
'status',
];
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function afhankelijkVan(): BelongsTo
{
return $this->belongsTo(Project::class, 'afhankelijk_van_project_id');
}
}

33
app/Models/AuditLog.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AuditLog extends Model
{
// Append-only: no updated_at
const UPDATED_AT = null;
protected $fillable = [
'user_id',
'action',
'entity_type',
'entity_id',
'payload',
];
protected function casts(): array
{
return [
'payload' => 'array',
'created_at' => 'datetime',
];
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

41
app/Models/Besluit.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
namespace App\Models;
use App\Enums\BesluitStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Besluit extends Model
{
protected $table = 'besluiten';
protected $fillable = [
'project_id',
'titel',
'beschrijving',
'datum',
'type',
'status',
'onderbouwing',
];
protected function casts(): array
{
return [
'status' => BesluitStatus::class,
'datum' => 'date',
];
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function commitments(): HasMany
{
return $this->hasMany(Commitment::class);
}
}

32
app/Models/Besteding.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Besteding extends Model
{
protected $table = 'bestedingen';
protected $fillable = [
'budget_id',
'bedrag',
'beschrijving',
'datum',
'categorie',
];
protected function casts(): array
{
return [
'bedrag' => 'decimal:2',
'datum' => 'date',
];
}
public function budget(): BelongsTo
{
return $this->belongsTo(Budget::class);
}
}

37
app/Models/Budget.php Normal file
View File

@@ -0,0 +1,37 @@
<?php
namespace App\Models;
use App\Enums\BudgetStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Budget extends Model
{
protected $fillable = [
'project_id',
'bedrag',
'type',
'periode',
'status',
];
protected function casts(): array
{
return [
'status' => BudgetStatus::class,
'bedrag' => 'decimal:2',
];
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function bestedingen(): HasMany
{
return $this->hasMany(Besteding::class);
}
}

49
app/Models/Commitment.php Normal file
View File

@@ -0,0 +1,49 @@
<?php
namespace App\Models;
use App\Enums\CommitmentStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Commitment extends Model
{
protected $fillable = [
'project_id',
'besluit_id',
'beschrijving',
'eigenaar_id',
'deadline',
'status',
'bron',
];
protected function casts(): array
{
return [
'status' => CommitmentStatus::class,
'deadline' => 'date',
];
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function besluit(): BelongsTo
{
return $this->belongsTo(Besluit::class);
}
public function eigenaar(): BelongsTo
{
return $this->belongsTo(User::class, 'eigenaar_id');
}
public function acties(): HasMany
{
return $this->hasMany(Actie::class);
}
}

31
app/Models/Criterium.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use App\Enums\CriteriumStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Criterium extends Model
{
protected $table = 'criteria';
protected $fillable = [
'overdrachtsplan_id',
'beschrijving',
'status',
'verificatie',
];
protected function casts(): array
{
return [
'status' => CriteriumStatus::class,
];
}
public function overdrachtsplan(): BelongsTo
{
return $this->belongsTo(Overdrachtsplan::class);
}
}

51
app/Models/Document.php Normal file
View File

@@ -0,0 +1,51 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Document extends Model
{
use SoftDeletes;
protected $fillable = [
'project_id',
'fase_id',
'titel',
'type',
'inhoud',
'bestandspad',
'versie',
'auteur_id',
];
protected function casts(): array
{
return [
'versie' => 'integer',
];
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function fase(): BelongsTo
{
return $this->belongsTo(Fase::class);
}
public function auteur(): BelongsTo
{
return $this->belongsTo(User::class, 'auteur_id');
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class);
}
}

46
app/Models/Fase.php Normal file
View File

@@ -0,0 +1,46 @@
<?php
namespace App\Models;
use App\Enums\FaseStatus;
use App\Enums\FaseType;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Fase extends Model
{
protected $fillable = [
'project_id',
'type',
'status',
'startdatum',
'einddatum',
'opmerkingen',
];
protected function casts(): array
{
return [
'type' => FaseType::class,
'status' => FaseStatus::class,
'startdatum' => 'date',
'einddatum' => 'date',
];
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function documents(): HasMany
{
return $this->hasMany(Document::class);
}
public function lessonsLearned(): HasMany
{
return $this->hasMany(LessonLearned::class);
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class KennisArtikel extends Model
{
use SoftDeletes;
protected $table = 'kennis_artikelen';
protected $fillable = [
'titel',
'inhoud',
'auteur_id',
];
public function auteur(): BelongsTo
{
return $this->belongsTo(User::class, 'auteur_id');
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class, 'kennis_artikel_tag');
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class LessonLearned extends Model
{
protected $table = 'lessons_learned';
protected $fillable = [
'project_id',
'fase_id',
'titel',
'inhoud',
];
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function fase(): BelongsTo
{
return $this->belongsTo(Fase::class);
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class, 'lesson_learned_tag');
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Models;
use App\Enums\OverdrachtsStatus;
use App\Enums\OverdrachtsType;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Overdrachtsplan extends Model
{
protected $table = 'overdrachtsplannen';
protected $fillable = [
'project_id',
'type',
'status',
'eigenaar_rnd_id',
'eigenaar_ontvanger_id',
];
protected function casts(): array
{
return [
'type' => OverdrachtsType::class,
'status' => OverdrachtsStatus::class,
];
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function eigenaarRnd(): BelongsTo
{
return $this->belongsTo(User::class, 'eigenaar_rnd_id');
}
public function eigenaarOntvanger(): BelongsTo
{
return $this->belongsTo(User::class, 'eigenaar_ontvanger_id');
}
public function criteria(): HasMany
{
return $this->hasMany(Criterium::class);
}
public function acceptaties(): HasMany
{
return $this->hasMany(Acceptatie::class);
}
}

106
app/Models/Project.php Normal file
View File

@@ -0,0 +1,106 @@
<?php
namespace App\Models;
use App\Enums\Prioriteit;
use App\Enums\ProjectRol;
use App\Enums\ProjectStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Project extends Model
{
use SoftDeletes;
protected $fillable = [
'speerpunt_id',
'naam',
'beschrijving',
'eigenaar_id',
'status',
'prioriteit',
'startdatum',
'streef_einddatum',
];
protected function casts(): array
{
return [
'status' => ProjectStatus::class,
'prioriteit' => Prioriteit::class,
'startdatum' => 'date',
'streef_einddatum' => 'date',
];
}
public function speerpunt(): BelongsTo
{
return $this->belongsTo(Speerpunt::class);
}
public function eigenaar(): BelongsTo
{
return $this->belongsTo(User::class, 'eigenaar_id');
}
public function teamleden(): BelongsToMany
{
return $this->belongsToMany(User::class)
->withPivot('rol')
->withTimestamps()
->using(ProjectUser::class);
}
public function fases(): HasMany
{
return $this->hasMany(Fase::class);
}
public function risicos(): HasMany
{
return $this->hasMany(Risico::class);
}
public function afhankelijkheden(): HasMany
{
return $this->hasMany(Afhankelijkheid::class);
}
public function afhankelijkVanMij(): HasMany
{
return $this->hasMany(Afhankelijkheid::class, 'afhankelijk_van_project_id');
}
public function besluiten(): HasMany
{
return $this->hasMany(Besluit::class);
}
public function commitments(): HasMany
{
return $this->hasMany(Commitment::class);
}
public function budgets(): HasMany
{
return $this->hasMany(Budget::class);
}
public function documents(): HasMany
{
return $this->hasMany(Document::class);
}
public function lessonsLearned(): HasMany
{
return $this->hasMany(LessonLearned::class);
}
public function overdrachtsplannen(): HasMany
{
return $this->hasMany(Overdrachtsplan::class);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Models;
use App\Enums\ProjectRol;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ProjectUser extends Pivot
{
protected $table = 'project_user';
public $incrementing = false;
protected $fillable = [
'project_id',
'user_id',
'rol',
];
protected function casts(): array
{
return [
'rol' => ProjectRol::class,
];
}
}

37
app/Models/Risico.php Normal file
View File

@@ -0,0 +1,37 @@
<?php
namespace App\Models;
use App\Enums\Prioriteit;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Risico extends Model
{
protected $fillable = [
'project_id',
'beschrijving',
'impact',
'kans',
'mitigatie',
'eigenaar_id',
];
protected function casts(): array
{
return [
'impact' => Prioriteit::class,
'kans' => Prioriteit::class,
];
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function eigenaar(): BelongsTo
{
return $this->belongsTo(User::class, 'eigenaar_id');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class RoadmapItem extends Model
{
protected $fillable = [
'thema_id',
'titel',
'start',
'eind',
'type',
'status',
];
protected function casts(): array
{
return [
'start' => 'date',
'eind' => 'date',
];
}
public function thema(): BelongsTo
{
return $this->belongsTo(Thema::class);
}
}

27
app/Models/Role.php Normal file
View File

@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Role extends Model
{
protected $fillable = [
'naam',
'beschrijving',
'permissies',
];
protected function casts(): array
{
return [
'permissies' => 'array',
];
}
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}

44
app/Models/Speerpunt.php Normal file
View File

@@ -0,0 +1,44 @@
<?php
namespace App\Models;
use App\Enums\SpeerpuntStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Speerpunt extends Model
{
use SoftDeletes;
protected $fillable = [
'thema_id',
'naam',
'beschrijving',
'eigenaar_id',
'status',
];
protected function casts(): array
{
return [
'status' => SpeerpuntStatus::class,
];
}
public function thema(): BelongsTo
{
return $this->belongsTo(Thema::class);
}
public function eigenaar(): BelongsTo
{
return $this->belongsTo(User::class, 'eigenaar_id');
}
public function projects(): HasMany
{
return $this->hasMany(Project::class);
}
}

29
app/Models/Tag.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Tag extends Model
{
protected $fillable = [
'naam',
'categorie',
];
public function documents(): BelongsToMany
{
return $this->belongsToMany(Document::class);
}
public function kennisArtikelen(): BelongsToMany
{
return $this->belongsToMany(KennisArtikel::class, 'kennis_artikel_tag');
}
public function lessonsLearned(): BelongsToMany
{
return $this->belongsToMany(LessonLearned::class, 'lesson_learned_tag');
}
}

40
app/Models/Thema.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
namespace App\Models;
use App\Enums\Prioriteit;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Thema extends Model
{
use SoftDeletes;
protected $fillable = [
'naam',
'beschrijving',
'prioriteit',
'periode_start',
'periode_eind',
];
protected function casts(): array
{
return [
'prioriteit' => Prioriteit::class,
'periode_start' => 'date',
'periode_eind' => 'date',
];
}
public function speerpunten(): HasMany
{
return $this->hasMany(Speerpunt::class);
}
public function roadmapItems(): HasMany
{
return $this->hasMany(RoadmapItem::class);
}
}

View File

@@ -2,31 +2,87 @@
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Enums\ProjectRol;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable
{
/** @use HasFactory<UserFactory> */
use HasFactory, Notifiable;
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected $fillable = [
'name',
'email',
'password',
'functie',
'afdeling',
];
protected $hidden = [
'password',
'remember_token',
];
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'password' => 'hashed',
];
}
// Relationships
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class);
}
public function projects(): BelongsToMany
{
return $this->belongsToMany(Project::class)
->withPivot('rol')
->withTimestamps()
->using(ProjectUser::class);
}
public function eigenProjecten(): HasMany
{
return $this->hasMany(Project::class, 'eigenaar_id');
}
public function eigenSpeerpunten(): HasMany
{
return $this->hasMany(Speerpunt::class, 'eigenaar_id');
}
public function commitments(): HasMany
{
return $this->hasMany(Commitment::class, 'eigenaar_id');
}
public function acties(): HasMany
{
return $this->hasMany(Actie::class, 'eigenaar_id');
}
public function documents(): HasMany
{
return $this->hasMany(Document::class, 'auteur_id');
}
public function kennisArtikelen(): HasMany
{
return $this->hasMany(KennisArtikel::class, 'auteur_id');
}
public function auditLogs(): HasMany
{
return $this->hasMany(AuditLog::class);
}
}