PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.3.1
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.3.1
2.4.3 2.4.4 2.4.2 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 All 60 releases
easy-invoice / templates / invoices / form.php

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

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