@php /** * One printed table-card side (front or back). Each element is optional and — where noted — placed on * the shared 3x3 grid the TV editor uses: * • a full-bleed background image * • the restaurant's own logo (anchored) * • a featured-product overlay (anchored) * • the table's own scan-to-order QR (anchored), with the table number + zone above it and, * optionally, the restaurant logo dropped into its centre * • a footer line (a disclaimer + an optional "powered by" credit) * * Rendered identically by the design editor preview and by the print sheet; only the per-table bits * differ (the QR target and the number/zone). The QR itself is drawn client-side into [data-qr], so * this partial never touches the existing scan-code logic — it only places the code. * * @var array $side One side of the QrCard config. * @var array $card The whole card config (theme/accent/bgColor/orientation/poweredBy). * @var array|null $product Resolved featured dish ['name','desc','price'] or null. * @var string $qrPath Scan path to encode on this side ('' = draw no QR). * @var array $logo The restaurant's brand mark ['src'=>'','initials'=>'','color'=>'']. * @var array $table This table's identity ['label'=>'','zone'=>''] (per-table; a sample in preview). */ $side = $side ?? []; $card = $card ?? []; $product = $product ?? null; $qrPath = (string) ($qrPath ?? ''); $logo = $logo ?? []; $table = $table ?? []; $placeholder = (bool) ($placeholder ?? false); // preview/template mode: show a "background image" slot $theme = ($card['theme'] ?? 'light') === 'dark' ? 'dark' : 'light'; $orient = ($card['orientation'] ?? 'portrait') === 'landscape' ? 'landscape' : 'portrait'; $accent = preg_match('/^#[0-9a-fA-F]{6}$/', (string) ($card['accent'] ?? '')) ? $card['accent'] : '#e11d2a'; $bgColor = (string) ($card['bgColor'] ?? ''); $bgImg = ($side['background'] ?? '') !== '' ? media($side['background']) : ''; $hasBg = $bgImg !== ''; $fontStack = \App\Services\Receipt::fontStack((string) ($card['font'] ?? 'sans')); // The card's aspect ratio matches the chosen paper so it fills the printed sheet exactly. $paper = \App\Models\QrCard::PAPERS[$card['paper'] ?? 'a5'] ?? \App\Models\QrCard::PAPERS['a5']; $ar = $orient === 'landscape' ? ($paper['h'] . ' / ' . $paper['w']) : ($paper['w'] . ' / ' . $paper['h']); // Per-corner radius (0–10 → cqmin), so each corner can round independently or stay a straight 90° cut. $rad = static fn ($n) => round(max(0, min(10, (int) $n)) * 0.8, 2) . 'cqmin'; $borderRad = $rad($card['cornerTl'] ?? 3) . ' ' . $rad($card['cornerTr'] ?? 3) . ' ' . $rad($card['cornerBr'] ?? 3) . ' ' . $rad($card['cornerBl'] ?? 3); $styleVars = '--qrc-accent:' . e($accent) . ';--qrc-font:' . $fontStack . ';aspect-ratio:' . $ar . ';border-radius:' . $borderRad . ';'; if ($bgColor !== '') { $styleVars .= '--qrc-bg:' . e($bgColor) . ';'; } /** One element's offset-nudge style vars (cqw/cqh of the card). */ $off = static fn (string $x, string $y) => '--qrc-ox:' . (int) ($side[$x] ?? 0) . 'cqw;--qrc-oy:' . (int) ($side[$y] ?? 0) . 'cqh;'; $showLogo = !empty($side['logo']); // A design can CARRY a QR without yet having anything to encode: a platform payment-card template (the // pay link is stripped as tenant data), or a vendor's card before they paste their link. Drawing nothing // hid the element entirely, so the author could not see where the code sits or how big it is. Instead the // slot renders as an OUTLINE at its true size — the same language the printable menu uses. $qrWanted = !empty($side['qr']); $showQr = $qrWanted && $qrPath !== ''; $qrOutline = $qrWanted && $qrPath === ''; $tLabel = (string) ($table['label'] ?? ''); $tZone = (string) ($table['zone'] ?? ''); $showTable = !empty($side['showTable']) && ($tLabel !== '' || $tZone !== ''); $footer = mb_substr(trim((string) ($side['footer'] ?? '')), 0, 120); $powered = !empty($card['poweredBy']); $hasContent = $hasBg || $showLogo || $product || $showQr || $qrOutline || $footer !== '' || $powered || trim((string) ($side['title'] ?? '')) !== ''; // 0–10 size sliders → the scale ranges the stylesheet expects. $prodScale = round(0.7 + (max(0, min(10, (int) ($side['productSize'] ?? 5))) / 10) * 1.3, 3); // 0.7–2.0 // QR size uses the printable menu's vocabulary (small | medium | large) so one word means one thing // across every design surface. Legacy 0-10 slider values are bucketed by QrCard::qrSize(). $qrScale = ['small' => 0.75, 'medium' => 1.1, 'large' => 1.5][\App\Models\QrCard::qrSize($side['qrSize'] ?? null)]; $logoScale = round(0.6 + (max(0, min(10, (int) ($side['logoSize'] ?? 4))) / 10) * 1.6, 3); // 0.6–2.2 $titleScale = round(0.6 + (max(0, min(10, (int) ($side['titleSize'] ?? 6))) / 10) * 1.6, 3); // 0.6–2.2 $title = mb_substr(trim((string) ($side['title'] ?? '')), 0, 40); $caption = mb_substr(trim((string) ($side['qrCaption'] ?? '')), 0, 60); // The code's surround and its ink. Both are re-cleaned here rather than trusted, because this partial // also renders a preview built straight from a GET payload. An ink that misses the contrast floor comes // back empty, so the code draws in the product's default near-black instead of an unscannable tint. $qrFrame = in_array($side['qrFrame'] ?? '', \App\Models\QrCard::QR_FRAMES, true) ? (string) $side['qrFrame'] : 'plate'; $qrInk = \App\Models\QrCard::cleanInk((string) ($side['qrInk'] ?? '')); // Error correction, in the order the budget is spent: a centre logo punches a hole in the code (H), and // a gradient is at its palest somewhere along the sweep, so buy the redundancy back there too (Q). A // flat ink has one contrast value everywhere and needs no more than the default M. $qrEc = !empty($side['qrLogo']) ? 'H' : (str_contains($qrInk, ',') ? 'Q' : 'M'); // A table card has no single code to draw while it is being designed, so the QR renders as an outline // (see $qrOutline below). Hand the ink's two stops down as vars so that outline can still show which // colours were chosen — otherwise the ink is invisible on the one surface where it is being picked. $inkStops = $qrInk !== '' ? array_pad(explode(',', $qrInk), 2, '') : []; $inkVars = $inkStops ? '--qrc-ink-a:' . e($inkStops[0]) . ';--qrc-ink-b:' . e($inkStops[1] !== '' ? $inkStops[1] : $inkStops[0]) . ';' : ''; /** The restaurant mark as an when it has a logo, else its branded initials monogram. */ $brandMark = static function (string $cls) use ($logo, $__env) { $src = (string) ($logo['src'] ?? ''); if ($src !== '') { return ''; } return '' . e((string) ($logo['initials'] ?? '')) . ''; }; @endphp
@if ($hasBg)
@elseif ($placeholder)
{{ t('vendor.qr_card.bg_ph') }}
@endif @if ($showLogo) @endif @if ($title !== '')
{{ $title }}
@endif @if ($product)
@if (!empty($side['showName']) && ($product['name'] ?? '') !== '')

{{ $product['name'] }}

@endif @if (!empty($side['showDesc']) && ($product['desc'] ?? '') !== '')

{{ $product['desc'] }}

@endif @if (!empty($side['showPrice']) && ($product['price'] ?? null) !== null) {{ money($product['price']) }} @endif
@endif @if ($showQr || $qrOutline)
@if ($showTable)
@if ($tLabel !== ''){{ $tLabel }}@endif @if ($tZone !== ''){{ $tZone }}@endif
@endif
@if ($qrOutline) @else @if (!empty($side['qrLogo'])) @endif @endif
@if ($caption !== '') {{ $caption }} @endif
@endif @if ($footer !== '' || $powered) @endif @if (!$hasContent)
{{ t_raw('vendor.qr_card.empty_side') }}
@endif