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:
16
.env.example
16
.env.example
@@ -20,12 +20,12 @@ LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_DATABASE=laravel
|
||||
# DB_USERNAME=root
|
||||
# DB_PASSWORD=
|
||||
DB_CONNECTION=pgsql
|
||||
DB_HOST=postgresql
|
||||
DB_PORT=5432
|
||||
DB_DATABASE=innovatieplatform
|
||||
DB_USERNAME=innovatie
|
||||
DB_PASSWORD=secret
|
||||
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
@@ -43,7 +43,7 @@ CACHE_STORE=database
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_HOST=redis
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
@@ -63,3 +63,5 @@ AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
|
||||
AI_SERVICE_URL=http://ai-service:8000
|
||||
|
||||
21
ai-service/Dockerfile
Normal file
21
ai-service/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies needed for psycopg2
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libpq-dev \
|
||||
gcc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy and install Python dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
0
ai-service/app/__init__.py
Normal file
0
ai-service/app/__init__.py
Normal file
117
ai-service/app/main.py
Normal file
117
ai-service/app/main.py
Normal file
@@ -0,0 +1,117 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
import os
|
||||
|
||||
app = FastAPI(
|
||||
title="Innovatieplatform AI Service",
|
||||
description="AI service providing chat, summarization and semantic search for the Innovatieplatform.",
|
||||
version="0.1.0",
|
||||
)
|
||||
|
||||
# CORS — allow requests from the Laravel app and local development
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=[
|
||||
"http://laravel-app",
|
||||
"http://nginx",
|
||||
"http://localhost",
|
||||
"http://localhost:80",
|
||||
os.getenv("LARAVEL_APP_URL", "http://localhost"),
|
||||
],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# --- Request/Response models ---
|
||||
|
||||
class ChatRequest(BaseModel):
|
||||
message: str
|
||||
project_id: Optional[int] = None
|
||||
conversation_history: Optional[list] = []
|
||||
|
||||
|
||||
class ChatResponse(BaseModel):
|
||||
reply: str
|
||||
project_id: Optional[int] = None
|
||||
|
||||
|
||||
class SummarizeRequest(BaseModel):
|
||||
content: str
|
||||
project_id: Optional[int] = None
|
||||
summary_type: Optional[str] = "general"
|
||||
|
||||
|
||||
class SummarizeResponse(BaseModel):
|
||||
summary: str
|
||||
project_id: Optional[int] = None
|
||||
|
||||
|
||||
class SearchRequest(BaseModel):
|
||||
query: str
|
||||
project_id: Optional[int] = None
|
||||
limit: Optional[int] = 10
|
||||
|
||||
|
||||
class SearchResult(BaseModel):
|
||||
id: int
|
||||
content: str
|
||||
score: float
|
||||
metadata: Optional[dict] = {}
|
||||
|
||||
|
||||
class SearchResponse(BaseModel):
|
||||
results: list[SearchResult]
|
||||
query: str
|
||||
|
||||
|
||||
# --- Endpoints ---
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
"""Health check endpoint used by Docker and monitoring."""
|
||||
return {"status": "ok", "service": "ai-service"}
|
||||
|
||||
|
||||
@app.post("/api/chat", response_model=ChatResponse)
|
||||
async def chat(request: ChatRequest):
|
||||
"""
|
||||
Handle a chat message, optionally scoped to a project.
|
||||
Placeholder — wire up LangGraph + Anthropic in the full implementation.
|
||||
"""
|
||||
# TODO: integrate LangGraph agent with Anthropic Claude
|
||||
reply = (
|
||||
f"[AI placeholder] Received: '{request.message}'. "
|
||||
"Full AI integration pending."
|
||||
)
|
||||
return ChatResponse(reply=reply, project_id=request.project_id)
|
||||
|
||||
|
||||
@app.post("/api/summarize", response_model=SummarizeResponse)
|
||||
async def summarize(request: SummarizeRequest):
|
||||
"""
|
||||
Summarize content for a given project.
|
||||
Placeholder — wire up Anthropic in the full implementation.
|
||||
"""
|
||||
# TODO: integrate Anthropic Claude summarization
|
||||
summary = (
|
||||
f"[AI placeholder] Summary of {len(request.content)} characters "
|
||||
f"(type: {request.summary_type}). Full AI integration pending."
|
||||
)
|
||||
return SummarizeResponse(summary=summary, project_id=request.project_id)
|
||||
|
||||
|
||||
@app.post("/api/search", response_model=SearchResponse)
|
||||
async def search(request: SearchRequest):
|
||||
"""
|
||||
Semantic search using pgvector embeddings.
|
||||
Placeholder — wire up pgvector + embeddings in the full implementation.
|
||||
"""
|
||||
# TODO: integrate pgvector similarity search with embeddings
|
||||
return SearchResponse(
|
||||
results=[],
|
||||
query=request.query,
|
||||
)
|
||||
11
ai-service/requirements.txt
Normal file
11
ai-service/requirements.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
fastapi>=0.110.0
|
||||
uvicorn>=0.27.0
|
||||
langgraph>=0.0.40
|
||||
langchain>=0.1.0
|
||||
anthropic>=0.30.0
|
||||
pgvector>=0.2.0
|
||||
psycopg2-binary>=2.9.0
|
||||
numpy>=1.26.0
|
||||
pydantic>=2.0.0
|
||||
python-dotenv>=1.0.0
|
||||
httpx>=0.27.0
|
||||
10
app/Enums/AcceptatieStatus.php
Normal file
10
app/Enums/AcceptatieStatus.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum AcceptatieStatus: string
|
||||
{
|
||||
case Geaccepteerd = 'geaccepteerd';
|
||||
case Afgewezen = 'afgewezen';
|
||||
case Voorwaardelijk = 'voorwaardelijk';
|
||||
}
|
||||
10
app/Enums/ActieStatus.php
Normal file
10
app/Enums/ActieStatus.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum ActieStatus: string
|
||||
{
|
||||
case Open = 'open';
|
||||
case InUitvoering = 'in_uitvoering';
|
||||
case Afgerond = 'afgerond';
|
||||
}
|
||||
11
app/Enums/BesluitStatus.php
Normal file
11
app/Enums/BesluitStatus.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum BesluitStatus: string
|
||||
{
|
||||
case Concept = 'concept';
|
||||
case Voorgelegd = 'voorgelegd';
|
||||
case Goedgekeurd = 'goedgekeurd';
|
||||
case Afgewezen = 'afgewezen';
|
||||
}
|
||||
10
app/Enums/BudgetStatus.php
Normal file
10
app/Enums/BudgetStatus.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum BudgetStatus: string
|
||||
{
|
||||
case Aangevraagd = 'aangevraagd';
|
||||
case Toegekend = 'toegekend';
|
||||
case Uitgeput = 'uitgeput';
|
||||
}
|
||||
11
app/Enums/CommitmentStatus.php
Normal file
11
app/Enums/CommitmentStatus.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum CommitmentStatus: string
|
||||
{
|
||||
case Open = 'open';
|
||||
case InUitvoering = 'in_uitvoering';
|
||||
case Afgerond = 'afgerond';
|
||||
case Verlopen = 'verlopen';
|
||||
}
|
||||
10
app/Enums/CriteriumStatus.php
Normal file
10
app/Enums/CriteriumStatus.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum CriteriumStatus: string
|
||||
{
|
||||
case Open = 'open';
|
||||
case Voldaan = 'voldaan';
|
||||
case NietVoldaan = 'niet_voldaan';
|
||||
}
|
||||
10
app/Enums/FaseStatus.php
Normal file
10
app/Enums/FaseStatus.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum FaseStatus: string
|
||||
{
|
||||
case Open = 'open';
|
||||
case Actief = 'actief';
|
||||
case Afgerond = 'afgerond';
|
||||
}
|
||||
16
app/Enums/FaseType.php
Normal file
16
app/Enums/FaseType.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum FaseType: string
|
||||
{
|
||||
case Signaal = 'signaal';
|
||||
case Verkenning = 'verkenning';
|
||||
case Concept = 'concept';
|
||||
case Experiment = 'experiment';
|
||||
case Pilot = 'pilot';
|
||||
case Besluitvorming = 'besluitvorming';
|
||||
case OverdrachtBouwen = 'overdracht_bouwen';
|
||||
case OverdrachtBeheer = 'overdracht_beheer';
|
||||
case Evaluatie = 'evaluatie';
|
||||
}
|
||||
10
app/Enums/OverdrachtsStatus.php
Normal file
10
app/Enums/OverdrachtsStatus.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum OverdrachtsStatus: string
|
||||
{
|
||||
case Concept = 'concept';
|
||||
case InUitvoering = 'in_uitvoering';
|
||||
case Afgerond = 'afgerond';
|
||||
}
|
||||
9
app/Enums/OverdrachtsType.php
Normal file
9
app/Enums/OverdrachtsType.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum OverdrachtsType: string
|
||||
{
|
||||
case NaarBouwen = 'naar_bouwen';
|
||||
case NaarBeheer = 'naar_beheer';
|
||||
}
|
||||
10
app/Enums/Prioriteit.php
Normal file
10
app/Enums/Prioriteit.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum Prioriteit: string
|
||||
{
|
||||
case Laag = 'laag';
|
||||
case Midden = 'midden';
|
||||
case Hoog = 'hoog';
|
||||
}
|
||||
11
app/Enums/ProjectRol.php
Normal file
11
app/Enums/ProjectRol.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum ProjectRol: string
|
||||
{
|
||||
case Eigenaar = 'eigenaar';
|
||||
case Lid = 'lid';
|
||||
case Reviewer = 'reviewer';
|
||||
case Stakeholder = 'stakeholder';
|
||||
}
|
||||
19
app/Enums/ProjectStatus.php
Normal file
19
app/Enums/ProjectStatus.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum ProjectStatus: string
|
||||
{
|
||||
case Signaal = 'signaal';
|
||||
case Verkenning = 'verkenning';
|
||||
case Concept = 'concept';
|
||||
case Experiment = 'experiment';
|
||||
case Pilot = 'pilot';
|
||||
case Besluitvorming = 'besluitvorming';
|
||||
case OverdrachtBouwen = 'overdracht_bouwen';
|
||||
case OverdrachtBeheer = 'overdracht_beheer';
|
||||
case Evaluatie = 'evaluatie';
|
||||
case Geparkeerd = 'geparkeerd';
|
||||
case Gestopt = 'gestopt';
|
||||
case Afgerond = 'afgerond';
|
||||
}
|
||||
10
app/Enums/SpeerpuntStatus.php
Normal file
10
app/Enums/SpeerpuntStatus.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum SpeerpuntStatus: string
|
||||
{
|
||||
case Concept = 'concept';
|
||||
case Actief = 'actief';
|
||||
case Afgerond = 'afgerond';
|
||||
}
|
||||
43
app/Http/Middleware/HandleInertiaRequests.php
Normal file
43
app/Http/Middleware/HandleInertiaRequests.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Middleware;
|
||||
|
||||
class HandleInertiaRequests extends Middleware
|
||||
{
|
||||
/**
|
||||
* The root template that's loaded on the first page visit.
|
||||
*
|
||||
* @see https://inertiajs.com/server-side-setup#root-template
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rootView = 'app';
|
||||
|
||||
/**
|
||||
* Determines the current asset version.
|
||||
*
|
||||
* @see https://inertiajs.com/asset-versioning
|
||||
*/
|
||||
public function version(Request $request): ?string
|
||||
{
|
||||
return parent::version($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the props that are shared by default.
|
||||
*
|
||||
* @see https://inertiajs.com/shared-data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function share(Request $request): array
|
||||
{
|
||||
return [
|
||||
...parent::share($request),
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
38
app/Models/Acceptatie.php
Normal file
38
app/Models/Acceptatie.php
Normal 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
39
app/Models/Actie.php
Normal 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');
|
||||
}
|
||||
}
|
||||
27
app/Models/Afhankelijkheid.php
Normal file
27
app/Models/Afhankelijkheid.php
Normal 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
33
app/Models/AuditLog.php
Normal 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
41
app/Models/Besluit.php
Normal 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
32
app/Models/Besteding.php
Normal 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
37
app/Models/Budget.php
Normal 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
49
app/Models/Commitment.php
Normal 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
31
app/Models/Criterium.php
Normal 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
51
app/Models/Document.php
Normal 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
46
app/Models/Fase.php
Normal 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);
|
||||
}
|
||||
}
|
||||
31
app/Models/KennisArtikel.php
Normal file
31
app/Models/KennisArtikel.php
Normal 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');
|
||||
}
|
||||
}
|
||||
34
app/Models/LessonLearned.php
Normal file
34
app/Models/LessonLearned.php
Normal 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');
|
||||
}
|
||||
}
|
||||
55
app/Models/Overdrachtsplan.php
Normal file
55
app/Models/Overdrachtsplan.php
Normal 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
106
app/Models/Project.php
Normal 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);
|
||||
}
|
||||
}
|
||||
26
app/Models/ProjectUser.php
Normal file
26
app/Models/ProjectUser.php
Normal 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
37
app/Models/Risico.php
Normal 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');
|
||||
}
|
||||
}
|
||||
31
app/Models/RoadmapItem.php
Normal file
31
app/Models/RoadmapItem.php
Normal 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
27
app/Models/Role.php
Normal 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
44
app/Models/Speerpunt.php
Normal 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
29
app/Models/Tag.php
Normal 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
40
app/Models/Thema.php
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
//
|
||||
$middleware->web(append: [
|
||||
\App\Http\Middleware\HandleInertiaRequests::class,
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.3",
|
||||
"inertiajs/inertia-laravel": "^3.0",
|
||||
"laravel/framework": "^13.0",
|
||||
"laravel/tinker": "^3.0"
|
||||
},
|
||||
|
||||
75
composer.lock
generated
75
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "c57754c93ae34ac3b9b716a0fd2f2149",
|
||||
"content-hash": "1c6dc1e0948ef3b8fd1905a824a40c58",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
@@ -1053,6 +1053,79 @@
|
||||
],
|
||||
"time": "2025-08-22T14:27:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "inertiajs/inertia-laravel",
|
||||
"version": "v3.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/inertiajs/inertia-laravel.git",
|
||||
"reference": "4675331c428c0f77b2539684835c5e0fd27ee023"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/4675331c428c0f77b2539684835c5e0fd27ee023",
|
||||
"reference": "4675331c428c0f77b2539684835c5e0fd27ee023",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"laravel/framework": "^11.0|^12.0|^13.0",
|
||||
"php": "^8.2.0",
|
||||
"symfony/console": "^7.0|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"laravel/boost": "<2.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"larastan/larastan": "^3.0",
|
||||
"laravel/pint": "^1.16",
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench": "^9.2|^10.0|^11.0",
|
||||
"phpunit/phpunit": "^11.5|^12.0",
|
||||
"roave/security-advisories": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pcntl": "Recommended when running the Inertia SSR server via the `inertia:start-ssr` artisan command."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Inertia\\ServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"./helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Inertia\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jonathan Reinink",
|
||||
"email": "jonathan@reinink.ca",
|
||||
"homepage": "https://reinink.ca"
|
||||
}
|
||||
],
|
||||
"description": "The Laravel adapter for Inertia.js.",
|
||||
"keywords": [
|
||||
"inertia",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
|
||||
"source": "https://github.com/inertiajs/inertia-laravel/tree/v3.0.1"
|
||||
},
|
||||
"time": "2026-03-25T21:07:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v13.2.0",
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
37
database/migrations/2026_04_01_000006_create_fases_table.php
Normal file
37
database/migrations/2026_04_01_000006_create_fases_table.php
Normal 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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
30
database/migrations/2026_04_01_000015_create_tags_tables.php
Normal file
30
database/migrations/2026_04_01_000015_create_tags_tables.php
Normal 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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
152
docker-compose.yml
Normal file
152
docker-compose.yml
Normal file
@@ -0,0 +1,152 @@
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
|
||||
depends_on:
|
||||
- laravel-app
|
||||
networks:
|
||||
- innovatieplatform
|
||||
|
||||
laravel-app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/php/Dockerfile
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
environment:
|
||||
- APP_ENV=${APP_ENV:-local}
|
||||
- APP_KEY=${APP_KEY}
|
||||
- DB_CONNECTION=pgsql
|
||||
- DB_HOST=postgresql
|
||||
- DB_PORT=5432
|
||||
- DB_DATABASE=${DB_DATABASE:-innovatieplatform}
|
||||
- DB_USERNAME=${DB_USERNAME:-innovatie}
|
||||
- DB_PASSWORD=${DB_PASSWORD:-secret}
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- AI_SERVICE_URL=http://ai-service:8000
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- innovatieplatform
|
||||
|
||||
laravel-worker:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/php/Dockerfile
|
||||
command: php artisan queue:work --sleep=3 --tries=3 --max-time=3600
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
environment:
|
||||
- APP_ENV=${APP_ENV:-local}
|
||||
- APP_KEY=${APP_KEY}
|
||||
- DB_CONNECTION=pgsql
|
||||
- DB_HOST=postgresql
|
||||
- DB_PORT=5432
|
||||
- DB_DATABASE=${DB_DATABASE:-innovatieplatform}
|
||||
- DB_USERNAME=${DB_USERNAME:-innovatie}
|
||||
- DB_PASSWORD=${DB_PASSWORD:-secret}
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- AI_SERVICE_URL=http://ai-service:8000
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- innovatieplatform
|
||||
|
||||
laravel-scheduler:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/php/Dockerfile
|
||||
entrypoint: ["/usr/local/bin/scheduler-entrypoint.sh"]
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
- ./docker/scheduler/entrypoint.sh:/usr/local/bin/scheduler-entrypoint.sh
|
||||
environment:
|
||||
- APP_ENV=${APP_ENV:-local}
|
||||
- APP_KEY=${APP_KEY}
|
||||
- DB_CONNECTION=pgsql
|
||||
- DB_HOST=postgresql
|
||||
- DB_PORT=5432
|
||||
- DB_DATABASE=${DB_DATABASE:-innovatieplatform}
|
||||
- DB_USERNAME=${DB_USERNAME:-innovatie}
|
||||
- DB_PASSWORD=${DB_PASSWORD:-secret}
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- innovatieplatform
|
||||
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg16
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_DB=${DB_DATABASE:-innovatieplatform}
|
||||
- POSTGRES_USER=${DB_USERNAME:-innovatie}
|
||||
- POSTGRES_PASSWORD=${DB_PASSWORD:-secret}
|
||||
volumes:
|
||||
- postgresql_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME:-innovatie} -d ${DB_DATABASE:-innovatieplatform}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- innovatieplatform
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- innovatieplatform
|
||||
|
||||
ai-service:
|
||||
build:
|
||||
context: ./ai-service
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./ai-service:/app
|
||||
environment:
|
||||
- DB_HOST=postgresql
|
||||
- DB_PORT=5432
|
||||
- DB_DATABASE=${DB_DATABASE:-innovatieplatform}
|
||||
- DB_USERNAME=${DB_USERNAME:-innovatie}
|
||||
- DB_PASSWORD=${DB_PASSWORD:-secret}
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- innovatieplatform
|
||||
|
||||
volumes:
|
||||
postgresql_data:
|
||||
redis_data:
|
||||
|
||||
networks:
|
||||
innovatieplatform:
|
||||
driver: bridge
|
||||
46
docker/nginx/default.conf
Normal file
46
docker/nginx/default.conf
Normal file
@@ -0,0 +1,46 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /var/www/html/public;
|
||||
index index.php index.html;
|
||||
|
||||
charset utf-8;
|
||||
|
||||
# Handle static files directly
|
||||
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|map)$ {
|
||||
expires max;
|
||||
log_not_found off;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Laravel routing — try files, then directories, then pass to index.php
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location = /favicon.ico { log_not_found off; access_log off; }
|
||||
location = /robots.txt { log_not_found off; access_log off; }
|
||||
|
||||
# PHP-FPM — pass .php requests to laravel-app
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass laravel-app:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_read_timeout 300;
|
||||
fastcgi_buffers 16 16k;
|
||||
fastcgi_buffer_size 32k;
|
||||
}
|
||||
|
||||
# Deny access to hidden files
|
||||
location ~ /\.(?!well-known).* {
|
||||
deny all;
|
||||
}
|
||||
|
||||
error_log /var/log/nginx/error.log;
|
||||
access_log /var/log/nginx/access.log;
|
||||
}
|
||||
53
docker/php/Dockerfile
Normal file
53
docker/php/Dockerfile
Normal file
@@ -0,0 +1,53 @@
|
||||
FROM php:8.4-fpm
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
curl \
|
||||
libpng-dev \
|
||||
libjpeg-dev \
|
||||
libfreetype6-dev \
|
||||
libzip-dev \
|
||||
libicu-dev \
|
||||
libpq-dev \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Configure and install PHP extensions
|
||||
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||
&& docker-php-ext-install -j$(nproc) \
|
||||
pdo_pgsql \
|
||||
pgsql \
|
||||
pcntl \
|
||||
bcmath \
|
||||
gd \
|
||||
zip \
|
||||
intl
|
||||
|
||||
# Install Redis extension via PECL
|
||||
RUN pecl install redis \
|
||||
&& docker-php-ext-enable redis
|
||||
|
||||
# Install Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# Copy application files
|
||||
COPY . .
|
||||
|
||||
# Install PHP dependencies (production-ready; skip dev deps when APP_ENV=production)
|
||||
RUN composer install --no-interaction --prefer-dist --optimize-autoloader
|
||||
|
||||
# Set ownership to www-data
|
||||
RUN chown -R www-data:www-data /var/www/html \
|
||||
&& chmod -R 755 /var/www/html/storage \
|
||||
&& chmod -R 755 /var/www/html/bootstrap/cache
|
||||
|
||||
# Switch to non-root user
|
||||
USER www-data
|
||||
|
||||
EXPOSE 9000
|
||||
|
||||
CMD ["php-fpm"]
|
||||
8
docker/scheduler/entrypoint.sh
Normal file
8
docker/scheduler/entrypoint.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Starting Laravel Scheduler..."
|
||||
|
||||
while true; do
|
||||
php /var/www/html/artisan schedule:run --verbose --no-interaction
|
||||
sleep 60
|
||||
done
|
||||
2322
package-lock.json
generated
Normal file
2322
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -7,11 +7,18 @@
|
||||
"dev": "vite"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"@tailwindcss/vite": "^4.2.2",
|
||||
"axios": ">=1.11.0 <=1.14.0",
|
||||
"concurrently": "^9.0.1",
|
||||
"laravel-vite-plugin": "^3.0.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"vite": "^8.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@inertiajs/vue3": "^3.0.1",
|
||||
"@vitejs/plugin-vue": "^6.0.5",
|
||||
"@vueuse/core": "^14.2.1",
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "^3.5.31"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
||||
@source '../../storage/framework/views/*.php';
|
||||
@source '../**/*.blade.php';
|
||||
@source '../**/*.js';
|
||||
|
||||
@theme {
|
||||
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
@import "tailwindcss";
|
||||
|
||||
25
resources/js/Layouts/AppLayout.vue
Normal file
25
resources/js/Layouts/AppLayout.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script setup>
|
||||
import { Link } from '@inertiajs/vue3'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<nav class="bg-white border-b border-gray-200">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex items-center">
|
||||
<span class="text-xl font-semibold text-gray-800">Innovatieplatform</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<Link href="/dashboard" class="text-gray-600 hover:text-gray-900">Dashboard</Link>
|
||||
<Link href="/projects" class="text-gray-600 hover:text-gray-900">Projecten</Link>
|
||||
<Link href="/roadmap" class="text-gray-600 hover:text-gray-900">Roadmap</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
||||
<slot />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
10
resources/js/Pages/Dashboard.vue
Normal file
10
resources/js/Pages/Dashboard.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<script setup>
|
||||
import AppLayout from '@/Layouts/AppLayout.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppLayout>
|
||||
<h1 class="text-2xl font-bold text-gray-900">Dashboard</h1>
|
||||
<p class="mt-2 text-gray-600">Welkom bij het Innovatieplatform van het R&D Lab.</p>
|
||||
</AppLayout>
|
||||
</template>
|
||||
@@ -1 +1,17 @@
|
||||
import './bootstrap';
|
||||
import { createApp, h } from 'vue'
|
||||
import { createInertiaApp } from '@inertiajs/vue3'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
createInertiaApp({
|
||||
resolve: name => {
|
||||
const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
|
||||
return pages[`./Pages/${name}.vue`]
|
||||
},
|
||||
setup({ el, App, props, plugin }) {
|
||||
const pinia = createPinia()
|
||||
createApp({ render: () => h(App, props) })
|
||||
.use(plugin)
|
||||
.use(pinia)
|
||||
.mount(el)
|
||||
},
|
||||
})
|
||||
|
||||
13
resources/views/app.blade.php
Normal file
13
resources/views/app.blade.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="nl">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Innovatieplatform</title>
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
@inertiaHead
|
||||
</head>
|
||||
<body>
|
||||
@inertia
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Inertia\Inertia;
|
||||
|
||||
Route::get('/', function () {
|
||||
return view('welcome');
|
||||
return Inertia::render('Dashboard');
|
||||
});
|
||||
|
||||
Route::get('/dashboard', function () {
|
||||
return Inertia::render('Dashboard');
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import laravel from 'laravel-vite-plugin';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { defineConfig } from 'vite'
|
||||
import laravel from 'laravel-vite-plugin'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { resolve } from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
@@ -8,11 +10,17 @@ export default defineConfig({
|
||||
input: ['resources/css/app.css', 'resources/js/app.js'],
|
||||
refresh: true,
|
||||
}),
|
||||
vue(),
|
||||
tailwindcss(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'resources/js'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
watch: {
|
||||
ignored: ['**/storage/framework/views/**'],
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user