Files
ladill-give/resources/views/qr-codes/partials/qr-preview-card.blade.php
T
isaaccladandCursor 0860b08af7 Initial Ladill Give extraction — online giving pages at give.ladill.com.
Donation checkout via Ladill Pay (9% fee), church/giving QR pages, SSO, and platform scan forwarding.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 07:25:49 +00:00

189 lines
14 KiB
PHP

@php
$showDownloads = $showDownloads ?? true;
@endphp
<div class="overflow-hidden rounded-2xl border border-slate-200/80 bg-white shadow-sm">
{{-- QR display area with gradient background --}}
<div class="relative flex items-center justify-center bg-gradient-to-br from-indigo-50/60 via-white to-violet-50/40 px-8 py-10">
{{-- Spinner overlay --}}
<div x-show="previewLoading" x-cloak
class="absolute inset-0 z-10 flex items-center justify-center bg-white/60 backdrop-blur-[2px]">
<svg class="h-8 w-8 animate-spin text-indigo-500" fill="none" viewBox="0 0 24 24">
<circle class="opacity-20" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3"/>
<path class="opacity-80" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
</svg>
</div>
{{-- Floating QR card --}}
<div class="w-full max-w-xs transition-all duration-300"
:class="previewLoading ? 'opacity-40 scale-[0.97]' : 'opacity-100 scale-100'">
<div class="overflow-hidden rounded-2xl bg-white shadow-2xl"
:style="frameStyle === 'thin'
? 'box-shadow: 0 0 0 2px ' + frameColor + ', 0 25px 50px -12px rgb(0 0 0 / 0.25)'
: 'box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25), 0 0 0 1px rgb(0 0 0 / 0.06)'">
{{-- Frame wrapper: adds padding + CTA for label/pill frames --}}
<div :class="{
'p-[8px]': frameStyle === 'thin',
'p-[14px]': frameStyle === 'scan_me' || frameStyle === 'tap_to_scan',
'p-4': frameStyle === 'none',
}">
{{-- QR code area SVG renderer only, no static fallback --}}
<div x-ref="qrPreview"
class="aspect-square w-full [&>svg]:block [&>svg]:h-full [&>svg]:w-full"></div>
{{-- Scan me label --}}
<div x-show="frameStyle === 'scan_me'" x-cloak
:style="'border-top: 1px solid ' + frameColor + '40; color: ' + frameColor"
class="pt-2.5 pb-0.5 text-center">
<span x-text="(frameText && frameText.trim() ? frameText : 'SCAN ME').toUpperCase()"
class="text-[11px] font-bold tracking-[0.18em]"></span>
</div>
{{-- Tap to scan pill --}}
<div x-show="frameStyle === 'tap_to_scan'" x-cloak
class="mt-2.5 flex justify-center pb-0.5">
<span x-text="(frameText && frameText.trim() ? frameText : 'TAP TO SCAN').toUpperCase()"
:style="'background-color: ' + frameColor + '; color: ' + frameTextColor"
class="rounded-full px-5 py-1.5 text-[10px] font-bold tracking-widest"></span>
</div>
</div>
</div>
</div>
</div>
{{-- Scanability score --}}
<div class="border-t border-slate-100 px-5 py-4">
<div class="flex items-center justify-between">
<span class="text-[10px] font-semibold uppercase tracking-[0.1em] text-slate-400">Scan quality</span>
<span class="text-xs font-bold tabular-nums"
:class="{
'text-emerald-600': scanability.level === 'excellent',
'text-amber-500': scanability.level === 'good',
'text-red-500': scanability.level === 'warning',
}"
x-text="scanability.score + '% — ' + scanability.label"></span>
</div>
<div class="mt-2 h-1.5 w-full overflow-hidden rounded-full bg-slate-100">
<div class="h-full rounded-full transition-all duration-300"
:style="'width: ' + scanability.score + '%'"
:class="{
'bg-emerald-500': scanability.level === 'excellent',
'bg-amber-400': scanability.level === 'good',
'bg-red-500': scanability.level === 'warning',
}"></div>
</div>
<template x-if="scanability.warnings.length > 0">
<ul class="mt-2.5 space-y-1.5">
<template x-for="w in scanability.warnings" :key="w">
<li class="flex items-start gap-1.5">
<svg class="mt-px h-3.5 w-3.5 shrink-0 text-amber-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"/>
</svg>
<span class="text-[11px] leading-4 text-amber-700" x-text="w"></span>
</li>
</template>
</ul>
</template>
</div>
{{-- Download + Share buttons --}}
@if($showDownloads && isset($qrCode))
@php
$shareUrl = $qrCode->publicUrl();
$shareEnc = urlencode($shareUrl);
$shareText = urlencode($qrCode->label . ' — scan my QR code');
@endphp
<div class="border-t border-slate-100 bg-slate-50/50 px-6 py-5">
<p class="mb-3 text-[10px] font-semibold uppercase tracking-[0.1em] text-slate-400">Download &amp; Share</p>
<div class="grid grid-cols-4 gap-2">
<a href="{{ route('events.download', [$qrCode, 'png']) }}"
class="group flex flex-col items-center gap-1.5 rounded-xl border border-slate-200 bg-white py-3.5 text-center transition hover:border-indigo-300 hover:bg-indigo-50">
<svg class="h-4 w-4 text-slate-400 transition group-hover:text-indigo-500" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12M12 16.5V3"/>
</svg>
<span class="text-xs font-semibold text-slate-600 transition group-hover:text-indigo-700">PNG</span>
</a>
<a href="{{ route('events.download', [$qrCode, 'svg']) }}"
class="group flex flex-col items-center gap-1.5 rounded-xl border border-slate-200 bg-white py-3.5 text-center transition hover:border-violet-300 hover:bg-violet-50">
<svg class="h-4 w-4 text-slate-400 transition group-hover:text-violet-500" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21 3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5"/>
</svg>
<span class="text-xs font-semibold text-slate-600 transition group-hover:text-violet-700">SVG</span>
</a>
<a href="{{ route('events.download', [$qrCode, 'pdf']) }}"
class="group flex flex-col items-center gap-1.5 rounded-xl border border-slate-200 bg-white py-3.5 text-center transition hover:border-rose-300 hover:bg-rose-50">
<svg class="h-4 w-4 text-slate-400 transition group-hover:text-rose-500" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"/>
</svg>
<span class="text-xs font-semibold text-slate-600 transition group-hover:text-rose-700">PDF</span>
</a>
{{-- Share button with popover --}}
<div class="relative" x-data="{ open: false, copied: false }">
<button type="button"
@click="open = !open"
:class="open ? 'border-sky-300 bg-sky-50' : 'border-slate-200 bg-white hover:border-sky-300 hover:bg-sky-50'"
class="group flex w-full flex-col items-center gap-1.5 rounded-xl border py-3.5 text-center transition">
<svg class="h-4 w-4 transition" :class="open ? 'text-sky-500' : 'text-slate-400 group-hover:text-sky-500'" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.933-2.185 2.25 2.25 0 0 0-3.933 2.185Z"/>
</svg>
<span class="text-xs font-semibold transition" :class="open ? 'text-sky-700' : 'text-slate-600 group-hover:text-sky-700'">Share</span>
</button>
<div x-show="open" x-cloak
@click.outside="open = false"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute bottom-full right-0 z-50 mb-2 w-52 origin-bottom-right rounded-xl border border-slate-200 bg-white p-1.5 shadow-xl">
<a href="https://wa.me/?text={{ $shareText }}%20{{ $shareEnc }}" target="_blank" rel="noopener"
@click="open = false"
class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-slate-700 transition hover:bg-slate-50">
<svg class="h-4 w-4 shrink-0 text-green-500" fill="currentColor" viewBox="0 0 24 24"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z"/></svg>
WhatsApp
</a>
<a href="https://twitter.com/intent/tweet?url={{ $shareEnc }}&text={{ $shareText }}" target="_blank" rel="noopener"
@click="open = false"
class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-slate-700 transition hover:bg-slate-50">
<svg class="h-4 w-4 shrink-0 text-slate-800" fill="currentColor" viewBox="0 0 24 24"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.748l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
X (Twitter)
</a>
<a href="https://www.facebook.com/sharer/sharer.php?u={{ $shareEnc }}" target="_blank" rel="noopener"
@click="open = false"
class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-slate-700 transition hover:bg-slate-50">
<svg class="h-4 w-4 shrink-0 text-blue-600" fill="currentColor" viewBox="0 0 24 24"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
Facebook
</a>
<div class="my-1 border-t border-slate-100"></div>
<button type="button"
@click="navigator.clipboard.writeText('{{ $shareUrl }}').then(() => { copied = true; setTimeout(() => { copied = false; open = false }, 1500) })"
class="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition hover:bg-slate-50"
:class="copied ? 'text-emerald-600' : 'text-slate-700'">
<svg x-show="!copied" class="h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"/></svg>
<svg x-show="copied" x-cloak class="h-4 w-4 shrink-0 text-emerald-500" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg>
<span x-text="copied ? 'Copied!' : 'Copy link'">Copy link</span>
</button>
</div>
</div>
</div>
</div>
@endif
</div>