| 1 |
<?php |
| 2 |
// Enqueue CSS and JS files |
| 3 |
wp_enqueue_style('easy-invoice-form', plugin_dir_url(__FILE__) . '../../assets/css/invoice-form.css', array(), '1.0.0'); |
| 4 |
wp_enqueue_script('easy-invoice-form', plugin_dir_url(__FILE__) . '../../assets/js/invoice-form.js', array('jquery'), '1.0.0', true); |
| 5 |
|
| 6 |
// Initialize the form manager |
| 7 |
$form_manager = new EasyInvoice\Forms\Invoice\InvoiceFormManager(); |
| 8 |
|
| 9 |
// Get field configuration for JavaScript |
| 10 |
$invoice_field_config = $form_manager->getFieldConfigForJavaScript(); |
| 11 |
|
| 12 |
// Trigger form initialization to allow pro version to register new elements |
| 13 |
do_action('easy_invoice_form_init'); |
| 14 |
|
| 15 |
$tabs = $form_manager->getTabs(); |
| 16 |
$templates = $form_manager->getTemplates(); |
| 17 |
|
| 18 |
// Create a separate array for browse templates modal with all available templates |
| 19 |
$all_templates = apply_filters('easy_invoice_invoice_templates', [ |
| 20 |
'standard' => [ |
| 21 |
'name' => __('Standard', 'easy-invoice'), |
| 22 |
'icon' => 'fas fa-file-invoice', |
| 23 |
'description' => __('Clean and professional standard template', 'easy-invoice'), |
| 24 |
'order' => 1, |
| 25 |
'is_free' => true |
| 26 |
], |
| 27 |
'legacy' => [ |
| 28 |
'name' => __('Legacy', 'easy-invoice'), |
| 29 |
'icon' => 'fas fa-file-alt', |
| 30 |
'description' => __('Clean, minimalist design with traditional business layout', 'easy-invoice'), |
| 31 |
'order' => 2, |
| 32 |
'is_free' => true |
| 33 |
], |
| 34 |
'professional' => [ |
| 35 |
'name' => __('Professional', 'easy-invoice'), |
| 36 |
'icon' => 'fas fa-briefcase', |
| 37 |
'description' => __('Professional business template with modern design', 'easy-invoice'), |
| 38 |
'order' => 3, |
| 39 |
'is_free' => false |
| 40 |
], |
| 41 |
'modern' => [ |
| 42 |
'name' => __('Modern', 'easy-invoice'), |
| 43 |
'icon' => 'fas fa-rocket', |
| 44 |
'description' => __('Modern and sleek design template', 'easy-invoice'), |
| 45 |
'order' => 4, |
| 46 |
'is_free' => false |
| 47 |
], |
| 48 |
'classic' => [ |
| 49 |
'name' => __('Classic', 'easy-invoice'), |
| 50 |
'icon' => 'fas fa-landmark', |
| 51 |
'description' => __('Traditional business template', 'easy-invoice'), |
| 52 |
'order' => 5, |
| 53 |
'is_free' => false |
| 54 |
], |
| 55 |
'minimal' => [ |
| 56 |
'name' => __('Minimal', 'easy-invoice'), |
| 57 |
'icon' => 'fas fa-minus', |
| 58 |
'description' => __('Simple and clean minimal template', 'easy-invoice'), |
| 59 |
'order' => 6, |
| 60 |
'is_free' => false |
| 61 |
], |
| 62 |
|
| 63 |
'corporate' => [ |
| 64 |
'name' => __('Corporate', 'easy-invoice'), |
| 65 |
'icon' => 'fas fa-building', |
| 66 |
'description' => __('Professional corporate template', 'easy-invoice'), |
| 67 |
'order' => 7, |
| 68 |
'is_free' => false |
| 69 |
], |
| 70 |
'elegant' => [ |
| 71 |
'name' => __('Elegant', 'easy-invoice'), |
| 72 |
'icon' => 'fas fa-gem', |
| 73 |
'description' => __('Elegant and sophisticated design', 'easy-invoice'), |
| 74 |
'order' => 8, |
| 75 |
'is_free' => false |
| 76 |
], |
| 77 |
'creative' => [ |
| 78 |
'name' => __('Creative', 'easy-invoice'), |
| 79 |
'icon' => 'fas fa-palette', |
| 80 |
'description' => __('Creative and artistic design template', 'easy-invoice'), |
| 81 |
'order' => 9, |
| 82 |
'is_free' => false |
| 83 |
] |
| 84 |
]); |
| 85 |
|
| 86 |
|
| 87 |
|
| 88 |
// Load client data directly in PHP if invoice has a client |
| 89 |
$client_data = null; |
| 90 |
if ($invoice && $invoice->getClientId()) { |
| 91 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 92 |
if ($client) { |
| 93 |
$client_data = array( |
| 94 |
'id' => $client->getId(), |
| 95 |
'name' => $client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName()), |
| 96 |
'email' => $client->getEmail() ?: '', |
| 97 |
'phone' => $client->getExtraInfo() ?: '', |
| 98 |
'company' => $client->getBusinessClientName() ?: '', |
| 99 |
'address' => $client->getAddress() ?: '', |
| 100 |
'website' => $client->getWebsite() ?: '', |
| 101 |
); |
| 102 |
} |
| 103 |
} |
| 104 |
?> |
| 105 |
|
| 106 |
<style> |
| 107 |
/* Premium Template Styling */ |
| 108 |
.premium-template-blurred { |
| 109 |
position: relative; |
| 110 |
opacity: 0.8; |
| 111 |
transition: all 0.4s ease; |
| 112 |
cursor: pointer; |
| 113 |
border: 2px solid #e5e7eb; |
| 114 |
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); |
| 115 |
transform: scale(0.98); |
| 116 |
} |
| 117 |
|
| 118 |
.premium-template-blurred::before { |
| 119 |
content: ''; |
| 120 |
position: absolute; |
| 121 |
top: 0; |
| 122 |
left: 0; |
| 123 |
right: 0; |
| 124 |
bottom: 0; |
| 125 |
background: linear-gradient(135deg, rgba(139, 92, 246, 0.1) 0%, rgba(168, 85, 247, 0.1) 100%); |
| 126 |
border-radius: 0.5rem; |
| 127 |
z-index: 1; |
| 128 |
pointer-events: none; |
| 129 |
} |
| 130 |
|
| 131 |
.premium-template-blurred::after { |
| 132 |
content: 'PRO'; |
| 133 |
position: absolute; |
| 134 |
top: 8px; |
| 135 |
right: 8px; |
| 136 |
background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 100%); |
| 137 |
color: white; |
| 138 |
font-size: 10px; |
| 139 |
font-weight: 700; |
| 140 |
padding: 2px 6px; |
| 141 |
border-radius: 4px; |
| 142 |
z-index: 2; |
| 143 |
box-shadow: 0 2px 4px rgba(139, 92, 246, 0.3); |
| 144 |
letter-spacing: 0.5px; |
| 145 |
} |
| 146 |
|
| 147 |
.premium-template-blurred:hover { |
| 148 |
opacity: 0.95; |
| 149 |
transform: scale(1.02); |
| 150 |
border-color: #8b5cf6; |
| 151 |
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.15); |
| 152 |
} |
| 153 |
|
| 154 |
.premium-template-blurred:hover::after { |
| 155 |
background: linear-gradient(135deg, #7c3aed 0%, #9333ea 100%); |
| 156 |
box-shadow: 0 4px 8px rgba(139, 92, 246, 0.4); |
| 157 |
} |
| 158 |
|
| 159 |
.premium-template-blurred .template-radio { |
| 160 |
pointer-events: none; |
| 161 |
} |
| 162 |
|
| 163 |
.premium-template-blurred > div { |
| 164 |
position: relative; |
| 165 |
z-index: 3; |
| 166 |
} |
| 167 |
</style> |
| 168 |
|
| 169 |
<div class="bg-white shadow rounded-lg"> |
| 170 |
|
| 171 |
<div class="border-b border-gray-200"> |
| 172 |
<nav class="flex -mb-px" aria-label="<?php esc_attr_e( 'Invoice form sections', 'easy-invoice' ); ?>"> |
| 173 |
<?php foreach ($tabs as $tab): ?> |
| 174 |
<button type="button" |
| 175 |
class="tab-button whitespace-nowrap py-3 px-6 border-b-2 <?php echo $tab['active'] ? 'border-indigo-500 font-medium text-sm text-indigo-600' : 'border-transparent font-medium text-sm text-gray-500 hover:text-gray-700 hover:border-gray-300'; ?> flex flex-col items-center" |
| 176 |
data-tab="<?php echo esc_attr($tab['id']); ?>"> |
| 177 |
<i class="<?php echo esc_attr($tab['icon']); ?> mb-1 text-lg"></i> |
| 178 |
<span><?php echo esc_html($tab['label']); ?></span> |
| 179 |
</button> |
| 180 |
<?php endforeach; ?> |
| 181 |
</nav> |
| 182 |
</div> |
| 183 |
<?php |
| 184 |
// Lifecycle-stage lock check. Addons (PartialPayments) |
| 185 |
// can return false here to mark this invoice as |
| 186 |
// immutable — currently used to freeze paid deposit |
| 187 |
// invoices so admins don't accidentally invalidate |
| 188 |
// the linked balance pair. Default: true (no listener). |
| 189 |
$ei_can_edit = ! $invoice_id || apply_filters('easy_invoice_can_edit_invoice', true, (int) $invoice_id); |
| 190 |
if (! $ei_can_edit && $invoice_id): |
| 191 |
$ei_balance_id = (int) get_post_meta((int) $invoice_id, '_balance_invoice_id', true); |
| 192 |
$ei_paid_date = (string) get_post_meta((int) $invoice_id, '_easy_invoice_payment_date', true); |
| 193 |
?> |
| 194 |
<div class="mx-6 mt-4 mb-4 rounded-md border border-amber-200 bg-amber-50 p-4"> |
| 195 |
<div class="flex items-start"> |
| 196 |
<i class="fas fa-lock text-amber-500 mt-0.5 mr-3"></i> |
| 197 |
<div class="flex-1 text-sm text-amber-800"> |
| 198 |
<p class="font-semibold mb-1"><?php esc_html_e('This deposit invoice is locked.', 'easy-invoice'); ?></p> |
| 199 |
<p class="mb-2"> |
| 200 |
<?php |
| 201 |
if ($ei_paid_date) { |
| 202 |
printf( |
| 203 |
/* translators: %s: payment date */ |
| 204 |
esc_html__('A payment was recorded on %s. Items and totals are frozen so the deposit receipt the customer already saw stays accurate. To add new charges, open the linked balance invoice instead.', 'easy-invoice'), |
| 205 |
esc_html($ei_paid_date) |
| 206 |
); |
| 207 |
} else { |
| 208 |
esc_html_e('A payment has been recorded against this deposit. Items and totals are frozen so the deposit receipt the customer already saw stays accurate. To add new charges, open the linked balance invoice instead.', 'easy-invoice'); |
| 209 |
} |
| 210 |
?> |
| 211 |
</p> |
| 212 |
<?php if ($ei_balance_id > 0): ?> |
| 213 |
<a href="<?php echo esc_url(admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $ei_balance_id)); ?>" |
| 214 |
class="inline-flex items-center px-3 py-1.5 rounded text-xs font-medium bg-amber-600 text-white hover:bg-amber-700"> |
| 215 |
<i class="fas fa-arrow-right mr-1"></i> |
| 216 |
<?php esc_html_e('Open the balance invoice', 'easy-invoice'); ?> |
| 217 |
</a> |
| 218 |
<?php endif; ?> |
| 219 |
</div> |
| 220 |
</div> |
| 221 |
</div> |
| 222 |
<?php endif; ?> |
| 223 |
<form id="invoice-form"<?php echo $ei_can_edit ? '' : ' data-ei-locked="1"'; ?>> |
| 224 |
<input type="hidden" id="invoice-id" name="invoice_id" value="<?php echo $invoice_id ? esc_attr($invoice_id) : ''; ?>"> |
| 225 |
|
| 226 |
<input type="hidden" id="invoice_nonce" name="invoice_nonce" value="<?php echo wp_create_nonce('easy_invoice_nonce'); ?>"> |
| 227 |
|
| 228 |
<?php foreach ($tabs as $tab): ?> |
| 229 |
<div class="tab-content <?php echo $tab['active'] ? 'active' : 'hidden'; ?> px-6 py-5" id="<?php echo esc_attr($tab['id']); ?>"> |
| 230 |
<?php if ($tab['id'] === 'invoice-tab'): ?> |
| 231 |
<div class="mb-8 pb-6 border-b border-gray-200"> |
| 232 |
<div class="flex justify-between items-center mb-4"> |
| 233 |
<h3 class="text-lg font-medium text-gray-900"><?php _e('Invoice Template', 'easy-invoice'); ?></h3> |
| 234 |
<button type="button" id="browse-all-templates" class="text-sm text-indigo-600 hover:text-indigo-800 flex items-center"> |
| 235 |
<span class="mr-1"><?php _e('Browse Invoice Templates', 'easy-invoice'); ?></span> |
| 236 |
<i class="fas fa-chevron-right"></i> |
| 237 |
</button> |
| 238 |
</div> |
| 239 |
|
| 240 |
<?php |
| 241 |
// Get the current template (default to 'standard') |
| 242 |
$current_template = $invoice ? $invoice->getTemplate() : 'standard'; |
| 243 |
|
| 244 |
if (empty($current_template)) { |
| 245 |
$current_template = 'standard'; |
| 246 |
} |
| 247 |
|
| 248 |
// Always include the hidden input for the template field |
| 249 |
?> |
| 250 |
<input type="hidden" name="invoice_template" value="<?php echo esc_attr($current_template); ?>"> |
| 251 |
<?php |
| 252 |
|
| 253 |
// Check if this is the free version |
| 254 |
$is_free_version = !easy_invoice_has_pro(); |
| 255 |
|
| 256 |
|
| 257 |
|
| 258 |
// Show all templates grid |
| 259 |
?> |
| 260 |
<div class="grid grid-cols-2 md:grid-cols-3 gap-4" id="main-templates-grid"> |
| 261 |
<?php |
| 262 |
// Build the main grid templates array - always include current template plus 2 others |
| 263 |
$main_grid_templates = []; |
| 264 |
|
| 265 |
// First, add the current template if it exists in all_templates |
| 266 |
if (isset($all_templates[$current_template])) { |
| 267 |
// For free users, if current template is premium, default to standard |
| 268 |
if ($is_free_version && isset($all_templates[$current_template]['is_free']) && !$all_templates[$current_template]['is_free']) { |
| 269 |
$current_template = 'standard'; |
| 270 |
$main_grid_templates['standard'] = $all_templates['standard']; |
| 271 |
} else { |
| 272 |
$main_grid_templates[$current_template] = $all_templates[$current_template]; |
| 273 |
} |
| 274 |
} |
| 275 |
|
| 276 |
// Add other templates to fill up to 3, prioritizing standard templates |
| 277 |
$default_templates = ['standard', 'legacy', 'professional']; |
| 278 |
foreach ($default_templates as $template_id) { |
| 279 |
if (count($main_grid_templates) >= 3) break; |
| 280 |
if ($template_id !== $current_template && isset($all_templates[$template_id])) { |
| 281 |
$main_grid_templates[$template_id] = $all_templates[$template_id]; |
| 282 |
} |
| 283 |
} |
| 284 |
|
| 285 |
// If we still don't have 3 templates, add any remaining templates |
| 286 |
foreach ($all_templates as $template_id => $template) { |
| 287 |
if (count($main_grid_templates) >= 3) break; |
| 288 |
if (!isset($main_grid_templates[$template_id])) { |
| 289 |
$main_grid_templates[$template_id] = $template; |
| 290 |
} |
| 291 |
} |
| 292 |
|
| 293 |
// Display the main grid templates |
| 294 |
foreach ($main_grid_templates as $template_id => $template) : |
| 295 |
$checked = ($current_template === $template_id) ? 'checked' : ''; |
| 296 |
$selected_class = ($current_template === $template_id) ? 'selected' : ''; |
| 297 |
|
| 298 |
// Check if this template is premium (not free) |
| 299 |
$is_premium = isset($template['is_free']) && !$template['is_free']; |
| 300 |
|
| 301 |
// Add premium overlay classes |
| 302 |
$premium_classes = ''; |
| 303 |
if ($is_free_version && $is_premium) { |
| 304 |
$premium_classes = 'premium-template-blurred'; |
| 305 |
} |
| 306 |
?> |
| 307 |
<div class="template-card <?php echo $selected_class . ' ' . $premium_classes; ?>" data-template-id="<?php echo esc_attr($template_id); ?>" data-is-premium="<?php echo $is_premium ? 'true' : 'false'; ?>"> |
| 308 |
<input type="radio" id="template-<?php echo esc_attr($template_id); ?>" name="invoice_template" value="<?php echo esc_attr($template_id); ?>" class="template-radio" <?php echo $checked; ?> <?php echo ($is_free_version && $is_premium) ? 'disabled' : ''; ?>> |
| 309 |
<div class="p-4 border rounded-lg"> |
| 310 |
<div class="flex items-center mb-2"> |
| 311 |
<i class="<?php echo esc_attr($template['icon']); ?> text-indigo-600 mr-2"></i> |
| 312 |
<span class="text-sm font-medium"><?php echo esc_html($template['name']); ?></span> |
| 313 |
</div> |
| 314 |
<p class="text-sm text-gray-600"><?php echo esc_html($template['description']); ?></p> |
| 315 |
<div class="mt-2 flex-grow"> |
| 316 |
<div class="template-preview bg-gray-100 rounded h-16 flex items-center justify-center <?php echo esc_attr($template_id); ?>"> |
| 317 |
</div> |
| 318 |
</div> |
| 319 |
</div> |
| 320 |
</div> |
| 321 |
<?php endforeach; ?> |
| 322 |
</div> |
| 323 |
</div> |
| 324 |
|
| 325 |
<div class="grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6"> |
| 326 |
<?php |
| 327 |
$main_fields = $form_manager->getFields('invoice-tab'); |
| 328 |
foreach ($main_fields as $field): |
| 329 |
// Skip fields that have a section (like notes) - they're rendered separately |
| 330 |
// Also skip the template field as it's rendered manually in the template selection section |
| 331 |
if (!isset($field['section']) && $field['name'] !== 'invoice_template') { |
| 332 |
echo $form_manager->renderField($field, $invoice); |
| 333 |
} |
| 334 |
endforeach; |
| 335 |
?> |
| 336 |
</div> |
| 337 |
|
| 338 |
<?php if (!$invoice): ?> |
| 339 |
<div class="mt-4 p-3 bg-blue-50 border border-blue-200 rounded-md"> |
| 340 |
<div class="flex"> |
| 341 |
<div class="flex-shrink-0"> |
| 342 |
<i class="fas fa-info-circle text-blue-400"></i> |
| 343 |
</div> |
| 344 |
<div class="ml-3"> |
| 345 |
<p class="text-sm text-blue-700"> |
| 346 |
<?php _e('Invoice number will be automatically generated when you save the invoice.', 'easy-invoice'); ?> |
| 347 |
</p> |
| 348 |
</div> |
| 349 |
</div> |
| 350 |
</div> |
| 351 |
<?php endif; ?> |
| 352 |
|
| 353 |
<?php if (easy_invoice_has_pro()): ?> |
| 354 |
<?php |
| 355 |
$recurring_fields = $form_manager->getFieldsBySection('invoice-tab', 'recurring'); |
| 356 |
if (!empty($recurring_fields)): |
| 357 |
?> |
| 358 |
<div class="mt-8 border-t border-gray-200 pt-6"> |
| 359 |
<h3 class="text-lg font-medium text-gray-900"><?php _e('Recurring Invoice Settings', 'easy-invoice-pro'); ?></h3> |
| 360 |
<div class="mt-4 grid grid-cols-1 gap-y-6 gap-x-4 sm:col-span-6"> |
| 361 |
<?php |
| 362 |
foreach ($recurring_fields as $field): |
| 363 |
echo $form_manager->renderField($field, $invoice); |
| 364 |
endforeach; |
| 365 |
?> |
| 366 |
</div> |
| 367 |
</div> |
| 368 |
<?php endif; ?> |
| 369 |
<?php else: ?> |
| 370 |
<div class="mt-6 border-t border-gray-200 pt-4"> |
| 371 |
<div class="bg-gradient-to-r from-purple-50 to-indigo-50 border border-purple-200 rounded-lg p-4"> |
| 372 |
<div class="flex items-start"> |
| 373 |
<div class="flex-shrink-0"> |
| 374 |
<div class="w-8 h-8 bg-gradient-to-r from-purple-500 to-indigo-600 rounded-lg flex items-center justify-center"> |
| 375 |
<i class="fas fa-sync-alt text-white text-sm"></i> |
| 376 |
</div> |
| 377 |
</div> |
| 378 |
<div class="ml-3 flex-1"> |
| 379 |
<h3 class="text-base font-semibold text-gray-900 mb-1"><?php _e('Recurring Invoices', 'easy-invoice'); ?></h3> |
| 380 |
<p class="text-sm text-gray-600 mb-3"><?php _e('Automate your billing with recurring invoices. Set up weekly, monthly, or custom intervals to automatically generate and send invoices to your clients.', 'easy-invoice'); ?></p> |
| 381 |
<div class="flex items-center space-x-3 mb-3"> |
| 382 |
<div class="flex items-center text-xs text-gray-500"> |
| 383 |
<i class="fas fa-check text-green-500 mr-1"></i> |
| 384 |
<span><?php _e('Automated billing cycles', 'easy-invoice'); ?></span> |
| 385 |
</div> |
| 386 |
<div class="flex items-center text-xs text-gray-500"> |
| 387 |
<i class="fas fa-check text-green-500 mr-1"></i> |
| 388 |
<span><?php _e('Custom intervals', 'easy-invoice'); ?></span> |
| 389 |
</div> |
| 390 |
<div class="flex items-center text-xs text-gray-500"> |
| 391 |
<i class="fas fa-check text-green-500 mr-1"></i> |
| 392 |
<span><?php _e('Auto-send notifications', 'easy-invoice'); ?></span> |
| 393 |
</div> |
| 394 |
</div> |
| 395 |
<button type="button" class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md text-white bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 transition-all duration-200" onclick="if(typeof EasyInvoiceConfirmation !== 'undefined' && EasyInvoiceConfirmation.showFeatureUpgrade) { EasyInvoiceConfirmation.showFeatureUpgrade('<?php _e('Recurring Invoices', 'easy-invoice'); ?>', '<?php _e('Automate your billing with recurring invoices. Set up weekly, monthly, or custom intervals to automatically generate and send invoices to your clients.', 'easy-invoice'); ?>'); } else { alert('<?php _e('This is a premium feature. Please upgrade to Pro to access recurring invoices.', 'easy-invoice'); ?>'); }"> |
| 396 |
<i class="fas fa-crown mr-1.5"></i> |
| 397 |
<?php _e('Upgrade to Pro', 'easy-invoice'); ?> |
| 398 |
</button> |
| 399 |
</div> |
| 400 |
</div> |
| 401 |
</div> |
| 402 |
</div> |
| 403 |
<?php endif; ?> |
| 404 |
|
| 405 |
<?php if (easy_invoice_has_pro()): ?> |
| 406 |
<?php |
| 407 |
// Check if subscription is actually enabled |
| 408 |
$subscription_enabled = get_option('easy_invoice_pro_subscription_enabled', '0'); |
| 409 |
if ($subscription_enabled): |
| 410 |
$subscription_fields = $form_manager->getFieldsBySection('invoice-tab', 'subscription'); |
| 411 |
if (!empty($subscription_fields)): |
| 412 |
?> |
| 413 |
<div class="mt-8 border-t border-gray-200 pt-6"> |
| 414 |
<h3 class="text-lg font-medium text-gray-900"><?php _e('Subscription Settings', 'easy-invoice-pro'); ?></h3> |
| 415 |
<div class="mt-4 grid grid-cols-1 gap-y-6 gap-x-4 sm:col-span-6"> |
| 416 |
<?php |
| 417 |
foreach ($subscription_fields as $field): |
| 418 |
echo $form_manager->renderField($field, $invoice); |
| 419 |
endforeach; |
| 420 |
?> |
| 421 |
</div> |
| 422 |
</div> |
| 423 |
<?php endif; ?> |
| 424 |
<?php endif; ?> |
| 425 |
<?php else: ?> |
| 426 |
<div class="mt-6 border-t border-gray-200 pt-4"> |
| 427 |
<div class="bg-gradient-to-r from-purple-50 to-indigo-50 border border-purple-200 rounded-lg p-4"> |
| 428 |
<div class="flex items-start"> |
| 429 |
<div class="flex-shrink-0"> |
| 430 |
<div class="w-8 h-8 bg-gradient-to-r from-purple-500 to-indigo-600 rounded-lg flex items-center justify-center"> |
| 431 |
<i class="fas fa-calendar-alt text-white text-sm"></i> |
| 432 |
</div> |
| 433 |
</div> |
| 434 |
<div class="ml-3 flex-1"> |
| 435 |
<h3 class="text-base font-semibold text-gray-900 mb-1"><?php _e('Partial Payment', 'easy-invoice'); ?></h3> |
| 436 |
<p class="text-sm text-gray-600 mb-3"><?php _e('Accept partial payments from clients with flexible payment schedules, payment tracking, and outstanding balance management.', 'easy-invoice'); ?></p> |
| 437 |
<div class="flex items-center space-x-3 mb-3"> |
| 438 |
<div class="flex items-center text-xs text-gray-500"> |
| 439 |
<i class="fas fa-check text-green-500 mr-1"></i> |
| 440 |
<span><?php _e('Flexible payment schedules', 'easy-invoice'); ?></span> |
| 441 |
</div> |
| 442 |
<div class="flex items-center text-xs text-gray-500"> |
| 443 |
<i class="fas fa-check text-green-500 mr-1"></i> |
| 444 |
<span><?php _e('Payment tracking', 'easy-invoice'); ?></span> |
| 445 |
</div> |
| 446 |
<div class="flex items-center text-xs text-gray-500"> |
| 447 |
<i class="fas fa-check text-green-500 mr-1"></i> |
| 448 |
<span><?php _e('Balance management', 'easy-invoice'); ?></span> |
| 449 |
</div> |
| 450 |
</div> |
| 451 |
<button type="button" class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md text-white bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 transition-all duration-200" onclick="if(typeof EasyInvoiceConfirmation !== 'undefined' && EasyInvoiceConfirmation.showFeatureUpgrade) { EasyInvoiceConfirmation.showFeatureUpgrade('<?php _e('Partial Payment', 'easy-invoice'); ?>', '<?php _e('Accept partial payments from clients with flexible payment schedules, payment tracking, and outstanding balance management.', 'easy-invoice'); ?>'); } else { alert('<?php _e('This is a premium feature. Please upgrade to Pro to access partial payment functionality.', 'easy-invoice'); ?>'); }"> |
| 452 |
<i class="fas fa-crown mr-1.5"></i> |
| 453 |
<?php _e('Upgrade to Pro', 'easy-invoice'); ?> |
| 454 |
</button> |
| 455 |
</div> |
| 456 |
</div> |
| 457 |
</div> |
| 458 |
</div> |
| 459 |
<?php endif; ?> |
| 460 |
|
| 461 |
<div class="mt-8 border-t border-gray-200 pt-6"> |
| 462 |
<h3 class="text-lg font-medium text-gray-900"><?php _e('Notes & Terms', 'easy-invoice'); ?></h3> |
| 463 |
<div class="mt-4 grid grid-cols-1 gap-y-6 gap-x-4 sm:col-span-6"> |
| 464 |
<?php |
| 465 |
// Only render notes and terms fields, not all invoice tab fields |
| 466 |
$notes_fields = $form_manager->getFieldsBySection('invoice-tab', 'notes'); |
| 467 |
foreach ($notes_fields as $field): |
| 468 |
echo $form_manager->renderField($field, $invoice); |
| 469 |
endforeach; |
| 470 |
?> |
| 471 |
</div> |
| 472 |
</div> |
| 473 |
|
| 474 |
<?php elseif ($tab['id'] === 'items-tab'): ?> |
| 475 |
<div class="flex justify-between items-center mb-6"> |
| 476 |
<h2 class="text-lg font-medium text-gray-900"><?php _e('Invoice Items', 'easy-invoice'); ?></h2> |
| 477 |
<button type="button" id="collapse-all-items" class="text-sm text-indigo-600 hover:text-indigo-800 flex items-center"> |
| 478 |
<i class="fas fa-chevron-down mr-1"></i> |
| 479 |
<span><?php _e('Collapse All', 'easy-invoice'); ?></span> |
| 480 |
</button> |
| 481 |
</div> |
| 482 |
|
| 483 |
<div class="space-y-4 invoice-items-container"> |
| 484 |
<?php |
| 485 |
// If editing an existing invoice, directly output invoice items |
| 486 |
if ($invoice && $invoice->getItems()) : |
| 487 |
$index = 0; |
| 488 |
foreach ($invoice->getItems() as $item) : |
| 489 |
// Generate a unique ID for the item |
| 490 |
$item_id = 'item_' . time() . '_' . $index . '_' . rand(1000, 9999); |
| 491 |
|
| 492 |
// Get display title for header using dynamic field access |
| 493 |
$title = $item->getName(); |
| 494 |
|
| 495 |
|
| 496 |
if (empty($title) && method_exists($item, 'getData')) { |
| 497 |
$item_data = $item->getData(); |
| 498 |
if (is_array($item_data) && isset($item_data['title'])) { |
| 499 |
$title = $item_data['title']; |
| 500 |
} |
| 501 |
} |
| 502 |
|
| 503 |
$display_title = !empty($title) ? $title : sprintf(__('Item #%d', 'easy-invoice'), $index + 1); |
| 504 |
$short_title = strlen($display_title) > 30 ? substr($display_title, 0, 30) . '...' : $display_title; |
| 505 |
?> |
| 506 |
<div class="invoice-item p-6 rounded-xl shadow-sm border border-blue-100 hover:shadow-md transition-all duration-200 mb-6" style="background-color: #f8fbff;" id="<?php echo $item_id; ?>"> |
| 507 |
<div class="flex items-center justify-between mb-4 pb-3 border-b border-gray-100"> |
| 508 |
<div class="flex items-center"> |
| 509 |
<button type="button" class="item-collapse-toggle mr-3 text-gray-400 hover:text-indigo-600 focus:outline-none transition-colors"> |
| 510 |
<i class="fas fa-chevron-down text-sm"></i> |
| 511 |
</button> |
| 512 |
<h3 class="text-base font-normal text-gray-800 flex-1"> |
| 513 |
<?php |
| 514 |
echo !empty($title) ? esc_html($title) : __('New Item', 'easy-invoice'); |
| 515 |
?> |
| 516 |
</h3> |
| 517 |
</div> |
| 518 |
<div class="flex items-center gap-3"> |
| 519 |
<div class="item-collapsed-summary hidden text-sm text-gray-600 bg-gray-50 px-3 py-1 rounded-full"> |
| 520 |
<span class="quantity-summary"><?php echo esc_html($item->getQuantity()); ?></span> × $<span class="price-summary"><?php echo esc_html(number_format(floatval($item->getPrice()), 2)); ?></span> |
| 521 |
= $<span class="total-summary font-medium"><?php echo esc_html(number_format($item->getAmount(), 2)); ?></span> |
| 522 |
</div> |
| 523 |
<button type="button" class="fill-sample-data-btn text-gray-400 hover:text-green-600 p-2 rounded-lg hover:bg-green-50 transition-all duration-200" data-tooltip="<?php _e('Fill this item with sample data', 'easy-invoice'); ?>"> |
| 524 |
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" class="inline-block"> |
| 525 |
<path d="M2 2a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H2zm0 1h16v14H2V3z"/> |
| 526 |
<path d="M4 5h12v1.5H4V5zm0 3h10v1.5H4V8zm0 3h11v1.5H4V11zm0 3h8v1.5H4V14z"/> |
| 527 |
<path d="M14 14l3 3-3 3" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/> |
| 528 |
<path d="M14 17h3" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/> |
| 529 |
</svg> |
| 530 |
</button> |
| 531 |
<?php if (easy_invoice_has_pro() && \EasyInvoice\Addons\AddonManager::shouldLoad('item_library')): ?> |
| 532 |
<button type="button" class="item-library-btn text-gray-400 hover:text-blue-600 p-2 rounded-lg hover:bg-blue-50 transition-all duration-200" data-tooltip="<?php _e('Select from Item Library', 'easy-invoice'); ?>"> |
| 533 |
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" class="inline-block"> |
| 534 |
<path d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z"/> |
| 535 |
</svg> |
| 536 |
</button> |
| 537 |
<button type="button" class="save-to-library-btn text-gray-400 hover:text-green-600 p-2 rounded-lg hover:bg-green-50 transition-all duration-200" data-tooltip="<?php _e('Save to Item Library', 'easy-invoice'); ?>" data-item-index="<?php echo $index; ?>" data-nonce="<?php echo wp_create_nonce('easy_invoice_pro_item_library'); ?>"> |
| 538 |
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" class="inline-block"> |
| 539 |
<path d="M7.707 10.293a1 1 0 10-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L11 11.586V6h5a2 2 0 012 2v7a2 2 0 01-2 2H4a2 2 0 01-2-2V8a2 2 0 012-2h5v5.586l-1.293-1.293zM9 4a1 1 0 012 0v2H9V4z"/> |
| 540 |
</svg> |
| 541 |
</button> |
| 542 |
<?php endif; ?> |
| 543 |
<button type="button" class="remove-item text-gray-400 hover:text-red-600 hover:bg-red-50 p-2 rounded-lg transition-all duration-200 text-sm font-medium"> |
| 544 |
<i class="fas fa-trash-alt mr-1"></i> <?php _e('Remove', 'easy-invoice'); ?> |
| 545 |
</button> |
| 546 |
</div> |
| 547 |
</div> |
| 548 |
<div class="item-content"> |
| 549 |
<?php echo $form_manager->generateExistingItemHtml($item, $index); ?> |
| 550 |
</div> |
| 551 |
</div> |
| 552 |
<?php |
| 553 |
$index++; |
| 554 |
endforeach; |
| 555 |
else: |
| 556 |
// For new invoices, render one empty item by default |
| 557 |
$index = 0; |
| 558 |
// Get the item fields config |
| 559 |
$item_fields = $form_manager->getItemFields(); |
| 560 |
$title_placeholder = ''; |
| 561 |
foreach ($item_fields as $field) { |
| 562 |
if ($field['name'] === 'title') { |
| 563 |
$title_placeholder = $field['placeholder'] ?? __('Item', 'easy-invoice'); |
| 564 |
break; |
| 565 |
} |
| 566 |
} |
| 567 |
// Try to get the value from POST (if form was submitted and errored) |
| 568 |
$item_title = isset($_POST['items'][0]['title']) ? trim($_POST['items'][0]['title']) : ''; |
| 569 |
?> |
| 570 |
<div class="invoice-item p-6 rounded-xl shadow-sm border border-blue-100 hover:shadow-md transition-all duration-200 mb-6" style="background-color: #f8fbff;"> |
| 571 |
<div class="flex items-center justify-between mb-4 pb-3 border-b border-gray-100"> |
| 572 |
<div class="flex items-center"> |
| 573 |
<button type="button" class="item-collapse-toggle mr-3 text-gray-400 hover:text-indigo-600 focus:outline-none transition-colors"> |
| 574 |
<i class="fas fa-chevron-down text-sm"></i> |
| 575 |
</button> |
| 576 |
<h3 class="text-base font-normal text-gray-800 flex-1"> |
| 577 |
<?php |
| 578 |
echo !empty($item_title) ? esc_html($item_title) : esc_html($title_placeholder); |
| 579 |
?> |
| 580 |
</h3> |
| 581 |
</div> |
| 582 |
<div class="flex items-center gap-3"> |
| 583 |
<div class="item-collapsed-summary hidden text-sm text-gray-600 bg-gray-50 px-3 py-1 rounded-full"> |
| 584 |
<span class="quantity-summary">0</span> × $<span class="price-summary">0.00</span> |
| 585 |
= $<span class="total-summary">0.00</span> |
| 586 |
</div> |
| 587 |
<button type="button" class="fill-sample-data-btn text-gray-400 hover:text-green-600 p-2 rounded-lg hover:bg-green-50 transition-all duration-200" data-tooltip="<?php _e('Fill this item with sample data', 'easy-invoice'); ?>"> |
| 588 |
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" class="inline-block"> |
| 589 |
<path d="M2 2a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H2zm0 1h16v14H2V3z"/> |
| 590 |
<path d="M4 5h12v1.5H4V5zm0 3h10v1.5H4V8zm0 3h11v1.5H4V11zm0 3h8v1.5H4V14z"/> |
| 591 |
<path d="M14 14l3 3-3 3" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/> |
| 592 |
<path d="M14 17h3" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/> |
| 593 |
</svg> |
| 594 |
</button> |
| 595 |
<?php if (easy_invoice_has_pro() && \EasyInvoice\Addons\AddonManager::shouldLoad('item_library')): ?> |
| 596 |
<button type="button" class="item-library-btn text-gray-400 hover:text-blue-600 p-2 rounded-lg hover:bg-blue-50 transition-all duration-200" data-tooltip="<?php _e('Select from Item Library', 'easy-invoice'); ?>"> |
| 597 |
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" class="inline-block"> |
| 598 |
<path d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z"/> |
| 599 |
</svg> |
| 600 |
</button> |
| 601 |
<button type="button" class="save-to-library-btn text-gray-400 hover:text-green-600 p-2 rounded-lg hover:bg-green-50 transition-all duration-200" data-tooltip="<?php _e('Save to Item Library', 'easy-invoice'); ?>" data-item-index="0" data-nonce="<?php echo wp_create_nonce('easy_invoice_pro_item_library'); ?>"> |
| 602 |
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" class="inline-block"> |
| 603 |
<path d="M7.707 10.293a1 1 0 10-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L11 11.586V6h5a2 2 0 012 2v7a2 2 0 01-2 2H4a2 2 0 01-2-2V8a2 2 0 012-2h5v5.586l-1.293-1.293zM9 4a1 1 0 012 0v2H9V4z"/> |
| 604 |
</svg> |
| 605 |
</button> |
| 606 |
<?php endif; ?> |
| 607 |
<button type="button" class="remove-item text-gray-400 hover:text-red-600 hover:bg-red-50 p-2 rounded-lg transition-all duration-200 text-sm font-medium"> |
| 608 |
<i class="fas fa-trash-alt mr-1"></i> <?php _e('Remove', 'easy-invoice'); ?> |
| 609 |
</button> |
| 610 |
</div> |
| 611 |
</div> |
| 612 |
<div class="item-content"> |
| 613 |
<?php echo $form_manager->generateItemTemplateHtml(); ?> |
| 614 |
</div> |
| 615 |
</div> |
| 616 |
<?php endif; ?> |
| 617 |
</div> |
| 618 |
|
| 619 |
<div class="mt-6"> |
| 620 |
<button type="button" class="add-item-button w-full flex justify-center items-center px-6 py-4 bg-white border border-blue-100 rounded-xl text-indigo-700 hover:bg-blue-50 hover:border-blue-200 hover:shadow-md transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 group"> |
| 621 |
<div class="flex items-center"> |
| 622 |
<div class="w-10 h-10 bg-indigo-100 rounded-full flex items-center justify-center mr-3 group-hover:bg-indigo-200 transition-colors"> |
| 623 |
<i class="fas fa-plus text-indigo-600 text-lg"></i> |
| 624 |
</div> |
| 625 |
<div class="text-left"> |
| 626 |
<span class="font-semibold text-base"><?php _e('Add Another Item', 'easy-invoice'); ?></span> |
| 627 |
<p class="text-sm text-indigo-600 mt-0.5"><?php _e('Click to add a new line item', 'easy-invoice'); ?></p> |
| 628 |
</div> |
| 629 |
</div> |
| 630 |
</button> |
| 631 |
</div> |
| 632 |
<?php elseif ($tab['id'] === 'client-tab'): ?> |
| 633 |
<div class="mb-6"> |
| 634 |
<label class="block text-sm font-medium text-gray-700 mb-3"><?php _e('Select Existing Client', 'easy-invoice'); ?></label> |
| 635 |
|
| 636 |
<div class="relative"> |
| 637 |
<div class="flex items-center space-x-3"> |
| 638 |
<div class="relative flex-grow"> |
| 639 |
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> |
| 640 |
<i class="fas fa-search text-gray-400"></i> |
| 641 |
</div> |
| 642 |
<input |
| 643 |
type="text" |
| 644 |
id="client-search-input" |
| 645 |
class="block w-full rounded-md border border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm py-2 px-3 pl-10" |
| 646 |
placeholder="<?php _e('Search clients by name, email, or company...', 'easy-invoice'); ?>" |
| 647 |
autocomplete="off" |
| 648 |
> |
| 649 |
</div> |
| 650 |
<button type="button" id="add-new-client-btn" class="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"> |
| 651 |
<i class="fas fa-plus -ml-1 mr-2"></i> |
| 652 |
<?php _e('New Client', 'easy-invoice'); ?> |
| 653 |
</button> |
| 654 |
</div> |
| 655 |
|
| 656 |
<div id="client-search-results" class="absolute z-50 w-full mt-2 bg-white border border-gray-200 rounded-lg shadow-xl max-h-80 overflow-y-auto" style="display: none;"> |
| 657 |
<div id="client-search-loading" class="p-6 text-center text-gray-500" style="display: none;"> |
| 658 |
<i class="fas fa-spinner fa-spin mr-2"></i> |
| 659 |
<?php _e('Searching...', 'easy-invoice'); ?> |
| 660 |
</div> |
| 661 |
<div id="client-search-empty" class="p-6 text-center text-gray-500" style="display: none;"> |
| 662 |
<i class="fas fa-search mr-2"></i> |
| 663 |
<?php _e('No clients found', 'easy-invoice'); ?> |
| 664 |
</div> |
| 665 |
<div id="client-search-list" class="py-2"> |
| 666 |
</div> |
| 667 |
</div> |
| 668 |
|
| 669 |
<div id="selected-client-display" class="mt-4 p-4 bg-gradient-to-r from-indigo-50 to-blue-50 border border-indigo-200 rounded-lg shadow-sm <?php echo ($invoice && $invoice->getClientId()) ? '' : 'hidden'; ?>"> |
| 670 |
<div class="flex items-center justify-between"> |
| 671 |
<div class="flex items-center"> |
| 672 |
<div class="flex-shrink-0"> |
| 673 |
<div class="w-10 h-10 bg-gradient-to-br from-indigo-500 to-indigo-600 rounded-full flex items-center justify-center shadow-sm"> |
| 674 |
<i class="fas fa-user text-white text-sm"></i> |
| 675 |
</div> |
| 676 |
</div> |
| 677 |
<div class="ml-4"> |
| 678 |
<p id="selected-client-name" class="text-sm font-semibold text-gray-900"> |
| 679 |
<?php |
| 680 |
if ($invoice && $invoice->getClientId()) { |
| 681 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 682 |
if ($client) { |
| 683 |
echo esc_html($client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName())); |
| 684 |
} else { |
| 685 |
echo esc_html__('—', 'easy-invoice'); |
| 686 |
} |
| 687 |
} else { |
| 688 |
echo esc_html__('—', 'easy-invoice'); |
| 689 |
} |
| 690 |
?> |
| 691 |
</p> |
| 692 |
<p id="selected-client-email" class="text-sm text-gray-600"> |
| 693 |
<?php |
| 694 |
if ($invoice && $invoice->getClientId()) { |
| 695 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 696 |
if ($client) { |
| 697 |
echo esc_html($client->getEmail() ?: esc_html__('—', 'easy-invoice')); |
| 698 |
} else { |
| 699 |
echo esc_html__('—', 'easy-invoice'); |
| 700 |
} |
| 701 |
} else { |
| 702 |
echo esc_html__('—', 'easy-invoice'); |
| 703 |
} |
| 704 |
?> |
| 705 |
</p> |
| 706 |
</div> |
| 707 |
</div> |
| 708 |
<button type="button" id="clear-client-selection" class="text-gray-400 hover:text-gray-600 transition-colors p-2 rounded-full hover:bg-white hover:shadow-sm"> |
| 709 |
<i class="fas fa-times"></i> |
| 710 |
</button> |
| 711 |
</div> |
| 712 |
</div> |
| 713 |
</div> |
| 714 |
|
| 715 |
<select id="select-client" class="hidden"> |
| 716 |
<option value=""><?php _e('-- Select a client --', 'easy-invoice'); ?></option> |
| 717 |
<?php |
| 718 |
// Ensure $clients is defined |
| 719 |
if (!isset($clients)) { |
| 720 |
$client_repository = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository(); |
| 721 |
$clients = $client_repository->all(); |
| 722 |
} |
| 723 |
|
| 724 |
// Output client options |
| 725 |
if (!empty($clients)) { |
| 726 |
foreach ($clients as $client) { |
| 727 |
// Get the WordPress user data directly |
| 728 |
$user = get_user_by('id', $client->getId()); |
| 729 |
if (!$user) { |
| 730 |
continue; |
| 731 |
} |
| 732 |
|
| 733 |
// Use Client model properties first, fallback to WordPress user fields |
| 734 |
$business_name = $client->business_client_name ?: ''; |
| 735 |
$first_name = $client->first_name ?: $user->first_name ?: ''; |
| 736 |
$last_name = $client->last_name ?: $user->last_name ?: ''; |
| 737 |
$client_email = $client->email ?: $user->user_email ?: ''; |
| 738 |
|
| 739 |
// Create display name |
| 740 |
$client_name = $business_name ?: ($first_name . ' ' . $last_name); |
| 741 |
if (empty(trim($client_name))) { |
| 742 |
$client_name = $user->display_name ?: 'User ' . $client->getId(); |
| 743 |
} |
| 744 |
|
| 745 |
$selected = $invoice && $invoice->getClientId() == $client->getId() ? 'selected' : ''; |
| 746 |
printf( |
| 747 |
'<option value="%s" %s data-name="%s" data-email="%s">%s (%s)</option>', |
| 748 |
esc_attr($client->getId()), |
| 749 |
$selected, |
| 750 |
esc_attr($client_name), |
| 751 |
esc_attr($client_email), |
| 752 |
esc_html($client_name), |
| 753 |
esc_html($client_email) |
| 754 |
); |
| 755 |
} |
| 756 |
} |
| 757 |
?> |
| 758 |
</select> |
| 759 |
</div> |
| 760 |
|
| 761 |
<div class="border-t border-gray-200 pt-4 mt-4"> |
| 762 |
<input type="hidden" name="client_id" id="client-id" value="<?php echo $invoice ? esc_attr($invoice->getClientId()) : ''; ?>"> |
| 763 |
|
| 764 |
<div id="client-info-display" class="mt-6 <?php echo $invoice && $invoice->getClientId() ? '' : 'hidden'; ?>"> |
| 765 |
<div class="bg-white p-6 rounded-xl border border-gray-200 shadow-sm"> |
| 766 |
<div class="flex items-center justify-between pb-4 border-b border-gray-200 mb-4"> |
| 767 |
<h4 class="text-lg font-semibold text-gray-800 flex items-center"> |
| 768 |
<i class="fas fa-user-check text-indigo-500 mr-3"></i> |
| 769 |
<span><?php _e('Client Information', 'easy-invoice'); ?></span> |
| 770 |
</h4> |
| 771 |
<a href="<?php echo admin_url('admin.php?page=easy-invoice-client-edit&client_id=' . ($invoice && $invoice->getClientId() ? $invoice->getClientId() : '')); ?>" |
| 772 |
id="edit-selected-client" |
| 773 |
class="text-sm text-indigo-600 hover:text-indigo-800 font-medium" |
| 774 |
target="_blank"> |
| 775 |
<i class="fas fa-pen mr-1"></i> |
| 776 |
<?php _e('Edit', 'easy-invoice'); ?> |
| 777 |
</a> |
| 778 |
</div> |
| 779 |
|
| 780 |
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 pt-2"> |
| 781 |
<div class="flex items-start group"> |
| 782 |
<div class="flex-shrink-0 w-8 text-center pt-1"><i class="fas fa-user-tie text-gray-400 group-hover:text-indigo-500 transition-colors"></i></div> |
| 783 |
<div class="ml-3"> |
| 784 |
<label class="block text-xs font-medium text-gray-500 uppercase tracking-wider"><?php _e('Name', 'easy-invoice'); ?></label> |
| 785 |
<p id="display-client-name" class="text-sm font-semibold text-gray-800 mt-1"> |
| 786 |
<?php |
| 787 |
if ($invoice && $invoice->getClientId()) { |
| 788 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 789 |
if ($client) { |
| 790 |
echo esc_html($client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName())); |
| 791 |
} else { |
| 792 |
echo esc_html__('—', 'easy-invoice'); |
| 793 |
} |
| 794 |
} else { |
| 795 |
echo esc_html__('—', 'easy-invoice'); |
| 796 |
} |
| 797 |
?> |
| 798 |
</p> |
| 799 |
</div> |
| 800 |
</div> |
| 801 |
|
| 802 |
<div class="flex items-start group"> |
| 803 |
<div class="flex-shrink-0 w-8 text-center pt-1"><i class="fas fa-building text-gray-400 group-hover:text-indigo-500 transition-colors"></i></div> |
| 804 |
<div class="ml-3"> |
| 805 |
<label class="block text-xs font-medium text-gray-500 uppercase tracking-wider"><?php _e('Company', 'easy-invoice'); ?></label> |
| 806 |
<p id="display-client-company" class="text-sm font-semibold text-gray-800 mt-1"> |
| 807 |
<?php |
| 808 |
if ($invoice && $invoice->getClientId()) { |
| 809 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 810 |
if ($client) { |
| 811 |
echo esc_html($client->getBusinessClientName() ?: esc_html__('—', 'easy-invoice')); |
| 812 |
} else { |
| 813 |
echo esc_html__('—', 'easy-invoice'); |
| 814 |
} |
| 815 |
} else { |
| 816 |
echo esc_html__('—', 'easy-invoice'); |
| 817 |
} |
| 818 |
?> |
| 819 |
</p> |
| 820 |
</div> |
| 821 |
</div> |
| 822 |
|
| 823 |
<div class="flex items-start group"> |
| 824 |
<div class="flex-shrink-0 w-8 text-center pt-1"><i class="fas fa-envelope text-gray-400 group-hover:text-indigo-500 transition-colors"></i></div> |
| 825 |
<div class="ml-3"> |
| 826 |
<label class="block text-xs font-medium text-gray-500 uppercase tracking-wider"><?php _e('Email', 'easy-invoice'); ?></label> |
| 827 |
<p id="display-client-email" class="text-sm text-gray-800 mt-1 break-all"> |
| 828 |
<?php |
| 829 |
if ($invoice && $invoice->getClientId()) { |
| 830 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 831 |
if ($client) { |
| 832 |
echo esc_html($client->getEmail() ?: esc_html__('—', 'easy-invoice')); |
| 833 |
} else { |
| 834 |
echo esc_html__('—', 'easy-invoice'); |
| 835 |
} |
| 836 |
} else { |
| 837 |
echo esc_html__('—', 'easy-invoice'); |
| 838 |
} |
| 839 |
?> |
| 840 |
</p> |
| 841 |
</div> |
| 842 |
</div> |
| 843 |
|
| 844 |
<div class="flex items-start group"> |
| 845 |
<div class="flex-shrink-0 w-8 text-center pt-1"><i class="fas fa-phone-alt text-gray-400 group-hover:text-indigo-500 transition-colors"></i></div> |
| 846 |
<div class="ml-3"> |
| 847 |
<label class="block text-xs font-medium text-gray-500 uppercase tracking-wider"><?php _e('Phone', 'easy-invoice'); ?></label> |
| 848 |
<p id="display-client-phone" class="text-sm text-gray-800 mt-1"> |
| 849 |
<?php |
| 850 |
if ($invoice && $invoice->getClientId()) { |
| 851 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 852 |
if ($client) { |
| 853 |
echo esc_html($client->getExtraInfo() ?: esc_html__('—', 'easy-invoice')); |
| 854 |
} else { |
| 855 |
echo esc_html__('—', 'easy-invoice'); |
| 856 |
} |
| 857 |
} else { |
| 858 |
echo esc_html__('—', 'easy-invoice'); |
| 859 |
} |
| 860 |
?> |
| 861 |
</p> |
| 862 |
</div> |
| 863 |
</div> |
| 864 |
|
| 865 |
<div class="flex items-start group md:col-span-2"> |
| 866 |
<div class="flex-shrink-0 w-8 text-center pt-1"><i class="fas fa-globe-americas text-gray-400 group-hover:text-indigo-500 transition-colors"></i></div> |
| 867 |
<div class="ml-3"> |
| 868 |
<label class="block text-xs font-medium text-gray-500 uppercase tracking-wider"><?php _e('Website', 'easy-invoice'); ?></label> |
| 869 |
<p id="display-client-website" class="text-sm text-gray-800 mt-1"> |
| 870 |
<?php |
| 871 |
if ($invoice && $invoice->getClientId()) { |
| 872 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 873 |
if ($client) { |
| 874 |
echo esc_html($client->getWebsite() ?: esc_html__('—', 'easy-invoice')); |
| 875 |
} else { |
| 876 |
echo esc_html__('—', 'easy-invoice'); |
| 877 |
} |
| 878 |
} else { |
| 879 |
echo esc_html__('—', 'easy-invoice'); |
| 880 |
} |
| 881 |
?> |
| 882 |
</p> |
| 883 |
</div> |
| 884 |
</div> |
| 885 |
|
| 886 |
<div class="flex items-start group md:col-span-2"> |
| 887 |
<div class="flex-shrink-0 w-8 text-center pt-1"><i class="fas fa-map-marker-alt text-gray-400 group-hover:text-indigo-500 transition-colors"></i></div> |
| 888 |
<div class="ml-3"> |
| 889 |
<label class="block text-xs font-medium text-gray-500 uppercase tracking-wider"><?php _e('Address', 'easy-invoice'); ?></label> |
| 890 |
<p id="display-client-address" class="text-sm text-gray-800 mt-1 whitespace-pre-wrap"> |
| 891 |
<?php |
| 892 |
if ($invoice && $invoice->getClientId()) { |
| 893 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 894 |
if ($client) { |
| 895 |
echo esc_html($client->getAddress() ?: esc_html__('—', 'easy-invoice')); |
| 896 |
} else { |
| 897 |
echo esc_html__('—', 'easy-invoice'); |
| 898 |
} |
| 899 |
} else { |
| 900 |
echo esc_html__('—', 'easy-invoice'); |
| 901 |
} |
| 902 |
?> |
| 903 |
</p> |
| 904 |
</div> |
| 905 |
</div> |
| 906 |
</div> |
| 907 |
</div> |
| 908 |
</div> |
| 909 |
|
| 910 |
<div id="no-client-message" class="mt-4 text-center py-6 border-2 border-dashed border-gray-300 rounded-lg <?php echo $invoice && $invoice->getClientId() ? 'hidden' : ''; ?>"> |
| 911 |
<p class="text-gray-600"><?php _e('Select a client from the dropdown above to view their information.', 'easy-invoice'); ?></p> |
| 912 |
</div> |
| 913 |
</div> |
| 914 |
<?php elseif ($tab['id'] === 'discounts-taxes-tab'): ?> |
| 915 |
<div> |
| 916 |
<h3 class="text-lg font-medium text-gray-900"><?php _e('Discount & Tax Settings', 'easy-invoice'); ?></h3> |
| 917 |
<div class="mt-4 grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6"> |
| 918 |
<?php |
| 919 |
// Get all discount and tax fields |
| 920 |
$discount_fields = $form_manager->getFieldsBySection('discounts-taxes-tab', 'discount'); |
| 921 |
$tax_fields = $form_manager->getFieldsBySection('discounts-taxes-tab', 'tax'); |
| 922 |
$additional_tax_fields = $form_manager->getFieldsBySection('discounts-taxes-tab', 'additional_tax'); |
| 923 |
|
| 924 |
// Combine and sort by order |
| 925 |
$all_fields = array_merge($discount_fields, $tax_fields, $additional_tax_fields); |
| 926 |
usort($all_fields, function($a, $b) { |
| 927 |
return ($a['order'] ?? 0) - ($b['order'] ?? 0); |
| 928 |
}); |
| 929 |
|
| 930 |
foreach ($all_fields as $field): |
| 931 |
echo $form_manager->renderField($field, $invoice); |
| 932 |
endforeach; |
| 933 |
?> |
| 934 |
</div> |
| 935 |
</div> |
| 936 |
<?php elseif ($tab['id'] === 'payments-tab'): ?> |
| 937 |
<div> |
| 938 |
<h3 class="text-lg font-medium text-gray-900"><?php _e('Payment Information', 'easy-invoice'); ?></h3> |
| 939 |
<div class="mt-4 grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6"> |
| 940 |
<?php |
| 941 |
$payment_fields = $form_manager->getFieldsBySection('payments-tab', 'payment'); |
| 942 |
foreach ($payment_fields as $field): |
| 943 |
echo $form_manager->renderField($field, $invoice); |
| 944 |
endforeach; |
| 945 |
?> |
| 946 |
</div> |
| 947 |
|
| 948 |
<div class="mt-8 border-t border-gray-200 pt-6"> |
| 949 |
<h3 class="text-lg font-medium text-gray-900"><?php _e('Currency', 'easy-invoice'); ?></h3> |
| 950 |
<div class="mt-4 grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6"> |
| 951 |
<?php |
| 952 |
$currency_fields = $form_manager->getFieldsBySection('payments-tab', 'currency'); |
| 953 |
foreach ($currency_fields as $field): |
| 954 |
echo $form_manager->renderField($field, $invoice); |
| 955 |
endforeach; |
| 956 |
?> |
| 957 |
</div> |
| 958 |
</div> |
| 959 |
</div> |
| 960 |
<?php endif; ?> |
| 961 |
</div> |
| 962 |
<?php endforeach; ?> |
| 963 |
</form> |
| 964 |
</div> |
| 965 |
|
| 966 |
<div id="template-gallery-modal" style="display: none;" class="fixed inset-0 bg-gray-600 bg-opacity-75 overflow-y-auto h-full w-full z-50 flex items-center justify-center"> |
| 967 |
<div class="mx-auto p-6 border w-full md:w-4/5 max-w-4xl shadow-lg rounded-md bg-white max-h-[80vh] overflow-y-auto"> |
| 968 |
<div class="flex justify-between items-center mb-4"> |
| 969 |
<h3 class="text-xl font-medium text-gray-900"><?php _e('Browse Invoice Templates', 'easy-invoice'); ?></h3> |
| 970 |
<button type="button" class="close-gallery-modal text-gray-400 hover:text-gray-500 focus:outline-none"> |
| 971 |
<span class="sr-only"><?php _e('Close', 'easy-invoice'); ?></span> |
| 972 |
<i class="fas fa-times text-xl"></i> |
| 973 |
</button> |
| 974 |
</div> |
| 975 |
|
| 976 |
<div class="templates-grid grid grid-cols-2 md:grid-cols-3 gap-4 mb-4 max-h-[45vh] overflow-y-auto p-2"> |
| 977 |
<?php |
| 978 |
// Display all template options in the modal |
| 979 |
foreach ($all_templates as $template_id => $template) : |
| 980 |
$is_current = ($current_template === $template_id); |
| 981 |
$selected_class = $is_current ? 'selected' : ''; |
| 982 |
?> |
| 983 |
<div class="gallery-template-card <?php echo $selected_class; ?>" data-template-id="<?php echo esc_attr($template_id); ?>" data-is-premium="<?php echo isset($template['is_free']) && !$template['is_free'] ? 'true' : 'false'; ?>"> |
| 984 |
<div class="p-4 border rounded-lg"> |
| 985 |
<div class="flex items-center mb-2"> |
| 986 |
<i class="<?php echo esc_attr($template['icon']); ?> text-indigo-600 mr-2"></i> |
| 987 |
<span class="text-sm font-medium"><?php echo esc_html($template['name']); ?></span> |
| 988 |
</div> |
| 989 |
<p class="text-sm text-gray-600"><?php echo esc_html($template['description']); ?></p> |
| 990 |
<div class="mt-2 flex-grow"> |
| 991 |
<div class="template-preview bg-gray-100 rounded h-16 flex items-center justify-center <?php echo esc_attr($template_id); ?>"> |
| 992 |
</div> |
| 993 |
</div> |
| 994 |
</div> |
| 995 |
</div> |
| 996 |
<?php endforeach; ?> |
| 997 |
</div> |
| 998 |
|
| 999 |
<div class="flex justify-end mt-4 space-x-3"> |
| 1000 |
<button type="button" id="apply-selected-template" class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> |
| 1001 |
<?php _e('Apply', 'easy-invoice'); ?> |
| 1002 |
</button> |
| 1003 |
<button type="button" class="gallery-close-btn inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm 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"> |
| 1004 |
<?php _e('Close', 'easy-invoice'); ?> |
| 1005 |
</button> |
| 1006 |
</div> |
| 1007 |
</div> |
| 1008 |
</div> |
| 1009 |
|
| 1010 |
<template id="invoice-item-template" style="display: none;"> |
| 1011 |
<div class="invoice-item p-6 rounded-xl shadow-sm border border-blue-100 hover:shadow-md transition-all duration-200 mb-6" style="background-color: #f8fbff;"> |
| 1012 |
<div class="flex items-center justify-between mb-4 pb-3 border-b border-gray-100"> |
| 1013 |
<div class="flex items-center"> |
| 1014 |
<button type="button" class="item-collapse-toggle mr-3 text-gray-400 hover:text-indigo-600 focus:outline-none transition-colors"> |
| 1015 |
<i class="fas fa-chevron-down text-sm"></i> |
| 1016 |
</button> |
| 1017 |
<h3 class="text-base font-normal text-gray-800 flex-1"> |
| 1018 |
<?php |
| 1019 |
echo !empty($title) ? esc_html($title) : __('New Item', 'easy-invoice'); |
| 1020 |
?> |
| 1021 |
</h3> |
| 1022 |
</div> |
| 1023 |
<div class="flex items-center gap-3"> |
| 1024 |
<div class="item-collapsed-summary hidden text-sm text-gray-600 bg-gray-50 px-3 py-1 rounded-full"> |
| 1025 |
<span class="quantity-summary">0</span> × $<span class="price-summary">0.00</span> |
| 1026 |
= $<span class="total-summary">0.00</span> |
| 1027 |
</div> |
| 1028 |
<button type="button" class="fill-sample-data-btn text-gray-400 hover:text-green-600 p-2 rounded-lg hover:bg-green-50 transition-all duration-200" data-tooltip="<?php _e('Fill this item with sample data', 'easy-invoice'); ?>"> |
| 1029 |
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" class="inline-block"> |
| 1030 |
<path d="M2 2a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H2zm0 1h16v14H2V3z"/> |
| 1031 |
<path d="M4 5h12v1.5H4V5zm0 3h10v1.5H4V8zm0 3h11v1.5H4V11zm0 3h8v1.5H4V14z"/> |
| 1032 |
<path d="M14 14l3 3-3 3" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/> |
| 1033 |
<path d="M14 17h3" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/> |
| 1034 |
</svg> |
| 1035 |
</button> |
| 1036 |
<?php if (easy_invoice_has_pro() && \EasyInvoice\Addons\AddonManager::shouldLoad('item_library')): ?> |
| 1037 |
<button type="button" class="item-library-btn text-gray-400 hover:text-blue-600 p-2 rounded-lg hover:bg-blue-50 transition-all duration-200" data-tooltip="<?php _e('Select from Item Library', 'easy-invoice'); ?>"> |
| 1038 |
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" class="inline-block"> |
| 1039 |
<path d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z"/> |
| 1040 |
</svg> |
| 1041 |
</button> |
| 1042 |
<button type="button" class="save-to-library-btn text-gray-400 hover:text-green-600 p-2 rounded-lg hover:bg-green-50 transition-all duration-200" data-tooltip="<?php _e('Save to Item Library', 'easy-invoice'); ?>" data-nonce="<?php echo wp_create_nonce('easy_invoice_pro_item_library'); ?>"> |
| 1043 |
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" class="inline-block"> |
| 1044 |
<path d="M7.707 10.293a1 1 0 10-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L11 11.586V6h5a2 2 0 012 2v7a2 2 0 01-2 2H4a2 2 0 01-2-2V8a2 2 0 012-2h5v5.586l-1.293-1.293zM9 4a1 1 0 012 0v2H9V4z"/> |
| 1045 |
</svg> |
| 1046 |
</button> |
| 1047 |
<?php endif; ?> |
| 1048 |
<button type="button" class="remove-item text-gray-400 hover:text-red-600 hover:bg-red-50 p-2 rounded-lg transition-all duration-200 text-sm font-medium"> |
| 1049 |
<i class="fas fa-trash-alt mr-1"></i> <?php _e('Remove', 'easy-invoice'); ?> |
| 1050 |
</button> |
| 1051 |
</div> |
| 1052 |
</div> |
| 1053 |
<div class="item-content"> |
| 1054 |
<?php echo $form_manager->generateItemTemplateHtml(); ?> |
| 1055 |
</div> |
| 1056 |
</div> |
| 1057 |
</template> |
| 1058 |
|
| 1059 |
<div id="add_client_modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-75 overflow-y-auto h-full w-full z-50"> |
| 1060 |
<div class="relative top-20 mx-auto p-5 border w-11/12 md:w-2/3 lg:w-1/2 shadow-lg rounded-md bg-white"> |
| 1061 |
<div class="flex justify-between items-center mb-4"> |
| 1062 |
<h3 class="text-lg font-medium text-gray-900">Add New Client</h3> |
| 1063 |
<button type="button" class="close-modal text-gray-400 hover:text-gray-500"> |
| 1064 |
<span class="sr-only">Close</span> |
| 1065 |
<i class="fas fa-times"></i> |
| 1066 |
</button> |
| 1067 |
</div> |
| 1068 |
|
| 1069 |
<?php |
| 1070 |
// Unset the $client variable from the foreach loop to ensure clean add form |
| 1071 |
unset($client); |
| 1072 |
include_once EASY_INVOICE_PLUGIN_DIR . 'templates/client-form.php'; |
| 1073 |
?> |
| 1074 |
</div> |
| 1075 |
</div> |
| 1076 |
|
| 1077 |
<?php |
| 1078 |
|
| 1079 |
// Always localize script with AJAX URL and nonce |
| 1080 |
wp_localize_script('easy-invoice-form', 'easyInvoice', array( |
| 1081 |
'ajaxUrl' => admin_url('admin-ajax.php'), |
| 1082 |
'nonce' => wp_create_nonce('easy_invoice_nonce'), |
| 1083 |
'clientData' => $client_data ?: array() |
| 1084 |
)); |
| 1085 |
?> |
| 1086 |
|
| 1087 |
<script> |
| 1088 |
jQuery(document).ready(function($) { |
| 1089 |
|
| 1090 |
|
| 1091 |
// Client search functionality for invoice form |
| 1092 |
var searchTimeout; |
| 1093 |
|
| 1094 |
// Initialize existing client data |
| 1095 |
var existingClientId = '<?php echo $invoice ? esc_js($invoice->getClientId()) : ''; ?>'; |
| 1096 |
var existingClientName = '<?php |
| 1097 |
if ($invoice && $invoice->getClientId()) { |
| 1098 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 1099 |
if ($client) { |
| 1100 |
echo esc_js($client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName())); |
| 1101 |
} |
| 1102 |
} |
| 1103 |
?>'; |
| 1104 |
var existingClientEmail = '<?php |
| 1105 |
if ($invoice && $invoice->getClientId()) { |
| 1106 |
$client = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->find($invoice->getClientId()); |
| 1107 |
if ($client) { |
| 1108 |
echo esc_js($client->getEmail() ?: ''); |
| 1109 |
} |
| 1110 |
} |
| 1111 |
?>'; |
| 1112 |
var existingClientData = <?php echo json_encode($client_data); ?>; |
| 1113 |
|
| 1114 |
if (existingClientId && existingClientName && existingClientName !== '-') { |
| 1115 |
// Show the selected client display and client info display |
| 1116 |
$('#selected-client-display').show(); |
| 1117 |
$('#client-info-display').show(); |
| 1118 |
$('#no-client-message').hide(); |
| 1119 |
|
| 1120 |
// Set the select client dropdown value |
| 1121 |
$('#select-client').val(existingClientId); |
| 1122 |
|
| 1123 |
// Populate the search input with the existing client name |
| 1124 |
$('#client-search-input').val(existingClientName); |
| 1125 |
|
| 1126 |
|
| 1127 |
} |
| 1128 |
|
| 1129 |
// Client search input functionality |
| 1130 |
$('#client-search-input').on('input', function() { |
| 1131 |
const query = $(this).val().trim(); |
| 1132 |
clearTimeout(searchTimeout); |
| 1133 |
|
| 1134 |
// Don't hide results immediately for short queries - let the search function handle it |
| 1135 |
if (query.length === 0) { |
| 1136 |
// Show all clients when input is cleared |
| 1137 |
searchClients(''); |
| 1138 |
return; |
| 1139 |
} |
| 1140 |
|
| 1141 |
searchTimeout = setTimeout(function() { |
| 1142 |
searchClients(query); |
| 1143 |
}, 300); |
| 1144 |
}); |
| 1145 |
|
| 1146 |
// Focus on search input - ensure client tab is active |
| 1147 |
$('#client-search-input').on('focus', function() { |
| 1148 |
// Switch to client tab first to ensure the search results are visible |
| 1149 |
if (window.switchToTab) { |
| 1150 |
window.switchToTab('client-tab'); |
| 1151 |
} else { |
| 1152 |
// Fallback: manually switch tabs |
| 1153 |
$('.tab-button').removeClass('border-indigo-500 text-indigo-600').addClass('border-transparent text-gray-500'); |
| 1154 |
$('.tab-content').addClass('hidden'); |
| 1155 |
$('.tab-button[data-tab="client-tab"]').addClass('border-indigo-500 text-indigo-600'); |
| 1156 |
$('#client-tab').removeClass('hidden'); |
| 1157 |
} |
| 1158 |
}); |
| 1159 |
|
| 1160 |
// Click on search input to show all clients |
| 1161 |
$("#client-search-input").on("click", function() { |
| 1162 |
// Show the dropdown and call AJAX to populate it |
| 1163 |
$('#client-search-results').show(); |
| 1164 |
$('#client-search-loading').show(); |
| 1165 |
$('#client-search-empty').hide(); |
| 1166 |
|
| 1167 |
// Call the AJAX method to populate the dropdown |
| 1168 |
if (typeof easyInvoice !== 'undefined' && easyInvoice.ajaxUrl) { |
| 1169 |
searchClients(''); |
| 1170 |
} else { |
| 1171 |
$('#client-search-loading').hide(); |
| 1172 |
$('#client-search-empty').show(); |
| 1173 |
} |
| 1174 |
}); |
| 1175 |
|
| 1176 |
// Client selection |
| 1177 |
$(document).on('click', '.client-option', function(e) { |
| 1178 |
e.preventDefault(); |
| 1179 |
e.stopPropagation(); |
| 1180 |
|
| 1181 |
const clientId = $(this).data('client-id'); |
| 1182 |
const clientName = $(this).data('client-name'); |
| 1183 |
const clientEmail = $(this).data('client-email'); |
| 1184 |
const clientCompany = $(this).data('client-company'); |
| 1185 |
const clientPhone = $(this).data('client-phone'); |
| 1186 |
const clientWebsite = $(this).data('client-website'); |
| 1187 |
const clientAddress = $(this).data('client-address'); |
| 1188 |
|
| 1189 |
// Create client data object with all available information |
| 1190 |
const clientData = { |
| 1191 |
name: clientName, |
| 1192 |
email: clientEmail, |
| 1193 |
company: clientCompany, |
| 1194 |
phone: clientPhone, |
| 1195 |
website: clientWebsite, |
| 1196 |
address: clientAddress |
| 1197 |
}; |
| 1198 |
|
| 1199 |
selectClient(clientId, clientName, clientEmail, clientData); |
| 1200 |
$('#client-search-results').hide(); |
| 1201 |
$('#client-search-input').val(''); |
| 1202 |
}); |
| 1203 |
|
| 1204 |
// Alternative click handler for client options |
| 1205 |
$('#client-search-list').on('click', '.client-option', function(e) { |
| 1206 |
e.preventDefault(); |
| 1207 |
e.stopPropagation(); |
| 1208 |
|
| 1209 |
const clientId = $(this).data('client-id'); |
| 1210 |
const clientName = $(this).data('client-name'); |
| 1211 |
const clientEmail = $(this).data('client-email'); |
| 1212 |
const clientCompany = $(this).data('client-company'); |
| 1213 |
const clientPhone = $(this).data('client-phone'); |
| 1214 |
const clientWebsite = $(this).data('client-website'); |
| 1215 |
const clientAddress = $(this).data('client-address'); |
| 1216 |
|
| 1217 |
// Create client data object with all available information |
| 1218 |
const clientData = { |
| 1219 |
name: clientName, |
| 1220 |
email: clientEmail, |
| 1221 |
company: clientCompany, |
| 1222 |
phone: clientPhone, |
| 1223 |
website: clientWebsite, |
| 1224 |
address: clientAddress |
| 1225 |
}; |
| 1226 |
|
| 1227 |
selectClient(clientId, clientName, clientEmail, clientData); |
| 1228 |
$('#client-search-results').hide(); |
| 1229 |
$('#client-search-input').val(''); |
| 1230 |
}); |
| 1231 |
|
| 1232 |
// Clear client selection |
| 1233 |
$('#clear-client-selection').on('click', function() { |
| 1234 |
clearClientSelection(); |
| 1235 |
}); |
| 1236 |
|
| 1237 |
// Add new client button |
| 1238 |
$('#add-new-client-btn').on('click', function() { |
| 1239 |
$('#add_client_modal').show(); |
| 1240 |
}); |
| 1241 |
|
| 1242 |
// Close modal |
| 1243 |
$('.close-modal').on('click', function() { |
| 1244 |
$('#add_client_modal').hide(); |
| 1245 |
}); |
| 1246 |
|
| 1247 |
// Functions |
| 1248 |
function searchClients(query) { |
| 1249 |
$('#client-search-loading').show(); |
| 1250 |
$('#client-search-empty').hide(); |
| 1251 |
$('#client-search-list').empty(); |
| 1252 |
|
| 1253 |
// If query is empty or less than 2 characters, show all clients |
| 1254 |
if (!query || query.trim().length < 2) { |
| 1255 |
query = ''; // Set to empty string to get all clients |
| 1256 |
} |
| 1257 |
|
| 1258 |
$.ajax({ |
| 1259 |
url: easyInvoice.ajaxUrl, |
| 1260 |
type: 'POST', |
| 1261 |
data: { |
| 1262 |
action: 'easy_invoice_search_clients', |
| 1263 |
query: query, |
| 1264 |
nonce: easyInvoice.nonce |
| 1265 |
}, |
| 1266 |
success: function(response) { |
| 1267 |
$('#client-search-loading').hide(); |
| 1268 |
|
| 1269 |
if (response.success && response.data.length > 0) { |
| 1270 |
response.data.forEach(function(client) { |
| 1271 |
const option = $(` |
| 1272 |
<div class="client-option px-4 py-3 hover:bg-gray-50 cursor-pointer border-b border-gray-100 last:border-b-0" |
| 1273 |
data-client-id="${client.id}" |
| 1274 |
data-client-name="${client.name.replace(/"/g, '"')}" |
| 1275 |
data-client-email="${client.email.replace(/"/g, '"')}" |
| 1276 |
data-client-company="${(client.company || '').replace(/"/g, '"')}" |
| 1277 |
data-client-phone="${(client.phone || '').replace(/"/g, '"')}" |
| 1278 |
data-client-website="${(client.website || '').replace(/"/g, '"')}" |
| 1279 |
data-client-address="${(client.address || '').replace(/"/g, '"')}" |
| 1280 |
onclick="selectClientFromOption(this)"> |
| 1281 |
<div class="font-medium text-gray-900">${client.name}</div> |
| 1282 |
<div class="text-sm text-gray-600">${client.email}</div> |
| 1283 |
</div> |
| 1284 |
`); |
| 1285 |
|
| 1286 |
$('#client-search-list').append(option); |
| 1287 |
}); |
| 1288 |
$('#client-search-results').show(); |
| 1289 |
} else { |
| 1290 |
$('#client-search-empty').show(); |
| 1291 |
$('#client-search-results').show(); |
| 1292 |
} |
| 1293 |
}, |
| 1294 |
error: function(xhr, status, error) { |
| 1295 |
$('#client-search-loading').hide(); |
| 1296 |
$('#client-search-empty').show(); |
| 1297 |
$('#client-search-results').show(); |
| 1298 |
} |
| 1299 |
}); |
| 1300 |
} |
| 1301 |
|
| 1302 |
function selectClient(clientId, clientName, clientEmail, clientData = null) { |
| 1303 |
|
| 1304 |
$('#select-client').val(clientId); |
| 1305 |
$('#client-id').val(clientId); |
| 1306 |
$('#selected-client-name').text(clientName); |
| 1307 |
$('#selected-client-email').text(clientEmail); |
| 1308 |
|
| 1309 |
$('#selected-client-display').show().removeClass('hidden'); |
| 1310 |
$('#client-info-display').show().removeClass('hidden'); |
| 1311 |
$('#no-client-message').hide().removeClass('hidden'); |
| 1312 |
|
| 1313 |
// Update the edit client button URL |
| 1314 |
$('#edit-selected-client').attr('href', '<?php echo admin_url('admin.php?page=easy-invoice-client-edit&client_id='); ?>' + clientId); |
| 1315 |
|
| 1316 |
// If we have full client data, populate all fields directly |
| 1317 |
if (clientData) { |
| 1318 |
$('#display-client-name').text(clientData.name || clientName || '-'); |
| 1319 |
$('#display-client-company').text(clientData.company || '-'); |
| 1320 |
$('#display-client-email').text(clientData.email || clientEmail || '-'); |
| 1321 |
$('#display-client-phone').text(clientData.phone || '-'); |
| 1322 |
$('#display-client-website').text(clientData.website || '-'); |
| 1323 |
$('#display-client-address').text(clientData.address || '-'); |
| 1324 |
} else { |
| 1325 |
// Fallback to just the basic info we have |
| 1326 |
$('#display-client-name').text(clientName); |
| 1327 |
$('#display-client-email').text(clientEmail); |
| 1328 |
$('#display-client-company').text('-'); |
| 1329 |
$('#display-client-phone').text('-'); |
| 1330 |
$('#display-client-website').text('-'); |
| 1331 |
$('#display-client-address').text('-'); |
| 1332 |
} |
| 1333 |
} |
| 1334 |
|
| 1335 |
function clearClientSelection() { |
| 1336 |
$('#select-client').val(''); |
| 1337 |
$('#client-id').val(''); |
| 1338 |
$('#selected-client-display').hide(); |
| 1339 |
$('#client-info-display').hide(); |
| 1340 |
$('#no-client-message').show(); |
| 1341 |
|
| 1342 |
// Clear display fields |
| 1343 |
$('#display-client-name, #display-client-email, #display-client-company, #display-client-phone, #display-client-website, #display-client-address').text('-'); |
| 1344 |
} |
| 1345 |
|
| 1346 |
// Global function for inline onclick |
| 1347 |
window.selectClientFromOption = function(element) { |
| 1348 |
const $element = $(element); |
| 1349 |
const clientId = $element.data('client-id'); |
| 1350 |
const clientName = $element.data('client-name'); |
| 1351 |
const clientEmail = $element.data('client-email'); |
| 1352 |
const clientCompany = $element.data('client-company'); |
| 1353 |
const clientPhone = $element.data('client-phone'); |
| 1354 |
const clientWebsite = $element.data('client-website'); |
| 1355 |
const clientAddress = $element.data('client-address'); |
| 1356 |
|
| 1357 |
// Create client data object with all available information |
| 1358 |
const clientData = { |
| 1359 |
name: clientName, |
| 1360 |
email: clientEmail, |
| 1361 |
company: clientCompany, |
| 1362 |
phone: clientPhone, |
| 1363 |
website: clientWebsite, |
| 1364 |
address: clientAddress |
| 1365 |
}; |
| 1366 |
|
| 1367 |
selectClient(clientId, clientName, clientEmail, clientData); |
| 1368 |
$('#client-search-results').hide(); |
| 1369 |
$('#client-search-input').val(''); |
| 1370 |
}; |
| 1371 |
}); |
| 1372 |
</script> |
| 1373 |
|
| 1374 |
<script> |
| 1375 |
// Invoice field configuration and pre-loaded data |
| 1376 |
window.easyInvoice = window.easyInvoice || {}; |
| 1377 |
<?php |
| 1378 |
$json_field_config = json_encode($invoice_field_config); |
| 1379 |
?> |
| 1380 |
window.easyInvoice.fieldConfig = <?php echo $json_field_config; ?>; |
| 1381 |
window.easyInvoice.adminUrl = '<?php echo admin_url(); ?>'; |
| 1382 |
window.easyInvoice.isPro = <?php echo easy_invoice_has_pro() ? 'true' : 'false'; ?>; |
| 1383 |
window.easyInvoice.translations = { |
| 1384 |
premiumTemplates: '<?php _e('Premium Templates', 'easy-invoice'); ?>', |
| 1385 |
premiumTemplatesDescription: '<?php _e('Access all premium templates including Modern, Professional, Classic, and more to give your invoices a unique and professional look.', 'easy-invoice'); ?>', |
| 1386 |
premiumFeatureMessage: '<?php _e('This is a premium feature. Please upgrade to Pro to access all templates.', 'easy-invoice'); ?>', |
| 1387 |
selectTemplateFirst: '<?php _e('Please select a template first', 'easy-invoice'); ?>' |
| 1388 |
}; |
| 1389 |
<?php if ($client_data): ?> |
| 1390 |
window.easyInvoice.clientData = <?php echo json_encode($client_data); ?>; |
| 1391 |
<?php endif; ?> |
| 1392 |
</script> |
| 1393 |
|