Sprint 2: Live data, CRUD modals, commitments, document upload
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>
This commit is contained in:
103
app/Services/CommitmentService.php
Normal file
103
app/Services/CommitmentService.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\CommitmentStatus;
|
||||
use App\Models\AuditLog;
|
||||
use App\Models\Commitment;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CommitmentService
|
||||
{
|
||||
/**
|
||||
* Get all commitments for a project with owner and acties.
|
||||
*/
|
||||
public function getForProject(int $projectId): Collection
|
||||
{
|
||||
return Commitment::with(['eigenaar', 'acties'])
|
||||
->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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
77
app/Services/DocumentService.php
Normal file
77
app/Services/DocumentService.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user