PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.2.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.2.0
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / templates / invoices / form.php

form.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.2.0, at templates/invoices/form.php

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