PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.18
ووسلام – همگام سازی ووکامرس و باسلام v1.10.18
1.10.18 1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.2 1.9.1 1.9.0 1.8.8 1.8.5 1.8.6 1.8.7 1.8.4 All 51 releases
sync-basalam / templates / admin / CategoryMapping.php

CategoryMapping.php in ووسلام – همگام سازی ووکامرس و باسلام 1.10.18, at templates/admin/CategoryMapping.php

687 lines 25.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use SyncBasalam\Admin\Settings\SettingsConfig;
4
5 defined('ABSPATH') || exit;
6
7 if (!syncBasalamSettings()->getSettings(SettingsConfig::TOKEN)) {
8 require_once(syncBasalamPlugin()->templatePath() . "/admin/main/NotConnected.php");
9 return;
10 }
11 ?>
12
13 <div class="basalam-container">
14 <div class="basalam-header">
15 <div class="basalam-header-data">
16 <img src="<?php echo esc_url(syncBasalamPlugin()->assetsUrl() . '/images/basalam.svg'); ?>" alt="Basalam">
17 <div class="basalam-text-right basalam-flex-fill">
18 <h2 class="basalam-h basalam-margin-bottom-8">اتصال دسته‌بندی‌ها</h2>
19 <p class="basalam-p basalam-text-right">
20 هنگا�
21 ایجاد یا آپدیت �
22 حصول در باسلا�
23 ، پلاگین ابتدا اتصال دسته‌بندی �
24 یان ووکا�
25 رس و باسلا�
26 را بررسی �
27 ی‌کند و در صورت وجود از آن استفاده خواهد شد. اگر اتصالی وجود نداشته باشد، دسته‌بندی بر اساس عنوان �
28 حصول و با ک�
29 ک هوش �
30 صنوعی تعیین �
31 ی‌شود.
32 </p>
33 </div>
34 </div>
35 </div>
36
37 <!-- Warning Note -->
38 <div class="basalam-bg-warning-section">
39 <div class="basalam-flex-start basalam-gap-10">
40 <span class="dashicons dashicons-warning basalam-warning-20"></span>
41 <div class="basalam-flex-fill">
42 <p class="basalam-p basalam-margin-warning-justify">
43 اگر قبلاً �
44 حصولات خود را به باسلا�
45 اضافه کرده‌اید و �
46 ی‌خواهید دسته‌بندی آن‌ها را تغییر دهید، باید این کار را از طریق پنل باسلا�
47 انجا�
48 دهید:
49 <a href="https://vendor.basalam.com/change-categories" target="_blank" class="basalam-warning-underline">
50 تغییر دسته‌بندی �
51 حصولات در باسلا�
52
53 </a>
54 <br>
55 اتصال دسته‌بندی در این صفحه تنها برای �
56 حصولاتی که هنوز به باسلا�
57 اضافه نشده‌اند یا �
58 حصولات جدید اع�
59 ال �
60 ی‌شود.
61 </p>
62 </div>
63 </div>
64 </div>
65
66 <!-- Mapping Actions Section -->
67 <div class="basalam-setup-wizard basalam-margin-bottom-20">
68 <div class="basalam-step basalam-opacity-1">
69 <h3 class="basalam-h basalam-margin-bottom-16 basalam-text-right">انجا�
70 اتصال</h3>
71 <p class="basalam-p basalam-text-right basalam-margin-bottom-20">دسته‌بندی ووکا�
72 رس را انتخاب کنید و سپس دسته‌بندی �
73 ربوطه در باسلا�
74 را انتخاب کنید.</p>
75
76 <div class="selected-categories basalam-margin-20-0">
77 <div class="basalam-grid-3">
78 <div id="selected-woo-category" class="basalam-padding-box-center">
79 دسته‌بندی ووکا�
80 رس انتخاب نشده
81 </div>
82 <span class="dashicons dashicons-arrow-left-alt2 basalam-arrow-large"></span>
83 <div id="selected-basalam-category" class="basalam-padding-box-center">
84 دسته‌بندی باسلا�
85 انتخاب نشده
86 </div>
87 </div>
88 </div>
89
90 <div class="basalam-text-center basalam-margin-top-20">
91 <button id="create-mapping-btn" class="basalam-btn basalam-btn-primary basalam-margin-left-10 basalam-padding-0-20" disabled>
92 ایجاد اتصال
93 </button>
94 <button id="clear-selection-btn" class="basalam-btn basalam-btn-secondary basalam-margin-right-10 basalam-padding-0-20">
95 پاک کردن انتخاب
96 </button>
97 </div>
98 </div>
99 </div>
100
101 <!-- Categories Grid -->
102 <div class="category-mapping-grid basalam-grid-2">
103 <!-- WooCommerce Categories Section -->
104 <div class="basalam-setup-wizard">
105 <div class="basalam-step basalam-opacity-1-padding">
106 <h3 class="basalam-h basalam-margin-bottom-16 basalam-text-right">
107 <span class="dashicons dashicons-wordpress basalam-margin-left-8"></span>
108 دسته‌بندی‌های ووکا�
109 رس
110 </h3>
111
112 <div class="search-container basalam-margin-bottom-15">
113 <input type="text" id="woo-category-search" placeholder="جستجوی دسته‌بندی..."
114 class="basalam-input-search">
115 </div>
116
117 <div id="woo-categories-list" class="basalam-height-400-scroll">
118 <div class="loading-spinner basalam-text-center-padding">
119 <span class="spinner is-active"></span>
120 در حال بارگذاری دسته‌بندی‌ها...
121 </div>
122 </div>
123 </div>
124 </div>
125
126 <!-- Basalam Categories Section -->
127 <div class="basalam-setup-wizard">
128 <div class="basalam-step basalam-opacity-1-padding">
129 <h3 class="basalam-h basalam-margin-bottom-16 basalam-text-right">
130 <span class="dashicons dashicons-external basalam-margin-left-8"></span>
131 دسته‌بندی‌های باسلا�
132
133 </h3>
134
135 <div class="search-container basalam-margin-bottom-15">
136 <input type="text" id="basalam-category-search" placeholder="جستجوی دسته‌بندی..."
137 class="basalam-input-search">
138 </div>
139
140 <div id="basalam-categories-list" class="basalam-height-400-scroll">
141 <div class="loading-spinner basalam-text-center-padding">
142 <span class="spinner is-active"></span>
143 در حال بارگذاری دسته‌بندی‌ها...
144 </div>
145 </div>
146 </div>
147 </div>
148 </div>
149
150 <!-- Current Mappings -->
151 <div class="basalam-setup-wizard basalam-margin-top-20">
152 <div class="basalam-step basalam-opacity-1">
153 <h3 class="basalam-h basalam-margin-bottom-16 basalam-text-right">
154 <span class="dashicons dashicons-networking basalam-margin-left-8"></span>
155 اتصال‌های فعلی
156 </h3>
157 <div id="mappings-list">
158 <div class="loading-spinner basalam-text-center-padding">
159 <span class="spinner is-active"></span>
160 در حال بارگذاری اتصال‌ها...
161 </div>
162 </div>
163 </div>
164 </div>
165 </div>
166
167 <style>
168 .category-item {
169 padding: 12px 15px;
170 border-bottom: 1px solid var(--basalam-gray-200);
171 cursor: pointer;
172 transition: all 0.2s ease;
173 display: flex;
174 justify-content: space-between;
175 align-items: center;
176 font-family: PelakFA;
177 color: var(--basalam-gray-700);
178 }
179
180 .category-item.selectable:hover {
181 background-color: var(--basalam-gray-100);
182 }
183
184 .category-item.non-selectable:hover {
185 background-color: var(--basalam-gray-50);
186 }
187
188 .category-item.selected {
189 background-color:
190 border-right: 3px solid var(--basalam-primary-color);
191 color: var(--basalam-gray-800);
192 font-weight: 600;
193 }
194
195 .category-item:last-child {
196 border-bottom: none;
197 }
198
199 .category-hierarchy {
200 font-size: 12px;
201 color: var(--basalam-gray-500);
202 margin-top: 4px;
203 font-family: PelakFA;
204 }
205
206 .mapping-item {
207 display: grid;
208 grid-template-columns: 1fr auto 1fr auto;
209 gap: 15px;
210 align-items: center;
211 padding: 15px;
212 border-bottom: 1px solid var(--basalam-gray-200);
213 font-family: PelakFA;
214 }
215
216 .mapping-item:last-child {
217 border-bottom: none;
218 }
219
220 .mapping-arrow {
221 color: var(--basalam-gray-500);
222 font-size: 18px;
223 }
224
225 .delete-mapping-btn {
226 background: var(--basalam-danger-color);
227 color: white;
228 border: none;
229 padding: 6px 12px;
230 border-radius: 4px;
231 cursor: pointer;
232 font-size: 12px;
233 font-family: PelakFA;
234 transition: background 0.2s ease;
235 }
236
237 .delete-mapping-btn:hover {
238 background: var(--basalam-danger-hover);
239 }
240
241 .no-categories {
242 text-align: center;
243 padding: 40px 20px;
244 color: var(--basalam-gray-500);
245 font-family: PelakFA;
246 }
247
248 .basalam-category-parent {
249 font-size: 12px;
250 color: var(--basalam-gray-500);
251 margin-bottom: 5px;
252 font-family: PelakFA;
253 }
254
255 .basalam-category-wrapper {
256 position: relative;
257 }
258
259 .children-container {
260 margin-right: 15px;
261 border-right: 2px solid var(--basalam-gray-200);
262 }
263
264 .expand-icon {
265 transition: transform 0.2s;
266 color: var(--basalam-gray-500);
267 }
268
269 .expand-icon:hover {
270 color: var(--basalam-primary-color);
271 }
272
273 .basalam-btn {
274 border: none;
275 border-radius: 6px;
276 cursor: pointer;
277 font-family: PelakFA;
278 font-size: 14px;
279 transition: all 0.2s ease;
280 text-decoration: none;
281 display: inline-block;
282 }
283
284 .basalam-btn-primary {
285 background: var(--basalam-primary-color);
286 color: white;
287 }
288
289 .basalam-btn-primary:hover:not(:disabled) {
290 background: var(--basalam-primary-hover);
291 }
292
293 .basalam-btn-primary:disabled {
294 background: var(--basalam-gray-400);
295 cursor: not-allowed;
296 }
297
298 .basalam-btn-secondary {
299 background: var(--basalam-gray-200);
300 color: var(--basalam-gray-700);
301 }
302
303 .basalam-btn-secondary:hover {
304 background: var(--basalam-gray-300);
305 }
306 </style>
307
308 <script>
309 jQuery(document).ready(function($) {
310 let selectedWooCategory = null;
311 let selectedBasalamCategory = null;
312 let wooCategories = [];
313 let basalamCategories = [];
314 let categoryMappings = [];
315
316 loadWooCommerceCategories();
317 loadBasalamCategories();
318 loadCategoryMappings();
319
320 $('#woo-category-search').on('input', function() {
321 const searchTerm = $(this).val().toLowerCase();
322 filterWooCategories(searchTerm);
323 });
324
325 $('#basalam-category-search').on('input', function() {
326 const searchTerm = $(this).val().toLowerCase();
327 filterBasalamCategories(searchTerm);
328 });
329
330 $('#create-mapping-btn').on('click', function() {
331 if (selectedWooCategory && selectedBasalamCategory) {
332 createCategoryMapping();
333 }
334 });
335
336 $('#clear-selection-btn').on('click', function() {
337 clearSelection();
338 });
339
340 function loadWooCommerceCategories() {
341 $.ajax({
342 url: ajaxurl,
343 method: 'POST',
344 data: {
345 action: 'get_woocommerce_categories',
346 _wpnonce: '<?php echo esc_attr(wp_create_nonce('get_woocommerce_categories_nonce')); ?>'
347 },
348 success: function(response) {
349 if (response.success) {
350 wooCategories = response.data;
351 renderWooCategories(wooCategories);
352 } else {
353 $('#woo-categories-list').html('<div class="no-categories">خطا در بارگذاری دسته‌بندی‌ها</div>');
354 }
355 },
356 error: function() {
357 $('#woo-categories-list').html('<div class="no-categories">خطا در اتصال به سرور</div>');
358 }
359 });
360 }
361
362 function loadBasalamCategories() {
363 $.ajax({
364 url: ajaxurl,
365 method: 'POST',
366 data: {
367 action: 'get_basalam_categories',
368 _wpnonce: '<?php echo esc_attr(wp_create_nonce('get_basalam_categories_nonce')); ?>'
369 },
370 success: function(response) {
371 if (response.success) {
372 basalamCategories = response.data;
373 renderBasalamCategories(basalamCategories);
374 } else {
375 $('#basalam-categories-list').html('<div class="no-categories">خطا در بارگذاری دسته‌بندی‌ها</div>');
376 }
377 },
378 error: function() {
379 $('#basalam-categories-list').html('<div class="no-categories">خطا در اتصال به سرور</div>');
380 }
381 });
382 }
383
384 function loadCategoryMappings() {
385 $.ajax({
386 url: ajaxurl,
387 method: 'POST',
388 data: {
389 action: 'get_category_mappings',
390 _wpnonce: '<?php echo esc_attr(wp_create_nonce('get_category_mappings_nonce')); ?>'
391 },
392 success: function(response) {
393 if (response.success) {
394 categoryMappings = response.data;
395 renderCategoryMappings(categoryMappings);
396 } else {
397 $('#mappings-list').html('<div class="no-categories">هیچ اتصالی یافت نشد</div>');
398 }
399 },
400 error: function() {
401 $('#mappings-list').html('<div class="no-categories">خطا در بارگذاری اتصال‌ها</div>');
402 }
403 });
404 }
405
406 function renderWooCategories(categories) {
407 let html = '';
408 if (categories.length === 0) {
409 html = '<div class="no-categories">دسته‌بندی‌ای یافت نشد</div>';
410 } else {
411 categories.forEach(function(category) {
412 const hierarchy = category.hierarchy ? `<div class="category-hierarchy">${category.hierarchy}</div>` : '';
413 html += `
414 <div class="category-item woo-category" data-id="${category.id}" data-name="${category.name}">
415 <div>
416 <strong>${category.name}</strong>
417 ${hierarchy}
418 </div>
419 <span class="category-count">${category.count} �
420 حصول</span>
421 </div>
422 `;
423 });
424 }
425 $('#woo-categories-list').html(html);
426
427 $('.woo-category').on('click', function() {
428 $('.woo-category').removeClass('selected');
429 $(this).addClass('selected');
430
431 selectedWooCategory = {
432 id: $(this).data('id'),
433 name: $(this).data('name')
434 };
435
436 updateSelectedCategory('woo');
437 updateMappingButton();
438 });
439 }
440
441 function renderBasalamCategories(categories, parentElement = null, level = 0, parentIds = []) {
442 if (!parentElement) {
443 $('#basalam-categories-list').empty();
444 parentElement = $('#basalam-categories-list');
445 }
446
447 if (categories.length === 0 && level === 0) {
448 parentElement.html('<div class="no-categories">دسته‌بندی‌ای یافت نشد</div>');
449 return;
450 }
451
452 categories.forEach(function(category) {
453 const hasChildren = category.children && category.children.length > 0;
454 const paddingRight = level;
455 const isSelectable = level === 2;
456 const currentPath = [...parentIds, category.id];
457
458 const categoryHtml = `
459 <div class="basalam-category-wrapper basalam-dynamic-padding-right" data-padding="${paddingRight}">
460 <div class="category-item basalam-category ${isSelectable ? 'selectable' : 'non-selectable'}"
461 data-id="${category.id}"
462 data-name="${category.name}"
463 data-level="${level}"
464 data-path='${JSON.stringify(currentPath)}'>
465 <div class="basalam-flex-width-100">
466 ${hasChildren ? `<span class="expand-icon dashicons dashicons-arrow-left-alt2 basalam-cursor-pointer-small" data-category-id="${category.id}"></span>` : '<span class="basalam-width-20-block"></span>'}
467 <strong class="basalam-flex-1">${category.name}</strong>
468 </div>
469 </div>
470 ${hasChildren ? `<div class="children-container basalam-display-none" id="children-${category.id}"></div>` : ''}
471 </div>
472 `;
473
474 const categoryElement = $(categoryHtml);
475 // Apply dynamic padding from data attribute
476 categoryElement.find('.basalam-dynamic-padding-right').css('padding-right', categoryElement.find('.basalam-dynamic-padding-right').data('padding') + 'px');
477 parentElement.append(categoryElement);
478
479 if (hasChildren) {
480 renderBasalamCategories(category.children, categoryElement.find(`#children-${category.id}`), level + 1, currentPath);
481 }
482 });
483
484 $('.expand-icon').off('click').on('click', function(e) {
485 e.stopPropagation();
486 const categoryId = $(this).data('category-id');
487 const childrenContainer = $(`#children-${categoryId}`);
488
489 if (childrenContainer.is(':visible')) {
490 childrenContainer.slideUp(200);
491 $(this).removeClass('dashicons-arrow-down-alt2').addClass('dashicons-arrow-left-alt2');
492 } else {
493 childrenContainer.slideDown(200);
494 $(this).removeClass('dashicons-arrow-left-alt2').addClass('dashicons-arrow-down-alt2');
495 }
496 });
497
498 $('.basalam-category.selectable').off('click').on('click', function(e) {
499 e.stopPropagation();
500 $('.basalam-category').removeClass('selected');
501 $(this).addClass('selected');
502
503 const categoryPath = JSON.parse($(this).attr('data-path'));
504 selectedBasalamCategory = {
505 id: $(this).data('id'),
506 name: $(this).data('name'),
507 path: categoryPath
508 };
509
510 updateSelectedCategory('basalam');
511 updateMappingButton();
512 });
513
514 $('.basalam-category.non-selectable').off('click').on('click', function(e) {
515 e.stopPropagation();
516 const categoryElement = $(this);
517 const categoryId = categoryElement.data('id');
518 const expandIcon = categoryElement.find('.expand-icon');
519 const childrenContainer = $(`#children-${categoryId}`);
520
521 if (childrenContainer.length > 0) {
522 if (childrenContainer.is(':visible')) {
523 childrenContainer.slideUp(200);
524 expandIcon.removeClass('dashicons-arrow-down-alt2').addClass('dashicons-arrow-left-alt2');
525 } else {
526 childrenContainer.slideDown(200);
527 expandIcon.removeClass('dashicons-arrow-left-alt2').addClass('dashicons-arrow-down-alt2');
528 }
529 }
530 });
531 }
532
533 function renderCategoryMappings(mappings) {
534 let html = '';
535 if (mappings.length === 0) {
536 html = '<div class="no-categories">هیچ اتصالی ایجاد نشده است</div>';
537 } else {
538 mappings.forEach(function(mapping) {
539 html += `
540 <div class="mapping-item">
541 <div><strong>${mapping.woo_category_name}</strong></div>
542 <span class="mapping-arrow dashicons dashicons-arrow-left-alt2"></span>
543 <div><strong>${mapping.basalam_category_name}</strong></div>
544 <button class="delete-mapping-btn" data-mapping-id="${mapping.id}">حذف</button>
545 </div>
546 `;
547 });
548 }
549 $('#mappings-list').html(html);
550
551 $('.delete-mapping-btn').on('click', function() {
552 const mappingId = $(this).data('mapping-id');
553 if (confirm('آیا از حذف این اتصال اط�
554 ینان دارید؟')) {
555 deleteCategoryMapping(mappingId);
556 }
557 });
558 }
559
560 function filterWooCategories(searchTerm) {
561 const filtered = wooCategories.filter(category =>
562 category.name.toLowerCase().includes(searchTerm)
563 );
564 renderWooCategories(filtered);
565 }
566
567 function filterBasalamCategories(searchTerm) {
568 if (!searchTerm) {
569 renderBasalamCategories(basalamCategories);
570 return;
571 }
572
573 function filterTree(categories) {
574 let filtered = [];
575 categories.forEach(function(category) {
576 let includeCategory = category.name.toLowerCase().includes(searchTerm);
577 let filteredChildren = [];
578
579 if (category.children && category.children.length > 0) {
580 filteredChildren = filterTree(category.children);
581 if (filteredChildren.length > 0) {
582 includeCategory = true;
583 }
584 }
585
586 if (includeCategory) {
587 filtered.push({
588 ...category,
589 children: filteredChildren
590 });
591 }
592 });
593 return filtered;
594 }
595
596 const filtered = filterTree(basalamCategories);
597 renderBasalamCategories(filtered);
598
599 if (searchTerm) {
600 $('.children-container').show();
601 $('.expand-icon').removeClass('dashicons-arrow-left-alt2').addClass('dashicons-arrow-down-alt2');
602 }
603 }
604
605 function updateSelectedCategory(type) {
606 if (type === 'woo' && selectedWooCategory) {
607 $('#selected-woo-category').html(`<strong>${selectedWooCategory.name}</strong>`);
608 } else if (type === 'basalam' && selectedBasalamCategory) {
609 $('#selected-basalam-category').html(`<strong>${selectedBasalamCategory.name}</strong>`);
610 }
611 }
612
613 function updateMappingButton() {
614 if (selectedWooCategory && selectedBasalamCategory) {
615 $('#create-mapping-btn').prop('disabled', false);
616 } else {
617 $('#create-mapping-btn').prop('disabled', true);
618 }
619 }
620
621 function clearSelection() {
622 selectedWooCategory = null;
623 selectedBasalamCategory = null;
624
625 $('.category-item').removeClass('selected');
626 $('#selected-woo-category').text('دسته‌بندی ووکا�
627 رس انتخاب نشده');
628 $('#selected-basalam-category').text('دسته‌بندی باسلا�
629 انتخاب نشده');
630
631 updateMappingButton();
632 }
633
634 function createCategoryMapping() {
635 $.ajax({
636 url: ajaxurl,
637 method: 'POST',
638 data: {
639 action: 'create_category_mapping',
640 woo_category_id: selectedWooCategory.id,
641 woo_category_name: selectedWooCategory.name,
642 basalam_category_ids: selectedBasalamCategory.path,
643 basalam_category_name: selectedBasalamCategory.name,
644 _wpnonce: '<?php echo esc_attr(wp_create_nonce('create_category_mapping_nonce')); ?>'
645 },
646 success: function(response) {
647 if (response.success) {
648 window.BasalamToast.success('اتصال با �
649 وفقیت ایجاد شد!');
650 clearSelection();
651 loadCategoryMappings();
652 } else {
653 window.BasalamToast.error('خطا در ایجاد اتصال: ' + response.data);
654 }
655 },
656 error: function() {
657 window.BasalamToast.error('خطا در اتصال به سرور');
658 }
659 });
660 }
661
662 function deleteCategoryMapping(mappingId) {
663 $.ajax({
664 url: ajaxurl,
665 method: 'POST',
666 data: {
667 action: 'delete_category_mapping',
668 mapping_id: mappingId,
669 _wpnonce: '<?php echo esc_attr(wp_create_nonce('delete_category_mapping_nonce')); ?>'
670 },
671 success: function(response) {
672 if (response.success) {
673 window.BasalamToast.success('اتصال با �
674 وفقیت حذف شد!');
675 loadCategoryMappings();
676 } else {
677 window.BasalamToast.error('خطا در حذف اتصال: ' + response.data);
678 }
679 },
680 error: function() {
681 window.BasalamToast.error('خطا در اتصال به سرور');
682 }
683 });
684 }
685
686 });
687 </script>