@extends('layouts.dashboard') @section('content') @php /** * Vendor → Tables → QR → Design card. One printable design (a FRONT and a BACK) applied to every * table; the scan code on each printed card stays that table's own. Built on the SAME components as the * TV screen editor (tv.css chrome, the 3x3 position grid, the overlay switches), a background presets * grid, plus the app's standard FilePond uploader for a custom background. * * Layout: a full-width settings card, then a balanced two-card row — the controls card (a Front/Back * tab bar + the active side's controls) beside the live-preview card. * * @var array $config the saved (or default) QrCard design * @var array $creatives background presets [ ['image'=>path], ... ] * @var array $categories menu categories @var array $items available menu items */ use App\Models\QrCard; $config = $config ?? QrCard::defaultConfig(); $sides = ['front' => $config['front'], 'back' => $config['back']]; // This designer is shared: the SAME controls edit either the table-ordering card or the payment card // (its QR encodes the vendor's own static pay link). $type switches the copy + the pay-link field. $type = (($type ?? 'table') === 'payment') ? 'payment' : 'table'; $isPay = $type === 'payment'; // Payment is MANY named cards: $payCardId 0 = a new card, >0 = editing that card. The table card is // always singular. $isNewPay drives where the link field lives (create form vs its own Save-link form). $payCardId = (int) ($payCardId ?? 0); $payName = (string) ($payName ?? ''); $isNewPay = $isPay && $payCardId === 0; // Template mode: the admin authors a platform card template with this same designer. Values default to // the vendor's own flow, so a normal vendor edit is unchanged. $tplMode = $tplMode ?? false; $tplType = $tplType ?? ($isPay ? 'qr_pay' : 'qr_card'); $tplId = (int) ($tplId ?? 0); $backUrl = $backUrl ?? route('vendor.tables.qr'); $formAction = $formAction ?? ($isPay ? route('vendor.tables.qr.pay.post') : route('vendor.tables.qr.design')); $hasTableTemplates = $hasTableTemplates ?? false; $jsData = [ 'previewUrl' => $previewUrl ?? route('vendor.tables.qr.design.preview'), 'type' => $type, 'card' => [ 'orientation' => $config['orientation'], 'paper' => $config['paper'], 'cornerTl' => (int) $config['cornerTl'], 'cornerTr' => (int) $config['cornerTr'], 'cornerBr' => (int) $config['cornerBr'], 'cornerBl' => (int) $config['cornerBl'], 'theme' => $config['theme'], 'font' => $config['font'], 'accent' => $config['accent'], 'bgColor' => $config['bgColor'], 'poweredBy' => (bool) $config['poweredBy'], ], 'front' => $sides['front'], 'back' => $sides['back'], 'themeBg' => QrCard::THEME_BG, 'papers' => QrCard::PAPERS, 'i18n' => ['cancel' => t_raw('vendor.qr_card.crop_cancel'), 'apply' => t_raw('vendor.qr_card.crop_apply')], ]; /** One 3x3 position grid (the exact control the TV editor uses), current anchor pre-marked. */ $posGrid = static function (string $field, string $cur): string { $out = '
'; foreach (QrCard::POSITIONS as $p) { $out .= ''; } return $out . '
'; }; /** One labelled switch row (reusing the dashboard switch component). */ $switch = static function (string $field, bool $on, string $label): string { return ''; }; $bgHex = $config['bgColor'] !== '' ? $config['bgColor'] : (QrCard::THEME_BG[$config['theme']] ?? '#f4f1ec'); // The second stop a gradient ink opens on: the card's own accent when it clears the contrast floor, // otherwise the default near-black — so switching to a gradient never seeds a colour the server would // then refuse. The floor itself stays in QrCard, the one place it is enforced. $inkSeed = QrCard::inkError((string) $config['accent']) === '' ? strtolower((string) $config['accent']) : '#141210'; /** One side's stored qrInk split into its editor state: the mode plus the two swatch values. */ $inkState = static function (array $s) use ($inkSeed, $__env): array { $stops = array_values(array_filter(array_map('trim', explode(',', (string) ($s['qrInk'] ?? ''))))); return [ 'mode' => count($stops) === 2 ? 'gradient' : (count($stops) === 1 ? 'solid' : 'default'), 'a' => $stops[0] ?? '#141210', 'b' => $stops[1] ?? $inkSeed, ]; }; @endphp @php /* .qm-d2: the dashboard density standard (app.css §35) */ @endphp

{{ t_raw($isPay ? 'vendor.qr_card.pay_form_heading' : 'vendor.qr_card.form_heading') }}

{{ t_raw($isPay ? 'vendor.qr_card.pay_form_sub' : 'vendor.qr_card.form_sub') }}

@if ($isPay && !$isNewPay)
@csrf

{{ t_raw('vendor.qr_card.pay_details_label') }}

@endif
@csrf @if ($tplMode) @else @if ($isPay)@endif @endif @if ($tplMode && !$isPay) @php // a table-card TEMPLATE needs a name (the singular vendor card has none); @endphp
@endif @if ($isNewPay)

{{ t_raw('vendor.qr_card.pay_details_label') }}

@if (!$tplMode) @php // a template carries no payment link — the vendor adds their own on their copy; @endphp @endif
@endif
@php // Phone/tablet: the same tap-to-open dropdown the Orders filter bar uses (labelled "Settings"). @endphp @partial('filter-collapse', ['id' => 'qrcard-set-open', 'label' => t_raw('common.settings')])
@foreach ($sides as $key => $s) @php $catOf = []; foreach ($items as $it) { $catOf[(int) $it['menu_category_id']][] = $it; }; @endphp

{{ t_raw('vendor.qr_card.bg_label') }}

@php /* Scope follows who is authoring — see the same note on the print-menu background. An admin has no restaurant, so the default /vendor/media endpoint returned nothing: the library looked empty and Change did nothing, because the request behind it had no tenant to read. */ @endphp {!! media_field_html($key . '_bg', (string) ($s['background'] ?? ''), [ 'accept' => 'image', 'allowClear' => true, 'crop' => true, 'endpoint' => ($tplMode ?? false) ? '/admin/media' : '/vendor/media', 'id' => 'qrc-bg-' . $key, ]) !!}
{!! $posGrid('logoPos', (string) ($s['logoPos'] ?? 'top-left')) !!}
{{ (int) ($s['logoSize'] ?? 4) }}
{!! $posGrid('titlePos', (string) ($s['titlePos'] ?? 'top-center')) !!}
{{ (int) ($s['titleSize'] ?? 6) }}

{{ t_raw('vendor.qr_card.product_label') }}

@php /* 'auto' is a stored VALUE, not an absence — "this card features a dish, and whichever restaurant uses it supplies one". A card adopted from a template carries it, so the select has to be able to represent it: without the option it fell back to "0" ("no featured product") and the first save deleted the feature outright. Same fix as the TV board editor. */ $sProduct = $s['product'] ?? 0; $sHasProduct = $sProduct === 'auto' || (int) $sProduct > 0; @endphp
{!! $posGrid('productPos', (string) ($s['productPos'] ?? 'top-center')) !!}
{{ (int) ($s['productSize'] ?? 5) }}
{!! $switch('showName', !empty($s['showName']), t_raw('vendor.screens.overlay_name')) !!} {!! $switch('showDesc', !empty($s['showDesc']), t_raw('vendor.screens.overlay_desc')) !!} {!! $switch('showPrice', !empty($s['showPrice']), t_raw('vendor.screens.overlay_price')) !!}
{!! $posGrid('qrPos', (string) ($s['qrPos'] ?? 'bottom-center')) !!} @php $qrSizeCur = \App\Models\QrCard::qrSize($s['qrSize'] ?? null); @endphp @php $ink = $inkState($s); @endphp
@php // A payment card isn't tied to a table, so the table-number line is hidden for it. @endphp @if (!$isPay){!! $switch('showTable', !empty($s['showTable']), t_raw('vendor.qr_card.qr_table')) !!}@endif {!! $switch('qrLogo', !empty($s['qrLogo']), t_raw('vendor.qr_card.qr_logo')) !!}
@endforeach

{{ t_raw('vendor.screens.preview_label') }}

@foreach ($sides as $key => $s)
@endforeach
@php /* /.qm-d2 */ @endphp @php // JSON_HEX_TAG guards this inline would otherwise break out of the block. @endphp @endsection