| @@ -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", |