| @@ -1,16 +1,12 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace FluentCart\App\Services\Renderer; |
| 4 | 4 | |
| 5 | -use FluentCart\Api\CurrencySettings; | |
| 5 | + | |
| 6 | 6 | use FluentCart\Api\StoreSettings; |
| 7 | 7 | use FluentCart\App\Helpers\Helper; |
| 8 | -use FluentCart\App\Models\Product; | |
| 9 | -use FluentCart\App\Models\ProductVariation; | |
| 10 | -use FluentCart\App\Vite; | |
| 11 | 8 | use FluentCart\Framework\Support\Arr; |
| 12 | -use FluentCart\App\App; | |
| 13 | 9 | use FluentCart\Framework\Support\Str; |
| 14 | 10 | use FluentCart\App\Helpers\CurrenciesHelper; |
| 15 | 11 | use FluentCart\App\Hooks\Handlers\ShortCodes\Buttons\AddToCartShortcode; |
| 16 | 12 | use FluentCart\App\Hooks\Handlers\ShortCodes\Buttons\DirectCheckoutShortcode; |
| @@ -43,8 +39,11 @@ | ||
| 43 | 39 | |
| 44 | 40 | protected $licenseEnable = 'no'; |
| 45 | 41 | |
| 46 | 42 | |
| 43 | + protected $productPerRow = 4; | |
| 44 | + | |
| 45 | + | |
| 47 | 46 | public function __construct($viewData) |
| 48 | 47 | { |
| 49 | 48 | $this->viewData = $viewData; |
| 50 | 49 | $this->variants = $this->viewData['variants']; |
| @@ -54,30 +53,156 @@ | ||
| 54 | 53 | |
| 55 | 54 | $this->sign = CurrenciesHelper::getCurrencySign($signName); |
| 56 | 55 | |
| 57 | 56 | $this->showCartButton = Arr::get($this->viewData, 'show_cart_button', 1); |
| 58 | - | |
| 59 | - | |
| 57 | + $productPerRow = absint(Arr::get($this->viewData, 'product_per_row', 4)); | |
| 58 | + $this->productPerRow = $productPerRow > 0 ? $productPerRow : 4; | |
| 60 | 59 | } |
| 61 | 60 | |
| 62 | 61 | public function render() |
| 63 | 62 | { |
| 63 | + $groupBy = Arr::get($this->viewData, 'group_by', 'repeat_interval'); | |
| 64 | + $groups = $this->groupVariants($groupBy); | |
| 65 | + $useTabs = $groupBy !== 'none' && count($groups) > 1; | |
| 64 | 66 | |
| 65 | 67 | ?> |
| 66 | 68 | <div class="fluent-cart-pricing-table" data-fluent-cart-pricing-table role="region" |
| 67 | 69 | aria-label="<?php esc_attr_e('Pricing table', 'fluent-cart'); ?>"> |
| 68 | - <div class="fluent-cart-pricing-table-variants-iterator" role="list"> | |
| 69 | - <?php $this->renderVariant(); ?> | |
| 70 | + <?php if ($useTabs) : ?> | |
| 71 | + <?php $this->renderTabs($groups); ?> | |
| 72 | + <?php else : ?> | |
| 73 | + <div | |
| 74 | + class="fluent-cart-pricing-table-variants-iterator" | |
| 75 | + role="list" | |
| 76 | + style="--fluent-cart-pricing-table-product-per-row: <?php echo esc_attr($this->productPerRow); ?>;" | |
| 77 | + > | |
| 78 | + <?php $this->renderVariant($this->variants); ?> | |
| 79 | + </div> | |
| 80 | + <?php endif; ?> | |
| 81 | + </div> | |
| 82 | + <?php | |
| 83 | + } | |
| 84 | + | |
| 85 | + protected function groupVariants(string $groupBy): array | |
| 86 | + { | |
| 87 | + if ($groupBy === 'none') { | |
| 88 | + return ['onetime' => $this->variants]; | |
| 89 | + } | |
| 90 | + | |
| 91 | + $key = $groupBy === 'payment_type' ? 'payment_type' : 'repeat_interval'; | |
| 92 | + | |
| 93 | + $groups = []; | |
| 94 | + foreach ($this->variants as $variant) { | |
| 95 | + $bucket = Arr::get($variant, "other_info.{$key}") ?: 'onetime'; | |
| 96 | + if (Arr::get($variant, 'other_info.installment') === 'yes') { | |
| 97 | + $bucket = 'installment'; | |
| 98 | + } | |
| 99 | + $groups[$bucket][] = $variant; | |
| 100 | + } | |
| 101 | + | |
| 102 | + return $groups; | |
| 103 | + } | |
| 104 | + | |
| 105 | + protected function renderTabs(array $groups) | |
| 106 | + { | |
| 107 | + $groupKeys = array_keys($groups); | |
| 108 | + $activeIndex = (int) Arr::get($this->viewData, 'active_tab', 0); | |
| 109 | + if (!isset($groupKeys[$activeIndex])) { | |
| 110 | + $activeIndex = 0; | |
| 111 | + } | |
| 112 | + $activeKey = $groupKeys[$activeIndex]; | |
| 113 | + $activeVariantMap = Arr::get($this->viewData, 'active_variant', []); | |
| 114 | + if (!\is_array($activeVariantMap)) { | |
| 115 | + $activeVariantMap = []; | |
| 116 | + } | |
| 117 | + | |
| 118 | + ?> | |
| 119 | + <div class="fluent-cart-pricing-table-tab" data-fluent-cart-pricing-table-tab> | |
| 120 | + <div class="fluent-cart-pricing-table-tab-nav"> | |
| 121 | + <div class="fluent-cart-pricing-table-tab-nav-inner-wrap"> | |
| 122 | + <div class="fluent-cart-pricing-table-tab-nav-inner" role="tablist"> | |
| 123 | + <div class="tab-active-bar" data-tab-active-bar></div> | |
| 124 | + <?php foreach ($groupKeys as $key) : | |
| 125 | + $isActive = $key === $activeKey; | |
| 126 | + ?> | |
| 127 | + <div class="fluent-cart-pricing-table-tab-nav-item<?php echo $isActive ? ' active' : ''; ?>" | |
| 128 | + data-tab="<?php echo esc_attr($key); ?>"> | |
| 129 | + <?php echo esc_html($this->getTabLabel($key)); ?> | |
| 130 | + </div> | |
| 131 | + <?php endforeach; ?> | |
| 132 | + </div> | |
| 133 | + </div> | |
| 70 | 134 | </div> |
| 135 | + <div class="fluent-cart-pricing-table-tab-content" data-tab-contents> | |
| 136 | + <?php foreach ($groups as $key => $groupVariants) : | |
| 137 | + $isActive = $key === $activeKey; | |
| 138 | + $currentActiveVariant = Arr::get($activeVariantMap, $key, ''); | |
| 139 | + ?> | |
| 140 | + <div id="<?php echo esc_attr($key); ?>" | |
| 141 | + class="fluent-cart-pricing-table-tab-pane<?php echo $isActive ? ' active' : ''; ?>" | |
| 142 | + data-tab-content | |
| 143 | + data-active_variant="<?php echo esc_attr($currentActiveVariant); ?>" | |
| 144 | + role="tabpanel"> | |
| 145 | + <div | |
| 146 | + class="fluent-cart-pricing-table-variants-iterator" | |
| 147 | + role="list" | |
| 148 | + style="--fluent-cart-pricing-table-product-per-row: <?php echo esc_attr($this->productPerRow); ?>;" | |
| 149 | + > | |
| 150 | + <?php $this->renderVariant($groupVariants); ?> | |
| 151 | + </div> | |
| 152 | + </div> | |
| 153 | + <?php endforeach; ?> | |
| 154 | + </div> | |
| 71 | 155 | </div> |
| 72 | 156 | <?php |
| 73 | 157 | } |
| 74 | 158 | |
| 75 | - public function renderVariant() | |
| 159 | + protected function getTabLabel(string $key): string | |
| 76 | 160 | { |
| 161 | + $labels = [ | |
| 162 | + 'daily' => __('Daily', 'fluent-cart'), | |
| 163 | + 'weekly' => __('Weekly', 'fluent-cart'), | |
| 164 | + 'monthly' => __('Monthly', 'fluent-cart'), | |
| 165 | + 'yearly' => __('Yearly', 'fluent-cart'), | |
| 166 | + 'onetime' => __('One Time', 'fluent-cart'), | |
| 167 | + 'subscription' => __('Subscription', 'fluent-cart'), | |
| 168 | + 'installment' => __('Installment', 'fluent-cart'), | |
| 169 | + ]; | |
| 77 | 170 | |
| 171 | + return $labels[$key] ?? ucfirst(str_replace('_', ' ', $key)); | |
| 172 | + } | |
| 78 | 173 | |
| 79 | - foreach ($this->variants as $index => $variant) { | |
| 174 | + protected function isVariantActive($variant): bool | |
| 175 | + { | |
| 176 | + $groupBy = Arr::get($this->viewData, 'group_by', 'repeat_interval'); | |
| 177 | + | |
| 178 | + if ($groupBy === 'none') { | |
| 179 | + $variantGroupKey = 'onetime'; | |
| 180 | + } else { | |
| 181 | + $groupKey = $groupBy === 'payment_type' ? 'payment_type' : 'repeat_interval'; | |
| 182 | + | |
| 183 | + $variantGroupKey = Arr::get($variant, "other_info.{$groupKey}") ?: 'onetime'; | |
| 184 | + | |
| 185 | + if (Arr::get($variant, 'other_info.installment') === 'yes') { | |
| 186 | + $variantGroupKey = 'installment'; | |
| 187 | + } | |
| 188 | + } | |
| 189 | + | |
| 190 | + $activeVariantMap = Arr::get($this->viewData, 'active_variant', []); | |
| 191 | + if (!\is_array($activeVariantMap)) { | |
| 192 | + $activeVariantMap = []; | |
| 193 | + } | |
| 194 | + | |
| 195 | + $variantId = Arr::get($variant, 'id'); | |
| 196 | + | |
| 197 | + return isset($activeVariantMap[$variantGroupKey]) && $activeVariantMap[$variantGroupKey] == $variantId; | |
| 198 | + } | |
| 199 | + | |
| 200 | + public function renderVariant(?array $variants = null) | |
| 201 | + { | |
| 202 | + $variants = $variants ?? $this->variants; | |
| 203 | + | |
| 204 | + foreach ($variants as $index => $variant) { | |
| 80 | 205 | $this->repeatInterval = Arr::get($variant, 'other_info.repeat_interval', null); |
| 81 | 206 | |
| 82 | 207 | $this->itemPrice = esc_html(Helper::toDecimal(Arr::get($variant, 'item_price', 0), false, false, false)); |
| 83 | 208 | |
| @@ -98,11 +223,24 @@ | ||
| 98 | 223 | $licenseMeta = Arr::get($variant, 'product.licenses_meta.meta_value', null); |
| 99 | 224 | |
| 100 | 225 | $this->licenseEnable = Arr::get($licenseMeta, 'enabled', 'no'); |
| 101 | 226 | |
| 227 | + $isActiveVariant = $this->isVariantActive($variant); | |
| 228 | + | |
| 229 | + // Parse badge settings from viewData | |
| 230 | + $badgeData = Arr::get($this->viewData, 'badge', ''); | |
| 231 | + $badgeConfig = is_string($badgeData) ? self::stringToAssocArray($badgeData) : (is_array($badgeData) ? $badgeData : []); | |
| 232 | + $badgeText = Arr::get($badgeConfig, 'text', __('Recommended', 'fluent-cart')); | |
| 233 | + $badgePosition = Arr::get($badgeConfig, 'position', 'right'); | |
| 234 | + | |
| 102 | 235 | ?> |
| 103 | 236 | |
| 104 | - <div class="fluent-cart-pricing-table-variant" data-fluent-cart-pricing-table-variant role="listitem"> | |
| 237 | + <div class="fluent-cart-pricing-table-variant<?php echo $isActiveVariant ? ' active' : ''; ?>" data-fluent-cart-pricing-table-variant role="listitem"> | |
| 238 | + <?php if ($isActiveVariant) : ?> | |
| 239 | + <div class="fluent-cart-pricing-table-variant-badge <?php echo esc_attr($badgePosition); ?>"> | |
| 240 | + <?php echo esc_html($badgeText); ?> | |
| 241 | + </div> | |
| 242 | + <?php endif; ?> | |
| 105 | 243 | |
| 106 | 244 | <div class="fluent-cart-pricing-table-variant-contents"> |
| 107 | 245 | <div class="fluent-cart-pricing-table-variant-title"> |
| 108 | 246 | <?php echo esc_html(Arr::get($variant, 'product.post_title', '')); ?> |
| @@ -235,8 +373,9 @@ | ||
| 235 | 373 | 'product_id' => Arr::get($variant, 'id', ''), |
| 236 | 374 | 'quantity' => 1, |
| 237 | 375 | 'variation_type' => Arr::get($variant, 'variation_type', ''), |
| 238 | 376 | 'payment_type' => Arr::get($variant, 'payment_type', ''), |
| 377 | + 'variation_id' => Arr::get($variant, 'id', ''), | |
| 239 | 378 | ]; |
| 240 | 379 | |
| 241 | 380 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped in view template |
| 242 | 381 | echo AddToCartShortcode::make()->enqueueAssets()->render($data); |
| @@ -312,9 +451,13 @@ | ||
| 312 | 451 | 'is_pricing_table' => true, |
| 313 | 452 | 'quantity' => 1, |
| 314 | 453 | 'variation_type' => Arr::get($variant, 'variation_type', ''), |
| 315 | 454 | 'stock_availability' => Arr::get($variant, 'stock_status', ''), |
| 455 | + 'variation_id' => Arr::get($variant, 'id', ''), | |
| 316 | 456 | ]; |
| 457 | + | |
| 458 | + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped in view template | |
| 459 | + echo DirectCheckoutShortcode::make()->enqueueAssets()->render($data); | |
| 317 | 460 | } |
| 318 | 461 | } |
| 319 | 462 | |
| 320 | 463 | private static function stringToAssocArray($string): array |