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 | templates/payments/list.php +130 -95 2.1.12.4.0 View file →
@@ -92,8 +92,9 @@
92 92 $notification = false;
93 93
94 94 if (isset($_GET['bulk_trashed']) && $_GET['bulk_trashed'] > 0) {
95 95 $count = intval($_GET['bulk_trashed']);
96 + /* translators: %s: number of items. */
96 97 easy_invoice_display_notification('success', sprintf(_n('%s payment moved to trash.', '%s payments moved to trash.', $count, 'easy-invoice'), $count));
97 98 $notification = true;
98 99 }
99 100
@@ -98,8 +99,9 @@
98 99 }
99 100
100 101 if (isset($_GET['bulk_restored']) && $_GET['bulk_restored'] > 0) {
101 102 $count = intval($_GET['bulk_restored']);
103 + /* translators: %s: number of items. */
102 104 easy_invoice_display_notification('success', sprintf(_n('%s payment restored from trash.', '%s payments restored from trash.', $count, 'easy-invoice'), $count));
103 105 $notification = true;
104 106 }
105 107
@@ -104,8 +106,9 @@
104 106 }
105 107
106 108 if (isset($_GET['bulk_deleted']) && $_GET['bulk_deleted'] > 0) {
107 109 $count = intval($_GET['bulk_deleted']);
110 + /* translators: %s: number of items. */
108 111 easy_invoice_display_notification('success', sprintf(_n('%s payment permanently deleted.', '%s payments permanently deleted.', $count, 'easy-invoice'), $count));
109 112 $notification = true;
110 113 }
111 114
@@ -124,22 +127,38 @@
124 127 }
125 128
126 129 ?>
127 130 <div class="p-8">
128 - <!-- Header with Actions -->
129 - <div class="bg-white border-b border-gray-200 px-6 py-5" style="margin-left: -2rem; margin-top: -2rem; margin-right: -2rem; padding-left: 2rem;">
131 + <div class="ei-app-page-header bg-white border-b border-gray-200 px-6" style="margin-left: -2rem; margin-top: -2rem; margin-right: -2rem; padding-left: 2rem; padding-right: 2rem;">
130 132 <div class="flex items-center justify-between">
131 133 <div>
132 - <h1 class="text-2xl font-bold text-gray-900"><?php _e('Payments', 'easy-invoice'); ?></h1>
133 - <p class="mt-1 text-sm text-gray-500"><?php _e('Manage all payment records', 'easy-invoice'); ?></p>
134 + <h1 class="text-2xl font-bold text-gray-900"><?php esc_html_e('Payments', 'easy-invoice'); ?></h1>
135 + <p class="mt-1 text-sm text-gray-500"><?php esc_html_e('Manage all payment records', 'easy-invoice'); ?></p>
134 136 </div>
135 137 <div class="flex items-center space-x-3">
136 - <?php if (!easy_invoice_has_pro()): ?>
138 + <?php
139 + // "Export All" is part of the bulk_operations addon
140 + // (Personal tier). See templates/invoices/listing.php
141 + // for the canonical three-state gate pattern.
142 + $ei_bulk_ops_loaded = \EasyInvoice\Addons\AddonManager::shouldLoad('bulk_operations');
143 + ?>
144 + <?php if ($ei_bulk_ops_loaded): ?>
145 + <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline;">
146 + <input type="hidden" name="action" value="easy_invoice_export_all_payments" />
147 + <input type="hidden" name="_wpnonce" value="<?php echo esc_attr(wp_create_nonce('easy_invoice_export_all_payments')); ?>" />
148 + <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">
149 + <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">
150 + <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>
151 + </svg>
152 + <?php esc_html_e('Export All', 'easy-invoice'); ?>
153 + </button>
154 + </form>
155 + <?php elseif (!easy_invoice_has_pro()): ?>
137 156 <button type="button" id="export-all-payments-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">
138 157 <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">
139 158 <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>
140 159 </svg>
141 - <?php _e('Export All', 'easy-invoice'); ?>
160 + <?php esc_html_e('Export All', 'easy-invoice'); ?>
142 161 <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">
143 162 <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"/>
144 163 </svg>
145 164 </button>
@@ -146,42 +165,30 @@
146 165 <script>
147 166 jQuery(document).ready(function($) {
148 167 $('#export-all-payments-btn').on('click', function(e) {
149 168 e.preventDefault();
150 - // Show premium popup using the proper confirmation modal
151 169 if (typeof EasyInvoiceConfirmation !== 'undefined') {
152 170 EasyInvoiceConfirmation.showFeatureUpgrade('Export All Payments', 'Export all your payments to CSV, Excel, or PDF format for backup, analysis, or sharing with your team.');
153 171 } else {
154 - // Fallback to toast if confirmation modal is not available
155 172 EasyInvoiceToast.error('<?php echo esc_js(__('Exporting all payments is a premium feature. Upgrade to Easy Invoice Pro to unlock this feature.', 'easy-invoice')); ?>');
156 173 }
157 174 });
158 175 });
159 176 </script>
160 - <?php else: ?>
161 - <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline;">
162 - <input type="hidden" name="action" value="easy_invoice_export_all_payments" />
163 - <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('easy_invoice_export_all_payments'); ?>" />
164 - <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">
165 - <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">
166 - <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>
167 - </svg>
168 - <?php _e('Export All', 'easy-invoice'); ?>
169 - </button>
170 - </form>
171 177 <?php endif; ?>
172 178
173 - <a href="?page=easy-invoice-payment-new" 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">
179 + <?php if (easy_invoice_user_can('ei_record_payment')) : ?>
180 +<a href="?page=easy-invoice-payment-new" 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">
174 181 <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
175 182 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
176 183 </svg>
177 - <?php _e('Add New Payment', 'easy-invoice'); ?>
184 + <?php esc_html_e('Add New Payment', 'easy-invoice'); ?>
178 185 </a>
186 +<?php endif; ?>
179 187 </div>
180 188 </div>
181 189 </div>
182 190
183 - <!-- Stats Cards -->
184 191 <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8 mt-8">
185 192 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
186 193 <div class="flex items-center">
187 194 <div class="rounded-full bg-indigo-100 p-3 mr-4">
@@ -189,10 +196,10 @@
189 196 <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>
190 197 </svg>
191 198 </div>
192 199 <div>
193 - <h3 class="text-sm font-medium text-gray-500">Total Payments</h3>
194 - <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo $stats['total_payments']; ?></p>
200 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Total Payments', 'easy-invoice'); ?></h3>
201 + <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo esc_html($stats['total_payments']); ?></p>
195 202 </div>
196 203 </div>
197 204 </div>
198 205 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
@@ -202,20 +209,20 @@
202 209 <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>
203 210 </svg>
204 211 </div>
205 212 <div>
206 - <h3 class="text-sm font-medium text-gray-500">Total Amount</h3>
213 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Total Amount', 'easy-invoice'); ?></h3>
207 214 <?php if (!empty($amounts_by_currency)): ?>
208 215 <div class="mt-1">
209 216 <?php foreach ($amounts_by_currency as $currency => $data): ?>
210 217 <p class="text-lg font-bold text-gray-900">
211 - <?php echo $data['symbol'] . number_format($data['amount'], 2); ?>
212 - <span class="text-sm font-normal text-gray-500"><?php echo strtoupper($currency ?? 'USD'); ?></span>
218 + <?php echo esc_html($data['symbol'] . number_format($data['amount'], 2)); ?>
219 + <span class="text-sm font-normal text-gray-500"><?php echo esc_html(strtoupper($currency ?? 'USD')); ?></span>
213 220 </p>
214 221 <?php endforeach; ?>
215 222 </div>
216 223 <?php else: ?>
217 - <p class="mt-1 text-lg font-bold text-gray-900"><?php echo get_option('easy_invoice_currency_symbol', '$') . '0.00'; ?></p>
224 + <p class="mt-1 text-lg font-bold text-gray-900"><?php echo esc_html(get_option('easy_invoice_currency_symbol', '$') . '0.00'); ?></p>
218 225 <?php endif; ?>
219 226 </div>
220 227 </div>
221 228 </div>
@@ -226,10 +233,10 @@
226 233 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
227 234 </svg>
228 235 </div>
229 236 <div>
230 - <h3 class="text-sm font-medium text-gray-500">Completed Payments</h3>
231 - <p class="mt-1 text-2xl font-bold text-green-600"><?php echo $stats['completed_payments']; ?></p>
237 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Completed Payments', 'easy-invoice'); ?></h3>
238 + <p class="mt-1 text-2xl font-bold text-green-600"><?php echo esc_html($stats['completed_payments']); ?></p>
232 239 </div>
233 240 </div>
234 241 </div>
235 242 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
@@ -239,16 +246,15 @@
239 246 <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>
240 247 </svg>
241 248 </div>
242 249 <div>
243 - <h3 class="text-sm font-medium text-gray-500">Pending Payments</h3>
244 - <p class="mt-1 text-2xl font-bold text-yellow-600"><?php echo $stats['pending_payments']; ?></p>
250 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Pending Payments', 'easy-invoice'); ?></h3>
251 + <p class="mt-1 text-2xl font-bold text-yellow-600"><?php echo esc_html($stats['pending_payments']); ?></p>
245 252 </div>
246 253 </div>
247 254 </div>
248 255 </div>
249 256
250 - <!-- Top Pagination -->
251 257 <?php if ($total_payments > 0): ?>
252 258 <div class="mb-4">
253 259 <div class="px-4 py-3 flex items-center justify-between">
254 260 <div class="flex items-center space-x-2">
@@ -305,9 +311,9 @@
305 311 if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) {
306 312 $link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link);
307 313 }
308 314
309 - echo $link;
315 + echo wp_kses_post($link);
310 316 }
311 317 echo '</div>';
312 318 }
313 319 ?>
@@ -316,11 +322,9 @@
316 322 </div>
317 323 </div>
318 324 <?php endif; ?>
319 325
320 - <!-- Tabs with more modern styling -->
321 326 <div class="bg-white rounded-lg shadow mb-6 overflow-hidden">
322 - <!-- Tabs with more modern styling -->
323 327 <div class="border-b border-gray-200 bg-gradient-to-r from-indigo-50 to-white">
324 328 <nav class="flex">
325 329 <?php
326 330 // View Tabs (All, Trash)
@@ -335,11 +339,11 @@
335 339 $class = $is_current
336 340 ? 'border-b-2 border-indigo-500 text-indigo-600 bg-white'
337 341 : 'border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300';
338 342 ?>
339 - <a href="?page=easy-invoice-payments&view=<?php echo $view . $status_param; ?>"
340 - class="flex items-center whitespace-nowrap py-4 px-6 font-medium text-sm transition-colors duration-200 <?php echo $class; ?>">
341 - <?php echo $label; ?>
343 + <a href="?page=easy-invoice-payments&view=<?php echo esc_attr($view . $status_param); ?>"
344 + class="flex items-center whitespace-nowrap py-4 px-6 font-medium text-sm transition-colors duration-200 <?php echo esc_attr($class); ?>">
345 + <?php echo esc_html($label); ?>
342 346 </a>
343 347 <?php } ?>
344 348 </nav>
345 349 </div>
@@ -344,11 +348,9 @@
344 348 </nav>
345 349 </div>
346 350 </div>
347 351
348 - <!-- Payments Table -->
349 352 <div class="bg-white shadow overflow-hidden sm:rounded-lg">
350 - <!-- Bulk Actions -->
351 353 <div class="px-6 py-3 border-b border-gray-200 bg-gray-50">
352 354 <div class="flex items-center justify-between">
353 355 <div class="flex items-center space-x-4">
354 356 <form id="bulk-action-form" method="post" class="flex items-center gap-2">
@@ -354,16 +356,16 @@
354 356 <form id="bulk-action-form" method="post" class="flex items-center gap-2">
355 357 <?php wp_nonce_field('easy_invoice_payment_bulk_action', 'easy_invoice_payment_bulk_nonce'); ?>
356 358 <input type="hidden" name="action" value="easy_invoice_payment_bulk_action">
357 359
358 - <select name="bulk_action" class="block w-full px-3 py-2 border border-gray-300 rounded-md leading-5 bg-white text-gray-700 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
359 - <option value="">Bulk Actions</option>
360 + <select name="bulk_action" aria-label="Bulk action for selected payments" class="block w-full px-3 py-2 border border-gray-300 rounded-md leading-5 bg-white text-gray-700 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
361 + <option value=""><?php esc_html_e('Bulk Actions', 'easy-invoice'); ?></option>
360 362 <?php if ($current_view === 'trash'): ?>
361 - <option value="restore">Restore</option>
362 - <option value="delete">Delete Permanently</option>
363 + <option value="restore"><?php esc_html_e('Restore', 'easy-invoice'); ?></option>
364 + <option value="delete"><?php esc_html_e('Delete Permanently', 'easy-invoice'); ?></option>
363 365 <?php else: ?>
364 - <option value="trash">Move to Trash</option>
365 - <option value="delete">Delete Permanently</option>
366 + <option value="trash"><?php esc_html_e('Move to Trash', 'easy-invoice'); ?></option>
367 + <option value="delete"><?php esc_html_e('Delete Permanently', 'easy-invoice'); ?></option>
366 368 <?php endif; ?>
367 369 </select>
368 370
369 371 <button type="submit" 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">
@@ -369,24 +371,25 @@
369 371 <button type="submit" 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">
370 372 Apply
371 373 </button>
372 374 </form>
373 - </div>
375 + </div>
374 376 <div class="text-sm text-gray-500">
375 377 <span id="selected-count">0</span> payments selected
376 378 </div>
379 + </div>
377 380 </div>
378 - </div>
379 381
380 - <table class="min-w-full divide-y divide-gray-200">
382 + <div class="ei-table-responsive">
383 + <table class="min-w-full divide-y divide-gray-200 ei-payments-table">
381 384 <thead class="bg-gray-50">
382 385 <tr>
383 386 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
384 - <input type="checkbox" id="select-all-payments" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
387 + <input type="checkbox" id="select-all-payments" aria-label="Select all payments on this page" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
385 388 </th>
386 389 <th scope="col" 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">
387 390 <div class="flex items-center space-x-1">
388 - <span>Payment ID</span>
391 + <span><?php esc_html_e('Payment ID', 'easy-invoice'); ?></span>
389 392 <div class="sort-indicator">
390 393 <i class="fas fa-sort text-gray-400"></i>
391 394 </div>
392 395 </div>
@@ -392,9 +395,9 @@
392 395 </div>
393 396 </th>
394 397 <th scope="col" 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="invoice">
395 398 <div class="flex items-center space-x-1">
396 - <span>Invoice</span>
399 + <span><?php esc_html_e('Invoice', 'easy-invoice'); ?></span>
397 400 <div class="sort-indicator">
398 401 <i class="fas fa-sort text-gray-400"></i>
399 402 </div>
400 403 </div>
@@ -400,9 +403,9 @@
400 403 </div>
401 404 </th>
402 405 <th scope="col" 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="amount">
403 406 <div class="flex items-center space-x-1">
404 - <span>Amount</span>
407 + <span><?php esc_html_e('Amount', 'easy-invoice'); ?></span>
405 408 <div class="sort-indicator">
406 409 <i class="fas fa-sort text-gray-400"></i>
407 410 </div>
408 411 </div>
@@ -408,9 +411,9 @@
408 411 </div>
409 412 </th>
410 413 <th scope="col" 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="method">
411 414 <div class="flex items-center space-x-1">
412 - <span>Method</span>
415 + <span><?php esc_html_e('Method', 'easy-invoice'); ?></span>
413 416 <div class="sort-indicator">
414 417 <i class="fas fa-sort text-gray-400"></i>
415 418 </div>
416 419 </div>
@@ -416,9 +419,9 @@
416 419 </div>
417 420 </th>
418 421 <th scope="col" 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="status">
419 422 <div class="flex items-center space-x-1">
420 - <span>Status</span>
423 + <span><?php esc_html_e('Status', 'easy-invoice'); ?></span>
421 424 <div class="sort-indicator">
422 425 <i class="fas fa-sort text-gray-400"></i>
423 426 </div>
424 427 </div>
@@ -424,17 +427,17 @@
424 427 </div>
425 428 </th>
426 429 <th scope="col" 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="date">
427 430 <div class="flex items-center space-x-1">
428 - <span>Date</span>
431 + <span><?php esc_html_e('Date', 'easy-invoice'); ?></span>
429 432 <div class="sort-indicator">
430 433 <i class="fas fa-sort text-gray-400"></i>
431 434 </div>
432 435 </div>
433 436 </th>
434 - <th scope="col" 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="type">
437 + <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100 hidden sm:table-cell" data-sort="type">
435 438 <div class="flex items-center space-x-1">
436 - <span>Type</span>
439 + <span><?php esc_html_e('Type', 'easy-invoice'); ?></span>
437 440 <div class="sort-indicator">
438 441 <i class="fas fa-sort text-gray-400"></i>
439 442 </div>
440 443 </div>
@@ -456,14 +459,14 @@
456 459 // Temporarily show all payments regardless of invoice
457 460 ?>
458 461 <tr>
459 462 <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
460 - <input type="checkbox" name="payment_ids[]" value="<?php echo $payment->getId(); ?>" form="bulk-action-form" class="payment-checkbox rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
463 + <input type="checkbox" name="payment_ids[]" value="<?php echo (int) $payment->getId(); ?>" form="bulk-action-form" class="payment-checkbox rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
461 464 </td>
462 465 <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
463 - #<?php echo $payment->getId(); ?>
466 + #<?php echo (int) $payment->getId(); ?>
464 467 </td>
465 - <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
468 + <td class="px-6 py-4 text-sm text-gray-500">
466 469 <?php if ($invoice_id): ?>
467 470 <?php
468 471 $invoice_post = get_post($invoice_id);
469 472 if ($invoice_post && $invoice_post->post_type === 'easy_invoice'):
@@ -469,10 +472,10 @@
469 472 if ($invoice_post && $invoice_post->post_type === 'easy_invoice'):
470 473 $invoice = new \EasyInvoice\Models\Invoice($invoice_id);
471 474 ?>
472 475 <div>
473 - <a href="<?php echo get_permalink($invoice->getId()); ?>" class="text-indigo-600 hover:text-indigo-900 font-medium" target="_blank">
474 - <?php echo $invoice->getNumber(); ?>
476 + <a href="<?php echo esc_url(get_permalink($invoice->getId())); ?>" class="text-indigo-600 hover:text-indigo-900 font-medium" target="_blank">
477 + <?php echo esc_html($invoice->getNumber()); ?>
475 478 </a>
476 479 <div class="text-xs text-gray-400 mt-1">
477 480 <?php echo esc_html($invoice->getTitle() ?: __('Untitled Invoice', 'easy-invoice')); ?>
478 481 </div>
@@ -478,24 +481,24 @@
478 481 </div>
479 482 </div>
480 483 <?php else: ?>
481 484 <span class="text-gray-400">
482 - <?php _e('Invoice not found', 'easy-invoice'); ?>
485 + <?php esc_html_e('Invoice not found', 'easy-invoice'); ?>
483 486 <?php if (current_user_can('manage_options')): ?>
484 - <br><small class="text-xs text-gray-500">ID: <?php echo $invoice_id; ?> (Type: <?php echo $invoice_post ? $invoice_post->post_type : 'null'; ?>)</small>
487 + <br><small class="text-xs text-gray-500">ID: <?php echo (int) $invoice_id; ?> (Type: <?php echo esc_html($invoice_post ? $invoice_post->post_type : 'null'); ?>)</small>
485 488 <?php endif; ?>
486 489 </span>
487 490 <?php endif; ?>
488 491 <?php else: ?>
489 492 <span class="text-gray-400">
490 - <?php _e('No invoice ID', 'easy-invoice'); ?>
493 + <?php esc_html_e('No invoice ID', 'easy-invoice'); ?>
491 494 </span>
492 495 <?php endif; ?>
493 496 </td>
494 497 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
495 - <?php echo $payment->getCurrencySymbol() . number_format($payment->getAmount(), 2); ?>
498 + <?php echo esc_html($payment->getCurrencySymbol() . number_format((float) $payment->getAmount(), 2)); ?>
496 499 </td>
497 - <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
500 + <td class="px-6 py-4 text-sm text-gray-500">
498 501 <?php
499 502 // Get payment method label dynamically from registered gateways
500 503 $payment_method = $payment->getPaymentMethod();
501 504 $gateway_manager = \EasyInvoice\EasyInvoice::getInstance()->getGatewayManager();
@@ -542,27 +545,27 @@
542 545 $status_class = 'bg-gray-100 text-gray-800';
543 546 $status_text = ucfirst($status);
544 547 }
545 548 ?>
546 - <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <?php echo $status_class; ?>">
547 - <?php echo $status_text; ?>
549 + <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <?php echo esc_attr($status_class); ?>">
550 + <?php echo esc_html($status_text); ?>
548 551 </span>
549 552 </td>
550 553 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
551 - <?php echo $payment->getPaymentDate() ? date('M j, Y', strtotime($payment->getPaymentDate())) : date('M j, Y', strtotime($payment->getPost()->post_date)); ?>
554 + <?php echo esc_html($payment->getPaymentDate() ? gmdate('M j, Y', strtotime($payment->getPaymentDate())) : gmdate('M j, Y', strtotime((string) get_post_field('post_date', $payment->getId())) ?: time())); ?>
552 555 </td>
553 - <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
554 - <?php echo ucfirst($payment->getPaymentType() ?: 'one-time'); ?>
556 + <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 hidden sm:table-cell">
557 + <?php echo esc_html(ucfirst($payment->getPaymentType() ?: 'one-time')); ?>
555 558 </td>
556 559 <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
557 560 <div class="flex items-center space-x-2">
558 - <a href="?page=easy-invoice-payments&action=view&id=<?php echo $payment->getId(); ?>" class="text-indigo-600 hover:text-indigo-900">
561 + <a href="?page=easy-invoice-payments&action=view&id=<?php echo (int) $payment->getId(); ?>" class="text-indigo-600 hover:text-indigo-900">
559 562 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
560 563 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
561 564 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
562 565 </svg>
563 566 </a>
564 - <a href="?page=easy-invoice-payments&action=edit&id=<?php echo $payment->getId(); ?>" class="text-blue-600 hover:text-blue-900">
567 + <a href="?page=easy-invoice-payments&action=edit&id=<?php echo (int) $payment->getId(); ?>" class="text-blue-600 hover:text-blue-900">
565 568 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
566 569 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
567 570 </svg>
568 571 </a>
@@ -567,16 +570,21 @@
567 570 </svg>
568 571 </a>
569 572 <?php if (in_array($status, ['pending', 'pending-bank', 'pending-cheque'])): ?>
570 573 <button type="button" class="approve-payment-btn text-green-600 hover:text-green-900"
571 - data-payment-id="<?php echo $payment->getId(); ?>"
572 - data-invoice-id="<?php echo $payment->getInvoiceId(); ?>"
573 - data-nonce="<?php echo wp_create_nonce('easy_invoice_approve_payment'); ?>">
574 + data-payment-id="<?php echo (int) $payment->getId(); ?>"
575 + data-invoice-id="<?php echo esc_attr($payment->getInvoiceId()); ?>"
576 + data-nonce="<?php echo esc_attr(wp_create_nonce('easy_invoice_approve_payment')); ?>">
574 577 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
575 578 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
576 579 </svg>
577 580 </button>
578 581 <?php endif; ?>
582 +
583 + <?php
584 + // Hook for additional payment actions (used by Pro plugin for receipts)
585 + do_action('easy_invoice_payment_row_actions', $payment, $status);
586 + ?>
579 587 </div>
580 588 </td>
581 589 </tr>
582 590 <?php endforeach; ?>
@@ -581,18 +589,33 @@
581 589 </tr>
582 590 <?php endforeach; ?>
583 591 <?php else : ?>
584 592 <tr>
585 - <td colspan="9" class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-center">
586 - <?php _e('No payments found.', 'easy-invoice'); ?>
593 + <?php
594 + // Was a bare "No payments found." with no way forward. Every
595 + // other empty state in the plugin offers the next action, so
596 + // this one now points at Add New Payment too.
597 + //
598 + // `whitespace-nowrap` also had to go: it is meant for table
599 + // cells holding short values, and on a 9-column empty row it
600 + // stopped the message wrapping.
601 + $ei_add_payment_link = '<a href="' . esc_url(admin_url('admin.php?page=easy-invoice-payment-new')) . '" class="text-indigo-600 hover:text-indigo-900">'
602 + . esc_html__('Record your first payment', 'easy-invoice')
603 + . '</a>';
604 + ?>
605 + <td colspan="9" class="px-6 py-4 text-sm text-gray-500 text-center">
606 + <?php
607 + /* translators: %s: link reading "Record your first payment". */
608 + printf(esc_html__('No payments found. %s.', 'easy-invoice'), wp_kses_post($ei_add_payment_link));
609 + ?>
587 610 </td>
588 611 </tr>
589 612 <?php endif; ?>
590 613 </tbody>
591 614 </table>
615 + </div>
592 616 </div>
593 617
594 - <!-- Bottom Pagination -->
595 618 <?php if ($total_payments > 0): ?>
596 619 <div class="mt-4">
597 620 <div class="px-4 py-3 flex items-center justify-between">
598 621 <div class="flex items-center space-x-2">
@@ -649,9 +672,9 @@
649 672 if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) {
650 673 $link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link);
651 674 }
652 675
653 - echo $link;
676 + echo wp_kses_post($link);
654 677 }
655 678 echo '</div>';
656 679 }
657 680 ?>
@@ -661,15 +684,12 @@
661 684 </div>
662 685 <?php endif; ?>
663 686 </div>
664 687
665 -<!-- Approval Modal -->
666 688 <div id="payment-approval-modal" class="fixed inset-0 z-50 overflow-y-auto hidden" aria-labelledby="modal-title" role="dialog" aria-modal="true">
667 689 <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
668 - <!-- Background overlay -->
669 690 <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
670 691
671 - <!-- Modal panel -->
672 692 <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
673 693 <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
674 694 <div class="sm:flex sm:items-start">
675 695 <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-green-100 sm:mx-0 sm:h-10 sm:w-10">
@@ -678,19 +698,19 @@
678 698 </svg>
679 699 </div>
680 700 <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
681 701 <h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
682 - <?php _e('Approve Payment', 'easy-invoice'); ?>
702 + <?php esc_html_e('Approve Payment', 'easy-invoice'); ?>
683 703 </h3>
684 704 <div class="mt-2">
685 705 <p class="text-sm text-gray-500">
686 - <?php _e('You are about to approve this payment. This will mark the invoice as paid and update the payment records.', 'easy-invoice'); ?>
706 + <?php esc_html_e('You are about to approve this payment. This will mark the invoice as paid and update the payment records.', 'easy-invoice'); ?>
687 707 </p>
688 708 <div class="mt-4">
689 709 <label for="approval-notes" class="block text-sm font-medium text-gray-700">
690 - <?php _e('Notes (optional)', 'easy-invoice'); ?>
710 + <?php esc_html_e('Notes (optional)', 'easy-invoice'); ?>
691 711 </label>
692 - <textarea id="approval-notes" name="notes" rows="3" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" placeholder="<?php _e('Add any notes about this payment approval...', 'easy-invoice'); ?>"></textarea>
712 + <textarea id="approval-notes" name="notes" rows="3" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" placeholder="<?php echo esc_js(__('Add any notes about this payment approval...', 'easy-invoice')); ?>"></textarea>
693 713 </div>
694 714 </div>
695 715 </div>
696 716 </div>
@@ -696,12 +716,12 @@
696 716 </div>
697 717 </div>
698 718 <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
699 719 <button type="button" id="confirm-approve-btn" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-600 text-base font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 sm:ml-3 sm:w-auto sm:text-sm">
700 - <?php _e('Approve Payment', 'easy-invoice'); ?>
720 + <?php esc_html_e('Approve Payment', 'easy-invoice'); ?>
701 721 </button>
702 722 <button type="button" id="cancel-approve-btn" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
703 - <?php _e('Cancel', 'easy-invoice'); ?>
723 + <?php esc_html_e('Cancel', 'easy-invoice'); ?>
704 724 </button>
705 725 </div>
706 726 </div>
707 727 </div>
@@ -706,15 +726,30 @@
706 726 </div>
707 727 </div>
708 728 </div>
709 729
710 -<!-- Notification area for AJAX responses -->
711 730 <div id="payment-approve-notification" class="fixed top-0 right-0 m-4 hidden">
712 - <!-- Content will be injected via JavaScript -->
713 731 </div>
714 732
715 733 <style>
716 734 /* Add any additional custom styles here */
735 +.ei-table-responsive{
736 + width: 100%;
737 + max-width: 100%;
738 + overflow-x: auto;
739 + -webkit-overflow-scrolling: touch;
740 +}
741 +.ei-payments-table{
742 + width: 100%;
743 + min-width: 900px;
744 +}
745 +@media (max-width: 640px) {
746 + .ei-payments-table th,
747 + .ei-payments-table td{
748 + padding-left: 0.75rem !important;
749 + padding-right: 0.75rem !important;
750 + }
751 +}
717 752 .payment-status {
718 753 display: inline-block;
719 754 padding: 3px 8px;
720 755 border-radius: 3px;
@@ -736,9 +771,9 @@
736 771 </style>
737 772
738 773 <script>
739 774 // Define ajaxurl for AJAX requests
740 -var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
775 +var ajaxurl = '<?php echo esc_url(admin_url('admin-ajax.php')); ?>';
741 776
742 777 jQuery(document).ready(function($) {
743 778 // Variables to store the current payment being approved
744 779 let currentInvoiceId = null;