Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions app/Exceptions/AssessmentFrameworkMismatchException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace App\Exceptions;

use Exception;

class AssessmentFrameworkMismatchException extends Exception
{
public ?int $assessmentId;
public ?int $frameworkId;

public function __construct(
?int $assessmentId = null,
?int $frameworkId = null,
string $message = null
) {
$this->assessmentId = $assessmentId;
$this->frameworkId = $frameworkId;

parent::__construct($message);
}

public function report()
{
\Log::warning('Assessment does not belong to the specified framework', [
'assessment_id' => $this->assessmentId,
'framework_id' => $this->frameworkId,
'user_id' => auth()->id(),
'url' => request()->fullUrl(),
]);
}

public function render()
{
return response()->view('errors.403', [
'message' => $this->getMessage(),
], 403);
}
}
16 changes: 16 additions & 0 deletions app/Exceptions/AssessmentNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace App\Exceptions;

use Exception;

class AssessmentNotFoundException extends Exception
{
public function render()
{
return response()->view('errors.404', [
'message' => $this->getMessage(),
], 404);
}
}

33 changes: 33 additions & 0 deletions app/Exceptions/AssessmentNotSubmittedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Exceptions;

use Exception;

class AssessmentNotSubmittedException extends Exception
{
public ?int $assessmentId;

public function __construct(?int $assessmentId = null, string $message = null)
{
$this->assessmentId = $assessmentId;

parent::__construct($message);
}

public function report()
{
\Log::notice('Attempt to access an unsubmitted assessment', [
'assessment_id' => $this->assessmentId,
'user_id' => auth()->id(),
'url' => request()->fullUrl(),
]);
}

public function render()
{
return response()->view('errors.403', [
'message' => $this->getMessage(),
], 403);
}
}
16 changes: 16 additions & 0 deletions app/Exceptions/FrameworkNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace App\Exceptions;

use Exception;

class FrameworkNotFoundException extends Exception
{
public function render()
{
return response()->view('errors.404', [
'message' => $this->getMessage(),
], 404);
}
}

42 changes: 42 additions & 0 deletions app/Http/Controllers/AssessmentReportPdfController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Barryvdh\DomPDF\Facade\Pdf;
use App\Services\AssessmentReportService;

class AssessmentReportPdfController extends Controller
{
public function __invoke($frameworkId, $assessmentId, Request $request): \Illuminate\Http\Response
{
$service = new AssessmentReportService($frameworkId, $assessmentId);

$barImagesRaw = json_decode($request->barImages, true) ?? [];

$barImages = collect($barImagesRaw)
->pluck('image', 'id')
->toArray();

$signposts = [];

foreach ($service->nodes() as $node) {
$signpostsForNode = $service->signpostsForNode($node);
if ($signpostsForNode) {
$signposts[$node->id] = $signpostsForNode;
}
}

return Pdf::loadView('pdf.assessment-report', [
'framework' => $service->framework(),
'nodes' => $service->nodes(),
'responses' => $service->responses(),
'assessment' => $service->assessment(),
'rater' => $service->rater(),
'radarImage' => $request->radarImage,
'barImages' => $barImages,
'barCharts' => $service->barCharts(),
'signposts' => $signposts,
])->download('assessment-report.pdf');
}
}
8 changes: 7 additions & 1 deletion app/Livewire/AssessmentCompleted.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ public function mount()

}


public function viewReport()
{
return redirect()->route('assessment-report', [
'frameworkId' => $this->assessment?->framework?->id,
'assessmentId' => $this->assessmentId
]);
}
public function render()
{
return view('livewire.assessment-completed');
Expand Down
145 changes: 145 additions & 0 deletions app/Livewire/AssessmentReport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php

namespace App\Livewire;

use App\Exceptions\AssessmentFrameworkMismatchException;
use App\Exceptions\AssessmentNotFoundException;
use App\Exceptions\AssessmentNotSubmittedException;
use App\Exceptions\FrameworkNotFoundException;
use App\Models\Assessment;
use App\Models\Framework;
use App\Models\Node;
use App\Models\Rater;
use App\Services\AssessmentReportService;
use App\Traits\AssessmentHelperTrait;
use App\Traits\UserTrait;
use Illuminate\Support\Collection;
use Livewire\Attributes\Computed;
use Livewire\Component;

class AssessmentReport extends Component
{
use AssessmentHelperTrait;
use UserTrait;

public ?int $frameworkId = null;
public ?int $assessmentId = null;

public array $barCharts = [];
public array $radarOptions = [];
public array $radarData = [];
/**
* @var array|mixed
*/
public array $signposts;

/**
* @throws FrameworkNotFoundException
* @throws AssessmentNotFoundException
* @throws AssessmentFrameworkMismatchException
* @throws AssessmentNotSubmittedException
*/
public function mount(int $frameworkId, int $assessmentId): void
{
$this->frameworkId = $frameworkId;
$this->assessmentId = $assessmentId;

// Validate framework
if (!$this->framework()) {
throw new FrameworkNotFoundException(__('alerts.errors.framework-not-found'));
}

// Validate assessment
if (!$this->assessment()) {
throw new AssessmentNotFoundException(__('alerts.errors.assessment-not-found'));
}

if ($this->assessment()->framework_id !== $this->framework()->id) {
throw new AssessmentFrameworkMismatchException(
assessmentId: $this->assessmentId,
frameworkId: $this->frameworkId,
message: __('alerts.errors.assessment-not-belong-to-framework')
);
}

if (is_null($this->assessment()->submitted_at)) {
throw new AssessmentNotSubmittedException(
assessmentId: $this->assessmentId,
message: __('alerts.errors.assessment-not-submitted')
);
}


// Use the shared service for all report data
$service = new AssessmentReportService($frameworkId, $assessmentId);

$this->barCharts = $service->barCharts();

$radar = $service->radarChart();
$this->radarData = $radar['data'];
$this->radarOptions = $radar['options'];

$this->signposts = [];

foreach ($service->nodes() as $node) {
$signposts = $service->signpostsForNode($node);
if ($signposts) {
$this->signposts[$node->id] = $signposts;
}
}
}

#[Computed]
public function framework(): ?Framework
{
if (empty($this->frameworkId)) {
return null;
}

return Framework::find($this->frameworkId);
}

#[Computed]
public function nodes(): ?Collection
{
return Node::where('framework_id', $this->frameworkId)
->orderBy('order')
->orderBy('id')
->get();
}

#[Computed]
public function assessment(): ?Assessment
{
if (empty($this->assessmentId)) {
return null;
}

return Assessment::find($this->assessmentId);
}

#[Computed]
public function responses(): ?Collection
{
return $this->assessment()?->responses()->get();
}

#[Computed]
public function rater(): ?Rater
{
if (empty($this->assessmentId) || empty($this->user()?->user_id)) {
return null;
}

return Rater::where('user_id', $this->user()?->user_id)
->whereHas('assessments', function ($q) {
$q->where('assessments.id', $this->assessmentId);
})
->first();
}

public function render()
{
return view('livewire.assessment-report');
}
}
8 changes: 8 additions & 0 deletions app/Livewire/Summary.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ public function rater()
->first();
}

public function viewReport()
{
return redirect()->route('assessment-report', [
'frameworkId' => $this->frameworkId,
'assessmentId' => $this->assessmentId
]);
}

public function render()
{
return view('livewire.summary', [
Expand Down
Loading
Loading