PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.0
1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 All 50 releases
fluent-cart / app / Modules / Templating / AssetLoader.php

AssetLoader.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.7.0, at app/Modules/Templating/AssetLoader.php

808 lines 31.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Modules\Templating;
4
5 use FluentCart\Api\CurrencySettings;
6 use FluentCart\Api\StoreSettings;
7 use FluentCart\App\App;
8 use FluentCart\App\Helpers\CartHelper;
9 use FluentCart\App\Helpers\Helper;
10 use FluentCart\App\Helpers\UtmHelper;
11 use FluentCart\App\Hooks\Cart\CartLoader;
12 use FluentCart\App\Hooks\Handlers\ShortCodes\CustomerProfileHandler;
13 use FluentCart\App\Models\Cart;
14 use FluentCart\App\Modules\Tax\TaxModule;
15 use FluentCart\App\Services\Localization\LocalizationManager;
16 use FluentCart\App\Services\Renderer\ProductFilterRender;
17 use FluentCart\App\Services\TemplateService;
18 use FluentCart\App\Services\Translations\TransStrings;
19 use FluentCart\App\Services\URL;
20 use FluentCart\App\Vite;
21 use FluentCart\App\Services\Renderer\CartRenderer;
22 use FluentCart\Framework\Support\Arr;
23 use FluentCart\Api\Resource\FrontendResource\CartResource;
24
25 class AssetLoader
26 {
27 static $loadedAssets = [];
28 protected static bool $frontendAssetsRequired = false;
29
30 public static function markFrontendAssetsRequired(): void
31 {
32 self::$frontendAssetsRequired = true;
33 }
34
35 public static function isFrontendAssetsMarked(): bool
36 {
37 return self::$frontendAssetsRequired;
38 }
39
40 public static function shouldLoadGlobalFrontendAssets(): bool
41 {
42 $shouldLoad = true;
43
44 return (bool) apply_filters('fluent_cart/frontend_assets/should_load_global', $shouldLoad, [
45 'page_type' => TemplateService::getCurrentFcPageType(),
46 'is_marked' => self::isFrontendAssetsMarked(),
47 'is_fluentcart_context' => self::isFluentCartContext(),
48 'is_instant_checkout' => (bool) App::request()->get(Helper::INSTANT_CHECKOUT_URL_PARAM),
49 'is_modal_checkout' => App::request()->get('fluent-cart') === 'modal_checkout',
50 ]);
51 }
52
53 public static function isFluentCartContext(): bool
54 {
55 if (self::isFrontendAssetsMarked()) {
56 return true;
57 }
58
59 $pageType = TemplateService::getCurrentFcPageType();
60 if (in_array($pageType, [
61 'single_product',
62 'product_taxonomy',
63 'shop',
64 'cart',
65 'checkout',
66 'registration',
67 'login'
68 ], true)) {
69 return true;
70 }
71
72 if (App::request()->get(Helper::INSTANT_CHECKOUT_URL_PARAM)) {
73 return true;
74 }
75
76 return App::request()->get('fluent-cart') === 'modal_checkout';
77 }
78
79 public static function register()
80 {
81 add_action('wp_enqueue_scripts', [self::class, 'enqueueAssets']);
82 add_action('template_redirect', [self::class, 'markOrderReviewPageNoIndex']);
83 }
84
85 /**
86 * The store's chosen order-review page is reached from an email with a
87 * bearer credential in its URL and must not be indexed. The shortcode
88 * renders inside the page body, after wp_head() has already printed the
89 * robots tag, so the page has to be flagged before the head — here, once
90 * the main query knows which page it is.
91 *
92 * @return void
93 */
94 public static function markOrderReviewPageNoIndex()
95 {
96 if (TemplateService::getCurrentFcPageType() === 'order_review') {
97 add_filter('wp_robots', 'wp_robots_no_robots');
98 }
99 }
100
101 public static function enqueueAssets()
102 {
103 $pageType = TemplateService::getCurrentFcPageType();
104
105 switch ($pageType) {
106 case 'single_product':
107 self::loadSingleProductAssets();
108 break;
109 case 'product_taxonomy':
110 case 'shop':
111 self::loadProductArchiveAssets();
112 break;
113 case 'customer_dashboard':
114 self::loadCustomerDashboardGlobalAssets();
115 break;
116 case 'cart':
117 self::loadCartAssets();
118 break;
119 case 'checkout':
120 self::loadCheckoutAssets();
121 break;
122 case 'order_review':
123 // The store's chosen review page: enqueued here, in the head,
124 // rather than mid-body by the shortcode, so the form and the
125 // page do not paint unstyled first.
126 self::loadReviewSubmissionFormAssets();
127 Vite::enqueueStyle('fluent-cart-order-review', 'public/order-review/order-review.scss');
128 break;
129 default:
130 return;
131 }
132 }
133
134 // Shared between the full single-product bundle and the review-form-only
135 // one, so a form rendered either way is enqueued under one handle and
136 // localized with one array.
137 const REVIEW_FORM_SCRIPT_HANDLE = 'fluent-cart-review-form';
138 const REVIEW_FORM_STYLE_HANDLE = 'fluent-cart-reviews';
139
140 /**
141 * Just enough to run a review form: the form script, the review
142 * stylesheet, and the strings the script reads.
143 *
144 * A review page is not a product page. loadSingleProductAssets() also
145 * carries gallery zoom, the single-product behaviour, the review list,
146 * the product-card assets and the add-to-cart and direct-checkout
147 * styles — none of which a standalone form uses, and all of which every
148 * visitor to that page would download and execute.
149 *
150 * Idempotent, and idempotent against the full bundle too: both enqueue
151 * the same file under REVIEW_FORM_SCRIPT_HANDLE, so whichever runs
152 * second is dropped by WordPress rather than loading the script twice
153 * and giving every form two controllers.
154 */
155 public static function loadReviewSubmissionFormAssets()
156 {
157 self::markFrontendAssetsRequired();
158
159 if (wp_script_is(self::REVIEW_FORM_SCRIPT_HANDLE, 'enqueued')) {
160 return;
161 }
162
163 // Through the same helper the full bundle uses, so the version and
164 // the localization are stamped on identically.
165 Vite::enqueueAllScripts(
166 [
167 [
168 'source' => 'public/single-product/ReviewForm.js',
169 'dependencies' => [],
170 'inFooter' => true,
171 'handle' => self::REVIEW_FORM_SCRIPT_HANDLE,
172 ],
173 ],
174 self::REVIEW_FORM_SCRIPT_HANDLE,
175 [
176 'fluentcart_review_vars' => static::reviewJsVars(),
177 ]
178 );
179
180 Vite::enqueueStyle(
181 self::REVIEW_FORM_STYLE_HANDLE,
182 'public/single-product/reviews.scss'
183 );
184
185 /**
186 * The review form's assets are on this page. An add-on that extends
187 * the form (Pro's photo upload) enqueues its own script and styles
188 * here, so a surface that loads the form without being a product
189 * page — the customer dashboard, the order-review page — gets them
190 * too. Fired once per request, when the assets are first enqueued.
191 *
192 * @param array $context empty for now; the single payload every
193 * action carries, so listeners keep one
194 * signature as context is added
195 */
196 do_action('fluent_cart/review/submission_form_assets_loaded', []);
197 }
198
199 /**
200 * @return array
201 */
202 public static function reviewJsVars(): array
203 {
204 $reviewVars = [
205 // Threaded replies (customer replies + multiple store replies)
206 // are a Pro capability, off by default. The frontend uses this to
207 // decide whether to render the reviewer reply form in the modal.
208 'trans' => [
209 'no_reviews' => __('No reviews yet. Be the first to write a review!', 'fluent-cart'),
210 'verified_purchase' => __('Verified Purchase', 'fluent-cart'),
211 'rating_required' => __('Please select a rating', 'fluent-cart'),
212 'photo_preview' => __('Photo preview', 'fluent-cart'),
213 /* translators: %s: number of attachments still behind the + */
214 'view_more_attachments' => __('View %s more attachments', 'fluent-cart'),
215 'content_required' => __('Please write a review', 'fluent-cart'),
216 'name_required' => __('Please enter your name', 'fluent-cart'),
217 'email_required' => __('Please enter your email', 'fluent-cart'),
218 'submitting' => __('Submitting...', 'fluent-cart'),
219 'submit_review' => __('Submit Review', 'fluent-cart'),
220 'store_reply' => __('Store Reply', 'fluent-cart'),
221 'reviewer' => __('Reviewer', 'fluent-cart'),
222 'reply' => __('Reply', 'fluent-cart'),
223 'view_reply' => __('View Reply', 'fluent-cart'),
224 'review_thread' => __('Review Thread', 'fluent-cart'),
225 'no_replies' => __('No replies yet.', 'fluent-cart'),
226 'close' => __('Close', 'fluent-cart'),
227 'all' => __('All', 'fluent-cart'),
228 'prev' => __('Prev', 'fluent-cart'),
229 'next' => __('Next', 'fluent-cart'),
230 'prev_page' => __('Previous page', 'fluent-cart'),
231 'next_page' => __('Next page', 'fluent-cart'),
232 /* translators: 1: the page this segment goes to, 2: how many there are. */
233 'slider_page_of' => __('Page %1$s of %2$s', 'fluent-cart'),
234 'read_more' => __('Read more', 'fluent-cart'),
235 'show_less' => __('Show less', 'fluent-cart'),
236 'load_error' => __('Unable to load reviews. Please try again later.', 'fluent-cart'),
237 'edit_review' => __('Edit your review', 'fluent-cart'),
238 'rated_out_of' => __('Rated %d out of 5', 'fluent-cart'),
239 /* translators: 1: current step, 2: total steps */
240 'step_x_of_y' => __('Step %1$s of %2$s', 'fluent-cart'),
241 'rating_poor' => __('Poor', 'fluent-cart'),
242 'rating_average' => __('Average', 'fluent-cart'),
243 'rating_good' => __('Good', 'fluent-cart'),
244 'rating_very_good' => __('Very Good', 'fluent-cart'),
245 'rating_excellent' => __('Excellent', 'fluent-cart'),
246 'rating_step_hint' => __('Rate this product', 'fluent-cart'),
247 'details_step_hint' => __('Tell us more', 'fluent-cart'),
248 'photos_step_hint' => __('Photos are optional', 'fluent-cart'),
249 ],
250 ];
251
252 return apply_filters('fluent_cart/review/js_vars', $reviewVars);
253 }
254
255 public static function loadSingleProductAssets()
256 {
257 self::markFrontendAssetsRequired();
258 static $isLoaded = false;
259 if ($isLoaded) {
260 return;
261 }
262 self::loadProductCardAssets();
263 $isLoaded = true;
264 $singlePageScripts = [
265 [
266 'source' => 'public/single-product/xzoom/xzoom.js',
267 'dependencies' => [],
268 'inFooter' => true,
269 ],
270 [
271 'source' => 'public/single-product/SingleProduct.js',
272 'dependencies' => [],
273 'inFooter' => true
274 ],
275 [
276 'source' => 'public/single-product/Reviews.js',
277 'dependencies' => [],
278 'inFooter' => true
279 ],
280 [
281 'source' => 'public/single-product/ReviewForm.js',
282 'dependencies' => [],
283 'inFooter' => true,
284 // Named, not numbered, so loadReviewSubmissionFormAssets() enqueueing
285 // the same file lands on the same handle and WordPress drops
286 // the second one.
287 'handle' => self::REVIEW_FORM_SCRIPT_HANDLE,
288 ]
289 ];
290 $localizeData = [
291 'fluentcart_review_vars' => static::reviewJsVars(),
292 'fluentcart_single_product_vars' => [
293 'trans' => TransStrings::singleProductPageString(),
294 'cart_button_text' => apply_filters('fluent_cart/product/add_to_cart_text', __('Add To Cart', 'fluent-cart'), []),
295 // App::storeSettings()->get('cart_button_text', __('Add to Cart', 'fluent-cart')),
296 'out_of_stock_button_text' => apply_filters('fluent_cart/product/out_of_stock_text', __('Not Available', 'fluent-cart'), []),
297
298 'buy_now_button_text' => apply_filters('fluent_cart/product/buy_now_button_text', __('Buy Now', 'fluent-cart'), []),
299 'in_stock_status' => Helper::IN_STOCK,
300 'out_of_stock_status' => Helper::OUT_OF_STOCK,
301 'enable_image_zoom' => (new StoreSettings())->get('enable_image_zoom_in_single_product'),
302 'enable_image_zoom_in_modal' => (new StoreSettings())->get('enable_image_zoom_in_modal')
303 ]
304 ];
305 Vite::enqueueAllScripts($singlePageScripts, 'fluent-cart-single-product-page', $localizeData);
306
307 $singlePageStyles = [
308 'public/single-product/single-product.scss',
309 'public/single-product/similar-product.scss',
310 'public/single-product/reviews.scss',
311 'public/product-card/style/product-card.scss',
312 'public/single-product/xzoom/xzoom.css',
313 'public/buttons/add-to-cart/style/style.scss',
314 'public/buttons/direct-checkout/style/style.scss'
315 ];
316
317 Vite::enqueueAllStyles($singlePageStyles, 'fluent-cart-single-product-page');
318
319
320 }
321
322 public static function loadAddToCartCss()
323 {
324 self::markFrontendAssetsRequired();
325 $singlePageStyles = [
326 'public/buttons/add-to-cart/style/style.scss',
327 ];
328
329 Vite::enqueueAllStyles($singlePageStyles, 'fluent-cart-add-to-cart');
330 }
331
332 public static function loadModalCheckoutAssets()
333 {
334 static $isLoaded = false;
335 if ($isLoaded) {
336 return;
337 }
338
339 $isLoaded = true;
340
341 // $scripts = [
342 // 'source' => 'public/checkout/ModalCheckoutHandler.js',
343 // 'dependencies' => [],
344 // 'inFooter' => true
345 // ];
346
347 Vite::enqueueStyle(
348 'fluentcart-modal-checkout-css',
349 'public/checkout/style/modal-checkout.scss'
350 );
351
352 // Vite::enqueueAllScripts($scripts, 'fluent-cart-modal-checkout');
353 }
354
355 public static function loadProductCardAssets()
356 {
357 self::markFrontendAssetsRequired();
358 static $isLoaded = false;
359 if ($isLoaded) {
360 return;
361 }
362 $isLoaded = true;
363 $singlePageStyles = [
364 'public/product-card/style/product-card.scss',
365 ];
366 Vite::enqueueAllStyles($singlePageStyles, 'fluent-cart-product-card-page');
367 Vite::enqueueStyle(
368 'fluent-cart-sold-out-badge',
369 'admin/BlockEditor/SoldOutBadge/style/sold-out-badge-block-editor.scss'
370 );
371 Vite::enqueueStyle(
372 'fluent-cart-sale-badge',
373 'admin/BlockEditor/SaleBadge/style/sale-badge-block-editor.scss'
374 );
375 Vite::enqueueScript(
376 'fluent-cart-product-card-js',
377 'public/product-card/product-card.js',
378 [],
379 null,
380 true
381 );
382 }
383
384 public static function loadProductArchiveAssets()
385 {
386 self::markFrontendAssetsRequired();
387 static $isLoaded = false;
388 if ($isLoaded) {
389 return;
390 }
391 $isLoaded = true;
392 static::loadProductCardAssets();
393
394 $app = App::getInstance();
395 $slug = $app->config->get('app.slug');
396 Vite::enqueueStyle(
397 'fluentcart-product-card-page-css',
398 'public/product-card/style/product-card.scss'
399 );
400
401 Vite::enqueueStyle(
402 'fluentcart-single-product-css',
403 'public/single-product/single-product.scss'
404 );
405 Vite::enqueueStyle(
406 'fluentcart-similar-product-css',
407 'public/single-product/similar-product.scss'
408 );
409 Vite::enqueueStyle(
410 'fluentcart-add-to-cart-btn-css',
411 'public/buttons/add-to-cart/style/style.scss'
412 );
413 Vite::enqueueStyle(
414 'fluentcart-direct-checkout-btn-css',
415 'public/buttons/direct-checkout/style/style.scss'
416 );
417 Vite::enqueueStyle(
418 $slug . '-fluentcart-product-page-css',
419 'public/product-page/style/shop-app.scss',
420 );
421 Vite::enqueueStaticStyle(
422 $slug . '-fluentcart-product-filter-slider-css',
423 'public/lib/nouislider/nouislider-15.7.1.css',
424 );
425
426
427 ob_start();
428 ProductFilterRender::renderResponsiveFilter();
429 $responsiveFilterWrapper = ob_get_clean();
430
431 $assetsPath = $app['url.assets'];
432
433
434 Vite::enqueueScript(
435 $slug . '-fluentcart-product-page-js',
436 'public/product-page/ShopApp.js',
437 []
438 )->with([
439 'fluentcart_shop_vars' => [
440 'rest' => Helper::getRestInfo(),
441 'shop' => Helper::shopConfig(),
442 'currency_settings' => CurrencySettings::get(),
443 'checkout_page' => (new StoreSettings)->getCheckoutPage(),
444 'cart_image' => $assetsPath . 'images/cart.svg',
445 'cart_driver' => Helper::getCartDriver(),
446 'responsive_filter_wrapper' => $responsiveFilterWrapper,
447 'is_admin_bar_showing' => is_admin_bar_showing(),
448 'responsive_filter_breakpoint' => apply_filters('shop_app_responsive_filter_breakpoint', 768)
449 ],
450 'fluentCartRestVars' => [
451 'rest' => Helper::getRestInfo(),
452 'ajaxurl' => admin_url('admin-ajax.php')
453 ]
454 ]);
455 Vite::enqueueStaticScript(
456 $slug . '-fluentcart-product-filter-slider',
457 'public/lib/nouislider/nouislider-15.7.1.min.js',
458 [$slug . '-fluentcart-product-page-js']
459 );
460 Vite::enqueueScript(
461 'fluentcart-zoom-js',
462 'public/single-product/xzoom/xzoom.js',
463 []
464 );
465
466 Vite::enqueueScript(
467 'fluentcart-single-product-js',
468 'public/single-product/SingleProduct.js',
469 []
470 )->with([
471 'fluentcart_single_product_vars' => [
472 'trans' => TransStrings::singleProductPageString(),
473 'cart_button_text' => apply_filters('fluent_cart/product/add_to_cart_text', __('Add To Cart', 'fluent-cart'), []),
474 // App::storeSettings()->get('cart_button_text', __('Add to Cart', 'fluent-cart')),
475 'out_of_stock_button_text' => apply_filters('fluent_cart/product/out_of_stock_button_text', __('Not Available', 'fluent-cart'), []),
476 'buy_now_button_text' => apply_filters('fluent_cart/product/buy_now_button_text', __('Buy Now', 'fluent-cart'), []),
477 'in_stock_status' => Helper::IN_STOCK,
478 'out_of_stock_status' => Helper::OUT_OF_STOCK,
479 'enable_image_zoom' => (new StoreSettings())->get('enable_image_zoom_in_single_product')
480 ]
481 ]);
482 }
483
484 public static function loadCustomerDashboardGlobalAssets()
485 {
486 static $isLoaded = false;
487 if ($isLoaded) {
488 return;
489 }
490 $isLoaded = true;
491 Vite::enqueueStyle('fluent-cart-customer-profile-global',
492 'public/customer-profile/style/customer-profile-global.scss',
493 );
494 }
495
496 public static function loadCustomerDashboardAssets()
497 {
498 static $isLoaded = false;
499 if ($isLoaded) {
500 return;
501 }
502 $isLoaded = true;
503 Vite::enqueueStyle('fluentcart-customer-css',
504 'public/customer-profile/style/customer-profile.scss'
505 );
506
507 // My Reviews opens the storefront review form in a modal, fetched
508 // per product; the form's own script and stylesheet have to be on
509 // the page before that markup arrives.
510 if (\FluentCart\Api\ModuleSettings::isActive('reviews')) {
511 self::loadReviewSubmissionFormAssets();
512 }
513
514 // wp-i18n: this bundle pulls in the admin translator transitively
515 // (Start.js -> @/Bits/common.js -> @/utils/translator/Translator.js),
516 // which resolves strings through window.wp.i18n.
517 Vite::enqueueScript(
518 'fluentcart-customer-js',
519 'public/customer-profile/Start.js',
520 ['wp-i18n']
521 )->with(CustomerProfileHandler::getLocalizationData());
522
523 /**
524 * Enqueue additional customer-dashboard assets — the hook an add-on
525 * uses to ship the JS backing a portal section it registers through
526 * `fluent_cart/customer_portal/profile_sections`.
527 */
528 do_action('fluent_cart/customer_dashboard/enqueue_assets');
529 }
530
531 public static function loadCartAssets()
532 {
533 self::markFrontendAssetsRequired();
534 static $isLoaded = false;
535 if ($isLoaded) {
536 return;
537 }
538 $isLoaded = true;
539 $app = fluentCart();
540 $slug = $app->config->get('app.slug');
541 Vite::enqueueStaticScript(
542 $slug . '-fluentcart-toastify-notify-style',
543 'public/lib/toastify/toastify-js-1.12.0.js',
544 [$slug . '-app',]
545 );
546
547 Vite::enqueueStaticStyle(
548 $slug . '-fluentcart-toastify-notify-js',
549 'public/lib/toastify/toastify.min-1.12.0.css',
550 );
551
552 $cart = CartHelper::getCart(null, false);
553
554 $isInstantCheckout = (bool) App::request()->get(Helper::INSTANT_CHECKOUT_URL_PARAM);
555
556 $cartItemLayout = '';
557 $emptyCartLayout = '';
558
559 if (!App::request()->get(Helper::INSTANT_CHECKOUT_URL_PARAM)) {
560 $cartItems = Arr::get(CartResource::getStatus(), 'cart_data', []);
561 $cartRenderer = new CartRenderer($cartItems);
562
563 ob_start();
564 $cartRenderer->renderDummyItems();
565 $cartItemLayout = ob_get_clean();
566 ob_start();
567
568 $cartRenderer->renderEmpty();
569
570 $emptyCartLayout = ob_get_clean();
571 }
572
573 Vite::enqueueScript(
574 $slug . '-app',
575 'public/globals/FluentCartApp.js',
576 []
577 )->with([
578 'fluentCartRestVars' => [
579 'rest' => Helper::getRestInfo(),
580 'ajaxurl' => admin_url('admin-ajax.php'),
581 ],
582 'fluentcart_drawer_vars' => [
583 'placeholder_image' => Helper::getProductPlaceholderUrl(),
584 'cart_item_layout' => $cartItemLayout,
585 'empty_cart_layout' => $emptyCartLayout,
586 'cart_driver' => Helper::getCartDriver(),
587 'cart_image' => Vite::getAssetUrl('images/cart.svg'),
588 'currency_settings' => CurrencySettings::get(),
589 'has_active_cart' => !!$cart,
590 'is_drawer_hidden' => CartLoader::shouldHideCartDrawer(),
591 'is_admin_bar_showing' => is_admin_bar_showing(),
592 'cart_item_count' => $isInstantCheckout ? 0 : ($cart ? count($cart->cart_data ?? []) : 0),
593 'cart_total_quantity' => $isInstantCheckout ? 0 : ($cart ? array_sum(array_map(function ($item) { return (int) ($item['quantity'] ?? 1); }, $cart->cart_data ?? [])) : 0),
594 ],
595 'fluentcart_utm_vars' => [
596 'allowed_keys' => UtmHelper::allowedUtmParameterKey(),
597 'internal_domains' => UtmHelper::getInternalDomains()
598 ]
599 ]);
600
601 Vite::enqueueStyle(
602 $slug . '-fluentcart-drawer',
603 'public/cart-drawer/cart-drawer.scss',
604 );
605
606 Vite::enqueueStyle(
607 $slug . '-global-styles',
608 'public/globals/style.scss',
609 );
610 }
611
612 public static function loadCheckoutAssets($cart = null)
613 {
614 self::markFrontendAssetsRequired();
615
616 static $isLoaded = false;
617 if ($isLoaded) {
618 return;
619 }
620 $isLoaded = true;
621 if (!$cart) {
622 $cart = CartHelper::getCart();
623 }
624
625 static::loadCartAssets();
626
627 if (!$cart) {
628 return;
629 }
630
631 static $alreadyLoaded = false;
632 if ($alreadyLoaded) {
633 return;
634 }
635
636 $alreadyLoaded = true;
637
638 Vite::enqueueAllStyles(
639 [
640 [
641 'source' => 'public/checkout/style/checkout.scss',
642 ],
643 [
644 'source' => 'public/components/select/style/style.scss',
645 ]
646 ],
647 'checkout-page'
648 );
649 $scripts = [
650 [
651 'source' => 'public/checkout/FluentCartCheckout.js',
652 'dependencies' => ['fluent-cart-app'],
653 'inFooter' => true,
654 'handle' => 'fct-checkout'
655 ],
656 [
657 'source' => 'public/orderbump/orderbump.js',
658 'dependencies' => ['fluent-cart-app'],
659 'inFooter' => true,
660 'handle' => 'fct-orderbump'
661 ]
662 ];
663 Vite::enqueueAllScripts($scripts, 'fct-checkout');
664
665 self::localizeCheckoutData($cart);
666 }
667
668 private static function localizeCheckoutData(Cart $cart)
669 {
670 static $isLoaded = false;
671 if ($isLoaded) {
672 return;
673 }
674 $isLoaded = true;
675 $countryCode = (string)Arr::get($cart->checkout_data, 'form_data.billing_country');
676 $countryInfo = [];
677 if (!empty($countryCode)) {
678 $countryInfo[$countryCode] = LocalizationManager::getCountryInfoFromRequest(null, $countryCode);
679 $shippingCountryCode = (string)Arr::get($cart->checkout_data, 'form_data.shipping_country');
680 if (Arr::get($cart->checkout_data, 'form_data.ship_to_different') === 'yes' && $shippingCountryCode !== $countryCode) {
681 $countryInfo[$shippingCountryCode] = LocalizationManager::getCountryInfoFromRequest(null, $shippingCountryCode);
682 }
683 }
684
685
686 //if url has cart hash then it's an instant checkout
687 $hasInstantCheckout = App::request()->get(Helper::INSTANT_CHECKOUT_URL_PARAM);
688 $hasModalCheckout = App::request()->get('fluent-cart') === 'modal_checkout';
689 $isInstantCheckout = 'no';
690 if ($hasInstantCheckout || $hasModalCheckout) {
691 $isInstantCheckout = 'yes';
692 }
693
694 $checkoutErrorNotices = Arr::get($cart->checkout_data, '__checkout_error_notices', []);
695 if ($checkoutErrorNotices) {
696 $checkoutData = is_array($cart->checkout_data) ? $cart->checkout_data : [];
697 unset($checkoutData['__checkout_error_notices']);
698 $cart->checkout_data = $checkoutData;
699 $cart->save();
700 }
701
702 $data = [
703 'fluentcart_checkout_vars' => [
704 'rest' => Helper::getRestInfo(),
705 'ajaxurl' => admin_url('admin-ajax.php'),
706 'is_all_digital' => !$cart->requireShipping(),
707 'is_cart_locked' => $cart->checkout_data['is_locked'] ?? 'no',
708 'disable_coupons' => $cart->checkout_data['disable_coupons'] ?? 'no',
709 'notices' => array_values($checkoutErrorNotices),
710 'payment_methods_with_custom_checkout_buttons' => apply_filters('fluent_cart/payment_methods_with_custom_checkout_buttons', []),
711 'tax_settings' => (new TaxModule())->getSettings(),
712 'submit_button' => [
713 'text' => __('Place Order', 'fluent-cart'),
714 ],
715 'trans' => TransStrings::checkoutPageString(),
716 'payments_trans' => TransStrings::paymentsString(),
717 'cart_hash' => $cart->cart_hash,
718 'is_instant_checkout' => $isInstantCheckout,
719 'is_modal_checkout' => $hasModalCheckout ? 'yes' : 'no',
720
721 ],
722 'fluentcart_checkout_info' => [
723 'baseUrl' => site_url(),
724 'rest_url' => Helper::getRestInfo()['url'],
725 'checkout_nonce' => wp_create_nonce('fluentcart'),
726 'ajax_url' => admin_url('admin-ajax.php'),
727 'is_user_logged_in' => is_user_logged_in(),
728 'is_admin' => current_user_can('manage_options'),
729 'has_subscription' => $cart->hasSubscription(),
730 'rest' => Helper::getRestInfo(),
731 'order_confirmation_url' => admin_url('admin-ajax.php?action=fluent_cart_place_order'),
732 'order_information_url' => URL::getApiUrl('/checkout/get-order-info'),
733 'custom_checkout_url' => URL::getApiUrl('/checkout'),
734 'redirect_url' => (new StoreSettings())->getCheckoutPage(),
735 'store_country' => (new StoreSettings())->get('store_country'),
736 'country_info' => $countryInfo,
737 'is_zero_payment' => $cart->isZeroPayment() ? 'yes' : 'no'
738 ]
739 ];
740
741 // Apply filters to allow modules to modify localized data
742 $data = apply_filters('fluent_cart/checkout/localize_data', $data, $cart);
743
744 foreach ($data as $key => $datum) {
745 wp_localize_script('fct-checkout', $key, $datum);
746 }
747 }
748
749 private static function markAssetLoaded($handle)
750 {
751 self::$loadedAssets[$handle] = true;
752 }
753
754 public static function enqueueProductInfoFrontendStyles()
755 {
756 self::markFrontendAssetsRequired();
757 static $isLoaded = false;
758 if ($isLoaded) {
759 return;
760 }
761 $isLoaded = true;
762 // Enqueue frontend styles for the product info block
763 Vite::enqueueStyle(
764 'fluentcart-single-product',
765 'public/single-product/single-product.scss'
766 );
767
768 // Enqueue related component styles
769 Vite::enqueueStyle(
770 'fluentcart-add-to-cart-btn-css',
771 'public/buttons/add-to-cart/style/style.scss'
772 );
773
774 Vite::enqueueStyle(
775 'fluentcart-direct-checkout-btn-css',
776 'public/buttons/direct-checkout/style/style.scss'
777 );
778 }
779
780
781 public static function enqueueThankYouPageAssets()
782 {
783 Vite::enqueueStyle(
784 'fluentcart-thank-you-css',
785 'public/receipt/style/thank_you.scss',
786 );
787 }
788
789 public static function loadMiniCartAssets()
790 {
791 self::markFrontendAssetsRequired();
792 static $isLoaded = false;
793 if ($isLoaded) {
794 return;
795 }
796 $isLoaded = true;
797
798 $app = fluentCart();
799 $slug = $app->config->get('app.slug');
800
801 Vite::enqueueStyle(
802 $slug . '-mini-cart',
803 'public/cart-drawer/mini-cart.scss',
804 );
805 }
806
807 }
808