Snapshot of main at 9f39a6e1e (2026-11-02)
This commit is contained in:
commit
6ccf1792d7
2580 files changed
+1040202
No files matched your search
@@ -0,0 +1,86 @@
|
||||
{{-- Variables: $scale, $margin, $logoTop, $logoRight, $logoW, $logoH,
|
||||
$titleMarginTop, $titleFont, $catFont, $excerptFont, $excerptMT,
|
||||
$bottomBottom, $qrSize, $venueFont, $dateFont, $footerFont, $footerMT
|
||||
Plus post data: $heroBase64, $logoWhiteBase64, $translation, $categoryName,
|
||||
$brandColor, $pdfExcerpt, $isEvent, $isNews, $isArticle, $isImagePost,
|
||||
$post, $meetingCityName, $author, $qrBase64 --}}
|
||||
|
||||
{{-- Hero background --}}
|
||||
@if($heroBase64)
|
||||
<img src="{{ $heroBase64 }}" style="position: absolute; top: 0; left: 0; width: {{ $cardW }}mm; height: {{ $cardH }}mm; object-fit: cover; object-position: center center;">
|
||||
@endif
|
||||
<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #000000; opacity: 0.55;"></div>
|
||||
|
||||
{{-- Logo top-right --}}
|
||||
@if($logoWhiteBase64)
|
||||
<div style="position: absolute; top: {{ $logoTop }}; right: {{ $logoRight }}; width: {{ $logoW }}; height: {{ $logoH }}; overflow: hidden;">
|
||||
<img src="{{ $logoWhiteBase64 }}" style="width: {{ $logoW }}; height: {{ $logoH }};" alt="{{ config('app.name') }}">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@php
|
||||
// Compute title font to fit 2 lines — uses double-underscore vars to avoid parent scope collision
|
||||
$__titleText = mb_strtoupper($translation?->title ?? '');
|
||||
$__words = explode(' ', $__titleText);
|
||||
$__maxPt = $cardTitleMaxPt;
|
||||
$__containerMm = $cardTitleContainerMm;
|
||||
$__fitFont = $__maxPt;
|
||||
$__longestWord = max(array_map('mb_strlen', $__words));
|
||||
for ($__try = $__maxPt; $__try >= 10; $__try -= 0.5) {
|
||||
$__charW = $__try * 0.353 * 0.55;
|
||||
$__spaceW = $__try * 0.353 * 0.30;
|
||||
// A single word must never exceed the container
|
||||
if ($__longestWord * $__charW > $__containerMm) continue;
|
||||
$__lineW = 0; $__lines = 1;
|
||||
foreach ($__words as $__wi => $__word) {
|
||||
$__wordW = mb_strlen($__word) * $__charW;
|
||||
$__gap = $__wi > 0 ? $__spaceW : 0;
|
||||
if ($__lineW > 0 && $__lineW + $__gap + $__wordW > $__containerMm) {
|
||||
$__lines++; $__lineW = $__wordW;
|
||||
} else {
|
||||
$__lineW += $__gap + $__wordW;
|
||||
}
|
||||
}
|
||||
if ($__lines <= 2) { $__fitFont = $__try; break; }
|
||||
}
|
||||
$__computedFont = round($__fitFont, 1) . 'pt';
|
||||
@endphp
|
||||
{{-- Title + category badge --}}
|
||||
<div style="position: absolute; top: 0; left: {{ $margin }}; right: {{ $margin }}; padding-top: {{ $titleMarginTop }};">
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $__computedFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase; margin-bottom: {{ $excerptMT }};">{{ $translation?->title }}</div>
|
||||
@if($categoryName)
|
||||
<span style="display: inline-block; background-color: {{ $brandColor }}; color: #ffffff; padding: {{ $catPadV }} {{ $catPadH }}; border-radius: 2px; font-family: 'Oswald', sans-serif; font-weight: 400; font-size: {{ $catFont }}; line-height: 1; text-transform: uppercase;">{{ config('app.name') }} {{ $categoryName }}</span>
|
||||
@endif
|
||||
@if(!$isImagePost && $pdfExcerpt)
|
||||
<div style="font-family: 'Poppins', sans-serif; font-weight: normal; font-size: {{ $excerptFont }}; line-height: 1.4; color: #ffffff; margin-top: {{ $excerptMT }}; overflow: hidden; max-height: {{ $excerptMaxH }};">{{ $pdfExcerpt }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Bottom panel --}}
|
||||
<div style="position: absolute; bottom: {{ $bottomBottom }}; left: {{ $margin }}; right: {{ $margin }};">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
@if($qrBase64)
|
||||
<td style="width: {{ $qrCellW }}; padding-right: {{ $qrCellPad }}; vertical-align: bottom;">
|
||||
<img src="{{ $qrBase64 }}" style="display: block; width: {{ $qrSize }}; height: {{ $qrSize }};">
|
||||
</td>
|
||||
@endif
|
||||
<td style="vertical-align: bottom; text-align: right;">
|
||||
@if($isEvent && $post->meeting && ($post->meeting->venue || $meetingCityName || $post->meeting->from))
|
||||
@php $venueParts = array_filter([$post->meeting->venue ?? null, $meetingCityName ?? null]); @endphp
|
||||
@if(count($venueParts))
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $venueFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase;">{{ implode(', ', $venueParts) }}</div>
|
||||
@endif
|
||||
@if($post->meeting->from)
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $dateFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase;">{{ \Illuminate\Support\Carbon::parse($post->meeting->from)->translatedFormat('l j F') }}, {{ \Illuminate\Support\Carbon::parse($post->meeting->from)->format('H:i') }} {{ __('messages.hour_abbrevation') }}</div>
|
||||
@endif
|
||||
@elseif($isNews)
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $venueFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase;">{{ timebank_config('footer.tagline') }}</div>
|
||||
@elseif($author)
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $dateFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase;">{{ $author->full_name ?? $author->name }}</div>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="margin-top: {{ $footerMT }}; font-family: 'Poppins', sans-serif; font-size: {{ $footerFont }}; color: #aaaaaa; text-align: left;">{{ config('app.name') }} — {{ $isNews && $translation?->from ? \Illuminate\Support\Carbon::parse($translation->from)->translatedFormat('l j F Y') : timebank_config('footer.tagline') }}</div>
|
||||
</div>
|
||||
@@ -0,0 +1,84 @@
|
||||
{{-- Variables: $scale, $margin, $logoTop, $logoRight, $logoW, $logoH,
|
||||
$titleMarginTop, $titleFont, $catFont, $excerptFont, $excerptMT,
|
||||
$bottomBottom, $qrSize, $venueFont, $dateFont, $footerFont, $footerMT
|
||||
Plus post data: $heroBase64, $logoWhiteBase64, $translation, $categoryName,
|
||||
$brandColor, $pdfExcerpt, $isEvent, $isNews, $isArticle, $isImagePost,
|
||||
$post, $meetingCityName, $author, $qrBase64 --}}
|
||||
|
||||
{{-- Hero background --}}
|
||||
@if($heroBase64)
|
||||
<img src="{{ $heroBase64 }}" style="position: absolute; top: 0; left: 0; width: {{ $cardW }}mm; height: {{ $cardH }}mm; object-fit: cover; object-position: center center;">
|
||||
@endif
|
||||
<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #000000; opacity: 0.55;"></div>
|
||||
|
||||
{{-- Logo top-right --}}
|
||||
@if($logoWhiteBase64)
|
||||
<div style="position: absolute; top: {{ $logoTop }}; right: {{ $logoRight }}; width: {{ $logoW }}; height: {{ $logoH }}; overflow: hidden;">
|
||||
<img src="{{ $logoWhiteBase64 }}" style="width: {{ $logoW }}; height: {{ $logoH }};" alt="{{ config('app.name') }}">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@php
|
||||
$__titleText = mb_strtoupper($translation?->title ?? '');
|
||||
$__words = explode(' ', $__titleText);
|
||||
$__maxPt = $cardTitleMaxPt;
|
||||
$__containerMm = $cardTitleContainerMm;
|
||||
$__fitFont = $__maxPt;
|
||||
$__longestWord = max(array_map('mb_strlen', $__words));
|
||||
for ($__try = $__maxPt; $__try >= 10; $__try -= 0.5) {
|
||||
$__charW = $__try * 0.353 * 0.55;
|
||||
$__spaceW = $__try * 0.353 * 0.30;
|
||||
if ($__longestWord * $__charW > $__containerMm) continue;
|
||||
$__lineW = 0; $__lines = 1;
|
||||
foreach ($__words as $__wi => $__word) {
|
||||
$__wordW = mb_strlen($__word) * $__charW;
|
||||
$__gap = $__wi > 0 ? $__spaceW : 0;
|
||||
if ($__lineW > 0 && $__lineW + $__gap + $__wordW > $__containerMm) {
|
||||
$__lines++; $__lineW = $__wordW;
|
||||
} else {
|
||||
$__lineW += $__gap + $__wordW;
|
||||
}
|
||||
}
|
||||
if ($__lines <= 2) { $__fitFont = $__try; break; }
|
||||
}
|
||||
$__computedFont = round($__fitFont, 1) . 'pt';
|
||||
@endphp
|
||||
{{-- Title + category badge --}}
|
||||
<div style="position: absolute; top: 0; left: {{ $margin }}; right: {{ $margin }}; padding-top: {{ $titleMarginTop }};">
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $__computedFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase; margin-bottom: {{ $excerptMT }};">{{ $translation?->title }}</div>
|
||||
@if($categoryName)
|
||||
<span style="display: inline-block; background-color: {{ $brandColor }}; color: #ffffff; padding: {{ $catPadV }} {{ $catPadH }}; border-radius: 2px; font-family: 'Oswald', sans-serif; font-weight: 400; font-size: {{ $catFont }}; line-height: 1; text-transform: uppercase;">{{ config('app.name') }} {{ $categoryName }}</span>
|
||||
@endif
|
||||
@if(!$isImagePost && $pdfExcerpt)
|
||||
<div style="font-family: 'Poppins', sans-serif; font-weight: normal; font-size: {{ $excerptFont }}; line-height: 1.4; color: #ffffff; margin-top: {{ $excerptMT }}; overflow: hidden; max-height: {{ $excerptMaxH }};">{{ $pdfExcerpt }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Bottom panel --}}
|
||||
<div style="position: absolute; bottom: {{ $bottomBottom }}; left: {{ $margin }}; right: {{ $margin }};">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
@if($qrBase64)
|
||||
<td style="width: {{ $qrCellW }}; padding-right: {{ $qrCellPad }}; vertical-align: bottom;">
|
||||
<img src="{{ $qrBase64 }}" style="display: block; width: {{ $qrSize }}; height: {{ $qrSize }};">
|
||||
</td>
|
||||
@endif
|
||||
<td style="vertical-align: bottom; text-align: right;">
|
||||
@if($isEvent && $post->meeting && ($post->meeting->venue || $meetingCityName || $post->meeting->from))
|
||||
@php $venueParts = array_filter([$post->meeting->venue ?? null, $meetingCityName ?? null]); @endphp
|
||||
@if(count($venueParts))
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $venueFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase;">{{ implode(', ', $venueParts) }}</div>
|
||||
@endif
|
||||
@if($post->meeting->from)
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $dateFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase;">{{ \Illuminate\Support\Carbon::parse($post->meeting->from)->translatedFormat('l j F') }}, {{ \Illuminate\Support\Carbon::parse($post->meeting->from)->format('H:i') }} {{ __('messages.hour_abbrevation') }}</div>
|
||||
@endif
|
||||
@elseif($isNews)
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $venueFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase;">{{ timebank_config('footer.tagline') }}</div>
|
||||
@elseif($author)
|
||||
<div style="font-family: 'Oswald', sans-serif; font-weight: 600; font-size: {{ $dateFont }}; line-height: 0.95; color: #ffffff; text-transform: uppercase;">{{ $author->full_name ?? $author->name }}</div>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="margin-top: {{ $footerMT }}; font-family: 'Poppins', sans-serif; font-size: {{ $footerFont }}; color: #aaaaaa; text-align: left;">{{ config('app.name') }} — {{ $isNews && $translation?->from ? \Illuminate\Support\Carbon::parse($translation->from)->translatedFormat('l j F Y') : timebank_config('footer.tagline') }}</div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
{{ __('Posts') }}
|
||||
</x-slot>
|
||||
|
||||
<div class="py-6">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-theme-background overflow-x-visible shadow-xl sm:rounded-lg">
|
||||
|
||||
|
||||
<div class="p-12 px-6 sm:px-20 bg-theme-background">
|
||||
|
||||
@livewire('side-post', [
|
||||
'type' => 'SiteContents\Manage\Posts' ?? null,
|
||||
'latest' => true,
|
||||
'fallbackTitle' => __('Manage posts'),
|
||||
'fallbackDescription' => '' ])
|
||||
|
||||
<!--- Admin section -->
|
||||
@livewire('posts.manage')
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('scroll-to-top', event => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<x-guest-layout>
|
||||
<x-slot name="header">
|
||||
{{ __('Posts') }}
|
||||
</x-slot>
|
||||
|
||||
<div class="py-0 md:py-12" id="custom-timebank-style">
|
||||
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div class="overflow-hidden bg-theme-background shadow-xl sm:rounded-lg">
|
||||
<div class="px-3 sm:px-0">
|
||||
<div class="max-w-4xl mx-auto my-12 p-4">
|
||||
<div class="mt-48 mb-36 flex flex-col items-center justify-center">
|
||||
<h1 class="text-3xl font-bold text-theme-primary dark:text-gray-200">{{ __('Sorry') }}</h1>
|
||||
<p class="text-lg text-theme-secondary dark:text-theme-muted mt-4">{{ __('No page available in your language at the moment') }}</p>
|
||||
|
||||
@if(isset($fallbackUrl) && $fallbackUrl)
|
||||
<a href="{{ $fallbackUrl }}">
|
||||
<x-jetstream.button class="mt-6">
|
||||
{{ __('messages.view_in_language', ['lang' => $fallbackLanguageName]) }}
|
||||
</x-jetstream.button>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-guest-layout>
|
||||
@@ -0,0 +1,28 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
{{ __('Posts') }}
|
||||
</x-slot>
|
||||
|
||||
<div class="py-0 md:py-12" id="custom-timebank-style">
|
||||
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div class="overflow-hidden bg-theme-background shadow-xl sm:rounded-lg">
|
||||
<div class="px-3 sm:px-0">
|
||||
<div class="max-w-4xl mx-auto my-12 p-4">
|
||||
<div class="mt-48 mb-36 flex flex-col items-center justify-center">
|
||||
<h1 class="text-3xl font-bold text-theme-primary dark:text-gray-200">{{ __('Sorry') }}</h1>
|
||||
<p class="text-lg text-theme-secondary dark:text-theme-muted mt-4">{{ __('No page available in your language at the moment') }}</p>
|
||||
|
||||
@if(isset($fallbackUrl) && $fallbackUrl)
|
||||
<a href="{{ $fallbackUrl }}">
|
||||
<x-jetstream.button class="mt-6">
|
||||
{{ __('messages.view_in_language', ['lang' => $fallbackLanguageName]) }}
|
||||
</x-jetstream.button>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,27 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
{{ __('Posts') }}
|
||||
</x-slot>
|
||||
|
||||
<div class="py-6">
|
||||
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div class="overflow-hidden bg-white shadow-xl sm:rounded-lg">
|
||||
<div class="border-b border-gray-200 bg-white p-6 sm:px-20">
|
||||
|
||||
<div class="mt-12">
|
||||
<!-- TODO: insert not found image here -->
|
||||
|
||||
<div class="mt-8 text-5xl font-bold text-gray-900">
|
||||
{{ __('Sorry, this page does not exist') }}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="mb-12 mt-6">
|
||||
<span class="text-xl font-bold text-gray-900">{{ __('This web-address does not link to a published page.') }}'
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,156 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ __('Posts') }}</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
src: url('/fonts/static/oswald/Oswald-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
src: url('/fonts/static/oswald/Oswald-SemiBold.ttf') format('truetype');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
src: url('/fonts/static/poppins/Poppins-Regular.otf') format('opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@page { margin: 0; }
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html, body {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
background-color: #1a1a2e;
|
||||
font-family: DejaVu Sans, Arial, sans-serif;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
@php
|
||||
// 1-up: portrait A4, 1 col × 1 row, card 210×297mm, scale 1.0
|
||||
// 2-up: landscape A4, 2 cols × 1 row, each card 148×210mm (A5 portrait), scale ≈ 0.705
|
||||
// 4-up: portrait A4, 2 cols × 2 rows, each card 105×148mm, scale 0.498
|
||||
$cols = $perPage >= 2 ? 2 : 1;
|
||||
$rows = $perPage === 4 ? 2 : 1;
|
||||
$cardW = $perPage === 4 ? 105 : ($perPage === 2 ? 148 : 210);
|
||||
$cardH = $perPage === 4 ? 148 : ($perPage === 2 ? 210 : 297);
|
||||
|
||||
$scale = match($perPage) {
|
||||
2 => 148/210, // A5 portrait ≈ 0.705
|
||||
4 => 0.498,
|
||||
default => 1.0,
|
||||
};
|
||||
|
||||
// Scaled measurements (baseline from posts/pdf.blade.php A4 layout)
|
||||
$margin = round(20 * $scale, 1) . 'mm';
|
||||
$logoTop = round(14 * $scale, 1) . 'mm';
|
||||
$logoRight = round(20 * $scale, 1) . 'mm';
|
||||
$logoW = round(75 * $scale, 1) . 'mm';
|
||||
$logoH = round(53 * $scale, 1) . 'mm';
|
||||
$titleMarginTop = round(70 * $scale, 1) . 'mm';
|
||||
$titleFontMaxPt = round(54 * $scale, 1); // numeric, no unit
|
||||
$titleContainerMm = $cardW - 2 * 20 * $scale; // card width minus left+right margin
|
||||
$titleFont = $titleFontMaxPt . 'pt'; // fallback string for non-partial use
|
||||
$catFont = round(17 * $scale, 1) . 'pt';
|
||||
$catPadH = round(16 * $scale, 1) . 'px';
|
||||
$catPadV = round(8 * $scale, 1) . 'px';
|
||||
$excerptFont = round(17.5 * $scale, 1) . 'pt';
|
||||
$excerptMT = round(6 * $scale, 1) . 'mm';
|
||||
$excerptMaxH = round(60 * $scale, 1) . 'mm';
|
||||
$bottomBottom= round(20 * $scale, 1) . 'mm';
|
||||
$qrSize = round(20 * $scale, 1) . 'mm';
|
||||
$qrCellW = round(22 * $scale, 1) . 'mm';
|
||||
$qrCellPad = round(4 * $scale, 1) . 'mm';
|
||||
$venueFont = round(22.5 * $scale, 1) . 'pt';
|
||||
$dateFont = round(18 * $scale, 1) . 'pt';
|
||||
$footerFont = round(10.4 * $scale, 1) . 'pt';
|
||||
$footerMT = round(8 * $scale, 1) . 'mm';
|
||||
@endphp
|
||||
|
||||
@if($perPage === 2)
|
||||
@page { size: A4 landscape; margin: 0; }
|
||||
html, body { width: 297mm; height: 210mm; }
|
||||
@endif
|
||||
|
||||
.page-table {
|
||||
width: {{ $perPage === 2 ? '297mm' : '210mm' }};
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.page-table-break { page-break-after: always; }
|
||||
.page-table tr { height: {{ $cardH }}mm; }
|
||||
.card-cell {
|
||||
width: {{ $cardW }}mm;
|
||||
height: {{ $cardH }}mm;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
background-color: #1a1a2e;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@php
|
||||
$cardsPerPage = $rows * $cols;
|
||||
$pages = array_chunk($posts, $cardsPerPage);
|
||||
@endphp
|
||||
|
||||
@foreach($pages as $pageIndex => $pagePosts)
|
||||
@php
|
||||
$padded = array_pad($pagePosts, $cardsPerPage, null);
|
||||
$isLast = $pageIndex === count($pages) - 1;
|
||||
@endphp
|
||||
|
||||
<table class="page-table {{ $isLast ? '' : 'page-table-break' }}">
|
||||
@for($row = 0; $row < $rows; $row++)
|
||||
<tr>
|
||||
@for($col = 0; $col < $cols; $col++)
|
||||
@php $idx = $row * $cols + $col; $data = $padded[$idx] ?? null; @endphp
|
||||
<td class="card-cell">
|
||||
@if($data)
|
||||
@include('posts._pdf-card-bw', array_merge($data, [
|
||||
'scale' => $scale,
|
||||
'cardW' => $cardW,
|
||||
'cardH' => $cardH,
|
||||
'margin' => $margin,
|
||||
'logoTop' => $logoTop,
|
||||
'logoRight' => $logoRight,
|
||||
'logoW' => $logoW,
|
||||
'logoH' => $logoH,
|
||||
'titleMarginTop' => $titleMarginTop,
|
||||
'cardTitleMaxPt' => $titleFontMaxPt,
|
||||
'cardTitleContainerMm' => $titleContainerMm,
|
||||
'catFont' => $catFont,
|
||||
'catPadH' => $catPadH,
|
||||
'catPadV' => $catPadV,
|
||||
'excerptFont' => $excerptFont,
|
||||
'excerptMT' => $excerptMT,
|
||||
'excerptMaxH' => $excerptMaxH,
|
||||
'bottomBottom' => $bottomBottom,
|
||||
'qrSize' => $qrSize,
|
||||
'qrCellW' => $qrCellW,
|
||||
'qrCellPad' => $qrCellPad,
|
||||
'venueFont' => $venueFont,
|
||||
'dateFont' => $dateFont,
|
||||
'footerFont' => $footerFont,
|
||||
'footerMT' => $footerMT,
|
||||
]))
|
||||
@endif
|
||||
</td>
|
||||
@endfor
|
||||
</tr>
|
||||
@endfor
|
||||
</table>
|
||||
@endforeach
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,156 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ __('Posts') }}</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
src: url('/fonts/static/oswald/Oswald-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
src: url('/fonts/static/oswald/Oswald-SemiBold.ttf') format('truetype');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
src: url('/fonts/static/poppins/Poppins-Regular.otf') format('opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@page { margin: 0; }
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html, body {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
background-color: #1a1a2e;
|
||||
font-family: DejaVu Sans, Arial, sans-serif;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
@php
|
||||
// 1-up: portrait A4, 1 col × 1 row, card 210×297mm, scale 1.0
|
||||
// 2-up: landscape A4, 2 cols × 1 row, each card 148×210mm (A5 portrait), scale ≈ 0.705
|
||||
// 4-up: portrait A4, 2 cols × 2 rows, each card 105×148mm, scale 0.498
|
||||
$cols = $perPage >= 2 ? 2 : 1;
|
||||
$rows = $perPage === 4 ? 2 : 1;
|
||||
$cardW = $perPage === 4 ? 105 : ($perPage === 2 ? 148 : 210);
|
||||
$cardH = $perPage === 4 ? 148 : ($perPage === 2 ? 210 : 297);
|
||||
|
||||
$scale = match($perPage) {
|
||||
2 => 148/210, // A5 portrait ≈ 0.705
|
||||
4 => 0.498,
|
||||
default => 1.0,
|
||||
};
|
||||
|
||||
// Scaled measurements (baseline from posts/pdf.blade.php A4 layout)
|
||||
$margin = round(20 * $scale, 1) . 'mm';
|
||||
$logoTop = round(14 * $scale, 1) . 'mm';
|
||||
$logoRight = round(20 * $scale, 1) . 'mm';
|
||||
$logoW = round(75 * $scale, 1) . 'mm';
|
||||
$logoH = round(53 * $scale, 1) . 'mm';
|
||||
$titleMarginTop = round(70 * $scale, 1) . 'mm';
|
||||
$titleFontMaxPt = round(54 * $scale, 1);
|
||||
$titleContainerMm = $cardW - 2 * 20 * $scale;
|
||||
$titleFont = $titleFontMaxPt . 'pt';
|
||||
$catFont = round(17 * $scale, 1) . 'pt';
|
||||
$catPadH = round(16 * $scale, 1) . 'px';
|
||||
$catPadV = round(8 * $scale, 1) . 'px';
|
||||
$excerptFont = round(17.5 * $scale, 1) . 'pt';
|
||||
$excerptMT = round(6 * $scale, 1) . 'mm';
|
||||
$excerptMaxH = round(60 * $scale, 1) . 'mm';
|
||||
$bottomBottom= round(20 * $scale, 1) . 'mm';
|
||||
$qrSize = round(20 * $scale, 1) . 'mm';
|
||||
$qrCellW = round(22 * $scale, 1) . 'mm';
|
||||
$qrCellPad = round(4 * $scale, 1) . 'mm';
|
||||
$venueFont = round(22.5 * $scale, 1) . 'pt';
|
||||
$dateFont = round(18 * $scale, 1) . 'pt';
|
||||
$footerFont = round(10.4 * $scale, 1) . 'pt';
|
||||
$footerMT = round(8 * $scale, 1) . 'mm';
|
||||
@endphp
|
||||
|
||||
@if($perPage === 2)
|
||||
@page { size: A4 landscape; margin: 0; }
|
||||
html, body { width: 297mm; height: 210mm; }
|
||||
@endif
|
||||
|
||||
.page-table {
|
||||
width: {{ $perPage === 2 ? '297mm' : '210mm' }};
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.page-table-break { page-break-after: always; }
|
||||
.page-table tr { height: {{ $cardH }}mm; }
|
||||
.card-cell {
|
||||
width: {{ $cardW }}mm;
|
||||
height: {{ $cardH }}mm;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
background-color: #1a1a2e;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@php
|
||||
$cardsPerPage = $rows * $cols;
|
||||
$pages = array_chunk($posts, $cardsPerPage);
|
||||
@endphp
|
||||
|
||||
@foreach($pages as $pageIndex => $pagePosts)
|
||||
@php
|
||||
$padded = array_pad($pagePosts, $cardsPerPage, null);
|
||||
$isLast = $pageIndex === count($pages) - 1;
|
||||
@endphp
|
||||
|
||||
<table class="page-table {{ $isLast ? '' : 'page-table-break' }}">
|
||||
@for($row = 0; $row < $rows; $row++)
|
||||
<tr>
|
||||
@for($col = 0; $col < $cols; $col++)
|
||||
@php $idx = $row * $cols + $col; $data = $padded[$idx] ?? null; @endphp
|
||||
<td class="card-cell">
|
||||
@if($data)
|
||||
@include('posts._pdf-card-color', array_merge($data, [
|
||||
'scale' => $scale,
|
||||
'cardW' => $cardW,
|
||||
'cardH' => $cardH,
|
||||
'margin' => $margin,
|
||||
'logoTop' => $logoTop,
|
||||
'logoRight' => $logoRight,
|
||||
'logoW' => $logoW,
|
||||
'logoH' => $logoH,
|
||||
'titleMarginTop' => $titleMarginTop,
|
||||
'cardTitleMaxPt' => $titleFontMaxPt,
|
||||
'cardTitleContainerMm' => $titleContainerMm,
|
||||
'catFont' => $catFont,
|
||||
'catPadH' => $catPadH,
|
||||
'catPadV' => $catPadV,
|
||||
'excerptFont' => $excerptFont,
|
||||
'excerptMT' => $excerptMT,
|
||||
'excerptMaxH' => $excerptMaxH,
|
||||
'bottomBottom' => $bottomBottom,
|
||||
'qrSize' => $qrSize,
|
||||
'qrCellW' => $qrCellW,
|
||||
'qrCellPad' => $qrCellPad,
|
||||
'venueFont' => $venueFont,
|
||||
'dateFont' => $dateFont,
|
||||
'footerFont' => $footerFont,
|
||||
'footerMT' => $footerMT,
|
||||
]))
|
||||
@endif
|
||||
</td>
|
||||
@endfor
|
||||
</tr>
|
||||
@endfor
|
||||
</table>
|
||||
@endforeach
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,294 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ $translation?->title ?? config('app.name') }}</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
src: url('/fonts/static/oswald/Oswald-Regular.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
src: url('/fonts/static/oswald/Oswald-SemiBold.ttf') format('truetype');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
src: url('/fonts/static/oswald/Oswald-Bold.ttf') format('truetype');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
src: url('/fonts/static/poppins/Poppins-Regular.otf') format('opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
src: url('/fonts/static/poppins/Poppins-SemiBold.otf') format('opentype');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
src: url('/fonts/static/poppins/Poppins-Bold.otf') format('opentype');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html, body {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
background-color: #1a1a2e;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: #000000;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
/* ── Full-page hero ── */
|
||||
.hero {
|
||||
position: relative;
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
background-color: #1a1a2e;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-overlay {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background-color: #000000;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
/* ── Top section: title + badges + excerpt ── */
|
||||
.hero-content {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
padding: 14mm 20mm 0 20mm;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-family: 'Oswald', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 54pt;
|
||||
line-height: 0.95;
|
||||
color: #ffffff;
|
||||
margin-top: 53mm;
|
||||
margin-bottom: 5mm;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hero-category {
|
||||
display: inline-block;
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
padding: 0 8px 4px 8px;
|
||||
border-radius: 2px;
|
||||
font-family: 'Oswald', sans-serif;
|
||||
font-weight: normal;
|
||||
font-size: 17pt;
|
||||
line-height: 1;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hero-excerpt {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-weight: normal;
|
||||
font-size: 17.5pt;
|
||||
line-height: 1.4;
|
||||
color: #ffffff;
|
||||
margin-top: 6mm;
|
||||
max-height: 150mm;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── Logo top-right ── */
|
||||
.hero-logo {
|
||||
position: absolute;
|
||||
top: 14mm;
|
||||
right: 20mm;
|
||||
width: 75mm;
|
||||
height: 53mm;
|
||||
}
|
||||
.hero-logo img { width: 75mm; height: 53mm; }
|
||||
|
||||
/* ── Bottom panel pinned to page bottom ── */
|
||||
.hero-bottom-panel {
|
||||
position: absolute;
|
||||
bottom: 20mm; left: 0; right: 0;
|
||||
padding: 0 20mm;
|
||||
}
|
||||
.hero-bottom-footer {
|
||||
margin-top: 8mm;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-size: 10.4pt;
|
||||
color: #aaaaaa;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Bottom-right: venue+date or author name */
|
||||
.hero-bottom-text-row {
|
||||
text-align: right;
|
||||
margin-bottom: 4mm;
|
||||
}
|
||||
.hero-bottom-text {
|
||||
font-family: 'Oswald', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 22.5pt;
|
||||
line-height: 0.95;
|
||||
color: #ffffff;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.hero-bottom-text-venue {
|
||||
font-family: 'Oswald', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 22.5pt;
|
||||
line-height: 0.95;
|
||||
color: #ffffff;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Footer row */
|
||||
.footer-row {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
.footer-qr-cell {
|
||||
display: table-cell;
|
||||
vertical-align: bottom;
|
||||
width: 22mm;
|
||||
}
|
||||
.footer-qr-cell img { width: 20mm; height: 20mm; }
|
||||
.footer-app-name-cell {
|
||||
display: table-cell;
|
||||
vertical-align: bottom;
|
||||
padding-left: 3mm;
|
||||
}
|
||||
.footer-app-name {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-size: 12pt;
|
||||
color: #cccccc;
|
||||
}
|
||||
.footer-author-cell {
|
||||
display: table-cell;
|
||||
vertical-align: bottom;
|
||||
text-align: right;
|
||||
}
|
||||
.footer-author-row { display: table; }
|
||||
.footer-avatar-cell {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
width: 10mm;
|
||||
}
|
||||
.footer-avatar {
|
||||
width: 9mm;
|
||||
height: 9mm;
|
||||
border-radius: 4.5mm;
|
||||
border: 1px solid #aaaaaa;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #444444;
|
||||
}
|
||||
.footer-name-cell {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
padding-right: 3mm;
|
||||
text-align: right;
|
||||
}
|
||||
.footer-author-label {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-weight: normal;
|
||||
font-size: 11pt;
|
||||
color: #cccccc;
|
||||
}
|
||||
.footer-author-name {
|
||||
font-family: 'Oswald', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 15pt;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="hero">
|
||||
@if($heroBase64)
|
||||
<img src="{{ $heroBase64 }}" style="position: absolute; top: 0; left: 0; width: 210mm; height: 297mm; object-fit: cover; object-position: center center;">
|
||||
@endif
|
||||
<div class="hero-overlay"></div>
|
||||
|
||||
{{-- Logo top-right --}}
|
||||
@if($logoWhiteBase64)
|
||||
<div class="hero-logo">
|
||||
<img src="{{ $logoWhiteBase64 }}" alt="{{ config('app.name') }}">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Top: title + category badge + excerpt (ImagePost: title only, smaller) --}}
|
||||
@if($isImagePost)
|
||||
@if($translation?->title)
|
||||
<div class="hero-content">
|
||||
<div class="hero-title">{{ $translation->title }}</div>
|
||||
@if($categoryName)
|
||||
<span class="hero-category" style="background-color: {{ $brandColor }};">{{ config('app.name') }} {{ $categoryName }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<div class="hero-content">
|
||||
<div class="hero-title">{{ $translation?->title }}</div>
|
||||
@if($categoryName && !$isArticle)
|
||||
<span class="hero-category" style="background-color: {{ $brandColor }};">{{ config('app.name') }} {{ $categoryName }}</span>
|
||||
@endif
|
||||
@if($pdfExcerpt)
|
||||
<div class="hero-excerpt">{{ $pdfExcerpt }}</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Bottom panel: QR bottom-left, type-specific info bottom-right --}}
|
||||
<div class="hero-bottom-panel">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
@if($qrBase64)
|
||||
<td style="width: 22mm; padding-right: 4mm; vertical-align: bottom;">
|
||||
<img src="{{ $qrBase64 }}" alt="QR" style="width: 20mm; height: 20mm; display: block;">
|
||||
</td>
|
||||
@endif
|
||||
<td style="vertical-align: bottom; text-align: right;">
|
||||
@if($isEvent && $post->meeting && ($post->meeting->venue || $meetingCityName || $post->meeting->from))
|
||||
{{-- Event: venue + city, then date + time --}}
|
||||
@php $venueParts = array_filter([$post->meeting->venue ?? null, $meetingCityName ?? null]); @endphp
|
||||
@if(count($venueParts))
|
||||
<div class="hero-bottom-text-venue">{{ implode(', ', $venueParts) }}</div>
|
||||
@endif
|
||||
@if($post->meeting->from)
|
||||
<div class="hero-bottom-text">{{ \Illuminate\Support\Carbon::parse($post->meeting->from)->translatedFormat('l j F') }}, {{ \Illuminate\Support\Carbon::parse($post->meeting->from)->format('H:i') }} {{ __('messages.hour_abbrevation') }}</div>
|
||||
@endif
|
||||
@elseif($isNews)
|
||||
{{-- News: tagline --}}
|
||||
<div class="hero-bottom-text">{{ timebank_config('footer.tagline') }}</div>
|
||||
@elseif($author)
|
||||
{{-- Article / ImagePost: author name --}}
|
||||
<div class="hero-bottom-text">{{ $author->full_name ?? $author->name }}</div>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="hero-bottom-footer">{{ config('app.name') }} — {{ $isNews && $translation?->from ? \Illuminate\Support\Carbon::parse($translation->from)->translatedFormat('l j F Y') : timebank_config('footer.tagline') }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,203 @@
|
||||
<x-guest-layout>
|
||||
<x-slot name="header">
|
||||
{{ $category }}
|
||||
</x-slot>
|
||||
|
||||
<div class="py-0 md:py-12" id="custom-timebank-style">
|
||||
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div class="overflow-hidden bg-theme-background shadow-xl sm:rounded-lg">
|
||||
<div class="border-b border-theme-primary bg-theme-background p-6 sm:px-20 lg:px-32 lg:py-18">
|
||||
|
||||
|
||||
<div class="mb-2 mt-0 lg:mt-6 flex justify-between">
|
||||
<span class="text-xs lg:text-sm text-theme-secondary">{{ $update }}</span>
|
||||
</div>
|
||||
<h2 class="mt-4 lg:mt-8 text-3xl lg:text-5xl font-semibold leading-tight text-theme-primary">
|
||||
{{ $post->translations->first()->title }}
|
||||
</h2>
|
||||
|
||||
@if ($post->category && $post->category->type === 'App\Models\Article' && $post->author)
|
||||
<div class="mt-6 flex items-center space-x-4 cursor-pointer hover:opacity-80"
|
||||
onclick="window.location='{{ $post->author->profile_url }}'">
|
||||
@if ($post->author->profile_photo_path)
|
||||
<img class="h-9 w-9 rounded-full profile-photo object-cover outline outline-1 outline-offset-1 outline-theme-secondary"
|
||||
src="{{ url(Storage::url($post->author->profile_photo_path)) }}"
|
||||
alt="{{ $post->author->full_name ?? $post->author->name }}">
|
||||
@endif
|
||||
<span class="text-lg">
|
||||
{{ $post->author->full_name ?? $post->author->name }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (isset($post->meeting->from))
|
||||
<h3 class="mt-4 lg:mt-8 text-xl lg:text-4xl font-semibold leading-tight text-theme-primary">
|
||||
{{ ucfirst(Illuminate\Support\Carbon::parse($post->meeting->from)->isoFormat('dddd D MMMM, HH:mm')) }}{{ $post->meeting->till ? ' - ' . Illuminate\Support\Carbon::parse($post->meeting->till)->format('H:i') : '' }} {{ __('h.') }}
|
||||
</h3>
|
||||
@endif
|
||||
@if (isset($post->translations->first()->excerpt))
|
||||
<div class="mt-6">
|
||||
<div class="px-0 py-2 text-xl lg:text-2xl leading-relaxed font-bold text-theme-primary">
|
||||
{{ $post->translations->first()->excerpt }}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Image --->
|
||||
<div class="my-4 lg:my-12">
|
||||
@if ($media != null)
|
||||
{{ $media('hero', ['class' => 'w-full h-auto']) }}
|
||||
@php
|
||||
$locale = App::getLocale();
|
||||
$imageCaption = $media->getCustomProperty('caption-' . $locale);
|
||||
|
||||
// Fallback to other locales if caption not found
|
||||
if (!$imageCaption) {
|
||||
$fallbackLocales = array_keys(config('laravellocalization.supportedLocales'));
|
||||
foreach ($fallbackLocales as $fallbackLocale) {
|
||||
$imageCaption = $media->getCustomProperty('caption-' . $fallbackLocale);
|
||||
if ($imageCaption) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$imageOwner = $media->getCustomProperty('owner');
|
||||
$captionParts = array_filter([$imageCaption, $imageOwner]);
|
||||
$captionText = implode(' ', $captionParts);
|
||||
@endphp
|
||||
@if ($captionText)
|
||||
<div class="mt-1 lg:mt-2 text-right text-3xs lg:text-xs text-theme-secondary">
|
||||
{{ $captionText }}
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if (isset($post->translations->first()->content))
|
||||
{{-- Post content with list styling --}}
|
||||
<div class="post-content mt-2 lg:mt-6 mb-12 lg:mb-16 md:mx-32 text-base lg:text-lg leading-relaxed lg:leading-loose text-theme-primary">
|
||||
{!! \App\Helpers\StringHelper::sanitizeHtml($post->translations->first()->content) !!}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (isset($post->meeting))
|
||||
<div class="my-12 bg-theme-surface rounded-lg p-6 md:mx-32">
|
||||
<table class="w-full">
|
||||
<tbody>
|
||||
@if($post->meeting->venue)
|
||||
<tr>
|
||||
<td class="py-2 pr-4 font-semibold text-theme-primary w-1/3">{{ __('Venue') }}</td>
|
||||
<td class="py-2 text-theme-primary">{{ $post->meeting->venue }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if($post->meeting->address)
|
||||
<tr>
|
||||
<td class="py-2 pr-4 font-semibold text-theme-primary w-1/3">{{ __('Address') }}</td>
|
||||
<td class="py-2 text-theme-primary">
|
||||
<a href="https://www.openstreetmap.org/search?query={{ urlencode($post->meeting->address) }}"
|
||||
target="_blank"
|
||||
class="underline hover:text-theme-secondary">
|
||||
{{ $post->meeting->address }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if($post->meeting->from)
|
||||
<tr>
|
||||
<td class="py-2 pr-4 font-semibold text-theme-primary w-1/3">{{ __('Date & Time') }}</td>
|
||||
<td class="py-2 text-theme-primary">{{ ucfirst(Illuminate\Support\Carbon::parse($post->meeting->from)->isoFormat('dddd D MMMM YYYY, H:mm')) }}{{ $post->meeting->till ? ' - ' . Illuminate\Support\Carbon::parse($post->meeting->till)->format('H:i') : '' }} {{ __('h.') }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if(isset($post->meeting->meetingable->name))
|
||||
<tr>
|
||||
<td class="py-2 pr-4 font-semibold text-theme-primary w-1/3">{{ __('Organizer') }}</td>
|
||||
<td class="py-2 text-theme-primary">
|
||||
<a href="{{ $post->meeting->meetingable->profile_url }}"
|
||||
class="flex items-center space-x-3 hover:opacity-80 cursor-pointer">
|
||||
@if(isset($post->meeting->meetingable->profile_photo_path))
|
||||
<img src="{{ url(Storage::url($post->meeting->meetingable->profile_photo_path)) }}"
|
||||
class="w-10 h-10 rounded-full profile-photo object-cover">
|
||||
@endif
|
||||
<span>{{ $post->meeting->meetingable->name }}</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="my-12 flex flex-col md:flex-row justify-between gap-6">
|
||||
<!-- Social Share Buttons + Print -->
|
||||
<div class="md:self-end">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{!! Share::mastodon()
|
||||
->bluesky()
|
||||
->linkedin()
|
||||
->instagram()
|
||||
->facebook()
|
||||
->whatsapp()
|
||||
{{-- ->x() --}}
|
||||
{{-- ->telegram() --}}
|
||||
->text($post->translations->first()->title)
|
||||
->render() !!}
|
||||
<a href="{{ route('post.pdf', ['id' => $post->id]) }}"
|
||||
target="_blank"
|
||||
title="{{ __('Print as PDF') }}"
|
||||
class="inline-flex items-center justify-center rounded-md border border-theme-border bg-theme-surface p-2 transition-all duration-200 hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-theme-surface focus:ring-offset-2">
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center">
|
||||
<x-icon name="printer" class="h-5 w-5" solid />
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-2 text-sm invisible"><!-- Spacer to match reservation count --> </div>
|
||||
</div>
|
||||
|
||||
<!-- Reservation Button -->
|
||||
<div>
|
||||
<livewire:reserve-button :post="$post" :wire:key="'reserve-guest-'.$post->id" />
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col md:flex-row justify-between gap-6">
|
||||
<!-- Social Share Buttons + Print -->
|
||||
<div class="md:self-end">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{!! Share::mastodon()
|
||||
->bluesky()
|
||||
->linkedin()
|
||||
->instagram()
|
||||
->facebook()
|
||||
->whatsapp()
|
||||
{{-- ->x() --}}
|
||||
{{-- ->telegram() --}}
|
||||
->text($post->translations->first()->title)
|
||||
->render() !!}
|
||||
<a href="{{ route('post.pdf', ['id' => $post->id]) }}"
|
||||
target="_blank"
|
||||
title="{{ __('Print as PDF') }}"
|
||||
class="inline-flex items-center justify-center rounded-md border border-theme-border bg-theme-surface p-2 transition-all duration-200 hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-theme-surface focus:ring-offset-2">
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center">
|
||||
<x-icon name="printer" class="h-5 w-5" solid />
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-2 text-sm invisible"><!-- Spacer to match reservation count --> </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
<div class="my-12 flex justify-between">
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-guest-layout>
|
||||
@@ -0,0 +1,221 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
{{ $category }}
|
||||
</x-slot>
|
||||
|
||||
<div class="py-0 md:py-12" id="custom-timebank-style">
|
||||
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div class="overflow-hidden bg-theme-background shadow-xl sm:rounded-lg">
|
||||
<div class="border-b border-theme-primary bg-theme-background p-6 sm:px-20 lg:px-32 lg:py-18">
|
||||
<livewire:posts.manage-actions :post="$post" />
|
||||
<div class="mb-2 mt-0 md:mt-6 lg:mt-12">
|
||||
<div class="text-xs lg:text-sm text-theme-secondary">{{ $update }}</div>
|
||||
</div>
|
||||
@if (!$post->category || !str_starts_with($post->category->type ?? '', 'App\Models\ImagePost'))
|
||||
<h2 class="mt-4 lg:mt-8 text-3xl lg:text-5xl font-semibold leading-tight text-theme-primary">
|
||||
{{ $post->translations->first()->title }}
|
||||
</h2>
|
||||
@endif
|
||||
|
||||
@if ($post->category && $post->category->type === 'App\Models\Article' && $post->author)
|
||||
<div class="mt-6 flex items-center space-x-4 cursor-pointer hover:opacity-80"
|
||||
onclick="window.location='{{ $post->author->profile_url }}'">
|
||||
@if ($post->author->profile_photo_path)
|
||||
<img class="h-9 w-9 rounded-full profile-photo object-cover outline outline-1 outline-offset-1 outline-theme-secondary"
|
||||
src="{{ url(Storage::url($post->author->profile_photo_path)) }}"
|
||||
alt="{{ $post->author->full_name ?? $post->author->name }}">
|
||||
@endif
|
||||
<span class="text-lg">
|
||||
{{ $post->author->full_name ?? $post->author->name }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (isset($post->meeting->from))
|
||||
<h3 class="mt-4 lg:mt-8 text-xl lg:text-4xl font-semibold leading-tight text-theme-primary">
|
||||
{{ ucfirst(Illuminate\Support\Carbon::parse($post->meeting->from)->isoFormat('dddd D MMMM, HH:mm')) }}{{ $post->meeting->till ? ' - ' . Illuminate\Support\Carbon::parse($post->meeting->till)->format('H:i') : '' }} {{ __('h.') }}
|
||||
</h3>
|
||||
@endif
|
||||
@if (isset($post->translations->first()->excerpt))
|
||||
<div class="mt-6">
|
||||
<div class="px-0 py-2 text-xl lg:text-2xl leading-normal lg:leading-loose font-bold text-theme-primary">
|
||||
{{ $post->translations->first()->excerpt }}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Image --->
|
||||
<div class="my-4 lg:my-12">
|
||||
@if ($media != null)
|
||||
{{ $media('hero', ['class' => 'w-full h-auto']) }}
|
||||
@php
|
||||
$locale = App::getLocale();
|
||||
$imageCaption = $media->getCustomProperty('caption-' . $locale);
|
||||
|
||||
// Fallback to other locales if caption not found
|
||||
if (!$imageCaption) {
|
||||
$fallbackLocales = array_keys(config('laravellocalization.supportedLocales'));
|
||||
foreach ($fallbackLocales as $fallbackLocale) {
|
||||
$imageCaption = $media->getCustomProperty('caption-' . $fallbackLocale);
|
||||
if ($imageCaption) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$imageOwner = $media->getCustomProperty('owner');
|
||||
$captionParts = array_filter([$imageCaption, $imageOwner]);
|
||||
$captionText = implode(' ', $captionParts);
|
||||
@endphp
|
||||
@if ($captionText)
|
||||
<div class="mt-1 lg:mt-2 text-right text-3xs lg:text-xs text-theme-secondary">
|
||||
{{ $captionText }}
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if (isset($post->translations->first()->content))
|
||||
{{-- Post content with list styling --}}
|
||||
<div class="post-content mt-2 lg:mt-6 mb-12 lg:mb-16 lg:mx-32 text-base lg:text-lg leading-relaxed md:leading-loose lg:leading-loose text-theme-primary">
|
||||
{!! \App\Helpers\StringHelper::sanitizeHtml($post->translations->first()->content) !!}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (isset($post->meeting))
|
||||
<div class="my-12 bg-theme-surface rounded-lg p-6 md:mx-32 border">
|
||||
<table class="w-full">
|
||||
<tbody>
|
||||
|
||||
@if(isset($post->meeting->price))
|
||||
<tr>
|
||||
<td class="py-2 pr-4 font-semibold text-theme-primary w-1/3">{{ __('Price') }}</td>
|
||||
<td class="py-2 text-theme-primary">
|
||||
@php $txType = strtolower($post->meeting->transactionType->name ?? ''); @endphp
|
||||
@if($txType == 'work')
|
||||
{{ $post->meeting->price === 0 ? __('Free') : tbFormat($post->meeting->price) }}
|
||||
( {{ __('messages.posts.based_on_quantity', ['nr'=> $post->meeting->based_on_quantity]) }} )
|
||||
@elseif($txType == 'gift')
|
||||
{{ __('messages.posts.transaction_types.gift') }}
|
||||
@elseif($txType == 'donation')
|
||||
{{ __('messages.posts.transaction_types.donation') }}
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if($post->meeting->venue)
|
||||
<tr>
|
||||
<td class="py-2 pr-4 font-semibold text-theme-primary w-1/3">{{ __('Location') }}</td>
|
||||
<td class="py-2 text-theme-primary">{{ $post->meeting->venue }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if($post->meeting->address)
|
||||
<tr>
|
||||
<td class="py-2 pr-4 font-semibold text-theme-primary w-1/3">{{ __('Address') }}</td>
|
||||
<td class="py-2 text-theme-primary">
|
||||
<a href="https://www.openstreetmap.org/search?query={{ urlencode($post->meeting->address) }}"
|
||||
target="_blank"
|
||||
class="underline hover:text-theme-secondary">
|
||||
{{ $post->meeting->address }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if($post->meeting->from)
|
||||
<tr>
|
||||
<td class="py-2 pr-4 font-semibold text-theme-primary w-1/3">{{ __('Date & Time') }}</td>
|
||||
<td class="py-2 text-theme-primary">{{ ucfirst(Illuminate\Support\Carbon::parse($post->meeting->from)->isoFormat('dddd D MMMM YYYY, H:mm')) }}{{ $post->meeting->till ? ' - ' . Illuminate\Support\Carbon::parse($post->meeting->till)->format('H:i') : '' }} {{ __('h.') }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@php $organizer = $post->meeting->meetingable ?? null; @endphp
|
||||
@if($organizer && $organizer->name)
|
||||
<tr>
|
||||
<td class="py-2 pr-4 font-semibold text-theme-primary w-1/3">{{ __('Organizer') }}</td>
|
||||
<td class="py-2 text-theme-primary">
|
||||
<a href="{{ $organizer->profile_url ?? '#' }}"
|
||||
class="flex items-center space-x-3 hover:opacity-80 cursor-pointer">
|
||||
@if($organizer->profile_photo_path)
|
||||
<img src="{{ url(Storage::url($organizer->profile_photo_path)) }}"
|
||||
class="w-10 h-10 rounded-full profile-photo object-cover outline outline-1 outline-offset-1 outline-theme-secondary">
|
||||
@endif
|
||||
<span>{{ $organizer->name }}</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="my-12 flex flex-col md:flex-row justify-between gap-6">
|
||||
<!-- Social Share Buttons + Print -->
|
||||
<div class="md:self-end">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{!! Share::mastodon()
|
||||
->bluesky()
|
||||
->linkedin()
|
||||
->instagram()
|
||||
->facebook()
|
||||
->whatsapp()
|
||||
{{-- ->x() --}}
|
||||
{{-- ->telegram() --}}
|
||||
->text($post->translations->first()->title)
|
||||
->render() !!}
|
||||
<a href="{{ route('post.pdf', ['id' => $post->id]) }}"
|
||||
target="_blank"
|
||||
title="{{ __('Print as PDF') }}"
|
||||
class="inline-flex items-center justify-center rounded-md border border-theme-border bg-theme-surface p-2 transition-all duration-200 hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-theme-surface focus:ring-offset-2">
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center">
|
||||
<x-icon name="printer" class="h-5 w-5" solid />
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-2 text-sm invisible"><!-- Spacer to match reservation count --> </div>
|
||||
</div>
|
||||
|
||||
<!-- Reservation Button -->
|
||||
<div>
|
||||
<livewire:reserve-button :post="$post" :wire:key="'reserve-'.$post->id" />
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col md:flex-row justify-between gap-6">
|
||||
<!-- Social Share Buttons + Print -->
|
||||
<div class="md:self-end">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{!! Share::mastodon()
|
||||
->bluesky()
|
||||
->linkedin()
|
||||
->instagram()
|
||||
->facebook()
|
||||
->whatsapp()
|
||||
{{-- ->x() --}}
|
||||
{{-- ->telegram() --}}
|
||||
->text($post->translations->first()->title)
|
||||
->render() !!}
|
||||
<a href="{{ route('post.pdf', ['id' => $post->id]) }}"
|
||||
target="_blank"
|
||||
title="{{ __('Print as PDF') }}"
|
||||
class="inline-flex items-center justify-center rounded-md border border-theme-border bg-theme-surface p-2 transition-all duration-200 hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-theme-surface focus:ring-offset-2">
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center">
|
||||
<x-icon name="printer" class="h-5 w-5" solid />
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-2 text-sm invisible"><!-- Spacer to match reservation count --> </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
<livewire:posts.manage-actions :post="$post" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
Whitespace-only changes.
Reference in new issue
Block a user