Plugin Directory

Changeset 3490772


Ignore:
Timestamp:
03/25/2026 10:53:38 AM (7 days ago)
Author:
plgnplay
Message:

Updated to v1.0.1 — redesigned admin UI

Location:
smart-product-sort/trunk
Files:
5 edited

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 ─────────────────────────── */
    440.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);
    14206    vertical-align: middle;
    15207}
    16208
    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
     235thead .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 ─────────────────────────────── */
    18281.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;
    22300}
    23301
    24302.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);
    28310}
    29311
    30312.spsort-toggle-rule.spsort-active:hover {
    31     background: #007a1f;
    32     border-color: #007a1f;
    33     color: #fff;
     313    background: #A7F3D0;
    34314}
    35315
    36316.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);
    40323}
    41324
    42325.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 ─────────────────────────────── */
    49382#spsort-modal-overlay {
    50383    position: fixed;
    51384    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);
    53388    z-index: 100000;
    54389    display: flex;
    55390    align-items: center;
    56391    justify-content: center;
     392    padding: 20px;
    57393}
    58394
    59395#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%;
    66401    max-height: 90vh;
    67402    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 ──────────────────────────────── */
    82577.spsort-modal-footer {
    83578    display: flex;
     579    justify-content: flex-end;
    84580    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
    17/* global spsortData, jQuery */
    28( function ( $ ) {
    39    'use strict';
    410
    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' );
    915
    1016    // ── Helpers ───────────────────────────────────────────────────────────
     
    4854    // ── Add rule ──────────────────────────────────────────────────────────
    4955
    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    );
    5362
    5463    // ── Edit rule ─────────────────────────────────────────────────────────
    5564
    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    );
    6982
    7083    // ── Close modal ───────────────────────────────────────────────────────
    7184
    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    );
    85104
    86105    // ── Save rule (create / update) ───────────────────────────────────────
    87106
    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    );
    126157
    127158    // ── Delete rule ───────────────────────────────────────────────────────
    128159
    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 );
    152201                    }
    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    );
    165212
    166213    // ── Toggle rule ───────────────────────────────────────────────────────
    167214
    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    );
    198258
    199259} )( jQuery );
  • smart-product-sort/trunk/admin/views/main-page.php

    r3489912 r3490772  
    1111
    1212defined( 'ABSPATH' ) || exit;
     13
     14$sort_options = SPSORT_Sort_Rules::get_sort_options();
    1315?>
    1416<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 -->
    89145
    90146    <!-- Add / Edit Modal -->
    91147    <div id="spsort-modal-overlay" style="display:none;">
    92148        <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>
    94158
    95159            <form id="spsort-rule-form" novalidate>
    96160                <input type="hidden" id="spsort-rule-id" name="rule_id" value="">
    97161
    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">
    113175                                <?php foreach ( SPSORT_Sort_Rules::get_sort_options() as $key => $label ) : ?>
    114176                                    <option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $label ); ?></option>
    115177                                <?php endforeach; ?>
    116178                            </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">
    125183                                <option value="DESC"><?php esc_html_e( 'Descending (High → Low)', 'smart-product-sort' ); ?></option>
    126184                                <option value="ASC"><?php esc_html_e( 'Ascending (Low → High)', 'smart-product-sort' ); ?></option>
    127185                            </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>
    137194                                <?php if ( ! is_wp_error( $categories ) ) : ?>
    138195                                    <?php foreach ( $categories as $category ) : ?>
     
    143200                                <?php endif; ?>
    144201                            </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">
    162213                            <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>
    166219
    167220                <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>
    169228                        <?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' ); ?>
    173229                    </button>
    174230                </div>
  • smart-product-sort/trunk/readme.txt

    r3489912 r3490772  
    77WC requires at least: 7.1
    88WC tested up to:   10.5.3
    9 Stable tag:        1.0.0
     9Stable tag:        1.0.1
    1010License:           GPL-2.0-or-later
    1111License URI:       https://www.gnu.org/licenses/gpl-2.0.html
     
    9797== Changelog ==
    9898
     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
    99104= 1.0.0 =
    100105* Initial release.
     
    102107== Upgrade Notice ==
    103108
     109= 1.0.1 =
     110Redesigned admin interface with a cleaner, modern look.
     111
    104112= 1.0.0 =
    105113Initial release of Smart Product Sort.
  • smart-product-sort/trunk/smart-product-sort.php

    r3489912 r3490772  
    33 * Plugin Name:       Smart Product Sort
    44 * Description:       Advanced product sorting rules for WooCommerce — per-category, priority-based, toggle on/off instantly.
    5  * Version:           1.0.0
     5 * Version:           1.0.1
    66 * Author:            plgnplay
    77 * License:           GPL-2.0-or-later
     
    2525
    2626// Plugin constants.
    27 define( 'SPSORT_VERSION', '1.0.0' );
     27define( 'SPSORT_VERSION', '1.0.1' );
    2828define( 'SPSORT_PLUGIN_FILE', __FILE__ );
    2929define( 'SPSORT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
Note: See TracChangeset for help on using the changeset viewer.