<?php
|
namespace JVBase\ui;
|
|
use JVBase\meta\Form;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* Shared Checkout UI
|
*
|
* Provider-agnostic checkout markup used by any payment integration
|
* (Square, Helcim, etc). Integrations hook into this via:
|
* - add_filter('jvbAdditionalActions', [Checkout::class, 'render'])
|
*
|
* The active provider is determined by `jvbGetPaymentProvider()`,
|
* which returns the integration instance (Square or Helcim).
|
*
|
* Provider-specific differences are handled by:
|
* - data-provider attribute on the form (for JS to detect)
|
* - #payment-container (provider JS attaches its own UI here)
|
* - Filterable sections for provider-specific content
|
*
|
* @since 1.0.0
|
*/
|
class Checkout
|
{
|
/**
|
* Render the checkout aside and append to actions.
|
*
|
* Hooked via: add_filter('jvbAdditionalActions', [Checkout::class, 'render'])
|
*/
|
public static function render(array $actions): array
|
{
|
if (is_singular(BASE . 'dash') || is_post_type_archive(BASE . 'dash')) {
|
return $actions;
|
}
|
|
$provider = jvbGetPaymentProvider();
|
if (!$provider || !$provider->isSetUp()) {
|
return $actions;
|
}
|
|
$providerName = strtolower($provider->getServiceName());
|
|
$form = '<aside id="cart" class="right main">
|
<form id="checkout" data-form-id="checkout" data-save="checkout" data-provider="' . esc_attr($providerName) . '">';
|
|
$tabs = [
|
'cartItems' => [
|
'title' => 'Your Order',
|
'icon' => 'cart',
|
'description' => 'Here\'s your order. You can change quantities, remove items, or clear your cart.',
|
'content' => self::cartContent(),
|
],
|
'checkout' => [
|
'title' => 'Checkout',
|
'icon' => 'checkout',
|
'description' => apply_filters('jvb_checkout_description',
|
'Securely checkout with your name, email, and payment.',
|
$providerName
|
),
|
'content' => self::checkoutContent($providerName),
|
],
|
'order' => [
|
'title' => 'Your Order',
|
'icon' => 'truck',
|
'hidden' => true,
|
'description' => '',
|
'content' => self::orderStatus(),
|
],
|
];
|
|
$form .= jvbRenderTabs($tabs, true);
|
|
$form .= '<div class="cart-total row right">
|
<p class="tax">Tax: <span></span></p>
|
<p class="total">GRAND TOTAL: <span></span></p>
|
</div>
|
</form>
|
</aside>';
|
|
$form .= self::templates($providerName);
|
|
$actions[] = [
|
'button' => self::toggleButton(),
|
'content' => $form,
|
];
|
|
return $actions;
|
}
|
|
/*****************************************************************
|
* SECTIONS
|
*****************************************************************/
|
|
/**
|
* Checkout tab content: customer info + payment container
|
*/
|
private static function checkoutContent(string $provider): string
|
{
|
$fields = '<div class="checkout-section">
|
<h3>Customer Information</h3>'
|
. Form::render('cart_name', null, [
|
'type' => 'text',
|
'label' => 'Your Name',
|
'required' => true,
|
'autocomplete' => 'name',
|
])
|
. Form::render('cart_email', null, [
|
'type' => 'email',
|
'label' => 'Your Email',
|
'required' => true,
|
'autocomplete' => 'email',
|
])
|
. Form::render('cart_phone', null, [
|
'type' => 'tel',
|
'label' => 'Your Phone',
|
'required' => true,
|
'autocomplete' => 'phone',
|
]);
|
|
// Optional sections — integrations can add pickup, scheduling, etc.
|
$fields .= apply_filters('jvb_checkout_fields', '', $provider);
|
|
$fields .= '</div>';
|
|
// Payment section — provider JS mounts its own UI inside #payment-container
|
$fields .= '<div class="checkout-section">
|
<h3>Payment Information</h3>
|
<div id="saved-cards"></div>
|
<div id="payment-container" data-provider="' . esc_attr($provider) . '"></div>
|
</div>';
|
|
return $fields;
|
}
|
|
/**
|
* Cart items tab: table + account details
|
*/
|
private static function cartContent(): string
|
{
|
ob_start();
|
?>
|
<div class="cart-items">
|
<table>
|
<thead>
|
<tr>
|
<th scope="col">Item</th>
|
<th scope="col">Price</th>
|
<th scope="col">Total</th>
|
</tr>
|
</thead>
|
<tbody></tbody>
|
</table>
|
</div>
|
|
<details class="account">
|
<summary>
|
<?php
|
if (is_user_logged_in()) {
|
echo 'Your Favourites and Order History';
|
} else {
|
echo '<a href="' . wp_login_url(get_the_permalink()) . '">Log in</a> to save your favourites and view order history.';
|
}
|
?>
|
</summary>
|
<?php
|
if (is_user_logged_in()) {
|
$tabs = [
|
'history' => [
|
'title' => 'Order History',
|
'icon' => 'checkout',
|
'description' => 'View your past orders and quickly reorder',
|
'content' => apply_filters('jvb_checkout_order_history', ''),
|
],
|
'favourites' => [
|
'title' => 'Favourites',
|
'icon' => 'heart',
|
'description' => 'View your favourites',
|
'content' => apply_filters('jvb_checkout_favourites', ''),
|
],
|
];
|
jvbRenderTabs($tabs);
|
}
|
?>
|
</details>
|
<?php
|
return ob_get_clean();
|
}
|
|
/**
|
* Order confirmation / status tracking
|
*/
|
private static function orderStatus(): string
|
{
|
$statuses = apply_filters('jvb_checkout_order_statuses', [
|
'received' => 'Order Received',
|
'preparing' => 'Preparing',
|
'ready' => 'Ready for Pickup',
|
]);
|
|
ob_start();
|
?>
|
<div class="order-confirmation">
|
<h2>Order Confirmed!</h2>
|
<div id="order-status" data-order="">
|
<p>Order #<span class="order-num"></span></p>
|
<div class="status-timeline">
|
<?php foreach ($statuses as $key => $label): ?>
|
<div class="status-item<?php echo $key === array_key_first($statuses) ? ' active' : ''; ?>"
|
data-status="<?php echo esc_attr($key); ?>">
|
<?php echo esc_html($label); ?>
|
</div>
|
<?php endforeach; ?>
|
</div>
|
<div class="pickup-time">
|
Estimated pickup: <span id="eta">Calculating...</span>
|
</div>
|
</div>
|
</div>
|
<?php
|
return ob_get_clean();
|
}
|
|
/*****************************************************************
|
* TEMPLATES — cloned by JS at runtime
|
*****************************************************************/
|
|
private static function templates(string $provider): string
|
{
|
// Browse link is filterable per-site
|
$browseUrl = apply_filters('jvb_checkout_browse_url', '#');
|
$browseText = apply_filters('jvb_checkout_browse_text', 'browse our products');
|
|
return '<template class="restoredCart">
|
<div class="restored">
|
<h3>Looks like we left things hanging</h3>
|
<p>We\'ve restored your cart from your last session below.</p>
|
<p>If you\'d rather start over, click the button below.</p>
|
<div class="row x-btw">
|
<button type="button" data-clear-cart>' . jvbIcon('trash') . 'Clear Cart</button>
|
<button type="button" data-dismiss>' . jvbIcon('x') . 'Dismiss</button>
|
</div>
|
</div>
|
</template>
|
<template class="cartItem">
|
<tr class="item">
|
<td class="item">
|
<label for="quantity"></label>
|
<div class="quantity field" data-min="0" data-max="50" data-step="1" data-price="" data-id="" data-catalog-id="">
|
<button type="button" class="decrease" aria-label="Decrease quantity">' . jvbIcon('minus-square') . '</button>
|
<input type="number" id="quantity" name="quantity" value="0" min="0" max="50" step="1" class="quantity-input">
|
<button type="button" class="increase" aria-label="Increase quantity">' . jvbIcon('plus-square') . '</button>
|
</div>
|
</td>
|
<td class="price"><span class="price"></span></td>
|
<td class="total"><span class="total"></span></td>
|
<td>
|
<button type="button" data-remove-from-cart>' . jvbIcon('trash') . '</button>
|
</td>
|
</tr>
|
</template>
|
<template class="emptyCart">
|
<div class="empty">
|
<p><i><b>No items in cart.</b></i></p>
|
<p>You can <a href="' . esc_url($browseUrl) . '" title="' . esc_attr($browseText) . '">' . $browseText . '</a> to order.</p>
|
</div>
|
</template>';
|
}
|
|
/**
|
* Cart toggle button
|
*/
|
private static function toggleButton(): string
|
{
|
return '<button type="button" class="toggle-cart row" title="Your Cart"
|
data-action="toggle-cart" aria-label="Open Cart"
|
aria-controls="checkout" aria-expanded="false">'
|
. jvbIcon('shopping-cart')
|
. '<span class="abs"></span><span class="abs count"></span>
|
</button>';
|
}
|
}
|