@extends('layouts.public') @section('content') @php /** * Restaurant sign-up: four steps SIDE BY SIDE on one page, one form, one submit. * 1 Your plan - arrives chosen from the pricing page; the other tiers are one click away, the * Monthly/Annual switch shows the annual price against the struck monthly x 12 * 2 Your details - "Continue with Google" (a pop-up; the page stays) fills and locks identity - no * password to invent - or type * 3 Your restaurant - "Find your restaurant on Google" (Places) fills the venue in place; where the * install has Business Profile, "Connect" (a pop-up) lists the owner's listings and * the one picked fills the form; or type. The venue then reads as three sections * that open and close: Restaurant details, Opening hours (the weekly editor), Menu * (the listing's dishes, to tick through) * 4 Payment - the gateway for a paid tier, the agreement, the button * Every Google path is an OFFER beside the fields; the manual form is complete on its own and nothing * Google supplies is saved unreviewed - it lands in the fields, editable, and is validated like typed text. * Every step head carries a check that turns green once the step is complete; the button is live only * when all four are (the server re-validates everything regardless). * * @var array $cuisines Ordered cuisine rows (id, name, slug). * @var array|null $selectedPlan The chosen plan (name, slug, ...), or null when none exist. * @var array $plans Every active plan, for step 1. * @var array $payMethods Gateways that can take this tier's money; empty when it is free. * @var string $selectedCycle monthly | yearly * @var bool $googleOn Sign in with Google is configured on this install. * @var array $googleProfile The Google-verified identity parked for this application ([] = none). * @var bool $placesOn A Google Maps key is present, so listings can be looked up. * @var bool $gbpOn Business Profile connect is configured (or sandboxed). * @var array $gbp A Business Profile connected at sign-up: refresh, locations, picked, details, menu ([] = none). */ $fieldErrors = \App\Support\Session::old('__errors', []); $fieldErrors = is_array($fieldErrors) ? $fieldErrors : []; $err = static function (string $field) use ($fieldErrors, $__env): string { return isset($fieldErrors[$field][0]) ? (string) $fieldErrors[$field][0] : ''; }; $payMethods = (array) ($payMethods ?? []); $plans = (array) ($plans ?? []); $planName = (string) ($selectedPlan['name'] ?? ''); $planValue = (string) ($selectedPlan['name'] ?? ''); $planSlug = (string) ($selectedPlan['slug'] ?? ''); $selectedCycle = (string) ($selectedCycle ?? 'monthly'); $google = (array) ($googleProfile ?? []); $viaGoogle = ($google['email'] ?? '') !== ''; $gbp = (array) ($gbp ?? []); $gbpConnected = ($gbp['refresh'] ?? '') !== ''; $gbpLocations = (array) ($gbp['locations'] ?? []); $gbpPicked = (array) ($gbp['picked'] ?? []); $placesOn = !empty($placesOn); $gbpOn = !empty($gbpOn); $googleOn = !empty($googleOn); // Step 1 rows: name, slug, the amount for each cadence, the monthly x 12 the annual price is struck // against, the cadence suffix, whether the tier sells annually and whether it needs a gateway. // Rendered here (money(), suffixes) so the script only swaps strings. $vsPlans = []; foreach ($plans as $p) { $own = (string) ($p['billing_cycle'] ?? 'monthly'); $price = (float) ($p['price'] ?? 0); $free = $price <= 0; $sells = \App\Models\Subscription::sellsAnnually($p); $vsPlans[] = [ 'name' => (string) $p['name'], 'slug' => (string) $p['slug'], 'sells' => $sells, 'free' => $free, 'monthly' => $free ? t_raw('auth.plan_free') : money(\App\Models\Subscription::priceFor($p, 'monthly')), 'yearly' => $sells ? money(\App\Models\Subscription::priceFor($p, 'yearly')) : '', 'yearly_full' => $sells ? money($price * 12) : '', 'cycle_m' => $free ? '' : ($own === 'lifetime' ? t_raw('vendor.billing.cycle_lifetime_suffix') : ($own === 'yearly' ? t_raw('partner.cycle_yearly') : t_raw('partner.cycle_monthly'))), 'cycle_y' => $sells ? t_raw('partner.cycle_yearly') : '', 'pay' => $free ? ((int) ($p['require_payment_method'] ?? 1) === 1) : $own !== 'lifetime', ]; } $vsCur = null; foreach ($vsPlans as $vp) { if ($vp['slug'] === $planSlug) { $vsCur = $vp; break; } } if ($vsCur === null && $vsPlans) { $vsCur = $vsPlans[0]; $planValue = $vsCur['name']; $planSlug = $vsCur['slug']; } $vsSave = (int) round(\App\Models\Subscription::annualDiscountPercent()); // A connected Business Profile that lists businesses IS the way the venue is entered: the fields open // under the listing picked, already filled, and "type the details" is not offered beside it. $gbpLive = $gbpConnected && $gbpLocations; // What the picked listing said, kept for a reload or a validation bounce; the page's own input wins. $fill = (array) ($gbp['details'] ?? []); $vsVal = static function (string $field, string $key) use ($fill, $__env): string { $own = (string) \App\Support\Session::old($field, ''); return $own !== '' ? $own : (string) ($fill[$key] ?? ''); }; $prefilled = $fill !== [] && (string) \App\Support\Session::old('rest_name', '') === ''; $pre = static function (string $field, string $key) use ($fill, $prefilled, $__env): string { return ($prefilled && (string) \App\Support\Session::old($field, '') === '' && (string) ($fill[$key] ?? '') !== '') ? ' is-prefilled' : ''; }; // Identity fields: a validation bounce's own input first, then the Google profile, then empty. $firstVal = (string) (\App\Support\Session::old('first_name', '') ?: ($google['first'] ?? '')); $lastVal = (string) (\App\Support\Session::old('last_name', '') ?: ($google['last'] ?? '')); $emailVal = $viaGoogle ? (string) $google['email'] : (string) \App\Support\Session::old('email', ''); // Steps 2 and 3 open on the Google offer when there is one; the fields unfold on request - or at once // when there is nothing to offer, when Google already answered, or when a bounce hands input back. $errIn = static function (array $fields) use ($fieldErrors, $__env): bool { foreach ($fields as $f) { if (!empty($fieldErrors[$f])) { return true; } } return false; }; $expand2 = !$googleOn || $viaGoogle || $firstVal !== '' || $emailVal !== '' || (string) \App\Support\Session::old('phone', '') !== '' || $errIn(['first_name', 'last_name', 'email', 'phone', 'password', 'password_confirm']); $offer3 = $placesOn || $gbpOn; $venueErr = $errIn(['rest_name', 'address', 'city', 'state', 'postcode', 'website', 'cuisine_id']); $expand3 = !$offer3 || !empty($gbpPicked) || $prefilled || (string) \App\Support\Session::old('rest_name', '') !== '' || $venueErr; $offerHidden3 = $expand3 && !$gbpLive; // Weekly hours, keyed 0=Sun..6=Sat like the hours table: the page's own input, else the listing's // (Google keys Monday..Sunday 1..7; a day it lists no period for is closed), else the 09:00-22:00 // rota Settings assumes for a day nobody has set. $vsDayKeys = [0 => 'sunday', 1 => 'monday', 2 => 'tuesday', 3 => 'wednesday', 4 => 'thursday', 5 => 'friday', 6 => 'saturday']; $vsDayShort = [0 => 'sun', 1 => 'mon', 2 => 'tue', 3 => 'wed', 4 => 'thu', 5 => 'fri', 6 => 'sat']; $hoursOld = \App\Support\Session::old('hours', []); $hoursOld = is_array($hoursOld) ? $hoursOld : []; $gHours = is_array($fill['hours'] ?? null) ? $fill['hours'] : []; // Hours are OPTIONAL: the rota is saved only when the applicant set it or a listing supplied it; an // untouched editor saves nothing and the venue counts as open until Settings says otherwise. $hoursSet = (string) \App\Support\Session::old('hours_set', '') === '1' || $gHours !== []; $vsHours = []; for ($d = 0; $d < 7; $d++) { $row = ['open' => '09:00', 'close' => '22:00', 'closed' => false, 'google' => false]; if ($gHours) { $g = $gHours[$d === 0 ? 7 : $d] ?? null; $row['google'] = true; if (is_array($g) && $g) { $row['open'] = (string) ($g[0][0] ?? '09:00'); $row['close'] = (string) ($g[count($g) - 1][1] ?? '22:00'); } else { $row['closed'] = true; } } if (is_array($hoursOld[$d] ?? null)) { $row['open'] = ((string) ($hoursOld[$d]['open'] ?? '')) ?: $row['open']; $row['close'] = ((string) ($hoursOld[$d]['close'] ?? '')) ?: $row['close']; $row['closed'] = !empty($hoursOld[$d]['closed']); $row['google'] = false; } $vsHours[$d] = $row; } // The connected listing's menu (rows the pick fetched) and the ticks a bounce handed back. $menuRows = array_values((array) ($gbp['menu'] ?? [])); $menuPicks = \App\Support\Session::old('menu', []); $menuPicks = is_array($menuPicks) ? $menuPicks : []; $menuOn = $menuRows !== [] && !empty($gbpPicked['eligible']); // The step check: grey until the script finds the step complete, green then. $vsCheck = static function (int $n): string { return ''; }; $vsGateways = array_values(array_filter(['stripe', 'paypal'], static fn ($g) => \App\Services\Payment::isEnabled($g))); $consentLink = static function (string $slug, string $text): string { return cms_page_live($slug) ? '' . e($text) . '' : e($text); }; @endphp