PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.3
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.3
2.4.3 2.4.4 2.4.2 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 All 60 releases
← All changes | assets/js/client-manager.js +59 -31 2.1.0 → 2.4.3 View file →
@@ -69,9 +69,9 @@
69 69
70 70 // Update display fields
71 71 $("#display-client-name").text(clientData.business_client_name || (clientData.first_name + ' ' + clientData.last_name) || 'N/A');
72 72 $("#display-client-email").text(clientData.email || 'N/A');
73 - $("#display-client-phone").text(clientData.extra_info || 'N/A');
73 + $("#display-client-phone").text(clientData.phone || 'N/A');
74 74 $("#display-client-company").text(clientData.business_client_name || 'N/A');
75 75 $("#display-client-address").text(clientData.address || 'N/A');
76 76 $("#display-client-website").text(clientData.website || 'N/A');
77 77
@@ -92,9 +92,9 @@
92 92 // Store client data for later use
93 93 clientsData[clientData.id] = {
94 94 name: clientData.business_client_name || (clientData.first_name + ' ' + clientData.last_name),
95 95 email: clientData.email,
96 - phone: clientData.extra_info,
96 + phone: clientData.phone,
97 97 address: clientData.address,
98 98 website: clientData.website,
99 99 business_name: clientData.business_client_name,
100 100 first_name: clientData.first_name,
@@ -121,9 +121,9 @@
121 121
122 122 // Update display fields
123 123 $("#display-client-name").text(client.business_client_name || (client.first_name + ' ' + client.last_name) || 'N/A');
124 124 $("#display-client-email").text(client.email || 'N/A');
125 - $("#display-client-phone").text(client.extra_info || 'N/A');
125 + $("#display-client-phone").text(client.phone || 'N/A');
126 126 $("#display-client-company").text(client.business_client_name || 'N/A');
127 127 $("#display-client-address").text(client.address || 'N/A');
128 128 $("#display-client-website").text(client.website || 'N/A');
129 129
@@ -144,9 +144,9 @@
144 144 // Store client data for later use
145 145 clientsData[clientId] = {
146 146 name: client.business_client_name || (client.first_name + ' ' + client.last_name),
147 147 email: client.email,
148 - phone: client.extra_info,
148 + phone: client.phone,
149 149 address: client.address,
150 150 website: client.website,
151 151 business_name: client.business_client_name,
152 152 first_name: client.first_name,
@@ -291,9 +291,9 @@
291 291 email: email,
292 292 username: username,
293 293 password: password,
294 294 address: $("#add-client-address").val(),
295 - extra_info: $("#add-client-extra-info").val(),
295 + phone: $("#add-client-phone").val(),
296 296 first_name: $("#add-client-first-name").val(),
297 297 last_name: $("#add-client-last-name").val(),
298 298 website: $("#add-client-website").val(),
299 299 action: "easy_invoice_add_client",
@@ -315,9 +315,9 @@
315 315 timeout: 10000, // 10 second timeout
316 316 success: function(response) {
317 317 // Always reset the button and clear the form
318 318 submitBtn.prop("disabled", false).html(originalText);
319 - $("#add-client-business-name, #add-client-email, #add-client-username, #add-client-password, #add-client-first-name, #add-client-last-name, #add-client-address, #add-client-extra-info, #add-client-website").val("");
319 + $("#add-client-business-name, #add-client-email, #add-client-username, #add-client-password, #add-client-first-name, #add-client-last-name, #add-client-address, #add-client-phone, #add-client-website").val("");
320 320
321 321 if (response.success) {
322 322 // Close modal
323 323 $("#add_client_modal").addClass("hidden");
@@ -328,9 +328,9 @@
328 328 var newClientData = {
329 329 name: clientData.business_client_name || (clientData.first_name + ' ' + clientData.last_name),
330 330 email: clientData.email,
331 331 company: clientData.business_client_name,
332 - phone: clientData.extra_info,
332 + phone: clientData.phone,
333 333 website: clientData.website,
334 334 address: clientData.address
335 335 };
336 336
@@ -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) {
@@ -540,9 +568,9 @@
540 568 email: $("#edit-client-email").val(),
541 569 username: $("#edit-client-username").val(),
542 570 password: $("#edit-client-password").val(),
543 571 address: $("#edit-client-address").val(),
544 - extra_info: $("#edit-client-extra-info").val(),
572 + phone: $("#edit-client-phone").val(),
545 573 first_name: $("#edit-client-first-name").val(),
546 574 last_name: $("#edit-client-last-name").val(),
547 575 website: $("#edit-client-website").val(),
548 576 action: "easy_invoice_update_client",