Frontend: - Connect MetroMap to live Inertia props (replace hardcoded demo data) - Drill-down navigation via router.visit for project-level maps - Reactive breadcrumb based on map level - Empty state when no projects exist - Reusable Modal component with retro styling - ProjectForm and CommitmentForm with Inertia useForm - FormInput reusable component (text, date, textarea, select) - FloatingActions FAB button for creating projects/themes Backend: - CommitmentService + CommitmentController (CRUD, mark complete, overdue) - DocumentService + DocumentController (upload, download, delete) - MapController now passes users and speerpunten to frontend - 7 new routes (4 commitment, 3 document) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
78 lines
2.2 KiB
PHP
78 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\AuditLog;
|
|
use App\Models\Document;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class DocumentService
|
|
{
|
|
/**
|
|
* Get all documents for a project with auteur.
|
|
*/
|
|
public function getForProject(int $projectId): Collection
|
|
{
|
|
return Document::with('auteur')
|
|
->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,
|
|
]);
|
|
}
|
|
}
|