diff --git a/app/Http/Controllers/CommitmentController.php b/app/Http/Controllers/CommitmentController.php
new file mode 100644
index 0000000..bd3bf36
--- /dev/null
+++ b/app/Http/Controllers/CommitmentController.php
@@ -0,0 +1,61 @@
+validate([
+ 'beschrijving' => 'required|string',
+ 'eigenaar_id' => 'required|exists:users,id',
+ 'deadline' => 'required|date',
+ 'project_id' => 'required|exists:projects,id',
+ 'prioriteit' => 'nullable|string',
+ 'bron' => 'nullable|string',
+ 'besluit_id' => 'nullable|exists:besluiten,id',
+ ]);
+
+ $this->commitmentService->create($validated);
+
+ return back()->with('success', 'Commitment aangemaakt.');
+ }
+
+ public function update(Request $request, Commitment $commitment)
+ {
+ $validated = $request->validate([
+ 'beschrijving' => 'sometimes|required|string',
+ 'eigenaar_id' => 'sometimes|required|exists:users,id',
+ 'deadline' => 'sometimes|required|date',
+ 'prioriteit' => 'nullable|string',
+ 'bron' => 'nullable|string',
+ 'status' => 'nullable|string',
+ ]);
+
+ $this->commitmentService->update($commitment, $validated);
+
+ return back()->with('success', 'Commitment bijgewerkt.');
+ }
+
+ public function complete(Commitment $commitment)
+ {
+ $this->commitmentService->markComplete($commitment);
+
+ return back()->with('success', 'Commitment afgerond.');
+ }
+
+ public function destroy(Commitment $commitment)
+ {
+ $commitment->delete();
+
+ return back()->with('success', 'Commitment verwijderd.');
+ }
+}
diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php
new file mode 100644
index 0000000..5a17116
--- /dev/null
+++ b/app/Http/Controllers/DocumentController.php
@@ -0,0 +1,51 @@
+validate([
+ 'titel' => 'required|string|max:255',
+ 'bestand' => 'required|file|max:20480',
+ 'project_id' => 'required|exists:projects,id',
+ 'fase_id' => 'nullable|exists:fases,id',
+ 'type' => 'nullable|string|max:50',
+ ]);
+
+ $this->documentService->upload($validated, $request->file('bestand'));
+
+ return back()->with('success', 'Document geupload.');
+ }
+
+ public function download(Document $document)
+ {
+ abort_unless(
+ Storage::disk('local')->exists($document->bestandspad),
+ 404,
+ 'Bestand niet gevonden.'
+ );
+
+ return Storage::disk('local')->download(
+ $document->bestandspad,
+ $document->titel
+ );
+ }
+
+ public function destroy(Document $document)
+ {
+ $this->documentService->delete($document);
+
+ return back()->with('success', 'Document verwijderd.');
+ }
+}
diff --git a/app/Http/Controllers/MapController.php b/app/Http/Controllers/MapController.php
index cdd484f..93f1f6d 100644
--- a/app/Http/Controllers/MapController.php
+++ b/app/Http/Controllers/MapController.php
@@ -17,7 +17,9 @@ class MapController extends Controller
$mapData = $this->mapDataService->getStrategyMap();
return Inertia::render('Map/MetroMap', [
- 'mapData' => $mapData,
+ 'mapData' => $mapData,
+ 'users' => \App\Models\User::select('id', 'name')->get(),
+ 'speerpunten' => \App\Models\Speerpunt::select('id', 'naam', 'thema_id')->with('thema:id,naam')->get(),
]);
}
@@ -26,7 +28,9 @@ class MapController extends Controller
$mapData = $this->mapDataService->getProjectMap($projectId);
return Inertia::render('Map/MetroMap', [
- 'mapData' => $mapData,
+ 'mapData' => $mapData,
+ 'users' => \App\Models\User::select('id', 'name')->get(),
+ 'speerpunten' => \App\Models\Speerpunt::select('id', 'naam', 'thema_id')->with('thema:id,naam')->get(),
]);
}
diff --git a/app/Services/CommitmentService.php b/app/Services/CommitmentService.php
new file mode 100644
index 0000000..39630c2
--- /dev/null
+++ b/app/Services/CommitmentService.php
@@ -0,0 +1,103 @@
+where('project_id', $projectId)
+ ->orderBy('deadline')
+ ->get();
+ }
+
+ /**
+ * Create a new commitment with audit log.
+ */
+ public function create(array $data): Commitment
+ {
+ return DB::transaction(function () use ($data) {
+ $commitment = Commitment::create([
+ 'project_id' => $data['project_id'],
+ 'beschrijving' => $data['beschrijving'],
+ 'eigenaar_id' => $data['eigenaar_id'],
+ 'deadline' => $data['deadline'],
+ 'status' => CommitmentStatus::Open,
+ 'bron' => $data['bron'] ?? null,
+ 'besluit_id' => $data['besluit_id'] ?? null,
+ ]);
+
+ $this->audit('created', $commitment);
+
+ return $commitment;
+ });
+ }
+
+ /**
+ * Update a commitment with audit log.
+ */
+ public function update(Commitment $commitment, array $data): Commitment
+ {
+ return DB::transaction(function () use ($commitment, $data) {
+ $commitment->update(array_filter([
+ 'beschrijving' => $data['beschrijving'] ?? null,
+ 'eigenaar_id' => $data['eigenaar_id'] ?? null,
+ 'deadline' => $data['deadline'] ?? null,
+ 'status' => $data['status'] ?? null,
+ 'bron' => $data['bron'] ?? null,
+ ], fn ($v) => $v !== null));
+
+ $this->audit('updated', $commitment);
+
+ return $commitment->fresh();
+ });
+ }
+
+ /**
+ * Mark a commitment as afgerond with audit log.
+ */
+ public function markComplete(Commitment $commitment): Commitment
+ {
+ return DB::transaction(function () use ($commitment) {
+ $commitment->update(['status' => CommitmentStatus::Afgerond]);
+
+ $this->audit('completed', $commitment);
+
+ return $commitment->fresh();
+ });
+ }
+
+ /**
+ * Get all commitments past their deadline that are not afgerond.
+ */
+ public function getOverdue(): Collection
+ {
+ return Commitment::with(['eigenaar', 'project'])
+ ->where('deadline', '<', now()->toDateString())
+ ->where('status', '!=', CommitmentStatus::Afgerond)
+ ->orderBy('deadline')
+ ->get();
+ }
+
+ private function audit(string $action, Commitment $commitment, ?array $extra = null): void
+ {
+ AuditLog::create([
+ 'user_id' => Auth::id(),
+ 'action' => "commitment.{$action}",
+ 'entity_type' => 'commitment',
+ 'entity_id' => $commitment->id,
+ 'payload' => $extra,
+ ]);
+ }
+}
diff --git a/app/Services/DocumentService.php b/app/Services/DocumentService.php
new file mode 100644
index 0000000..aaf6e69
--- /dev/null
+++ b/app/Services/DocumentService.php
@@ -0,0 +1,77 @@
+where('project_id', $projectId)
+ ->latest()
+ ->get();
+ }
+
+ /**
+ * Store a file and create the Document record.
+ */
+ public function upload(array $data, UploadedFile $file): Document
+ {
+ return DB::transaction(function () use ($data, $file) {
+ $projectId = $data['project_id'];
+ $path = $file->store("documents/{$projectId}", 'local');
+
+ $document = Document::create([
+ 'project_id' => $projectId,
+ 'fase_id' => $data['fase_id'] ?? null,
+ 'titel' => $data['titel'],
+ 'type' => $data['type'] ?? $file->getClientOriginalExtension(),
+ 'bestandspad' => $path,
+ 'versie' => 1,
+ 'auteur_id' => Auth::id(),
+ ]);
+
+ $this->audit('uploaded', $document);
+
+ return $document;
+ });
+ }
+
+ /**
+ * Delete the file from storage and soft-delete the Document record.
+ */
+ public function delete(Document $document): void
+ {
+ DB::transaction(function () use ($document) {
+ if ($document->bestandspad && Storage::disk('local')->exists($document->bestandspad)) {
+ Storage::disk('local')->delete($document->bestandspad);
+ }
+
+ $this->audit('deleted', $document);
+
+ $document->delete();
+ });
+ }
+
+ private function audit(string $action, Document $document, ?array $extra = null): void
+ {
+ AuditLog::create([
+ 'user_id' => Auth::id(),
+ 'action' => "document.{$action}",
+ 'entity_type' => 'document',
+ 'entity_id' => $document->id,
+ 'payload' => $extra,
+ ]);
+ }
+}
diff --git a/resources/js/Components/Forms/CommitmentForm.vue b/resources/js/Components/Forms/CommitmentForm.vue
new file mode 100644
index 0000000..26d6de6
--- /dev/null
+++ b/resources/js/Components/Forms/CommitmentForm.vue
@@ -0,0 +1,186 @@
+
+
+
+ {{ error }}{{ title }}
+
+