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 | templates/clients-page.php +89 -142 2.2.0 → 2.4.3 View file →
@@ -1,5 +1,8 @@
1 1 <?php
2 +if ( ! defined( 'ABSPATH' ) ) {
3 + exit;
4 +}
2 5 // Extract data passed from controller
3 6 $clients = $clients ?? [];
4 7 $stats = $stats ?? [];
5 8 $current_page = $current_page ?? 1;
@@ -9,23 +12,48 @@
9 12
10 13 // $clients variable contains Client objects
11 14
12 15 ?>
16 +<style>
17 +<?php if (!easy_invoice_user_can('ei_manage_clients')) : ?>.client-list-table a[href*="client-edit"], .client-list-table .delete-client { display: none; }<?php endif; ?>
18 +/* Eight columns do not fit a laptop; the username and role are the least useful. */
19 +.client-list-table th, .client-list-table td { padding-left: 0.875rem; padding-right: 0.875rem; }
20 +@media (max-width: 1700px) {
21 + .client-list-table th:nth-child(6), .client-list-table td:nth-child(6),
22 + .client-list-table th:nth-child(7), .client-list-table td:nth-child(7) { display: none; }
23 +}
24 +</style>
13 25 <div class="p-8">
14 - <!-- Header with Actions -->
15 26 <div class="ei-app-page-header bg-white border-b border-gray-200 px-6" style="margin-left: -2rem; margin-top: -2rem; padding-left:2rem; padding-right:2rem; margin-right: -2rem;">
16 27 <div class="flex items-center justify-between">
17 28 <div>
18 - <h1 class="text-2xl font-bold text-gray-900"><?php _e('Clients', 'easy-invoice'); ?></h1>
19 - <p class="mt-1 text-sm text-gray-500"><?php _e('Manage all client records', 'easy-invoice'); ?></p>
29 + <h1 class="text-2xl font-bold text-gray-900"><?php esc_html_e('Clients', 'easy-invoice'); ?></h1>
30 + <p class="mt-1 text-sm text-gray-500"><?php esc_html_e('Manage all client records', 'easy-invoice'); ?></p>
20 31 </div>
21 32 <div class="flex items-center space-x-3">
22 - <?php if (!easy_invoice_has_pro()): ?>
33 + <?php
34 + // "Export All" is part of the bulk_operations addon
35 + // (Personal tier). See templates/invoices/listing.php
36 + // for the canonical three-state gate pattern.
37 + $ei_bulk_ops_loaded = \EasyInvoice\Addons\AddonManager::shouldLoad('bulk_operations');
38 + ?>
39 + <?php if ($ei_bulk_ops_loaded): ?>
40 + <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline;">
41 + <input type="hidden" name="action" value="easy_invoice_export_all_clients" />
42 + <input type="hidden" name="_wpnonce" value="<?php echo esc_attr(wp_create_nonce('easy_invoice_export_all_clients')); ?>" />
43 + <button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
44 + <svg class="w-4 h-4 mr-2 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
45 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
46 + </svg>
47 + <?php esc_html_e('Export All', 'easy-invoice'); ?>
48 + </button>
49 + </form>
50 + <?php elseif (!easy_invoice_has_pro()): ?>
23 51 <button type="button" id="export-all-clients-btn" class="premium-export-btn inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200 relative">
24 52 <svg class="w-4 h-4 mr-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
25 53 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
26 54 </svg>
27 - <?php _e('Export All', 'easy-invoice'); ?>
55 + <?php esc_html_e('Export All', 'easy-invoice'); ?>
28 56 <svg class="ml-2 w-5 h-5 text-purple-600 crown-icon" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
29 57 <path d="M12 8L15 13.2L18 10.5L17.3 14H6.7L6 10.5L9 13.2L12 8M12 4L8.5 10L3 5L5 16H19L21 5L15.5 10L12 4Z"/>
30 58 </svg>
31 59 </button>
@@ -32,41 +60,29 @@
32 60 <script>
33 61 jQuery(document).ready(function($) {
34 62 $('#export-all-clients-btn').on('click', function(e) {
35 63 e.preventDefault();
36 - // Show premium popup using the proper confirmation modal
37 64 if (typeof EasyInvoiceConfirmation !== 'undefined') {
38 65 EasyInvoiceConfirmation.showFeatureUpgrade('Export All Clients', 'Export all your clients to CSV, Excel, or PDF format for backup, analysis, or sharing with your team.');
39 66 } else {
40 - // Fallback to toast if confirmation modal is not available
41 67 EasyInvoiceToast.error('<?php echo esc_js(__('Exporting all clients is a premium feature. Upgrade to Easy Invoice Pro to unlock this feature.', 'easy-invoice')); ?>');
42 68 }
43 69 });
44 70 });
45 71 </script>
46 - <?php else: ?>
47 - <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline;">
48 - <input type="hidden" name="action" value="easy_invoice_export_all_clients" />
49 - <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('easy_invoice_export_all_clients'); ?>" />
50 - <button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
51 - <svg class="w-4 h-4 mr-2 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
52 - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
53 - </svg>
54 - <?php _e('Export All', 'easy-invoice'); ?>
55 - </button>
56 - </form>
57 72 <?php endif; ?>
58 - <button id="add-new-client-btn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
73 + <?php if (easy_invoice_user_can('ei_manage_clients')) : ?>
74 +<button id="add-new-client-btn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
59 75 <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
60 76 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
61 77 </svg>
62 - <?php _e('Add New Client', 'easy-invoice'); ?>
78 + <?php esc_html_e('Add New Client', 'easy-invoice'); ?>
63 79 </button>
80 +<?php endif; ?>
64 81 </div>
65 82 </div>
66 83 </div>
67 84
68 - <!-- Stats Cards -->
69 85 <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8 mt-8">
70 86 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
71 87 <div class="flex items-center">
72 88 <div class="rounded-full bg-blue-100 p-3 mr-4">
@@ -77,10 +93,10 @@
77 93 <path d="M16.32 11.6722C18.36 11.6722 20 10.0322 20 7.99219C20 5.95219 18.36 4.31219 16.32 4.31219" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
78 94 </svg>
79 95 </div>
80 96 <div>
81 - <h3 class="text-sm font-medium text-gray-500">Total Clients</h3>
82 - <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo isset($stats['total_clients']) ? $stats['total_clients'] : count($clients); ?></p>
97 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Total Clients', 'easy-invoice'); ?></h3>
98 + <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo (int) (isset($stats['total_clients']) ? $stats['total_clients'] : count($clients)); ?></p>
83 99 </div>
84 100 </div>
85 101 </div>
86 102 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
@@ -90,10 +106,10 @@
90 106 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
91 107 </svg>
92 108 </div>
93 109 <div>
94 - <h3 class="text-sm font-medium text-gray-500">Total Invoices</h3>
95 - <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo isset($stats['total_invoices']) ? $stats['total_invoices'] : 0; ?></p>
110 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Total Invoices', 'easy-invoice'); ?></h3>
111 + <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo esc_html(isset($stats['total_invoices']) ? $stats['total_invoices'] : 0); ?></p>
96 112 </div>
97 113 </div>
98 114 </div>
99 115 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
@@ -103,9 +119,9 @@
103 119 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
104 120 </svg>
105 121 </div>
106 122 <div>
107 - <h3 class="text-sm font-medium text-gray-500">Total Revenue</h3>
123 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Total Revenue', 'easy-invoice'); ?></h3>
108 124 <?php if (is_array($stats['total_revenue']) && !empty($stats['total_revenue'])): ?>
109 125 <div class="mt-1">
110 126 <?php
111 127 $currency_count = count($stats['total_revenue']);
@@ -114,10 +130,10 @@
114 130 // Get currency code from the data array or use a fallback
115 131 $currency_code = isset($data['currency_code']) ? $data['currency_code'] : 'USD';
116 132 ?>
117 133 <div class="<?php echo $currency_count > 1 ? 'text-sm' : 'text-lg'; ?> font-bold text-gray-900 <?php echo !$is_last ? 'mb-1' : ''; ?>">
118 - <?php echo $data['symbol'] . number_format($data['amount'], 2); ?>
119 - <span class="text-xs font-normal text-gray-500"><?php echo strtoupper($currency_code ?? 'USD'); ?></span>
134 + <?php echo esc_html($data['symbol'] . number_format($data['amount'], 2)); ?>
135 + <span class="text-xs font-normal text-gray-500"><?php echo esc_html(strtoupper($currency_code ?? 'USD')); ?></span>
120 136 </div>
121 137 <?php endforeach; ?>
122 138 </div>
123 139 <?php else: ?>
@@ -133,16 +149,15 @@
133 149 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
134 150 </svg>
135 151 </div>
136 152 <div>
137 - <h3 class="text-sm font-medium text-gray-500">Pending Invoices</h3>
138 - <p class="mt-1 text-2xl font-bold text-yellow-600"><?php echo isset($stats['pending_invoices']) ? $stats['pending_invoices'] : 0; ?></p>
153 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Pending Invoices', 'easy-invoice'); ?></h3>
154 + <p class="mt-1 text-2xl font-bold text-yellow-600"><?php echo esc_html(isset($stats['pending_invoices']) ? $stats['pending_invoices'] : 0); ?></p>
139 155 </div>
140 156 </div>
141 157 </div>
142 158 </div>
143 159
144 - <!-- Top Pagination -->
145 160 <?php if ($total_clients > 0): ?>
146 161 <div class="mb-4">
147 162 <div class="px-4 py-3 flex items-center justify-between">
148 163 <div class="flex items-center space-x-2">
@@ -197,9 +212,9 @@
197 212 if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) {
198 213 $link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link);
199 214 }
200 215
201 - echo $link;
216 + echo wp_kses_post($link);
202 217 }
203 218 echo '</div>';
204 219 }
205 220 ?>
@@ -208,10 +223,17 @@
208 223 </div>
209 224 </div>
210 225 <?php endif; ?>
211 226
212 - <!-- Clients Table -->
213 227 <div class="bg-white rounded-lg shadow">
228 + <div class="px-4 py-3 border-b border-gray-200 flex items-center justify-between gap-3 flex-wrap">
229 + <label for="client-search" class="sr-only"><?php esc_html_e('Search clients', 'easy-invoice'); ?></label>
230 + <div class="relative w-full sm:w-80">
231 + <svg class="absolute left-3 top-2.5 h-4 w-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-4.35-4.35M17 11A6 6 0 115 11a6 6 0 0112 0z"/></svg>
232 + <input type="search" id="client-search" placeholder="<?php esc_attr_e('Search by name, company, email or phone…', 'easy-invoice'); ?>" class="block w-full pl-9 pr-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-indigo-500 focus:border-indigo-500">
233 + </div>
234 + <span id="client-search-count" class="text-sm text-gray-500"></span>
235 + </div>
214 236 <div class="overflow-x-auto">
215 237 <table class="min-w-full divide-y divide-gray-200 client-list-table">
216 238 <thead class="bg-gray-50">
217 239 <tr>
@@ -216,9 +238,9 @@
216 238 <thead class="bg-gray-50">
217 239 <tr>
218 240 <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" data-sort="id">
219 241 <div class="flex items-center space-x-1">
220 - <span>Client ID</span>
242 + <span><?php esc_html_e('Client ID', 'easy-invoice'); ?></span>
221 243 <div class="sort-indicator">
222 244 <i class="fas fa-sort text-gray-400"></i>
223 245 </div>
224 246 </div>
@@ -224,9 +246,9 @@
224 246 </div>
225 247 </th>
226 248 <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" data-sort="business_name">
227 249 <div class="flex items-center space-x-1">
228 - <span>Business Name</span>
250 + <span><?php esc_html_e('Business Name', 'easy-invoice'); ?></span>
229 251 <div class="sort-indicator">
230 252 <i class="fas fa-sort text-gray-400"></i>
231 253 </div>
232 254 </div>
@@ -232,9 +254,9 @@
232 254 </div>
233 255 </th>
234 256 <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" data-sort="name">
235 257 <div class="flex items-center space-x-1">
236 - <span>Client Name</span>
258 + <span><?php esc_html_e('Client Name', 'easy-invoice'); ?></span>
237 259 <div class="sort-indicator">
238 260 <i class="fas fa-sort text-gray-400"></i>
239 261 </div>
240 262 </div>
@@ -240,9 +262,9 @@
240 262 </div>
241 263 </th>
242 264 <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" data-sort="email">
243 265 <div class="flex items-center space-x-1">
244 - <span>Email</span>
266 + <span><?php esc_html_e('Email', 'easy-invoice'); ?></span>
245 267 <div class="sort-indicator">
246 268 <i class="fas fa-sort text-gray-400"></i>
247 269 </div>
248 270 </div>
@@ -248,9 +270,9 @@
248 270 </div>
249 271 </th>
250 272 <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" data-sort="phone">
251 273 <div class="flex items-center space-x-1">
252 - <span>Phone</span>
274 + <span><?php esc_html_e('Phone', 'easy-invoice'); ?></span>
253 275 <div class="sort-indicator">
254 276 <i class="fas fa-sort text-gray-400"></i>
255 277 </div>
256 278 </div>
@@ -256,9 +278,9 @@
256 278 </div>
257 279 </th>
258 280 <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" data-sort="username">
259 281 <div class="flex items-center space-x-1">
260 - <span>Username</span>
282 + <span><?php esc_html_e('Username', 'easy-invoice'); ?></span>
261 283 <div class="sort-indicator">
262 284 <i class="fas fa-sort text-gray-400"></i>
263 285 </div>
264 286 </div>
@@ -264,9 +286,9 @@
264 286 </div>
265 287 </th>
266 288 <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" data-sort="role">
267 289 <div class="flex items-center space-x-1">
268 - <span>Role</span>
290 + <span><?php esc_html_e('Role', 'easy-invoice'); ?></span>
269 291 <div class="sort-indicator">
270 292 <i class="fas fa-sort text-gray-400"></i>
271 293 </div>
272 294 </div>
@@ -278,9 +300,9 @@
278 300 </thead>
279 301 <tbody class="bg-white divide-y divide-gray-200">
280 302 <?php if (empty($clients)): ?>
281 303 <tr>
282 - <td colspan="7" class="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-500">
304 + <td colspan="8" class="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-500">
283 305 No clients found
284 306 </td>
285 307 </tr>
286 308 <?php endif; ?>
@@ -350,10 +372,10 @@
350 372 <td class="px-6 py-4 whitespace-nowrap">
351 373 <div class="text-sm text-gray-500"><?php echo esc_html($client->getUsername()); ?></div>
352 374 </td>
353 375 <td class="px-6 py-4 whitespace-nowrap">
354 - <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <?php echo $role_class; ?>">
355 - <i class="<?php echo $role_icon; ?> mr-1"></i>
376 + <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <?php echo esc_attr($role_class); ?>">
377 + <i class="<?php echo esc_attr($role_icon); ?> mr-1"></i>
356 378 <?php echo esc_html(ucfirst($client_role)); ?>
357 379 </span>
358 380 </td>
359 381 <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
@@ -363,14 +385,20 @@
363 385 title="View Client">
364 386 <i class="fas fa-eye mr-1"></i>
365 387 View
366 388 </a>
367 - <a href="?page=easy-invoice-client-edit&client_id=<?php echo esc_attr($clientId); ?>"
389 + <a href="?page=easy-invoice-client-edit&client_id=<?php echo esc_attr($clientId); ?>"
368 390 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"
369 391 title="Edit Client">
370 392 <i class="fas fa-edit mr-1"></i>
371 393 Edit
372 394 </a>
395 + <a href="<?php echo esc_url(\EasyInvoice\Controllers\StatementController::pageUrl((int) $clientId)); ?>"
396 + class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded text-gray-700 bg-gray-100 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transition-colors duration-200"
397 + title="<?php esc_attr_e('Statement of account', 'easy-invoice'); ?>">
398 + <i class="fas fa-file-invoice-dollar mr-1"></i>
399 + <?php esc_html_e('Statement', 'easy-invoice'); ?>
400 + </a>
373 401 <?php if ($client_role === 'administrator'): ?>
374 402 <button type="button"
375 403 class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded text-gray-400 bg-gray-100 cursor-not-allowed opacity-50"
376 404 title="Cannot delete administrator"
@@ -395,9 +423,8 @@
395 423 </table>
396 424 </div>
397 425 </div>
398 426
399 - <!-- Bottom Pagination -->
400 427 <?php if ($total_clients > 0): ?>
401 428 <div class="mt-4">
402 429 <div class="px-4 py-3 flex items-center justify-between">
403 430 <div class="flex items-center space-x-2">
@@ -452,9 +479,9 @@
452 479 if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) {
453 480 $link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link);
454 481 }
455 482
456 - echo $link;
483 + echo wp_kses_post($link);
457 484 }
458 485 echo '</div>';
459 486 }
460 487 ?>
@@ -463,15 +490,14 @@
463 490 </div>
464 491 </div>
465 492 <?php endif; ?>
466 493
467 - <!-- Add New Client Modal -->
468 494 <div id="add_client_modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50">
469 495 <div class="bg-white rounded-lg shadow-xl max-w-3xl w-full p-6 relative">
470 496 <button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500">
471 497 <i class="fas fa-times"></i>
472 498 </button>
473 - <h2 class="text-xl font-medium text-gray-900 mb-4">Add New Client</h2>
499 + <h2 class="text-xl font-medium text-gray-900 mb-4"><?php esc_html_e('Add New Client', 'easy-invoice'); ?></h2>
474 500
475 501 <?php
476 502 // Unset the $client variable from the foreach loop to ensure clean add form
477 503 unset($client);
@@ -578,101 +604,22 @@
578 604 // Initialize sorting
579 605 initSorting();
580 606
581 607 // Delete client functionality
582 - $(".delete-client").on("click", function(e) {
583 - e.preventDefault();
584 -
585 - const clientId = $(this).data("id");
586 - const clientName = $(this).closest("tr").find("td:first-child div").text().trim();
587 -
588 - // Use the new confirmation system instead of browser confirm
589 - if (typeof EasyInvoiceConfirmation !== 'undefined') {
590 - EasyInvoiceConfirmation.confirmAction('delete', 'client "' + clientName + '"', function() {
591 - $.ajax({
592 - url: easyInvoice.ajaxUrl,
593 - type: "POST",
594 - data: {
595 - action: "easy_invoice_delete_client",
596 - client_id: clientId,
597 - nonce: easyInvoice.nonce
598 - },
599 - success: function(response) {
600 - if (response.success) {
601 - EasyInvoiceToast.success(response.data.message || 'Client deleted successfully');
602 - $("[data-id='" + clientId + "']").closest("tr").fadeOut(400, function() {
603 - $(this).remove();
604 -
605 - // Update total clients count
606 - var currentCount = parseInt($("h3:contains('Total Clients')").next().text()) || 0;
607 - $("h3:contains('Total Clients')").next().text(Math.max(0, currentCount - 1));
608 -
609 - // Check if table is empty and show "No clients found"
610 - if ($("tbody tr").length === 0) {
611 - $("tbody").html('<tr><td colspan="6" class="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-500">No clients found</td></tr>');
612 - }
613 - });
614 - } else {
615 - EasyInvoiceToast.error(response.data.message || 'Error deleting client');
616 - }
617 - },
618 - error: function() {
619 - EasyInvoiceToast.error('Error connecting to server');
620 - }
621 - });
608 + // Filter the rows as you type; no request, the list is already here.
609 + (function () {
610 + var $input = $('#client-search'), $count = $('#client-search-count');
611 + if (!$input.length) { return; }
612 + $input.on('input', function () {
613 + var q = $.trim($(this).val()).toLowerCase(), shown = 0, total = 0;
614 + $('.client-list-table tbody tr').each(function () {
615 + var $tr = $(this); if ($tr.find('td').length < 3) { return; }
616 + total++;
617 + var hit = !q || $tr.text().toLowerCase().indexOf(q) !== -1;
618 + $tr.toggle(hit); if (hit) { shown++; }
622 619 });
623 - } else {
624 - // Fallback to browser confirm if toast system not available
625 - if (confirm("Are you sure you want to delete the client \"" + clientName + "\"? This action cannot be undone.")) {
626 - $.ajax({
627 - url: easyInvoice.ajaxUrl,
628 - type: "POST",
629 - data: {
630 - action: "easy_invoice_delete_client",
631 - client_id: clientId,
632 - nonce: easyInvoice.nonce
633 - },
634 - success: function(response) {
635 - if (response.success) {
636 - // Use unified toast system
637 - if (typeof EasyInvoiceToast !== 'undefined') {
638 - EasyInvoiceToast.success("Client \"" + clientName + "\" has been deleted");
639 - } else {
640 - alert("Client \"" + clientName + "\" has been deleted");
641 - }
642 -
643 - // Remove the row from the table
644 - $("[data-id='" + clientId + "']").closest("tr").fadeOut(300, function() {
645 - $(this).remove();
646 -
647 - // Update total clients count
648 - var currentCount = parseInt($("h3:contains('Total Clients')").next().text()) || 0;
649 - $("h3:contains('Total Clients')").next().text(Math.max(0, currentCount - 1));
650 -
651 - // Check if table is empty and show "No clients found"
652 - if ($("tbody tr").length === 0) {
653 - $("tbody").html('<tr><td colspan="6" class="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-500">No clients found</td></tr>');
654 - }
655 - });
656 - } else {
657 - // Use unified toast system
658 - if (typeof EasyInvoiceToast !== 'undefined') {
659 - EasyInvoiceToast.error(response.data.message || "Error deleting client");
660 - } else {
661 - alert(response.data.message || "Error deleting client");
662 - }
663 - }
664 - },
665 - error: function() {
666 - // Use unified toast system
667 - if (typeof EasyInvoiceToast !== 'undefined') {
668 - EasyInvoiceToast.error("Error connecting to server");
669 - } else {
670 - alert("Error connecting to server");
671 - }
672 - }
673 - });
674 - }
675 - }
676 - });
620 + $count.text(q ? shown + ' / ' + total : '');
621 + });
622 + })();
623 + // Delete is handled by assets/js/clients.js (choice of keeping or removing the client's documents).
677 624 });
678 625 </script>