@extends('layouts.dashboard') @section('content') @php /** * "Start from a template" gallery: the platform's active starter designs for one surface as live preview * thumbnails (rendered against the vendor's OWN data via $previewBase), plus a "Start blank" card. * Picking one POSTs its id to $applyUrl, which copies it into a new editable instance the vendor owns. * * The PRINT-MENU gallery mirrors the admin gallery: a double-sided card shows the FRONT and flips (in 3D, * on hover) to the BACK — NOT both sides side by side — and Style + Folds dropdowns narrow the grid. It * lays out three per row. Other surfaces keep their single-thumbnail card. * * @var array $templates design_templates rows (id, name, type, config) * @var string $previewBase URL that renders a config -> a thumbnail (?config=... appended) * @var string $applyUrl POST template_id here to copy + open * @var string $blankUrl start from a blank design * @var string $backUrl @var string $heading @var string $subtitle @var string $previewAspect */ $templates = $templates ?? []; $previewBase = (string) ($previewBase ?? ''); $applyUrl = (string) ($applyUrl ?? ''); $blankUrl = (string) ($blankUrl ?? ''); $aspect = (string) ($previewAspect ?? '4 / 3'); /* PER-CARD SHAPE IS OPT-IN, and only one surface wants it. Giving every card its own aspect sounds right and looks wrong: a grid row is as tall as its tallest card, so one vertical board among widescreen ones — or one portrait sheet among landscape ones — leaves a band of white inside every other card on that row. The QR surfaces ask for it because a landscape card there is a genuinely different product from a portrait one and must read as such; boards and printable menus keep ONE frame per surface and letterbox inside it, which is how they looked before and why they looked even. */ $aspectPerCard = !empty($aspectPerCard); $aspectWide = (string) ($aspectWide ?? $aspect); $aspectTall = (string) ($aspectTall ?? $aspect); // Columns are the caller's call too: three is the default that boards and sheets are laid out for. $tplCols = (int) ($previewCols ?? 3); $tplCols = $tplCols >= 2 && $tplCols <= 4 ? $tplCols : 3; // collect()->first(), not reset(): reset() on a Collection hands back its internal // items array, so $first['type'] was '' and the print gallery silently lost its // Style/Folds filters and its double-sided flip cards. $first = collect($templates)->first(); $isPrint = $first && (($first['type'] ?? '') === 'print_menu'); // Build the card list once: preview src + (print) the two filter axes + the double-sided flag. The style // label is just the template name's prefix ("Fine Dining · Single Page" -> "Fine Dining"), so that filter // needs no config parsing; the fold count comes from the config, since it is what the sheet physically is. $cards = []; $styles = []; $folds = []; foreach ($templates as $tpl) { $double = false; $fold = 0; $cfg = \App\Models\DesignTemplate::config($tpl); // One frame per surface unless the caller opted into per-card shapes (see above). $cardAspect = $aspect; if ($aspectPerCard) { $orient = (string) ($cfg['orientation'] ?? ''); $cardAspect = in_array($orient, ['portrait', 'vertical'], true) ? $aspectTall : $aspectWide; } if (isset($tpl['_preview'])) { $src = (string) $tpl['_preview']; } else { if ($isPrint) { // Keep each template's showcase scope so the card is a CLEAN style preview of the vendor's own // menu (a curated volume), not a crammed dump; applying resets the cap so the real menu is whole. $double = !empty($cfg['doubleSided']); $fold = max(1, min(5, (int) ($cfg['fold'] ?? 1))); } $src = $previewBase . '?config=' . rawurlencode((string) json_encode($cfg, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } $label = trim((string) ($tpl['name'] ?? '')); $style = $isPrint ? trim(explode(' · ', $label)[0] ?? '') : ''; $slug = $style !== '' ? preg_replace('/[^a-z0-9]+/', '-', strtolower($style)) : ''; if ($slug !== '') { $styles[$slug] = $style; } if ($fold > 0) { $folds[$fold] = $fold; } $cards[] = ['id' => (int) ($tpl['id'] ?? 0), 'name' => $label, 'src' => $src, 'double' => $double, 'slug' => $slug, 'fold' => $fold, 'aspect' => $cardAspect]; } ksort($styles); ksort($folds); /* THE FILTERS NARROW THE LIBRARY, NOT THE PAGE — so they have to run BEFORE the slice below. Both axes are built from every template (the loop above), so the dropdowns offer the whole library; the grid, however, is paginated nine at a time. Hiding cards in the DOM could therefore never reveal a match that sits on page 2 — "Ocean Navy" showed 1 of its 5, and picking fold 2, an option the page itself offers, emptied the gallery while four two-fold sheets existed. Filtering here instead means the grid, the "showing X of Y" count and the pager all describe the same set, and pagination.php already carries these parameters through its page links. Anything not on the offered axes falls back to "all", so a hand-typed value can only ever widen. */ $fStyle = trim((string) (is_scalar($st = request()->query('style')) ? $st : '')); $fFold = (int) request()->query('fold', 0); if (!isset($styles[$fStyle])) { $fStyle = ''; } if (!isset($folds[$fFold])) { $fFold = 0; } if ($fStyle !== '' || $fFold > 0) { $cards = array_values(array_filter( $cards, static fn ($c) => ($fStyle === '' || $c['slug'] === $fStyle) && ($fFold === 0 || $c['fold'] === $fFold) )); } @endphp

{{ $heading ?? '' }}

{{ $subtitle ?? '' }}

{{ t_raw('common.back') }}
@if ($isPrint && (count($styles) > 1 || count($folds) > 1)) @php // Two axes, because a vendor shops on both: STYLE is how it looks, FOLD is what it physically is. @endphp
@if (count($styles) > 1) @endif @if (count($folds) > 1) @endif
@endif @php /* * Nine per page. * * Every card is a LIVE iframe rendering a real sheet, so an unbounded grid made this page * enormous — two dozen documents all rendering before the vendor can pick one. Nine keeps it * to three rows on a laptop and reuses the pager every list page already has, so no new * pagination style is introduced. * * The "start blank" tile belongs to page one only, so it does not repeat down the pages. */ /* paginate(), not a hand-rolled slice: the footer below renders table-toolbar, whose rows-per-page picker writes ?per_page= and expects paginate() to read it back off the whitelist. This computed its own page metadata instead, so the picker moved the URL and nothing else — and, offering the default 10 / 20 / 50 / 100, it also SAID "10" for a grid that pages at nine. The sizes are whole rows of this gallery's own column count, so every choice still fills its rows evenly. */ $perPage = $tplCols * 3; // three whole rows, whatever the column count is $pager = paginate(count($cards), $perPage, true, '', [$tplCols * 3, $tplCols * 6, $tplCols * 9]); $cards = array_slice($cards, $pager['offset'], $pager['perPage']); $pages = (int) $pager['pages']; $pageNo = (int) $pager['page']; @endphp
@if ($pageNo === 1)
{{ t_raw('vendor.templates.start_blank') }}
@endif @foreach ($cards as $c)
0 ? ' data-fold="' . (int) $c['fold'] . '"' : '' !!}>
@if ($c['double'])
@else @endif
{{ $c['name'] }}
@csrf
@endforeach
@php // The same pager as every list page — counts plus page links, nothing bespoke. @endphp @if ($pages > 1) @partial('table-toolbar', ['pager' => $pager]) @endif @if ($isPrint && (count($styles) > 1 || count($folds) > 1)) @endif @endsection