| @@ -6,8 +6,9 @@ | ||
| 6 | 6 | use TierPricingTable\Addons\GlobalTieredPricing\CPT\Form\Tabs\Settings; |
| 7 | 7 | use TierPricingTable\Addons\GlobalTieredPricing\CPT\Form\Tabs\UsersAndRoles; |
| 8 | 8 | use TierPricingTable\Addons\GlobalTieredPricing\CPT\GlobalTieredPricingCPT; |
| 9 | 9 | use TierPricingTable\Addons\GlobalTieredPricing\GlobalPricingRule; |
| 10 | +use TierPricingTable\Addons\GlobalTieredPricing\RuleOverlaps; | |
| 10 | 11 | use TierPricingTable\Core\ServiceContainerTrait; |
| 11 | 12 | use WP_Post; |
| 12 | 13 | |
| 13 | 14 | class Form { |
| @@ -24,10 +25,17 @@ | ||
| 24 | 25 | protected $defaultTab = 'pricing'; |
| 25 | 26 | |
| 26 | 27 | protected $pricingRuleInstance = null; |
| 27 | 28 | |
| 29 | + /** | |
| 30 | + * @var Guide | |
| 31 | + */ | |
| 32 | + protected $guide; | |
| 33 | + | |
| 28 | 34 | public function __construct() { |
| 29 | 35 | |
| 36 | + $this->guide = new Guide(); | |
| 37 | + | |
| 30 | 38 | add_action( 'init', function () { |
| 31 | 39 | $tabs = array( |
| 32 | 40 | new Pricing( $this ), |
| 33 | 41 | new ProductAndCategories( $this ), |
| @@ -80,8 +88,18 @@ | ||
| 80 | 88 | margin-top: 10px; |
| 81 | 89 | border: 1px solid #888; |
| 82 | 90 | } |
| 83 | 91 | |
| 92 | + .tpt-global-pricing-rule-hint--warning { | |
| 93 | + border-left-color: #dba617; | |
| 94 | + background: #fcf9e8; | |
| 95 | + color: #6d4f00 !important; | |
| 96 | + } | |
| 97 | + | |
| 98 | + .tpt-global-pricing-rule-hint--top-level + .tpt-global-pricing-rule-hint--top-level { | |
| 99 | + margin-top: -10px; | |
| 100 | + } | |
| 101 | + | |
| 84 | 102 | .tpt-global-pricing-rule-hint__icon { |
| 85 | 103 | margin-right: 10px; |
| 86 | 104 | } |
| 87 | 105 | |
| @@ -301,19 +319,15 @@ | ||
| 301 | 319 | protected function render( WP_Post $post ) { |
| 302 | 320 | |
| 303 | 321 | $this->includeAssets(); |
| 304 | 322 | |
| 305 | - $rulesCount = (int) wp_count_posts( GlobalTieredPricingCPT::SLUG )->publish; | |
| 323 | + $this->guide->render(); | |
| 306 | 324 | |
| 307 | - if ( $this->isNewRule() && $rulesCount < 1 ) { | |
| 308 | - $this->renderHelpingSteps(); | |
| 325 | + // a rule that changes nothing is flagged as "Skipped" by the Rule status box; no second notice here | |
| 326 | + if ( ! $this->isNewRule() ) { | |
| 327 | + $this->renderStatusHints( $this->getPricingRuleInstance( $post ) ); | |
| 309 | 328 | } |
| 310 | 329 | |
| 311 | - if ( ! $this->isNewRule() && ! $this->getPricingRuleInstance( $post )->isValidPricing() ) { | |
| 312 | - $this->tabs[0]->renderHint( __( 'The pricing rule does not affect either prices or product quantity limits. The rule will be skipped.', | |
| 313 | - 'tier-pricing-table' ), array( 'custom_class' => 'tpt-global-pricing-rule-hint--top-level' ) ); | |
| 314 | - } | |
| 315 | - | |
| 316 | 330 | ?> |
| 317 | 331 | <div class="tpt-global-pricing-rule-form"> |
| 318 | 332 | |
| 319 | 333 | <nav class="tpt-global-pricing-rule-form__tabs"> |
| @@ -356,8 +370,39 @@ | ||
| 356 | 370 | <?php |
| 357 | 371 | } |
| 358 | 372 | |
| 359 | 373 | /** |
| 374 | + * What the admin should know before editing: other rules have a higher priority for products and | |
| 375 | + * customers they share, so this rule is skipped there. The Rule status box lists them. | |
| 376 | + */ | |
| 377 | + protected function renderStatusHints( GlobalPricingRule $rule ) { | |
| 378 | + // a suspended, expired or skipped rule is not applied anyway, so nothing overrides it | |
| 379 | + if ( 'publish' !== get_post_status( $rule->getId() ) || ! $rule->canBeOverridden() ) { | |
| 380 | + return; | |
| 381 | + } | |
| 382 | + | |
| 383 | + $overriding = ( new RuleOverlaps() )->findOverriding( $rule, GlobalTieredPricingCPT::getGlobalRules( true, false ) ); | |
| 384 | + | |
| 385 | + if ( ! $overriding ) { | |
| 386 | + return; | |
| 387 | + } | |
| 388 | + | |
| 389 | + $this->tabs[0]->renderHint( | |
| 390 | + sprintf( | |
| 391 | + /* translators: %d: number of rules */ | |
| 392 | + _n( | |
| 393 | + '%d other rule has a higher priority and is applied instead of this rule for the products and customers they both match. See "Overridden by" in the Rule status box.', | |
| 394 | + '%d other rules have a higher priority and are applied instead of this rule for the products and customers they both match. See "Overridden by" in the Rule status box.', | |
| 395 | + count( $overriding ), | |
| 396 | + 'tier-pricing-table' | |
| 397 | + ), | |
| 398 | + count( $overriding ) | |
| 399 | + ), | |
| 400 | + array( 'custom_class' => 'tpt-global-pricing-rule-hint--top-level tpt-global-pricing-rule-hint--warning' ) | |
| 401 | + ); | |
| 402 | + } | |
| 403 | + | |
| 404 | + /** | |
| 360 | 405 | * Get pricing rule instance |
| 361 | 406 | * |
| 362 | 407 | * @param WP_Post $post |
| 363 | 408 | * |
| @@ -368,195 +413,8 @@ | ||
| 368 | 413 | $this->pricingRuleInstance = GlobalPricingRule::build( $post->ID ); |
| 369 | 414 | } |
| 370 | 415 | |
| 371 | 416 | return $this->pricingRuleInstance; |
| 372 | - } | |
| 373 | - | |
| 374 | - public function renderHelpingSteps() { | |
| 375 | - ?> | |
| 376 | - <style> | |
| 377 | - .tpt-global-pricing-rule-helping { | |
| 378 | - background: #ffffff; | |
| 379 | - border: 1px solid #e2e4e7; | |
| 380 | - border-radius: 8px; | |
| 381 | - padding: 30px; | |
| 382 | - position: relative; | |
| 383 | - margin: 20px 0; | |
| 384 | - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); | |
| 385 | - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; | |
| 386 | - } | |
| 387 | - | |
| 388 | - .tpt-global-pricing-rule-helping__close { | |
| 389 | - position: absolute; | |
| 390 | - top: 15px; | |
| 391 | - width: 32px; | |
| 392 | - height: 32px; | |
| 393 | - background: #f0f0f1; | |
| 394 | - color: #3c434a; | |
| 395 | - text-align: center; | |
| 396 | - line-height: 32px; | |
| 397 | - right: 15px; | |
| 398 | - font-weight: 600; | |
| 399 | - border-radius: 50%; | |
| 400 | - transition: all 0.2s ease; | |
| 401 | - } | |
| 402 | - | |
| 403 | - .tpt-global-pricing-rule-helping__close:hover { | |
| 404 | - background: #dcdcdd; | |
| 405 | - cursor: pointer; | |
| 406 | - transform: scale(1.05); | |
| 407 | - } | |
| 408 | - | |
| 409 | - .tpt-global-pricing-rule-helping__header { | |
| 410 | - text-align: center; | |
| 411 | - margin-bottom: 30px; | |
| 412 | - } | |
| 413 | - | |
| 414 | - .tpt-global-pricing-rule-helping__title { | |
| 415 | - font-size: 22px; | |
| 416 | - font-weight: 600; | |
| 417 | - color: #1d2327; | |
| 418 | - margin-bottom: 20px; | |
| 419 | - } | |
| 420 | - | |
| 421 | - .tpt-global-pricing-rule-helping__subtitle { | |
| 422 | - font-size: 14px; | |
| 423 | - color: #646970; | |
| 424 | - max-width: 650px; | |
| 425 | - margin: 0 auto; | |
| 426 | - line-height: 1.5; | |
| 427 | - } | |
| 428 | - | |
| 429 | - .tpt-global-pricing-rule-helping__steps { | |
| 430 | - display: grid; | |
| 431 | - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); | |
| 432 | - gap: 20px; | |
| 433 | - } | |
| 434 | - | |
| 435 | - .tpt-global-pricing-rule-helping-step { | |
| 436 | - background: #ffffff; | |
| 437 | - border: 1px solid #e0e0e0; | |
| 438 | - border-radius: 8px; | |
| 439 | - padding: 24px 20px; | |
| 440 | - text-align: center; | |
| 441 | - transition: transform 0.2s ease, box-shadow 0.2s ease; | |
| 442 | - position: relative; | |
| 443 | - } | |
| 444 | - | |
| 445 | - .tpt-global-pricing-rule-helping-step:hover { | |
| 446 | - transform: translateY(-2px); | |
| 447 | - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06); | |
| 448 | - border-color: #c3c4c7; | |
| 449 | - } | |
| 450 | - | |
| 451 | - .tpt-global-pricing-rule-helping-step__icon { | |
| 452 | - width: 52px; | |
| 453 | - height: 52px; | |
| 454 | - border-radius: 50%; | |
| 455 | - background: #e0f0fa; | |
| 456 | - color: #0070bc; | |
| 457 | - margin: 0 auto 16px; | |
| 458 | - display: flex; | |
| 459 | - align-items: center; | |
| 460 | - justify-content: center; | |
| 461 | - font-size: 24px; | |
| 462 | - font-weight: bold; | |
| 463 | - } | |
| 464 | - | |
| 465 | - .tpt-global-pricing-rule-helping-step__title { | |
| 466 | - font-size: 15px; | |
| 467 | - font-weight: 600; | |
| 468 | - color: #1d2327; | |
| 469 | - margin-bottom: 8px; | |
| 470 | - } | |
| 471 | - | |
| 472 | - .tpt-global-pricing-rule-helping-step__description { | |
| 473 | - font-size: 13px; | |
| 474 | - color: #50575e; | |
| 475 | - line-height: 1.5; | |
| 476 | - } | |
| 477 | - | |
| 478 | - </style> | |
| 479 | - <script> | |
| 480 | - jQuery(document).ready(function () { | |
| 481 | - jQuery('.tpt-global-pricing-rule-helping__close').click(function () { | |
| 482 | - jQuery(this).closest('.tpt-global-pricing-rule-helping').slideUp(200); | |
| 483 | - }) | |
| 484 | - }) | |
| 485 | - </script> | |
| 486 | - <?php | |
| 487 | - $steps = array( | |
| 488 | - array( | |
| 489 | - 'title' => __( 'Set Custom Pricing', 'tier-pricing-table' ), | |
| 490 | - 'description' => __( 'Set custom regular and tiered pricing for matching products.', | |
| 491 | - 'tier-pricing-table' ), | |
| 492 | - 'icon' => '$', | |
| 493 | - ), | |
| 494 | - array( | |
| 495 | - 'title' => __( 'Select Products', 'tier-pricing-table' ), | |
| 496 | - 'description' => __( 'Target specific products, categories, tags, or brands. Leave empty to apply store-wide.', | |
| 497 | - 'tier-pricing-table' ), | |
| 498 | - 'icon' => '<span class="dashicons dashicons-archive"></span>', | |
| 499 | - ), | |
| 500 | - array( | |
| 501 | - 'title' => __( 'Filter Users', 'tier-pricing-table' ), | |
| 502 | - 'description' => 'Restrict this pricing to specific customers or user roles.', | |
| 503 | - 'icon' => '<span class="dashicons dashicons-admin-users"></span>', | |
| 504 | - ), | |
| 505 | - array( | |
| 506 | - 'title' => __( 'Quantity Limits', 'tier-pricing-table' ), | |
| 507 | - 'description' => __( 'Enforce minimum, maximum, and step increments for purchasing.', | |
| 508 | - 'tier-pricing-table' ), | |
| 509 | - 'icon' => '<span class="dashicons dashicons-database"></span>', | |
| 510 | - ), | |
| 511 | - ) | |
| 512 | - ?> | |
| 513 | - <div class="tpt-global-pricing-rule-helping"> | |
| 514 | - <div class="tpt-global-pricing-rule-helping__close" | |
| 515 | - title="<?php esc_attr_e( 'Dismiss', 'tier-pricing-table' ); ?>"> | |
| 516 | - × | |
| 517 | - </div> | |
| 518 | - | |
| 519 | - <div class="tpt-global-pricing-rule-helping__header"> | |
| 520 | - <div class="tpt-global-pricing-rule-helping__title"> | |
| 521 | - <?php esc_html_e( 'How global pricing rules work', 'tier-pricing-table' ); ?> | |
| 522 | - </div> | |
| 523 | - <div class="tpt-global-pricing-rule-helping__subtitle"> | |
| 524 | - <p style="margin: 0 0 6px;"> | |
| 525 | - <?php | |
| 526 | - esc_html_e( 'Global rules enable you to bulk-apply dynamic pricing and quantity limits to selected groups of products and users simultaneously.', | |
| 527 | - 'tier-pricing-table' ); | |
| 528 | - ?> | |
| 529 | - </p> | |
| 530 | - <p style="margin: 0;"> | |
| 531 | - <?php | |
| 532 | - echo sprintf( '<strong>%s</strong> %s', esc_html__( 'Note:', 'tier-pricing-table' ), | |
| 533 | - esc_html__( 'Depending on your priority settings, global rules may override product-level pricing configurations.', | |
| 534 | - 'tier-pricing-table' ) ); | |
| 535 | - ?> | |
| 536 | - </p> | |
| 537 | - </div> | |
| 538 | - </div> | |
| 539 | - | |
| 540 | - <div class="tpt-global-pricing-rule-helping__steps"> | |
| 541 | - <?php foreach ( $steps as $index => $step ) : ?> | |
| 542 | - <div class="tpt-global-pricing-rule-helping-step"> | |
| 543 | - <div class="tpt-global-pricing-rule-helping-step__icon"> | |
| 544 | - <?php echo wp_kses_post( $step['icon'] ); ?> | |
| 545 | - </div> | |
| 546 | - | |
| 547 | - <div class="tpt-global-pricing-rule-helping-step__title"> | |
| 548 | - <?php echo esc_html( $step['title'] ); ?> | |
| 549 | - </div> | |
| 550 | - | |
| 551 | - <div class="tpt-global-pricing-rule-helping-step__description"> | |
| 552 | - <?php echo esc_html( $step['description'] ); ?> | |
| 553 | - </div> | |
| 554 | - </div> | |
| 555 | - <?php endforeach; ?> | |
| 556 | - </div> | |
| 557 | - </div> | |
| 558 | - <?php | |
| 559 | 417 | } |
| 560 | 418 | |
| 561 | 419 | public function isNewRule(): bool { |
| 562 | 420 | global $pagenow; |