| @@ -30,16 +30,16 @@ | ||
| 30 | 30 | <div class="p-8"> |
| 31 | 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;"> |
| 32 | 32 | <div class="flex items-center justify-between"> |
| 33 | 33 | <div> |
| 34 | - <h1 class="text-2xl font-bold text-gray-900">Reports</h1> | |
| 35 | - <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> | |
| 36 | 36 | </div> |
| 37 | 37 | <div class="flex items-center space-x-3"> |
| 38 | - <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"> | |
| 39 | 39 | <input type="hidden" name="page" value="easy-invoice-reports"> |
| 40 | 40 | <div class="flex items-center"> |
| 41 | - <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> | |
| 42 | 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"> |
| 43 | 43 | </div> |
| 44 | 44 | <div class="flex items-center"> |
| 45 | 45 | <label for="end_date" class="mr-2 text-sm font-medium text-gray-700">To:</label> |
| @@ -65,9 +65,9 @@ | ||
| 65 | 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> |
| 66 | 66 | </svg> |
| 67 | 67 | </div> |
| 68 | 68 | <div> |
| 69 | - <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> | |
| 70 | 70 | <?php if (is_array($summary_stats['total_revenue']) && !empty($summary_stats['total_revenue'])): ?> |
| 71 | 71 | <div class="mt-1"> |
| 72 | 72 | <?php |
| 73 | 73 | $currency_count = count($summary_stats['total_revenue']); |
| @@ -76,10 +76,10 @@ | ||
| 76 | 76 | // Get currency code from the data array or use a fallback |
| 77 | 77 | $currency_code = isset($data['currency_code']) ? $data['currency_code'] : 'USD'; |
| 78 | 78 | ?> |
| 79 | 79 | <div class="<?php echo $currency_count > 1 ? 'text-sm' : 'text-lg'; ?> font-bold text-gray-900 <?php echo !$is_last ? 'mb-1' : ''; ?>"> |
| 80 | - <?php echo $data['symbol'] . number_format($data['amount'], 2); ?> | |
| 81 | - <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> | |
| 82 | 82 | </div> |
| 83 | 83 | <?php endforeach; ?> |
| 84 | 84 | </div> |
| 85 | 85 | <?php else: ?> |
| @@ -96,9 +96,9 @@ | ||
| 96 | 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> |
| 97 | 97 | </svg> |
| 98 | 98 | </div> |
| 99 | 99 | <div> |
| 100 | - <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> | |
| 101 | 101 | <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo esc_html($summary_stats['total_invoices']); ?></p> |
| 102 | 102 | </div> |
| 103 | 103 | </div> |
| 104 | 104 | </div> |
| @@ -113,9 +113,9 @@ | ||
| 113 | 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"/> |
| 114 | 114 | </svg> |
| 115 | 115 | </div> |
| 116 | 116 | <div> |
| 117 | - <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> | |
| 118 | 118 | <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo esc_html($summary_stats['active_clients']); ?></p> |
| 119 | 119 | </div> |
| 120 | 120 | </div> |
| 121 | 121 | </div> |
| @@ -127,10 +127,10 @@ | ||
| 127 | 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> |
| 128 | 128 | </svg> |
| 129 | 129 | </div> |
| 130 | 130 | <div> |
| 131 | - <h3 class="text-sm font-medium text-gray-500">Average Payment Time</h3> | |
| 132 | - <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'] ? '—' : esc_html($summary_stats['avg_payment_time']) . ' ' . esc_html__('days', 'easy-invoice'); ?></p> | |
| 133 | 133 | </div> |
| 134 | 134 | </div> |
| 135 | 135 | </div> |
| 136 | 136 | </div> |
| @@ -136,9 +136,9 @@ | ||
| 136 | 136 | </div> |
| 137 | 137 | |
| 138 | 138 | <div class="grid grid-cols-1 gap-5 lg:grid-cols-2 mb-8"> |
| 139 | 139 | <div class="bg-white shadow rounded-lg p-6"> |
| 140 | - <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> | |
| 141 | 141 | <div class="h-64"> |
| 142 | 142 | <canvas id="revenue-chart"></canvas> |
| 143 | 143 | </div> |
| 144 | 144 | </div> |
| @@ -143,9 +143,9 @@ | ||
| 143 | 143 | </div> |
| 144 | 144 | </div> |
| 145 | 145 | |
| 146 | 146 | <div class="bg-white shadow rounded-lg p-6"> |
| 147 | - <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> | |
| 148 | 148 | <div class="h-64 flex items-center justify-center"> |
| 149 | 149 | <div class="w-48 h-48 relative"> |
| 150 | 150 | <canvas id="status-chart"></canvas> |
| 151 | 151 | </div> |
| @@ -152,12 +152,18 @@ | ||
| 152 | 152 | <div class="ml-8"> |
| 153 | 153 | <?php |
| 154 | 154 | $statusColors = [ |
| 155 | 155 | 'Paid' => 'bg-green-500', |
| 156 | + 'Partial' => 'bg-blue-500', | |
| 156 | 157 | 'Unpaid' => 'bg-yellow-500', |
| 157 | 158 | 'Overdue' => 'bg-red-500', |
| 158 | - 'Draft' => 'bg-gray-500' | |
| 159 | + 'Draft' => 'bg-gray-300', | |
| 160 | + 'Canceled' => 'bg-gray-500' | |
| 159 | 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 | + ]; | |
| 160 | 166 | |
| 161 | 167 | if (isset($invoice_status['percentages']) && isset($invoice_status['counts'])) { |
| 162 | 168 | foreach ($invoice_status['percentages'] as $status => $percentage) { |
| 163 | 169 | $color = isset($statusColors[ucfirst($status)]) ? $statusColors[ucfirst($status)] : 'bg-gray-500'; |
| @@ -163,9 +169,9 @@ | ||
| 163 | 169 | $color = isset($statusColors[ucfirst($status)]) ? $statusColors[ucfirst($status)] : 'bg-gray-500'; |
| 164 | 170 | $count = isset($invoice_status['counts'][$status]) ? $invoice_status['counts'][$status] : 0; |
| 165 | 171 | echo '<div class="flex items-center mb-2">'; |
| 166 | 172 | echo '<div class="w-4 h-4 rounded-full ' . esc_attr($color) . ' mr-2"></div>'; |
| 167 | - 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>'; | |
| 168 | 174 | echo '</div>'; |
| 169 | 175 | } |
| 170 | 176 | } |
| 171 | 177 | ?> |
| @@ -175,9 +181,9 @@ | ||
| 175 | 181 | </div> |
| 176 | 182 | |
| 177 | 183 | <div class="bg-white shadow rounded-lg mb-8"> |
| 178 | 184 | <div class="px-6 py-4 border-b border-gray-200"> |
| 179 | - <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> | |
| 180 | 186 | </div> |
| 181 | 187 | <div class="overflow-x-auto"> |
| 182 | 188 | <table class="min-w-full divide-y divide-gray-200"> |
| 183 | 189 | <thead class="bg-gray-50"> |
| @@ -228,10 +234,10 @@ | ||
| 228 | 234 | <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
| 229 | 235 | <?php if (isset($client['total_amount']) && is_array($client['total_amount']) && !empty($client['total_amount'])): ?> |
| 230 | 236 | <?php foreach ($client['total_amount'] as $currency => $currency_data): ?> |
| 231 | 237 | <div> |
| 232 | - <?php echo $currency_data['symbol'] . number_format($currency_data['amount'], 2); ?> | |
| 233 | - <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> | |
| 234 | 240 | </div> |
| 235 | 241 | <?php endforeach; ?> |
| 236 | 242 | <?php else: ?> |
| 237 | 243 | $0.00 |
| @@ -240,9 +246,9 @@ | ||
| 240 | 246 | <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
| 241 | 247 | <?php echo esc_html($client['total_invoices']); ?> invoices |
| 242 | 248 | </td> |
| 243 | 249 | <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
| 244 | - <?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'); ?> | |
| 245 | 251 | </td> |
| 246 | 252 | |
| 247 | 253 | |
| 248 | 254 | <td class="px-6 py-4 whitespace-nowrap text-sm font-medium"> |
| @@ -251,9 +257,9 @@ | ||
| 251 | 257 | </tr> |
| 252 | 258 | <?php endforeach; ?> |
| 253 | 259 | <?php else: ?> |
| 254 | 260 | <tr> |
| 255 | - <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> | |
| 256 | 262 | </tr> |
| 257 | 263 | <?php endif; ?> |
| 258 | 264 | </tbody> |
| 259 | 265 | </table> |
| @@ -259,12 +265,25 @@ | ||
| 259 | 265 | </table> |
| 260 | 266 | </div> |
| 261 | 267 | </div> |
| 262 | 268 | |
| 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 | + | |
| 263 | 282 | <div class="bg-white shadow rounded-lg"> |
| 264 | 283 | <div class="border-b border-gray-200"> |
| 265 | 284 | <div class="flex items-center justify-between px-6 py-3"> |
| 266 | - <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> | |
| 267 | 286 | <div> |
| 268 | 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"> |
| 269 | 288 | <i class="fas fa-download mr-2"></i> Export |
| 270 | 289 | </button> |
| @@ -283,13 +302,13 @@ | ||
| 283 | 302 | <div id="payment-report-content" class="tab-content block" style="display: block !important;"> |
| 284 | 303 | <div class="p-4 bg-gray-50 border-b"> |
| 285 | 304 | <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> |
| 286 | 305 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 287 | - <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> | |
| 288 | 307 | <div class="text-xl font-bold"><?php echo esc_html($payment_report['count']); ?></div> |
| 289 | 308 | </div> |
| 290 | 309 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 291 | - <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> | |
| 292 | 311 | <div class="text-lg font-bold"> |
| 293 | 312 | <?php |
| 294 | 313 | // Calculate total amount from all payment methods and currencies |
| 295 | 314 | $total_amounts_by_currency = []; |
| @@ -311,10 +330,10 @@ | ||
| 311 | 330 | |
| 312 | 331 | if (!empty($total_amounts_by_currency)): ?> |
| 313 | 332 | <?php foreach ($total_amounts_by_currency as $currency => $data): ?> |
| 314 | 333 | <div class="text-sm"> |
| 315 | - <?php echo $data['symbol'] . number_format($data['amount'], 2); ?> | |
| 316 | - <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> | |
| 317 | 336 | </div> |
| 318 | 337 | <?php endforeach; ?> |
| 319 | 338 | <?php else: ?> |
| 320 | 339 | $0.00 |
| @@ -321,9 +340,9 @@ | ||
| 321 | 340 | <?php endif; ?> |
| 322 | 341 | </div> |
| 323 | 342 | </div> |
| 324 | 343 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 325 | - <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> | |
| 326 | 345 | <div class="text-lg font-bold"> |
| 327 | 346 | <?php |
| 328 | 347 | // Calculate average payments by currency |
| 329 | 348 | $average_amounts_by_currency = []; |
| @@ -352,9 +371,9 @@ | ||
| 352 | 371 | |
| 353 | 372 | if (!empty($average_amounts_by_currency)): ?> |
| 354 | 373 | <?php foreach ($average_amounts_by_currency as $currency => $data): ?> |
| 355 | 374 | <div class="text-sm"> |
| 356 | - <?php echo $data['symbol'] . number_format($data['amount'], 2); ?> | |
| 375 | + <?php echo esc_html(easy_invoice_format_money($data['amount'], null, (string) $currency)); ?> | |
| 357 | 376 | <span class="text-xs text-gray-500"> |
| 358 | 377 | <?php |
| 359 | 378 | $display_currency = $currency; |
| 360 | 379 | if ($display_currency === 'GLOBAL') { |
| @@ -365,9 +384,9 @@ | ||
| 365 | 384 | </span> |
| 366 | 385 | </div> |
| 367 | 386 | <?php endforeach; ?> |
| 368 | 387 | <?php elseif ($payment_count > 0): ?> |
| 369 | - <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> | |
| 370 | 389 | <?php else: ?> |
| 371 | 390 | $0.00 |
| 372 | 391 | <?php endif; ?> |
| 373 | 392 | </div> |
| @@ -375,9 +394,9 @@ | ||
| 375 | 394 | </div> |
| 376 | 395 | </div> |
| 377 | 396 | |
| 378 | 397 | <div class="p-4"> |
| 379 | - <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> | |
| 380 | 399 | <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| 381 | 400 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 382 | 401 | <canvas id="payment-methods-chart" height="200"></canvas> |
| 383 | 402 | </div> |
| @@ -384,11 +403,11 @@ | ||
| 384 | 403 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 385 | 404 | <table class="min-w-full divide-y divide-gray-200"> |
| 386 | 405 | <thead> |
| 387 | 406 | <tr> |
| 388 | - <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Method</th> | |
| 389 | - <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Count</th> | |
| 390 | - <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> | |
| 391 | 410 | </tr> |
| 392 | 411 | </thead> |
| 393 | 412 | <tbody> |
| 394 | 413 | <?php |
| @@ -405,10 +424,10 @@ | ||
| 405 | 424 | <td class="px-3 py-2 whitespace-nowrap text-sm text-right text-gray-900"> |
| 406 | 425 | <?php if (isset($data['amounts_by_currency']) && !empty($data['amounts_by_currency'])): ?> |
| 407 | 426 | <?php foreach ($data['amounts_by_currency'] as $currency => $currency_data): ?> |
| 408 | 427 | <div class="text-xs"> |
| 409 | - <?php echo $currency_data['symbol'] . number_format($currency_data['amount'], 2); ?> | |
| 410 | - <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> | |
| 411 | 430 | </div> |
| 412 | 431 | <?php endforeach; ?> |
| 413 | 432 | <?php else: ?> |
| 414 | 433 | $0.00 |
| @@ -419,9 +438,9 @@ | ||
| 419 | 438 | endforeach; |
| 420 | 439 | else: |
| 421 | 440 | ?> |
| 422 | 441 | <tr> |
| 423 | - <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> | |
| 424 | 443 | </tr> |
| 425 | 444 | <?php endif; ?> |
| 426 | 445 | </tbody> |
| 427 | 446 | </table> |
| @@ -429,20 +448,20 @@ | ||
| 429 | 448 | </div> |
| 430 | 449 | </div> |
| 431 | 450 | |
| 432 | 451 | <div class="p-4"> |
| 433 | - <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> | |
| 434 | 453 | <div class="overflow-x-auto"> |
| 435 | 454 | <table class="min-w-full divide-y divide-gray-200"> |
| 436 | 455 | <thead class="bg-gray-50"> |
| 437 | 456 | <tr> |
| 438 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th> | |
| 439 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Invoice</th> | |
| 440 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th> | |
| 441 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Method</th> | |
| 442 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> | |
| 443 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Transaction ID</th> | |
| 444 | - <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> | |
| 445 | 464 | </tr> |
| 446 | 465 | </thead> |
| 447 | 466 | <tbody class="bg-white divide-y divide-gray-200"> |
| 448 | 467 | <?php if (!empty($payment_report['payments'])) : ?> |
| @@ -448,17 +467,21 @@ | ||
| 448 | 467 | <?php if (!empty($payment_report['payments'])) : ?> |
| 449 | 468 | <?php foreach ($payment_report['payments'] as $payment): ?> |
| 450 | 469 | <tr class="hover:bg-gray-50"> |
| 451 | 470 | <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500"> |
| 452 | - <?php echo date('M d, Y', strtotime($payment['date'])); ?> | |
| 471 | + <?php echo esc_html(gmdate('M d, Y', strtotime($payment['date']))); ?> | |
| 453 | 472 | </td> |
| 454 | 473 | <td class="px-4 py-3 whitespace-nowrap"> |
| 455 | - <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"> | |
| 456 | 476 | <?php echo esc_html($payment['invoice_number']); ?> |
| 457 | 477 | </a> |
| 478 | + <?php else : ?> | |
| 479 | + <span class="text-sm text-gray-500"><?php echo esc_html($payment['invoice_number'] ?: '—'); ?></span> | |
| 480 | + <?php endif; ?> | |
| 458 | 481 | </td> |
| 459 | 482 | <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900"> |
| 460 | - <?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'] ?? ''))); ?> | |
| 461 | 484 | </td> |
| 462 | 485 | <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500"> |
| 463 | 486 | <?php echo esc_html(ucfirst($payment['payment_method'])); ?> |
| 464 | 487 | </td> |
| @@ -486,9 +509,9 @@ | ||
| 486 | 509 | </tr> |
| 487 | 510 | <?php endforeach; ?> |
| 488 | 511 | <?php else: ?> |
| 489 | 512 | <tr> |
| 490 | - <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> | |
| 491 | 514 | </tr> |
| 492 | 515 | <?php endif; ?> |
| 493 | 516 | </tbody> |
| 494 | 517 | </table> |
| @@ -499,13 +522,13 @@ | ||
| 499 | 522 | <div id="invoice-report-content" class="tab-content hidden" style="display: none;"> |
| 500 | 523 | <div class="p-4 bg-gray-50 border-b"> |
| 501 | 524 | <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> |
| 502 | 525 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 503 | - <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> | |
| 504 | 527 | <div class="text-xl font-bold"><?php echo esc_html($invoice_report['count']); ?></div> |
| 505 | 528 | </div> |
| 506 | 529 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 507 | - <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> | |
| 508 | 531 | <div class="text-lg font-bold"> |
| 509 | 532 | <?php |
| 510 | 533 | // Display total amounts by currency |
| 511 | 534 | if (isset($invoice_report['total_amounts_by_currency']) && !empty($invoice_report['total_amounts_by_currency'])): ?> |
| @@ -510,9 +533,9 @@ | ||
| 510 | 533 | // Display total amounts by currency |
| 511 | 534 | if (isset($invoice_report['total_amounts_by_currency']) && !empty($invoice_report['total_amounts_by_currency'])): ?> |
| 512 | 535 | <?php foreach ($invoice_report['total_amounts_by_currency'] as $currency => $currency_data): ?> |
| 513 | 536 | <div class="text-sm"> |
| 514 | - <?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)); ?> | |
| 515 | 538 | <span class="text-xs text-gray-500"> |
| 516 | 539 | <?php |
| 517 | 540 | $display_currency = $currency; |
| 518 | 541 | if ($display_currency === 'GLOBAL') { |
| @@ -528,9 +551,9 @@ | ||
| 528 | 551 | <?php endif; ?> |
| 529 | 552 | </div> |
| 530 | 553 | </div> |
| 531 | 554 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 532 | - <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> | |
| 533 | 556 | <div class="text-lg font-bold"> |
| 534 | 557 | <?php |
| 535 | 558 | // Calculate average amounts by currency |
| 536 | 559 | if (isset($invoice_report['total_amounts_by_currency']) && !empty($invoice_report['total_amounts_by_currency']) && $invoice_report['count'] > 0): ?> |
| @@ -537,9 +560,9 @@ | ||
| 537 | 560 | <?php foreach ($invoice_report['total_amounts_by_currency'] as $currency => $currency_data): ?> |
| 538 | 561 | <div class="text-sm"> |
| 539 | 562 | <?php |
| 540 | 563 | $avg_amount = $currency_data['amount'] / $invoice_report['count']; |
| 541 | - echo $currency_data['symbol'] . number_format($avg_amount, 2); | |
| 564 | + echo esc_html(easy_invoice_format_money($avg_amount, null, (string) $currency)); | |
| 542 | 565 | ?> |
| 543 | 566 | <span class="text-xs text-gray-500"> |
| 544 | 567 | <?php |
| 545 | 568 | $display_currency = $currency; |
| @@ -559,9 +582,9 @@ | ||
| 559 | 582 | </div> |
| 560 | 583 | </div> |
| 561 | 584 | |
| 562 | 585 | <div class="p-4"> |
| 563 | - <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> | |
| 564 | 587 | <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| 565 | 588 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 566 | 589 | <canvas id="invoice-status-chart" height="200"></canvas> |
| 567 | 590 | </div> |
| @@ -568,22 +591,23 @@ | ||
| 568 | 591 | <div class="bg-white p-4 rounded-md shadow-sm"> |
| 569 | 592 | <table class="min-w-full divide-y divide-gray-200"> |
| 570 | 593 | <thead> |
| 571 | 594 | <tr> |
| 572 | - <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> | |
| 573 | - <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Count</th> | |
| 574 | - <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> | |
| 575 | 598 | </tr> |
| 576 | 599 | </thead> |
| 577 | 600 | <tbody> |
| 578 | 601 | <?php |
| 579 | 602 | $status_list = [ |
| 580 | - 'paid' => 'Paid', | |
| 581 | - 'unpaid' => 'Unpaid', | |
| 582 | - 'overdue' => 'Overdue', | |
| 583 | - 'draft' => 'Draft', | |
| 584 | - 'canceled' => 'Canceled', | |
| 585 | - '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') | |
| 586 | 610 | ]; |
| 587 | 611 | |
| 588 | 612 | foreach ($status_list as $status_key => $status_label) : |
| 589 | 613 | $status_data = isset($invoice_report['by_status'][$status_key]) ? $invoice_report['by_status'][$status_key] : ['amount' => 0, 'count' => 0]; |
| @@ -598,9 +622,9 @@ | ||
| 598 | 622 | <td class="px-3 py-2 whitespace-nowrap text-sm text-right text-gray-900"> |
| 599 | 623 | <?php if (isset($status_data['amounts_by_currency']) && !empty($status_data['amounts_by_currency'])): ?> |
| 600 | 624 | <?php foreach ($status_data['amounts_by_currency'] as $currency => $currency_data): ?> |
| 601 | 625 | <div class="text-xs"> |
| 602 | - <?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)); ?> | |
| 603 | 627 | <span class="text-xs text-gray-500"> |
| 604 | 628 | <?php |
| 605 | 629 | $display_currency = $currency; |
| 606 | 630 | if ($display_currency === 'GLOBAL') { |
| @@ -623,20 +647,20 @@ | ||
| 623 | 647 | </div> |
| 624 | 648 | </div> |
| 625 | 649 | |
| 626 | 650 | <div class="p-4"> |
| 627 | - <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> | |
| 628 | 652 | <div class="overflow-x-auto"> |
| 629 | 653 | <table class="min-w-full divide-y divide-gray-200"> |
| 630 | 654 | <thead class="bg-gray-50"> |
| 631 | 655 | <tr> |
| 632 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th> | |
| 633 | - <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> | |
| 634 | 658 | <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Invoice #</th> |
| 635 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Client</th> | |
| 636 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th> | |
| 637 | - <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> | |
| 638 | - <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> | |
| 639 | 663 | </tr> |
| 640 | 664 | </thead> |
| 641 | 665 | <tbody class="bg-white divide-y divide-gray-200"> |
| 642 | 666 | <?php if (!empty($invoice_report['invoices'])) : ?> |
| @@ -642,14 +666,14 @@ | ||
| 642 | 666 | <?php if (!empty($invoice_report['invoices'])) : ?> |
| 643 | 667 | <?php foreach ($invoice_report['invoices'] as $invoice): ?> |
| 644 | 668 | <tr class="hover:bg-gray-50"> |
| 645 | 669 | <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500"> |
| 646 | - <?php echo date('M d, Y', strtotime($invoice['issue_date'])); ?> | |
| 670 | + <?php echo esc_html(gmdate('M d, Y', strtotime($invoice['issue_date']))); ?> | |
| 647 | 671 | </td> |
| 648 | 672 | <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500"> |
| 649 | 673 | <?php |
| 650 | 674 | if (!empty($invoice['due_date']) && $invoice['due_date'] !== '0000-00-00' && $invoice['due_date'] !== '1970-01-01') { |
| 651 | - echo date('M d, Y', strtotime($invoice['due_date'])); | |
| 675 | + echo esc_html(gmdate('M d, Y', strtotime($invoice['due_date']))); | |
| 652 | 676 | } else { |
| 653 | 677 | echo '-'; |
| 654 | 678 | } |
| 655 | 679 | ?> |
| @@ -654,9 +678,9 @@ | ||
| 654 | 678 | } |
| 655 | 679 | ?> |
| 656 | 680 | </td> |
| 657 | 681 | <td class="px-4 py-3 whitespace-nowrap"> |
| 658 | - <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"> | |
| 659 | 683 | <?php echo esc_html($invoice['invoice_number']); ?> |
| 660 | 684 | </a> |
| 661 | 685 | </td> |
| 662 | 686 | <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900"> |
| @@ -675,9 +699,9 @@ | ||
| 675 | 699 | $display_currency = $invoice['currency']; |
| 676 | 700 | if ($display_currency === 'GLOBAL') { |
| 677 | 701 | $display_currency = get_option('easy_invoice_currency_code', 'USD'); |
| 678 | 702 | } |
| 679 | - 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)); ?> | |
| 680 | 704 | <span class="text-xs text-gray-500"><?php echo esc_html(strtoupper($display_currency)); ?></span> |
| 681 | 705 | </td> |
| 682 | 706 | <td class="px-4 py-3 whitespace-nowrap"> |
| 683 | 707 | <?php |
| @@ -685,8 +709,10 @@ | ||
| 685 | 709 | if (in_array($invoice['status'], ['paid', 'completed'])) { |
| 686 | 710 | $status_class = 'bg-green-100 text-green-800'; |
| 687 | 711 | } elseif ($invoice['status'] === 'unpaid') { |
| 688 | 712 | $status_class = 'bg-yellow-100 text-yellow-800'; |
| 713 | + } elseif ($invoice['status'] === 'partial') { | |
| 714 | + $status_class = 'bg-blue-100 text-blue-800'; | |
| 689 | 715 | } elseif ($invoice['status'] === 'overdue') { |
| 690 | 716 | $status_class = 'bg-red-100 text-red-800'; |
| 691 | 717 | } elseif ($invoice['status'] === 'draft') { |
| 692 | 718 | $status_class = 'bg-gray-100 text-gray-800'; |
| @@ -692,19 +718,19 @@ | ||
| 692 | 718 | $status_class = 'bg-gray-100 text-gray-800'; |
| 693 | 719 | } |
| 694 | 720 | ?> |
| 695 | 721 | <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?php echo esc_attr($status_class); ?>"> |
| 696 | - <?php echo esc_html(ucfirst($invoice['status'])); ?> | |
| 722 | + <?php echo esc_html($status_list[$invoice['status']] ?? ucfirst($invoice['status'])); ?> | |
| 697 | 723 | </span> |
| 698 | 724 | </td> |
| 699 | 725 | <td class="px-4 py-3 whitespace-nowrap text-sm font-medium"> |
| 700 | - <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> | |
| 701 | 727 | </td> |
| 702 | 728 | </tr> |
| 703 | 729 | <?php endforeach; ?> |
| 704 | 730 | <?php else: ?> |
| 705 | 731 | <tr> |
| 706 | - <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> | |
| 707 | 733 | </tr> |
| 708 | 734 | <?php endif; ?> |
| 709 | 735 | </tbody> |
| 710 | 736 | </table> |
| @@ -713,9 +739,14 @@ | ||
| 713 | 739 | </div> |
| 714 | 740 | </div> |
| 715 | 741 | </div> |
| 716 | 742 | |
| 717 | -<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 | +?> | |
| 718 | 749 | <script> |
| 719 | 750 | |
| 720 | 751 | |
| 721 | 752 | // Monthly revenue chart |
| @@ -836,9 +867,9 @@ | ||
| 836 | 867 | if (chartContainer) { |
| 837 | 868 | chartContainer.style.display = 'flex'; |
| 838 | 869 | chartContainer.style.alignItems = 'center'; |
| 839 | 870 | chartContainer.style.justifyContent = 'center'; |
| 840 | - 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>'; | |
| 841 | 872 | } |
| 842 | 873 | } |
| 843 | 874 | } else { |
| 844 | 875 | const chartContainer = document.getElementById('revenue-chart'); |
| @@ -845,9 +876,9 @@ | ||
| 845 | 876 | if (chartContainer) { |
| 846 | 877 | chartContainer.style.display = 'flex'; |
| 847 | 878 | chartContainer.style.alignItems = 'center'; |
| 848 | 879 | chartContainer.style.justifyContent = 'center'; |
| 849 | - 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>'; | |
| 850 | 881 | } |
| 851 | 882 | return; |
| 852 | 883 | } |
| 853 | 884 | } |
| @@ -904,12 +935,23 @@ | ||
| 904 | 935 | |
| 905 | 936 | const statusLabels = <?php |
| 906 | 937 | $status_labels = []; |
| 907 | 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 | + ]; | |
| 908 | 949 | if (isset($invoice_status['percentages']) && is_array($invoice_status['percentages'])) { |
| 909 | 950 | foreach ($invoice_status['percentages'] as $status => $percentage) { |
| 910 | - $status_labels[] = ucfirst($status); | |
| 951 | + $status_labels[] = $ei_status_names[$status] ?? ucfirst($status); | |
| 911 | 952 | $status_data[] = $percentage; |
| 953 | + $status_rgba[] = $ei_overview_colors[$status] ?? 'rgba(156, 163, 175, 0.8)'; | |
| 912 | 954 | } |
| 913 | 955 | } |
| 914 | 956 | echo json_encode($status_labels); |
| 915 | 957 | ?>; |
| @@ -917,17 +959,10 @@ | ||
| 917 | 959 | const statusData = <?php |
| 918 | 960 | echo json_encode($status_data); |
| 919 | 961 | ?>; |
| 920 | 962 | |
| 963 | + const statusColors = <?php echo json_encode($status_rgba); ?>; | |
| 921 | 964 | |
| 922 | - | |
| 923 | - const statusColors = [ | |
| 924 | - 'rgba(34, 197, 94, 0.8)', // Green for Paid | |
| 925 | - 'rgba(234, 179, 8, 0.8)', // Yellow for Unpaid | |
| 926 | - 'rgba(239, 68, 68, 0.8)', // Red for Overdue | |
| 927 | - 'rgba(209, 213, 219, 0.8)' // Gray for Draft | |
| 928 | - ]; | |
| 929 | - | |
| 930 | 965 | try { |
| 931 | 966 | if (statusLabels.length === 0 || statusData.length === 0) { |
| 932 | 967 | return; |
| 933 | 968 | } |
| @@ -1044,33 +1079,25 @@ | ||
| 1044 | 1079 | if (document.getElementById('invoice-status-chart')) { |
| 1045 | 1080 | const invoiceStatusData = <?php |
| 1046 | 1081 | $statuses = []; |
| 1047 | 1082 | $statusCounts = []; |
| 1083 | + // Colours keyed by status so a slice keeps its colour whichever statuses are present. | |
| 1048 | 1084 | $statusChartColors = [ |
| 1049 | - 'rgba(34, 197, 94, 0.8)', // Green for Paid | |
| 1050 | - 'rgba(234, 179, 8, 0.8)', // Yellow for Unpaid | |
| 1051 | - 'rgba(239, 68, 68, 0.8)', // Red for Overdue | |
| 1052 | - 'rgba(209, 213, 219, 0.8)', // Gray for Draft | |
| 1053 | - 'rgba(107, 114, 128, 0.8)', // Dark Gray for Canceled | |
| 1054 | - '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)', | |
| 1055 | 1092 | ]; |
| 1056 | - | |
| 1057 | - $status_list = [ | |
| 1058 | - 'paid' => 'Paid', | |
| 1059 | - 'unpaid' => 'Unpaid', | |
| 1060 | - 'overdue' => 'Overdue', | |
| 1061 | - 'draft' => 'Draft', | |
| 1062 | - 'canceled' => 'Canceled', | |
| 1063 | - 'other' => 'Other' | |
| 1064 | - ]; | |
| 1065 | - | |
| 1066 | - $i = 0; | |
| 1093 | + $statusColors = []; | |
| 1067 | 1094 | foreach ($status_list as $status_key => $status_label) { |
| 1068 | 1095 | $status_data = $invoice_report['by_status'][$status_key] ?? ['count' => 0]; |
| 1069 | 1096 | if ($status_data['count'] > 0) { |
| 1070 | 1097 | $statuses[] = $status_label; |
| 1071 | 1098 | $statusCounts[] = $status_data['count']; |
| 1072 | - $i++; | |
| 1099 | + $statusColors[] = $statusChartColors[$status_key] ?? 'rgba(156, 163, 175, 0.8)'; | |
| 1073 | 1100 | } |
| 1074 | 1101 | } |
| 1075 | 1102 | |
| 1076 | 1103 | echo json_encode([ |
| @@ -1075,15 +1102,15 @@ | ||
| 1075 | 1102 | |
| 1076 | 1103 | echo json_encode([ |
| 1077 | 1104 | 'labels' => $statuses, |
| 1078 | 1105 | 'data' => $statusCounts, |
| 1079 | - 'colors' => array_slice($statusChartColors, 0, $i) | |
| 1106 | + 'colors' => $statusColors | |
| 1080 | 1107 | ]); |
| 1081 | 1108 | ?>; |
| 1082 | 1109 | |
| 1083 | 1110 | if (invoiceStatusData.labels && invoiceStatusData.labels.length > 0) { |
| 1084 | 1111 | const invoiceStatusCtx = document.getElementById('invoice-status-chart').getContext('2d'); |
| 1085 | - new Chart(invoiceStatusCtx, { | |
| 1112 | + window.easyInvoiceStatusChart = new Chart(invoiceStatusCtx, { | |
| 1086 | 1113 | type: 'pie', |
| 1087 | 1114 | data: { |
| 1088 | 1115 | labels: invoiceStatusData.labels, |
| 1089 | 1116 | datasets: [{ |
| @@ -1176,8 +1203,12 @@ | ||
| 1176 | 1203 | setTimeout(() => { |
| 1177 | 1204 | targetTab.classList.remove('hidden'); |
| 1178 | 1205 | targetTab.classList.add('block'); |
| 1179 | 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 | + } | |
| 1180 | 1211 | }, 10); |
| 1181 | 1212 | } else { |
| 1182 | 1213 | // Fallback to payment report if target not found |
| 1183 | 1214 | if (paymentReportTab) { |
| @@ -1212,16 +1243,16 @@ | ||
| 1212 | 1243 | // Add summary stats |
| 1213 | 1244 | csvContent += 'Summary Statistics\r\n'; |
| 1214 | 1245 | <?php if (isset($summary_stats['total_revenue']) && is_array($summary_stats['total_revenue']) && !empty($summary_stats['total_revenue'])): ?> |
| 1215 | 1246 | <?php foreach ($summary_stats['total_revenue'] as $currency => $data): ?> |
| 1216 | - 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'; | |
| 1217 | 1248 | <?php endforeach; ?> |
| 1218 | 1249 | <?php else: ?> |
| 1219 | 1250 | csvContent += 'Total Revenue,$0.00\r\n'; |
| 1220 | 1251 | <?php endif; ?> |
| 1221 | - csvContent += 'Total Invoices,<?php echo isset($summary_stats['total_invoices']) ? $summary_stats['total_invoices'] : 0; ?>\r\n'; | |
| 1222 | - csvContent += 'Active Clients,<?php echo isset($summary_stats['active_clients']) ? $summary_stats['active_clients'] : 0; ?>\r\n'; | |
| 1223 | - 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'; | |
| 1224 | 1255 | |
| 1225 | 1256 | // Add monthly revenue |
| 1226 | 1257 | csvContent += 'Monthly Revenue\r\n'; |
| 1227 | 1258 | csvContent += 'Month,USD,EUR,Other Currencies\r\n'; |
| @@ -1239,9 +1270,9 @@ | ||
| 1239 | 1270 | } |
| 1240 | 1271 | } |
| 1241 | 1272 | $other_text = !empty($other_currencies) ? implode('; ', $other_currencies) : ''; |
| 1242 | 1273 | ?> |
| 1243 | - 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'; | |
| 1244 | 1275 | <?php endforeach; ?> |
| 1245 | 1276 | <?php endif; ?> |
| 1246 | 1277 | csvContent += '\r\n'; |
| 1247 | 1278 | |
| @@ -1250,9 +1281,9 @@ | ||
| 1250 | 1281 | csvContent += 'Status,Percentage,Count\r\n'; |
| 1251 | 1282 | <?php if (isset($invoice_status['percentages']) && is_array($invoice_status['percentages'])): ?> |
| 1252 | 1283 | <?php foreach ($invoice_status['percentages'] as $status => $percentage): ?> |
| 1253 | 1284 | <?php $count = isset($invoice_status['counts'][$status]) ? $invoice_status['counts'][$status] : 0; ?> |
| 1254 | - 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'; | |
| 1255 | 1286 | <?php endforeach; ?> |
| 1256 | 1287 | <?php endif; ?> |
| 1257 | 1288 | csvContent += '\r\n'; |
| 1258 | 1289 | |
| @@ -1275,11 +1306,11 @@ | ||
| 1275 | 1306 | $other_text = !empty($other_currencies) ? implode('; ', $other_currencies) : ''; |
| 1276 | 1307 | $client_name = isset($client['name']) ? $client['name'] : 'Unknown'; |
| 1277 | 1308 | $client_email = isset($client['email']) ? $client['email'] : ''; |
| 1278 | 1309 | $total_invoices = isset($client['total_invoices']) ? $client['total_invoices'] : 0; |
| 1279 | - $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'; | |
| 1280 | 1311 | ?> |
| 1281 | - 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'; | |
| 1282 | 1313 | <?php endforeach; ?> |
| 1283 | 1314 | <?php endif; ?> |
| 1284 | 1315 | |
| 1285 | 1316 | // Create download link |