'; break; case 'error': $bg_class = 'bg-red-50'; $text_class = 'text-red-800'; $icon = ''; break; case 'warning': $bg_class = 'bg-yellow-50'; $text_class = 'text-yellow-800'; $icon = ''; break; default: $bg_class = 'bg-blue-50'; $text_class = 'text-blue-800'; $icon = ''; } echo '
'; echo '
'; echo '
' . wp_kses_post($icon) . '
'; echo '
'; echo '

' . esc_html($message) . '

'; echo '
'; echo '
'; echo '
'; echo ''; echo '
'; echo '
'; echo '
'; echo '
'; } } // One-time fix: Update all existing draft invoices to published status for proper permalinks if (!get_option('easy_invoice_draft_invoices_fixed', false)) { $invoice_repository = \EasyInvoice\Providers\InvoiceServiceProvider::getInvoiceRepository(); $updated_count = $invoice_repository->updateAllExistingInvoices(); if ($updated_count > 0) { // Show a one-time notification add_action('admin_notices', function() use ($updated_count) { echo '
'; /* translators: %d: number updated. */ echo '

' . sprintf(esc_html__('%d draft invoices updated to published status for proper permalinks.', 'easy-invoice'), (int) $updated_count) . '

'; echo '
'; }); } // Mark as fixed update_option('easy_invoice_draft_invoices_fixed', true); } // Get current view (all, trash) $current_view = isset($_GET['view']) ? sanitize_text_field($_GET['view']) : 'all'; // Get status filter $status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : ''; // Get recurring filter $recurring_filter = isset($_GET['recurring']) ? sanitize_text_field($_GET['recurring']) : ''; // Get client filter (from template data, falls back to GET) $client_filter = isset($client_filter) ? (int) $client_filter : (isset($_GET['client_id']) ? absint($_GET['client_id']) : 0); $clients_list = isset($clients_list) && is_array($clients_list) ? $clients_list : []; // Check for bulk action notifications $notification = false; if (isset($_GET['bulk_trashed']) && $_GET['bulk_trashed'] > 0) { $count = intval($_GET['bulk_trashed']); /* translators: %s: number of items. */ easy_invoice_display_notification('success', sprintf(_n('%s invoice moved to trash.', '%s invoices moved to trash.', $count, 'easy-invoice'), $count)); $notification = true; } if (isset($_GET['bulk_restored']) && $_GET['bulk_restored'] > 0) { $count = intval($_GET['bulk_restored']); /* translators: %s: number of items. */ easy_invoice_display_notification('success', sprintf(_n('%s invoice restored from trash.', '%s invoices restored from trash.', $count, 'easy-invoice'), $count)); $notification = true; } if (isset($_GET['bulk_deleted']) && $_GET['bulk_deleted'] > 0) { $count = intval($_GET['bulk_deleted']); /* translators: %s: number of items. */ easy_invoice_display_notification('success', sprintf(_n('%s invoice permanently deleted.', '%s invoices permanently deleted.', $count, 'easy-invoice'), $count)); $notification = true; } if (isset($_GET['bulk_drafted']) && $_GET['bulk_drafted'] > 0) { $count = intval($_GET['bulk_drafted']); /* translators: %s: number of items. */ easy_invoice_display_notification('success', sprintf(_n('%s invoice set to draft status.', '%s invoices set to draft status.', $count, 'easy-invoice'), $count)); $notification = true; } if (isset($_GET['bulk_published']) && $_GET['bulk_published'] > 0) { $count = intval($_GET['bulk_published']); /* translators: %s: number of items. */ easy_invoice_display_notification('success', sprintf(_n('%s invoice published successfully.', '%s invoices published successfully.', $count, 'easy-invoice'), $count)); $notification = true; } if (isset($_GET['bulk_error'])) { $error = sanitize_text_field($_GET['bulk_error']); $error_message = 'An error occurred while processing the bulk action.'; if ($error === 'no_selection') { $error_message = 'Please select at least one invoice to perform this action.'; } elseif ($error === 'invalid_action') { $error_message = 'Please select a valid bulk action.'; } easy_invoice_display_notification('error', $error_message); $notification = true; } // Extract invoice data for statistics $formatted_invoices = array(); foreach ($invoices as $invoice) { // For Invoice model objects if (is_object($invoice)) { $formatted_invoices[] = array( 'id' => $invoice->getId(), 'amount' => $invoice->getTotal(), 'status' => $invoice->getStatus(), ); } else { // For already formatted invoice arrays $formatted_invoices[] = array( 'id' => $invoice['id'], 'amount' => isset($invoice['total_amount']) ? $invoice['total_amount'] : $invoice['amount'], 'status' => $invoice['status'], ); } } // Get stats from all invoices, not just the current page $invoice_controller = new \EasyInvoice\Controllers\InvoiceController(); $stats = $invoice_controller->getInvoiceStats(); // The controller passes the real counts (found_posts and SQL counts); the // template used to overwrite the total with the size of the current page and // load every trashed / draft invoice as a model just to count them. $all_count = (int) ($stats['total_invoices'] ?? 0); if (!isset($trash_count)) { $trash_count = (int) $repository->count(['post_status' => 'trash']); } if (!isset($draft_count)) { $draft_count = (int) $repository->count(['meta_key' => '_easy_invoice_status', 'meta_value' => 'draft']); // phpcs:ignore WordPress.DB.SlowDBQuery } // Define available status filters // The statuses an invoice can actually hold — "Pending" was offered but no // invoice is ever in that state, while "Available" (every new invoice) was not. $status_filters = array( 'available' => __('Available', 'easy-invoice'), 'paid' => __('Paid', 'easy-invoice'), 'partial' => __('Partially paid', 'easy-invoice'), 'unpaid' => __('Unpaid', 'easy-invoice'), 'overdue' => __('Overdue', 'easy-invoice'), 'draft' => __('Draft', 'easy-invoice'), 'cancelled' => __('Cancelled', 'easy-invoice'), ); ?>

$data): ?> setCurrencyCode($currency); $formatter = new \EasyInvoice\Helpers\InvoiceFormatter($temp_invoice); ?>

format($data['amount'])); ?>

$0.00

$data): ?>
( 1 ? 'invoices' : 'invoice'; ?>)

$0.00

0): ?>
Showing to of results
1): ?>
Page of add_query_arg('paged', '%#%'), 'format' => '', 'current' => $current_page, 'total' => $total_pages, 'prev_text' => '‹ Previous', 'next_text' => 'Next ›', 'type' => 'array', 'end_size' => 1, 'mid_size' => 2, 'add_args' => array( 'view' => $current_view, 'status' => $status_filter, 'recurring' => $recurring_filter, 'client_id' => $client_filter ?: false, ) ); $top_pagination_links = paginate_links($top_pagination_args); if ($top_pagination_links) { echo '
'; foreach ($top_pagination_links as $link) { // Add null check to prevent passing null to easy_invoice_str_replace if ($link === null) { continue; } // Convert WordPress pagination to our styling $link = easy_invoice_str_replace('page-numbers', 'px-3 py-2 text-sm font-medium border border-gray-300 rounded-md', $link); $link = easy_invoice_str_replace('current', 'bg-indigo-50 border-indigo-500 text-indigo-600', $link); $link = easy_invoice_str_replace('prev', 'bg-white text-gray-500 hover:bg-gray-50', $link); $link = easy_invoice_str_replace('next', 'bg-white text-gray-500 hover:bg-gray-50', $link); $link = easy_invoice_str_replace('dots', 'bg-white text-gray-700', $link); // Add default styling for regular page numbers if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) { $link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link); } echo wp_kses_post($link); } echo '
'; } ?>
All 'bg-blue-100 text-blue-800 ring-1 ring-blue-300', 'paid' => 'bg-green-100 text-green-800 ring-1 ring-green-300', 'partial' => 'bg-yellow-100 text-yellow-800 ring-1 ring-yellow-300', 'unpaid' => 'bg-yellow-100 text-yellow-800 ring-1 ring-yellow-300', 'pending' => 'bg-blue-100 text-blue-800 ring-1 ring-blue-300', 'draft' => 'bg-gray-100 text-gray-800 ring-1 ring-gray-300', 'overdue' => 'bg-red-100 text-red-800 ring-1 ring-red-300', 'cancelled' => 'bg-gray-100 text-gray-800 ring-1 ring-gray-300' ]; foreach ($status_filters as $status_key => $status_label): $is_current = $status_filter === $status_key; $base_class = 'px-3 py-1 rounded-full text-xs font-medium transition-colors duration-200'; if ($is_current) { $color_class = $status_colors[$status_key] ?? 'bg-indigo-100 text-indigo-800 ring-1 ring-indigo-300'; } else { $color_class = 'bg-gray-50 text-gray-700 hover:bg-gray-100'; } // Build URL with status filter $status_url = $base_url . '&status=' . urlencode($status_key); ?>
getId(); $invoice_number = $invoice->getNumber(); $invoice_title = $invoice->getTitle(); // Get client name directly from client repository $client_name = 'No Client'; if ($invoice->getClientId() > 0) { $client_repository = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository(); $client = $client_repository->find($invoice->getClientId()); if ($client) { $client_name = $client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName()); if (empty(trim($client_name))) { $client_name = $client->getDisplayName() ?: 'Client ' . $client->getId(); } } } elseif ($invoice->getCustomerName()) { $client_name = $invoice->getCustomerName(); } $client_email = $invoice->getCustomerEmail(); $status = $invoice->getStatus(); // Use formatter to display amount properly $formatter = new \EasyInvoice\Helpers\InvoiceFormatter($invoice); $amount = $formatter->format($invoice->getTotal()); $due_date = $invoice->getDueDate(); } else { $invoice_id = $invoice['id']; $invoice_number = $invoice['invoice_number']; $invoice_title = isset($invoice['title']) ? $invoice['title'] : $invoice_number; $client_name = $invoice['client_name']; $client_email = isset($invoice['client_email']) ? $invoice['client_email'] : ''; $status = $invoice['status']; $amount = $invoice['total_amount']; $due_date = $invoice['due_date']; } // Format the display title $display_title = $invoice_title ?: 'Untitled Invoice'; // An invoice still owed after its due date reads as overdue // whatever its stored status says (nothing flips the status // by itself); the underlying status stays for the filters. $status = \EasyInvoice\Helpers\InvoiceStatus::displayKey((string) $status, $due_date ? (string) $due_date : null); $status_class = 'available' === $status ? 'bg-gray-100 text-gray-800 ring-1 ring-gray-200' : \EasyInvoice\Helpers\InvoiceStatus::badgeClass($status); ?>
Export Duplicate $action_html) { echo '' . wp_kses_post($action_html) . ''; } ?>
getStatus(), ['available', 'unpaid', 'overdue'], true)) : // paid, part-paid and cancelled invoices are not un-issued ?> getStatus()), ['available', 'unpaid', 'overdue'], true)) : // paid, part-paid and cancelled invoices are not un-issued ?> Export Duplicate $action_html) { echo '' . wp_kses_post($action_html) . ''; } ?>
' . esc_html__('Create your first invoice', 'easy-invoice') . ''; ?>
0): ?>
Showing to of results
1): ?>
Page of add_query_arg('paged', '%#%'), 'format' => '', 'current' => $current_page, 'total' => $total_pages, 'prev_text' => '‹ Previous', 'next_text' => 'Next ›', 'type' => 'array', 'end_size' => 1, 'mid_size' => 2, 'add_args' => array( 'view' => $current_view, 'status' => $status_filter, 'recurring' => $recurring_filter ) ); $bottom_pagination_links = paginate_links($bottom_pagination_args); if ($bottom_pagination_links) { echo '
'; foreach ($bottom_pagination_links as $link) { // Add null check to prevent passing null to easy_invoice_str_replace if ($link === null) { continue; } // Convert WordPress pagination to our styling $link = easy_invoice_str_replace('page-numbers', 'px-3 py-2 text-sm font-medium border border-gray-300 rounded-md', $link); $link = easy_invoice_str_replace('current', 'bg-indigo-50 border-indigo-500 text-indigo-600', $link); $link = easy_invoice_str_replace('prev', 'bg-white text-gray-500 hover:bg-gray-50', $link); $link = easy_invoice_str_replace('next', 'bg-white text-gray-500 hover:bg-gray-50', $link); $link = easy_invoice_str_replace('dots', 'bg-white text-gray-700', $link); // Add default styling for regular page numbers if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) { $link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link); } echo wp_kses_post($link); } echo '
'; } ?>
admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('easy_invoice_nonce') ); ?>