PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.5.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.5.0
2.5.0 2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 All 34 releases
← All changes | app/Hooks/Handlers/AdminMenuHandler.php +258 -89 1.5.20 → 2.5.0 View file →
@@ -2,15 +2,16 @@
2 2
3 3 namespace FluentBooking\App\Hooks\Handlers;
4 4
5 5 use FluentBooking\App\App;
6 +use FluentBooking\App\Vite;
7 +use FluentBooking\App\Models\Booking;
6 8 use FluentBooking\App\Models\Calendar;
7 -use FluentBooking\App\Models\CalendarSlot;
8 -use FluentBooking\App\Models\User;
9 9 use FluentBooking\App\Services\DateTimeHelper;
10 10 use FluentBooking\App\Services\Helper;
11 11 use FluentBooking\App\Services\PermissionManager;
12 12 use FluentBooking\App\Services\TransStrings;
13 +use FluentBooking\Framework\Support\Arr;
13 14
14 15 class AdminMenuHandler
15 16 {
16 17 public function register()
@@ -16,16 +17,50 @@
16 17 public function register()
17 18 {
18 19 add_action('admin_menu', [$this, 'add']);
19 20
20 - add_action('admin_enqueue_scripts', function () {
21 - if (!isset($_REQUEST['page']) || $_REQUEST['page'] != 'fluent-booking') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
21 + add_action('admin_enqueue_scripts', function ($hookSuffix) {
22 + if ($hookSuffix !== 'toplevel_page_fluent-booking') {
22 23 return;
23 24 }
25 +
24 26 $this->enqueueAssets();
27 +
28 + add_action('admin_head', function () {
29 + $this->printThemeClass();
30 + }, 1);
31 +
25 32 }, 100);
26 33 }
27 34
35 + /**
36 + * Put the dark class on the document before the page paints.
37 + *
38 + * The theme is chosen in the browser, so the server cannot know it - which means
39 + * without this the first frame is always light, and somebody on dark gets a white flash
40 + * on every page load. This runs in <head>, before wp-admin's own markup, and is the
41 + * only reason it is inline rather than in global_admin.js: a file in the footer is too
42 + * late to matter, and one in the header is still a request the paint would wait on.
43 + *
44 + * `fluent_theme_mode` is the key every Fluent plugin shares (see global_admin.js), and it
45 + * holds a bare `light`, `dark` or `system`, so `system` is resolved here against the
46 + * machine's own preference. <body> does not exist yet, so only <html> is marked;
47 + * global_admin.js puts the same classes on <body> and #wpbody-content once it runs.
48 + *
49 + * @return void
50 + */
51 + public function printThemeClass()
52 + {
53 + $script = <<<'JS'
54 +(function(){try{var s=localStorage.getItem('fluent_theme_mode')||'';
55 +if(!s){s=localStorage.getItem('fcal_color_mode')==='dark'?'dark':'light';}
56 +if(s==='system'){s='system:'+(window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light');}
57 +if(s==='dark'||s==='system:dark'){var e=document.documentElement;e.classList.add('fluent_theme_dark');e.classList.add('dark');}}catch(e){}})();
58 +JS;
59 +
60 + echo '<script id="fluent-booking-theme-class">' . $script . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
61 + }
62 +
28 63 public function add()
29 64 {
30 65 $capability = PermissionManager::getMenuPermission();
31 66 $menuPriority = 26;
@@ -83,14 +118,94 @@
83 118 add_submenu_page(
84 119 'fluent-booking',
85 120 __('Settings', 'fluent-booking'),
86 121 __('Settings', 'fluent-booking'),
87 - 'manage_options',
122 + $capability,
88 123 'fluent-booking#/settings/general-settings',
89 124 [$this, 'render']
90 125 );
126 +
127 + if ($this->shouldShowUpgrade()) {
128 + add_submenu_page(
129 + 'fluent-booking',
130 + __('Upgrade to Pro', 'fluent-booking'),
131 + '<span class="fcal_upgrade_pro_menu">' . esc_html__('Upgrade to Pro', 'fluent-booking') . '</span>',
132 + 'manage_options',
133 + Helper::getUpgradeUrl('admin_sidebar_menu')
134 + );
135 +
136 + add_action('admin_head', [$this, 'printUpgradeMenuStyle']);
137 + add_action('admin_footer', [$this, 'printUpgradeMenuScript']);
138 + }
91 139 }
92 140
141 + /**
142 + * The upsell is for whoever can buy the license, so it stays hidden once Pro is loaded
143 + * and from hosts who only reach the app through their own calendars.
144 + *
145 + * @return bool
146 + */
147 + public function shouldShowUpgrade()
148 + {
149 + return !defined('FLUENT_BOOKING_PRO_DIR_FILE') && current_user_can('manage_options');
150 + }
151 +
152 + /**
153 + * Onboarding asks on its final step. The dashboard card waits until the site has a completed
154 + * booking, when FluentBooking has proven useful, and counts one the way the Bookings page's
155 + * "Completed" tab does.
156 + *
157 + * @return array
158 + */
159 + private function getOptinVars()
160 + {
161 + $showPrompt = $this->showOptinPrompt();
162 +
163 + return [
164 + 'share_stats' => Arr::get((array) Helper::getMeta('option', 0, 'optin'), 'share_stats', ''),
165 + 'can_manage' => PermissionManager::userCan('manage_all_data'),
166 + 'show_prompt' => $showPrompt,
167 + 'show_card' => $showPrompt && Booking::applyComputedStatus('completed')->exists(),
168 + ];
169 + }
170 +
171 + /**
172 + * The email opt-in prompt (dashboard card and onboarding) is for free sites that have not
173 + * subscribed yet. Closing it hides it for 30 days.
174 + *
175 + * @return bool
176 + */
177 + private function showOptinPrompt()
178 + {
179 + if (defined('FLUENT_BOOKING_PRO_DIR_FILE') || !PermissionManager::userCan('manage_all_data')) {
180 + return false;
181 + }
182 +
183 + $optin = (array) Helper::getMeta('option', 0, 'optin');
184 +
185 + if (!empty($optin['email'])) {
186 + return false;
187 + }
188 +
189 + return empty($optin['dismissed_at']) || (time() - (int) $optin['dismissed_at']) > 30 * DAY_IN_SECONDS;
190 + }
191 +
192 + public function printUpgradeMenuStyle()
193 + {
194 + echo '<style id="fluent-booking-upgrade-menu">#adminmenu .fcal_upgrade_pro_menu{color:#72aee6;font-weight:500;}</style>';
195 + }
196 +
197 + /**
198 + * The submenu points off-site, and WordPress gives submenu links no target, so it would
199 + * otherwise take the admin away from the dashboard.
200 + *
201 + * @return void
202 + */
203 + public function printUpgradeMenuScript()
204 + {
205 + echo '<script id="fluent-booking-upgrade-menu-js">(function(){var s=document.querySelector("#adminmenu .fcal_upgrade_pro_menu");if(s&&s.parentNode){s.parentNode.setAttribute("target","_blank");s.parentNode.setAttribute("rel","noopener");}})();</script>';
206 + }
207 +
93 208 public function render()
94 209 {
95 210 if (!as_has_scheduled_action('fluent_booking_five_minutes_tasks')) {
96 211 as_schedule_recurring_action(time(), (60 * 5), 'fluent_booking_five_minutes_tasks', [], 'fluent-booking', true);
@@ -95,9 +210,17 @@
95 210 if (!as_has_scheduled_action('fluent_booking_five_minutes_tasks')) {
96 211 as_schedule_recurring_action(time(), (60 * 5), 'fluent_booking_five_minutes_tasks', [], 'fluent-booking', true);
97 212 }
98 213
214 + // The daily task carries the summary report and the MCP record purge.
215 + // A site that never got it — a secondary blog on a network activation,
216 + // or one that lost it — would grow fcal_mcp_g_* rows forever.
217 + if (!as_has_scheduled_action('fluent_booking/daily_tasks')) {
218 + as_schedule_recurring_action(time(), (60 * 60 * 24), 'fluent_booking/daily_tasks', [], 'fluent-booking', true);
219 + }
220 +
99 221 $this->changeFooter();
222 +
100 223 $app = App::getInstance();
101 224
102 225 $config = $app->config;
103 226
@@ -126,9 +249,9 @@
126 249 ],
127 250 [
128 251 'key' => 'scheduled-events',
129 252 'label' => __('Bookings', 'fluent-booking'),
130 - 'permalink' => $baseUrl . 'scheduled-events?period=upcoming&author=me',
253 + 'permalink' => $baseUrl . 'scheduled-events',
131 254 ],
132 255 [
133 256 'key' => 'availability',
134 257 'label' => __('Availability', 'fluent-booking'),
@@ -135,10 +258,11 @@
135 258 'permalink' => $baseUrl . 'availability'
136 259 ]
137 260 ];
138 261
139 - if (current_user_can('manage_options')) {
140 - $menuItems[] = [
262 + $settingItems = [];
263 + if (PermissionManager::userCan('manage_all_data')) {
264 + $settingItems = [
141 265 'key' => 'settings',
142 266 'label' => __('Settings', 'fluent-booking'),
143 267 'permalink' => $baseUrl . 'settings/general-settings'
144 268 ];
@@ -143,28 +267,25 @@
143 267 'permalink' => $baseUrl . 'settings/general-settings'
144 268 ];
145 269 }
146 270
147 - if (!defined('FLUENT_BOOKING_PRO_DIR_FILE')) {
148 - $menuItems[] = [
149 - 'key' => 'buy',
150 - 'label' => __('Upgrade to Pro', 'fluent-booking'),
151 - 'permalink' => Helper::getUpgradeUrl()
152 - ];
153 - }
154 -
155 271 $menuItems = apply_filters('fluent_booking/admin_menu_items', $menuItems);
156 272
157 273 $assets = $app['url.assets'];
158 274
159 275 $portalVars = apply_filters('fluent_booking/admin_portal_vars', [
160 - 'name' => $name,
161 - 'slug' => $slug,
162 - 'menuItems' => $menuItems,
163 - 'baseUrl' => $baseUrl,
164 - 'logo' => $assets . 'images/logo.svg',
276 + 'name' => $name,
277 + 'slug' => $slug,
278 + 'menuItems' => $menuItems,
279 + 'settings' => $settingItems,
280 + 'baseUrl' => $baseUrl,
281 + 'logo' => $assets . 'images/logo.svg',
282 + 'dark_logo' => $assets . 'images/logo_dark.svg',
283 + 'upgradeUrl' => $this->shouldShowUpgrade() ? Helper::getUpgradeUrl('top_bar') : '',
165 284 ]);
166 285
286 + do_action('fluent_booking/admin_app_rendering');
287 +
167 288 $app->view->render('admin.menu', $portalVars);
168 289 }
169 290
170 291 public function changeFooter()
@@ -179,22 +300,20 @@
179 300 return FLUENT_BOOKING_VERSION;
180 301 });
181 302 }
182 303
183 - public function enqueueAssets()
304 + public function enqueueAssets($withApp = true)
184 305 {
185 306 $app = App::getInstance();
186 307
187 308 $isRtl = Helper::fluentbooking_is_rtl();
188 309
189 - $assets = $app['url.assets'];
310 + $slug = $app->config->get('app.slug');
190 311
191 - $slug = $app->config->get('app.slug');
312 + $assetsVersion = Vite::isDev() ? time() : FLUENT_BOOKING_ASSETS_VERSION;
192 313
193 - $adminAppCss = 'admin/admin.css';
194 314 if ($isRtl) {
195 - $adminAppCss = 'admin/admin-rtl.css';
196 - wp_enqueue_style('fluentbooking_admin_rtl', $assets . 'admin/fluentbooking_admin_rtl.css', [], FLUENT_BOOKING_ASSETS_VERSION);
315 + Vite::enqueueStyle('fluentbooking_admin_rtl', 'admin_rtl_css', [], $assetsVersion);
197 316 }
198 317
199 318 add_action('wp_print_scripts', function () {
200 319
@@ -285,28 +404,22 @@
285 404 wp_dequeue_style($wp_styles->registered[$script]->handle);
286 405 }
287 406 }, 999999);
288 407
289 - wp_enqueue_style('fluent_booing_admin_app', $assets . $adminAppCss, [], FLUENT_BOOKING_ASSETS_VERSION, 'all');
408 + Vite::enqueueStyle('fluent_booing_admin_app', 'admin_css', [], $assetsVersion, 'all');
290 409
291 - do_action($slug . '_loading_app');
410 + // The dashboard payload is per-user and includes every host's email address,
411 + // so the app bundle is only ever emitted for an authenticated, permitted viewer.
412 + if (!$withApp || !get_current_user_id()) {
413 + return;
414 + }
292 415
293 - wp_enqueue_script(
294 - $slug . '_admin_app',
295 - $assets . 'admin/app.js',
296 - array('jquery'),
297 - FLUENT_BOOKING_ASSETS_VERSION,
298 - true
299 - );
416 + do_action($slug . '_loading_app'); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
300 417
301 - wp_enqueue_script(
302 - $slug . '_global_admin',
303 - $assets . 'admin/global_admin.js',
304 - array(),
305 - FLUENT_BOOKING_ASSETS_VERSION,
306 - true
307 - );
418 + Vite::enqueueScript($slug . '_admin_app', 'admin_app', array('jquery'), $assetsVersion);
308 419
420 + Vite::enqueueScript($slug . '_global_admin', 'global_admin', array(), $assetsVersion);
421 +
309 422 if (function_exists('wp_enqueue_editor')) {
310 423 add_filter('user_can_richedit', '__return_true');
311 424 wp_enqueue_editor();
312 425 wp_enqueue_media();
@@ -319,8 +432,13 @@
319 432 {
320 433 $assets = $app['url.assets'];
321 434 $currentUser = get_user_by('ID', get_current_user_id());
322 435
436 + $currentUsername = trim($currentUser->first_name . ' ' . $currentUser->last_name);
437 + if (!$currentUsername) {
438 + $currentUsername = $currentUser->display_name;
439 + }
440 +
323 441 $isNew = $this->isNew();
324 442
325 443 $requireSlug = false;
326 444
@@ -355,9 +473,9 @@
355 473 $customFieldTypes = Helper::getCustomFieldTypes();
356 474 $weekSelectTimes = Helper::getWeekSelectTimes();
357 475 $overrideSelectTimes = Helper::getOverrideSelectTimes();
358 476 $statusChangingTimes = Helper::getBookingStatusChangingTimes();
359 - $locationFields = (new CalendarSlot())->getLocationFields();
477 + $defaultTermsAndConditions = Helper::getDefaultTermsAndConditions();
360 478
361 479 return apply_filters('fluent_booking/admin_vars', [
362 480 'slug' => $slug = $app->config->get('app.slug'),
363 481 'nonce' => wp_create_nonce($slug),
@@ -369,9 +487,8 @@
369 487 'multi_durations' => $multiDurations,
370 488 'buffer_times' => $bufferTimes,
371 489 'slot_intervals' => $slotIntervals,
372 490 'schedule_schema' => $scheduleSchema,
373 - 'location_fields' => $locationFields,
374 491 'custom_field_types' => $customFieldTypes,
375 492 'week_select_times' => $weekSelectTimes,
376 493 'duration_lookup' => $durationLookup,
377 494 'multi_duration_lookup' => $multiDurationLookup,
@@ -376,12 +493,14 @@
376 493 'duration_lookup' => $durationLookup,
377 494 'multi_duration_lookup' => $multiDurationLookup,
378 495 'override_select_times' => $overrideSelectTimes,
379 496 'status_changing_times' => $statusChangingTimes,
497 + 'default_terms' => $defaultTermsAndConditions,
380 498 'me' => [
381 499 'id' => $currentUser->ID,
382 500 'calendar_id' => $calendarId,
383 - 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
501 + 'full_name' => $currentUsername,
502 + 'avatar' => get_avatar_url($currentUser->ID, ['size' => 96]),
384 503 'email' => $currentUser->user_email,
385 504 'is_admin' => $hasAllAccess,
386 505 'permissions' => PermissionManager::getUserPermissions($currentUser, false),
387 506 ],
@@ -388,22 +507,35 @@
388 507 'all_hosts' => Calendar::getAllHosts(),
389 508 'is_new' => $isNew,
390 509 'require_slug' => $requireSlug,
391 510 'site_url' => site_url('/'),
511 + 'site_title' => get_bloginfo('name'),
392 512 'upgrade_url' => Helper::getUpgradeUrl(),
393 513 'timezones' => DateTimeHelper::getTimeZones(true),
514 + 'features' => Helper::getFeatures(),
394 515 'supported_features' => apply_filters('fluent_booking/supported_featured', [
395 516 'multi_users' => true
396 517 ]),
397 - 'has_pro' => defined('FLUENT_BOOKING_PRO_DIR_FILE'),
398 - 'require_upgrade' => defined('FLUENT_BOOKING_PRO_DIR_FILE') && !defined('FLUENT_BOOKING_LITE'),
399 - 'dashboard_notices' => apply_filters('fluent_booking/dashboard_notices', []),
400 - 'trans' => TransStrings::getStrings(),
401 - 'date_format' => DateTimeHelper::getDateFormatter(true),
402 - 'time_format' => DateTimeHelper::getTimeFormatter(true),
403 - 'date_time_formatter' => DateTimeHelper::getDateFormatter(true) . ', ' . DateTimeHelper::getTimeFormatter(true),
404 - 'available_date_formats' => DateTimeHelper::getAvailableDateFormats(),
405 - 'admin_url' => admin_url(),
518 + 'i18' => [
519 + 'date_time_config' => DateTimeHelper::getI18nDateTimeConfig(),
520 + ],
521 + 'has_pro' => defined('FLUENT_BOOKING_PRO_DIR_FILE'),
522 + 'require_upgrade' => defined('FLUENT_BOOKING_PRO_DIR_FILE') && !defined('FLUENT_BOOKING_LITE'),
523 + 'dashboard_notices' => apply_filters('fluent_booking/dashboard_notices', []),
524 + 'optin' => $this->getOptinVars(),
525 + 'payment_methods' => apply_filters('fluent_booking/payment/get_all_methods', []),
526 + 'trans' => TransStrings::getStrings(),
527 + 'date_format' => DateTimeHelper::getDateFormatter(true),
528 + 'time_format' => DateTimeHelper::getTimeFormatter(true),
529 + 'date_time_formatter' => DateTimeHelper::getDateFormatter(true) . ', ' . DateTimeHelper::getTimeFormatter(true),
530 + 'available_date_formats' => DateTimeHelper::getAvailableDateFormats(),
531 + 'default_booking_filters' => Helper::getDefaultBookingFilters(),
532 + 'default_paginations' => Helper::getDefaultPaginations(),
533 + 'pref_settings' => Helper::getPrefSettings(),
534 + 'settings_menu_items' => static::settingsMenuItems(),
535 + 'admin_url' => admin_url(),
536 + 'is_rtl' => Helper::fluentbooking_is_rtl(),
537 + 'iframe_html' => Helper::getIframeHtml()
406 538 ]);
407 539 }
408 540
409 541 protected function getRestInfo($app)
@@ -472,9 +604,9 @@
472 604 $urlAssets = $app['url.assets'];
473 605
474 606 return apply_filters('fluent_booking/settings_menu_items', [
475 607 'general_settings' => [
476 - 'title' => __('General Settings', 'fluent-booking'),
608 + 'title' => __('General', 'fluent-booking'),
477 609 'disable' => false,
478 610 'el_icon' => 'Operation',
479 611 'component_type' => 'StandAloneComponent',
480 612 'class' => 'general_settings',
@@ -491,8 +623,44 @@
491 623 'route' => [
492 624 'name' => 'team_members'
493 625 ]
494 626 ],
627 + 'payment_settings' => [
628 + 'title' => __('Payment', 'fluent-booking'),
629 + 'disable' => true,
630 + 'el_icon' => 'Money',
631 + 'component_type' => 'PaymentSettingsComponent',
632 + 'class' => 'payment_settings',
633 + 'route' => [
634 + 'name' => 'global_payment_settings',
635 + ],
636 + 'children' => [
637 + 'payment_settings' => [
638 + 'title' => __('Settings', 'fluent-booking'),
639 + 'disable' => true,
640 + 'route' => [
641 + 'name' => 'global_payment_settings'
642 + ]
643 + ],
644 + 'payment_methods' => [
645 + 'title' => __('Payment Methods', 'fluent-booking'),
646 + 'disable' => true,
647 + 'route' => [
648 + 'name' => 'payment_methods',
649 + 'params' => [
650 + 'settings_key' => 'stripe'
651 + ]
652 + ]
653 + ],
654 + 'payment_coupons' => [
655 + 'title' => __('Coupons', 'fluent-booking'),
656 + 'disable' => true,
657 + 'route' => [
658 + 'name' => 'payment_coupons'
659 + ]
660 + ]
661 + ],
662 + ],
495 663 'google' => [
496 664 'title' => __('Google Calendar / Meet', 'fluent-booking'),
497 665 'disable' => true,
498 666 'icon_url' => $urlAssets . 'images/gg-calendar.svg',
@@ -530,9 +698,9 @@
530 698 ],
531 699 'next_cloud_calendar' => [
532 700 'title' => __('Nextcloud Calendar', 'fluent-booking'),
533 701 'disable' => true,
534 - 'icon_url' => $urlAssets . 'images/Ncloud.svg',
702 + 'svg_icon' => '<svg xmlns="http://www.w3.org/2000/svg" height="800" width="1200" viewBox="-17.0838 -18.65685 148.0596 111.9411"><path d="M57.0328 0C45.2275 0 35.2216 8.0032 32.1204 18.8466c-2.6952-5.7515-8.5359-9.781-15.2633-9.781C7.6053 9.0657 0 16.671 0 25.9228c0 9.2519 7.6052 16.8606 16.857 16.8606 6.7275 0 12.5682-4.0319 15.2635-9.7844 3.1011 10.8442 13.107 18.85 24.9123 18.85 11.718 0 21.6729-7.885 24.8533-18.607 2.745 5.622 8.5135 9.5414 15.1454 9.5414 9.2518 0 16.8605-7.6087 16.8605-16.8606 0-9.2518-7.6087-16.857-16.8605-16.857-6.632 0-12.4003 3.917-15.1454 9.5378C78.7057 7.8825 68.7507 0 57.0328 0zm0 9.8955c8.9116 0 16.0307 7.1156 16.0307 16.0272s-7.119 16.0308-16.0307 16.0308c-8.9116 0-16.0272-7.1192-16.0272-16.0308S48.1212 9.8955 57.0328 9.8955zm-40.1757 9.0657c3.9044 0 6.965 3.0571 6.965 6.9615s-3.0606 6.965-6.965 6.965-6.9616-3.0606-6.9616-6.965 3.0572-6.9615 6.9616-6.9615zm80.1744 0c3.9044 0 6.965 3.0571 6.965 6.9615s-3.0606 6.965-6.965 6.965-6.9616-3.0606-6.9616-6.965 3.0572-6.9615 6.9616-6.9615z" color="currentColor" font-weight="400" font-family="sans-serif" overflow="visible" fill="currentColor"></path><path d="M29.1085 63.7615c2.7752 0 4.3275 1.9756 4.3275 4.939 0 .2822-.2352.5174-.5174.5174h-7.4792c.047 2.6342 1.8816 4.1394 3.9983 4.1394 1.3171 0 2.2579-.5644 2.7283-.9408.2822-.1881.5174-.141.6585.1412l.1411.2352c.1411.2351.094.4703-.1411.6585-.5645.4233-1.7875 1.129-3.4338 1.129-3.0575 0-5.4094-2.2109-5.4094-5.4095.047-3.3868 2.3048-5.4094 5.1272-5.4094zm2.8693 4.4216c-.094-2.1638-1.4112-3.2457-2.9164-3.2457-1.7404 0-3.2456 1.129-3.575 3.2457zm15.584-2.9164v-3.622c0-.3292.1882-.5174.5174-.5174h.3764c.3292 0 .4703.1882.4703.5174v2.446h2.1168c.3292 0 .5174.1882.5174.5174v.1412c0 .3292-.1882.4703-.5174.4703h-2.1168v5.1743c0 2.399 1.4582 2.6812 2.2579 2.7282.4233.047.5644.1411.5644.5174v.2823c0 .3292-.141.4704-.5644.4704-2.2579 0-3.622-1.3642-3.622-3.8102zm10.7718-1.5052c1.7875 0 2.9164.7526 3.4339 1.176.2351.188.2822.4233.047.7055l-.1411.2352c-.1882.2822-.4234.2822-.7056.094-.4704-.3292-1.3641-.9407-2.5871-.9407-2.2579 0-4.0453 1.6934-4.0453 4.1864 0 2.446 1.7874 4.1394 4.0453 4.1394 1.4582 0 2.446-.6585 2.9164-1.0819.2822-.1881.4704-.141.6585.1411l.1411.1882c.1411.2822.0941.4704-.141.7056-.5175.4233-1.7876 1.317-3.6691 1.317-3.0575 0-5.4094-2.2108-5.4094-5.4094.047-3.1986 2.399-5.4564 5.4564-5.4564zm6.2562-3.3398c0-.3292-.1882-.5174.141-.5174h.3764c.3293 0 .8467.1882.8467.5174V71.664c0 1.3171.6115 1.4582 1.0819 1.5053.2352 0 .4233.141.4233.4703v.3293c0 .3293-.1411.5174-.5174.5174-.8467 0-2.352-.2822-2.352-2.54zm9.6429 3.3398c3.0104 0 5.4564 2.3048 5.4564 5.3623 0 3.1046-2.446 5.4565-5.4564 5.4565-3.0105 0-5.4565-2.352-5.4565-5.4565 0-3.0575 2.446-5.3623 5.4565-5.3623zm0 9.5958c2.2108 0 3.9982-1.7875 3.9982-4.2335 0-2.3519-1.7874-4.0923-3.9982-4.0923s-4.0454 1.7875-4.0454 4.0923c.047 2.399 1.8346 4.2335 4.0454 4.2335zm23.4722-9.5958c2.493 0 3.3868 2.0697 3.3868 2.0697h.047s-.047-.3293-.047-.7997v-4.6568c0-.3293-.1412-.5174.1881-.5174h.3763c.3293 0 .8467.1881.8467.5174v13.406c0 .3292-.1411.5174-.4704.5174h-.3292c-.3293 0-.5175-.1411-.5175-.4704v-.7997c0-.3763.0941-.6585.0941-.6585h-.047s-.8938 2.1638-3.575 2.1638c-2.7752 0-4.5157-2.2108-4.5157-5.4095-.094-3.1986 1.8345-5.3623 4.5628-5.3623zm.047 9.5958c1.7404 0 3.3398-1.223 3.3398-4.1864 0-2.1167-1.082-4.1394-3.2927-4.1394-1.8345 0-3.3398 1.5052-3.3398 4.1394.047 2.54 1.3641 4.1864 3.2927 4.1864zm-85.8923.9878h.3763c.3293 0 .5174-.1881.5174-.5174V63.7274c0-1.5993 1.7404-2.7412 3.716-2.7412 1.9757 0 3.7161 1.142 3.7161 2.7412v10.1003c0 .3293.1882.5174.5174.5174h.3763c.3293 0 .4704-.1881.4704-.5174V63.6674c0-2.6812-2.6812-3.9983-5.1272-3.9983-2.3519 0-5.0331 1.317-5.0331 3.9983v10.1603c0 .3293.1411.5174.4704.5174zm78.5073-10.3485h-.3763c-.3293 0-.5175.1882-.5175.5175v5.6916c0 1.5993-1.0348 3.0575-3.0575 3.0575-1.9756 0-3.0575-1.4582-3.0575-3.0575v-5.6916c0-.3293-.1881-.5175-.5174-.5175h-.3763c-.3293 0-.4704.1882-.4704.5175v6.068c0 2.6811 1.9756 3.9982 4.4216 3.9982 2.446 0 4.4217-1.317 4.4217-3.9983v-6.068c.047-.3292-.1412-.5174-.4704-.5174zm-46.5643-.0771c-.1152.0184-.2258.0953-.3317.2214L41.5673 66.41l-1.4249 1.6978-2.1571-2.5715-1.1705-1.3955c-.1058-.1261-.2257-.195-.35-.2058-.1244-.0107-.2533.0366-.3795.1424l-.2884.2416c-.2523.2117-.2392.446-.0276.6982l1.9045 2.2693 1.5793 1.8815-2.3124 2.7553c-.0018.002-.0029.0043-.0046.0064l-1.1668 1.39c-.2117.2523-.188.5178.0643.7295l.2885.2407c.2522.2116.481.1585.6927-.0937l1.9036-2.2693 1.425-1.6977 2.157 2.5715c.0015.0016.0032.0029.0047.0046l1.1668 1.391c.2116.2521.4763.275.7285.0633l.2885-.2416c.2522-.2117.2392-.446.0275-.6983l-1.9045-2.2692-1.5792-1.8815 2.3124-2.7553c.0017-.002.0028-.0044.0046-.0064l1.1668-1.39c.2116-.2523.1879-.5179-.0643-.7295l-.2885-.2416c-.1261-.1058-.2459-.1452-.361-.1268z" fill="currentColor"></path></svg>',
535 703 'component_type' => 'GlobalSettingsComponent',
536 704 'class' => 'configure_nextcloud_calendar',
537 705 'route' => [
538 706 'name' => 'configure-integrations',
@@ -553,9 +721,9 @@
553 721 ],
554 722 'twilio' => [
555 723 'title' => __('SMS by Twilio', 'fluent-booking'),
556 724 'disable' => true,
557 - 'icon_url' => $urlAssets . 'images/tw.svg',
725 + 'svg_icon' => '<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24" id="twilio"><path d="M0,12c0,6.64,5.359,12,12,12s12-5.36,12-12c0-6.641-5.36-12-12-12C5.381-0.008,0.008,5.352,0,11.971V12z M3.199,12c-0.014-4.846,3.904-8.786,8.75-8.801H12c4.847-0.014,8.786,3.904,8.801,8.75V12c0.015,4.847-3.904,8.786-8.75,8.801H12c-4.846,0.015-8.786-3.904-8.801-8.75V12z"></path><path d="M14.959 17.44c1.361 0 2.481-1.12 2.481-2.48 0-1.359-1.12-2.48-2.481-2.479-1.359 0-2.479 1.12-2.479 2.479C12.486 16.326 13.592 17.432 14.959 17.44zM14.959 11.52c1.361 0 2.481-1.12 2.481-2.479 0-1.361-1.12-2.481-2.481-2.481-1.359 0-2.479 1.12-2.479 2.481C12.487 10.407 13.593 11.513 14.959 11.52zM9.042 17.44c1.359 0 2.479-1.12 2.479-2.48 0-1.359-1.121-2.48-2.479-2.479-1.361 0-2.481 1.12-2.481 2.479C6.567 16.327 7.674 17.433 9.042 17.44zM9.042 11.52c1.359 0 2.479-1.12 2.479-2.479 0-1.361-1.121-2.481-2.479-2.481-1.361 0-2.481 1.12-2.481 2.481C6.567 10.408 7.675 11.513 9.042 11.52z"></path></svg>',
558 726 'component_type' => 'GlobalSettingsComponent',
559 727 'class' => 'configure_twilio',
560 728 'route' => [
561 729 'name' => 'configure-integrations',
@@ -563,34 +731,8 @@
563 731 'settings_key' => 'twilio'
564 732 ]
565 733 ]
566 734 ],
567 - 'stripe' => [
568 - 'title' => __('Stripe', 'fluent-booking'),
569 - 'disable' => true,
570 - 'icon_url' => $urlAssets . 'images/payment-methods/stripe.svg',
571 - 'component_type' => 'GlobalSettingsComponent',
572 - 'class' => 'stripe_payment',
573 - 'route' => [
574 - 'name' => 'PaymentSettingsIndex',
575 - 'params' => [
576 - 'settings_key' => 'stripe'
577 - ]
578 - ]
579 - ],
580 - 'paypal' => [
581 - 'title' => __('PayPal', 'fluent-booking'),
582 - 'disable' => true,
583 - 'icon_url' => $urlAssets . 'images/payment-methods/paypal.svg',
584 - 'component_type' => 'GlobalSettingsComponent',
585 - 'class' => 'paypal_payment',
586 - 'route' => [
587 - 'name' => 'PaymentSettingsIndex',
588 - 'params' => [
589 - 'settings_key' => 'paypal'
590 - ]
591 - ]
592 - ],
593 735 'license' => [
594 736 'title' => __('License', 'fluent-booking'),
595 737 'disable' => true,
596 738 'el_icon' => 'Lock',
@@ -598,9 +740,9 @@
598 740 'class' => 'configure_license',
599 741 'route' => [
600 742 'name' => 'license'
601 743 ]
602 - ],
744 + ]
603 745 ]);
604 746 }
605 747
606 748 public static function getEventSettingsMenuItems($event)
@@ -617,9 +759,9 @@
617 759 'event_id' => $event->id
618 760 ]
619 761 ],
620 762 'label' => __('Event Details', 'fluent-booking'),
621 - 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M8 2V5" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 2V5" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 13H15" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 17H12" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 3.5C19.33 3.68 21 4.95 21 9.65V15.83C21 19.95 20 22.01 15 22.01H9C4 22.01 3 19.95 3 15.83V9.65C3 4.95 4.67 3.69 8 3.5H16Z" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
763 + 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M8 2V5" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 2V5" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 13H15" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 17H12" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 3.5C19.33 3.68 21 4.95 21 9.65V15.83C21 19.95 20 22.01 15 22.01H9C4 22.01 3 19.95 3 15.83V9.65C3 4.95 4.67 3.69 8 3.5H16Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
622 764 ],
623 765 'assignment' => [
624 766 'type' => 'route',
625 767 'visible' => false,
@@ -645,9 +787,9 @@
645 787 'event_id' => $event->id
646 788 ]
647 789 ],
648 790 'label' => __('Availability', 'fluent-booking'),
649 - 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M6.66666 1.66699V4.16699" stroke="#445164" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.3333 1.66699V4.16699" stroke="#445164" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.91666 7.5752H17.0833" stroke="#445164" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M17.5 7.08366V14.167C17.5 16.667 16.25 18.3337 13.3333 18.3337H6.66667C3.75 18.3337 2.5 16.667 2.5 14.167V7.08366C2.5 4.58366 3.75 2.91699 6.66667 2.91699H13.3333C16.25 2.91699 17.5 4.58366 17.5 7.08366Z" stroke="#445164" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.0789 11.4167H13.0864" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.0789 13.9167H13.0864" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.99623 11.4167H10.0037" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.99623 13.9167H10.0037" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.91194 11.4167H6.91942" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.91194 13.9167H6.91942" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
791 + 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M6.66666 1.66699V4.16699" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.3333 1.66699V4.16699" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.91666 7.5752H17.0833" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M17.5 7.08366V14.167C17.5 16.667 16.25 18.3337 13.3333 18.3337H6.66667C3.75 18.3337 2.5 16.667 2.5 14.167V7.08366C2.5 4.58366 3.75 2.91699 6.66667 2.91699H13.3333C16.25 2.91699 17.5 4.58366 17.5 7.08366Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.0789 11.4167H13.0864" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.0789 13.9167H13.0864" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.99623 11.4167H10.0037" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.99623 13.9167H10.0037" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.91194 11.4167H6.91942" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.91194 13.9167H6.91942" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
650 792 ],
651 793 'limit_settings' => [
652 794 'type' => 'route',
653 795 'visible' => true,
@@ -673,9 +815,9 @@
673 815 'event_id' => $event->id
674 816 ]
675 817 ],
676 818 'label' => __('Question Settings', 'fluent-booking'),
677 - 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 2V5" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 2V5" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 13.8714V13.6441C12 12.908 12.5061 12.5182 13.0121 12.2043C13.5061 11.9012 14 11.5115 14 10.797C14 9.8011 13.1085 9 12 9C10.8915 9 10 9.8011 10 10.797" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M11.9945 16.4587H12.0053" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 3.5C19.33 3.67504 21 4.91005 21 9.48055V15.4903C21 19.4968 20 21.5 15 21.5H9C4 21.5 3 19.4968 3 15.4903V9.48055C3 4.91005 4.67 3.68476 8 3.5H16Z" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
819 + 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 2V5" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 2V5" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 13.8714V13.6441C12 12.908 12.5061 12.5182 13.0121 12.2043C13.5061 11.9012 14 11.5115 14 10.797C14 9.8011 13.1085 9 12 9C10.8915 9 10 9.8011 10 10.797" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M11.9945 16.4587H12.0053" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 3.5C19.33 3.67504 21 4.91005 21 9.48055V15.4903C21 19.4968 20 21.5 15 21.5H9C4 21.5 3 19.4968 3 15.4903V9.48055C3 4.91005 4.67 3.68476 8 3.5H16Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
678 820 ],
679 821 'email_notification' => [
680 822 'type' => 'route',
681 823 'visible' => true,
@@ -703,8 +845,22 @@
703 845 ],
704 846 'label' => __('SMS Notification', 'fluent-booking'),
705 847 'elIcon' => 'Notification'
706 848 ],
849 + 'recurring_settings' => [
850 + 'type' => 'route',
851 + 'visible' => ($event->isOneToOne() || $event->isGroup()) ? true : false,
852 + 'disable' => true,
853 + 'route' => [
854 + 'name' => 'recurring_settings',
855 + 'params' => [
856 + 'calendar_id' => $event->calendar_id,
857 + 'event_id' => $event->id
858 + ]
859 + ],
860 + 'label' => __('Recurring Settings', 'fluent-booking'),
861 + 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M6 4H21C21.5523 4 22 4.44772 22 5V12H20V6H6V9L1 5L6 1V4ZM18 20H3C2.44772 20 2 19.5523 2 19V12H4V18H18V15L23 19L18 23V20Z"></path></svg>'
862 + ],
707 863 'advanced_settings' => [
708 864 'type' => 'route',
709 865 'visible' => true,
710 866 'disable' => true,
@@ -776,9 +932,22 @@
776 932 'calendar_id' => $calendar->id
777 933 ]
778 934 ],
779 935 'label' => __('Calendar Settings', 'fluent-booking'),
780 - 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M2 12.8799V11.1199C2 10.0799 2.85 9.21994 3.9 9.21994C5.71 9.21994 6.45 7.93994 5.54 6.36994C5.02 5.46994 5.33 4.29994 6.24 3.77994L7.97 2.78994C8.76 2.31994 9.78 2.59994 10.25 3.38994L10.36 3.57994C11.26 5.14994 12.74 5.14994 13.65 3.57994L13.76 3.38994C14.23 2.59994 15.25 2.31994 16.04 2.78994L17.77 3.77994C18.68 4.29994 18.99 5.46994 18.47 6.36994C17.56 7.93994 18.3 9.21994 20.11 9.21994C21.15 9.21994 22.01 10.0699 22.01 11.1199V12.8799C22.01 13.9199 21.16 14.7799 20.11 14.7799C18.3 14.7799 17.56 16.0599 18.47 17.6299C18.99 18.5399 18.68 19.6999 17.77 20.2199L16.04 21.2099C15.25 21.6799 14.23 21.3999 13.76 20.6099L13.65 20.4199C12.75 18.8499 11.27 18.8499 10.36 20.4199L10.25 20.6099C9.78 21.3999 8.76 21.6799 7.97 21.2099L6.24 20.2199C5.33 19.6999 5.02 18.5299 5.54 17.6299C6.45 16.0599 5.71 14.7799 3.9 14.7799C2.85 14.7799 2 13.9199 2 12.8799Z" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
936 + 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M2 12.8799V11.1199C2 10.0799 2.85 9.21994 3.9 9.21994C5.71 9.21994 6.45 7.93994 5.54 6.36994C5.02 5.46994 5.33 4.29994 6.24 3.77994L7.97 2.78994C8.76 2.31994 9.78 2.59994 10.25 3.38994L10.36 3.57994C11.26 5.14994 12.74 5.14994 13.65 3.57994L13.76 3.38994C14.23 2.59994 15.25 2.31994 16.04 2.78994L17.77 3.77994C18.68 4.29994 18.99 5.46994 18.47 6.36994C17.56 7.93994 18.3 9.21994 20.11 9.21994C21.15 9.21994 22.01 10.0699 22.01 11.1199V12.8799C22.01 13.9199 21.16 14.7799 20.11 14.7799C18.3 14.7799 17.56 16.0599 18.47 17.6299C18.99 18.5399 18.68 19.6999 17.77 20.2199L16.04 21.2099C15.25 21.6799 14.23 21.3999 13.76 20.6099L13.65 20.4199C12.75 18.8499 11.27 18.8499 10.36 20.4199L10.25 20.6099C9.78 21.3999 8.76 21.6799 7.97 21.2099L6.24 20.2199C5.33 19.6999 5.02 18.5299 5.54 17.6299C6.45 16.0599 5.71 14.7799 3.9 14.7799C2.85 14.7799 2 13.9199 2 12.8799Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
937 + ],
938 + 'calendar_events' => [
939 + 'type' => 'route',
940 + 'visible' => true,
941 + 'disable' => false,
942 + 'route' => [
943 + 'name' => 'calendar_events',
944 + 'params' => [
945 + 'calendar_id' => $calendar->id
946 + ]
947 + ],
948 + 'label' => __('Event Types', 'fluent-booking'),
949 + 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 6H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8 12H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8 18H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 6H3.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 12H3.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 18H3.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
781 950 ],
782 951 'remote_calendars' => [
783 952 'type' => 'route',
784 953 'visible' => true,