commit c993da3397b65488d919d1b929cbd4638754601b Author: ncoronel <ncoronel@quo.ar> Date: Tue Jul 28 09:54:10 2026 -0300 feat(tickets): update ticket metadata layout for improved readability and clarity commit 140fa4676d84bed788894e1d7994b26dccac7a34 Author: ncoronel <ncoronel@quo.ar> Date: Tue Jul 28 09:43:21 2026 -0300 feat(tickets): enhance PDF ticket download with dynamic filename and improved styling commit 8875c047b198776e61e8353c99cf4c7b00bc8fc9 Author: ncoronel <ncoronel@quo.ar> Date: Tue Jul 28 09:03:17 2026 -0300 feat(tickets): add PDF download functionality for tickets - Implemented a new endpoint to download tickets as a PDF. - Created DownloadTicketsPdfRequest for validating ticket IDs. - Added TicketPdfService to handle PDF generation and QR code embedding. - Developed a Blade view for rendering ticket details in PDF format. - Updated API routes to include the new PDF download route. - Added tests to ensure authenticated users can download their tickets and that users cannot download tickets belonging to others. - Updated composer.json and composer.lock to include necessary dependencies for PDF generation and QR code creation.
214 lines
7.1 KiB
PHP
214 lines
7.1 KiB
PHP
<!doctype html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
|
<style>
|
|
@page { margin: 0; }
|
|
* { box-sizing: border-box; }
|
|
body {
|
|
background: #f4f7f5;
|
|
color: #17211b;
|
|
font-family: DejaVu Sans, sans-serif;
|
|
font-size: 11px;
|
|
margin: 0;
|
|
}
|
|
.ticket {
|
|
background: #f4f7f5;
|
|
min-height: 1120px;
|
|
padding: 0 54px;
|
|
position: relative;
|
|
}
|
|
.ticket + .ticket { page-break-before: always; }
|
|
.accent { background: {{ $primaryColor }}; height: 8px; margin: 0 -54px; }
|
|
.header {
|
|
background: {{ $headerBackgroundColor }};
|
|
color: {{ $headerTextColor }};
|
|
height: 132px;
|
|
margin: 0 -54px;
|
|
padding: 32px 54px 0;
|
|
}
|
|
.header-table, .meta-table, .footer-table { border-collapse: collapse; width: 100%; }
|
|
.brand-cell { vertical-align: middle; }
|
|
.type-cell { text-align: right; vertical-align: middle; width: 190px; }
|
|
.logo {
|
|
height: 64px;
|
|
max-width: 180px;
|
|
object-fit: contain;
|
|
vertical-align: middle;
|
|
}
|
|
.tenant-name {
|
|
display: inline-block;
|
|
font-size: 22px;
|
|
font-weight: bold;
|
|
line-height: 1.2;
|
|
max-width: 340px;
|
|
vertical-align: middle;
|
|
}
|
|
.ticket-type {
|
|
background: #fff;
|
|
border-radius: 16px;
|
|
color: {{ $primaryColor }};
|
|
display: inline-block;
|
|
font-size: 9px;
|
|
font-weight: bold;
|
|
letter-spacing: 1.25px;
|
|
padding: 8px 13px;
|
|
}
|
|
.content { padding-top: 36px; text-align: center; }
|
|
.eyebrow {
|
|
color: {{ $primaryColor }};
|
|
font-size: 9px;
|
|
font-weight: bold;
|
|
letter-spacing: 1.4px;
|
|
margin: 0 0 10px;
|
|
text-transform: uppercase;
|
|
}
|
|
.ticket-name {
|
|
font-size: 27px;
|
|
font-weight: bold;
|
|
line-height: 1.2;
|
|
margin: 0 auto 23px;
|
|
max-width: 570px;
|
|
}
|
|
.meta-card {
|
|
background: #fff;
|
|
border: 1px solid #dfe7e2;
|
|
border-radius: 10px;
|
|
margin: 0 auto 24px;
|
|
padding: 15px 18px;
|
|
text-align: left;
|
|
width: 500px;
|
|
}
|
|
.meta-cell {
|
|
border-right: 1px solid #e5ebe7;
|
|
padding: 0 14px;
|
|
vertical-align: top;
|
|
width: 33.33%;
|
|
}
|
|
.meta-cell:first-child { padding-left: 0; }
|
|
.meta-cell:last-child { border-right: 0; padding-right: 0; }
|
|
.meta-label {
|
|
color: #758078;
|
|
font-size: 8px;
|
|
font-weight: bold;
|
|
letter-spacing: 0.9px;
|
|
margin-bottom: 5px;
|
|
text-transform: uppercase;
|
|
}
|
|
.meta-value { color: #26322a; font-size: 12px; font-weight: bold; }
|
|
.qr-card {
|
|
background: #fff;
|
|
border: 1px solid #dfe7e2;
|
|
border-radius: 16px;
|
|
display: inline-block;
|
|
padding: 18px 22px 17px;
|
|
}
|
|
.qr-frame {
|
|
border: 3px solid {{ $primaryColor }};
|
|
border-radius: 14px;
|
|
padding: 11px;
|
|
}
|
|
.qr { display: block; height: 276px; width: 276px; }
|
|
.scan-label {
|
|
color: #26322a;
|
|
font-size: 12px;
|
|
font-weight: bold;
|
|
margin: 14px 0 3px;
|
|
}
|
|
.scan-help { color: #77827b; font-size: 9px; margin: 0; }
|
|
.notice {
|
|
background: #eaf1ed;
|
|
border-left: 4px solid {{ $primaryColor }};
|
|
border-radius: 6px;
|
|
color: #445049;
|
|
line-height: 1.45;
|
|
margin: 22px auto 0;
|
|
padding: 11px 14px;
|
|
text-align: left;
|
|
width: 500px;
|
|
}
|
|
.notice strong { color: #26322a; }
|
|
.footer {
|
|
border-top: 1px solid #dfe7e2;
|
|
bottom: 32px;
|
|
color: #7b857e;
|
|
font-size: 8px;
|
|
left: 54px;
|
|
padding-top: 11px;
|
|
position: absolute;
|
|
right: 54px;
|
|
}
|
|
.footer-right { text-align: right; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
@foreach ($tickets as $ticket)
|
|
<section class="ticket">
|
|
<div class="accent"></div>
|
|
|
|
<header class="header">
|
|
<table class="header-table" role="presentation">
|
|
<tr>
|
|
<td class="brand-cell">
|
|
@if ($logoDataUri)
|
|
<img class="logo" src="{{ $logoDataUri }}" alt="Logo de {{ $tenant->nombre }}">
|
|
@else
|
|
<span class="tenant-name">{{ $tenant->nombre }}</span>
|
|
@endif
|
|
</td>
|
|
<td class="type-cell">
|
|
<span class="ticket-type">TICKET DIGITAL</span>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</header>
|
|
|
|
<main class="content">
|
|
<p class="eyebrow">Tu acceso</p>
|
|
<h1 class="ticket-name">{{ $ticket->name }}</h1>
|
|
|
|
<div class="meta-card">
|
|
<table class="meta-table" role="presentation">
|
|
<tr>
|
|
<td class="meta-cell">
|
|
<div class="meta-label">Número de ticket</div>
|
|
<div class="meta-value">#{{ $ticket->id }}</div>
|
|
</td>
|
|
<td class="meta-cell">
|
|
<div class="meta-label">Desde</div>
|
|
<div class="meta-value">{{ $ticket->starts_at?->format('d/m/Y') ?? 'Sin fecha inicial' }}</div>
|
|
</td>
|
|
<td class="meta-cell">
|
|
<div class="meta-label">Hasta</div>
|
|
<div class="meta-value">{{ $ticket->expires_at?->format('d/m/Y') ?? 'Sin vencimiento' }}</div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="qr-card">
|
|
<div class="qr-frame">
|
|
<img class="qr" src="{{ $qrCodes[$ticket->id] }}" alt="Código QR del ticket {{ $ticket->id }}">
|
|
</div>
|
|
<p class="scan-label">Escaneá este código al ingresar</p>
|
|
<p class="scan-help">Aumentá el brillo de la pantalla para una lectura más rápida.</p>
|
|
</div>
|
|
|
|
<div class="notice">
|
|
<strong>Importante:</strong> este ticket es personal. No compartas el código QR con otras personas.
|
|
</div>
|
|
</main>
|
|
|
|
<footer class="footer">
|
|
<table class="footer-table" role="presentation">
|
|
<tr>
|
|
<td>{{ $tenant->nombre }}</td>
|
|
<td class="footer-right">Conservá este ticket hasta finalizar tu ingreso.</td>
|
|
</tr>
|
|
</table>
|
|
</footer>
|
|
</section>
|
|
@endforeach
|
|
</body>
|
|
</html>
|