| 1 |
<?php |
| 2 |
// Ensure we're in the correct namespace |
| 3 |
namespace EasyInvoice\Templates\Invoices; |
| 4 |
|
| 5 |
// Exit if accessed directly |
| 6 |
if (!defined('ABSPATH')) { |
| 7 |
exit; |
| 8 |
} |
| 9 |
|
| 10 |
use EasyInvoice\Models\Invoice; |
| 11 |
use EasyInvoice\Models\Client; |
| 12 |
|
| 13 |
// Prevent function redeclaration |
| 14 |
if (!function_exists('easy_invoice_display_notification')) { |
| 15 |
function easy_invoice_display_notification($type, $message) { |
| 16 |
$bg_class = ''; |
| 17 |
$text_class = ''; |
| 18 |
$icon = ''; |
| 19 |
|
| 20 |
switch ($type) { |
| 21 |
case 'success': |
| 22 |
$bg_class = 'bg-green-50'; |
| 23 |
$text_class = 'text-green-800'; |
| 24 |
$icon = '<svg class="h-5 w-5 text-green-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>'; |
| 25 |
break; |
| 26 |
case 'error': |
| 27 |
$bg_class = 'bg-red-50'; |
| 28 |
$text_class = 'text-red-800'; |
| 29 |
$icon = '<svg class="h-5 w-5 text-red-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>'; |
| 30 |
break; |
| 31 |
case 'warning': |
| 32 |
$bg_class = 'bg-yellow-50'; |
| 33 |
$text_class = 'text-yellow-800'; |
| 34 |
$icon = '<svg class="h-5 w-5 text-yellow-400" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>'; |
| 35 |
break; |
| 36 |
default: |
| 37 |
$bg_class = 'bg-blue-50'; |
| 38 |
$text_class = 'text-blue-800'; |
| 39 |
$icon = '<svg class="h-5 w-5 text-blue-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>'; |
| 40 |
} |
| 41 |
|
| 42 |
echo '<div class="rounded-md ' . $bg_class . ' p-4 mb-4">'; |
| 43 |
echo '<div class="flex">'; |
| 44 |
echo '<div class="flex-shrink-0">' . $icon . '</div>'; |
| 45 |
echo '<div class="ml-3">'; |
| 46 |
echo '<p class="text-sm font-medium ' . $text_class . '">' . $message . '</p>'; |
| 47 |
echo '</div>'; |
| 48 |
echo '<div class="ml-auto pl-3">'; |
| 49 |
echo '<div class="-mx-1.5 -my-1.5">'; |
| 50 |
echo '<button type="button" class="inline-flex rounded-md p-1.5 ' . $text_class . ' hover:bg-green-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 notification-dismiss">'; |
| 51 |
echo '<span class="sr-only">Dismiss</span>'; |
| 52 |
echo '<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">'; |
| 53 |
echo '<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />'; |
| 54 |
echo '</svg>'; |
| 55 |
echo '</button>'; |
| 56 |
echo '</div>'; |
| 57 |
echo '</div>'; |
| 58 |
echo '</div>'; |
| 59 |
echo '</div>'; |
| 60 |
} |
| 61 |
} |
| 62 |
|
| 63 |
// One-time fix: Update all existing draft invoices to published status for proper permalinks |
| 64 |
if (!get_option('easy_invoice_draft_invoices_fixed', false)) { |
| 65 |
$invoice_repository = \EasyInvoice\Providers\InvoiceServiceProvider::getInvoiceRepository(); |
| 66 |
$updated_count = $invoice_repository->updateAllExistingInvoices(); |
| 67 |
|
| 68 |
if ($updated_count > 0) { |
| 69 |
// Show a one-time notification |
| 70 |
add_action('admin_notices', function() use ($updated_count) { |
| 71 |
echo '<div class="notice notice-success is-dismissible">'; |
| 72 |
echo '<p>' . sprintf(__('%d draft invoices updated to published status for proper permalinks.', 'easy-invoice'), $updated_count) . '</p>'; |
| 73 |
echo '</div>'; |
| 74 |
}); |
| 75 |
} |
| 76 |
|
| 77 |
// Mark as fixed |
| 78 |
update_option('easy_invoice_draft_invoices_fixed', true); |
| 79 |
} |
| 80 |
|
| 81 |
// Get current view (all, trash) |
| 82 |
$current_view = isset($_GET['view']) ? sanitize_text_field($_GET['view']) : 'all'; |
| 83 |
|
| 84 |
// Get status filter |
| 85 |
$status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : ''; |
| 86 |
|
| 87 |
// Get recurring filter |
| 88 |
$recurring_filter = isset($_GET['recurring']) ? sanitize_text_field($_GET['recurring']) : ''; |
| 89 |
|
| 90 |
// Get client filter (from template data, falls back to GET) |
| 91 |
$client_filter = isset($client_filter) ? (int) $client_filter : (isset($_GET['client_id']) ? absint($_GET['client_id']) : 0); |
| 92 |
$clients_list = isset($clients_list) && is_array($clients_list) ? $clients_list : []; |
| 93 |
|
| 94 |
// Check for bulk action notifications |
| 95 |
$notification = false; |
| 96 |
|
| 97 |
if (isset($_GET['bulk_trashed']) && $_GET['bulk_trashed'] > 0) { |
| 98 |
$count = intval($_GET['bulk_trashed']); |
| 99 |
easy_invoice_display_notification('success', sprintf(_n('%s invoice moved to trash.', '%s invoices moved to trash.', $count, 'easy-invoice'), $count)); |
| 100 |
$notification = true; |
| 101 |
} |
| 102 |
|
| 103 |
if (isset($_GET['bulk_restored']) && $_GET['bulk_restored'] > 0) { |
| 104 |
$count = intval($_GET['bulk_restored']); |
| 105 |
easy_invoice_display_notification('success', sprintf(_n('%s invoice restored from trash.', '%s invoices restored from trash.', $count, 'easy-invoice'), $count)); |
| 106 |
$notification = true; |
| 107 |
} |
| 108 |
|
| 109 |
if (isset($_GET['bulk_deleted']) && $_GET['bulk_deleted'] > 0) { |
| 110 |
$count = intval($_GET['bulk_deleted']); |
| 111 |
easy_invoice_display_notification('success', sprintf(_n('%s invoice permanently deleted.', '%s invoices permanently deleted.', $count, 'easy-invoice'), $count)); |
| 112 |
$notification = true; |
| 113 |
} |
| 114 |
|
| 115 |
if (isset($_GET['bulk_drafted']) && $_GET['bulk_drafted'] > 0) { |
| 116 |
$count = intval($_GET['bulk_drafted']); |
| 117 |
easy_invoice_display_notification('success', sprintf(_n('%s invoice set to draft status.', '%s invoices set to draft status.', $count, 'easy-invoice'), $count)); |
| 118 |
$notification = true; |
| 119 |
} |
| 120 |
|
| 121 |
if (isset($_GET['bulk_published']) && $_GET['bulk_published'] > 0) { |
| 122 |
$count = intval($_GET['bulk_published']); |
| 123 |
easy_invoice_display_notification('success', sprintf(_n('%s invoice published successfully.', '%s invoices published successfully.', $count, 'easy-invoice'), $count)); |
| 124 |
$notification = true; |
| 125 |
} |
| 126 |
|
| 127 |
if (isset($_GET['bulk_error'])) { |
| 128 |
$error = sanitize_text_field($_GET['bulk_error']); |
| 129 |
$error_message = 'An error occurred while processing the bulk action.'; |
| 130 |
|
| 131 |
if ($error === 'no_selection') { |
| 132 |
$error_message = 'Please select at least one invoice to perform this action.'; |
| 133 |
} elseif ($error === 'invalid_action') { |
| 134 |
$error_message = 'Please select a valid bulk action.'; |
| 135 |
} |
| 136 |
|
| 137 |
easy_invoice_display_notification('error', $error_message); |
| 138 |
$notification = true; |
| 139 |
} |
| 140 |
// Extract invoice data for statistics |
| 141 |
$formatted_invoices = array(); |
| 142 |
foreach ($invoices as $invoice) { |
| 143 |
// For Invoice model objects |
| 144 |
if (is_object($invoice)) { |
| 145 |
$formatted_invoices[] = array( |
| 146 |
'id' => $invoice->getId(), |
| 147 |
'amount' => $invoice->getTotal(), |
| 148 |
'status' => $invoice->getStatus(), |
| 149 |
); |
| 150 |
} else { |
| 151 |
// For already formatted invoice arrays |
| 152 |
$formatted_invoices[] = array( |
| 153 |
'id' => $invoice['id'], |
| 154 |
'amount' => isset($invoice['total_amount']) ? $invoice['total_amount'] : $invoice['amount'], |
| 155 |
'status' => $invoice['status'], |
| 156 |
); |
| 157 |
} |
| 158 |
} |
| 159 |
|
| 160 |
// Get stats from all invoices, not just the current page |
| 161 |
$invoice_controller = new \EasyInvoice\Controllers\InvoiceController(); |
| 162 |
$stats = $invoice_controller->getInvoiceStats(); |
| 163 |
|
| 164 |
// Add total invoices count from current page for display purposes |
| 165 |
$stats['total_invoices'] = count($formatted_invoices); |
| 166 |
|
| 167 |
// Get trash count for tab display |
| 168 |
$trash_args = ['post_status' => 'trash']; |
| 169 |
$trash_invoices = $repository->all($trash_args); |
| 170 |
$trash_count = count($trash_invoices); |
| 171 |
|
| 172 |
// Get draft count for tab display |
| 173 |
$draft_args = ['post_status' => 'draft']; |
| 174 |
$draft_invoices = $repository->all($draft_args); |
| 175 |
$draft_count = count($draft_invoices); |
| 176 |
|
| 177 |
// Define available status filters |
| 178 |
$status_filters = array( |
| 179 |
'paid' => 'Paid', |
| 180 |
'unpaid' => 'Unpaid', |
| 181 |
'pending' => 'Pending', |
| 182 |
'draft' => 'Draft', |
| 183 |
'overdue' => 'Overdue', |
| 184 |
'cancelled' => 'Cancelled' |
| 185 |
); |
| 186 |
|
| 187 |
?> |
| 188 |
<div class="p-8"> |
| 189 |
<!-- Page Header --> |
| 190 |
<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;"> |
| 191 |
<div class="flex items-center justify-between"> |
| 192 |
<div> |
| 193 |
<h1 class="text-2xl font-bold text-gray-900"><?php _e('Invoices', 'easy-invoice'); ?></h1> |
| 194 |
<p class="mt-1 text-sm text-gray-500"><?php _e('Manage and track your invoices', 'easy-invoice'); ?></p> |
| 195 |
</div> |
| 196 |
<div class="flex items-center space-x-3"> |
| 197 |
<?php if (isset($_GET['rewrite_flushed'])): ?> |
| 198 |
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded-md text-sm"> |
| 199 |
<?php _e('Rewrite rules flushed successfully!', 'easy-invoice'); ?> |
| 200 |
</div> |
| 201 |
<?php endif; ?> |
| 202 |
|
| 203 |
<?php if (isset($_GET['post_types_registered'])): ?> |
| 204 |
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded-md text-sm"> |
| 205 |
<?php _e('Post types registered successfully!', 'easy-invoice'); ?> |
| 206 |
</div> |
| 207 |
<?php endif; ?> |
| 208 |
|
| 209 |
<a href="<?php echo wp_nonce_url(admin_url('admin-post.php?action=flush_easy_invoice_rewrite_rules'), 'flush_easy_invoice_rewrite_rules'); ?>" |
| 210 |
class="bg-yellow-100 text-yellow-700 hover:bg-yellow-200 px-4 py-2 rounded-md text-sm font-medium transition-colors duration-200"> |
| 211 |
<?php _e('Flush Rewrite Rules', 'easy-invoice'); ?> |
| 212 |
</a> |
| 213 |
|
| 214 |
<?php if (!easy_invoice_has_pro()): ?> |
| 215 |
<button type="button" id="export-all-invoices-btn" class="premium-export-btn inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200 relative"> |
| 216 |
<svg class="w-4 h-4 mr-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 217 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path> |
| 218 |
</svg> |
| 219 |
<?php _e('Export All', 'easy-invoice'); ?> |
| 220 |
<svg class="ml-2 w-5 h-5 text-purple-600 crown-icon" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 221 |
<path d="M12 8L15 13.2L18 10.5L17.3 14H6.7L6 10.5L9 13.2L12 8M12 4L8.5 10L3 5L5 16H19L21 5L15.5 10L12 4Z"/> |
| 222 |
</svg> |
| 223 |
</button> |
| 224 |
<script> |
| 225 |
jQuery(document).ready(function($) { |
| 226 |
$('#export-all-invoices-btn').on('click', function(e) { |
| 227 |
e.preventDefault(); |
| 228 |
// Show premium popup using the proper confirmation modal |
| 229 |
if (typeof EasyInvoiceConfirmation !== 'undefined') { |
| 230 |
EasyInvoiceConfirmation.showFeatureUpgrade('Export All Invoices', 'Export all your invoices to CSV, Excel, or PDF format for backup, analysis, or sharing with your team.'); |
| 231 |
} else { |
| 232 |
// Fallback to alert if confirmation modal is not available |
| 233 |
alert('<?php echo esc_js(__('Exporting all invoices is a premium feature. Upgrade to Easy Invoice Pro to unlock this feature.', 'easy-invoice')); ?>'); |
| 234 |
} |
| 235 |
}); |
| 236 |
}); |
| 237 |
</script> |
| 238 |
<?php else: ?> |
| 239 |
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline;"> |
| 240 |
<input type="hidden" name="action" value="easy_invoice_export_all_invoices" /> |
| 241 |
<input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('easy_invoice_export_all_invoices'); ?>" /> |
| 242 |
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 243 |
<svg class="w-4 h-4 mr-2 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 244 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path> |
| 245 |
</svg> |
| 246 |
<?php _e('Export All', 'easy-invoice'); ?> |
| 247 |
</button> |
| 248 |
</form> |
| 249 |
<?php endif; ?> |
| 250 |
<a href="#" id="create-new-invoice-btn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 251 |
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 252 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path> |
| 253 |
</svg> |
| 254 |
<?php _e('Create New Invoice', 'easy-invoice'); ?> |
| 255 |
</a> |
| 256 |
</div> |
| 257 |
</div> |
| 258 |
</div> |
| 259 |
|
| 260 |
<!-- Stats Cards --> |
| 261 |
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mb-8 mt-8"> |
| 262 |
<div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1"> |
| 263 |
<div class="flex items-center"> |
| 264 |
<div class="rounded-full bg-indigo-100 p-3 mr-4"> |
| 265 |
<svg class="w-6 h-6 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 266 |
<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> |
| 267 |
</svg> |
| 268 |
</div> |
| 269 |
<div> |
| 270 |
<h3 class="text-sm font-medium text-gray-500">Total Invoices</h3> |
| 271 |
<p class="mt-1 text-2xl font-bold text-gray-900"><?php echo $stats['total_invoices']; ?></p> |
| 272 |
</div> |
| 273 |
</div> |
| 274 |
</div> |
| 275 |
<div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1"> |
| 276 |
<div class="flex items-center"> |
| 277 |
<div class="rounded-full bg-green-100 p-3 mr-4"> |
| 278 |
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 279 |
<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> |
| 280 |
</svg> |
| 281 |
</div> |
| 282 |
<div> |
| 283 |
<h3 class="text-sm font-medium text-gray-500">Total Revenue</h3> |
| 284 |
<?php if (is_array($stats['total_revenue']) && !empty($stats['total_revenue'])): ?> |
| 285 |
<div class="mt-1"> |
| 286 |
<?php foreach ($stats['total_revenue'] as $currency => $data): ?> |
| 287 |
<?php |
| 288 |
// Create a temporary invoice object with the currency for proper formatting |
| 289 |
$temp_invoice = new \EasyInvoice\Models\Invoice(); |
| 290 |
$temp_invoice->setCurrencyCode($currency); |
| 291 |
$formatter = new \EasyInvoice\Helpers\InvoiceFormatter($temp_invoice); |
| 292 |
?> |
| 293 |
<p class="text-lg font-bold text-gray-900"> |
| 294 |
<?php echo $formatter->format($data['amount']); ?> |
| 295 |
<span class="text-sm font-normal text-gray-500"><?php echo $currency; ?></span> |
| 296 |
</p> |
| 297 |
<?php endforeach; ?> |
| 298 |
</div> |
| 299 |
<?php else: ?> |
| 300 |
<p class="mt-1 text-lg font-bold text-gray-900">$0.00</p> |
| 301 |
<?php endif; ?> |
| 302 |
</div> |
| 303 |
</div> |
| 304 |
</div> |
| 305 |
<div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1"> |
| 306 |
<div class="flex items-center"> |
| 307 |
<div class="rounded-full bg-blue-100 p-3 mr-4"> |
| 308 |
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 309 |
<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> |
| 310 |
</svg> |
| 311 |
</div> |
| 312 |
<div> |
| 313 |
<h3 class="text-sm font-medium text-gray-500">Total Value</h3> |
| 314 |
<?php if (is_array($stats['total_value']) && !empty($stats['total_value'])): ?> |
| 315 |
<div class="mt-1"> |
| 316 |
<?php foreach ($stats['total_value'] as $currency => $data): ?> |
| 317 |
<?php |
| 318 |
$formatter = new \EasyInvoice\Helpers\InvoiceFormatter($data['invoice_object']); |
| 319 |
$formatted_amount = $formatter->format($data['amount']); |
| 320 |
?> |
| 321 |
<div class="text-lg font-bold text-gray-900"> |
| 322 |
<?php echo $formatted_amount; ?> |
| 323 |
<span class="text-xs font-normal text-gray-500 ml-1">(<?php echo $data['invoices']; ?> <?php echo $data['invoices'] > 1 ? 'invoices' : 'invoice'; ?>)</span> |
| 324 |
</div> |
| 325 |
<?php endforeach; ?> |
| 326 |
</div> |
| 327 |
<?php else: ?> |
| 328 |
<p class="mt-1 text-lg font-bold text-gray-900">$0.00</p> |
| 329 |
<?php endif; ?> |
| 330 |
</div> |
| 331 |
</div> |
| 332 |
</div> |
| 333 |
<div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1"> |
| 334 |
<div class="flex items-center"> |
| 335 |
<div class="rounded-full bg-green-100 p-3 mr-4"> |
| 336 |
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 337 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path> |
| 338 |
</svg> |
| 339 |
</div> |
| 340 |
<div> |
| 341 |
<h3 class="text-sm font-medium text-gray-500">Paid Invoices</h3> |
| 342 |
<p class="mt-1 text-2xl font-bold text-green-600"><?php echo $stats['paid_invoices']; ?></p> |
| 343 |
</div> |
| 344 |
</div> |
| 345 |
</div> |
| 346 |
<div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1"> |
| 347 |
<div class="flex items-center"> |
| 348 |
<div class="rounded-full bg-yellow-100 p-3 mr-4"> |
| 349 |
<svg class="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 350 |
<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> |
| 351 |
</svg> |
| 352 |
</div> |
| 353 |
<div> |
| 354 |
<h3 class="text-sm font-medium text-gray-500">Pending Invoices</h3> |
| 355 |
<p class="mt-1 text-2xl font-bold text-yellow-600"><?php echo $stats['pending_invoices']; ?></p> |
| 356 |
</div> |
| 357 |
</div> |
| 358 |
</div> |
| 359 |
</div> |
| 360 |
|
| 361 |
<!-- Top Pagination --> |
| 362 |
<?php if ($total_invoices > 0): ?> |
| 363 |
<div class="mb-4"> |
| 364 |
<div class="px-4 py-3 flex items-center justify-between"> |
| 365 |
<div class="flex items-center space-x-2"> |
| 366 |
<span class="text-sm text-gray-700"> |
| 367 |
Showing |
| 368 |
<span class="font-medium"><?php echo number_format((($current_page - 1) * $per_page) + 1); ?></span> |
| 369 |
to |
| 370 |
<span class="font-medium"><?php echo number_format(min($current_page * $per_page, $total_invoices)); ?></span> |
| 371 |
of |
| 372 |
<span class="font-medium"><?php echo number_format($total_invoices); ?></span> |
| 373 |
results |
| 374 |
</span> |
| 375 |
</div> |
| 376 |
<?php if ($total_pages > 1): ?> |
| 377 |
<div class="flex items-center space-x-2"> |
| 378 |
<span class="text-sm text-gray-500">Page <?php echo number_format($current_page); ?> of <?php echo number_format($total_pages); ?></span> |
| 379 |
<?php |
| 380 |
// Use WordPress's built-in pagination for top |
| 381 |
$top_pagination_args = array( |
| 382 |
'base' => add_query_arg('paged', '%#%'), |
| 383 |
'format' => '', |
| 384 |
'current' => $current_page, |
| 385 |
'total' => $total_pages, |
| 386 |
'prev_text' => '‹ Previous', |
| 387 |
'next_text' => 'Next ›', |
| 388 |
'type' => 'array', |
| 389 |
'end_size' => 1, |
| 390 |
'mid_size' => 2, |
| 391 |
'add_args' => array( |
| 392 |
'view' => $current_view, |
| 393 |
'status' => $status_filter, |
| 394 |
'recurring' => $recurring_filter, |
| 395 |
'client_id' => $client_filter ?: false, |
| 396 |
) |
| 397 |
); |
| 398 |
|
| 399 |
$top_pagination_links = paginate_links($top_pagination_args); |
| 400 |
|
| 401 |
if ($top_pagination_links) { |
| 402 |
echo '<div class="flex space-x-1">'; |
| 403 |
foreach ($top_pagination_links as $link) { |
| 404 |
// Add null check to prevent passing null to easy_invoice_str_replace |
| 405 |
if ($link === null) { |
| 406 |
continue; |
| 407 |
} |
| 408 |
|
| 409 |
// Convert WordPress pagination to our styling |
| 410 |
$link = easy_invoice_str_replace('page-numbers', 'px-3 py-2 text-sm font-medium border border-gray-300 rounded-md', $link); |
| 411 |
$link = easy_invoice_str_replace('current', 'bg-indigo-50 border-indigo-500 text-indigo-600', $link); |
| 412 |
$link = easy_invoice_str_replace('prev', 'bg-white text-gray-500 hover:bg-gray-50', $link); |
| 413 |
$link = easy_invoice_str_replace('next', 'bg-white text-gray-500 hover:bg-gray-50', $link); |
| 414 |
$link = easy_invoice_str_replace('dots', 'bg-white text-gray-700', $link); |
| 415 |
|
| 416 |
// Add default styling for regular page numbers |
| 417 |
if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) { |
| 418 |
$link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link); |
| 419 |
} |
| 420 |
|
| 421 |
echo $link; |
| 422 |
} |
| 423 |
echo '</div>'; |
| 424 |
} |
| 425 |
?> |
| 426 |
</div> |
| 427 |
<?php endif; ?> |
| 428 |
</div> |
| 429 |
</div> |
| 430 |
<?php endif; ?> |
| 431 |
|
| 432 |
<!-- View Tabs and Filters --> |
| 433 |
<div class="bg-white rounded-lg shadow mb-6 overflow-hidden"> |
| 434 |
<!-- Tabs with more modern styling --> |
| 435 |
<div class="border-b border-gray-200 bg-gradient-to-r from-indigo-50 to-white"> |
| 436 |
<nav class="flex"> |
| 437 |
<?php |
| 438 |
// View Tabs (All, Draft, Trash) |
| 439 |
$views = array( |
| 440 |
'all' => 'All (' . $stats['total_invoices'] . ')', |
| 441 |
'draft' => 'Draft (' . $draft_count . ')', |
| 442 |
'trash' => 'Trash (' . $trash_count . ')' |
| 443 |
); |
| 444 |
|
| 445 |
foreach ($views as $view => $label) { |
| 446 |
$is_current = $current_view === $view; |
| 447 |
$status_param = !empty($status_filter) && $view === 'all' ? '&status=' . $status_filter : ''; |
| 448 |
if (!empty($client_filter)) { |
| 449 |
$status_param .= '&client_id=' . (int) $client_filter; |
| 450 |
} |
| 451 |
$class = $is_current |
| 452 |
? 'border-b-2 border-indigo-500 text-indigo-600 bg-white' |
| 453 |
: 'border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'; |
| 454 |
?> |
| 455 |
<a href="?page=easy-invoice-all&view=<?php echo esc_attr($view . $status_param); ?>" |
| 456 |
class="flex items-center whitespace-nowrap py-4 px-6 font-medium text-sm transition-colors duration-200 <?php echo $class; ?>"> |
| 457 |
<?php if ($view === 'all'): ?> |
| 458 |
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 459 |
<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> |
| 460 |
</svg> |
| 461 |
<?php elseif ($view === 'draft'): ?> |
| 462 |
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 463 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path> |
| 464 |
</svg> |
| 465 |
<?php elseif ($view === 'trash'): ?> |
| 466 |
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 467 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path> |
| 468 |
</svg> |
| 469 |
<?php endif; ?> |
| 470 |
<?php echo $label; ?> |
| 471 |
</a> |
| 472 |
<?php |
| 473 |
} |
| 474 |
?> |
| 475 |
</nav> |
| 476 |
</div> |
| 477 |
|
| 478 |
<!-- Filters and Bulk Actions with Search --> |
| 479 |
<div class="p-4 flex flex-wrap items-center justify-between bg-white border-b border-gray-100"> |
| 480 |
<!-- Bulk Actions (Left Side) --> |
| 481 |
<div class="flex items-center space-x-2 my-2"> |
| 482 |
<form id="bulk-action-form" method="post" class="flex items-center gap-2"> |
| 483 |
<?php wp_nonce_field('easy_invoice_bulk_action', 'easy_invoice_bulk_nonce'); ?> |
| 484 |
<input type="hidden" name="action" value="easy_invoice_bulk_action"> |
| 485 |
|
| 486 |
<select name="bulk_action" class="block w-full px-3 py-2 border border-gray-300 rounded-md leading-5 bg-white text-gray-700 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"> |
| 487 |
<option value="">Bulk Actions</option> |
| 488 |
<?php if ($current_view === 'trash'): ?> |
| 489 |
<option value="restore">Restore</option> |
| 490 |
<option value="delete">Delete Permanently</option> |
| 491 |
<?php elseif ($current_view === 'draft'): ?> |
| 492 |
<option value="publish">Publish</option> |
| 493 |
<option value="trash">Move to Trash</option> |
| 494 |
<?php else: ?> |
| 495 |
<option value="trash">Move to Trash</option> |
| 496 |
<option value="draft">Set to Draft</option> |
| 497 |
<?php endif; ?> |
| 498 |
<option value="export" data-requires-pro="1"><?php esc_html_e('Export Selected (Pro)', 'easy-invoice'); ?></option> |
| 499 |
</select> |
| 500 |
|
| 501 |
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 whitespace-nowrap"> |
| 502 |
Apply |
| 503 |
</button> |
| 504 |
</form> |
| 505 |
</div> |
| 506 |
|
| 507 |
<?php if ($current_view !== 'trash'): ?> |
| 508 |
<!-- Search Form (Right Side) --> |
| 509 |
<div class="flex items-center gap-2"> |
| 510 |
<form method="get" class="flex items-center gap-2"> |
| 511 |
<input type="hidden" name="page" value="easy-invoice-all"> |
| 512 |
<input type="hidden" name="view" value="<?php echo esc_attr($current_view); ?>"> |
| 513 |
<?php if (!empty($status_filter)): ?> |
| 514 |
<input type="hidden" name="status" value="<?php echo esc_attr($status_filter); ?>"> |
| 515 |
<?php endif; ?> |
| 516 |
<?php if (!empty($recurring_filter)): ?> |
| 517 |
<input type="hidden" name="recurring" value="<?php echo esc_attr($recurring_filter); ?>"> |
| 518 |
<?php endif; ?> |
| 519 |
|
| 520 |
<?php if (!empty($clients_list)): ?> |
| 521 |
<select name="client_id" aria-label="<?php esc_attr_e('Filter by client', 'easy-invoice'); ?>" |
| 522 |
class="block px-3 py-2 border border-gray-300 rounded-md bg-white text-gray-700 sm:text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500" |
| 523 |
onchange="this.form.submit()"> |
| 524 |
<option value=""><?php esc_html_e('All clients', 'easy-invoice'); ?></option> |
| 525 |
<?php foreach ($clients_list as $client_option): ?> |
| 526 |
<option value="<?php echo esc_attr($client_option['id']); ?>" <?php selected((int) $client_filter, (int) $client_option['id']); ?>> |
| 527 |
<?php echo esc_html($client_option['name']); ?> |
| 528 |
</option> |
| 529 |
<?php endforeach; ?> |
| 530 |
</select> |
| 531 |
<?php endif; ?> |
| 532 |
|
| 533 |
<div class="relative"> |
| 534 |
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
| 535 |
<svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 536 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path> |
| 537 |
</svg> |
| 538 |
</div> |
| 539 |
<input type="text" |
| 540 |
name="search" |
| 541 |
value="<?php echo esc_attr($search_query); ?>" |
| 542 |
placeholder="<?php _e('Search invoices, numbers, or clients...', 'easy-invoice'); ?>" |
| 543 |
class="block w-64 pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"> |
| 544 |
</div> |
| 545 |
|
| 546 |
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 547 |
<?php _e('Search', 'easy-invoice'); ?> |
| 548 |
</button> |
| 549 |
|
| 550 |
<?php if (!empty($search_query)): ?> |
| 551 |
<a href="<?php echo esc_url(remove_query_arg('search', $_SERVER['REQUEST_URI'])); ?>" class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 552 |
<?php _e('Clear', 'easy-invoice'); ?> |
| 553 |
</a> |
| 554 |
<?php endif; ?> |
| 555 |
</form> |
| 556 |
</div> |
| 557 |
<?php endif; ?> |
| 558 |
</div> |
| 559 |
|
| 560 |
<!-- Status Filters --> |
| 561 |
<?php if ($current_view !== 'trash'): ?> |
| 562 |
<div class="p-4 bg-white border-b border-gray-100"> |
| 563 |
<div class="flex flex-wrap items-center gap-2 my-2"> |
| 564 |
<span class="text-sm text-gray-500 mr-2">Filter by status:</span> |
| 565 |
<div class="flex flex-wrap gap-1"> |
| 566 |
<?php |
| 567 |
// Build base URL with current view, recurring filter, client filter and search query |
| 568 |
$base_url = '?page=easy-invoice-all&view=' . $current_view; |
| 569 |
if (!empty($recurring_filter)) { |
| 570 |
$base_url .= '&recurring=' . urlencode($recurring_filter); |
| 571 |
} |
| 572 |
if (!empty($client_filter)) { |
| 573 |
$base_url .= '&client_id=' . (int) $client_filter; |
| 574 |
} |
| 575 |
if (!empty($search_query)) { |
| 576 |
$base_url .= '&search=' . urlencode($search_query); |
| 577 |
} |
| 578 |
?> |
| 579 |
<a href="<?php echo esc_url($base_url); ?>" class="<?php echo empty($status_filter) ? 'bg-indigo-100 text-indigo-800 ring-1 ring-indigo-300' : 'bg-gray-50 text-gray-700 hover:bg-gray-100'; ?> px-3 py-1 rounded-full text-xs font-medium transition-colors duration-200"> |
| 580 |
All |
| 581 |
</a> |
| 582 |
|
| 583 |
<?php |
| 584 |
$status_colors = [ |
| 585 |
'paid' => 'bg-green-100 text-green-800 ring-1 ring-green-300', |
| 586 |
'unpaid' => 'bg-yellow-100 text-yellow-800 ring-1 ring-yellow-300', |
| 587 |
'pending' => 'bg-blue-100 text-blue-800 ring-1 ring-blue-300', |
| 588 |
'draft' => 'bg-gray-100 text-gray-800 ring-1 ring-gray-300', |
| 589 |
'overdue' => 'bg-red-100 text-red-800 ring-1 ring-red-300', |
| 590 |
'cancelled' => 'bg-gray-100 text-gray-800 ring-1 ring-gray-300' |
| 591 |
]; |
| 592 |
|
| 593 |
foreach ($status_filters as $status_key => $status_label): |
| 594 |
$is_current = $status_filter === $status_key; |
| 595 |
$base_class = 'px-3 py-1 rounded-full text-xs font-medium transition-colors duration-200'; |
| 596 |
|
| 597 |
if ($is_current) { |
| 598 |
$color_class = $status_colors[$status_key] ?? 'bg-indigo-100 text-indigo-800 ring-1 ring-indigo-300'; |
| 599 |
} else { |
| 600 |
$color_class = 'bg-gray-50 text-gray-700 hover:bg-gray-100'; |
| 601 |
} |
| 602 |
|
| 603 |
// Build URL with status filter |
| 604 |
$status_url = $base_url . '&status=' . urlencode($status_key); |
| 605 |
?> |
| 606 |
<a href="<?php echo esc_url($status_url); ?>" |
| 607 |
class="<?php echo $base_class . ' ' . $color_class; ?>"> |
| 608 |
<?php echo $status_label; ?> |
| 609 |
</a> |
| 610 |
<?php endforeach; ?> |
| 611 |
</div> |
| 612 |
<?php do_action('easy_invoice_inline_type_filter'); ?> |
| 613 |
</div> |
| 614 |
|
| 615 |
<!-- Hook for Pro plugin to add additional filters --> |
| 616 |
<?php do_action('easy_invoice_before_invoice_listing'); ?> |
| 617 |
<?php endif; ?> |
| 618 |
</div> |
| 619 |
</div> |
| 620 |
|
| 621 |
<!-- Invoices Table --> |
| 622 |
<div class="bg-white rounded-lg shadow overflow-hidden"> |
| 623 |
<div class="overflow-x-auto"> |
| 624 |
<table class="min-w-full divide-y divide-gray-200"> |
| 625 |
<thead> |
| 626 |
<tr class="bg-gray-50"> |
| 627 |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-10 border-b border-gray-200"> |
| 628 |
<input type="checkbox" id="select-all" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"> |
| 629 |
</th> |
| 630 |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200"> |
| 631 |
<?php _e('Invoice', 'easy-invoice'); ?> |
| 632 |
</th> |
| 633 |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200"> |
| 634 |
<?php _e('Client', 'easy-invoice'); ?> |
| 635 |
</th> |
| 636 |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200"> |
| 637 |
<?php _e('Status', 'easy-invoice'); ?> |
| 638 |
</th> |
| 639 |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200"> |
| 640 |
<?php _e('Amount', 'easy-invoice'); ?> |
| 641 |
</th> |
| 642 |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200"> |
| 643 |
<?php _e('Due Date', 'easy-invoice'); ?> |
| 644 |
</th> |
| 645 |
<?php do_action('easy_invoice_after_invoice_listing_header'); ?> |
| 646 |
</tr> |
| 647 |
</thead> |
| 648 |
<tbody class="bg-white divide-y divide-gray-100"> |
| 649 |
<?php foreach ($invoices as $invoice) : |
| 650 |
// Handle both object and array formats |
| 651 |
if (is_object($invoice)) { |
| 652 |
$invoice_id = $invoice->getId(); |
| 653 |
$invoice_number = $invoice->getNumber(); |
| 654 |
$invoice_title = $invoice->getTitle(); |
| 655 |
|
| 656 |
// Get client name directly from client repository |
| 657 |
$client_name = 'No Client'; |
| 658 |
if ($invoice->getClientId() > 0) { |
| 659 |
$client_repository = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository(); |
| 660 |
$client = $client_repository->find($invoice->getClientId()); |
| 661 |
if ($client) { |
| 662 |
$client_name = $client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName()); |
| 663 |
if (empty(trim($client_name))) { |
| 664 |
$client_name = $client->getDisplayName() ?: 'Client ' . $client->getId(); |
| 665 |
} |
| 666 |
} |
| 667 |
} elseif ($invoice->getCustomerName()) { |
| 668 |
$client_name = $invoice->getCustomerName(); |
| 669 |
} |
| 670 |
|
| 671 |
$client_email = $invoice->getCustomerEmail(); |
| 672 |
$status = $invoice->getStatus(); |
| 673 |
|
| 674 |
// Use formatter to display amount properly |
| 675 |
$formatter = new \EasyInvoice\Helpers\InvoiceFormatter($invoice); |
| 676 |
$amount = $formatter->format($invoice->getTotal()); |
| 677 |
$due_date = $invoice->getDueDate(); |
| 678 |
} else { |
| 679 |
$invoice_id = $invoice['id']; |
| 680 |
$invoice_number = $invoice['invoice_number']; |
| 681 |
$invoice_title = isset($invoice['title']) ? $invoice['title'] : $invoice_number; |
| 682 |
$client_name = $invoice['client_name']; |
| 683 |
$client_email = isset($invoice['client_email']) ? $invoice['client_email'] : ''; |
| 684 |
$status = $invoice['status']; |
| 685 |
$amount = $invoice['total_amount']; |
| 686 |
$due_date = $invoice['due_date']; |
| 687 |
} |
| 688 |
|
| 689 |
// Format the display title |
| 690 |
$display_title = $invoice_title ?: 'Untitled Invoice'; |
| 691 |
|
| 692 |
// Determine classes for status badge |
| 693 |
$status_class = 'bg-gray-100 text-gray-800 ring-1 ring-gray-200'; |
| 694 |
if ($status === 'paid') { |
| 695 |
$status_class = 'bg-green-100 text-green-800 ring-1 ring-green-200'; |
| 696 |
} elseif ($status === 'pending') { |
| 697 |
$status_class = 'bg-yellow-100 text-yellow-800 ring-1 ring-yellow-200'; |
| 698 |
} elseif ($status === 'overdue') { |
| 699 |
$status_class = 'bg-red-100 text-red-800 ring-1 ring-red-200'; |
| 700 |
} |
| 701 |
?> |
| 702 |
<?php do_action('easy_invoice_before_invoice_listing_row', $invoice); ?> |
| 703 |
<tr class="hover:bg-gray-50 transition-colors duration-150" data-invoice-id="<?php echo $invoice_id; ?>"> |
| 704 |
<td class="px-6 py-4 whitespace-nowrap"> |
| 705 |
<input type="checkbox" name="invoice_ids[]" value="<?php echo $invoice_id; ?>" form="bulk-action-form" class="invoice-checkbox rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"> |
| 706 |
</td> |
| 707 |
<td class="px-6 py-4 whitespace-nowrap"> |
| 708 |
<?php if ($current_view === 'trash'): ?> |
| 709 |
<div class="text-sm font-medium text-gray-900"> |
| 710 |
<?php echo esc_html($display_title); ?> |
| 711 |
</div> |
| 712 |
<div class="text-xs text-gray-500 mt-1"> |
| 713 |
<?php echo apply_filters('easy_invoice_invoice_number_display', esc_html($invoice_number), $invoice); ?> |
| 714 |
<?php |
| 715 |
// Check if this invoice was converted from a quote |
| 716 |
$quote_info = \EasyInvoice\Helpers\QuoteInvoiceHelper::getQuoteInfoFromInvoice($invoice_id); |
| 717 |
if ($quote_info): ?> |
| 718 |
<span class="ml-2 inline-flex items-center space-x-1"> |
| 719 |
<i class="<?php echo esc_attr($quote_info['icon_class']); ?> text-blue-500 text-xs" title="<?php echo esc_attr($quote_info['tooltip_text']); ?>"></i> |
| 720 |
<a href="<?php echo esc_url($quote_info['url']); ?>" |
| 721 |
target="_blank" |
| 722 |
class="text-blue-600 hover:text-blue-800 hover:underline text-xs" |
| 723 |
title="<?php echo esc_attr($quote_info['tooltip_text']); ?>"> |
| 724 |
<?php echo esc_html($quote_info['number'] ?: $quote_info['title']); ?> |
| 725 |
</a> |
| 726 |
</span> |
| 727 |
<?php endif; ?> |
| 728 |
</div> |
| 729 |
<div class="row-actions mt-1"> |
| 730 |
<span class="restore"><a href="#" class="restore-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"></path></svg><?php _e('Restore', 'easy-invoice'); ?></a></span> |
| 731 |
<span class="delete"><a href="#" class="delete-permanently text-red-600" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg><?php _e('Delete Permanently', 'easy-invoice'); ?></a></span> |
| 732 |
</div> |
| 733 |
<?php elseif ($current_view === 'draft'): ?> |
| 734 |
<div class="text-sm font-medium text-gray-900"> |
| 735 |
<a href="<?php echo get_permalink($invoice_id); ?>" class="text-indigo-600 hover:text-indigo-900"> |
| 736 |
<?php echo apply_filters('easy_invoice_invoice_title', esc_html($display_title), $invoice); ?> |
| 737 |
</a> |
| 738 |
</div> |
| 739 |
<div class="text-xs text-gray-500 mt-1"> |
| 740 |
<?php echo apply_filters('easy_invoice_invoice_number_display', esc_html($invoice_number), $invoice); ?> |
| 741 |
<?php |
| 742 |
// Check if this invoice was converted from a quote |
| 743 |
$quote_info = \EasyInvoice\Helpers\QuoteInvoiceHelper::getQuoteInfoFromInvoice($invoice_id); |
| 744 |
if ($quote_info): ?> |
| 745 |
<span class="ml-2 inline-flex items-center space-x-1"> |
| 746 |
<i class="<?php echo esc_attr($quote_info['icon_class']); ?> text-blue-500 text-xs" title="<?php echo esc_attr($quote_info['tooltip_text']); ?>"></i> |
| 747 |
<a href="<?php echo esc_url($quote_info['url']); ?>" |
| 748 |
target="_blank" |
| 749 |
class="text-blue-600 hover:text-blue-800 hover:underline text-xs" |
| 750 |
title="<?php echo esc_attr($quote_info['tooltip_text']); ?>"> |
| 751 |
<?php echo esc_html($quote_info['number'] ?: $quote_info['title']); ?> |
| 752 |
</a> |
| 753 |
</span> |
| 754 |
<?php endif; ?> |
| 755 |
</div> |
| 756 |
<div class="row-actions mt-1"> |
| 757 |
<span class="edit"><a href="<?php echo admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $invoice_id); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg><?php _e('Edit', 'easy-invoice'); ?></a></span> |
| 758 |
<span class="publish"><a href="#" class="publish-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg><?php _e('Publish', 'easy-invoice'); ?></a></span> |
| 759 |
<span class="trash"><a href="#" class="trash-invoice text-red-600" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg><?php _e('Trash', 'easy-invoice'); ?></a></span> |
| 760 |
<span class="email"><a href="#" class="email-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg><?php _e('Send Email', 'easy-invoice'); ?></a></span> |
| 761 |
<span class="pdf"><a href="<?php echo get_permalink($invoice_id); ?>?auto_download_pdf=1" target="_blank" class="download-pdf" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg><?php _e('Download PDF', 'easy-invoice'); ?></a></span> |
| 762 |
<?php if (!easy_invoice_has_pro()): ?> |
| 763 |
<span class="export premium-feature"><a href="#" class="export-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>Export</a></span> |
| 764 |
<?php endif; ?> |
| 765 |
<?php if (!easy_invoice_has_pro()): ?> |
| 766 |
<span class="duplicate premium-feature"><a href="#" class="duplicate-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7l2 4 4 .5-3 2.5.5 4-3.5-2-3.5 2 .5-4-3-2.5 4-.5 2-4z"/></svg>Duplicate</a></span> |
| 767 |
<?php endif; ?> |
| 768 |
|
| 769 |
<?php |
| 770 |
// Apply filter for Pro plugin to add secure link actions |
| 771 |
$secure_actions = apply_filters('easy_invoice_invoice_row_actions', [], $invoice); |
| 772 |
foreach ($secure_actions as $action_key => $action_html) { |
| 773 |
echo '<span class="' . esc_attr($action_key) . '">' . $action_html . '</span>'; |
| 774 |
} |
| 775 |
?> |
| 776 |
</div> |
| 777 |
<?php else: ?> |
| 778 |
<div class="text-sm font-medium text-gray-900"> |
| 779 |
<a href="<?php echo admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $invoice_id); ?>" class="text-indigo-600 hover:text-indigo-900"> |
| 780 |
<?php echo apply_filters('easy_invoice_invoice_title', esc_html($display_title), $invoice); ?> |
| 781 |
</a> |
| 782 |
</div> |
| 783 |
<div class="text-xs text-gray-500 mt-1"> |
| 784 |
<?php echo apply_filters('easy_invoice_invoice_number_display', esc_html($invoice_number), $invoice); ?> |
| 785 |
<?php |
| 786 |
// Check if this invoice was converted from a quote |
| 787 |
$quote_info = \EasyInvoice\Helpers\QuoteInvoiceHelper::getQuoteInfoFromInvoice($invoice_id); |
| 788 |
if ($quote_info): ?> |
| 789 |
<span class="ml-2 inline-flex items-center space-x-1"> |
| 790 |
<i class="<?php echo esc_attr($quote_info['icon_class']); ?> text-blue-500 text-xs" title="<?php echo esc_attr($quote_info['tooltip_text']); ?>"></i> |
| 791 |
<a href="<?php echo esc_url($quote_info['url']); ?>" |
| 792 |
target="_blank" |
| 793 |
class="text-blue-600 hover:text-blue-800 hover:underline text-xs" |
| 794 |
title="<?php echo esc_attr($quote_info['tooltip_text']); ?>"> |
| 795 |
<?php echo esc_html($quote_info['number'] ?: $quote_info['title']); ?> |
| 796 |
</a> |
| 797 |
</span> |
| 798 |
<?php endif; ?> |
| 799 |
</div> |
| 800 |
<div class="row-actions mt-1"> |
| 801 |
<span class="view"><a href="<?php echo apply_filters('easy_invoice_invoice_view_url', get_permalink($invoice_id), $invoice_id); ?>" target="_blank"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path></svg><?php _e('View', 'easy-invoice'); ?></a></span> |
| 802 |
<span class="edit"><a href="<?php echo admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $invoice_id); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg><?php _e('Edit', 'easy-invoice'); ?></a></span> |
| 803 |
<span class="draft"><a href="#" class="draft-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><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></svg><?php _e('Draft', 'easy-invoice'); ?></a></span> |
| 804 |
<span class="trash"><a href="#" class="trash-invoice text-red-600" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg><?php _e('Trash', 'easy-invoice'); ?></a></span> |
| 805 |
<span class="email"><a href="#" class="email-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg><?php _e('Send Email', 'easy-invoice'); ?></a></span> |
| 806 |
<span class="pdf"><a href="<?php echo get_permalink($invoice_id); ?>?auto_download_pdf=1" target="_blank" class="download-pdf" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg><?php _e('Download PDF', 'easy-invoice'); ?></a></span> |
| 807 |
<?php if (!easy_invoice_has_pro()): ?> |
| 808 |
<span class="export premium-feature"><a href="#" class="export-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>Export</a></span> |
| 809 |
<?php endif; ?> |
| 810 |
<?php if (!easy_invoice_has_pro()): ?> |
| 811 |
<span class="duplicate premium-feature"><a href="#" class="duplicate-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7l2 4 4 .5-3 2.5.5 4-3.5-2-3.5 2 .5-4-3-2.5 4-.5 2-4z"/></svg>Duplicate</a></span> |
| 812 |
<?php endif; ?> |
| 813 |
|
| 814 |
<?php |
| 815 |
// Apply filter for Pro plugin to add secure link actions |
| 816 |
$secure_actions = apply_filters('easy_invoice_invoice_row_actions', [], $invoice); |
| 817 |
foreach ($secure_actions as $action_key => $action_html) { |
| 818 |
echo '<span class="' . esc_attr($action_key) . '">' . $action_html . '</span>'; |
| 819 |
} |
| 820 |
?> |
| 821 |
</div> |
| 822 |
<?php endif; ?> |
| 823 |
</td> |
| 824 |
<td class="px-6 py-4 whitespace-nowrap"> |
| 825 |
<div class="text-sm text-gray-900"><?php echo esc_html($client_name ?: 'No Client'); ?></div> |
| 826 |
<div class="text-sm text-gray-500"><?php echo esc_html($client_email ?: ''); ?></div> |
| 827 |
</td> |
| 828 |
<td class="px-6 py-4 whitespace-nowrap"> |
| 829 |
<span class="px-3 py-1 inline-flex text-xs leading-5 font-medium rounded-full <?php echo $status_class; ?>"> |
| 830 |
<?php echo ucfirst(esc_html($status)); ?> |
| 831 |
</span> |
| 832 |
</td> |
| 833 |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700 font-medium"> |
| 834 |
<?php echo esc_html($amount); ?> |
| 835 |
</td> |
| 836 |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
| 837 |
<?php echo esc_html($due_date); ?> |
| 838 |
</td> |
| 839 |
<?php do_action('easy_invoice_after_invoice_listing_row', $invoice); ?> |
| 840 |
</tr> |
| 841 |
<?php endforeach; ?> |
| 842 |
|
| 843 |
<?php if (empty($invoices)): ?> |
| 844 |
<tr> |
| 845 |
<td colspan="7" class="px-6 py-4 text-center text-sm text-gray-500"> |
| 846 |
<?php if ($current_view === 'trash'): ?> |
| 847 |
No invoices found in trash. |
| 848 |
<?php elseif ($current_view === 'draft'): ?> |
| 849 |
No invoices found in draft. <a href="#" class="text-indigo-600 hover:text-indigo-900 create-first-invoice">Create your first invoice</a>. |
| 850 |
<?php else: ?> |
| 851 |
No invoices found. <a href="#" class="text-indigo-600 hover:text-indigo-900 create-first-invoice">Create your first invoice</a>. |
| 852 |
<?php endif; ?> |
| 853 |
</td> |
| 854 |
</tr> |
| 855 |
<?php endif; ?> |
| 856 |
</tbody> |
| 857 |
</table> |
| 858 |
</div> |
| 859 |
</div> |
| 860 |
|
| 861 |
<!-- Pagination --> |
| 862 |
<?php if ($total_invoices > 0): ?> |
| 863 |
<div class="mt-4"> |
| 864 |
<div class="px-4 py-3 flex items-center justify-between"> |
| 865 |
<div class="flex items-center space-x-2"> |
| 866 |
<span class="text-sm text-gray-700"> |
| 867 |
Showing |
| 868 |
<span class="font-medium"><?php echo number_format((($current_page - 1) * $per_page) + 1); ?></span> |
| 869 |
to |
| 870 |
<span class="font-medium"><?php echo number_format(min($current_page * $per_page, $total_invoices)); ?></span> |
| 871 |
of |
| 872 |
<span class="font-medium"><?php echo number_format($total_invoices); ?></span> |
| 873 |
results |
| 874 |
</span> |
| 875 |
</div> |
| 876 |
<?php if ($total_pages > 1): ?> |
| 877 |
<div class="flex items-center space-x-2"> |
| 878 |
<span class="text-sm text-gray-500">Page <?php echo number_format($current_page); ?> of <?php echo number_format($total_pages); ?></span> |
| 879 |
<?php |
| 880 |
// Use WordPress's built-in pagination for bottom |
| 881 |
$bottom_pagination_args = array( |
| 882 |
'base' => add_query_arg('paged', '%#%'), |
| 883 |
'format' => '', |
| 884 |
'current' => $current_page, |
| 885 |
'total' => $total_pages, |
| 886 |
'prev_text' => '‹ Previous', |
| 887 |
'next_text' => 'Next ›', |
| 888 |
'type' => 'array', |
| 889 |
'end_size' => 1, |
| 890 |
'mid_size' => 2, |
| 891 |
'add_args' => array( |
| 892 |
'view' => $current_view, |
| 893 |
'status' => $status_filter, |
| 894 |
'recurring' => $recurring_filter |
| 895 |
) |
| 896 |
); |
| 897 |
|
| 898 |
$bottom_pagination_links = paginate_links($bottom_pagination_args); |
| 899 |
|
| 900 |
if ($bottom_pagination_links) { |
| 901 |
echo '<div class="flex space-x-1">'; |
| 902 |
foreach ($bottom_pagination_links as $link) { |
| 903 |
// Add null check to prevent passing null to easy_invoice_str_replace |
| 904 |
if ($link === null) { |
| 905 |
continue; |
| 906 |
} |
| 907 |
|
| 908 |
// Convert WordPress pagination to our styling |
| 909 |
$link = easy_invoice_str_replace('page-numbers', 'px-3 py-2 text-sm font-medium border border-gray-300 rounded-md', $link); |
| 910 |
$link = easy_invoice_str_replace('current', 'bg-indigo-50 border-indigo-500 text-indigo-600', $link); |
| 911 |
$link = easy_invoice_str_replace('prev', 'bg-white text-gray-500 hover:bg-gray-50', $link); |
| 912 |
$link = easy_invoice_str_replace('next', 'bg-white text-gray-500 hover:bg-gray-50', $link); |
| 913 |
$link = easy_invoice_str_replace('dots', 'bg-white text-gray-700', $link); |
| 914 |
|
| 915 |
// Add default styling for regular page numbers |
| 916 |
if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) { |
| 917 |
$link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link); |
| 918 |
} |
| 919 |
|
| 920 |
echo $link; |
| 921 |
} |
| 922 |
echo '</div>'; |
| 923 |
} |
| 924 |
?> |
| 925 |
</div> |
| 926 |
<?php endif; ?> |
| 927 |
</div> |
| 928 |
</div> |
| 929 |
<?php endif; ?> |
| 930 |
</div> |
| 931 |
|
| 932 |
<!-- Delete Invoice Confirmation Modal --> |
| 933 |
<div id="trash-invoice-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50"> |
| 934 |
<div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative"> |
| 935 |
<button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500"> |
| 936 |
<i class="fas fa-times"></i> |
| 937 |
</button> |
| 938 |
<h2 class="text-xl font-medium text-gray-900 mb-4">Move to Trash</h2> |
| 939 |
<p class="text-sm text-gray-500 mb-4">Are you sure you want to move invoice <span id="trash-invoice-number" class="font-medium"></span> to trash?</p> |
| 940 |
|
| 941 |
<div class="mt-5 flex justify-end space-x-3"> |
| 942 |
<button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 943 |
Cancel |
| 944 |
</button> |
| 945 |
<button type="button" id="confirm-trash" class="bg-red-600 py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"> |
| 946 |
Move to Trash |
| 947 |
</button> |
| 948 |
</div> |
| 949 |
</div> |
| 950 |
</div> |
| 951 |
|
| 952 |
<!-- Restore Invoice Confirmation Modal --> |
| 953 |
<div id="restore-invoice-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50"> |
| 954 |
<div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative"> |
| 955 |
<button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500"> |
| 956 |
<i class="fas fa-times"></i> |
| 957 |
</button> |
| 958 |
<h2 class="text-xl font-medium text-gray-900 mb-4">Restore Invoice</h2> |
| 959 |
<p class="text-sm text-gray-500 mb-4">Are you sure you want to restore invoice <span id="restore-invoice-number" class="font-medium"></span>?</p> |
| 960 |
|
| 961 |
<div class="mt-5 flex justify-end space-x-3"> |
| 962 |
<button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 963 |
Cancel |
| 964 |
</button> |
| 965 |
<button type="button" id="confirm-restore" class="bg-indigo-600 py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 966 |
Restore Invoice |
| 967 |
</button> |
| 968 |
</div> |
| 969 |
</div> |
| 970 |
</div> |
| 971 |
|
| 972 |
<!-- Permanently Delete Invoice Confirmation Modal --> |
| 973 |
<div id="delete-permanently-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50"> |
| 974 |
<div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative"> |
| 975 |
<button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500"> |
| 976 |
<i class="fas fa-times"></i> |
| 977 |
</button> |
| 978 |
<h2 class="text-xl font-medium text-gray-900 mb-4">Delete Invoice Permanently</h2> |
| 979 |
<p class="text-sm text-gray-500 mb-4">Are you sure you want to permanently delete invoice <span id="delete-permanently-number" class="font-medium"></span>? This action cannot be undone.</p> |
| 980 |
|
| 981 |
<div class="mt-5 flex justify-end space-x-3"> |
| 982 |
<button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 983 |
Cancel |
| 984 |
</button> |
| 985 |
<button type="button" id="confirm-delete-permanently" class="bg-red-600 py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"> |
| 986 |
Delete Permanently |
| 987 |
</button> |
| 988 |
</div> |
| 989 |
</div> |
| 990 |
</div> |
| 991 |
|
| 992 |
<!-- Publish Invoice Confirmation Modal --> |
| 993 |
<div id="publish-invoice-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50"> |
| 994 |
<div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative"> |
| 995 |
<button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500"> |
| 996 |
<i class="fas fa-times"></i> |
| 997 |
</button> |
| 998 |
<h2 class="text-xl font-medium text-gray-900 mb-4">Publish Invoice</h2> |
| 999 |
<p class="text-sm text-gray-500 mb-4">Are you sure you want to publish invoice <span id="publish-invoice-number" class="font-medium"></span>?</p> |
| 1000 |
|
| 1001 |
<div class="mt-5 flex justify-end space-x-3"> |
| 1002 |
<button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 1003 |
Cancel |
| 1004 |
</button> |
| 1005 |
<button type="button" id="confirm-publish" class="bg-green-600 py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"> |
| 1006 |
Publish Invoice |
| 1007 |
</button> |
| 1008 |
</div> |
| 1009 |
</div> |
| 1010 |
</div> |
| 1011 |
|
| 1012 |
<!-- Draft Invoice Confirmation Modal --> |
| 1013 |
<div id="draft-invoice-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50"> |
| 1014 |
<div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative"> |
| 1015 |
<button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500"> |
| 1016 |
<i class="fas fa-times"></i> |
| 1017 |
</button> |
| 1018 |
<h2 class="text-xl font-medium text-gray-900 mb-4">Set Invoice to Draft</h2> |
| 1019 |
<p class="text-sm text-gray-500 mb-4">Are you sure you want to set invoice <span id="draft-invoice-number" class="font-medium"></span> to draft status?</p> |
| 1020 |
|
| 1021 |
<div class="mt-5 flex justify-end space-x-3"> |
| 1022 |
<button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 1023 |
Cancel |
| 1024 |
</button> |
| 1025 |
<button type="button" id="confirm-draft" class="bg-indigo-600 py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 1026 |
Set to Draft |
| 1027 |
</button> |
| 1028 |
</div> |
| 1029 |
</div> |
| 1030 |
</div> |
| 1031 |
|
| 1032 |
<style> |
| 1033 |
/* Modern row actions styling */ |
| 1034 |
.row-actions { |
| 1035 |
visibility: hidden; |
| 1036 |
font-size: 12px; |
| 1037 |
padding-top: 4px; |
| 1038 |
display: flex; |
| 1039 |
gap: 8px; |
| 1040 |
} |
| 1041 |
|
| 1042 |
tr:hover .row-actions { |
| 1043 |
visibility: visible; |
| 1044 |
} |
| 1045 |
|
| 1046 |
.row-actions span a { |
| 1047 |
color: #6366f1; /* indigo-500 */ |
| 1048 |
text-decoration: none; |
| 1049 |
transition: all 0.2s; |
| 1050 |
display: inline-flex; |
| 1051 |
align-items: center; |
| 1052 |
} |
| 1053 |
|
| 1054 |
.row-actions span a:hover { |
| 1055 |
color: #4f46e5; /* indigo-600 */ |
| 1056 |
text-decoration: none; |
| 1057 |
} |
| 1058 |
|
| 1059 |
.row-actions span a.text-red-600 { |
| 1060 |
color: #dc2626; /* red-600 */ |
| 1061 |
} |
| 1062 |
|
| 1063 |
.row-actions span a.text-red-600:hover { |
| 1064 |
color: #b91c1c; /* red-700 */ |
| 1065 |
} |
| 1066 |
|
| 1067 |
/* Add subtle separators between action links */ |
| 1068 |
.row-actions span:not(:last-child)::after { |
| 1069 |
content: ""; |
| 1070 |
display: inline-block; |
| 1071 |
width: 3px; |
| 1072 |
height: 3px; |
| 1073 |
border-radius: 50%; |
| 1074 |
background-color: #d1d5db; /* gray-300 */ |
| 1075 |
margin-left: 8px; |
| 1076 |
vertical-align: middle; |
| 1077 |
} |
| 1078 |
|
| 1079 |
/* Premium duplicate button styling */ |
| 1080 |
.premium-feature .premium-duplicate-btn { |
| 1081 |
border: 1.5px solid #FFD700; |
| 1082 |
background: linear-gradient(90deg, #fffbe6 0%, #fff8dc 100%); |
| 1083 |
box-shadow: 0 0 6px 0 #ffe06633; |
| 1084 |
position: relative; |
| 1085 |
transition: box-shadow 0.2s, border-color 0.2s; |
| 1086 |
} |
| 1087 |
.premium-feature .premium-duplicate-btn:hover { |
| 1088 |
box-shadow: 0 0 16px 2px #ffd70099, 0 0 0 2px #fffbe6; |
| 1089 |
border-color: #FFA500; |
| 1090 |
animation: shimmer 1.2s linear infinite; |
| 1091 |
} |
| 1092 |
@keyframes shimmer { |
| 1093 |
0% { box-shadow: 0 0 6px 0 #ffe06633; } |
| 1094 |
50% { box-shadow: 0 0 24px 4px #ffd700cc; } |
| 1095 |
100% { box-shadow: 0 0 6px 0 #ffe06633; } |
| 1096 |
} |
| 1097 |
.premium-feature .premium-tooltip { |
| 1098 |
opacity: 0; |
| 1099 |
pointer-events: none; |
| 1100 |
position: absolute; |
| 1101 |
left: 50%; |
| 1102 |
top: 120%; |
| 1103 |
transform: translateX(-50%); |
| 1104 |
background: #222; |
| 1105 |
color: #fffbe6; |
| 1106 |
padding: 3px 10px; |
| 1107 |
border-radius: 4px; |
| 1108 |
font-size: 11px; |
| 1109 |
white-space: nowrap; |
| 1110 |
z-index: 10; |
| 1111 |
transition: opacity 0.2s; |
| 1112 |
box-shadow: 0 2px 8px #0002; |
| 1113 |
} |
| 1114 |
.premium-feature .group:hover .premium-tooltip { |
| 1115 |
opacity: 1; |
| 1116 |
} |
| 1117 |
.premium-feature .premium-duplicate-icon { |
| 1118 |
filter: drop-shadow(0 0 2px #ffd700); |
| 1119 |
} |
| 1120 |
|
| 1121 |
/* Premium Export Button Styling - Minimal */ |
| 1122 |
.premium-export-btn { |
| 1123 |
border-color: #d1d5db; |
| 1124 |
transition: all 0.2s ease; |
| 1125 |
} |
| 1126 |
|
| 1127 |
.premium-export-btn:hover { |
| 1128 |
border-color: #9ca3af; |
| 1129 |
background-color: #f9fafb; |
| 1130 |
} |
| 1131 |
|
| 1132 |
.premium-export-btn .crown-icon { |
| 1133 |
filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.2)); |
| 1134 |
color: #9333ea; |
| 1135 |
transition: all 0.3s ease; |
| 1136 |
} |
| 1137 |
|
| 1138 |
.premium-export-btn:hover .crown-icon { |
| 1139 |
filter: drop-shadow(0 2px 6px rgba(147, 51, 234, 0.3)); |
| 1140 |
transform: scale(1.1); |
| 1141 |
color: #7c3aed; |
| 1142 |
} |
| 1143 |
</style> |
| 1144 |
|
| 1145 |
<?php |
| 1146 |
// Localize script data for AJAX operations |
| 1147 |
$script_data = array( |
| 1148 |
'ajaxUrl' => admin_url('admin-ajax.php'), |
| 1149 |
'nonce' => wp_create_nonce('easy_invoice_nonce') |
| 1150 |
); |
| 1151 |
?> |
| 1152 |
<script type="text/javascript"> |
| 1153 |
// Make sure easyInvoice object is available for the AJAX operations |
| 1154 |
var easyInvoice = <?php echo json_encode($script_data); ?>; |
| 1155 |
|
| 1156 |
jQuery(document).ready(function($) { |
| 1157 |
var selectedInvoiceId = null; |
| 1158 |
|
| 1159 |
// Select All Checkboxes |
| 1160 |
$("#select-all").on("click", function() { |
| 1161 |
$(".invoice-checkbox").prop("checked", $(this).prop("checked")); |
| 1162 |
}); |
| 1163 |
|
| 1164 |
// Check if any checkbox is unchecked and update select all |
| 1165 |
$(".invoice-checkbox").on("click", function() { |
| 1166 |
if (!$(this).prop("checked")) { |
| 1167 |
$("#select-all").prop("checked", false); |
| 1168 |
} else { |
| 1169 |
// Check if all checkboxes are checked |
| 1170 |
var allChecked = true; |
| 1171 |
$(".invoice-checkbox").each(function() { |
| 1172 |
if (!$(this).prop("checked")) { |
| 1173 |
allChecked = false; |
| 1174 |
return false; // Break the loop |
| 1175 |
} |
| 1176 |
}); |
| 1177 |
$("#select-all").prop("checked", allChecked); |
| 1178 |
} |
| 1179 |
}); |
| 1180 |
|
| 1181 |
// Open trash modal when Trash link is clicked |
| 1182 |
$(".trash-invoice").on("click", function(e) { |
| 1183 |
e.preventDefault(); |
| 1184 |
selectedInvoiceId = $(this).data("invoice-id"); |
| 1185 |
var invoiceNumber = $(this).data("invoice-number"); |
| 1186 |
|
| 1187 |
$("#trash-invoice-number").text(invoiceNumber); |
| 1188 |
$("#trash-invoice-modal").removeClass("hidden"); |
| 1189 |
}); |
| 1190 |
|
| 1191 |
// Open restore modal when Restore link is clicked |
| 1192 |
$(".restore-invoice").on("click", function(e) { |
| 1193 |
e.preventDefault(); |
| 1194 |
selectedInvoiceId = $(this).data("invoice-id"); |
| 1195 |
var invoiceNumber = $(this).data("invoice-number"); |
| 1196 |
|
| 1197 |
$("#restore-invoice-number").text(invoiceNumber); |
| 1198 |
$("#restore-invoice-modal").removeClass("hidden"); |
| 1199 |
}); |
| 1200 |
|
| 1201 |
// Open delete permanently modal |
| 1202 |
$(".delete-permanently").on("click", function(e) { |
| 1203 |
e.preventDefault(); |
| 1204 |
selectedInvoiceId = $(this).data("invoice-id"); |
| 1205 |
var invoiceNumber = $(this).data("invoice-number"); |
| 1206 |
|
| 1207 |
$("#delete-permanently-number").text(invoiceNumber); |
| 1208 |
$("#delete-permanently-modal").removeClass("hidden"); |
| 1209 |
}); |
| 1210 |
|
| 1211 |
// Open publish invoice modal |
| 1212 |
$(".publish-invoice").on("click", function(e) { |
| 1213 |
e.preventDefault(); |
| 1214 |
selectedInvoiceId = $(this).data("invoice-id"); |
| 1215 |
var invoiceNumber = $(this).data("invoice-number"); |
| 1216 |
|
| 1217 |
$("#publish-invoice-number").text(invoiceNumber); |
| 1218 |
$("#publish-invoice-modal").removeClass("hidden"); |
| 1219 |
}); |
| 1220 |
|
| 1221 |
// Open draft invoice modal |
| 1222 |
$(".draft-invoice").on("click", function(e) { |
| 1223 |
e.preventDefault(); |
| 1224 |
selectedInvoiceId = $(this).data("invoice-id"); |
| 1225 |
var invoiceNumber = $(this).data("invoice-number"); |
| 1226 |
|
| 1227 |
$("#draft-invoice-number").text(invoiceNumber); |
| 1228 |
$("#draft-invoice-modal").removeClass("hidden"); |
| 1229 |
}); |
| 1230 |
|
| 1231 |
// Handle Email Invoice click |
| 1232 |
$(".email-invoice").on("click", function(e) { |
| 1233 |
e.preventDefault(); |
| 1234 |
var invoiceId = $(this).data("invoice-id"); |
| 1235 |
var invoiceNumber = $(this).data("invoice-number"); |
| 1236 |
|
| 1237 |
// Use the new confirmation system instead of browser confirm |
| 1238 |
if (typeof EasyInvoiceConfirmation !== 'undefined') { |
| 1239 |
EasyInvoiceConfirmation.confirmAction('send email', 'invoice ' + invoiceNumber, function() { |
| 1240 |
// Show loading state |
| 1241 |
var $link = $(this); |
| 1242 |
var originalText = $link.text(); |
| 1243 |
$link.text("Sending...").css("opacity", "0.7"); |
| 1244 |
|
| 1245 |
// Send AJAX request |
| 1246 |
$.ajax({ |
| 1247 |
url: easyInvoice.ajaxUrl, |
| 1248 |
type: "POST", |
| 1249 |
data: { |
| 1250 |
action: "easy_invoice_send_email", |
| 1251 |
invoice_id: invoiceId, |
| 1252 |
nonce: easyInvoice.nonce |
| 1253 |
}, |
| 1254 |
success: function(response) { |
| 1255 |
if (response.success) { |
| 1256 |
EasyInvoiceToast.success("Email sent successfully!"); |
| 1257 |
} else { |
| 1258 |
EasyInvoiceToast.error(response.data.message || "Error sending email"); |
| 1259 |
} |
| 1260 |
// Reset link text |
| 1261 |
$link.text(originalText).css("opacity", "1"); |
| 1262 |
}, |
| 1263 |
error: function() { |
| 1264 |
EasyInvoiceToast.error("Error connecting to server"); |
| 1265 |
// Reset link text |
| 1266 |
$link.text(originalText).css("opacity", "1"); |
| 1267 |
} |
| 1268 |
}); |
| 1269 |
}); |
| 1270 |
} else { |
| 1271 |
// Fallback to browser confirm if toast system not available |
| 1272 |
if (confirm("Are you sure you want to send invoice " + invoiceNumber + " via email?")) { |
| 1273 |
// Show loading state |
| 1274 |
var $link = $(this); |
| 1275 |
var originalText = $link.text(); |
| 1276 |
$link.text("Sending...").css("opacity", "0.7"); |
| 1277 |
|
| 1278 |
// Send AJAX request |
| 1279 |
$.ajax({ |
| 1280 |
url: easyInvoice.ajaxUrl, |
| 1281 |
type: "POST", |
| 1282 |
data: { |
| 1283 |
action: "easy_invoice_send_email", |
| 1284 |
invoice_id: invoiceId, |
| 1285 |
nonce: easyInvoice.nonce |
| 1286 |
}, |
| 1287 |
success: function(response) { |
| 1288 |
if (response.success) { |
| 1289 |
EasyInvoiceToast.success("Email sent successfully!"); |
| 1290 |
} else { |
| 1291 |
EasyInvoiceToast.error(response.data.message || "Error sending email"); |
| 1292 |
} |
| 1293 |
// Reset link text |
| 1294 |
$link.text(originalText).css("opacity", "1"); |
| 1295 |
}, |
| 1296 |
error: function() { |
| 1297 |
EasyInvoiceToast.error("Error connecting to server"); |
| 1298 |
// Reset link text |
| 1299 |
$link.text(originalText).css("opacity", "1"); |
| 1300 |
} |
| 1301 |
}); |
| 1302 |
} |
| 1303 |
} |
| 1304 |
}); |
| 1305 |
|
| 1306 |
// Handle Download PDF click |
| 1307 |
$(".download-pdf").on("click", function(e) { |
| 1308 |
e.preventDefault(); |
| 1309 |
var invoiceId = $(this).data("invoice-id"); |
| 1310 |
var invoiceNumber = $(this).data("invoice-number"); |
| 1311 |
|
| 1312 |
// Show loading state |
| 1313 |
var $link = $(this); |
| 1314 |
var originalText = $link.text(); |
| 1315 |
$link.text("Generating PDF...").css("opacity", "0.7"); |
| 1316 |
|
| 1317 |
// Get invoice data via AJAX and generate PDF |
| 1318 |
$.ajax({ |
| 1319 |
url: easyInvoice.ajaxUrl, |
| 1320 |
type: "POST", |
| 1321 |
data: { |
| 1322 |
action: "easy_invoice_download_invoice_pdf", |
| 1323 |
invoice_id: invoiceId, |
| 1324 |
nonce: easyInvoice.nonce |
| 1325 |
}, |
| 1326 |
success: function(response) { |
| 1327 |
if (response.success && response.data.download_url) { |
| 1328 |
// Open the download URL in a new tab |
| 1329 |
window.open(response.data.download_url, '_blank'); |
| 1330 |
EasyInvoiceToast.success("PDF download started"); |
| 1331 |
} else { |
| 1332 |
EasyInvoiceToast.error(response.data.message || "Error preparing PDF data"); |
| 1333 |
} |
| 1334 |
// Reset link text |
| 1335 |
$link.text(originalText).css("opacity", "1"); |
| 1336 |
}, |
| 1337 |
error: function() { |
| 1338 |
EasyInvoiceToast.error("Error connecting to server"); |
| 1339 |
// Reset link text |
| 1340 |
$link.text(originalText).css("opacity", "1"); |
| 1341 |
} |
| 1342 |
}); |
| 1343 |
}); |
| 1344 |
|
| 1345 |
// Close modals when Close button or Cancel is clicked |
| 1346 |
$(".close-modal, .cancel-modal").on("click", function() { |
| 1347 |
$(".fixed").addClass("hidden"); |
| 1348 |
selectedInvoiceId = null; |
| 1349 |
}); |
| 1350 |
|
| 1351 |
// Handle trash confirmation |
| 1352 |
$("#confirm-trash").on("click", function() { |
| 1353 |
if (selectedInvoiceId) { |
| 1354 |
// Send AJAX request to trash the invoice |
| 1355 |
$.ajax({ |
| 1356 |
url: easyInvoice.ajaxUrl, |
| 1357 |
type: "POST", |
| 1358 |
data: { |
| 1359 |
action: "easy_invoice_trash_invoice", |
| 1360 |
invoice_id: selectedInvoiceId, |
| 1361 |
nonce: easyInvoice.nonce |
| 1362 |
}, |
| 1363 |
success: function(response) { |
| 1364 |
if (response.success) { |
| 1365 |
// Reload the page to show updated list |
| 1366 |
window.location.reload(); |
| 1367 |
} else { |
| 1368 |
EasyInvoiceToast.error(response.data.message || "Error trashing invoice"); |
| 1369 |
} |
| 1370 |
}, |
| 1371 |
error: function() { |
| 1372 |
EasyInvoiceToast.error("Error connecting to server"); |
| 1373 |
} |
| 1374 |
}); |
| 1375 |
} |
| 1376 |
}); |
| 1377 |
|
| 1378 |
// Handle restore confirmation |
| 1379 |
$("#confirm-restore").on("click", function() { |
| 1380 |
if (selectedInvoiceId) { |
| 1381 |
// Send AJAX request to restore the invoice |
| 1382 |
$.ajax({ |
| 1383 |
url: easyInvoice.ajaxUrl, |
| 1384 |
type: "POST", |
| 1385 |
data: { |
| 1386 |
action: "easy_invoice_restore_invoice", |
| 1387 |
invoice_id: selectedInvoiceId, |
| 1388 |
nonce: easyInvoice.nonce |
| 1389 |
}, |
| 1390 |
success: function(response) { |
| 1391 |
if (response.success) { |
| 1392 |
// Reload the page to show updated list |
| 1393 |
window.location.reload(); |
| 1394 |
} else { |
| 1395 |
EasyInvoiceToast.error(response.data.message || "Error restoring invoice"); |
| 1396 |
} |
| 1397 |
}, |
| 1398 |
error: function() { |
| 1399 |
EasyInvoiceToast.error("Error connecting to server"); |
| 1400 |
} |
| 1401 |
}); |
| 1402 |
} |
| 1403 |
}); |
| 1404 |
|
| 1405 |
// Handle publish confirmation |
| 1406 |
$("#confirm-publish").on("click", function() { |
| 1407 |
if (selectedInvoiceId) { |
| 1408 |
// Send AJAX request to publish the invoice |
| 1409 |
$.ajax({ |
| 1410 |
url: easyInvoice.ajaxUrl, |
| 1411 |
type: "POST", |
| 1412 |
data: { |
| 1413 |
action: "easy_invoice_publish_invoice", |
| 1414 |
invoice_id: selectedInvoiceId, |
| 1415 |
nonce: easyInvoice.nonce |
| 1416 |
}, |
| 1417 |
success: function(response) { |
| 1418 |
if (response.success) { |
| 1419 |
// Reload the page to show updated list |
| 1420 |
window.location.reload(); |
| 1421 |
} else { |
| 1422 |
EasyInvoiceToast.error(response.data.message || "Error publishing invoice"); |
| 1423 |
} |
| 1424 |
}, |
| 1425 |
error: function() { |
| 1426 |
EasyInvoiceToast.error("Error connecting to server"); |
| 1427 |
} |
| 1428 |
}); |
| 1429 |
} |
| 1430 |
}); |
| 1431 |
|
| 1432 |
// Handle draft confirmation |
| 1433 |
$("#confirm-draft").on("click", function() { |
| 1434 |
if (selectedInvoiceId) { |
| 1435 |
// Send AJAX request to set the invoice to draft |
| 1436 |
$.ajax({ |
| 1437 |
url: easyInvoice.ajaxUrl, |
| 1438 |
type: "POST", |
| 1439 |
data: { |
| 1440 |
action: "easy_invoice_draft_invoice", |
| 1441 |
invoice_id: selectedInvoiceId, |
| 1442 |
nonce: easyInvoice.nonce |
| 1443 |
}, |
| 1444 |
success: function(response) { |
| 1445 |
if (response.success) { |
| 1446 |
// Reload the page to show updated list |
| 1447 |
window.location.reload(); |
| 1448 |
} else { |
| 1449 |
EasyInvoiceToast.error(response.data.message || "Error setting invoice to draft"); |
| 1450 |
} |
| 1451 |
}, |
| 1452 |
error: function() { |
| 1453 |
EasyInvoiceToast.error("Error connecting to server"); |
| 1454 |
} |
| 1455 |
}); |
| 1456 |
} |
| 1457 |
}); |
| 1458 |
|
| 1459 |
// Handle delete permanently confirmation |
| 1460 |
$("#confirm-delete-permanently").on("click", function() { |
| 1461 |
if (selectedInvoiceId) { |
| 1462 |
// Send AJAX request to delete the invoice permanently |
| 1463 |
$.ajax({ |
| 1464 |
url: easyInvoice.ajaxUrl, |
| 1465 |
type: "POST", |
| 1466 |
data: { |
| 1467 |
action: "easy_invoice_delete_invoice_permanently", |
| 1468 |
invoice_id: selectedInvoiceId, |
| 1469 |
nonce: easyInvoice.nonce |
| 1470 |
}, |
| 1471 |
success: function(response) { |
| 1472 |
if (response.success) { |
| 1473 |
// Reload the page to show updated list |
| 1474 |
window.location.reload(); |
| 1475 |
} else { |
| 1476 |
EasyInvoiceToast.error(response.data.message || "Error deleting invoice"); |
| 1477 |
} |
| 1478 |
}, |
| 1479 |
error: function() { |
| 1480 |
EasyInvoiceToast.error("Error connecting to server"); |
| 1481 |
} |
| 1482 |
}); |
| 1483 |
} |
| 1484 |
}); |
| 1485 |
|
| 1486 |
// Form submission validation for bulk actions |
| 1487 |
$("#bulk-action-form").on("submit", function(e) { |
| 1488 |
// Check if an action is selected |
| 1489 |
var bulkAction = $("select[name='bulk_action']").val(); |
| 1490 |
if (!bulkAction) { |
| 1491 |
e.preventDefault(); |
| 1492 |
EasyInvoiceToast.warning("Please select an action"); |
| 1493 |
return false; |
| 1494 |
} |
| 1495 |
|
| 1496 |
// Check if at least one invoice is selected |
| 1497 |
var checkedInvoices = $(".invoice-checkbox:checked").length; |
| 1498 |
if (checkedInvoices < 1) { |
| 1499 |
e.preventDefault(); |
| 1500 |
EasyInvoiceToast.warning("Please select at least one invoice"); |
| 1501 |
return false; |
| 1502 |
} |
| 1503 |
|
| 1504 |
// Confirm bulk actions using the new confirmation system |
| 1505 |
if (bulkAction === 'trash') { |
| 1506 |
e.preventDefault(); |
| 1507 |
if (typeof EasyInvoiceConfirmation !== 'undefined') { |
| 1508 |
EasyInvoiceConfirmation.confirmAction('move to trash', 'the selected invoices', function() { |
| 1509 |
$("#bulk-action-form")[0].submit(); |
| 1510 |
}); |
| 1511 |
} else { |
| 1512 |
// Fallback to browser confirm |
| 1513 |
if (!confirm('Are you sure you want to move the selected invoices to trash?')) { |
| 1514 |
return false; |
| 1515 |
} |
| 1516 |
} |
| 1517 |
return false; |
| 1518 |
} else if (bulkAction === 'delete') { |
| 1519 |
e.preventDefault(); |
| 1520 |
if (typeof EasyInvoiceConfirmation !== 'undefined') { |
| 1521 |
EasyInvoiceConfirmation.confirmAction('permanently delete', 'the selected invoices', function() { |
| 1522 |
$("#bulk-action-form")[0].submit(); |
| 1523 |
}); |
| 1524 |
} else { |
| 1525 |
// Fallback to browser confirm |
| 1526 |
if (!confirm('Are you sure you want to permanently delete the selected invoices? This cannot be undone.')) { |
| 1527 |
return false; |
| 1528 |
} |
| 1529 |
} |
| 1530 |
return false; |
| 1531 |
} |
| 1532 |
|
| 1533 |
return true; |
| 1534 |
}); |
| 1535 |
|
| 1536 |
// Dismiss notifications |
| 1537 |
$(".notification-dismiss").on("click", function() { |
| 1538 |
$(this).closest(".rounded-md").fadeOut(300, function() { |
| 1539 |
$(this).remove(); |
| 1540 |
}); |
| 1541 |
}); |
| 1542 |
|
| 1543 |
// Duplicate invoice functionality |
| 1544 |
$(document).on('click', '.duplicate-invoice-pro-placeholder', function(e) { |
| 1545 |
e.preventDefault(); |
| 1546 |
EasyInvoiceConfirmation.showFeatureUpgrade('Invoice Duplication'); |
| 1547 |
}); |
| 1548 |
|
| 1549 |
// Export invoice functionality |
| 1550 |
$(document).on('click', '.export-invoice-pro-placeholder', function(e) { |
| 1551 |
e.preventDefault(); |
| 1552 |
EasyInvoiceConfirmation.showFeatureUpgrade('Invoice Export', 'Export your invoices to CSV, Excel, or PDF format for backup, analysis, or sharing with your team.'); |
| 1553 |
}); |
| 1554 |
|
| 1555 |
// Handle "Create Sample Invoice" button click |
| 1556 |
$('#easy-invoice-create-sample').on('click', function() { |
| 1557 |
var $button = $(this); |
| 1558 |
var original_text = $button.html(); |
| 1559 |
$button.html('Creating...').prop('disabled', true); |
| 1560 |
|
| 1561 |
$.ajax({ |
| 1562 |
url: ajaxurl, // WordPress AJAX URL |
| 1563 |
type: 'POST', |
| 1564 |
data: { |
| 1565 |
action: 'easy_invoice_create_sample_invoice', |
| 1566 |
nonce: '<?php echo wp_create_nonce("easy_invoice_admin_nonce"); ?>' // Make sure this nonce matches the one checked in PHP |
| 1567 |
}, |
| 1568 |
success: function(response) { |
| 1569 |
if (response.success) { |
| 1570 |
EasyInvoiceToast.success(response.data.message + ' You can edit it at: ' + response.data.edit_link); |
| 1571 |
// Optionally, refresh the page or redirect |
| 1572 |
window.location.reload(); |
| 1573 |
} else { |
| 1574 |
EasyInvoiceToast.error('Error: ' + response.data.message); |
| 1575 |
} |
| 1576 |
}, |
| 1577 |
error: function(jqXHR, textStatus, errorThrown) { |
| 1578 |
EasyInvoiceToast.error('AJAX Error: ' + textStatus + ' - ' + errorThrown); |
| 1579 |
}, |
| 1580 |
complete: function() { |
| 1581 |
$button.html(original_text).prop('disabled', false); |
| 1582 |
} |
| 1583 |
}); |
| 1584 |
}); |
| 1585 |
}); |
| 1586 |
</script> |
| 1587 |
|
| 1588 |
<!-- New Invoice Modal --> |
| 1589 |
<div id="new-invoice-modal" class="fixed inset-0 bg-gray-600 bg-opacity-75 overflow-y-auto h-full w-full z-50 hidden"> |
| 1590 |
<div class="relative mx-auto p-6 border w-96 shadow-xl rounded-lg bg-white my-20"> |
| 1591 |
<div class="flex justify-between items-center mb-6"> |
| 1592 |
<h3 class="text-xl font-semibold text-gray-900"><?php esc_html_e('Create New Invoice', 'easy-invoice'); ?></h3> |
| 1593 |
<button type="button" id="close-new-invoice-modal" class="text-gray-400 hover:text-gray-600 transition-colors duration-200"> |
| 1594 |
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 1595 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> |
| 1596 |
</svg> |
| 1597 |
</button> |
| 1598 |
</div> |
| 1599 |
|
| 1600 |
<form id="new-invoice-form" class="space-y-6"> |
| 1601 |
<div> |
| 1602 |
<label for="new_invoice_title" class="block text-sm font-medium text-gray-700 mb-2">Invoice Title *</label> |
| 1603 |
<input type="text" |
| 1604 |
name="new_invoice_title" |
| 1605 |
id="new_invoice_title" |
| 1606 |
required |
| 1607 |
placeholder="Enter invoice title..." |
| 1608 |
class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-3 px-4 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm transition-colors duration-200"> |
| 1609 |
<p class="mt-2 text-sm text-gray-500">This will be the title of your invoice</p> |
| 1610 |
</div> |
| 1611 |
|
| 1612 |
<div class="flex justify-end space-x-3 pt-4"> |
| 1613 |
<button type="button" id="cancel-new-invoice" |
| 1614 |
class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200"> |
| 1615 |
Cancel |
| 1616 |
</button> |
| 1617 |
<button type="submit" |
| 1618 |
class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200"> |
| 1619 |
Create Invoice |
| 1620 |
</button> |
| 1621 |
</div> |
| 1622 |
</form> |
| 1623 |
|
| 1624 |
<div id="new-invoice-message" class="mt-4 hidden"></div> |
| 1625 |
</div> |
| 1626 |
</div> |
| 1627 |
|
| 1628 |
<script> |
| 1629 |
jQuery(document).ready(function($) { |
| 1630 |
// Initialize easyInvoice object for AJAX calls |
| 1631 |
window.easyInvoice = { |
| 1632 |
ajaxUrl: '<?php echo admin_url('admin-ajax.php'); ?>', |
| 1633 |
nonce: '<?php echo wp_create_nonce('easy_invoice_nonce'); ?>', |
| 1634 |
isPro: <?php echo easy_invoice_has_pro() ? 'true' : 'false'; ?> |
| 1635 |
}; |
| 1636 |
|
| 1637 |
// New Invoice Modal Functionality |
| 1638 |
$('#create-new-invoice-btn').on('click', function(e) { |
| 1639 |
e.preventDefault(); // Prevent the default link behavior |
| 1640 |
$('#new-invoice-modal').removeClass('hidden'); |
| 1641 |
$('#new_invoice_title').focus(); |
| 1642 |
}); |
| 1643 |
|
| 1644 |
// Handle "Create your first invoice" links |
| 1645 |
$(document).on('click', '.create-first-invoice', function(e) { |
| 1646 |
e.preventDefault(); // Prevent the default link behavior |
| 1647 |
$('#new-invoice-modal').removeClass('hidden'); |
| 1648 |
$('#new_invoice_title').focus(); |
| 1649 |
}); |
| 1650 |
|
| 1651 |
$('#close-new-invoice-modal, #cancel-new-invoice').on('click', function() { |
| 1652 |
$('#new-invoice-modal').addClass('hidden'); |
| 1653 |
$('#new-invoice-form')[0].reset(); |
| 1654 |
$('#new-invoice-message').addClass('hidden').html(''); |
| 1655 |
}); |
| 1656 |
|
| 1657 |
// Close modal when clicking outside |
| 1658 |
$('#new-invoice-modal').on('click', function(e) { |
| 1659 |
if (e.target === this) { |
| 1660 |
$(this).addClass('hidden'); |
| 1661 |
$('#new-invoice-form')[0].reset(); |
| 1662 |
$('#new-invoice-message').addClass('hidden').html(''); |
| 1663 |
} |
| 1664 |
}); |
| 1665 |
|
| 1666 |
// Handle form submission |
| 1667 |
$('#new-invoice-form').on('submit', function(e) { |
| 1668 |
e.preventDefault(); |
| 1669 |
|
| 1670 |
var $form = $(this); |
| 1671 |
var $submitBtn = $form.find('button[type="submit"]'); |
| 1672 |
var $message = $('#new-invoice-message'); |
| 1673 |
var title = $('#new_invoice_title').val().trim(); |
| 1674 |
|
| 1675 |
if (!title) { |
| 1676 |
$message.removeClass('hidden').html('<div class="text-red-600 text-sm">Please enter an invoice title.</div>'); |
| 1677 |
return; |
| 1678 |
} |
| 1679 |
|
| 1680 |
// Show loading state |
| 1681 |
$submitBtn.prop('disabled', true).html('Creating...'); |
| 1682 |
$message.addClass('hidden'); |
| 1683 |
|
| 1684 |
$.ajax({ |
| 1685 |
url: easyInvoice.ajaxUrl, |
| 1686 |
type: 'POST', |
| 1687 |
data: { |
| 1688 |
action: 'easy_invoice_create_new_invoice', |
| 1689 |
title: title, |
| 1690 |
nonce: easyInvoice.nonce |
| 1691 |
}, |
| 1692 |
success: function(response) { |
| 1693 |
if (response.success) { |
| 1694 |
// Show success message |
| 1695 |
$message.removeClass('hidden').html('<div class="text-green-600 text-sm">Invoice created successfully! Redirecting...</div>'); |
| 1696 |
|
| 1697 |
// Redirect to the builder page with the new invoice ID |
| 1698 |
setTimeout(function() { |
| 1699 |
window.location.href = response.data.redirect_url; |
| 1700 |
}, 1000); |
| 1701 |
} else { |
| 1702 |
$message.removeClass('hidden').html('<div class="text-red-600 text-sm">' + (response.data.message || 'Error creating invoice.') + '</div>'); |
| 1703 |
$submitBtn.prop('disabled', false).html('Create Invoice'); |
| 1704 |
} |
| 1705 |
}, |
| 1706 |
error: function() { |
| 1707 |
$message.removeClass('hidden').html('<div class="text-red-600 text-sm">Error connecting to server. Please try again.</div>'); |
| 1708 |
$submitBtn.prop('disabled', false).html('Create Invoice'); |
| 1709 |
} |
| 1710 |
}); |
| 1711 |
}); |
| 1712 |
}); |
| 1713 |
</script> |
| 1714 |
<?php |
| 1715 |
|