Files
ladill-pos/resources/views/pos/settings.blade.php
T
isaacclad 600aedb59d
Deploy Ladill POS / deploy (push) Successful in 31s
Email digital receipts, add promo settings, and apply tips at charge.
Send real receipt emails from the customer display and sale page, store
branch promo content for the idle screen, and fold selected tips into totals.
2026-07-15 16:10:20 +00:00

236 lines
18 KiB
PHP

<x-app-layout title="Settings">
<x-settings.page description="Configure this branch, manage locations, and invite team members.">
<x-settings.card title="This branch" description="Register name, currency, service style, and receipt options for {{ $location->name }}.">
<form method="POST" action="{{ route('pos.settings.update') }}" enctype="multipart/form-data" class="space-y-4">
@csrf
@method('PUT')
<div>
<label for="name" class="text-sm font-medium text-slate-700">Register name</label>
<input type="text" id="name" name="name" value="{{ old('name', $location->name) }}" required
class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div>
<label for="currency" class="text-sm font-medium text-slate-700">Currency</label>
<input type="text" id="currency" name="currency" value="{{ old('currency', $location->currency) }}" maxlength="3" required
class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm uppercase shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div>
<label for="service_style" class="text-sm font-medium text-slate-700">Service style</label>
<select id="service_style" name="service_style"
class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option value="retail" @selected(old('service_style', $location->service_style) === 'retail')>Retail quick register checkout</option>
<option value="restaurant" @selected(old('service_style', $location->service_style) === 'restaurant')>Restaurant / café tables, tabs & kitchen display</option>
</select>
<p class="mt-1 text-xs text-slate-400">Restaurant mode adds the Floor and Kitchen screens to the sidebar.</p>
</div>
<div>
<label for="receipt_footer" class="text-sm font-medium text-slate-700">Receipt footer</label>
<textarea id="receipt_footer" name="receipt_footer" rows="3"
class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">{{ old('receipt_footer', $location->receipt_footer) }}</textarea>
</div>
<div class="border-t border-slate-100 pt-4">
<p class="text-sm font-semibold text-slate-900">Receipt & printer</p>
<p class="mt-1 text-xs text-slate-400">Thermal receipt layout for browser print (58mm or 80mm paper). USB barcode scanners work as keyboard wedges on the register.</p>
</div>
<div>
<label for="receipt_header" class="text-sm font-medium text-slate-700">Receipt header</label>
<input type="text" id="receipt_header" name="receipt_header" maxlength="500"
value="{{ old('receipt_header', $location->receipt_header) }}"
placeholder="Your business name or tagline"
class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div>
<label class="text-sm font-medium text-slate-700">Receipt logo</label>
<p class="mt-0.5 text-xs text-slate-400">Optional. Shown at the top of printed receipts. A square PNG or JPG works best on thermal paper.</p>
<div class="mt-3 flex items-center gap-4">
@if ($location->receipt_logo_path)
<img src="{{ $location->receiptLogoUrl() }}?v={{ $location->updated_at?->timestamp }}"
alt="Current receipt logo"
class="h-16 w-16 rounded-xl border border-slate-200 bg-white object-contain p-1">
@else
<div class="flex h-16 w-16 items-center justify-center rounded-xl border border-dashed border-slate-300 text-[10px] text-slate-400">No logo</div>
@endif
<div class="min-w-0 flex-1 space-y-2">
<input type="file" name="receipt_logo" accept="image/png,image/jpeg,image/gif,image/webp"
class="block w-full text-sm text-slate-600 file:mr-3 file:rounded-lg file:border-0 file:bg-indigo-50 file:px-3 file:py-1.5 file:text-sm file:font-medium file:text-indigo-700 hover:file:bg-indigo-100">
@if ($location->receipt_logo_path)
<label class="inline-flex items-center gap-2 text-xs text-slate-500">
<input type="checkbox" name="remove_receipt_logo" value="1" class="rounded border-slate-300 text-indigo-600">
Remove current logo
</label>
@endif
</div>
</div>
</div>
<div>
<label for="printer_paper_mm" class="text-sm font-medium text-slate-700">Receipt paper width</label>
<select id="printer_paper_mm" name="printer_paper_mm"
class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option value="80" @selected((int) old('printer_paper_mm', $location->printer_paper_mm ?? 80) === 80)>80 mm (standard thermal)</option>
<option value="58" @selected((int) old('printer_paper_mm', $location->printer_paper_mm ?? 80) === 58)>58 mm (compact thermal)</option>
</select>
</div>
<label class="flex items-start gap-3 rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
<input type="checkbox" name="printer_auto_print" value="1"
@checked(old('printer_auto_print', $location->printer_auto_print))
class="mt-0.5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
<span>
<span class="block text-sm font-medium text-slate-800">Auto-print after cash sales</span>
<span class="mt-0.5 block text-xs text-slate-500">Opens the receipt print dialog when you record a cash payment.</span>
</span>
</label>
<div class="border-t border-slate-100 pt-4">
<p class="text-sm font-semibold text-slate-900">Customer screen promo</p>
<p class="mt-1 text-xs text-slate-400">Shown on the dual-screen customer display when the cart is idle. Leave blank to use the branch name and a default thank-you message.</p>
</div>
<div>
<label for="customer_promo_headline" class="text-sm font-medium text-slate-700">Promo headline</label>
<input type="text" id="customer_promo_headline" name="customer_promo_headline" maxlength="160"
value="{{ old('customer_promo_headline', $location->customer_promo_headline) }}"
placeholder="{{ $location->name }}"
class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div>
<label for="customer_promo_body" class="text-sm font-medium text-slate-700">Promo message</label>
<textarea id="customer_promo_body" name="customer_promo_body" rows="2" maxlength="500"
placeholder="Thank you for shopping with us."
class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">{{ old('customer_promo_body', $location->customer_promo_body) }}</textarea>
</div>
<div>
<label class="text-sm font-medium text-slate-700">Promo image</label>
<p class="mt-0.5 text-xs text-slate-400">Optional. Falls back to the receipt logo when empty.</p>
<div class="mt-3 flex items-center gap-4">
@if ($location->customer_promo_image_path || $location->receipt_logo_path)
<img src="{{ $location->customerPromoImageUrl() }}?v={{ $location->updated_at?->timestamp }}"
alt="Promo preview"
class="h-16 w-16 rounded-xl border border-slate-200 bg-white object-contain p-1">
@else
<div class="flex h-16 w-16 items-center justify-center rounded-xl border border-dashed border-slate-300 text-[10px] text-slate-400">No image</div>
@endif
<div class="min-w-0 flex-1 space-y-2">
<input type="file" name="customer_promo_image" accept="image/png,image/jpeg,image/gif,image/webp"
class="block w-full text-sm text-slate-600 file:mr-3 file:rounded-lg file:border-0 file:bg-indigo-50 file:px-3 file:py-1.5 file:text-sm file:font-medium file:text-indigo-700 hover:file:bg-indigo-100">
@if ($location->customer_promo_image_path)
<label class="inline-flex items-center gap-2 text-xs text-slate-500">
<input type="checkbox" name="remove_customer_promo_image" value="1" class="rounded border-slate-300 text-indigo-600">
Remove promo image
</label>
@endif
</div>
</div>
</div>
<div class="border-t border-slate-100 pt-4">
<p class="text-sm font-semibold text-slate-900">Payment gateway</p>
<p class="mt-1 text-xs text-slate-400">Connect Paystack, Flutterwave, or Hubtel. Online checkouts go 100% to you 0% Ladill fee.</p>
</div>
<div>
<label class="text-sm font-medium text-slate-700">Provider</label>
<select name="gateway_provider" class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option value="">Select a provider</option>
<option value="paystack" @selected(old('gateway_provider', $gateway?->provider ?? null) === 'paystack')>Paystack</option>
<option value="flutterwave" @selected(old('gateway_provider', $gateway?->provider ?? null) === 'flutterwave')>Flutterwave</option>
<option value="hubtel" @selected(old('gateway_provider', $gateway?->provider ?? null) === 'hubtel')>Hubtel</option>
</select>
</div>
<div class="grid gap-4 sm:grid-cols-2">
<div>
<label class="text-sm font-medium text-slate-700">Public key / Merchant account</label>
<input type="text" name="gateway_public_key" value="{{ old('gateway_public_key') }}" placeholder="{{ ($gateway?->public_key ?? null) ? '•••• saved — leave blank to keep' : 'pk_live_… or Hubtel account number' }}" class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div>
<label class="text-sm font-medium text-slate-700">Secret / API key</label>
<input type="password" name="gateway_secret_key" value="" placeholder="{{ ($gateway?->secret_key ?? null) ? '•••• saved — leave blank to keep' : 'sk_live_…' }}" class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500" autocomplete="new-password">
</div>
</div>
<div>
<label class="text-sm font-medium text-slate-700">Webhook secret (optional)</label>
<input type="password" name="gateway_webhook_secret" value="" placeholder="{{ ($gateway?->webhook_secret ?? null) ? '•••• saved — leave blank to keep' : 'Optional' }}" class="mt-1.5 block w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500" autocomplete="new-password">
</div>
<label class="flex items-start gap-3 rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
<input type="hidden" name="gateway_is_active" value="0">
<input type="checkbox" name="gateway_is_active" value="1" @checked(old('gateway_is_active', $gateway?->is_active ?? true)) class="mt-0.5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
<span>
<span class="block text-sm font-medium text-slate-800">Gateway enabled</span>
<span class="mt-0.5 block text-xs text-slate-500">Required for card / MoMo checkouts on the register.</span>
</span>
</label>
@if ($gateway?->isConfigured())
<p class="rounded-xl bg-emerald-50 px-3 py-2 text-sm text-emerald-800">Gateway connected ({{ ucfirst($gateway->provider) }}).</p>
@else
<p class="rounded-xl bg-amber-50 px-3 py-2 text-sm text-amber-800">Online checkouts stay disabled until a gateway is connected.</p>
@endif
<div class="flex justify-end">
<button type="submit" class="btn-primary">Save settings</button>
</div>
</form>
</x-settings.card>
<div class="mt-6 space-y-6">
@include('pos.settings._branches-section')
@if (! empty($canManageTeam))
@include('pos.settings._team-section')
@endif
</div>
@if ($location->isRestaurant())
<x-settings.card title="Import catalog" description="Seed your local POS catalog from CRM or Merchant storefronts. (Retail mode reads products live from CRM, so no import is needed.)">
<div class="flex flex-wrap gap-3">
<form method="POST" action="{{ route('pos.settings.import-crm') }}">
@csrf
<button type="submit" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">
Import from CRM
</button>
</form>
@if ($merchantImportEnabled)
<form method="POST" action="{{ route('pos.settings.import-merchant') }}">
@csrf
<button type="submit" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">
Import from Merchant
</button>
</form>
@endif
</div>
</x-settings.card>
<x-settings.card title="Tables" description="Dine-in tables shown on the Floor screen.">
<form method="POST" action="{{ route('pos.settings.tables.store') }}" class="grid gap-2 sm:grid-cols-[1fr_1fr_5rem_auto]">
@csrf
<input type="text" name="area" placeholder="Area (e.g. Patio)"
class="rounded-xl border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
<input type="text" name="label" placeholder="Label (e.g. T1)" required
class="rounded-xl border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
<input type="number" name="seats" value="2" min="1" max="99" placeholder="Seats"
class="rounded-xl border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
<button type="submit" class="btn-primary">Add</button>
</form>
@if ($tables->isNotEmpty())
<ul class="mt-4 divide-y divide-slate-100">
@foreach ($tables as $table)
<li class="flex items-center justify-between py-2.5 text-sm">
<span>
<span class="font-medium text-slate-900">{{ $table->label }}</span>
<span class="text-slate-400">· {{ $table->area ?: 'Floor' }} · {{ $table->seats }} seats · {{ $table->isFree() ? 'free' : 'occupied' }}</span>
</span>
<div class="flex items-center gap-3">
<a href="{{ route('pos.tables.qr', $table) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">QR</a>
<form method="POST" action="{{ route('pos.settings.tables.destroy', $table) }}"
onsubmit="return confirm('Remove this table?');">
@csrf
@method('DELETE')
<button class="text-sm font-medium text-red-500 hover:text-red-700">Remove</button>
</form>
</div>
</li>
@endforeach
</ul>
@endif
</x-settings.card>
@endif
</x-settings.page>
</x-app-layout>