PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
← All changes | assets/js/client-manager.js +51 -23 2.1.142.4.0 View file →
@@ -348,52 +348,75 @@
348 348 }
349 349 });
350 350 }
351 351
352 - // Add the new client to the table
353 - var clientName = clientData.business_client_name || (clientData.first_name + ' ' + clientData.last_name);
354 - if (clientName.trim() === '') {
355 - clientName = clientData.username;
352 + // Compose the two name columns separately — the table has
353 + // Business Name and Client Name as DISTINCT columns, so we
354 + // can't collapse them into one or the cells shift left.
355 + var businessName = (clientData.business_client_name || '').trim() || '-';
356 + var contactName = ((clientData.first_name || '') + ' ' + (clientData.last_name || '')).trim();
357 + if (contactName === '') {
358 + contactName = clientData.username || '';
356 359 }
357 -
358 - // Create new table row
360 +
361 + // Use the role label the server returned with the create
362 + // response. PHP renders the same row on the next refresh
363 + // by reading $user->roles[0] (currently 'customer'); using
364 + // the server-supplied label here keeps both states in sync
365 + // so the badge doesn't flicker from "Client" -> "Customer"
366 + // on first page reload.
367 + var roleLabel = (response.data && response.data.role_label)
368 + ? response.data.role_label
369 + : 'Customer';
370 +
371 + // Create new table row — MUST match the 8 columns in the
372 + // <thead> exactly (id, business name, contact name, email,
373 + // phone, username, role, actions). Skipping any of these
374 + // makes every later cell shift one column to the left.
375 + var esc = function(v){ return $('<div>').text(v == null ? '' : String(v)).html(); };
359 376 var newRow = `
360 377 <tr>
361 378 <td class="px-6 py-4 whitespace-nowrap">
362 - <div class="text-sm text-gray-500 font-mono font-semibold">${response.data.client_id}</div>
379 + <div class="text-sm text-gray-500 font-mono font-semibold">${esc(response.data.client_id)}</div>
363 380 </td>
364 381 <td class="px-6 py-4 whitespace-nowrap">
365 - <div class="text-sm font-medium text-gray-900">${clientName}</div>
382 + <div class="text-sm font-medium text-gray-900">${esc(businessName)}</div>
366 383 </td>
367 384 <td class="px-6 py-4 whitespace-nowrap">
368 - <div class="text-sm text-gray-500">${clientData.email}</div>
385 + <div class="text-sm font-medium text-gray-900">${esc(contactName)}</div>
369 386 </td>
370 387 <td class="px-6 py-4 whitespace-nowrap">
371 - <div class="text-sm text-gray-500">${clientData.username}</div>
388 + <div class="text-sm text-gray-500">${esc(clientData.email || '')}</div>
372 389 </td>
373 390 <td class="px-6 py-4 whitespace-nowrap">
391 + <div class="text-sm text-gray-500">${esc(clientData.phone || '')}</div>
392 + </td>
393 + <td class="px-6 py-4 whitespace-nowrap">
394 + <div class="text-sm text-gray-500">${esc(clientData.username || '')}</div>
395 + </td>
396 + <td class="px-6 py-4 whitespace-nowrap">
374 397 <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
375 398 <i class="fas fa-user mr-1"></i>
376 - Client
399 + ${esc(roleLabel)}
377 400 </span>
378 401 </td>
379 402 <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
380 403 <div class="flex items-center space-x-2">
381 - <a href="?page=easy-invoice-client-view&client_id=${response.data.client_id}"
404 + <a href="?page=easy-invoice-client-view&client_id=${esc(response.data.client_id)}"
382 405 class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200"
383 406 title="View Client">
384 407 <i class="fas fa-eye mr-1"></i>
385 408 View
386 409 </a>
387 - <a href="?page=easy-invoice-client-edit&client_id=${response.data.client_id}"
410 + <a href="?page=easy-invoice-client-edit&client_id=${esc(response.data.client_id)}"
388 411 class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded text-blue-700 bg-blue-100 hover:bg-blue-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200"
389 412 title="Edit Client">
390 413 <i class="fas fa-edit mr-1"></i>
391 414 Edit
392 415 </a>
393 - <button type="button"
416 + <button type="button"
394 417 class="delete-client inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200"
395 - data-id="${response.data.client_id}"
418 + data-id="${esc(response.data.client_id)}"
396 419 title="Delete Client">
397 420 <i class="fas fa-trash mr-1"></i>
398 421 Delete
399 422 </button>
@@ -400,12 +423,14 @@
400 423 </div>
401 424 </td>
402 425 </tr>
403 426 `;
427 +
428 + // Remove "No clients found" placeholder row if it exists.
429 + // The colspan must match what's emitted by the PHP empty
430 + // state and the JS delete handler (currently 8).
431 + $(".client-list-table tbody tr td[colspan='8']").closest("tr").remove();
404 432
405 - // Remove "No clients found" row if it exists
406 - $(".client-list-table tbody tr td[colspan='6']").closest("tr").remove();
407 -
408 433 // Add new row at the top of the table (since we order by ID DESC)
409 434 $(".client-list-table tbody").prepend(newRow);
410 435
411 436 // Update total clients count
@@ -411,15 +436,18 @@
411 436 // Update total clients count
412 437 var currentCount = parseInt($("h3:contains('Total Clients')").next().text()) || 0;
413 438 $("h3:contains('Total Clients')").next().text(currentCount + 1);
414 439
415 - // Show server's success message (don't add additional client-side message)
416 - if (typeof showToast === 'function' && response.data.message) {
417 - showToast(response.data.message, "success");
440 + // Show server's success message. The request sends
441 + // suppress_global_toast=true (so the global ajaxSuccess
442 + // handler in easy-invoice-toast.js doesn't auto-render a
443 + // duplicate toast); we surface the message manually here.
444 + if (typeof EasyInvoiceToast !== 'undefined' && response.data.message) {
445 + EasyInvoiceToast.success(response.data.message);
418 446 }
419 447 } else {
420 - if (typeof showToast === 'function') {
421 - showToast(response.data.message || "Error adding client", "error");
448 + if (typeof EasyInvoiceToast !== 'undefined') {
449 + EasyInvoiceToast.error((response.data && response.data.message) || "Error adding client");
422 450 }
423 451 }
424 452 },
425 453 error: function(xhr, status, error) {