improvements

This commit is contained in:
2026-02-19 14:32:42 +01:00
parent 78c51d55b5
commit f1824ff752
16 changed files with 1244 additions and 31 deletions

View File

@@ -1,6 +1,7 @@
<script setup>
import { computed } from 'vue'
import { Head } from '@inertiajs/vue3'
import { ArrowDownTrayIcon } from '@heroicons/vue/24/outline'
import AppLayout from '@/Layouts/AppLayout.vue'
import AppButton from '@/Components/AppButton.vue'
@@ -98,8 +99,12 @@ const resultDisplay = computed(() => {
</dl>
</div>
<!-- Again button -->
<div class="flex justify-center">
<!-- Action buttons -->
<div class="flex justify-center gap-4">
<AppButton variant="ghost" :href="`/sessions/${session.id}/pdf`" external>
<ArrowDownTrayIcon class="h-5 w-5" />
Download PDF
</AppButton>
<AppButton size="lg" href="/" data-cy="start-new">
Again
</AppButton>

View File

@@ -4,7 +4,6 @@ import { Head, useForm, router } from '@inertiajs/vue3'
import AppLayout from '@/Layouts/AppLayout.vue'
import AppButton from '@/Components/AppButton.vue'
import QuestionCard from '@/Components/QuestionCard.vue'
import ScoreIndicator from '@/Components/ScoreIndicator.vue'
defineOptions({ layout: AppLayout })
@@ -21,10 +20,6 @@ const props = defineProps({
type: Object,
default: () => ({}),
},
score: {
type: Number,
default: 0,
},
})
// Answer management
@@ -61,7 +56,7 @@ const saveAnswer = (questionId) => {
}, {
preserveScroll: true,
preserveState: true,
only: ['answers', 'score'],
only: ['answers'],
})
}, 500)
}
@@ -168,10 +163,6 @@ const completeSession = async () => {
})
}
// Check if any scored answers have been given
const hasScoredAnswers = computed(() => {
return props.score > 0
})
</script>
<template>
@@ -180,10 +171,7 @@ const hasScoredAnswers = computed(() => {
<div class="max-w-3xl mx-auto px-4 py-10">
<!-- Title area -->
<div class="mb-10">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-white">{{ session.category.name }} Questionnaire</h1>
<ScoreIndicator :score="score" :visible="hasScoredAnswers" />
</div>
<h1 class="text-2xl font-bold text-white">{{ session.category.name }} Questionnaire</h1>
<div class="h-px bg-gradient-to-r from-primary/40 via-primary/10 to-transparent mt-4"></div>
</div>

View File

@@ -0,0 +1,620 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Go / No Go Assessment Report</title>
<style>
/* ─── Reset & Base ─────────────────────────────────────────── */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 10pt;
background-color: #2b303a;
color: #ffffff;
line-height: 1.5;
}
/* ─── Page Layout ───────────────────────────────────────────── */
@page {
size: A4;
margin: 0;
}
.page-wrapper {
width: 210mm;
min-height: 297mm;
background-color: #2b303a;
padding: 0;
}
/* ─── Header ────────────────────────────────────────────────── */
.header {
background-color: #1e222b;
padding: 32pt 36pt 28pt 36pt;
border-bottom: 3pt solid #d1ec51;
}
.header-eyebrow {
font-size: 7pt;
font-weight: bold;
letter-spacing: 3pt;
text-transform: uppercase;
color: #d1ec51;
margin-bottom: 6pt;
}
.header-title {
font-size: 26pt;
font-weight: bold;
color: #ffffff;
letter-spacing: -0.5pt;
line-height: 1.1;
}
.header-title-accent {
color: #d1ec51;
}
.header-subtitle {
font-size: 11pt;
color: #9ca3af;
margin-top: 6pt;
letter-spacing: 0.5pt;
}
.header-category {
color: #00b7b3;
font-weight: bold;
}
/* ─── Content Area ──────────────────────────────────────────── */
.content {
padding: 28pt 36pt 36pt 36pt;
}
/* ─── Meta Block (User Info + Date + Result) ────────────────── */
.meta-block {
margin-bottom: 24pt;
}
/* Two-column table for user info + result */
.meta-table {
width: 100%;
border-collapse: collapse;
}
.meta-table td {
vertical-align: top;
padding: 0;
}
.meta-left {
width: 58%;
padding-right: 12pt;
}
.meta-right {
width: 42%;
}
/* ─── User Info Card ────────────────────────────────────────── */
.card {
background-color: #1e222b;
border: 1pt solid #3a404d;
border-radius: 6pt;
padding: 16pt 18pt;
}
.card-label {
font-size: 6.5pt;
font-weight: bold;
letter-spacing: 2.5pt;
text-transform: uppercase;
color: #d1ec51;
margin-bottom: 12pt;
padding-bottom: 8pt;
border-bottom: 1pt solid #3a404d;
}
.info-row {
margin-bottom: 8pt;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-key {
font-size: 7pt;
color: #9ca3af;
letter-spacing: 1pt;
text-transform: uppercase;
margin-bottom: 1pt;
}
.info-value {
font-size: 9.5pt;
color: #ffffff;
font-weight: bold;
}
.info-value-secondary {
font-size: 9pt;
color: #d1d5db;
font-weight: normal;
}
/* ─── Date Block ────────────────────────────────────────────── */
.date-block {
background-color: #1e222b;
border: 1pt solid #3a404d;
border-radius: 6pt;
padding: 12pt 18pt;
margin-bottom: 12pt;
}
.date-label {
font-size: 6.5pt;
font-weight: bold;
letter-spacing: 2.5pt;
text-transform: uppercase;
color: #9ca3af;
margin-bottom: 3pt;
}
.date-value {
font-size: 11pt;
font-weight: bold;
color: #ffffff;
}
/* ─── Result Badge ──────────────────────────────────────────── */
.result-badge {
border-radius: 6pt;
padding: 16pt 18pt;
text-align: center;
}
.result-badge-go {
background-color: #14532d;
border: 2pt solid #22c55e;
}
.result-badge-consult {
background-color: #451a03;
border: 2pt solid #f59e0b;
}
.result-badge-no-go {
background-color: #450a0a;
border: 2pt solid #ef4444;
}
.result-label {
font-size: 6.5pt;
font-weight: bold;
letter-spacing: 2.5pt;
text-transform: uppercase;
margin-bottom: 6pt;
color: #9ca3af;
}
.result-text {
font-size: 18pt;
font-weight: bold;
letter-spacing: 1pt;
line-height: 1.1;
}
.result-text-go {
color: #4ade80;
}
.result-text-consult {
color: #fbbf24;
}
.result-text-no-go {
color: #f87171;
}
.result-score {
margin-top: 8pt;
font-size: 8.5pt;
color: #9ca3af;
}
.result-score-value {
font-weight: bold;
color: #d1ec51;
}
/* ─── Section Divider ───────────────────────────────────────── */
.section-divider {
margin: 20pt 0 16pt 0;
border: none;
border-top: 1pt solid #3a404d;
}
/* ─── Question Group ────────────────────────────────────────── */
.group-header {
margin-bottom: 12pt;
}
.group-number {
display: inline;
font-size: 7pt;
font-weight: bold;
letter-spacing: 2pt;
text-transform: uppercase;
color: #00b7b3;
}
.group-name {
font-size: 13pt;
font-weight: bold;
color: #ffffff;
margin-top: 3pt;
line-height: 1.2;
}
.group-description {
font-size: 8.5pt;
color: #9ca3af;
margin-top: 4pt;
font-style: italic;
}
/* ─── Question Item ─────────────────────────────────────────── */
.question-list {
margin-bottom: 8pt;
}
.question-item {
background-color: #1e222b;
border: 1pt solid #3a404d;
border-radius: 5pt;
margin-bottom: 6pt;
overflow: hidden;
}
.question-item-table {
width: 100%;
border-collapse: collapse;
}
.question-body {
padding: 10pt 14pt;
width: 72%;
vertical-align: top;
}
.question-answer-cell {
width: 28%;
padding: 10pt 14pt;
border-left: 1pt solid #3a404d;
text-align: center;
vertical-align: middle;
background-color: #242830;
}
.question-text {
font-size: 9pt;
color: #e5e7eb;
line-height: 1.45;
}
.question-text-value {
margin-top: 5pt;
font-size: 8pt;
color: #9ca3af;
font-style: italic;
padding: 5pt 8pt;
background-color: #2b303a;
border-left: 2pt solid #3a404d;
border-radius: 2pt;
}
/* Answer pill */
.answer-pill {
display: inline;
font-size: 8pt;
font-weight: bold;
letter-spacing: 1pt;
text-transform: uppercase;
padding: 6pt 9pt;
border-radius: 20pt;
}
.answer-yes {
background-color: #14532d;
color: #4ade80;
border: 1pt solid #22c55e;
}
.answer-no {
background-color: #450a0a;
color: #f87171;
border: 1pt solid #ef4444;
}
.answer-na {
background-color: #1e222b;
color: #9ca3af;
border: 1pt solid #4b5563;
}
.answer-missing {
font-size: 7.5pt;
color: #6b7280;
font-style: italic;
}
/* ─── Additional Comments ───────────────────────────────────── */
.comments-section {
margin-top: 8pt;
}
.comments-card {
background-color: #1e222b;
border: 1pt solid #3a404d;
border-left: 3pt solid #d1ec51;
border-radius: 5pt;
padding: 14pt 16pt;
}
.comments-label {
font-size: 6.5pt;
font-weight: bold;
letter-spacing: 2.5pt;
text-transform: uppercase;
color: #d1ec51;
margin-bottom: 8pt;
}
.comments-text {
font-size: 9pt;
color: #d1d5db;
line-height: 1.6;
}
/* ─── Footer ────────────────────────────────────────────────── */
.footer {
margin-top: 28pt;
padding-top: 12pt;
border-top: 1pt solid #3a404d;
text-align: center;
}
.footer-text {
font-size: 7pt;
color: #4b5563;
letter-spacing: 0.5pt;
}
.footer-accent {
color: #d1ec51;
}
/* ─── Page break control ────────────────────────────────────── */
.no-break {
page-break-inside: avoid;
}
.group-block {
page-break-inside: avoid;
}
</style>
</head>
<body>
<div class="page-wrapper">
{{-- ═══ HEADER ═══════════════════════════════════════════════════════ --}}
<div class="header">
<div class="header-eyebrow">Assessment Report</div>
<div class="header-title">
Go&nbsp;<span class="header-title-accent">/</span>&nbsp;No Go
</div>
<div class="header-subtitle">
Category:&nbsp;
<span class="header-category">{{ $session->category->name ?? 'Unknown Category' }}</span>
</div>
</div>
{{-- ═══ CONTENT ════════════════════════════════════════════════════════ --}}
<div class="content">
{{-- ─── Meta Block ─────────────────────────────────────────────── --}}
<div class="meta-block">
<table class="meta-table">
<tr>
{{-- Left: user info --}}
<td class="meta-left">
<div class="card">
<div class="card-label">Submitted By</div>
<div class="info-row">
<div class="info-key">Name</div>
<div class="info-value">{{ $session->user->name ?? '—' }}</div>
</div>
<div class="info-row">
<div class="info-key">Email</div>
<div class="info-value-secondary">{{ $session->user->email ?? '—' }}</div>
</div>
@if (!empty($session->user->company_name))
<div class="info-row">
<div class="info-key">Company</div>
<div class="info-value-secondary">{{ $session->user->company_name }}</div>
</div>
@endif
@if (!empty($session->user->job_title))
<div class="info-row">
<div class="info-key">Job Title</div>
<div class="info-value-secondary">{{ $session->user->job_title }}</div>
</div>
@endif
@if (!empty($session->user->department))
<div class="info-row">
<div class="info-key">Department</div>
<div class="info-value-secondary">{{ $session->user->department }}</div>
</div>
@endif
</div>
</td>
{{-- Right: date + result --}}
<td class="meta-right">
{{-- Completion date --}}
<div class="date-block">
<div class="date-label">Completed</div>
<div class="date-value">
@if ($session->completed_at)
{{ $session->completed_at->format('d M Y, H:i') }}
@else
@endif
</div>
</div>
{{-- Result badge --}}
@php
$result = $session->result ?? 'no_go';
$badgeClass = match ($result) {
'go' => 'result-badge-go',
'consult_leadership' => 'result-badge-consult',
default => 'result-badge-no-go',
};
$textClass = match ($result) {
'go' => 'result-text-go',
'consult_leadership' => 'result-text-consult',
default => 'result-text-no-go',
};
$resultLabel = match ($result) {
'go' => 'GO',
'consult_leadership' => 'Consult Leadership',
default => 'NO GO',
};
@endphp
<div class="result-badge {{ $badgeClass }}">
<div class="result-label">Decision</div>
<div class="result-text {{ $textClass }}">{{ $resultLabel }}</div>
@if (isset($session->score))
<div class="result-score">
Score:&nbsp;<span class="result-score-value">{{ $session->score }}&nbsp;pts</span>
</div>
@endif
</div>
</td>
</tr>
</table>
</div>
{{-- ─── Question Groups ─────────────────────────────────────── --}}
@php
// Key answers by question_id for fast lookup
$answersMap = $session->answers->keyBy('question_id');
@endphp
@foreach ($questionGroups as $groupIndex => $group)
<hr class="section-divider">
<div class="group-block no-break">
{{-- Group heading --}}
<div class="group-header">
<div class="group-number">Group {{ $groupIndex + 1 }}</div>
<div class="group-name">{{ $group->name }}</div>
@if (!empty($group->description))
<div class="group-description">{{ $group->description }}</div>
@endif
</div>
{{-- Questions --}}
<div class="question-list">
@forelse ($group->questions as $question)
@php
$answer = $answersMap->get($question->id);
$answerValue = $answer?->value;
$textValue = $answer?->text_value;
$pillClass = match ($answerValue) {
'yes' => 'answer-pill answer-yes',
'no' => 'answer-pill answer-no',
'na' => 'answer-pill answer-na',
default => '',
};
$pillLabel = match ($answerValue) {
'yes' => 'Yes',
'no' => 'No',
'na' => 'N/A',
default => null,
};
@endphp
<div class="question-item no-break">
<table class="question-item-table">
<tr>
<td class="question-body">
<div class="question-text">{{ $question->text }}</div>
@if (!empty($textValue))
<div class="question-text-value">{{ $textValue }}</div>
@endif
</td>
<td class="question-answer-cell">
@if ($pillLabel !== null)
<span class="{{ $pillClass }}">{{ $pillLabel }}</span>
@else
<span class="answer-missing">Not answered</span>
@endif
</td>
</tr>
</table>
</div>
@empty
<div style="color: #6b7280; font-size: 8.5pt; font-style: italic; padding: 8pt 0;">
No questions in this group.
</div>
@endforelse
</div>
</div>
@endforeach
{{-- ─── Additional Comments ─────────────────────────────────── --}}
@if (!empty($session->additional_comments))
<hr class="section-divider">
<div class="comments-section no-break">
<div class="comments-card">
<div class="comments-label">Additional Comments</div>
<div class="comments-text">{{ $session->additional_comments }}</div>
</div>
</div>
@endif
{{-- ─── Footer ──────────────────────────────────────────────── --}}
<div class="footer">
<div class="footer-text">
Generated by&nbsp;<span class="footer-accent">Go / No Go</span>
&nbsp;&bull;&nbsp;
{{ now()->format('d M Y') }}
</div>
</div>
</div>{{-- /content --}}
</div>{{-- /page-wrapper --}}
</body>
</html>