feat: implement internship journal entry system with printable reports and approval workflow
This commit is contained in:
@@ -25,6 +25,84 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================= PAGE 3: STAJ DEFTERİ (PORTRAIT A4) ================= -->
|
||||
@if($application->notebook_approved || $application->notebook_supervisor_signed || $application->notebook_unit_signed)
|
||||
<div class="notebook-container-wrapper overflow-hidden w-full flex justify-center items-start mt-8">
|
||||
<div id="notebook-node" class="transcript-page bg-white rounded-[2rem] shadow-2xl p-6 md:p-8 relative overflow-hidden mx-auto">
|
||||
|
||||
<!-- Thin border decor for notebook -->
|
||||
<div class="absolute inset-4 border border-slate-100 rounded-[1.5rem] pointer-events-none"></div>
|
||||
|
||||
<div class="relative z-10 flex flex-col justify-between h-full text-slate-700">
|
||||
<div>
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center pb-2 border-b border-slate-100 mb-3">
|
||||
<div>
|
||||
<h2 class="font-serif font-bold text-slate-900 transcript-header-title">ONAYLI STAJ DEFTERİ RAPORU</h2>
|
||||
<p class="text-slate-400 tracking-wider mt-0.5 transcript-header-subtitle">DİJİTAL İMZALI VE ONAYLI STAJ RAPORLARI</p>
|
||||
</div>
|
||||
<img src="{{ asset('logos/truncgil-yatay.svg') }}" alt="Trunçgil Logo" class="h-6 object-contain">
|
||||
</div>
|
||||
|
||||
<!-- Signatures Information -->
|
||||
<div class="bg-emerald-50 border border-emerald-200 rounded-xl p-3 mb-4 flex justify-between items-center">
|
||||
<div>
|
||||
<span class="text-xs font-extrabold text-emerald-800 uppercase block tracking-wider">Dijital İmza Durumu</span>
|
||||
<span class="text-[10px] text-emerald-600 font-semibold block mt-0.5">Bu staj defteri yetkililer tarafından elektronik olarak imzalanmıştır.</span>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
@if($application->notebook_supervisor_signed)
|
||||
<span class="inline-flex px-2 py-1 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">
|
||||
Sorumlu: {{ $application->notebook_supervisor_name ?: 'Alperen Trunç' }}
|
||||
</span>
|
||||
@endif
|
||||
@if($application->notebook_unit_signed)
|
||||
<span class="inline-flex px-2 py-1 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">
|
||||
Yetkili: {{ $application->notebook_unit_name ?: 'Birim Yetkilisi' }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notebook Days content overview -->
|
||||
<div class="overflow-y-auto max-h-[700px] pr-2 no-scrollbar space-y-4">
|
||||
@foreach($days as $day)
|
||||
@php
|
||||
$entry = $savedEntries->get($day['day_number']);
|
||||
@endphp
|
||||
@if($entry && trim($entry->content))
|
||||
<div class="p-3 bg-slate-50 border border-slate-100 rounded-xl">
|
||||
<div class="flex justify-between items-center pb-1.5 border-b border-slate-200/50 mb-1.5">
|
||||
<span class="text-xs font-extrabold text-slate-800">{{ $day['day_number'] }}. Gün Raporu</span>
|
||||
<span class="text-[10px] text-slate-400 font-bold">{{ $day['formatted_date'] }}</span>
|
||||
</div>
|
||||
<p class="text-xs text-slate-600 whitespace-pre-wrap leading-relaxed" style="font-size: 11px;">{{ $entry->content }}</p>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="w-full flex justify-between items-center pt-2 border-t border-slate-100 gap-4 mt-auto">
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data={{ urlencode(route('internship.verify', $application->certificate_code)) }}" alt="QR Code" class="w-14 h-14 border border-slate-200 p-0.5 rounded-lg bg-white">
|
||||
<div>
|
||||
<span class="font-bold text-slate-400 tracking-wider block verify-code-label">GÜVENLİ DOĞRULAMA KODU</span>
|
||||
<span class="font-mono font-bold text-slate-800 block verify-code">{{ $application->certificate_code }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right text-slate-400 footer-address">
|
||||
Trunçgil Teknoloji San. ve Tic. Ltd. Şti.<br>
|
||||
Gaziantep Üniversitesi Teknopark No: 4A
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -426,6 +504,7 @@
|
||||
function scaleAllDocuments() {
|
||||
scaleDocument('.certificate-container-wrapper', '#certificate-node', 1122, 794);
|
||||
scaleDocument('.transcript-container-wrapper', '#transcript-node', 794, 1122);
|
||||
scaleDocument('.notebook-container-wrapper', '#notebook-node', 794, 1122);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', scaleAllDocuments);
|
||||
@@ -455,7 +534,8 @@
|
||||
|
||||
const certEl = document.getElementById('certificate-node');
|
||||
const transEl = document.getElementById('transcript-node');
|
||||
|
||||
const notebookEl = document.getElementById('notebook-node');
|
||||
|
||||
// Force elements to normal scale prior to capturing for PDF
|
||||
const origCertTransform = certEl.style.transform;
|
||||
const origCertWidth = certEl.style.width;
|
||||
@@ -465,7 +545,20 @@
|
||||
const origTransWidth = transEl.style.width;
|
||||
const origTransHeight = transEl.style.height;
|
||||
const origTransRadius = transEl.style.borderRadius;
|
||||
|
||||
|
||||
let origNotebookTransform, origNotebookWidth, origNotebookHeight, origNotebookRadius;
|
||||
if (notebookEl) {
|
||||
origNotebookTransform = notebookEl.style.transform;
|
||||
origNotebookWidth = notebookEl.style.width;
|
||||
origNotebookHeight = notebookEl.style.height;
|
||||
origNotebookRadius = notebookEl.style.borderRadius;
|
||||
|
||||
notebookEl.style.transform = 'none';
|
||||
notebookEl.style.width = '794px';
|
||||
notebookEl.style.height = '1122px';
|
||||
notebookEl.style.borderRadius = '0';
|
||||
}
|
||||
|
||||
certEl.style.transform = 'none';
|
||||
certEl.style.width = '1122px';
|
||||
certEl.style.height = '794px';
|
||||
@@ -474,7 +567,7 @@
|
||||
transEl.style.width = '794px';
|
||||
transEl.style.height = '1122px';
|
||||
transEl.style.borderRadius = '0';
|
||||
|
||||
|
||||
// Render Page 1 (Landscape)
|
||||
const certCanvas = await html2canvas(certEl, {
|
||||
scale: 2,
|
||||
@@ -483,7 +576,7 @@
|
||||
logging: false
|
||||
});
|
||||
const certImgData = certCanvas.toDataURL('image/jpeg', 1.0);
|
||||
|
||||
|
||||
// Initialize landscape A4 document
|
||||
const pdf = new jsPDF({
|
||||
orientation: 'landscape',
|
||||
@@ -491,7 +584,7 @@
|
||||
format: 'a4'
|
||||
});
|
||||
pdf.addImage(certImgData, 'JPEG', 0, 0, 297, 210);
|
||||
|
||||
|
||||
// Render Page 2 (Portrait)
|
||||
const transCanvas = await html2canvas(transEl, {
|
||||
scale: 2,
|
||||
@@ -500,15 +593,30 @@
|
||||
logging: false
|
||||
});
|
||||
const transImgData = transCanvas.toDataURL('image/jpeg', 1.0);
|
||||
|
||||
|
||||
// Calculate exact height using aspect ratio to prevent vertical squishing
|
||||
const transWidth = 210;
|
||||
const transHeight = (transCanvas.height * transWidth) / transCanvas.width;
|
||||
|
||||
|
||||
// Add portrait page to PDF
|
||||
pdf.addPage('a4', 'portrait');
|
||||
pdf.addImage(transImgData, 'JPEG', 0, 0, transWidth, transHeight);
|
||||
|
||||
|
||||
// Render Page 3 (Notebook) if it exists
|
||||
if (notebookEl) {
|
||||
const notebookCanvas = await html2canvas(notebookEl, {
|
||||
scale: 2,
|
||||
useCORS: true,
|
||||
backgroundColor: '#ffffff',
|
||||
logging: false
|
||||
});
|
||||
const notebookImgData = notebookCanvas.toDataURL('image/jpeg', 1.0);
|
||||
const notebookHeight = (notebookCanvas.height * transWidth) / notebookCanvas.width;
|
||||
|
||||
pdf.addPage('a4', 'portrait');
|
||||
pdf.addImage(notebookImgData, 'JPEG', 0, 0, transWidth, notebookHeight);
|
||||
}
|
||||
|
||||
// Restore styles
|
||||
certEl.style.transform = origCertTransform;
|
||||
certEl.style.width = origCertWidth;
|
||||
@@ -518,7 +626,14 @@
|
||||
transEl.style.width = origTransWidth;
|
||||
transEl.style.height = origTransHeight;
|
||||
transEl.style.borderRadius = origTransRadius;
|
||||
|
||||
|
||||
if (notebookEl) {
|
||||
notebookEl.style.transform = origNotebookTransform;
|
||||
notebookEl.style.width = origNotebookWidth;
|
||||
notebookEl.style.height = origNotebookHeight;
|
||||
notebookEl.style.borderRadius = origNotebookRadius;
|
||||
}
|
||||
|
||||
// Trigger Save File
|
||||
pdf.save('{{ str()->slug($application->name) }}-staj-belgesi.pdf');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user