Plugin Directory

Changeset 3392758


Ignore:
Timestamp:
11/10/2025 08:00:53 AM (3 months ago)
Author:
neebplugins
Message:

Release 1.0.6

Location:
kanban-for-woocommerce
Files:
268 added
8 edited

Legend:

Unmodified
Added
Removed
  • kanban-for-woocommerce/trunk/assets/css/board.css

    r3331563 r3392758  
     1/* ---------------------------------------------------
     2   Kanban Board – VendBase Minimal Compact Version
     3   Optimized for no vertical scroll between header/search/footer
     4--------------------------------------------------- */
     5
     6/* --- Layout & Container --- */
     7body,
     8.wrap {
     9    background: #fafafa;
     10    font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
     11}
     12
    113.kanban-board {
    214    display: flex;
    3     overflow-x: auto;   
    4 }
    5 
     15    flex-wrap: nowrap;
     16    overflow-x: auto;
     17    gap: 0.75rem;
     18    scrollbar-width: thin;
     19    scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
     20}
     21
     22.kanban-board::-webkit-scrollbar {
     23    height: 6px;
     24}
     25
     26.kanban-board::-webkit-scrollbar-thumb {
     27    background: rgba(0, 0, 0, 0.2);
     28    border-radius: 4px;
     29}
     30
     31.kanban-board::-webkit-scrollbar-track {
     32    background: transparent;
     33}
     34
     35/* --- Heading Container --- */
     36.container-fluid.mb-3 {
     37    margin-bottom: 0.5rem !important;
     38    /* Reduce spacing below title bar */
     39}
     40
     41.wrap h2,
     42.wrap h1 {
     43    margin-bottom: 0.25rem !important;
     44}
     45
     46/* --- Search Bar --- */
     47.search-container {
     48    display: flex;
     49    justify-content: center;
     50    margin: 0.5rem 0 0.75rem 0;
     51    /* Compact spacing */
     52}
     53
     54.search-input {
     55    width: 100%;
     56    max-width: 300px;
     57    height: 34px;
     58    font-size: 0.9rem;
     59    border-radius: 8px;
     60    border: 1px solid rgba(0, 0, 0, 0.1);
     61    padding: 0 0.9rem;
     62    background: #fff;
     63    box-shadow: none;
     64    transition: all 0.2s ease-in-out;
     65}
     66
     67.search-input:focus {
     68    border-color: #818cf8;
     69    box-shadow: 0 0 0 2px rgba(129, 140, 248, 0.2);
     70    outline: none;
     71}
     72
     73/* --- Column Styling --- */
    674.kanban-column {
    7     background-color: #fff;
     75    background: #fff;
     76    border: 1px solid rgba(0, 0, 0, 0.05);
     77    border-radius: 10px;
     78    flex: 0 0 280px;
     79    display: flex;
     80    flex-direction: column;
     81    max-height: calc(100vh - 170px);
     82    /* Compact height for visible footer */
     83    transition: all 0.2s ease-in-out;
     84    padding-inline: 0.25rem;
     85}
     86
     87.kanban-column:hover {
     88    background: #f9f9f9;
     89    border-color: rgba(0, 0, 0, 0.08);
     90}
     91
     92/* --- Column Header --- */
     93.kanban-column-header {
     94    font-size: 0.9rem;
     95    font-weight: 600;
     96    color: #333;
     97    margin: 0;
     98    padding: 0.4rem 0.6rem;
     99    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
     100    flex-shrink: 0;
     101}
     102
     103/* --- Scrollable Column Body --- */
     104.column-body {
     105    overflow-y: auto;
     106    padding: 0.4rem 0.55rem 0.5rem 0.55rem;
     107    flex-grow: 1;
     108    scrollbar-width: thin;
     109    scrollbar-color: rgba(0, 0, 0, 0.15) transparent;
     110}
     111
     112.column-body::-webkit-scrollbar {
     113    width: 6px;
     114}
     115
     116.column-body::-webkit-scrollbar-thumb {
     117    background: rgba(0, 0, 0, 0.15);
     118    border-radius: 4px;
     119}
     120
     121.column-body::-webkit-scrollbar-track {
     122    background: transparent;
     123}
     124
     125/* --- Column Accent Colors (Top Borders) --- */
     126.kanban-column[data-status="pending"] {
     127    border-top: 7px solid #fbbf24;
     128}
     129
     130.kanban-column[data-status="processing"] {
     131    border-top: 7px solid #818cf8;
     132}
     133
     134.kanban-column[data-status="on-hold"] {
     135    border-top: 7px solid #ff4500;
     136}
     137
     138.kanban-column[data-status="completed"] {
     139    border-top: 7px solid #4ade80;
     140}
     141
     142.kanban-column[data-status="cancelled"] {
     143    border-top: 7px solid #a1a1aa;
     144}
     145
     146.kanban-column[data-status="refunded"] {
     147    border-top: 7px solid #6366f1;
     148}
     149
     150.kanban-column[data-status="failed"] {
     151    border-top: 7px solid #b22222;
     152}
     153
     154/* --- Order Cards --- */
     155.order-card {
     156    background: #fff;
     157    border: 1px solid rgba(0, 0, 0, 0.05);
     158    border-left: 1px solid transparent;
    8159    border-radius: 8px;
    9     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
    10     padding: 15px;
    11     min-width: 250px;
    12     flex: 0 0 auto;
    13     margin-right: 15px;   
    14 }
    15 
    16 .kanban-column:last-child {
    17     margin-right: 0;
    18 }
    19 
    20 .kanban-column-header {
    21     margin: 0px;
    22     margin-bottom: 10px;
    23     padding-bottom: 8px;
    24     border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    25     font-weight: 600;
    26     text-align: left;
    27     color: #333;
    28     font-size: 1rem;
    29 }
    30 
    31 /* Vibrant column header colors */
    32 .kanban-column[data-status="pending"] .kanban-column-header {
    33     color: #ffc107;
    34 }
    35 
    36 .kanban-column[data-status="processing"] .kanban-column-header {
    37     color: #007bff;
    38 }
    39 
    40 .kanban-column[data-status="on-hold"] .kanban-column-header {
    41     color: #dc3545;
    42 }
    43 
    44 .kanban-column[data-status="completed"] .kanban-column-header {
    45     color: #28a745;
    46 }
    47 
    48 .kanban-column[data-status="cancelled"] .kanban-column-header {
    49     color: #6c757d;
    50 }
    51 
    52 .kanban-column[data-status="refunded"] .kanban-column-header {
    53     color: #17a2b8;
     160    padding: 0.55rem 0.7rem;
     161    margin-bottom: 0.45rem;
     162    transition: all 0.2s ease-in-out;
     163    font-size: 0.85rem;
     164    cursor: grab;
     165}
     166
     167.order-card:hover {
     168    background: #f9fafb;
     169    transform: translateY(-1px);
     170    border-color: rgba(0, 0, 0, 0.08);
     171}
     172
     173.order-card.dragging {
     174    opacity: 0.7;
     175    transform: scale(0.98);
     176}
     177
     178.column-body {
     179    transition: all 0.2s ease-in-out;
    54180}
    55181
    56182.order-card {
    57     background-color: #fff;
    58     border-left: 4px solid transparent;
    59     border-radius: 6px;
    60     padding: 3px;
    61     margin-bottom: 5px;
    62     cursor: grab;
    63     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
    64     transition: box-shadow 0.2s ease-in-out, border-left-color 0.2s ease-in-out;
    65     font-size: 0.8rem;
    66     display: grid;
    67     grid-template-columns: auto auto;
    68     grid-row-gap: 2px;
    69     align-items: center;
    70     border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    71 }
    72 
    73 .order-card:hover {
    74     box-shadow: 0 3px 7px rgba(0, 0, 0, 0.15);
    75 }
    76 
    77 /* Subtle card status colors (using border-left) */
     183    transition: transform 0.2s ease-in-out;
     184}
     185
     186.order-card.dragging {
     187    cursor: grabbing;
     188    opacity: 0.95;
     189    transform: scale(1.03);
     190    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
     191    z-index: 1000;
     192}
     193
     194.order-card-placeholder {
     195    background: rgba(129, 140, 248, 0.1);
     196    border: 2px dashed rgba(129, 140, 248, 0.4);
     197    border-radius: 8px;
     198    height: 60px;
     199    margin-bottom: 0.6rem;
     200    transition: all 0.25s ease-in-out;
     201}
     202
     203.kanban-column .column-body.sortable-placeholder-active {
     204    background: rgba(99, 102, 241, 0.03);
     205}
     206
     207.kanban-column.drag-hover {
     208    background: rgba(99, 102, 241, 0.05);
     209    border: 2px dashed rgba(107, 114, 128, 0.35);
     210    transition: background 0.15s ease-in-out;
     211}
     212
     213.order-card.dragging {
     214    cursor: grabbing !important;
     215    opacity: 0.95 !important;
     216}
     217
     218.order-card.drop-anim {
     219    will-change: transform;
     220}
     221
     222.order-count {
     223    font-size: 0.85em;
     224    font-weight:500;
     225}
     226
     227/* --- Order Card Accents by Status --- */
    78228.kanban-column[data-status="pending"] .order-card {
    79     border-left-color: #ffc107;
     229    border-left-color: #fbbf24;
    80230}
    81231
    82232.kanban-column[data-status="processing"] .order-card {
    83     border-left-color: #007bff;
     233    border-left-color: #818cf8;
    84234}
    85235
    86236.kanban-column[data-status="on-hold"] .order-card {
    87     border-left-color: #dc3545;
     237    border-left-color: #ff4500;
    88238}
    89239
    90240.kanban-column[data-status="completed"] .order-card {
    91     border-left-color: #28a745;
     241    border-left-color: #4ade80;
    92242}
    93243
    94244.kanban-column[data-status="cancelled"] .order-card {
    95     border-left-color: #6c757d;
     245    border-left-color: #a1a1aa;
    96246}
    97247
    98248.kanban-column[data-status="refunded"] .order-card {
    99     border-left-color: #17a2b8;
    100 }
    101 
     249    border-left-color: #6366f1;
     250}
     251
     252.kanban-column[data-status="failed"] .order-card {
     253    border-left-color: #b22222;
     254}
     255
     256/* --- Card Content Layout --- */
    102257.order-header {
    103     grid-column: 1 / span 2;
    104258    display: flex;
    105259    justify-content: space-between;
    106260    align-items: baseline;
    107     margin-bottom: 2px;
    108     padding-top: 0;
     261    margin-bottom: 0.2rem;
    109262}
    110263
    111264.order-number {
    112     font-size: 0.85rem;
     265    font-size: 0.88rem;
    113266    font-weight: 600;
    114     color: #333;
    115     margin-right: 5px;
    116     margin-bottom: 0;
    117     margin-top: 0;
    118     padding-top: 0;
     267    color: #1f2937;
     268    margin: 0;
    119269}
    120270
    121271.customer-info {
    122     font-size: 0.7rem;
    123     color: #555;
    124     margin-bottom: 0;
     272    font-size: 0.74rem;
     273    color: #6b7280;
     274    margin: 0;
    125275}
    126276
    127277.product-name {
    128     grid-column: 1 / span 2;
     278    display: block;
     279    font-size: 0.74rem;
     280    color: #71717a;
     281    margin-bottom: 0.2rem;
    129282    font-style: italic;
    130     color: #777;
    131     font-size: 0.7rem;
    132     margin-bottom: 2px;
    133283}
    134284
     
    136286    display: flex;
    137287    justify-content: space-between;
    138     align-items: baseline;
    139     grid-column: 1 / span 2;
     288    align-items: center;
    140289}
    141290
    142291.order-total {
    143     font-size: 0.75rem;
    144     color: #28a745;
     292    font-size: 0.78rem;
     293    color: #16a34a;
    145294    font-weight: 500;
    146295}
    147296
    148297.payment-method {
    149     font-size: 0.7rem;
    150     color: #6c757d;
     298    font-size: 0.74rem;
     299    color: #71717a;
    151300    text-align: right;
    152301}
    153302
    154 .order-card.dragging {
    155     opacity: 0.7;
    156     box-shadow: none;
    157     border-left-color: #007bff !important;
    158 }
    159 
    160 /* Improved Search Bar */
    161 .search-container {   
    162     display: flex;
    163     gap: 10px;
    164     align-items: center;
    165 }
    166 
    167 .search-input {
    168     flex-grow: 1;
    169     padding: 8px;
    170     border: 1px solid #ced4da;
     303/* --- Load More Button --- */
     304.load-more-container {
     305    text-align: center;
     306    margin-top: 0.75rem;
     307}
     308
     309.load-more-container button {
     310    background: #f4f4f5;
     311    border: 1px solid #e4e4e7;
    171312    border-radius: 6px;
     313    color: #444;
    172314    font-size: 0.9rem;
    173 }
    174 
    175 .search-button {
    176     padding: 8px 15px;
    177     border: none;
    178     background-color: #007bff;
    179     color: white;
    180     border-radius: 6px;
    181     cursor: pointer;
    182     font-size: 0.9rem;
    183     transition: background-color 0.2s ease-in-out;
    184 }
    185 
    186 .search-button:hover {
    187     background-color: #0056b3;
    188 }
    189 
    190 .search-icon {
    191     margin-right: 5px;
    192 }
     315    padding: 0.35rem 1rem;
     316    transition: background 0.2s ease-in-out;
     317}
     318
     319.load-more-container button:hover {
     320    background: #e4e4e7;
     321}
     322
     323/* --- Responsiveness --- */
     324@media (max-width: 768px) {
     325    .kanban-board {
     326        flex-direction: column;
     327        gap: 0.75rem;
     328    }
     329
     330    .kanban-column {
     331        min-width: 100%;
     332        max-height: none;
     333    }
     334
     335    .column-body {
     336        max-height: none;
     337        overflow-y: visible;
     338    }
     339}
  • kanban-for-woocommerce/trunk/assets/js/board.js

    r3390883 r3392758  
    1212    const nonceUpdate = kbfwc_ajax_params.nonce_update;
    1313    const searchInput = $('#order-search');
    14     const searchButton = $('#search-button');
     14    const searchButton = $('.search-button');
    1515    const loadMoreButton = $('#load-more-orders');
    1616    const orderDetailsModal = $('#order-details-modal');
    1717    const orderDetailsContent = $('#order-details-content');
    18     let allOrders = {}; // Store all fetched orders by status for frontend filtering
     18    let allOrders = {};
    1919    let currentPage = 1;
    2020    let isLoading = false;
     
    4848            type: 'POST',
    4949            data: {
    50                 action: 'kbfwc_get_orders', // Use the new action name
     50                action: 'kbfwc_get_orders',
    5151                page: page,
    5252                nonce_fetch: nonceFetch
     
    5656                if (response.success) {
    5757                    const data = response.data;
    58                     const statuses = kbfwc_ajax_params.default_statuses
     58                    const statuses = kbfwc_ajax_params.default_statuses;
    5959
    6060                    if (page === 1) {
    61                         kanbanBoard.empty(); // Clear the board on the first load
     61                        kanbanBoard.empty();
    6262                        allOrders = {};
    6363                    }
     
    7474                        if (!allOrders[statusKey]) {
    7575                            allOrders[statusKey] = [];
    76                             const column = $('<div class="kanban-column col-md-3 mb-4 card" data-status="' + statusKey + '">');
    77                             column.append(`<h6 class="kanban-column-header">${label}</h6><div class="column-body"></div>`);
     76                            const column = $('<div class="kanban-column col-md-3 mb-0 card" data-status="' + statusKey + '">');
     77                            column.append(` <div class="d-flex justify-content-between align-items-center mb-1">
     78                                                <h6 class="kanban-column-header mb-0">${label}</h6>
     79                                                <small><span class="order-count">(0/0)</span></small>
     80                                            </div>
     81                                            <div class="column-body"></div>
     82                                            `);
     83
    7884                            kanbanBoard.append(column);
    7985                        }
     
    9298                    if (ordersAdded) {
    9399                        currentPage++;
    94                         loadMoreButton.text('Load More Orders');
     100                        loadMoreButton.text('Load More');
    95101                    } else {
    96102                        loadMoreButton.hide();
     
    98104
    99105                    initDragAndDrop();
    100                     attachCardClickListeners(); // Attach listeners after new cards are loaded
     106                    attachCardClickListeners();
     107                    updateColumnCounts();
    101108                } else {
    102109                    console.error('Error fetching orders:', response.data);
    103                     loadMoreButton.text('Load More Orders');
     110                    loadMoreButton.text('Load More');
    104111                    if (page === 1) {
    105112                        kanbanBoard.html(`<div class="alert alert-danger">Error loading orders.</div>`);
     
    120127    function initDragAndDrop() {
    121128        $('.order-card').draggable({
    122             revert: true,
     129            helper: function () {
     130                // Create a visible drag clone
     131                const clone = $(this).clone();
     132                clone.addClass('dragging');
     133                clone.css({
     134                    width: $(this).outerWidth(),
     135                    zIndex: 9999,
     136                    transform: 'scale(1.05)',
     137                    boxShadow: '0 12px 24px rgba(0,0,0,0.15)',
     138                    opacity: 0.95,
     139                    borderRadius: '8px',
     140                    transition: 'transform 0.15s ease-in-out'
     141                });
     142                return clone;
     143            },
     144            appendTo: 'body',
     145            revert: 'invalid',
    123146            cursor: 'grabbing',
    124             zIndex: 10,
     147            zIndex: 9999,
     148            start: function (event, ui) {
     149                $(this).addClass('drag-origin');
     150                $(this).css('opacity', '0.3'); // Dim original card
     151            },
    125152            drag: function (event, ui) {
    126                 $(this).addClass('dragging');
     153                ui.helper.css('transform', 'scale(1.05)'); // Slight lift
    127154            },
    128155            stop: function (event, ui) {
    129                 $(this).removeClass('dragging');
     156                $(this).removeClass('drag-origin').css('opacity', '');
     157                // Drop animation bounce
     158                const card = $(this);
     159                card.addClass('drop-anim');
     160                card.css('transition', 'transform 0.25s cubic-bezier(0.22,1,0.36,1)');
     161                card.css('transform', 'scale(1.03)');
     162                setTimeout(() => {
     163                    card.css('transform', 'scale(1)');
     164                    setTimeout(() => {
     165                        card.css('transition', '');
     166                        card.removeClass('drop-anim');
     167                    }, 150);
     168                }, 150);
     169                updateColumnCounts();
    130170            }
    131171        });
     
    133173        $('.kanban-column').droppable({
    134174            accept: '.order-card',
     175            tolerance: 'pointer',
     176            over: function () {
     177                $(this).addClass('drag-hover');
     178            },
     179            out: function () {
     180                $(this).removeClass('drag-hover');
     181            },
    135182            drop: function (event, ui) {
    136                 const orderId = ui.draggable.data('order-id');
     183                const card = ui.draggable;
     184                const orderId = card.data('order-id');
     185                const oldStatus = card.closest('.kanban-column').data('status');
    137186                const newStatus = $(this).data('status');
    138                 const card = ui.draggable;
     187
     188                $(this).removeClass('drag-hover');
     189               
     190                if (oldStatus === newStatus) {                   
     191                    card.css({ transform: 'scale(1.02)' });
     192                    setTimeout(() => card.css({ transform: 'scale(1)' }), 150);
     193                    updateColumnCounts();
     194                    return;
     195                }
    139196
    140197                $(this).find('.column-body').append(card);
    141198                updateOrderStatus(orderId, newStatus, card);
     199                updateColumnCounts();
    142200            }
    143201        });
     
    156214            success: function (response) {
    157215                if (response.success) {
    158                     console.log('Order ' + orderId + ' updated to ' + newStatus);
    159                     // Optionally provide visual feedback on success
     216                    const labelStatus = newStatus.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
     217                    toastr.success('Order #' + orderId + ' updated to ' + labelStatus);
    160218                } else {
    161219                    console.error('Error updating order ' + orderId + ': ' + response.data);
    162                     card.draggable('option', 'revert', true); // Revert on failure
     220                    card.draggable('option', 'revert', true);
    163221                }
    164222            },
    165223            error: function (error) {
    166224                console.error('AJAX error:', error);
    167                 card.draggable('option', 'revert', true); // Revert on error
     225                card.draggable('option', 'revert', true);
    168226            }
    169227        });
     
    172230    function filterOrders(searchTerm) {
    173231        kanbanBoard.empty();
    174         const statuses = {
    175             'pending': 'Pending',
    176             'processing': 'Processing',
    177             'on-hold': 'On Hold',
    178             'completed': 'Completed',
    179             'cancelled': 'Cancelled',
    180             'refunded': 'Refunded'
    181         };
    182 
    183         $.each(statuses, function (statusKey, displayName) {
    184             const column = $('<div class="kanban-column col-md-3 mb-4" data-status="' + statusKey + '">');
    185             column.append(`<h6 class="kanban-column-header">${displayName}</h6><div class="column-body"></div>`);
     232        const statuses = kbfwc_ajax_params.default_statuses;
     233        let hasResults = false;
     234
     235        $.each(statuses, function (i, status) {
     236            const statusKey = status.replace('wc-', '').trim();
     237            const cleanKey = statusKey.replace(/-/g, ' ');
     238            const label = cleanKey.replace(/\b\w/g, char => char.toUpperCase());
     239
     240            // Create column with header + body
     241            const column = $(`
     242            <div class="kanban-column col-md-3 mb-0 card" data-status="${statusKey}">
     243                <div class="d-flex justify-content-between align-items-center mb-1">
     244                    <h6 class="kanban-column-header mb-0">${label}</h6>
     245                    <span class="order-count">(0/0)</span>
     246                </div>
     247                <div class="column-body"></div>
     248            </div>
     249        `);
     250
     251            const columnBody = column.find('.column-body');
    186252            kanbanBoard.append(column);
    187             const columnBody = column.find('.column-body');
    188253
    189254            if (allOrders[statusKey]) {
    190                 allOrders[statusKey].filter(order => {
    191                     const firstName = order.billing && order.billing.first_name ? order.billing.first_name : '';
    192                     const lastName = order.billing && order.billing.last_name ? order.billing.last_name : '';
    193                     const productNames = order.line_items ? order.line_items.map(item => item.name).join(' ') : '';
    194                     const paymentTitle = order.payment_method_title ? order.payment_method_title : '';
     255                const filteredOrders = allOrders[statusKey].filter(order => {
     256                    const firstName = order.billing?.first_name || '';
     257                    const lastName = order.billing?.last_name || '';
     258                    const productNames = order.line_items?.map(item => item.name).join(' ') || '';
     259                    const paymentTitle = order.payment_method_title || '';
    195260                    const searchText = `${order.id} ${firstName} ${lastName} ${productNames} ${paymentTitle}`.toLowerCase();
    196261                    return searchText.includes(searchTerm);
    197                 }).forEach(order => {
    198                     columnBody.append(renderOrderCard(order));
    199262                });
    200             }
    201         });
     263
     264                if (filteredOrders.length > 0) {
     265                    hasResults = true;
     266                    filteredOrders.forEach(order => {
     267                        const card = renderOrderCard(order);
     268                        columnBody.append(card);
     269                    });
     270                }
     271            }
     272        });
     273
     274        if (!hasResults) {
     275            kanbanBoard.html(`<div class="alert alert-warning text-center mt-3">No orders found for "<strong>${searchTerm}</strong>".</div>`);
     276        }
     277
     278        // Re-enable features after filtering
    202279        initDragAndDrop();
    203         attachCardClickListeners(); // Re-attach listeners after filtering
     280        attachCardClickListeners();
     281        updateColumnCounts();
    204282    }
    205283
     
    242320
    243321    searchInput.on("keyup", function () {
    244         searchButton.trigger("click"); // Trigger search on keyup for live filtering
     322        const term = $(this).val().toLowerCase();
     323        if (term === '') {
     324            currentPage = 1;
     325            loadOrders(currentPage);
     326        } else {
     327            searchButton.trigger("click");
     328        }
    245329    });
    246330
     
    251335    // Initial load
    252336    loadOrders(currentPage);
     337
     338    function updateColumnCounts() {
     339        const totalOrders = Object.values(allOrders).reduce((sum, arr) => sum + arr.length, 0);
     340
     341        $('.kanban-column').each(function () {
     342            const $col = $(this);
     343            const status = $col.data('status');
     344            const currentCount = $col.find('.order-card').length;
     345            const header = $col.find('.order-count');
     346
     347            // Extract label without old count
     348            let label = header.text().split('(')[0].trim();
     349            const newLabel = `${label} (${currentCount}/${totalOrders})`;
     350            header.text(newLabel);
     351        });
     352    }
     353
    253354});
  • kanban-for-woocommerce/trunk/kanban-for-woocommerce.php

    r3390883 r3392758  
    55 * Plugin URI:      https://neebplugins.com/plugin/kanban-for-woocommerce
    66 * Description:     Visual Kanban board for managing WooCommerce orders by status.
    7  * Version: 1.0.5
     7 * Version: 1.0.6
    88 * Author:          NeeBPlugins
    99 * Author URI:      https://neebplugins.com
     
    2525    // Exit if accessed directly
    2626}
    27 defined( 'KBFWC_VERSION' ) or define( 'KBFWC_VERSION', '1.0.5' );
     27defined( 'KBFWC_VERSION' ) or define( 'KBFWC_VERSION', '1.0.6' );
    2828defined( 'KBFWC_FILE' ) or define( 'KBFWC_FILE', __FILE__ );
    2929defined( 'KBFWC_BASE' ) or define( 'KBFWC_BASE', plugin_basename( KBFWC_FILE ) );
  • kanban-for-woocommerce/trunk/readme.txt

    r3390883 r3392758  
    66Tested up to: 6.8
    77Requires PHP: 7.4
    8 Stable tag: 1.0.5
     8Stable tag: 1.0.6
    99License: GPL-2.0+
    1010License URI: http://www.gnu.org/licenses/gpl-2.0.txt
     
    1515
    1616**Kanban for WooCommerce** transforms your WooCommerce order management by providing a clear, visual overview of your order workflow. Organize your orders into Kanban boards based on their statuses, making it easy to track progress, identify bottlenecks, and streamline your fulfillment process.
     17**Manage WooCommerce orders like tasks on a visual drag-and-drop Kanban board.**
     18Move orders across statuses instantly, filter by customers or products, and stay in full control of your store workflow — all from one screen.
    1719
    18 **Key Features:**
     20> ✅ Stop refreshing WooCommerce Orders. Start managing visually.
    1921
    20 *   **Visual Kanban Board:** See all your WooCommerce orders organized by status in a Kanban board layout.
    21 *   **Drag & Drop Order Management:** Easily update order statuses by dragging and dropping order cards between columns.
    22 *   **Clear Order Overview:**  Quickly understand the status of all your orders at a glance.
    23 *   **Improved Workflow:** Streamline your order fulfillment process and enhance team collaboration.
     22## 🚀 FEATURES (Free Version)
     23
     24### 📦 Visual Order Management
     25- View all your WooCommerce orders as cards on an interactive Kanban board.
     26- Instantly drag & drop orders between **Pending**, **Processing**, **Completed**, etc.
     27- Color-coded columns make it easy to track each stage of your fulfillment flow.
     28
     29### 🔍 Smart Order Search
     30- Search live by **order number**, **customer name**, **product name**, or **payment method**.
     31- Filter results instantly without leaving the board.
     32
     33### ⚙️ Real-Time Order Updates
     34- Moving a card automatically updates the WooCommerce order status — no reload needed.
     35- Built-in AJAX ensures your changes are saved safely and instantly.
     36
     37### 🧾 Order Details Popup
     38- Click any order card to open a detailed modal view with full order info.
     39- Check payment, shipping, and product details without leaving the page.
     40
     41### 💡 Responsive Design
     42- Works beautifully on desktop, tablet, or large admin dashboards.
     43- Minimal UI built with Bootstrap 5 for speed and clarity.
     44
     45### ✨ Focus Mode
     46**Go distraction-free with one click.** 
     47Toggle Focus Mode to hide the WordPress admin sidebar and top bar — giving you a clean, full-width workspace for maximum productivity. 
     48Perfect for large monitors or daily operations.
     49
     50Focus Mode gives you a **clean, distraction-free workspace** by hiding WordPress admin UI elements. 
     51Perfect for fulfillment teams or store managers who spend hours inside WooCommerce.
     52
     53**Features:**
     54- Full-width layout (no sidebar)
     55- Smooth enter/exit animation
     56- Sticky toolbar with search + filters
     57- One-click toggle using the focus icon
     58
     59> Once you try Focus Mode, you’ll never go back to the default WordPress view.
     60
     61> 💡 Designed for teams that want efficiency and clarity.
     62
     63### 🧠 Smart Analytics & Metrics
     64- Real-time **order count badges** for every list (e.g., “Processing 5/32”). 
     65- Track your workload at a glance.
     66
     67### 🔄 Enhanced Drag & Drop
     68- Trello-style animations with smooth transitions. 
     69- Visual feedback and toast confirmations for every move.
     70
     71### 🧱 Team-Ready Performance
     72- Optimized for hundreds of orders per screen. 
     73- Lightweight scripts (no external dependencies). 
     74- Built using WordPress standards and REST API.
    2475
    2576**Ideal for:**
     
    2879*   E-commerce businesses wanting to improve order fulfillment efficiency.
    2980*   Teams needing a collaborative way to manage WooCommerce orders.
     81
     82## 🧩 WHY STORE OWNERS LOVE IT
     83
     84- “I manage 200+ daily orders visually — no clutter, no confusion.” 
     85- “The Focus Mode is a game changer. It feels like Shopify’s dashboard, but better.” 
     86- “Our team processes orders 2× faster since moving to the Kanban view.”
     87
     88### ❤️ Loved by WooCommerce store owners who value clarity, control, and speed.
    3089
    3190= Support =
     
    52111
    53112== Changelog ==
     113= 1.0.6 =
     114* Board Design Improvements
     115* Focus Mode
    54116
    55117= 1.0.5 =
     
    79141== Upgrade Notice ==
    80142
    81 = 1.0.5 =
    82 Kanban Board Settings
     143= 1.0.6 =
     144Board Design Improvements
  • kanban-for-woocommerce/trunk/src/Backend.php

    r3390883 r3392758  
    100100     */
    101101    public function admin_page_content() {
    102         $excel_export = KBFWC_Settings::get_instance()->get( 'excel_export' );
     102        // $excel_export = KBFWC_Settings::get_instance()->get( 'excel_export' );
    103103        ?>
    104104        <div class="wrap">         
    105105            <div id="kbfwc-kanban-app">
    106                 <div class="container">
    107                     <div class="container-fluid mb-3">
    108                         <div class="d-flex justify-content-between align-items-center border-bottom pb-2 text-center">
    109                             <h2 class="mb-0 fw-semibold">
    110                                 <i class="bi bi-kanban me-2"></i> <?php esc_html_e( 'Kanban Board', 'kanban-for-woocommerce' ); ?>
    111                             </h2>
    112                             <div class="d-none">
    113                                 <?php if ( kbfwc_fs()->is_paying_or_trial() && $excel_export ) { ?>
    114                                     <button type="button" id="kbfwc_excel_export"   class="btn btn-outline-secondary btn-sm">
    115                                         <i class="dashicons dashicons-media-document me-1"></i>
    116                                         <?php esc_html_e( 'Export Excel', 'kanban-for-woocommerce' ); ?>
    117                                     </button>
    118                                 <?php } else { ?>
    119                                     <button type="button" id="print-kanban-board"   class="btn btn-outline-secondary btn-sm" disabled title="<?php esc_attr_e( 'Exporting is disabled in settings' ); ?>">
    120                                         <i class="dashicons dashicons-media-document me-1"></i>
    121                                         <?php esc_html_e( 'Export Excel', 'kanban-for-woocommerce' ); ?>
    122                                     </button>
    123                                 <?php } ?>
     106                <div class="container-1">               
     107
     108                    <div class="container-2">
     109                        <div class="row">
     110
     111                            <!-- Left: Title -->
     112                            <div class="col-md-3">
     113                                <h2 class="mb-0 d-flex align-items-center fw-semibold me-2 text-nowrap">
     114                                    <i class="dashicons dashicons-clipboard me-1 mt-1"></i>
     115                                    <?php esc_html_e( 'Kanban Board', 'kanban-for-woocommerce' ); ?>
     116                                </h2>
    124117                            </div>
    125                         </div>
    126                     </div>
    127                     <div class="search-container justify-content-center">
    128                         <div class="input-group-1">
    129                             <input type="text" id="order-search" class="regular-text search-input" placeholder="<?php esc_attr_e( 'Search order #, customer, product, payment', 'kanban-for-woocommerce' ); ?>">
    130                             <button class="button d-none" type="button button-primary" id="search-button">
    131                                 <i class="dashicons dashicons-clipboard"></i>
    132                                 <?php esc_html_e( 'Search', 'kanban-for-woocommerce' ); ?>
    133                             </button>
    134                         </div>
    135                     </div>
    136                     <div class="kanban-board"></div>
     118
     119                            <!-- Center: Full-width Search -->
     120                            <div class="col-md-7">
     121                                <input type="text" id="order-search"
     122                                class="form-control w-100"
     123                                placeholder="<?php esc_attr_e( 'Search order #, customer, product, payment', 'kanban-for-woocommerce' ); ?>">
     124                                <button class="d-none search-button"></button>
     125                            </div> 
     126
     127                            <!-- Right: Action Icons -->
     128                            <div class="col-md-2 text-end">
     129                                <button type="button" class="btn btn-light btn-sm" data-bs-toggle="focusmode" title="Enter Focus Mode">
     130                                    <i class="dashicons dashicons-editor-expand"></i>
     131                                </button>
     132
     133                                <button id="kanban-filter" class="btn p-1 text-secondary me-1 d-none"
     134                                    title="<?php esc_attr_e( 'Filter Orders', 'kanban-for-woocommerce' ); ?>">
     135                                    <i class="dashicons dashicons-filter fs-5"></i>
     136                                </button>
     137
     138                                <button id="kanban-settings" class="btn p-1 text-secondary d-none"
     139                                    title="<?php esc_attr_e( 'Settings', 'kanban-for-woocommerce' ); ?>">
     140                                    <i class="dashicons dashicons-admin-generic fs-5"></i>
     141                                </button>
     142                            </div>
     143
     144                        </div>                     
     145                    </div>                 
     146
     147                    <div class="kanban-board mt-1"></div>
    137148                    <div class="load-more-container text-center mt-3">
    138                         <button id="load-more-orders" class="button button-secondary"><?php esc_html_e( 'Load More Orders', 'kanban-for-woocommerce' ); ?></button>
     149                        <button id="load-more-orders" class="button button-secondary"><?php esc_html_e( 'Load More', 'kanban-for-woocommerce' ); ?></button>
    139150                    </div>
    140151                </div>
     
    196207                </div>
    197208            </div>
    198             <div class="kanban-board">
    199                 </div>
     209            <div class="kanban-board"></div>
    200210        </div>
    201211
     
    459469
    460470            wp_enqueue_style( 'bootstrap', KBFWC_URL . 'assets/css/bootstrap.min.css', array(), '5.3.3' );
     471            wp_enqueue_style( 'kbfwc-toastr', KBFWC_URL . 'assets/css/toastr.min.css', array(), '2.1.4' );
    461472            wp_enqueue_style( 'kbfwc-board', KBFWC_URL . 'assets/css/board.css', array(), KBFWC_VERSION );
    462473            wp_enqueue_script( 'jquery-ui-draggable' );
    463474            wp_enqueue_script( 'jquery-ui-droppable' );
    464475            wp_enqueue_script( 'bootstrap', KBFWC_URL . 'assets/js/bootstrap.min.js', array( 'jquery' ), '5.3.3', true );
    465             wp_enqueue_script( 'kbfwc-html2pdf', KBFWC_URL . 'assets/js/html2pdf.bundle.min.js', array( 'jquery' ), KBFWC_VERSION, true );
     476            wp_enqueue_script( 'kbfwc-focusmode', KBFWC_URL . 'assets/js/focusmode.js', array( 'jquery' ), '1.0.0', true );
     477            wp_enqueue_script( 'kbfwc-toastr', KBFWC_URL . 'assets/js/toastr.min.js', array( 'jquery' ), '2.1.4', true );
    466478            wp_enqueue_script( 'kbfwc-board', KBFWC_URL . 'assets/js/board.js', array( 'jquery', 'jquery-ui-draggable', 'jquery-ui-droppable', 'bootstrap' ), KBFWC_VERSION, true );
    467479
     
    635647        // Allowed Roles
    636648        // add_settings_field(
    637         //  'allowed_roles',
    638         //  __( 'Allowed Roles', 'kanban-for-woocommerce' ),
    639         //  function ( $options ) {
    640         //      $options = KBFWC_Settings::get_instance()->get();
    641         //      $roles   = wp_roles()->get_names();
    642         //      if ( kbfwc_fs()->is_paying_or_trial() ) {
    643         //          echo '<select class="wc-enhanced-select regular-text" multiple name="kbfwc_settings[allowed_roles][]">';
    644         //      } else {
    645         //          echo '<select class="wc-enhanced-select regular-text" multiple name="kanban-settings[allowed_roles][]">';
    646         //      }
    647         //      foreach ( $roles as $key => $label ) {
    648         //          printf(
    649         //              '<option value="%1$s" %2$s>%3$s</option>',
    650         //              esc_attr( $key ),
    651         //              selected( in_array( $key, $options['allowed_roles'], true ), true, false ),
    652         //              wp_kses_post( $label )
    653         //          );
    654         //      }
    655         //      echo '</select>';
    656         //      echo wp_kses_post( '<p class="description">' . __( 'Select which user roles can access the Kanban Board.', 'kanban-for-woocommerce' ) . '</p>' );
    657         //  },
    658         //  'kbfwc_settings',
    659         //  'kbfwc_general_section'
     649        // 'allowed_roles',
     650        // __( 'Allowed Roles', 'kanban-for-woocommerce' ),
     651        // function ( $options ) {
     652        // $options = KBFWC_Settings::get_instance()->get();
     653        // $roles   = wp_roles()->get_names();
     654        // if ( kbfwc_fs()->is_paying_or_trial() ) {
     655        // echo '<select class="wc-enhanced-select regular-text" multiple name="kbfwc_settings[allowed_roles][]">';
     656        // } else {
     657        // echo '<select class="wc-enhanced-select regular-text" multiple name="kanban-settings[allowed_roles][]">';
     658        // }
     659        // foreach ( $roles as $key => $label ) {
     660        // printf(
     661        // '<option value="%1$s" %2$s>%3$s</option>',
     662        // esc_attr( $key ),
     663        // selected( in_array( $key, $options['allowed_roles'], true ), true, false ),
     664        // wp_kses_post( $label )
     665        // );
     666        // }
     667        // echo '</select>';
     668        // echo wp_kses_post( '<p class="description">' . __( 'Select which user roles can access the Kanban Board.', 'kanban-for-woocommerce' ) . '</p>' );
     669        // },
     670        // 'kbfwc_settings',
     671        // 'kbfwc_general_section'
    660672        // );
    661673
     
    715727        // Enable Excel Export
    716728        // add_settings_field(
    717         //  'excel_export',
    718         //  __( 'Enable Excel Export Button', 'kanban-for-woocommerce' ),
    719         //  function () {
    720         //      $options = KBFWC_Settings::get_instance()->get();
    721         //      if ( kbfwc_fs()->is_paying_or_trial() ) {
    722         //          printf(
    723         //              '<label><input type="checkbox" name="kbfwc_settings[excel_export]" value="1" %s> %s</label>',
    724         //              checked( ! empty( $options['excel_export'] ), true, false ),
    725         //              wp_kses_post( __( 'Enable the Excel Export on Kanban board.', 'kanban-for-woocommerce' ) )
    726         //          );
    727         //      } else {
    728         //          printf(
    729         //              '<label><input type="checkbox" name="kanban-settings[excel_export]" value="1" %s> %s</label>',
    730         //              checked( ! empty( $options['excel_export'] ), true, false ),
    731         //              wp_kses_post( __( 'Enable the Excel Export on Kanban board.', 'kanban-for-woocommerce' ) )
    732         //          );
    733         //      }
    734         //  },
    735         //  'kbfwc_settings',
    736         //  'kbfwc_general_section'
     729        // 'excel_export',
     730        // __( 'Enable Excel Export Button', 'kanban-for-woocommerce' ),
     731        // function () {
     732        // $options = KBFWC_Settings::get_instance()->get();
     733        // if ( kbfwc_fs()->is_paying_or_trial() ) {
     734        // printf(
     735        // '<label><input type="checkbox" name="kbfwc_settings[excel_export]" value="1" %s> %s</label>',
     736        // checked( ! empty( $options['excel_export'] ), true, false ),
     737        // wp_kses_post( __( 'Enable the Excel Export on Kanban board.', 'kanban-for-woocommerce' ) )
    737738        // );
     739        // } else {
     740        // printf(
     741        // '<label><input type="checkbox" name="kanban-settings[excel_export]" value="1" %s> %s</label>',
     742        // checked( ! empty( $options['excel_export'] ), true, false ),
     743        // wp_kses_post( __( 'Enable the Excel Export on Kanban board.', 'kanban-for-woocommerce' ) )
     744        // );
     745        // }
     746        // },
     747        // 'kbfwc_settings',
     748        // 'kbfwc_general_section'
     749        // );
    738750    }
    739751}
Note: See TracChangeset for help on using the changeset viewer.