@php // Helper function to convert images to base64 for reliable PDF rendering function getImageBase64($imageName) { $imagePath = public_path('images/' . $imageName); if (file_exists($imagePath)) { $extension = pathinfo($imagePath, PATHINFO_EXTENSION); $mimeType = $extension === 'png' ? 'image/png' : 'image/jpeg'; return 'data:' . $mimeType . ';base64,' . base64_encode(file_get_contents($imagePath)); } return ''; } // Helper function to convert dynamic image URLs to base64 for PDF function convertImageUrlToBase64($imageUrl) { if (!$imageUrl) return ''; // Convert asset URL to actual file path $relativePath = str_replace(asset(''), '', $imageUrl); $fullPath = public_path($relativePath); if (file_exists($fullPath)) { $extension = strtolower(pathinfo($fullPath, PATHINFO_EXTENSION)); $mimeType = match($extension) { 'png' => 'image/png', 'jpg', 'jpeg' => 'image/jpeg', 'gif' => 'image/gif', 'svg' => 'image/svg+xml', default => 'image/png' }; return 'data:' . $mimeType . ';base64,' . base64_encode(file_get_contents($fullPath)); } return ''; } if (!function_exists('reportPdfEstimateRichTextHeight')) { function reportPdfEstimateRichTextHeight($html, $fontSize = 12, $lineHeight = 18, $contentWidth = 520) { $plain = trim(preg_replace('/\s+/u', ' ', strip_tags($html))); if ($plain === '') { return $lineHeight + 12; } $avgCharWidth = max(5.6, $fontSize * 0.55); $charsPerLine = max(20, (int)floor($contentWidth / $avgCharWidth)); $baseLines = (int)ceil(mb_strlen($plain) / $charsPerLine); $extra = 0; $extra += substr_count($html, '') * 0.4; $extra += substr_count($html, ' $offset) { $prefix = substr($clean, $offset, $start - $offset); if (trim($prefix) !== '') { $blocks[] = $prefix; } } $blocks[] = $block; $offset = $start + strlen($block); } if ($offset < strlen($clean)) { $tail = substr($clean, $offset); if (trim($tail) !== '') { $blocks[] = $tail; } } } else { $blocks[] = $clean; } return $blocks; } } if (!function_exists('reportPdfSplitPlainTextBlock')) { function reportPdfSplitPlainTextBlock($html, $maxHeight, $fontSize = 12, $lineHeight = 18, $contentWidth = 520) { $plain = trim(strip_tags($html)); if ($plain === '') { return [[ 'html' => $html, 'height' => reportPdfEstimateRichTextHeight($html, $fontSize, $lineHeight, $contentWidth), ]]; } $avgCharWidth = max(5.6, $fontSize * 0.55); $charsPerLine = max(20, (int)floor($contentWidth / $avgCharWidth)); $words = preg_split('/\s+/u', $plain); $segments = []; $currentWords = []; foreach ($words as $word) { $currentWords[] = $word; $candidate = implode(' ', $currentWords); $lines = (int)ceil(mb_strlen($candidate) / $charsPerLine); $height = $lines * $lineHeight + 12; if ($height > $maxHeight && count($currentWords) > 1) { array_pop($currentWords); $chunk = implode(' ', $currentWords); $segments[] = [ 'html' => '

' . nl2br(e($chunk)) . '

', 'height' => max($lineHeight + 12, (int)ceil(mb_strlen($chunk) / $charsPerLine) * $lineHeight + 12), ]; $currentWords = [$word]; } } if (!empty($currentWords)) { $chunk = implode(' ', $currentWords); $segments[] = [ 'html' => '

' . nl2br(e($chunk)) . '

', 'height' => max($lineHeight + 12, (int)ceil(mb_strlen($chunk) / $charsPerLine) * $lineHeight + 12), ]; } return $segments ?: [[ 'html' => $html, 'height' => reportPdfEstimateRichTextHeight($html, $fontSize, $lineHeight, $contentWidth), ]]; } } if (!function_exists('reportPdfSplitHtmlContent')) { function reportPdfSplitHtmlContent($html, $maxHeight, $fontSize = 12, $lineHeight = 18, $contentWidth = 520) { $blocks = reportPdfExtractBlocks($html); if (empty($blocks)) { return [[ 'html' => '', 'height' => 0, ]]; } $segments = []; $segmentHtml = ''; $segmentHeight = 0; foreach ($blocks as $block) { $blockHeight = reportPdfEstimateRichTextHeight($block, $fontSize, $lineHeight, $contentWidth); if ($blockHeight > $maxHeight) { $subSegments = reportPdfSplitPlainTextBlock($block, $maxHeight, $fontSize, $lineHeight, $contentWidth); foreach ($subSegments as $sub) { if ($segmentHeight > 0 && ($segmentHeight + $sub['height']) > $maxHeight) { $segments[] = [ 'html' => $segmentHtml, 'height' => $segmentHeight, ]; $segmentHtml = ''; $segmentHeight = 0; } $segmentHtml .= $sub['html']; $segmentHeight += $sub['height']; if ($segmentHeight >= $maxHeight) { $segments[] = [ 'html' => $segmentHtml, 'height' => $segmentHeight, ]; $segmentHtml = ''; $segmentHeight = 0; } } continue; } if ($segmentHeight > 0 && ($segmentHeight + $blockHeight) > $maxHeight) { $segments[] = [ 'html' => $segmentHtml, 'height' => $segmentHeight, ]; $segmentHtml = ''; $segmentHeight = 0; } $segmentHtml .= $block; $segmentHeight += $blockHeight; } if (trim($segmentHtml) !== '' || empty($segments)) { $segments[] = [ 'html' => $segmentHtml, 'height' => $segmentHeight, ]; } return $segments; } } if (!function_exists('reportPdfEstimateAttachmentHeight')) { function reportPdfEstimateAttachmentHeight($files, $columns = 4, $thumbHeight = 150, $rowGap = 16, $basePadding = 32) { if (!is_iterable($files)) { return 0; } $count = 0; foreach ($files as $_) { $count++; } if ($count === 0) { return 0; } $rows = (int)ceil($count / max(1, $columns)); return $rows * ($thumbHeight + $rowGap) + $basePadding; } } @endphp @php $reportPdfPageLimit = 760; $reportPdfCurrentHeight = 0; @endphp
@if(isset($scheduleImages) && count($scheduleImages) > 0) {{-- Use first schedule image as base64 --}} Property Cover Image @elseif(isset($coverImage) && $coverImage) {{-- Fallback to cover image as base64 --}} Property Cover Image @endif
@if(isset($inspectors) && count($inspectors) > 0) @php $inspectorArray = $inspectors->toArray(); @endphp @for($i = 0; $i < count($inspectorArray); $i += 2) {{-- First inspector in the row --}} {{-- Second inspector in the row (if exists) --}} @if(isset($inspectorArray[$i + 1])) @else {{-- Fill empty cells if odd number of inspectors --}} @endif @endfor @else {{-- Fallback when no inspectors assigned --}} @endif
Inspectors
@if(isset($inspectorArray[$i]['avatar']) && $inspectorArray[$i]['avatar']) Inspector @elseif(getImageBase64('03.jpg')) @endif {{ $inspectorArray[$i]['name'] }} @if(isset($inspectorArray[$i + 1]['avatar']) && $inspectorArray[$i + 1]['avatar']) Inspector @elseif(getImageBase64('04.jpg')) @endif {{ $inspectorArray[$i + 1]['name'] }}
@if(getImageBase64('03.jpg')) @endif No Inspector Assigned
@if(isset($agents) && count($agents) > 0) @foreach($agents as $agent) @endforeach @else {{-- Fallback when no agents assigned --}} @endif
Agents
@if(isset($agent['avatar']) && $agent['avatar']) Agent @elseif(getImageBase64('01.jpg')) @endif
{{ $agent['name'] }}
@if($agent['phone'])
{{ $agent['phone'] }}
@endif
@if(getImageBase64('01.jpg')) @endif No Agent Assigned
@if(isset($customers) && count($customers) > 0) @foreach($customers as $customer) @endforeach @else {{-- Fallback when no customers assigned --}} @endif
Clients
@if(isset($customer['avatar']) && $customer['avatar']) Customer @elseif(getImageBase64('02.jpg')) @endif
{{ $customer['name'] }}
@if($customer['phone'])
{{ $customer['phone'] }}
@endif
@if(getImageBase64('02.jpg')) @endif No Client Assigned
{{ $propertyAddress ?: 'Property Address Not Available' }}
@if($inspectors->isNotEmpty() && $customers->isNotEmpty()) {{ $inspectors->first()['name'] }} Prepared for {{ $customers->first()['name'] }}
@elseif($inspectors->isNotEmpty()) Inspector: {{ $inspectors->first()['name'] }}
@elseif($customers->isNotEmpty()) Prepared for {{ $customers->first()['name'] }}
@endif {{ $scheduledDateTime ?: 'Schedule Date/Time Not Available' }}
Choose the right inspector the first time!
@php $reportPdfCurrentHeight = 0; @endphp
TABLE of content
{{-- Dynamic inspection sections from template --}} @if(isset($reportSections) && count($reportSections) > 0) @foreach($reportSections as $index => $section) @php // Priority: Dynamic icon from database, then fallback to keyword mapping $dynamicIcon = convertImageUrlToBase64($section->image_url ?? ''); // Fallback icon mapping for sections without dynamic icons $iconMap = [ 'Property Information' => 'icon_05.jpg', 'Grounds' => 'icon_06.png', 'Exterior' => 'icon_07.png', 'Roof' => 'icon_08.png', 'Garage' => 'icon_09.png', 'Kitchen' => 'icon_10.png', 'Bathroom' => 'icon_11.png', 'Plumbing' => 'icon_12.png', 'Heating' => 'icon_13.png', 'Cooling' => 'icon_14.png', 'Electrical' => 'icon_15.png', 'Interior' => 'icon_16.png', 'Basement' => 'icon_17.jpg', 'Foundation' => 'icon_17.jpg', 'Laundry' => 'icon_18.png', 'Attic' => 'icon_19.png' ]; // Find matching fallback icon if no dynamic icon $fallbackIcon = 'icon_05.jpg'; // default icon if (!$dynamicIcon) { foreach ($iconMap as $keyword => $icon) { if (stripos($section->title, $keyword) !== false) { $fallbackIcon = $icon; break; } } } // Use dynamic icon if available, otherwise use fallback $finalIcon = $dynamicIcon ?: getImageBase64($fallbackIcon); @endphp @endforeach @else {{-- Fallback to hardcoded sections if no dynamic data --}} @endif
Section Name
Introduction
Comment Key Or Definition Of Recommendation
Report Summary
Attachment
@if($finalIcon) @endif {{ $section->title }}
Property Information
Grounds
Attic
Closing Statement
@php $reportPdfCurrentHeight = 0; @endphp @php $hasIntro = false; if (!empty($introductions) && is_iterable($introductions)) { foreach ($introductions as $intro) { if (isset($intro->description) && trim(strip_tags($intro->description))) { $hasIntro = true; break; } } } if (!$hasIntro && isset($introductionContent) && trim(strip_tags($introductionContent))) { $hasIntro = true; } @endphp @if($hasIntro) @endif
@php $reportPdfCurrentHeight = 0; @endphp
Introduction
@php $reportPdfCurrentHeight = 0; @endphp @if(isset($introductions) && count($introductions) > 0) @foreach($introductions as $i => $intro) @if($i > 0) @php $reportPdfCurrentHeight = 0; @endphp @endif @endforeach @elseif(isset($introductionContent) && $introductionContent) @endif
Property & Inspection Information

Full Address

{{ $propertyAddress ?: 'Property Address Not Available' }}

Square Footage

{{ $property->square_feet ?? 'Not Available' }}

Year Built

{{ $property->year_built ?? 'Not Available' }}
Report Introduction
{!! $intro->description !!}
{!! $introductionContent !!}
COMMENT KEY OR DEFINITION OF RECOMMENDATION

The following definitions of comment descriptions represent this inspection report. All comments by the inspector should be considered before purchasing this home. Any recommendations by the inspector to repair or replace suggests a second opinion or further inspection by a qualified contractor. All costs associated with further inspection fees and repair or replacement of item, component or unit should be considered before you purchase the property.

# Image Name Description
1. Inspected Inspected(I) I visually observed the item, component or unit and if no other comments were made then it appeared to be functioning as intended allowing for normal wear and tear.
2. Not Inspected Not Inspected(N) I did not inspect this item, component or unit and made no representations of whether or not it was functioning as intended and will state a reason for not inspecting.
3. Absent Absent(A) This item, component or unit is not in this home or building and I am unable to determine if it is needed.
4. Repair/Replace Repair/Replace(R) The item, component or unit is not functioning as intended, or needs further inspection by a qualified contractor. Items, components or units that can be repaired to satisfactory condition may not need replacement.
5. High Priority High This item, component or unit has been identified as high priority and requires immediate attention.
6. Medium Priority Medium
7. Low Priority Low This item, component or unit has been identified as low priority and requires attention at a later time.
8. Repair or Replace Repair or Replace The item, component or unit is not functioning as intended. Replacement may not be required if repair can be made to satisfactory condition.
9. Safety Issues Safety Issues This defect poses an immediate hazard to occupant health or well-being. Further testing or evaluation may be recommended prior to occupancy.
10. FHA FHA This defect will likely be flagged for correction during the appraisal process if your loan is backed by the government through the Federal Housing Association.
11. HOA HOA This defect will likely fall under the responsibility of the Homeowner Association. Please consult the bylaws with your real estate agent or attorney.
12. Monitor Monitor The item, component, or unit is under suspicion and needs to be watched for further development or escalation.
@php $hasAttachment = false; if (!empty($inspectionattachments) && is_iterable($inspectionattachments)) { foreach ($inspectionattachments as $attachment) { if (!empty($attachment['file_url']) || !empty($attachment['name'])) { $hasAttachment = true; break; } } } @endphp @if($hasAttachment) @endif {{-- Dynamic Sections from Database --}} @if(isset($sectionsWithDetails) && count($sectionsWithDetails) > 0) @foreach($sectionsWithDetails as $sectionIndex => $section)
@php $reportPdfNonTypeItemTypes = array_values(array_filter($section['item_types'] ?? [], function ($type) { return ($type['item_type_id'] ?? null) != 1; })); $reportPdfTypeOneItemTypes = array_values(array_filter($section['item_types'] ?? [], function ($type) { return ($type['item_type_id'] ?? null) == 1; })); $reportPdfHasMaterials = isset($section['material_categories']) && is_array($section['material_categories']) && count($section['material_categories']) > 0; $reportPdfHasTypeOneContent = false; foreach ($reportPdfTypeOneItemTypes as $typeOneCandidate) { if (!empty($typeOneCandidate['items'])) { $reportPdfHasTypeOneContent = true; break; } } @endphp
ALL DEFECTS BY SUMMARY
@if(isset($defectSummary) && count($defectSummary) > 0) @foreach($defectSummary as $summary) @php $commentsCollection = collect($summary['comments'] ?? []); $commentsBySection = $commentsCollection ->filter(function ($comment) { $hasDescription = isset($comment['description']) && trim(strip_tags($comment['description'])) !== ''; $hasFiles = !empty($comment['files']); return $hasDescription || $hasFiles; }) ->groupBy(function ($comment) { return $comment['section_name'] ?? 'General'; }); $summaryHeaderHeight = 96; $sectionHeaderHeight = 56; $summaryHeaderRendered = false; $summaryContainerOpen = false; $summaryContinuationCount = 0; @endphp @continue($commentsBySection->isEmpty()) @foreach($commentsBySection as $sectionName => $comments) @php $sectionHeaderRendered = false; $sectionContinuationCount = 0; $comments = collect($comments)->values(); @endphp @foreach($comments as $commentLoopIndex => $comment) @php $descriptionHtml = $comment['description'] ?? ''; $commentSegments = reportPdfSplitHtmlContent($descriptionHtml, 460, 13, 20, 520); if (empty($commentSegments)) { $commentSegments = [[ 'html' => '', 'height' => 0, ]]; } $segmentCount = count($commentSegments); $attachments = is_iterable($comment['files'] ?? null) ? collect($comment['files'])->toArray() : []; $attachmentHeight = reportPdfEstimateAttachmentHeight($attachments, 4, 120, 16, 40); $priorityValue = $comment['priority_name'] ?? $comment['priority'] ?? null; $priorityValue = is_string($priorityValue) ? trim($priorityValue) : null; @endphp @foreach($commentSegments as $segmentIndex => $segment) @php $segmentBase = $segmentIndex === 0 ? 118 : 92; $segmentHeight = max(40, (int)round($segment['height'] ?? 0)) + $segmentBase; if ($segmentIndex === $segmentCount - 1 && $attachmentHeight > 0) { $segmentHeight += $attachmentHeight; } // Check if we need a page break before rendering this segment $requiresBreak = $reportPdfCurrentHeight > 0 && ($reportPdfCurrentHeight + $segmentHeight) > $reportPdfPageLimit; if ($requiresBreak) { $reportPdfCurrentHeight = 0; // Track that headers were rendered before break $hadSummaryHeader = $summaryHeaderRendered; $hadSectionHeader = $sectionHeaderRendered; // Reset header flags for new page $summaryHeaderRendered = false; $sectionHeaderRendered = false; // Increment continuation counts if ($hadSummaryHeader) { $summaryContinuationCount++; } if ($hadSectionHeader) { $sectionContinuationCount++; } } @endphp @if($requiresBreak) @if($summaryContainerOpen) @php $summaryContainerOpen = false; @endphp @endif
@endif {{-- Render Summary Header if needed --}} @if(!$summaryHeaderRendered)
icon {{ $summary['name'] }} @if($summaryContinuationCount > 0) (continued) @endif
@php $summaryHeaderRendered = true; $summaryContainerOpen = true; $reportPdfCurrentHeight += $summaryHeaderHeight; @endphp @endif {{-- Render Section Header if needed --}} @if(!$sectionHeaderRendered)
{{ $sectionName }} @if($sectionContinuationCount > 0) (continued) @endif
@php $sectionHeaderRendered = true; $reportPdfCurrentHeight += $sectionHeaderHeight; @endphp @endif @php $reportPdfCurrentHeight += $segmentHeight; @endphp
@if(!empty($comment['item_name']))
{{ $comment['item_name'] }}
@endif
{{ $comment['title'] ?? $comment['item_name'] ?? 'Comment' }} @if(!empty($comment['location'])) - {{ $comment['location'] }} @endif @if($segmentCount > 1 && $segmentIndex > 0) (continued) @endif
@if($priorityValue) @php $priorityKey = strtolower($priorityValue); $priorityMap = [ 'high' => ['#f44336', '#fff'], 'medium' => ['#ffc107', '#212529'], 'low' => ['#4caf50', '#fff'], ]; $priorityStyles = $priorityMap[$priorityKey] ?? ['#6c757d', '#fff']; @endphp {{ ucfirst($priorityValue) }} @endif
{!! $segment['html'] ?? '' !!}
@if($segmentIndex === $segmentCount - 1 && !empty($attachments)) @php $attachmentChunks = collect($attachments)->chunk(4); @endphp
@foreach($attachmentChunks as $chunk) @foreach($chunk as $file) @php $file = is_array($file) ? $file : (array)$file; $fileName = $file['original_name'] ?? ($file['name'] ?? 'Attachment'); $fileUrl = $file['file_url'] ?? ($file['url'] ?? ''); $isImage = $file['is_image'] ?? false; $isVideo = $file['is_video'] ?? false; if (!$isImage && $fileUrl) { $isImage = (bool)preg_match('/\\.(jpe?g|png|gif|bmp|webp)$/i', $fileUrl); } if (!$isVideo && $fileUrl) { $isVideo = (bool)preg_match('/\\.(mp4|mov|avi|mkv|webm|ogg|3gp|flv|wmv)$/i', $fileUrl); } $base64Preview = ($isImage && $fileUrl) ? convertImageUrlToBase64($fileUrl) : ''; $fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); @endphp @endforeach @for($i = $chunk->count(); $i < 4; $i++) @endfor @endforeach
@if($isImage && $base64Preview)
{{ $fileName }}
IMG
Click to view
@elseif($isVideo)
VID
{{ $fileName }}
Click to play
@else @php $fileIcon = '📄'; switch ($fileExtension) { case 'pdf': $fileIcon = '📄'; break; case 'doc': case 'docx': $fileIcon = '📝'; break; case 'xls': case 'xlsx': $fileIcon = '📊'; break; case 'ppt': case 'pptx': $fileIcon = '📋'; break; case 'txt': $fileIcon = '📃'; break; case 'zip': case 'rar': case '7z': $fileIcon = '🗂️'; break; } @endphp
{{ strtoupper($fileExtension ?: 'FILE') }}
{{ $fileIcon }}
{{ $fileName }}
@endif
@endif
@php $reportPdfCurrentHeight += $segmentHeight; @endphp @endforeach @endforeach @endforeach @if($summaryContainerOpen)
@php $summaryContainerOpen = false; @endphp @php $reportPdfCurrentHeight += 12; @endphp @endif @endforeach @else
No summary data available.
@endif
@php $reportPdfCurrentHeight = 0; @endphp @php $reportPdfCurrentHeight = 0; @endphp @php $reportPdfCurrentHeight = 0; @endphp
ATTACHMENTS
@if(isset($inspectionattachments) && count($inspectionattachments) > 0) @foreach(collect($inspectionattachments)->chunk(4) as $chunk) @foreach($chunk as $attachment) @php $attachmentArray = is_array($attachment) ? $attachment : (array)$attachment; $attachmentName = $attachmentArray['name'] ?? ($attachmentArray['original_name'] ?? 'Attachment'); $attachmentDescription = $attachmentArray['description'] ?? ''; $attachmentUrl = $attachmentArray['file_url'] ?? ($attachmentArray['url'] ?? ''); $attachmentIsPdf = $attachmentArray['is_pdf'] ?? false; $attachmentIsImage = $attachmentArray['is_image'] ?? false; $attachmentIsVideo = $attachmentArray['is_video'] ?? false; if (!$attachmentIsPdf && $attachmentUrl) { $attachmentIsPdf = (bool)preg_match('/\.pdf($|\?)/i', $attachmentUrl); } if (!$attachmentIsImage && $attachmentUrl) { $attachmentIsImage = (bool)preg_match('/\.(jpe?g|png|gif|bmp|webp)($|\?)/i', $attachmentUrl); } if (!$attachmentIsVideo && $attachmentUrl) { $attachmentIsVideo = (bool)preg_match('/\.(mp4|mov|avi|mkv|webm|ogg|3gp|flv|wmv)($|\?)/i', $attachmentUrl); } @endphp @if(!$loop->last) @endif @endforeach {{-- Fill remaining cells if less than 4 in this row --}} @for($i = $chunk->count(); $i < 4; $i++) @if($i > 0) @endif @endfor @endforeach
@if($attachmentUrl) @if($attachmentIsPdf) @elseif($attachmentIsImage) @elseif($attachmentIsVideo) @else @endif

{{ $attachmentName }}

@else @if($attachmentIsPdf) @elseif($attachmentIsImage) @elseif($attachmentIsVideo) @else @endif

{{ $attachmentName }}

@endif
   
@else
No attachments available.
@endif
{{-- Item Types != 1 --}} @if(count($reportPdfNonTypeItemTypes) > 0) @foreach($reportPdfNonTypeItemTypes as $itemType) @if($itemType['item_type_id'] != 1) @endif @endforeach {{-- Materials Section --}} @if($reportPdfHasMaterials) @endif {{-- Section Report for item_type_id == 1 --}} @foreach($reportPdfTypeOneItemTypes as $itemType) @if($itemType['item_type_id'] == 1) @endif @endforeach {{-- Section Report for item_type_id == 1 (single instance) --}} {{-- ...existing code... --}} @endif
{{ $section['title'] }}
@php $reportPdfTypeHasVisibleContent = !empty($itemType['item_type_title'] ?? '') || (!empty($itemType['item_description'] ?? '') && trim(strip_tags($itemType['item_description'])) !== '') || (isset($itemType['items']) && count($itemType['items']) > 0); @endphp @if(isset($itemType['item_type_title'])) @endif @if(isset($itemType['item_description']) && $itemType['item_description']) @endif {{-- Items Section --}} @if(isset($itemType['items']) && count($itemType['items']) > 0) @foreach($itemType['items'] as $item) @php // Pre-check whether the first comment segment will force a page break $reportPdfSkipInitialItemHeader = false; if (isset($item['comments']) && count($item['comments']) > 0) { $firstComment = $item['comments'][0]; $firstDesc = $firstComment['description'] ?? ''; $firstSegments = reportPdfSplitHtmlContent($firstDesc, 520, 13, 20, 520); $firstAttachHeight = reportPdfEstimateAttachmentHeight($firstComment['files'] ?? []); $firstSegmentHeight = max(40, (int)round($firstSegments[0]['height'] ?? 0)) + 130; if (count($firstSegments) === 1) { $firstSegmentHeight += $firstAttachHeight; } // Add item header height (≈60) to check if it would overflow if ($reportPdfCurrentHeight > 0 && ($reportPdfCurrentHeight + 60 + $firstSegmentHeight) > $reportPdfPageLimit) { $reportPdfSkipInitialItemHeader = true; } } @endphp @if(!$reportPdfSkipInitialItemHeader) @php $reportPdfCurrentHeight += 60; @endphp @endif {{-- Comments Section --}} @if(isset($item['comments']) && count($item['comments']) > 0) @foreach($item['comments'] as $commentLoopIndex => $comment) @php $commentDescription = $comment['description'] ?? ''; $commentSegments = reportPdfSplitHtmlContent($commentDescription, 520, 13, 20, 520); $attachmentHeight = reportPdfEstimateAttachmentHeight($comment['files'] ?? []); $segmentCount = count($commentSegments); @endphp @foreach($commentSegments as $segmentIndex => $segment) @php $segmentBase = $segmentIndex === 0 ? 130 : 90; $segmentHeight = max(40, (int)round($segment['height'])) + $segmentBase; if ($segmentIndex === $segmentCount - 1) { $segmentHeight += $attachmentHeight; } $requiresBreak = $reportPdfCurrentHeight > 0 && ($reportPdfCurrentHeight + $segmentHeight) > $reportPdfPageLimit; // If we skipped the initial header and this is the first segment, force the break and show header on new page if ($reportPdfSkipInitialItemHeader && $commentLoopIndex === 0 && $segmentIndex === 0) { $requiresBreak = true; $reportPdfSkipInitialItemHeader = false; // Consume the flag } if ($requiresBreak) { $reportPdfCurrentHeight = 0; } @endphp @if($requiresBreak) @php $reportPdfCurrentHeight = 0; @endphp @php $reportPdfCurrentHeight += 60; @endphp @endif @php $reportPdfCurrentHeight += $segmentHeight; @endphp @endforeach @endforeach @endif @endforeach @endif
{{ $itemType['item_type_title'] }}
{!! nl2br(e($itemType['item_description'])) !!}
{{ $item['item_title'] }}
{{ $item['item_title'] }} @if(!($commentLoopIndex === 0 && $segmentIndex === 0)) (continued) @endif
{{ $comment['title'] ?? $item['item_title'] }} @if(isset($comment['location']) && $comment['location']) - {{ $comment['location'] }} @endif @if($segmentCount > 1 && $segmentIndex > 0) (continued) @endif @if(isset($comment['priority_name']) && $comment['priority_name']) {{ $comment['priority_name'] }} @endif
{!! $segment['html'] !!}
@if($segmentCount > 1 && $segmentIndex < $segmentCount - 1)
Continued on next page…
@endif @if($segmentIndex === $segmentCount - 1 && isset($comment['files']) && count($comment['files']) > 0)
@php $fileCounter = 0; @endphp @foreach($comment['files'] as $file) @php $isImage = $file['is_image'] ?? (isset($file['original_name']) && preg_match('/\.(jpg|jpeg|png|gif|bmp|webp)$/i', $file['original_name'])); $isVideo = $file['is_video'] ?? (isset($file['original_name']) && preg_match('/\.(mp4|mov|avi|mkv|webm|ogg|3gp|flv|wmv)$/i', $file['original_name'])); $fileName = $file['original_name'] ?? 'Unknown File'; $fileUrl = $file['file_url'] ?? ''; $base64Url = $isImage && $fileUrl ? convertImageUrlToBase64($fileUrl) : ''; @endphp @if($fileCounter > 0 && $fileCounter % 4 == 0) @endif @php $fileCounter++; @endphp @endforeach {{-- Fill empty cells if not divisible by 4 --}} @while($fileCounter % 4 != 0) @php $fileCounter++; @endphp @endwhile
@if($isImage && $base64Url)
{{ $fileName }}
IMG
Click to view
@elseif($isVideo)
VID
{{ $fileName }}
Click to play
@else @php $fileIcon = '📄'; $fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); switch ($fileExtension) { case 'pdf': $fileIcon = '📄'; break; case 'doc': case 'docx': $fileIcon = '📝'; break; case 'xls': case 'xlsx': $fileIcon = '📊'; break; case 'ppt': case 'pptx': $fileIcon = '📋'; break; case 'txt': $fileIcon = '📃'; break; case 'zip': case 'rar': case '7z': $fileIcon = '🗂️'; break; default: $fileIcon = '📄'; } @endphp
{{ strtoupper($fileExtension ?: 'FILE') }}
{{ $fileIcon }}
{{ $fileName }}
@endif
@endif
@php $reportPdfShouldBreakAfterType = false; if ($reportPdfTypeHasVisibleContent && !$loop->last) { $reportPdfShouldBreakAfterType = true; } elseif ($reportPdfTypeHasVisibleContent && ($reportPdfHasMaterials || $reportPdfHasTypeOneContent)) { $reportPdfShouldBreakAfterType = true; } @endphp @if($reportPdfShouldBreakAfterType)
@php $reportPdfCurrentHeight = 0; @endphp @endif
@foreach($section['material_categories'] as $material) @endforeach
{{ $section['title'] }} Materials
{{ $material['name'] ?: $material['title'] }}
@if(isset($material['answers']) && $material['answers'])
{{ $material['answers'] }}
@elseif($material['description'])
{!! nl2br(e($material['description'])) !!}
@endif @if(isset($material['files']) && count($material['files']) > 0)
@php $fileCounter = 0; @endphp @foreach($material['files'] as $file) @php $isImage = $file['is_image'] ?? (isset($file['original_name']) && preg_match('/\\.(jpg|jpeg|png|gif|bmp|webp)$/i', $file['original_name'])); $isVideo = $file['is_video'] ?? (isset($file['original_name']) && preg_match('/\\.(mp4|mov|avi|mkv|webm|ogg|3gp|flv|wmv)$/i', $file['original_name'])); $fileName = $file['original_name'] ?? 'Unknown File'; $fileUrl = $file['file_url'] ?? ''; $base64Url = $isImage && $fileUrl ? convertImageUrlToBase64($fileUrl) : ''; @endphp @if($fileCounter > 0 && $fileCounter % 4 == 0) @endif @php $fileCounter++; @endphp @endforeach @while($fileCounter % 4 != 0) @php $fileCounter++; @endphp @endwhile
@if($isImage && $base64Url)
{{ $fileName }}
IMG
Click to view
@elseif($isVideo)
VID
{{ $fileName }}
Click to play
@else @php $fileIcon = '📄'; $fileExtension = pathinfo($fileName, PATHINFO_EXTENSION); $fileExtension = strtolower($fileExtension); switch ($fileExtension) { case 'pdf': $fileIcon = '📄'; break; case 'doc': case 'docx': $fileIcon = '📝'; break; case 'xls': case 'xlsx': $fileIcon = '📊'; break; case 'ppt': case 'pptx': $fileIcon = '📋'; break; case 'txt': $fileIcon = '📃'; break; case 'zip': case 'rar': case '7z': $fileIcon = '🗂️'; break; default: $fileIcon = '📄'; } @endphp
{{ strtoupper($fileExtension ?: 'FILE') }}
{{ $fileIcon }}
{{ $fileName }}
@endif
@endif
@if($reportPdfHasTypeOneContent)
@php $reportPdfCurrentHeight = 0; @endphp @endif
@if(isset($itemType['items']) && count($itemType['items']) > 0) @foreach($itemType['items'] as $item) @if($item['item_status'] != 5) @endif @endforeach @endif
Section Items I N A R
{{ $item['item_title'] }} @if(isset($item['comments']) && count($item['comments']) > 0) ({{ count($item['comments']) }} comment{{ count($item['comments']) > 1 ? 's' : '' }}) @endif @if($item['item_status'] == 1) @else   @endif @if($item['item_status'] == 2) @else   @endif @if($item['item_status'] == 3) @else   @endif @if($item['item_status'] == 4) @else   @endif
(I = Inspected, N = Not Inspected, A = Absent, R = Repair/Replace)
{{-- Comments Section --}} @if(isset($itemType['items']) && count($itemType['items']) > 0) @php $hasAnyComments = false; @endphp @foreach($itemType['items'] as $item) @if($item['item_status'] != 5 && isset($item['comments']) && count($item['comments']) > 0) @php $hasAnyComments = true; @endphp @endif @endforeach @if($hasAnyComments)
Comment
@endif @foreach($itemType['items'] as $item) @if($item['item_status'] != 5 && isset($item['comments']) && count($item['comments']) > 0)
{{ $item['item_title'] }}
@foreach($item['comments'] as $comment)
{{ $comment['title'] ?? $item['item_title'] }} @if(isset($comment['location']) && $comment['location']) - {{ $comment['location'] }} @endif @if(isset($comment['priority_name']) && $comment['priority_name']) {{ $comment['priority_name'] }} @endif
{!! $comment['description'] !!}
@if(isset($comment['files']) && count($comment['files']) > 0)
@php $fileCounter = 0; @endphp @foreach($comment['files'] as $file) @php $isImage = $file['is_image'] ?? (isset($file['original_name']) && preg_match('/\.(jpg|jpeg|png|gif|bmp|webp)$/i', $file['original_name'])); $isVideo = $file['is_video'] ?? (isset($file['original_name']) && preg_match('/\.(mp4|mov|avi|mkv|webm|ogg|3gp|flv|wmv)$/i', $file['original_name'])); $fileName = $file['original_name'] ?? 'Unknown File'; $fileUrl = $file['file_url'] ?? ''; // Convert image URL to base64 for PDF $base64Url = $isImage && $fileUrl ? convertImageUrlToBase64($fileUrl) : ''; @endphp @if($fileCounter > 0 && $fileCounter % 4 == 0) @endif @php $fileCounter++; @endphp @endforeach {{-- Fill empty cells if not divisible by 4 --}} @while($fileCounter % 4 != 0) @php $fileCounter++; @endphp @endwhile
@if($isImage && $base64Url) {{-- Image preview with base64 - clickable to open in new tab --}}
{{ $fileName }}
IMG
Click to view
@elseif($isVideo) {{-- Video indicator - clickable to open in new tab --}}
VID
{{ $fileName }}
Click to play
@else {{-- Other file types --}} @php $fileIcon = '📄'; $fileExtension = pathinfo($fileName, PATHINFO_EXTENSION); $fileExtension = strtolower($fileExtension); switch ($fileExtension) { case 'pdf': $fileIcon = '📄'; break; case 'doc': case 'docx': $fileIcon = '📝'; break; case 'xls': case 'xlsx': $fileIcon = '📊'; break; case 'ppt': case 'pptx': $fileIcon = '📋'; break; case 'txt': $fileIcon = '📃'; break; case 'zip': case 'rar': case '7z': $fileIcon = '🗂️'; break; default: $fileIcon = '📄'; } @endphp
{{ strtoupper($fileExtension ?: 'FILE') }}
{{ $fileIcon }}
{{ $fileName }}
@endif
@endif
@endforeach @endif @endforeach @endif
@endforeach @endif {{-- Closing Statement Section --}}
@php $reportPdfCurrentHeight = 0; @endphp
Closing Statement
RECOMMENDED NEXT STEPS
@if(isset($closingStatements) && count($closingStatements) > 0) {{-- Display all closing statements for the template --}} @foreach($closingStatements as $index => $closingStatement) @if($index > 0)
@endif @if(!empty($closingStatement['title']))

{{ $closingStatement['title'] }}

@endif
{!! $closingStatement['description'] ?? '' !!}
@endforeach @elseif(isset($closingStatementContent) && $closingStatementContent) {{-- Fallback to single closing statement --}}
{!! $closingStatementContent !!}
@else {{-- No closing statement found --}}
No closing statement available for this template.
@endif