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/reports-page.php +149 -140 2.1.132.4.0 View file →
@@ -4,9 +4,8 @@
4 4 exit;
5 5 }
6 6 ?>
7 7
8 -<!-- Immediate tab initialization -->
9 8 <script type="text/javascript">
10 9 // Run immediately to ensure payment tab is visible before anything else
11 10 (function() {
12 11 document.addEventListener('DOMContentLoaded', function() {
@@ -28,20 +27,19 @@
28 27 })();
29 28 </script>
30 29
31 30 <div class="p-8">
32 - <!-- Page Header -->
33 - <div class="bg-white border-b border-gray-200 px-6 py-5" style="margin-left: -2rem; margin-top: -2rem; padding-left:2rem; padding-right:2rem; margin-right: -2rem;">
31 + <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;">
34 32 <div class="flex items-center justify-between">
35 33 <div>
36 - <h1 class="text-2xl font-bold text-gray-900">Reports</h1>
37 - <p class="mt-1 text-sm text-gray-500">Comprehensive insights and analytics</p>
34 + <h1 class="text-2xl font-bold text-gray-900"><?php esc_html_e('Reports', 'easy-invoice'); ?></h1>
35 + <p class="mt-1 text-sm text-gray-500"><?php esc_html_e('Comprehensive insights and analytics', 'easy-invoice'); ?></p>
38 36 </div>
39 37 <div class="flex items-center space-x-3">
40 - <form method="get" action="<?php echo admin_url('admin.php'); ?>" class="flex space-x-2">
38 + <form method="get" action="<?php echo esc_url(admin_url('admin.php')); ?>" class="flex space-x-2">
41 39 <input type="hidden" name="page" value="easy-invoice-reports">
42 40 <div class="flex items-center">
43 - <label for="start_date" class="mr-2 text-sm font-medium text-gray-700">From:</label>
41 + <label for="start_date" class="mr-2 text-sm font-medium text-gray-700"><?php esc_html_e('From:', 'easy-invoice'); ?></label>
44 42 <input type="date" id="start_date" name="start_date" value="<?php echo esc_attr($start_date); ?>" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm transition-colors duration-200">
45 43 </div>
46 44 <div class="flex items-center">
47 45 <label for="end_date" class="mr-2 text-sm font-medium text-gray-700">To:</label>
@@ -58,9 +56,8 @@
58 56 </div>
59 57 </div>
60 58 </div>
61 59
62 - <!-- Summary Cards -->
63 60 <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8 mt-8">
64 61 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
65 62 <div class="flex items-center">
66 63 <div class="rounded-full bg-indigo-100 p-3 mr-4">
@@ -68,9 +65,9 @@
68 65 <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>
69 66 </svg>
70 67 </div>
71 68 <div>
72 - <h3 class="text-sm font-medium text-gray-500">Total Revenue</h3>
69 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Revenue (paid invoices in period, net of credits)', 'easy-invoice'); ?></h3>
73 70 <?php if (is_array($summary_stats['total_revenue']) && !empty($summary_stats['total_revenue'])): ?>
74 71 <div class="mt-1">
75 72 <?php
76 73 $currency_count = count($summary_stats['total_revenue']);
@@ -79,10 +76,10 @@
79 76 // Get currency code from the data array or use a fallback
80 77 $currency_code = isset($data['currency_code']) ? $data['currency_code'] : 'USD';
81 78 ?>
82 79 <div class="<?php echo $currency_count > 1 ? 'text-sm' : 'text-lg'; ?> font-bold text-gray-900 <?php echo !$is_last ? 'mb-1' : ''; ?>">
83 - <?php echo $data['symbol'] . number_format($data['amount'], 2); ?>
84 - <span class="text-xs font-normal text-gray-500"><?php echo strtoupper($currency_code ?? 'USD'); ?></span>
80 + <?php echo esc_html(easy_invoice_format_money($data['amount'], null, (string) ($currency_code ?? 'USD'))); ?>
81 + <span class="text-xs font-normal text-gray-500"><?php echo esc_html(strtoupper($currency_code ?? 'USD')); ?></span>
85 82 </div>
86 83 <?php endforeach; ?>
87 84 </div>
88 85 <?php else: ?>
@@ -99,9 +96,9 @@
99 96 <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>
100 97 </svg>
101 98 </div>
102 99 <div>
103 - <h3 class="text-sm font-medium text-gray-500">Total Invoices</h3>
100 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Total Invoices', 'easy-invoice'); ?></h3>
104 101 <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo esc_html($summary_stats['total_invoices']); ?></p>
105 102 </div>
106 103 </div>
107 104 </div>
@@ -116,9 +113,9 @@
116 113 <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"/>
117 114 </svg>
118 115 </div>
119 116 <div>
120 - <h3 class="text-sm font-medium text-gray-500">Active Clients</h3>
117 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Invoiced Clients', 'easy-invoice'); ?></h3>
121 118 <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo esc_html($summary_stats['active_clients']); ?></p>
122 119 </div>
123 120 </div>
124 121 </div>
@@ -130,28 +127,25 @@
130 127 <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>
131 128 </svg>
132 129 </div>
133 130 <div>
134 - <h3 class="text-sm font-medium text-gray-500">Average Payment Time</h3>
135 - <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo esc_html($summary_stats['avg_payment_time']); ?> days</p>
131 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Average Payment Time', 'easy-invoice'); ?></h3>
132 + <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo null === $summary_stats['avg_payment_time'] ? '&mdash;' : esc_html($summary_stats['avg_payment_time']) . ' ' . esc_html__('days', 'easy-invoice'); ?></p>
136 133 </div>
137 134 </div>
138 135 </div>
139 136 </div>
140 137
141 - <!-- Charts Section -->
142 138 <div class="grid grid-cols-1 gap-5 lg:grid-cols-2 mb-8">
143 - <!-- Revenue Chart -->
144 139 <div class="bg-white shadow rounded-lg p-6">
145 - <h2 class="text-lg font-medium text-gray-900 mb-4">Monthly Revenue</h2>
140 + <h2 class="text-lg font-medium text-gray-900 mb-4"><?php esc_html_e('Monthly Revenue (payments received, last 12 months)', 'easy-invoice'); ?></h2>
146 141 <div class="h-64">
147 142 <canvas id="revenue-chart"></canvas>
148 143 </div>
149 144 </div>
150 145
151 - <!-- Invoice Status Chart -->
152 146 <div class="bg-white shadow rounded-lg p-6">
153 - <h2 class="text-lg font-medium text-gray-900 mb-4">Invoice Status</h2>
147 + <h2 class="text-lg font-medium text-gray-900 mb-4"><?php esc_html_e('Invoice Status', 'easy-invoice'); ?></h2>
154 148 <div class="h-64 flex items-center justify-center">
155 149 <div class="w-48 h-48 relative">
156 150 <canvas id="status-chart"></canvas>
157 151 </div>
@@ -158,12 +152,18 @@
158 152 <div class="ml-8">
159 153 <?php
160 154 $statusColors = [
161 155 'Paid' => 'bg-green-500',
156 + 'Partial' => 'bg-blue-500',
162 157 'Unpaid' => 'bg-yellow-500',
163 158 'Overdue' => 'bg-red-500',
164 - 'Draft' => 'bg-gray-500'
159 + 'Draft' => 'bg-gray-300',
160 + 'Canceled' => 'bg-gray-500'
165 161 ];
162 + $ei_status_names = [
163 + 'paid' => __('Paid', 'easy-invoice'), 'partial' => __('Partially paid', 'easy-invoice'), 'unpaid' => __('Unpaid', 'easy-invoice'),
164 + 'overdue' => __('Overdue', 'easy-invoice'), 'draft' => __('Draft', 'easy-invoice'), 'canceled' => __('Cancelled', 'easy-invoice'),
165 + ];
166 166
167 167 if (isset($invoice_status['percentages']) && isset($invoice_status['counts'])) {
168 168 foreach ($invoice_status['percentages'] as $status => $percentage) {
169 169 $color = isset($statusColors[ucfirst($status)]) ? $statusColors[ucfirst($status)] : 'bg-gray-500';
@@ -169,9 +169,9 @@
169 169 $color = isset($statusColors[ucfirst($status)]) ? $statusColors[ucfirst($status)] : 'bg-gray-500';
170 170 $count = isset($invoice_status['counts'][$status]) ? $invoice_status['counts'][$status] : 0;
171 171 echo '<div class="flex items-center mb-2">';
172 172 echo '<div class="w-4 h-4 rounded-full ' . esc_attr($color) . ' mr-2"></div>';
173 - echo '<span class="text-sm text-gray-600">' . esc_html(ucfirst($status)) . ' (' . esc_html($percentage) . '% - ' . esc_html($count) . ')</span>';
173 + echo '<span class="text-sm text-gray-600">' . esc_html($ei_status_names[$status] ?? ucfirst($status)) . ' (' . esc_html($percentage) . '% - ' . esc_html($count) . ')</span>';
174 174 echo '</div>';
175 175 }
176 176 }
177 177 ?>
@@ -179,12 +179,11 @@
179 179 </div>
180 180 </div>
181 181 </div>
182 182
183 - <!-- Top Clients Table -->
184 183 <div class="bg-white shadow rounded-lg mb-8">
185 184 <div class="px-6 py-4 border-b border-gray-200">
186 - <h2 class="text-lg font-medium text-gray-900">Top Clients by Revenue</h2>
185 + <h2 class="text-lg font-medium text-gray-900"><?php esc_html_e('Top Clients by Revenue', 'easy-invoice'); ?></h2>
187 186 </div>
188 187 <div class="overflow-x-auto">
189 188 <table class="min-w-full divide-y divide-gray-200">
190 189 <thead class="bg-gray-50">
@@ -235,10 +234,10 @@
235 234 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
236 235 <?php if (isset($client['total_amount']) && is_array($client['total_amount']) && !empty($client['total_amount'])): ?>
237 236 <?php foreach ($client['total_amount'] as $currency => $currency_data): ?>
238 237 <div>
239 - <?php echo $currency_data['symbol'] . number_format($currency_data['amount'], 2); ?>
240 - <span class="text-xs text-gray-500"><?php echo $currency; ?></span>
238 + <?php echo esc_html(easy_invoice_format_money($currency_data['amount'], null, (string) $currency)); ?>
239 + <span class="text-xs text-gray-500"><?php echo esc_html($currency); ?></span>
241 240 </div>
242 241 <?php endforeach; ?>
243 242 <?php else: ?>
244 243 $0.00
@@ -247,9 +246,9 @@
247 246 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
248 247 <?php echo esc_html($client['total_invoices']); ?> invoices
249 248 </td>
250 249 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
251 - <?php echo !empty($client['last_invoice']) ? date('M d, Y', strtotime($client['last_invoice'])) : 'N/A'; ?>
250 + <?php echo esc_html(!empty($client['last_invoice']) ? gmdate('M d, Y', strtotime($client['last_invoice'])) : 'N/A'); ?>
252 251 </td>
253 252
254 253
255 254 <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
@@ -258,9 +257,9 @@
258 257 </tr>
259 258 <?php endforeach; ?>
260 259 <?php else: ?>
261 260 <tr>
262 - <td colspan="5" class="px-6 py-4 text-center text-gray-500">No client data available</td>
261 + <td colspan="5" class="px-6 py-4 text-center text-gray-500"><?php esc_html_e('No client data available', 'easy-invoice'); ?></td>
263 262 </tr>
264 263 <?php endif; ?>
265 264 </tbody>
266 265 </table>
@@ -266,13 +265,25 @@
266 265 </table>
267 266 </div>
268 267 </div>
269 268
270 - <!-- Detailed Reports Tabs -->
269 + <?php
270 + /**
271 + * Fires between the summary charts and the detailed reports.
272 + *
273 + * Chart.js is on the page as the `chartjs` handle. The Pro Reports
274 + * addon adds the profit & loss statement here.
275 + *
276 + * @param string $start_date Y-m-d.
277 + * @param string $end_date Y-m-d.
278 + */
279 + do_action('easy_invoice_reports_after_summary', $start_date, $end_date);
280 + ?>
281 +
271 282 <div class="bg-white shadow rounded-lg">
272 283 <div class="border-b border-gray-200">
273 284 <div class="flex items-center justify-between px-6 py-3">
274 - <h2 class="text-lg font-medium text-gray-900">Detailed Reports</h2>
285 + <h2 class="text-lg font-medium text-gray-900"><?php esc_html_e('Detailed Reports', 'easy-invoice'); ?></h2>
275 286 <div>
276 287 <button id="export-detailed-report" class="inline-flex items-center px-3 py-2 border border-transparent text-sm font-medium rounded-md text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
277 288 <i class="fas fa-download mr-2"></i> Export
278 289 </button>
@@ -287,18 +298,17 @@
287 298 </button>
288 299 </nav>
289 300 </div>
290 301
291 - <!-- Payment Report Tab Content -->
292 302 <div id="payment-report-content" class="tab-content block" style="display: block !important;">
293 303 <div class="p-4 bg-gray-50 border-b">
294 304 <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
295 305 <div class="bg-white p-4 rounded-md shadow-sm">
296 - <div class="text-sm text-gray-500">Total Payments</div>
306 + <div class="text-sm text-gray-500"><?php esc_html_e('Total Payments', 'easy-invoice'); ?></div>
297 307 <div class="text-xl font-bold"><?php echo esc_html($payment_report['count']); ?></div>
298 308 </div>
299 309 <div class="bg-white p-4 rounded-md shadow-sm">
300 - <div class="text-sm text-gray-500">Total Amount</div>
310 + <div class="text-sm text-gray-500"><?php esc_html_e('Total Amount', 'easy-invoice'); ?></div>
301 311 <div class="text-lg font-bold">
302 312 <?php
303 313 // Calculate total amount from all payment methods and currencies
304 314 $total_amounts_by_currency = [];
@@ -320,10 +330,10 @@
320 330
321 331 if (!empty($total_amounts_by_currency)): ?>
322 332 <?php foreach ($total_amounts_by_currency as $currency => $data): ?>
323 333 <div class="text-sm">
324 - <?php echo $data['symbol'] . number_format($data['amount'], 2); ?>
325 - <span class="text-xs text-gray-500"><?php echo $currency; ?></span>
334 + <?php echo esc_html(easy_invoice_format_money($data['amount'], null, (string) $currency)); ?>
335 + <span class="text-xs text-gray-500"><?php echo esc_html($currency); ?></span>
326 336 </div>
327 337 <?php endforeach; ?>
328 338 <?php else: ?>
329 339 $0.00
@@ -330,9 +340,9 @@
330 340 <?php endif; ?>
331 341 </div>
332 342 </div>
333 343 <div class="bg-white p-4 rounded-md shadow-sm">
334 - <div class="text-sm text-gray-500">Average Payment</div>
344 + <div class="text-sm text-gray-500"><?php esc_html_e('Average Payment', 'easy-invoice'); ?></div>
335 345 <div class="text-lg font-bold">
336 346 <?php
337 347 // Calculate average payments by currency
338 348 $average_amounts_by_currency = [];
@@ -361,9 +371,9 @@
361 371
362 372 if (!empty($average_amounts_by_currency)): ?>
363 373 <?php foreach ($average_amounts_by_currency as $currency => $data): ?>
364 374 <div class="text-sm">
365 - <?php echo $data['symbol'] . number_format($data['amount'], 2); ?>
375 + <?php echo esc_html(easy_invoice_format_money($data['amount'], null, (string) $currency)); ?>
366 376 <span class="text-xs text-gray-500">
367 377 <?php
368 378 $display_currency = $currency;
369 379 if ($display_currency === 'GLOBAL') {
@@ -374,9 +384,9 @@
374 384 </span>
375 385 </div>
376 386 <?php endforeach; ?>
377 387 <?php elseif ($payment_count > 0): ?>
378 - <span class="text-sm text-gray-500">No currency data</span>
388 + <span class="text-sm text-gray-500"><?php esc_html_e('No currency data', 'easy-invoice'); ?></span>
379 389 <?php else: ?>
380 390 $0.00
381 391 <?php endif; ?>
382 392 </div>
@@ -383,11 +393,10 @@
383 393 </div>
384 394 </div>
385 395 </div>
386 396
387 - <!-- Payment Methods Chart -->
388 397 <div class="p-4">
389 - <h3 class="text-md font-medium text-gray-700 mb-3">Payment Methods Distribution</h3>
398 + <h3 class="text-md font-medium text-gray-700 mb-3"><?php esc_html_e('Payment Methods Distribution', 'easy-invoice'); ?></h3>
390 399 <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
391 400 <div class="bg-white p-4 rounded-md shadow-sm">
392 401 <canvas id="payment-methods-chart" height="200"></canvas>
393 402 </div>
@@ -394,11 +403,11 @@
394 403 <div class="bg-white p-4 rounded-md shadow-sm">
395 404 <table class="min-w-full divide-y divide-gray-200">
396 405 <thead>
397 406 <tr>
398 - <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Method</th>
399 - <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Count</th>
400 - <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>
407 + <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Method', 'easy-invoice'); ?></th>
408 + <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Count', 'easy-invoice'); ?></th>
409 + <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Amount', 'easy-invoice'); ?></th>
401 410 </tr>
402 411 </thead>
403 412 <tbody>
404 413 <?php
@@ -415,10 +424,10 @@
415 424 <td class="px-3 py-2 whitespace-nowrap text-sm text-right text-gray-900">
416 425 <?php if (isset($data['amounts_by_currency']) && !empty($data['amounts_by_currency'])): ?>
417 426 <?php foreach ($data['amounts_by_currency'] as $currency => $currency_data): ?>
418 427 <div class="text-xs">
419 - <?php echo $currency_data['symbol'] . number_format($currency_data['amount'], 2); ?>
420 - <span class="text-xs text-gray-500"><?php echo $currency; ?></span>
428 + <?php echo esc_html(easy_invoice_format_money($currency_data['amount'], null, (string) $currency)); ?>
429 + <span class="text-xs text-gray-500"><?php echo esc_html($currency); ?></span>
421 430 </div>
422 431 <?php endforeach; ?>
423 432 <?php else: ?>
424 433 $0.00
@@ -429,9 +438,9 @@
429 438 endforeach;
430 439 else:
431 440 ?>
432 441 <tr>
433 - <td colspan="3" class="px-3 py-4 text-center text-sm text-gray-500">No payment method data available</td>
442 + <td colspan="3" class="px-3 py-4 text-center text-sm text-gray-500"><?php esc_html_e('No payment method data available', 'easy-invoice'); ?></td>
434 443 </tr>
435 444 <?php endif; ?>
436 445 </tbody>
437 446 </table>
@@ -438,22 +447,21 @@
438 447 </div>
439 448 </div>
440 449 </div>
441 450
442 - <!-- Payments List -->
443 451 <div class="p-4">
444 - <h3 class="text-md font-medium text-gray-700 mb-3">Recent Payments</h3>
452 + <h3 class="text-md font-medium text-gray-700 mb-3"><?php esc_html_e('Recent Payments', 'easy-invoice'); ?></h3>
445 453 <div class="overflow-x-auto">
446 454 <table class="min-w-full divide-y divide-gray-200">
447 455 <thead class="bg-gray-50">
448 456 <tr>
449 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
450 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Invoice</th>
451 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>
452 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Method</th>
453 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
454 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Transaction ID</th>
455 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
457 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Date', 'easy-invoice'); ?></th>
458 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Invoice', 'easy-invoice'); ?></th>
459 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Amount', 'easy-invoice'); ?></th>
460 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Method', 'easy-invoice'); ?></th>
461 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Status', 'easy-invoice'); ?></th>
462 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Transaction ID', 'easy-invoice'); ?></th>
463 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Actions', 'easy-invoice'); ?></th>
456 464 </tr>
457 465 </thead>
458 466 <tbody class="bg-white divide-y divide-gray-200">
459 467 <?php if (!empty($payment_report['payments'])) : ?>
@@ -459,17 +467,21 @@
459 467 <?php if (!empty($payment_report['payments'])) : ?>
460 468 <?php foreach ($payment_report['payments'] as $payment): ?>
461 469 <tr class="hover:bg-gray-50">
462 470 <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
463 - <?php echo date('M d, Y', strtotime($payment['date'])); ?>
471 + <?php echo esc_html(gmdate('M d, Y', strtotime($payment['date']))); ?>
464 472 </td>
465 473 <td class="px-4 py-3 whitespace-nowrap">
466 - <a href="<?php echo esc_url(admin_url('admin.php?page=easy-invoice&action=edit&id=' . $payment['invoice_id'])); ?>" class="text-indigo-600 hover:text-indigo-900 text-sm font-medium">
474 + <?php if (!empty($payment['invoice_id'])) : ?>
475 + <a href="<?php echo esc_url(admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . (int) $payment['invoice_id'])); ?>" class="text-indigo-600 hover:text-indigo-900 text-sm font-medium">
467 476 <?php echo esc_html($payment['invoice_number']); ?>
468 477 </a>
478 + <?php else : ?>
479 + <span class="text-sm text-gray-500"><?php echo esc_html($payment['invoice_number'] ?: '—'); ?></span>
480 + <?php endif; ?>
469 481 </td>
470 482 <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
471 - <?php echo esc_html($payment['currency_symbol'] . number_format($payment['amount'], 2)); ?>
483 + <?php echo esc_html(easy_invoice_format_money($payment['amount'], null, (string) ($payment['currency'] ?? ''))); ?>
472 484 </td>
473 485 <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
474 486 <?php echo esc_html(ucfirst($payment['payment_method'])); ?>
475 487 </td>
@@ -497,9 +509,9 @@
497 509 </tr>
498 510 <?php endforeach; ?>
499 511 <?php else: ?>
500 512 <tr>
501 - <td colspan="7" class="px-4 py-4 text-center text-gray-500">No payment records found for the selected period</td>
513 + <td colspan="7" class="px-4 py-4 text-center text-gray-500"><?php esc_html_e('No payment records found for the selected period', 'easy-invoice'); ?></td>
502 514 </tr>
503 515 <?php endif; ?>
504 516 </tbody>
505 517 </table>
@@ -506,18 +518,17 @@
506 518 </div>
507 519 </div>
508 520 </div>
509 521
510 - <!-- Invoice Report Tab Content -->
511 522 <div id="invoice-report-content" class="tab-content hidden" style="display: none;">
512 523 <div class="p-4 bg-gray-50 border-b">
513 524 <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
514 525 <div class="bg-white p-4 rounded-md shadow-sm">
515 - <div class="text-sm text-gray-500">Total Invoices</div>
526 + <div class="text-sm text-gray-500"><?php esc_html_e('Total Invoices', 'easy-invoice'); ?></div>
516 527 <div class="text-xl font-bold"><?php echo esc_html($invoice_report['count']); ?></div>
517 528 </div>
518 529 <div class="bg-white p-4 rounded-md shadow-sm">
519 - <div class="text-sm text-gray-500">Total Amount</div>
530 + <div class="text-sm text-gray-500"><?php esc_html_e('Total Amount', 'easy-invoice'); ?></div>
520 531 <div class="text-lg font-bold">
521 532 <?php
522 533 // Display total amounts by currency
523 534 if (isset($invoice_report['total_amounts_by_currency']) && !empty($invoice_report['total_amounts_by_currency'])): ?>
@@ -522,9 +533,9 @@
522 533 // Display total amounts by currency
523 534 if (isset($invoice_report['total_amounts_by_currency']) && !empty($invoice_report['total_amounts_by_currency'])): ?>
524 535 <?php foreach ($invoice_report['total_amounts_by_currency'] as $currency => $currency_data): ?>
525 536 <div class="text-sm">
526 - <?php echo $currency_data['symbol'] . number_format($currency_data['amount'], 2); ?>
537 + <?php echo esc_html(easy_invoice_format_money($currency_data['amount'], null, (string) $currency)); ?>
527 538 <span class="text-xs text-gray-500">
528 539 <?php
529 540 $display_currency = $currency;
530 541 if ($display_currency === 'GLOBAL') {
@@ -540,9 +551,9 @@
540 551 <?php endif; ?>
541 552 </div>
542 553 </div>
543 554 <div class="bg-white p-4 rounded-md shadow-sm">
544 - <div class="text-sm text-gray-500">Average Invoice</div>
555 + <div class="text-sm text-gray-500"><?php esc_html_e('Average Invoice', 'easy-invoice'); ?></div>
545 556 <div class="text-lg font-bold">
546 557 <?php
547 558 // Calculate average amounts by currency
548 559 if (isset($invoice_report['total_amounts_by_currency']) && !empty($invoice_report['total_amounts_by_currency']) && $invoice_report['count'] > 0): ?>
@@ -549,9 +560,9 @@
549 560 <?php foreach ($invoice_report['total_amounts_by_currency'] as $currency => $currency_data): ?>
550 561 <div class="text-sm">
551 562 <?php
552 563 $avg_amount = $currency_data['amount'] / $invoice_report['count'];
553 - echo $currency_data['symbol'] . number_format($avg_amount, 2);
564 + echo esc_html(easy_invoice_format_money($avg_amount, null, (string) $currency));
554 565 ?>
555 566 <span class="text-xs text-gray-500">
556 567 <?php
557 568 $display_currency = $currency;
@@ -570,11 +581,10 @@
570 581 </div>
571 582 </div>
572 583 </div>
573 584
574 - <!-- Invoice Status Chart -->
575 585 <div class="p-4">
576 - <h3 class="text-md font-medium text-gray-700 mb-3">Invoice Status Distribution</h3>
586 + <h3 class="text-md font-medium text-gray-700 mb-3"><?php esc_html_e('Invoice Status Distribution', 'easy-invoice'); ?></h3>
577 587 <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
578 588 <div class="bg-white p-4 rounded-md shadow-sm">
579 589 <canvas id="invoice-status-chart" height="200"></canvas>
580 590 </div>
@@ -581,22 +591,23 @@
581 591 <div class="bg-white p-4 rounded-md shadow-sm">
582 592 <table class="min-w-full divide-y divide-gray-200">
583 593 <thead>
584 594 <tr>
585 - <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
586 - <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Count</th>
587 - <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>
595 + <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Status', 'easy-invoice'); ?></th>
596 + <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Count', 'easy-invoice'); ?></th>
597 + <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Amount', 'easy-invoice'); ?></th>
588 598 </tr>
589 599 </thead>
590 600 <tbody>
591 601 <?php
592 602 $status_list = [
593 - 'paid' => 'Paid',
594 - 'unpaid' => 'Unpaid',
595 - 'overdue' => 'Overdue',
596 - 'draft' => 'Draft',
597 - 'canceled' => 'Canceled',
598 - 'other' => 'Other'
603 + 'paid' => __('Paid', 'easy-invoice'),
604 + 'partial' => __('Partially paid', 'easy-invoice'),
605 + 'unpaid' => __('Unpaid', 'easy-invoice'),
606 + 'overdue' => __('Overdue', 'easy-invoice'),
607 + 'draft' => __('Draft', 'easy-invoice'),
608 + 'canceled' => __('Cancelled', 'easy-invoice'),
609 + 'other' => __('Other', 'easy-invoice')
599 610 ];
600 611
601 612 foreach ($status_list as $status_key => $status_label) :
602 613 $status_data = isset($invoice_report['by_status'][$status_key]) ? $invoice_report['by_status'][$status_key] : ['amount' => 0, 'count' => 0];
@@ -611,9 +622,9 @@
611 622 <td class="px-3 py-2 whitespace-nowrap text-sm text-right text-gray-900">
612 623 <?php if (isset($status_data['amounts_by_currency']) && !empty($status_data['amounts_by_currency'])): ?>
613 624 <?php foreach ($status_data['amounts_by_currency'] as $currency => $currency_data): ?>
614 625 <div class="text-xs">
615 - <?php echo $currency_data['symbol'] . number_format($currency_data['amount'], 2); ?>
626 + <?php echo esc_html(easy_invoice_format_money($currency_data['amount'], null, (string) $currency)); ?>
616 627 <span class="text-xs text-gray-500">
617 628 <?php
618 629 $display_currency = $currency;
619 630 if ($display_currency === 'GLOBAL') {
@@ -635,22 +646,21 @@
635 646 </div>
636 647 </div>
637 648 </div>
638 649
639 - <!-- Invoices List -->
640 650 <div class="p-4">
641 - <h3 class="text-md font-medium text-gray-700 mb-3">Recent Invoices</h3>
651 + <h3 class="text-md font-medium text-gray-700 mb-3"><?php esc_html_e('Recent Invoices', 'easy-invoice'); ?></h3>
642 652 <div class="overflow-x-auto">
643 653 <table class="min-w-full divide-y divide-gray-200">
644 654 <thead class="bg-gray-50">
645 655 <tr>
646 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
647 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Due Date</th>
656 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Date', 'easy-invoice'); ?></th>
657 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Due Date', 'easy-invoice'); ?></th>
648 658 <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Invoice #</th>
649 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Client</th>
650 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>
651 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
652 - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
659 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Client', 'easy-invoice'); ?></th>
660 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Amount', 'easy-invoice'); ?></th>
661 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Status', 'easy-invoice'); ?></th>
662 + <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><?php esc_html_e('Actions', 'easy-invoice'); ?></th>
653 663 </tr>
654 664 </thead>
655 665 <tbody class="bg-white divide-y divide-gray-200">
656 666 <?php if (!empty($invoice_report['invoices'])) : ?>
@@ -656,14 +666,14 @@
656 666 <?php if (!empty($invoice_report['invoices'])) : ?>
657 667 <?php foreach ($invoice_report['invoices'] as $invoice): ?>
658 668 <tr class="hover:bg-gray-50">
659 669 <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
660 - <?php echo date('M d, Y', strtotime($invoice['issue_date'])); ?>
670 + <?php echo esc_html(gmdate('M d, Y', strtotime($invoice['issue_date']))); ?>
661 671 </td>
662 672 <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
663 673 <?php
664 674 if (!empty($invoice['due_date']) && $invoice['due_date'] !== '0000-00-00' && $invoice['due_date'] !== '1970-01-01') {
665 - echo date('M d, Y', strtotime($invoice['due_date']));
675 + echo esc_html(gmdate('M d, Y', strtotime($invoice['due_date'])));
666 676 } else {
667 677 echo '-';
668 678 }
669 679 ?>
@@ -668,9 +678,9 @@
668 678 }
669 679 ?>
670 680 </td>
671 681 <td class="px-4 py-3 whitespace-nowrap">
672 - <a href="<?php echo esc_url(get_permalink($invoice['id'])); ?>" class="text-indigo-600 hover:text-indigo-900 text-sm font-medium" target="_blank">
682 + <a href="<?php echo esc_url(admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . (int) $invoice['id'])); ?>" class="text-indigo-600 hover:text-indigo-900 text-sm font-medium">
673 683 <?php echo esc_html($invoice['invoice_number']); ?>
674 684 </a>
675 685 </td>
676 686 <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
@@ -689,9 +699,9 @@
689 699 $display_currency = $invoice['currency'];
690 700 if ($display_currency === 'GLOBAL') {
691 701 $display_currency = get_option('easy_invoice_currency_code', 'USD');
692 702 }
693 - echo esc_html($invoice['currency_symbol'] . number_format($invoice['total'], 2)); ?>
703 + echo esc_html(easy_invoice_format_money($invoice['total'], null, (string) $display_currency)); ?>
694 704 <span class="text-xs text-gray-500"><?php echo esc_html(strtoupper($display_currency)); ?></span>
695 705 </td>
696 706 <td class="px-4 py-3 whitespace-nowrap">
697 707 <?php
@@ -699,8 +709,10 @@
699 709 if (in_array($invoice['status'], ['paid', 'completed'])) {
700 710 $status_class = 'bg-green-100 text-green-800';
701 711 } elseif ($invoice['status'] === 'unpaid') {
702 712 $status_class = 'bg-yellow-100 text-yellow-800';
713 + } elseif ($invoice['status'] === 'partial') {
714 + $status_class = 'bg-blue-100 text-blue-800';
703 715 } elseif ($invoice['status'] === 'overdue') {
704 716 $status_class = 'bg-red-100 text-red-800';
705 717 } elseif ($invoice['status'] === 'draft') {
706 718 $status_class = 'bg-gray-100 text-gray-800';
@@ -706,19 +718,19 @@
706 718 $status_class = 'bg-gray-100 text-gray-800';
707 719 }
708 720 ?>
709 721 <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?php echo esc_attr($status_class); ?>">
710 - <?php echo esc_html(ucfirst($invoice['status'])); ?>
722 + <?php echo esc_html($status_list[$invoice['status']] ?? ucfirst($invoice['status'])); ?>
711 723 </span>
712 724 </td>
713 725 <td class="px-4 py-3 whitespace-nowrap text-sm font-medium">
714 - <a href="<?php echo esc_url(get_permalink($invoice['id'])); ?>" class="text-indigo-600 hover:text-indigo-900" target="_blank">View</a>
726 + <a href="<?php echo esc_url(admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . (int) $invoice['id'])); ?>" class="text-indigo-600 hover:text-indigo-900">View</a>
715 727 </td>
716 728 </tr>
717 729 <?php endforeach; ?>
718 730 <?php else: ?>
719 731 <tr>
720 - <td colspan="7" class="px-4 py-4 text-center text-gray-500">No invoice records found for the selected period</td>
732 + <td colspan="7" class="px-4 py-4 text-center text-gray-500"><?php esc_html_e('No invoice records found for the selected period', 'easy-invoice'); ?></td>
721 733 </tr>
722 734 <?php endif; ?>
723 735 </tbody>
724 736 </table>
@@ -727,10 +739,14 @@
727 739 </div>
728 740 </div>
729 741 </div>
730 742
731 -<!-- Chart.js for Revenue Chart -->
732 -<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
743 +<?php
744 +// Chart.js is enqueued as the 'chartjs' handle by ReportController::enqueueReportScripts()
745 +// from assets/js/vendors/chart.min.js. It was previously also pulled from jsDelivr here,
746 +// which both duplicated the local copy and broke the WordPress.org rule that all assets
747 +// ship inside the plugin. The guard below already waits for Chart to be defined.
748 +?>
733 749 <script>
734 750
735 751
736 752 // Monthly revenue chart
@@ -844,10 +860,8 @@
844 860 }
845 861 }
846 862 }
847 863 });
848 -
849 - console.log('Monthly Revenue Chart initialized successfully');
850 864 } catch (error) {
851 865 console.error('Error initializing Monthly Revenue Chart:', error);
852 866 const chartContainer = document.getElementById('revenue-chart');
853 867 if (chartContainer) {
@@ -853,26 +867,21 @@
853 867 if (chartContainer) {
854 868 chartContainer.style.display = 'flex';
855 869 chartContainer.style.alignItems = 'center';
856 870 chartContainer.style.justifyContent = 'center';
857 - chartContainer.innerHTML = '<div class="text-red-500 text-center"><p>Error loading chart. Please refresh the page.</p></div>';
871 + chartContainer.innerHTML = '<div class="text-red-500 text-center"><p><?php esc_html_e('Error loading chart. Please refresh the page.', 'easy-invoice'); ?></p></div>';
858 872 }
859 873 }
860 874 } else {
861 - console.log('No data available for Monthly Revenue Chart');
862 -
863 - // Show no data message
864 875 const chartContainer = document.getElementById('revenue-chart');
865 876 if (chartContainer) {
866 877 chartContainer.style.display = 'flex';
867 878 chartContainer.style.alignItems = 'center';
868 879 chartContainer.style.justifyContent = 'center';
869 - chartContainer.innerHTML = '<div class="text-gray-500 text-center"><p>No revenue data available for the selected period.</p><p class="text-sm mt-2">Revenue data will appear here once payments are recorded.</p></div>';
880 + chartContainer.innerHTML = '<div class="text-gray-500 text-center"><p><?php esc_html_e('No revenue data available for the selected period.', 'easy-invoice'); ?></p><p class="text-sm mt-2"><?php esc_html_e('Revenue data will appear here once payments are recorded.', 'easy-invoice'); ?></p></div>';
870 881 }
871 882 return;
872 883 }
873 - } else {
874 - console.log('Revenue chart element not found');
875 884 }
876 885 }
877 886
878 887 // Initialize chart when DOM is ready
@@ -926,12 +935,23 @@
926 935
927 936 const statusLabels = <?php
928 937 $status_labels = [];
929 938 $status_data = [];
939 + $status_rgba = [];
940 + // Colours keyed by status so a slice keeps its colour whichever statuses are present.
941 + $ei_overview_colors = [
942 + 'paid' => 'rgba(34, 197, 94, 0.8)',
943 + 'partial' => 'rgba(59, 130, 246, 0.8)',
944 + 'unpaid' => 'rgba(234, 179, 8, 0.8)',
945 + 'overdue' => 'rgba(239, 68, 68, 0.8)',
946 + 'draft' => 'rgba(209, 213, 219, 0.8)',
947 + 'canceled' => 'rgba(107, 114, 128, 0.8)',
948 + ];
930 949 if (isset($invoice_status['percentages']) && is_array($invoice_status['percentages'])) {
931 950 foreach ($invoice_status['percentages'] as $status => $percentage) {
932 - $status_labels[] = ucfirst($status);
951 + $status_labels[] = $ei_status_names[$status] ?? ucfirst($status);
933 952 $status_data[] = $percentage;
953 + $status_rgba[] = $ei_overview_colors[$status] ?? 'rgba(156, 163, 175, 0.8)';
934 954 }
935 955 }
936 956 echo json_encode($status_labels);
937 957 ?>;
@@ -939,17 +959,10 @@
939 959 const statusData = <?php
940 960 echo json_encode($status_data);
941 961 ?>;
942 962
963 + const statusColors = <?php echo json_encode($status_rgba); ?>;
943 964
944 -
945 - const statusColors = [
946 - 'rgba(34, 197, 94, 0.8)', // Green for Paid
947 - 'rgba(234, 179, 8, 0.8)', // Yellow for Unpaid
948 - 'rgba(239, 68, 68, 0.8)', // Red for Overdue
949 - 'rgba(209, 213, 219, 0.8)' // Gray for Draft
950 - ];
951 -
952 965 try {
953 966 if (statusLabels.length === 0 || statusData.length === 0) {
954 967 return;
955 968 }
@@ -1066,33 +1079,25 @@
1066 1079 if (document.getElementById('invoice-status-chart')) {
1067 1080 const invoiceStatusData = <?php
1068 1081 $statuses = [];
1069 1082 $statusCounts = [];
1083 + // Colours keyed by status so a slice keeps its colour whichever statuses are present.
1070 1084 $statusChartColors = [
1071 - 'rgba(34, 197, 94, 0.8)', // Green for Paid
1072 - 'rgba(234, 179, 8, 0.8)', // Yellow for Unpaid
1073 - 'rgba(239, 68, 68, 0.8)', // Red for Overdue
1074 - 'rgba(209, 213, 219, 0.8)', // Gray for Draft
1075 - 'rgba(107, 114, 128, 0.8)', // Dark Gray for Canceled
1076 - 'rgba(156, 163, 175, 0.8)' // Medium Gray for Other
1085 + 'paid' => 'rgba(34, 197, 94, 0.8)',
1086 + 'partial' => 'rgba(59, 130, 246, 0.8)',
1087 + 'unpaid' => 'rgba(234, 179, 8, 0.8)',
1088 + 'overdue' => 'rgba(239, 68, 68, 0.8)',
1089 + 'draft' => 'rgba(209, 213, 219, 0.8)',
1090 + 'canceled' => 'rgba(107, 114, 128, 0.8)',
1091 + 'other' => 'rgba(156, 163, 175, 0.8)',
1077 1092 ];
1078 -
1079 - $status_list = [
1080 - 'paid' => 'Paid',
1081 - 'unpaid' => 'Unpaid',
1082 - 'overdue' => 'Overdue',
1083 - 'draft' => 'Draft',
1084 - 'canceled' => 'Canceled',
1085 - 'other' => 'Other'
1086 - ];
1087 -
1088 - $i = 0;
1093 + $statusColors = [];
1089 1094 foreach ($status_list as $status_key => $status_label) {
1090 1095 $status_data = $invoice_report['by_status'][$status_key] ?? ['count' => 0];
1091 1096 if ($status_data['count'] > 0) {
1092 1097 $statuses[] = $status_label;
1093 1098 $statusCounts[] = $status_data['count'];
1094 - $i++;
1099 + $statusColors[] = $statusChartColors[$status_key] ?? 'rgba(156, 163, 175, 0.8)';
1095 1100 }
1096 1101 }
1097 1102
1098 1103 echo json_encode([
@@ -1097,15 +1102,15 @@
1097 1102
1098 1103 echo json_encode([
1099 1104 'labels' => $statuses,
1100 1105 'data' => $statusCounts,
1101 - 'colors' => array_slice($statusChartColors, 0, $i)
1106 + 'colors' => $statusColors
1102 1107 ]);
1103 1108 ?>;
1104 1109
1105 1110 if (invoiceStatusData.labels && invoiceStatusData.labels.length > 0) {
1106 1111 const invoiceStatusCtx = document.getElementById('invoice-status-chart').getContext('2d');
1107 - new Chart(invoiceStatusCtx, {
1112 + window.easyInvoiceStatusChart = new Chart(invoiceStatusCtx, {
1108 1113 type: 'pie',
1109 1114 data: {
1110 1115 labels: invoiceStatusData.labels,
1111 1116 datasets: [{
@@ -1198,8 +1203,12 @@
1198 1203 setTimeout(() => {
1199 1204 targetTab.classList.remove('hidden');
1200 1205 targetTab.classList.add('block');
1201 1206 targetTab.style.display = 'block';
1207 + // The chart was built while its tab was display:none (zero size); size it now.
1208 + if (targetId === 'invoice-report-content' && window.easyInvoiceStatusChart) {
1209 + window.easyInvoiceStatusChart.resize();
1210 + }
1202 1211 }, 10);
1203 1212 } else {
1204 1213 // Fallback to payment report if target not found
1205 1214 if (paymentReportTab) {
@@ -1234,16 +1243,16 @@
1234 1243 // Add summary stats
1235 1244 csvContent += 'Summary Statistics\r\n';
1236 1245 <?php if (isset($summary_stats['total_revenue']) && is_array($summary_stats['total_revenue']) && !empty($summary_stats['total_revenue'])): ?>
1237 1246 <?php foreach ($summary_stats['total_revenue'] as $currency => $data): ?>
1238 - csvContent += 'Total Revenue (<?php echo $currency; ?>),<?php echo $data['symbol'] . number_format($data['amount'], 2); ?>\r\n';
1247 + csvContent += 'Total Revenue (<?php echo esc_js($currency); ?>),<?php echo esc_js($data['symbol'] . number_format($data['amount'], 2)); ?>\r\n';
1239 1248 <?php endforeach; ?>
1240 1249 <?php else: ?>
1241 1250 csvContent += 'Total Revenue,$0.00\r\n';
1242 1251 <?php endif; ?>
1243 - csvContent += 'Total Invoices,<?php echo isset($summary_stats['total_invoices']) ? $summary_stats['total_invoices'] : 0; ?>\r\n';
1244 - csvContent += 'Active Clients,<?php echo isset($summary_stats['active_clients']) ? $summary_stats['active_clients'] : 0; ?>\r\n';
1245 - csvContent += 'Average Payment Time,<?php echo isset($summary_stats['avg_payment_time']) ? $summary_stats['avg_payment_time'] : 0; ?> days\r\n\r\n';
1252 + csvContent += 'Total Invoices,<?php echo esc_js(isset($summary_stats['total_invoices']) ? $summary_stats['total_invoices'] : 0); ?>\r\n';
1253 + csvContent += 'Active Clients,<?php echo esc_js(isset($summary_stats['active_clients']) ? $summary_stats['active_clients'] : 0); ?>\r\n';
1254 + csvContent += 'Average Payment Time,<?php echo esc_js(isset($summary_stats['avg_payment_time']) ? $summary_stats['avg_payment_time'] : 0); ?> days\r\n\r\n';
1246 1255
1247 1256 // Add monthly revenue
1248 1257 csvContent += 'Monthly Revenue\r\n';
1249 1258 csvContent += 'Month,USD,EUR,Other Currencies\r\n';
@@ -1261,9 +1270,9 @@
1261 1270 }
1262 1271 }
1263 1272 $other_text = !empty($other_currencies) ? implode('; ', $other_currencies) : '';
1264 1273 ?>
1265 - csvContent += '<?php echo $month; ?>,$<?php echo number_format($usd_amount, 2); ?>,€<?php echo number_format($eur_amount, 2); ?>,"<?php echo addslashes($other_text); ?>"\r\n';
1274 + csvContent += '<?php echo esc_js($month); ?>,$<?php echo esc_js(number_format($usd_amount, 2)); ?>,€<?php echo esc_js(number_format($eur_amount, 2)); ?>,"<?php echo esc_js($other_text); ?>"\r\n';
1266 1275 <?php endforeach; ?>
1267 1276 <?php endif; ?>
1268 1277 csvContent += '\r\n';
1269 1278
@@ -1272,9 +1281,9 @@
1272 1281 csvContent += 'Status,Percentage,Count\r\n';
1273 1282 <?php if (isset($invoice_status['percentages']) && is_array($invoice_status['percentages'])): ?>
1274 1283 <?php foreach ($invoice_status['percentages'] as $status => $percentage): ?>
1275 1284 <?php $count = isset($invoice_status['counts'][$status]) ? $invoice_status['counts'][$status] : 0; ?>
1276 - csvContent += '<?php echo ucfirst($status); ?>,<?php echo $percentage; ?>%,<?php echo $count; ?>\r\n';
1285 + csvContent += '<?php echo esc_js(ucfirst($status)); ?>,<?php echo esc_js($percentage); ?>%,<?php echo esc_js($count); ?>\r\n';
1277 1286 <?php endforeach; ?>
1278 1287 <?php endif; ?>
1279 1288 csvContent += '\r\n';
1280 1289
@@ -1297,11 +1306,11 @@
1297 1306 $other_text = !empty($other_currencies) ? implode('; ', $other_currencies) : '';
1298 1307 $client_name = isset($client['name']) ? $client['name'] : 'Unknown';
1299 1308 $client_email = isset($client['email']) ? $client['email'] : '';
1300 1309 $total_invoices = isset($client['total_invoices']) ? $client['total_invoices'] : 0;
1301 - $last_invoice = isset($client['last_invoice']) && !empty($client['last_invoice']) ? date('Y-m-d', strtotime($client['last_invoice'])) : 'N/A';
1310 + $last_invoice = isset($client['last_invoice']) && !empty($client['last_invoice']) ? gmdate('Y-m-d', strtotime($client['last_invoice'])) : 'N/A';
1302 1311 ?>
1303 - csvContent += '<?php echo addslashes($client_name); ?>,<?php echo addslashes($client_email); ?>,$<?php echo number_format($usd_amount, 2); ?>,€<?php echo number_format($eur_amount, 2); ?>,"<?php echo addslashes($other_text); ?>",<?php echo $total_invoices; ?>,<?php echo $last_invoice; ?>\r\n';
1312 + csvContent += '<?php echo esc_js($client_name); ?>,<?php echo esc_js($client_email); ?>,$<?php echo esc_js(number_format($usd_amount, 2)); ?>,€<?php echo esc_js(number_format($eur_amount, 2)); ?>,"<?php echo esc_js($other_text); ?>",<?php echo esc_js($total_invoices); ?>,<?php echo esc_js($last_invoice); ?>\r\n';
1304 1313 <?php endforeach; ?>
1305 1314 <?php endif; ?>
1306 1315
1307 1316 // Create download link