Changeset 3490772
- Timestamp:
- 03/25/2026 10:53:38 AM (7 days ago)
- Location:
- smart-product-sort/trunk
- Files:
-
- 5 edited
-
admin/css/admin.css (modified) (1 diff)
-
admin/js/admin.js (modified) (2 diffs)
-
admin/views/main-page.php (modified) (2 diffs)
-
readme.txt (modified) (3 diffs)
-
smart-product-sort.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
smart-product-sort/trunk/admin/css/admin.css
r3489912 r3490772 1 /* Smart Product Sort — Admin Styles */ 2 3 /* ── Layout ────────────────────────────────────── */ 1 /* ============================================================ 2 Smart Product Sort — Enterprise Admin UI 3 Color System: Deep Indigo + Clean Neutrals 4 ============================================================ */ 5 6 /* ── Variables ─────────────────────────────────── */ 7 :root { 8 --spsort-primary: #6C5CE7; 9 --spsort-primary-dark: #5A4BD1; 10 --spsort-primary-light: #A29BFE; 11 --spsort-primary-50: #F3F1FF; 12 --spsort-primary-glow: rgba(108, 92, 231, .2); 13 --spsort-green: #10B981; 14 --spsort-green-50: #ECFDF5; 15 --spsort-green-100: #D1FAE5; 16 --spsort-red: #EF4444; 17 --spsort-red-50: #FEF2F2; 18 --spsort-amber: #F59E0B; 19 --spsort-gray-50: #F9FAFB; 20 --spsort-gray-100: #F3F4F6; 21 --spsort-gray-200: #E5E7EB; 22 --spsort-gray-300: #D1D5DB; 23 --spsort-gray-400: #9CA3AF; 24 --spsort-gray-500: #6B7280; 25 --spsort-gray-600: #4B5563; 26 --spsort-gray-700: #374151; 27 --spsort-gray-800: #1F2937; 28 --spsort-gray-900: #111827; 29 --spsort-white: #FFFFFF; 30 --spsort-radius: 10px; 31 --spsort-radius-sm: 6px; 32 --spsort-radius-full: 9999px; 33 --spsort-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .04); 34 --spsort-shadow-md: 0 4px 6px rgba(0, 0, 0, .04), 0 2px 4px rgba(0, 0, 0, .03); 35 --spsort-shadow-lg: 0 10px 25px rgba(0, 0, 0, .06), 0 4px 10px rgba(0, 0, 0, .04); 36 --spsort-transition: .2s cubic-bezier(.4, 0, .2, 1); 37 } 38 39 /* ── Reset WP defaults ─────────────────────────── */ 4 40 .spsort-wrap { 5 max-width: 1100px; 6 } 7 8 .spsort-rules-table { 9 margin-top: 16px; 10 } 11 12 .spsort-rules-table th, 13 .spsort-rules-table td { 41 max-width: 1140px; 42 padding: 24px 0 40px; 43 } 44 45 .spsort-wrap h1, 46 .spsort-wrap h2 { 47 padding: 0; 48 margin: 0; 49 } 50 51 /* ── Card container ────────────────────────────── */ 52 .spsort-card { 53 background: var(--spsort-white); 54 border: 1px solid var(--spsort-gray-200); 55 border-radius: var(--spsort-radius); 56 box-shadow: var(--spsort-shadow); 57 overflow: hidden; 58 } 59 60 /* ── Card header ───────────────────────────────── */ 61 .spsort-card-header { 62 display: flex; 63 align-items: center; 64 justify-content: space-between; 65 padding: 20px 28px; 66 border-bottom: 1px solid var(--spsort-gray-100); 67 background: var(--spsort-white); 68 } 69 70 .spsort-card-header-left { 71 display: flex; 72 align-items: center; 73 gap: 12px; 74 } 75 76 .spsort-logo-icon { 77 width: 36px; 78 height: 36px; 79 background: var(--spsort-primary); 80 color: var(--spsort-white); 81 border-radius: 8px; 82 display: flex; 83 align-items: center; 84 justify-content: center; 85 flex-shrink: 0; 86 } 87 88 .spsort-card-title { 89 font-size: 18px !important; 90 font-weight: 700 !important; 91 color: var(--spsort-gray-900) !important; 92 letter-spacing: -0.01em; 93 } 94 95 /* ── Buttons ───────────────────────────────────── */ 96 .spsort-btn { 97 display: inline-flex; 98 align-items: center; 99 gap: 8px; 100 font-size: 13px; 101 font-weight: 600; 102 padding: 9px 20px; 103 border-radius: var(--spsort-radius-sm); 104 border: none; 105 cursor: pointer; 106 transition: all var(--spsort-transition); 107 line-height: 1.4; 108 text-decoration: none; 109 white-space: nowrap; 110 } 111 112 .spsort-btn-primary { 113 background: var(--spsort-primary); 114 color: var(--spsort-white); 115 box-shadow: 0 1px 2px rgba(108, 92, 231, .3); 116 } 117 118 .spsort-btn-primary:hover, 119 .spsort-btn-primary:focus { 120 background: var(--spsort-primary-dark); 121 color: var(--spsort-white); 122 box-shadow: 0 4px 12px rgba(108, 92, 231, .35); 123 transform: translateY(-1px); 124 } 125 126 .spsort-btn-primary:active { 127 transform: translateY(0); 128 } 129 130 .spsort-btn-ghost { 131 background: transparent; 132 color: var(--spsort-gray-500); 133 border: 1px solid var(--spsort-gray-300); 134 padding: 8px 20px; 135 } 136 137 .spsort-btn-ghost:hover { 138 background: var(--spsort-gray-50); 139 color: var(--spsort-gray-700); 140 border-color: var(--spsort-gray-400); 141 } 142 143 /* ── Icon buttons ──────────────────────────────── */ 144 .spsort-btn-icon { 145 width: 34px; 146 height: 34px; 147 display: inline-flex; 148 align-items: center; 149 justify-content: center; 150 border-radius: var(--spsort-radius-sm); 151 border: 1px solid var(--spsort-gray-200); 152 background: var(--spsort-white); 153 color: var(--spsort-gray-500); 154 cursor: pointer; 155 transition: all var(--spsort-transition); 156 } 157 158 .spsort-btn-icon:hover { 159 background: var(--spsort-primary-50); 160 color: var(--spsort-primary); 161 border-color: var(--spsort-primary-light); 162 } 163 164 .spsort-btn-icon-danger:hover { 165 background: var(--spsort-red-50); 166 color: var(--spsort-red); 167 border-color: var(--spsort-red); 168 } 169 170 .spsort-action-btns { 171 display: flex; 172 gap: 6px; 173 } 174 175 /* ── Table ─────────────────────────────────────── */ 176 .spsort-table-wrap { 177 overflow-x: auto; 178 } 179 180 .spsort-table { 181 width: 100%; 182 border-collapse: collapse; 183 border-spacing: 0; 184 } 185 186 .spsort-table thead th { 187 background: var(--spsort-gray-50); 188 color: var(--spsort-gray-400); 189 font-size: 11px; 190 font-weight: 600; 191 text-transform: uppercase; 192 letter-spacing: 0.06em; 193 padding: 12px 20px; 194 text-align: left; 195 border-bottom: 1px solid var(--spsort-gray-100); 196 white-space: nowrap; 197 -webkit-user-select: none; 198 user-select: none; 199 } 200 201 .spsort-table tbody td { 202 padding: 16px 20px; 203 font-size: 14px; 204 color: var(--spsort-gray-600); 205 border-bottom: 1px solid var(--spsort-gray-100); 14 206 vertical-align: middle; 15 207 } 16 208 17 /* ── Status badges ──────────────────────────────── */ 209 .spsort-table tbody tr:last-child td { 210 border-bottom: none; 211 } 212 213 .spsort-table tbody tr { 214 transition: background var(--spsort-transition); 215 } 216 217 .spsort-table tbody tr:hover { 218 background: var(--spsort-gray-50); 219 } 220 221 .spsort-table tbody td strong { 222 color: var(--spsort-gray-800); 223 font-weight: 600; 224 } 225 226 /* ── Column widths ─────────────────────────────── */ 227 .spsort-col-name { min-width: 180px; } 228 .spsort-col-sort { min-width: 120px; } 229 .spsort-col-order { min-width: 90px; } 230 .spsort-col-cat { min-width: 120px; } 231 .spsort-col-prio { min-width: 80px; text-align: center; } 232 .spsort-col-status { min-width: 100px; } 233 .spsort-col-actions { min-width: 90px; } 234 235 thead .spsort-col-prio { text-align: center; } 236 237 /* ── Inline badges ─────────────────────────────── */ 238 .spsort-sort-badge { 239 display: inline-block; 240 background: var(--spsort-primary-50); 241 color: var(--spsort-primary); 242 font-size: 12px; 243 font-weight: 600; 244 padding: 3px 10px; 245 border-radius: var(--spsort-radius-full); 246 } 247 248 .spsort-order-badge { 249 display: inline-flex; 250 align-items: center; 251 gap: 4px; 252 font-size: 12px; 253 font-weight: 500; 254 color: var(--spsort-gray-500); 255 } 256 257 .spsort-global-tag { 258 display: inline-block; 259 background: var(--spsort-gray-100); 260 color: var(--spsort-gray-600); 261 font-size: 12px; 262 font-weight: 600; 263 padding: 3px 10px; 264 border-radius: var(--spsort-radius-full); 265 } 266 267 .spsort-priority-num { 268 display: inline-flex; 269 align-items: center; 270 justify-content: center; 271 width: 30px; 272 height: 30px; 273 background: var(--spsort-gray-100); 274 color: var(--spsort-gray-700); 275 font-size: 13px; 276 font-weight: 700; 277 border-radius: 50%; 278 } 279 280 /* ── Status toggle ─────────────────────────────── */ 18 281 .spsort-toggle-rule { 19 min-width: 80px; 20 font-weight: 600; 21 border-radius: 3px; 282 display: inline-flex; 283 align-items: center; 284 gap: 6px; 285 font-size: 12px; 286 font-weight: 600; 287 padding: 5px 14px; 288 border-radius: var(--spsort-radius-full); 289 border: none; 290 cursor: pointer; 291 transition: all var(--spsort-transition); 292 line-height: 1.4; 293 } 294 295 .spsort-status-dot { 296 width: 7px; 297 height: 7px; 298 border-radius: 50%; 299 flex-shrink: 0; 22 300 } 23 301 24 302 .spsort-toggle-rule.spsort-active { 25 background: #00a32a; 26 border-color: #00a32a; 27 color: #fff; 303 background: var(--spsort-green-100); 304 color: #065F46; 305 } 306 307 .spsort-toggle-rule.spsort-active .spsort-status-dot { 308 background: var(--spsort-green); 309 box-shadow: 0 0 0 2px rgba(16, 185, 129, .3); 28 310 } 29 311 30 312 .spsort-toggle-rule.spsort-active:hover { 31 background: #007a1f; 32 border-color: #007a1f; 33 color: #fff; 313 background: #A7F3D0; 34 314 } 35 315 36 316 .spsort-toggle-rule.spsort-inactive { 37 background: #d63638; 38 border-color: #d63638; 39 color: #fff; 317 background: var(--spsort-gray-100); 318 color: var(--spsort-gray-500); 319 } 320 321 .spsort-toggle-rule.spsort-inactive .spsort-status-dot { 322 background: var(--spsort-gray-400); 40 323 } 41 324 42 325 .spsort-toggle-rule.spsort-inactive:hover { 43 background: #b32d2e; 44 border-color: #b32d2e; 45 color: #fff; 46 } 47 48 /* ── Modal overlay ──────────────────────────────── */ 326 background: var(--spsort-gray-200); 327 } 328 329 /* ── Empty state ───────────────────────────────── */ 330 .spsort-empty-state { 331 text-align: center; 332 padding: 48px 20px; 333 color: var(--spsort-gray-400); 334 } 335 336 .spsort-empty-state svg { 337 margin-bottom: 12px; 338 opacity: .5; 339 } 340 341 .spsort-empty-state p { 342 font-size: 16px; 343 font-weight: 600; 344 color: var(--spsort-gray-500); 345 margin: 0 0 4px; 346 } 347 348 .spsort-empty-state span { 349 font-size: 13px; 350 color: var(--spsort-gray-400); 351 } 352 353 /* ── Notice bar ────────────────────────────────── */ 354 .spsort-notice { 355 padding: 12px 20px; 356 border-radius: var(--spsort-radius-sm); 357 font-size: 13px; 358 font-weight: 500; 359 margin-bottom: 16px; 360 display: flex; 361 align-items: center; 362 gap: 8px; 363 } 364 365 .spsort-notice.notice-success { 366 background: var(--spsort-green-50); 367 color: #065F46; 368 border: 1px solid var(--spsort-green-100); 369 } 370 371 .spsort-notice.notice-error { 372 background: var(--spsort-red-50); 373 color: #991B1B; 374 border: 1px solid #FECACA; 375 } 376 377 .spsort-notice p { 378 margin: 0; 379 } 380 381 /* ── Modal overlay ─────────────────────────────── */ 49 382 #spsort-modal-overlay { 50 383 position: fixed; 51 384 inset: 0; 52 background: rgba(0, 0, 0, .6); 385 background: rgba(17, 24, 39, .5); 386 backdrop-filter: blur(4px); 387 -webkit-backdrop-filter: blur(4px); 53 388 z-index: 100000; 54 389 display: flex; 55 390 align-items: center; 56 391 justify-content: center; 392 padding: 20px; 57 393 } 58 394 59 395 #spsort-modal { 60 background: #fff; 61 border-radius: 6px; 62 box-shadow: 0 8px 32px rgba(0, 0, 0, .25); 63 padding: 28px 32px; 64 width: 640px; 65 max-width: 95vw; 396 background: var(--spsort-white); 397 border-radius: 12px; 398 box-shadow: var(--spsort-shadow-lg); 399 width: 580px; 400 max-width: 100%; 66 401 max-height: 90vh; 67 402 overflow-y: auto; 68 } 69 70 #spsort-modal h2 { 71 margin: 0 0 20px; 72 font-size: 1.3em; 73 border-bottom: 1px solid #e0e0e0; 74 padding-bottom: 12px; 75 } 76 77 #spsort-modal .form-table th { 78 width: 180px; 79 } 80 81 /* ── Modal footer ───────────────────────────────── */ 403 animation: spsortModalIn .25s cubic-bezier(.4, 0, .2, 1); 404 } 405 406 @keyframes spsortModalIn { 407 from { 408 opacity: 0; 409 transform: translateY(16px) scale(.98); 410 } 411 to { 412 opacity: 1; 413 transform: translateY(0) scale(1); 414 } 415 } 416 417 /* ── Modal header ──────────────────────────────── */ 418 .spsort-modal-header { 419 display: flex; 420 align-items: center; 421 justify-content: space-between; 422 padding: 20px 24px; 423 border-bottom: 1px solid var(--spsort-gray-100); 424 } 425 426 .spsort-modal-header h2 { 427 font-size: 16px !important; 428 font-weight: 700 !important; 429 color: var(--spsort-gray-900) !important; 430 } 431 432 .spsort-modal-close { 433 width: 32px; 434 height: 32px; 435 display: flex; 436 align-items: center; 437 justify-content: center; 438 border-radius: var(--spsort-radius-sm); 439 border: none; 440 background: transparent; 441 color: var(--spsort-gray-400); 442 cursor: pointer; 443 transition: all var(--spsort-transition); 444 } 445 446 .spsort-modal-close:hover { 447 background: var(--spsort-gray-100); 448 color: var(--spsort-gray-700); 449 } 450 451 /* ── Form body ─────────────────────────────────── */ 452 .spsort-form-body { 453 padding: 24px; 454 display: flex; 455 flex-direction: column; 456 gap: 20px; 457 } 458 459 .spsort-field-row { 460 display: grid; 461 grid-template-columns: 1fr 1fr; 462 gap: 16px; 463 } 464 465 .spsort-label { 466 display: block; 467 font-size: 13px; 468 font-weight: 600; 469 color: var(--spsort-gray-700); 470 margin-bottom: 6px; 471 } 472 473 .spsort-required { 474 color: var(--spsort-red); 475 } 476 477 .spsort-input, 478 .spsort-select { 479 width: 100%; 480 padding: 9px 12px; 481 font-size: 14px; 482 color: var(--spsort-gray-800); 483 background: var(--spsort-white); 484 border: 1px solid var(--spsort-gray-300); 485 border-radius: var(--spsort-radius-sm); 486 transition: border-color var(--spsort-transition), box-shadow var(--spsort-transition); 487 outline: none; 488 -webkit-appearance: none; 489 appearance: none; 490 } 491 492 .spsort-input:focus, 493 .spsort-select:focus { 494 border-color: var(--spsort-primary); 495 box-shadow: 0 0 0 3px var(--spsort-primary-glow); 496 } 497 498 .spsort-input::placeholder { 499 color: var(--spsort-gray-400); 500 } 501 502 .spsort-select { 503 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239CA3AF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); 504 background-repeat: no-repeat; 505 background-position: right 12px center; 506 padding-right: 32px; 507 } 508 509 .spsort-hint { 510 display: block; 511 font-size: 12px; 512 color: var(--spsort-gray-400); 513 margin-top: 4px; 514 } 515 516 /* ── Toggle switch ─────────────────────────────── */ 517 .spsort-field-toggle { 518 padding-top: 4px; 519 } 520 521 .spsort-toggle-label { 522 display: inline-flex; 523 align-items: center; 524 gap: 10px; 525 cursor: pointer; 526 } 527 528 .spsort-toggle-label input[type="checkbox"] { 529 position: absolute; 530 opacity: 0; 531 width: 0; 532 height: 0; 533 } 534 535 .spsort-toggle-switch { 536 position: relative; 537 width: 40px; 538 height: 22px; 539 background: var(--spsort-gray-300); 540 border-radius: var(--spsort-radius-full); 541 transition: background var(--spsort-transition); 542 flex-shrink: 0; 543 } 544 545 .spsort-toggle-switch::after { 546 content: ''; 547 position: absolute; 548 top: 3px; 549 left: 3px; 550 width: 16px; 551 height: 16px; 552 background: var(--spsort-white); 553 border-radius: 50%; 554 transition: transform var(--spsort-transition); 555 box-shadow: 0 1px 3px rgba(0, 0, 0, .15); 556 } 557 558 .spsort-toggle-label input:checked + .spsort-toggle-switch { 559 background: var(--spsort-primary); 560 } 561 562 .spsort-toggle-label input:checked + .spsort-toggle-switch::after { 563 transform: translateX(18px); 564 } 565 566 .spsort-toggle-label input:focus + .spsort-toggle-switch { 567 box-shadow: 0 0 0 3px var(--spsort-primary-glow); 568 } 569 570 .spsort-toggle-text { 571 font-size: 13px; 572 font-weight: 500; 573 color: var(--spsort-gray-700); 574 } 575 576 /* ── Modal footer ──────────────────────────────── */ 82 577 .spsort-modal-footer { 83 578 display: flex; 579 justify-content: flex-end; 84 580 gap: 10px; 85 margin-top: 20px; 86 padding-top: 16px; 87 border-top: 1px solid #e0e0e0; 88 } 89 90 /* ── Notice bar ─────────────────────────────────── */ 91 #spsort-notice { 92 margin: 16px 0 0; 93 } 581 padding: 16px 24px; 582 border-top: 1px solid var(--spsort-gray-100); 583 background: var(--spsort-gray-50); 584 border-radius: 0 0 12px 12px; 585 } 586 587 /* ── Responsive ────────────────────────────────── */ 588 @media (max-width: 782px) { 589 .spsort-card-header { 590 flex-direction: column; 591 align-items: flex-start; 592 gap: 12px; 593 } 594 595 .spsort-field-row { 596 grid-template-columns: 1fr; 597 } 598 599 #spsort-modal { 600 width: 100%; 601 margin: 10px; 602 } 603 604 .spsort-table thead th, 605 .spsort-table tbody td { 606 padding: 10px 12px; 607 font-size: 13px; 608 } 609 } 610 611 @media (max-width: 480px) { 612 .spsort-card-header { 613 padding: 16px; 614 } 615 616 .spsort-table thead th, 617 .spsort-table tbody td { 618 padding: 8px 10px; 619 font-size: 12px; 620 } 621 622 .spsort-modal-footer { 623 flex-direction: column-reverse; 624 } 625 626 .spsort-modal-footer .spsort-btn { 627 width: 100%; 628 justify-content: center; 629 } 630 } -
smart-product-sort/trunk/admin/js/admin.js
r3489912 r3490772 1 /** 2 * Smart Product Sort — Admin UI. 3 * 4 * @package Smart_Product_Sort 5 */ 6 1 7 /* global spsortData, jQuery */ 2 8 ( function ( $ ) { 3 9 'use strict'; 4 10 5 var $overlay = $( '#spsort-modal-overlay' );6 var $form = $( '#spsort-rule-form' );7 var $notice = $( '#spsort-notice' );8 var $saveBtn = $( '#spsort-save-btn' );11 var $overlay = $( '#spsort-modal-overlay' ); 12 var $form = $( '#spsort-rule-form' ); 13 var $notice = $( '#spsort-notice' ); 14 var $saveBtn = $( '#spsort-save-btn' ); 9 15 10 16 // ── Helpers ─────────────────────────────────────────────────────────── … … 48 54 // ── Add rule ────────────────────────────────────────────────────────── 49 55 50 $( '#spsort-add-rule' ).on( 'click', function () { 51 openModal( spsortData.i18n.addSortRule ); 52 } ); 56 $( '#spsort-add-rule' ).on( 57 'click', 58 function () { 59 openModal( spsortData.i18n.addSortRule ); 60 } 61 ); 53 62 54 63 // ── Edit rule ───────────────────────────────────────────────────────── 55 64 56 $( document ).on( 'click', '.spsort-edit-rule', function () { 57 var rule = $( this ).data( 'rule' ); 58 59 $( '#spsort-rule-id' ).val( rule.id ); 60 $( '#spsort-rule-name' ).val( rule.rule_name ); 61 $( '#spsort-sort-by' ).val( rule.sort_by ); 62 $( '#spsort-sort-order' ).val( rule.sort_order ); 63 $( '#spsort-category' ).val( rule.category_id || '' ); 64 $( '#spsort-priority' ).val( rule.priority ); 65 $( '#spsort-is-active' ).prop( 'checked', '1' === String( rule.is_active ) ); 66 67 openModal( spsortData.i18n.editSortRule ); 68 } ); 65 $( document ).on( 66 'click', 67 '.spsort-edit-rule', 68 function () { 69 var rule = $( this ).data( 'rule' ); 70 71 $( '#spsort-rule-id' ).val( rule.id ); 72 $( '#spsort-rule-name' ).val( rule.rule_name ); 73 $( '#spsort-sort-by' ).val( rule.sort_by ); 74 $( '#spsort-sort-order' ).val( rule.sort_order ); 75 $( '#spsort-category' ).val( rule.category_id || '' ); 76 $( '#spsort-priority' ).val( rule.priority ); 77 $( '#spsort-is-active' ).prop( 'checked', '1' === String( rule.is_active ) ); 78 79 openModal( spsortData.i18n.editSortRule ); 80 } 81 ); 69 82 70 83 // ── Close modal ─────────────────────────────────────────────────────── 71 84 72 $( '#spsort-cancel-btn' ).on( 'click', closeModal ); 73 74 $overlay.on( 'click', function ( e ) { 75 if ( $( e.target ).is( '#spsort-modal-overlay' ) ) { 76 closeModal(); 77 } 78 } ); 79 80 $( document ).on( 'keydown', function ( e ) { 81 if ( 27 === e.which ) { 82 closeModal(); 83 } 84 } ); 85 $( '#spsort-cancel-btn, #spsort-cancel-btn-footer' ).on( 'click', closeModal ); 86 87 $overlay.on( 88 'click', 89 function ( e ) { 90 if ( $( e.target ).is( '#spsort-modal-overlay' ) ) { 91 closeModal(); 92 } 93 } 94 ); 95 96 $( document ).on( 97 'keydown', 98 function ( e ) { 99 if ( 27 === e.which ) { 100 closeModal(); 101 } 102 } 103 ); 85 104 86 105 // ── Save rule (create / update) ─────────────────────────────────────── 87 106 88 $form.on( 'submit', function ( e ) { 89 e.preventDefault(); 90 91 var $name = $( '#spsort-rule-name' ); 92 if ( '' === $.trim( $name.val() ) ) { 93 $name.trigger( 'focus' ); 94 return; 95 } 96 97 $saveBtn.text( spsortData.i18n.saving ).prop( 'disabled', true ); 98 99 $.post( spsortData.ajaxUrl, { 100 action : 'spsort_save_rule', 101 nonce : spsortData.nonce, 102 rule_id : $( '#spsort-rule-id' ).val(), 103 rule_name : $( '#spsort-rule-name' ).val(), 104 sort_by : $( '#spsort-sort-by' ).val(), 105 sort_order : $( '#spsort-sort-order' ).val(), 106 category_id: $( '#spsort-category' ).val(), 107 priority : $( '#spsort-priority' ).val(), 108 is_active : $( '#spsort-is-active' ).is( ':checked' ) ? 1 : 0, 109 } ) 110 .done( function ( res ) { 111 if ( res.success ) { 112 showNotice( res.data.message, 'success' ); 113 closeModal(); 114 reloadPage(); 115 } else { 116 showNotice( res.data.message, 'error' ); 117 } 118 } ) 119 .fail( function () { 120 showNotice( spsortData.i18n.error, 'error' ); 121 } ) 122 .always( function () { 123 $saveBtn.text( spsortData.i18n.saveRule ).prop( 'disabled', false ); 124 } ); 125 } ); 107 $form.on( 108 'submit', 109 function ( e ) { 110 e.preventDefault(); 111 112 var $name = $( '#spsort-rule-name' ); 113 if ( '' === $.trim( $name.val() ) ) { 114 $name.trigger( 'focus' ); 115 return; 116 } 117 118 $saveBtn.text( spsortData.i18n.saving ).prop( 'disabled', true ); 119 120 $.post( 121 spsortData.ajaxUrl, 122 { 123 action : 'spsort_save_rule', 124 nonce : spsortData.nonce, 125 rule_id : $( '#spsort-rule-id' ).val(), 126 rule_name : $( '#spsort-rule-name' ).val(), 127 sort_by : $( '#spsort-sort-by' ).val(), 128 sort_order : $( '#spsort-sort-order' ).val(), 129 category_id: $( '#spsort-category' ).val(), 130 priority : $( '#spsort-priority' ).val(), 131 is_active : $( '#spsort-is-active' ).is( ':checked' ) ? 1 : 0, 132 } 133 ) 134 .done( 135 function ( res ) { 136 if ( res.success ) { 137 showNotice( res.data.message, 'success' ); 138 closeModal(); 139 reloadPage(); 140 } else { 141 showNotice( res.data.message, 'error' ); 142 } 143 } 144 ) 145 .fail( 146 function () { 147 showNotice( spsortData.i18n.error, 'error' ); 148 } 149 ) 150 .always( 151 function () { 152 $saveBtn.text( spsortData.i18n.saveRule ).prop( 'disabled', false ); 153 } 154 ); 155 } 156 ); 126 157 127 158 // ── Delete rule ─────────────────────────────────────────────────────── 128 159 129 $( document ).on( 'click', '.spsort-delete-rule', function () { 130 if ( ! window.confirm( spsortData.i18n.confirmDelete ) ) { 131 return; 132 } 133 134 var $btn = $( this ); 135 var id = $btn.data( 'id' ); 136 137 $btn.prop( 'disabled', true ); 138 139 $.post( spsortData.ajaxUrl, { 140 action : 'spsort_delete_rule', 141 nonce : spsortData.nonce, 142 rule_id : id, 143 } ) 144 .done( function ( res ) { 145 if ( res.success ) { 146 $btn.closest( 'tr' ).fadeOut( 300, function () { 147 $( this ).remove(); 148 if ( 0 === $( '#spsort-rules-body tr' ).length ) { 149 var $td = $( '<td>' ).attr( 'colspan', '7' ).text( spsortData.i18n.noRules ); 150 var $tr = $( '<tr>' ).attr( 'id', 'spsort-no-rules' ).append( $td ); 151 $( '#spsort-rules-body' ).append( $tr ); 160 $( document ).on( 161 'click', 162 '.spsort-delete-rule', 163 function () { 164 if ( ! window.confirm( spsortData.i18n.confirmDelete ) ) { 165 return; 166 } 167 168 var $btn = $( this ); 169 var id = $btn.data( 'id' ); 170 171 $btn.prop( 'disabled', true ); 172 173 $.post( 174 spsortData.ajaxUrl, 175 { 176 action : 'spsort_delete_rule', 177 nonce : spsortData.nonce, 178 rule_id : id, 179 } 180 ) 181 .done( 182 function ( res ) { 183 if ( res.success ) { 184 $btn.closest( 'tr' ).fadeOut( 185 300, 186 function () { 187 $( this ).remove(); 188 if ( 0 === $( '#spsort-rules-body tr' ).length ) { 189 var $div = $( '<div>' ).addClass( 'spsort-empty-state' ); 190 $div.append( $( '<p>' ).text( spsortData.i18n.noRules ) ); 191 var $td = $( '<td>' ).attr( 'colspan', '7' ).append( $div ); 192 var $tr = $( '<tr>' ).attr( 'id', 'spsort-no-rules' ).append( $td ); 193 $( '#spsort-rules-body' ).append( $tr ); 194 } 195 } 196 ); 197 showNotice( res.data.message, 'success' ); 198 } else { 199 showNotice( res.data.message, 'error' ); 200 $btn.prop( 'disabled', false ); 152 201 } 153 } ); 154 showNotice( res.data.message, 'success' ); 155 } else { 156 showNotice( res.data.message, 'error' ); 157 $btn.prop( 'disabled', false ); 158 } 159 } ) 160 .fail( function () { 161 showNotice( spsortData.i18n.error, 'error' ); 162 $btn.prop( 'disabled', false ); 163 } ); 164 } ); 202 } 203 ) 204 .fail( 205 function () { 206 showNotice( spsortData.i18n.error, 'error' ); 207 $btn.prop( 'disabled', false ); 208 } 209 ); 210 } 211 ); 165 212 166 213 // ── Toggle rule ─────────────────────────────────────────────────────── 167 214 168 $( document ).on( 'click', '.spsort-toggle-rule', function () { 169 var $btn = $( this ); 170 var id = $btn.data( 'id' ); 171 172 $btn.prop( 'disabled', true ); 173 174 $.post( spsortData.ajaxUrl, { 175 action : 'spsort_toggle_rule', 176 nonce : spsortData.nonce, 177 rule_id : id, 178 } ) 179 .done( function ( res ) { 180 if ( res.success ) { 181 var active = 1 === res.data.is_active; 182 $btn 183 .toggleClass( 'spsort-active', active ) 184 .toggleClass( 'spsort-inactive', ! active ) 185 .text( active ? spsortData.i18n.active : spsortData.i18n.inactive ); 186 showNotice( res.data.message, 'success' ); 187 } else { 188 showNotice( res.data.message, 'error' ); 189 } 190 } ) 191 .fail( function () { 192 showNotice( spsortData.i18n.error, 'error' ); 193 } ) 194 .always( function () { 195 $btn.prop( 'disabled', false ); 196 } ); 197 } ); 215 $( document ).on( 216 'click', 217 '.spsort-toggle-rule', 218 function () { 219 var $btn = $( this ); 220 var id = $btn.data( 'id' ); 221 222 $btn.prop( 'disabled', true ); 223 224 $.post( 225 spsortData.ajaxUrl, 226 { 227 action : 'spsort_toggle_rule', 228 nonce : spsortData.nonce, 229 rule_id : id, 230 } 231 ) 232 .done( 233 function ( res ) { 234 if ( res.success ) { 235 var active = 1 === res.data.is_active; 236 $btn 237 .toggleClass( 'spsort-active', active ) 238 .toggleClass( 'spsort-inactive', ! active ) 239 .text( active ? spsortData.i18n.active : spsortData.i18n.inactive ); 240 showNotice( res.data.message, 'success' ); 241 } else { 242 showNotice( res.data.message, 'error' ); 243 } 244 } 245 ) 246 .fail( 247 function () { 248 showNotice( spsortData.i18n.error, 'error' ); 249 } 250 ) 251 .always( 252 function () { 253 $btn.prop( 'disabled', false ); 254 } 255 ); 256 } 257 ); 198 258 199 259 } )( jQuery ); -
smart-product-sort/trunk/admin/views/main-page.php
r3489912 r3490772 11 11 12 12 defined( 'ABSPATH' ) || exit; 13 14 $sort_options = SPSORT_Sort_Rules::get_sort_options(); 13 15 ?> 14 16 <div class="wrap spsort-wrap"> 15 <h1 class="wp-heading-inline"> 16 <?php esc_html_e( 'Smart Product Sort', 'smart-product-sort' ); ?> 17 </h1> 18 <button type="button" class="page-title-action" id="spsort-add-rule"> 19 <?php esc_html_e( '+ Add Sort Rule', 'smart-product-sort' ); ?> 20 </button> 21 <hr class="wp-header-end"> 22 23 <div id="spsort-notice" class="notice" style="display:none;"></div> 24 25 <!-- Rules table --> 26 <table class="wp-list-table widefat fixed striped spsort-rules-table"> 27 <thead> 28 <tr> 29 <th scope="col"><?php esc_html_e( 'Rule Name', 'smart-product-sort' ); ?></th> 30 <th scope="col"><?php esc_html_e( 'Sort By', 'smart-product-sort' ); ?></th> 31 <th scope="col"><?php esc_html_e( 'Order', 'smart-product-sort' ); ?></th> 32 <th scope="col"><?php esc_html_e( 'Category', 'smart-product-sort' ); ?></th> 33 <th scope="col"><?php esc_html_e( 'Priority', 'smart-product-sort' ); ?></th> 34 <th scope="col"><?php esc_html_e( 'Status', 'smart-product-sort' ); ?></th> 35 <th scope="col"><?php esc_html_e( 'Actions', 'smart-product-sort' ); ?></th> 36 </tr> 37 </thead> 38 <tbody id="spsort-rules-body"> 39 <?php if ( ! empty( $rules ) ) : ?> 40 <?php foreach ( $rules as $rule ) : ?> 41 <tr data-id="<?php echo esc_attr( $rule->id ); ?>"> 42 <td><strong><?php echo esc_html( $rule->rule_name ); ?></strong></td> 43 <td> 44 <?php 45 $sort_options = SPSORT_Sort_Rules::get_sort_options(); 46 echo esc_html( isset( $sort_options[ $rule->sort_by ] ) ? $sort_options[ $rule->sort_by ] : $rule->sort_by ); 47 ?> 48 </td> 49 <td><?php echo esc_html( $rule->sort_order ); ?></td> 50 <td> 51 <?php 52 if ( $rule->category_id ) { 53 $rule_term = get_term( (int) $rule->category_id, 'product_cat' ); 54 echo esc_html( ( $rule_term && ! is_wp_error( $rule_term ) ) ? $rule_term->name : '—' ); 55 } else { 56 esc_html_e( 'All Products (Global)', 'smart-product-sort' ); 57 } 58 ?> 59 </td> 60 <td><?php echo esc_html( $rule->priority ); ?></td> 61 <td> 62 <button type="button" 63 class="button spsort-toggle-rule <?php echo esc_attr( $rule->is_active ? 'spsort-active' : 'spsort-inactive' ); ?>" 64 data-id="<?php echo esc_attr( $rule->id ); ?>" 65 aria-label="<?php esc_attr_e( 'Toggle rule status', 'smart-product-sort' ); ?>"> 66 <?php echo $rule->is_active ? esc_html__( 'Active', 'smart-product-sort' ) : esc_html__( 'Inactive', 'smart-product-sort' ); ?> 67 </button> 68 </td> 69 <td> 70 <button type="button" class="button spsort-edit-rule" 71 data-id="<?php echo esc_attr( $rule->id ); ?>" 72 data-rule="<?php echo esc_attr( wp_json_encode( $rule ) ); ?>"> 73 <?php esc_html_e( 'Edit', 'smart-product-sort' ); ?> 74 </button> 75 <button type="button" class="button button-link-delete spsort-delete-rule" 76 data-id="<?php echo esc_attr( $rule->id ); ?>"> 77 <?php esc_html_e( 'Delete', 'smart-product-sort' ); ?> 78 </button> 79 </td> 80 </tr> 81 <?php endforeach; ?> 82 <?php else : ?> 83 <tr id="spsort-no-rules"> 84 <td colspan="7"><?php esc_html_e( 'No sort rules found. Add one above!', 'smart-product-sort' ); ?></td> 85 </tr> 86 <?php endif; ?> 87 </tbody> 88 </table> 17 18 <div id="spsort-notice" class="spsort-notice" style="display:none;"></div> 19 20 <!-- Card container --> 21 <div class="spsort-card"> 22 23 <!-- Card header --> 24 <div class="spsort-card-header"> 25 <div class="spsort-card-header-left"> 26 <div class="spsort-logo-icon"> 27 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> 28 <line x1="4" y1="7" x2="20" y2="7"></line> 29 <line x1="4" y1="12" x2="16" y2="12"></line> 30 <line x1="4" y1="17" x2="12" y2="17"></line> 31 </svg> 32 </div> 33 <h1 class="spsort-card-title"><?php esc_html_e( 'Smart Product Sort', 'smart-product-sort' ); ?></h1> 34 </div> 35 <button type="button" class="spsort-btn spsort-btn-primary" id="spsort-add-rule"> 36 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> 37 <line x1="12" y1="5" x2="12" y2="19"></line> 38 <line x1="5" y1="12" x2="19" y2="12"></line> 39 </svg> 40 <?php esc_html_e( 'Add Sort Rule', 'smart-product-sort' ); ?> 41 </button> 42 </div> 43 44 <!-- Rules table --> 45 <div class="spsort-table-wrap"> 46 <table class="spsort-table"> 47 <thead> 48 <tr> 49 <th class="spsort-col-name"><?php esc_html_e( 'Rule Name', 'smart-product-sort' ); ?></th> 50 <th class="spsort-col-sort"><?php esc_html_e( 'Sort By', 'smart-product-sort' ); ?></th> 51 <th class="spsort-col-order"><?php esc_html_e( 'Order', 'smart-product-sort' ); ?></th> 52 <th class="spsort-col-cat"><?php esc_html_e( 'Category', 'smart-product-sort' ); ?></th> 53 <th class="spsort-col-prio"><?php esc_html_e( 'Priority', 'smart-product-sort' ); ?></th> 54 <th class="spsort-col-status"><?php esc_html_e( 'Status', 'smart-product-sort' ); ?></th> 55 <th class="spsort-col-actions"><?php esc_html_e( 'Actions', 'smart-product-sort' ); ?></th> 56 </tr> 57 </thead> 58 <tbody id="spsort-rules-body"> 59 <?php if ( ! empty( $rules ) ) : ?> 60 <?php foreach ( $rules as $rule ) : ?> 61 <tr data-id="<?php echo esc_attr( $rule->id ); ?>"> 62 <td class="spsort-col-name"> 63 <strong><?php echo esc_html( $rule->rule_name ); ?></strong> 64 </td> 65 <td class="spsort-col-sort"> 66 <span class="spsort-sort-badge"> 67 <?php echo esc_html( isset( $sort_options[ $rule->sort_by ] ) ? $sort_options[ $rule->sort_by ] : $rule->sort_by ); ?> 68 </span> 69 </td> 70 <td class="spsort-col-order"> 71 <span class="spsort-order-badge spsort-order-<?php echo esc_attr( strtolower( $rule->sort_order ) ); ?>"> 72 <?php if ( 'ASC' === $rule->sort_order ) : ?> 73 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"></polyline></svg> 74 <?php else : ?> 75 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg> 76 <?php endif; ?> 77 <?php echo esc_html( $rule->sort_order ); ?> 78 </span> 79 </td> 80 <td class="spsort-col-cat"> 81 <?php 82 if ( $rule->category_id ) { 83 $rule_term = get_term( (int) $rule->category_id, 'product_cat' ); 84 echo esc_html( ( $rule_term && ! is_wp_error( $rule_term ) ) ? $rule_term->name : '—' ); 85 } else { 86 echo '<span class="spsort-global-tag">' . esc_html__( 'Global', 'smart-product-sort' ) . '</span>'; 87 } 88 ?> 89 </td> 90 <td class="spsort-col-prio"> 91 <span class="spsort-priority-num"><?php echo esc_html( $rule->priority ); ?></span> 92 </td> 93 <td class="spsort-col-status"> 94 <button type="button" 95 class="spsort-toggle-rule <?php echo esc_attr( $rule->is_active ? 'spsort-active' : 'spsort-inactive' ); ?>" 96 data-id="<?php echo esc_attr( $rule->id ); ?>" 97 aria-label="<?php esc_attr_e( 'Toggle rule status', 'smart-product-sort' ); ?>"> 98 <span class="spsort-status-dot"></span> 99 <?php echo $rule->is_active ? esc_html__( 'Active', 'smart-product-sort' ) : esc_html__( 'Inactive', 'smart-product-sort' ); ?> 100 </button> 101 </td> 102 <td class="spsort-col-actions"> 103 <div class="spsort-action-btns"> 104 <button type="button" class="spsort-btn-icon spsort-edit-rule" 105 data-id="<?php echo esc_attr( $rule->id ); ?>" 106 data-rule="<?php echo esc_attr( wp_json_encode( $rule ) ); ?>" 107 title="<?php esc_attr_e( 'Edit', 'smart-product-sort' ); ?>"> 108 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 109 <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path> 110 <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path> 111 </svg> 112 </button> 113 <button type="button" class="spsort-btn-icon spsort-btn-icon-danger spsort-delete-rule" 114 data-id="<?php echo esc_attr( $rule->id ); ?>" 115 title="<?php esc_attr_e( 'Delete', 'smart-product-sort' ); ?>"> 116 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 117 <polyline points="3 6 5 6 21 6"></polyline> 118 <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path> 119 </svg> 120 </button> 121 </div> 122 </td> 123 </tr> 124 <?php endforeach; ?> 125 <?php else : ?> 126 <tr id="spsort-no-rules"> 127 <td colspan="7"> 128 <div class="spsort-empty-state"> 129 <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> 130 <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect> 131 <line x1="12" y1="8" x2="12" y2="16"></line> 132 <line x1="8" y1="12" x2="16" y2="12"></line> 133 </svg> 134 <p><?php esc_html_e( 'No sort rules yet', 'smart-product-sort' ); ?></p> 135 <span><?php esc_html_e( 'Click "Add Sort Rule" to create your first rule.', 'smart-product-sort' ); ?></span> 136 </div> 137 </td> 138 </tr> 139 <?php endif; ?> 140 </tbody> 141 </table> 142 </div> 143 144 </div><!-- .spsort-card --> 89 145 90 146 <!-- Add / Edit Modal --> 91 147 <div id="spsort-modal-overlay" style="display:none;"> 92 148 <div id="spsort-modal"> 93 <h2 id="spsort-modal-title"><?php esc_html_e( 'Add Sort Rule', 'smart-product-sort' ); ?></h2> 149 <div class="spsort-modal-header"> 150 <h2 id="spsort-modal-title"><?php esc_html_e( 'Add Sort Rule', 'smart-product-sort' ); ?></h2> 151 <button type="button" class="spsort-modal-close" id="spsort-cancel-btn" aria-label="<?php esc_attr_e( 'Close', 'smart-product-sort' ); ?>"> 152 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 153 <line x1="18" y1="6" x2="6" y2="18"></line> 154 <line x1="6" y1="6" x2="18" y2="18"></line> 155 </svg> 156 </button> 157 </div> 94 158 95 159 <form id="spsort-rule-form" novalidate> 96 160 <input type="hidden" id="spsort-rule-id" name="rule_id" value=""> 97 161 98 <table class="form-table" role="presentation"> 99 <tr> 100 <th scope="row"> 101 <label for="spsort-rule-name"><?php esc_html_e( 'Rule Name', 'smart-product-sort' ); ?> <span class="required">*</span></label> 102 </th> 103 <td> 104 <input type="text" id="spsort-rule-name" name="rule_name" class="regular-text" required> 105 </td> 106 </tr> 107 <tr> 108 <th scope="row"> 109 <label for="spsort-sort-by"><?php esc_html_e( 'Sort By', 'smart-product-sort' ); ?></label> 110 </th> 111 <td> 112 <select id="spsort-sort-by" name="sort_by"> 162 <div class="spsort-form-body"> 163 <div class="spsort-field"> 164 <label for="spsort-rule-name" class="spsort-label"> 165 <?php esc_html_e( 'Rule Name', 'smart-product-sort' ); ?> 166 <span class="spsort-required">*</span> 167 </label> 168 <input type="text" id="spsort-rule-name" name="rule_name" class="spsort-input" placeholder="<?php esc_attr_e( 'e.g. Premium Products First', 'smart-product-sort' ); ?>" required> 169 </div> 170 171 <div class="spsort-field-row"> 172 <div class="spsort-field"> 173 <label for="spsort-sort-by" class="spsort-label"><?php esc_html_e( 'Sort By', 'smart-product-sort' ); ?></label> 174 <select id="spsort-sort-by" name="sort_by" class="spsort-select"> 113 175 <?php foreach ( SPSORT_Sort_Rules::get_sort_options() as $key => $label ) : ?> 114 176 <option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $label ); ?></option> 115 177 <?php endforeach; ?> 116 178 </select> 117 </td> 118 </tr> 119 <tr> 120 <th scope="row"> 121 <label for="spsort-sort-order"><?php esc_html_e( 'Sort Order', 'smart-product-sort' ); ?></label> 122 </th> 123 <td> 124 <select id="spsort-sort-order" name="sort_order"> 179 </div> 180 <div class="spsort-field"> 181 <label for="spsort-sort-order" class="spsort-label"><?php esc_html_e( 'Sort Order', 'smart-product-sort' ); ?></label> 182 <select id="spsort-sort-order" name="sort_order" class="spsort-select"> 125 183 <option value="DESC"><?php esc_html_e( 'Descending (High → Low)', 'smart-product-sort' ); ?></option> 126 184 <option value="ASC"><?php esc_html_e( 'Ascending (Low → High)', 'smart-product-sort' ); ?></option> 127 185 </select> 128 </td> 129 </tr> 130 <tr> 131 <th scope="row"> 132 <label for="spsort-category"><?php esc_html_e( 'Apply to Category', 'smart-product-sort' ); ?></label> 133 </th> 134 <td> 135 <select id="spsort-category" name="category_id"> 136 <option value=""><?php esc_html_e( '— All Products (Global) —', 'smart-product-sort' ); ?></option> 186 </div> 187 </div> 188 189 <div class="spsort-field-row"> 190 <div class="spsort-field"> 191 <label for="spsort-category" class="spsort-label"><?php esc_html_e( 'Category', 'smart-product-sort' ); ?></label> 192 <select id="spsort-category" name="category_id" class="spsort-select"> 193 <option value=""><?php esc_html_e( 'All Products (Global)', 'smart-product-sort' ); ?></option> 137 194 <?php if ( ! is_wp_error( $categories ) ) : ?> 138 195 <?php foreach ( $categories as $category ) : ?> … … 143 200 <?php endif; ?> 144 201 </select> 145 <p class="description"><?php esc_html_e( 'Leave blank to apply globally. Category-specific rules take priority.', 'smart-product-sort' ); ?></p> 146 </td> 147 </tr> 148 <tr> 149 <th scope="row"> 150 <label for="spsort-priority"><?php esc_html_e( 'Priority', 'smart-product-sort' ); ?></label> 151 </th> 152 <td> 153 <input type="number" id="spsort-priority" name="priority" value="10" min="0" max="999" class="small-text"> 154 <p class="description"><?php esc_html_e( 'Lower number = higher priority. Default: 10.', 'smart-product-sort' ); ?></p> 155 </td> 156 </tr> 157 <tr> 158 <th scope="row"> 159 <label for="spsort-is-active"><?php esc_html_e( 'Active', 'smart-product-sort' ); ?></label> 160 </th> 161 <td> 202 <span class="spsort-hint"><?php esc_html_e( 'Category rules override global rules.', 'smart-product-sort' ); ?></span> 203 </div> 204 <div class="spsort-field"> 205 <label for="spsort-priority" class="spsort-label"><?php esc_html_e( 'Priority', 'smart-product-sort' ); ?></label> 206 <input type="number" id="spsort-priority" name="priority" value="10" min="0" max="999" class="spsort-input"> 207 <span class="spsort-hint"><?php esc_html_e( 'Lower = higher priority. Default: 10.', 'smart-product-sort' ); ?></span> 208 </div> 209 </div> 210 211 <div class="spsort-field spsort-field-toggle"> 212 <label class="spsort-toggle-label"> 162 213 <input type="checkbox" id="spsort-is-active" name="is_active" value="1" checked> 163 </td> 164 </tr> 165 </table> 214 <span class="spsort-toggle-switch"></span> 215 <span class="spsort-toggle-text"><?php esc_html_e( 'Active', 'smart-product-sort' ); ?></span> 216 </label> 217 </div> 218 </div> 166 219 167 220 <div class="spsort-modal-footer"> 168 <button type="submit" class="button button-primary" id="spsort-save-btn"> 221 <button type="button" class="spsort-btn spsort-btn-ghost" id="spsort-cancel-btn-footer"> 222 <?php esc_html_e( 'Cancel', 'smart-product-sort' ); ?> 223 </button> 224 <button type="submit" class="spsort-btn spsort-btn-primary" id="spsort-save-btn"> 225 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> 226 <polyline points="20 6 9 17 4 12"></polyline> 227 </svg> 169 228 <?php esc_html_e( 'Save Rule', 'smart-product-sort' ); ?> 170 </button>171 <button type="button" class="button" id="spsort-cancel-btn">172 <?php esc_html_e( 'Cancel', 'smart-product-sort' ); ?>173 229 </button> 174 230 </div> -
smart-product-sort/trunk/readme.txt
r3489912 r3490772 7 7 WC requires at least: 7.1 8 8 WC tested up to: 10.5.3 9 Stable tag: 1.0. 09 Stable tag: 1.0.1 10 10 License: GPL-2.0-or-later 11 11 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 97 97 == Changelog == 98 98 99 = 1.0.1 = 100 * Redesigned admin interface with enterprise-grade UI. 101 * Improved table layout, modal design, and status badges. 102 * Added sort order indicators and empty state design. 103 99 104 = 1.0.0 = 100 105 * Initial release. … … 102 107 == Upgrade Notice == 103 108 109 = 1.0.1 = 110 Redesigned admin interface with a cleaner, modern look. 111 104 112 = 1.0.0 = 105 113 Initial release of Smart Product Sort. -
smart-product-sort/trunk/smart-product-sort.php
r3489912 r3490772 3 3 * Plugin Name: Smart Product Sort 4 4 * Description: Advanced product sorting rules for WooCommerce — per-category, priority-based, toggle on/off instantly. 5 * Version: 1.0. 05 * Version: 1.0.1 6 6 * Author: plgnplay 7 7 * License: GPL-2.0-or-later … … 25 25 26 26 // Plugin constants. 27 define( 'SPSORT_VERSION', '1.0. 0' );27 define( 'SPSORT_VERSION', '1.0.1' ); 28 28 define( 'SPSORT_PLUGIN_FILE', __FILE__ ); 29 29 define( 'SPSORT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
Note: See TracChangeset
for help on using the changeset viewer.