PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.2
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.2
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 / live-preview.php

live-preview.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.2, at templates/invoices/live-preview.php

292 lines 15.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit;
4 }
5 ?> <div class="bg-white shadow rounded-lg" style="padding: 10px;">
6
7 <div class="px-6 py-5 border-b border-gray-200" style="margin-bottom: 10px;">
8 <div class="flex justify-between items-center">
9 <div class="flex items-center">
10 <div class="flex-shrink-0 bg-indigo-100 rounded-lg p-3">
11 <i class="fas fa-eye text-indigo-600 text-xl"></i>
12 </div>
13 <div class="ml-4">
14 <h2 class="text-lg font-medium text-gray-900"><?php esc_html_e('Invoice Preview', 'easy-invoice'); ?></h2>
15 <p class="mt-1 text-sm text-gray-500"><?php esc_html_e('Live preview of your invoice', 'easy-invoice'); ?></p>
16 </div>
17 </div>
18 <?php if($invoice_id > 0 ){ ?>
19 <a href="<?php echo esc_url(get_permalink($invoice_id)); ?>" target="_blank" class="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
20 <i class="fas fa-external-link-alt mr-2"></i>
21 Full Preview
22 </a>
23 <?php } ?>
24 </div>
25 </div>
26
27 <?php
28 // Get the current template (default to 'standard')
29 $current_template = $invoice ? $invoice->getTemplate() : 'standard';
30 if (empty($current_template)) {
31 $current_template = 'standard';
32 }
33 $template_class = 'template-' . $current_template;
34
35 // Initialize formatter for the invoice templates
36 $formatter = new \EasyInvoice\Helpers\InvoiceFormatter($invoice);
37 ?>
38
39 <div id="invoice-container" class="invoice-container">
40 <?php
41 // Add custom template builder CSS if using a custom template
42 if ($invoice && method_exists($invoice, 'getId')) {
43 $template_id = get_post_meta($invoice->getId(), '_easy_invoice_invoice_template', true);
44 if ($template_id && class_exists('\EasyInvoicePro\TemplateParser') && class_exists('\EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController')) {
45 $templateData = \EasyInvoicePro\TemplateParser::parseTemplateFromDatabase($template_id);
46 if ($templateData && isset($templateData['elements'])) {
47 echo '<style>';
48 // Canvas settings
49 $canvas = $templateData['canvas'] ?? [];
50 $canvasWidth = $canvas['width'] ?? 794;
51 $canvasHeight = $canvas['height'] ?? 1123;
52 $canvasDesignCSS = \EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController::generateCanvasCSS($templateData);
53
54 echo "#canvas { position: relative; width: " . floatval($canvasWidth) . "px; height: " . floatval($canvasHeight) . "px; margin: 0; overflow: visible; ";
55 if ($canvasDesignCSS) {
56 echo wp_strip_all_tags(str_replace('!important;', ';', $canvasDesignCSS)); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS inside a <style> element; tags stripped, entities are not decoded there.
57 }
58 echo " }\n";
59
60 // Element styles
61 foreach ($templateData['elements'] as $element) {
62 $elementId = $element['id'] ?? '';
63 if (!$elementId) continue;
64
65 echo '#' . esc_attr($elementId) . ' { position: absolute; ';
66 if (isset($element['position']['left'])) echo "left: " . floatval($element['position']['left']) . "px; ";
67 if (isset($element['position']['top'])) echo "top: " . floatval($element['position']['top']) . "px; ";
68 if (isset($element['dimensions']['width'])) echo "width: " . floatval($element['dimensions']['width']) . "px; ";
69 if (isset($element['dimensions']['height'])) echo "min-height: " . floatval($element['dimensions']['height']) . "px; ";
70
71 if (isset($element['styles']) && is_array($element['styles'])) {
72 foreach ($element['styles'] as $property => $value) {
73 if ($value !== '' && !in_array($property, ['position', 'dimensions'])) {
74 echo esc_attr($property) . ": " . esc_attr($value) . "; ";
75 }
76 }
77 }
78 echo "}\n";
79 }
80 echo '</style>';
81 }
82 }
83 }
84
85 // Dynamically load the selected template
86 $template_file = easy_invoice_design_template( 'invoice', (string) $current_template );
87 if ( ! $invoice instanceof \EasyInvoice\Models\Invoice ) {
88 // Nothing to render yet. The designs read the model
89 // directly, so including one here would be fatal.
90 ?>
91 <div class="ei-preview-empty" style="text-align:center;padding:48px 24px;color:#64748b;">
92 <p style="margin:0;font-size:14px;"><?php esc_html_e( 'Your invoice will appear here as you fill in the form.', 'easy-invoice' ); ?></p>
93 </div>
94 <?php
95 } elseif (file_exists($template_file)) {
96 include_once $template_file;
97 } else {
98 // Show template not found message
99 ?>
100 <div class="template-not-found">
101 <div class="template-not-found-icon">
102 <i class="fas fa-exclamation-circle"></i>
103 </div>
104 <h3><?php esc_html_e('Template Not Found', 'easy-invoice'); ?></h3>
105 <p>The selected template "<?php echo esc_html($current_template); ?>" does not exist.</p>
106 <p><?php esc_html_e('Please select a different template or contact support if you believe this is an error.', 'easy-invoice'); ?></p>
107 </div>
108 <style>
109 .template-not-found {
110 text-align: center;
111 padding: 40px;
112 background: #fff;
113 border-radius: 8px;
114 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
115 }
116 .template-not-found-icon {
117 font-size: 48px;
118 color: #dc3545;
119 margin-bottom: 20px;
120 }
121 .template-not-found h3 {
122 font-size: 24px;
123 color: #32325d;
124 margin-bottom: 10px;
125 }
126 .template-not-found p {
127 color: #8898aa;
128 margin-bottom: 10px;
129 }
130 </style>
131 <?php
132 }
133 ?>
134 </div>
135 </div>
136
137 <style>
138 /* Loading state styles */
139 .loading-overlay {
140 position: absolute;
141 top: 0;
142 left: 0;
143 right: 0;
144 bottom: 0;
145 background: rgba(255, 255, 255, 0.9);
146 display: flex;
147 justify-content: center;
148 align-items: center;
149 z-index: 1000;
150 }
151
152 .loading-spinner {
153 text-align: center;
154 }
155
156 .invoice-container.loading .loading-overlay {
157 display: flex;
158 }
159
160 /* (Resize logic removed per request) */
161 </style>
162
163 <script>
164 // Localize easyInvoice object for this page
165 window.easyInvoice = window.easyInvoice || {};
166 window.easyInvoice.isPro = <?php echo easy_invoice_has_pro() ? 'true' : 'false'; ?>;
167 window.easyInvoice.ajaxUrl = '<?php echo esc_url(admin_url('admin-ajax.php')); ?>';
168 window.easyInvoice.nonce = '<?php echo esc_attr(wp_create_nonce('easy_invoice_nonce')); ?>';
169
170 jQuery(document).ready(function($) {
171 // Function to update the live preview
172 function updateLivePreview(templateId) {
173 // Show loading state
174 $('#invoice-container').addClass('loading');
175
176 // Make AJAX call to load the template
177 $.ajax({
178 url: window.easyInvoice.ajaxUrl,
179 type: 'POST',
180 data: {
181 action: 'easy_invoice_load_template',
182 template: templateId,
183 form_data: jQuery('#invoice-form').length ? jQuery('#invoice-form').serialize() : '',
184 invoice_id: '<?php echo esc_js($invoice_id); ?>',
185 nonce: window.easyInvoice.nonce
186 },
187 success: function(response) {
188 if (response.success) {
189 // Update the preview content
190 $('#invoice-container').html(response.data.html);
191 } else {
192 // Show error message
193 $('#invoice-container').html(`
194 <div class="template-not-found">
195 <div class="template-not-found-icon">
196 <i class="fas fa-exclamation-circle"></i>
197 </div>
198 <h3><?php esc_html_e('Template Not Found', 'easy-invoice'); ?></h3>
199 <p>The selected template "${templateId}" does not exist.</p>
200 <p><?php esc_html_e('Please select a different template or contact support if you believe this is an error.', 'easy-invoice'); ?></p>
201 </div>
202 `);
203 }
204 },
205 error: function(xhr, status, error) {
206 // Show error message
207 $('#invoice-container').html(`
208 <div class="template-not-found">
209 <div class="template-not-found-icon">
210 <i class="fas fa-exclamation-circle"></i>
211 </div>
212 <h3><?php esc_html_e('Error Loading Template', 'easy-invoice'); ?></h3>
213 <p><?php esc_html_e('There was an error loading the template. Please try again.', 'easy-invoice'); ?></p>
214 </div>
215 `);
216 },
217 complete: function() {
218 // Remove loading state
219 $('#invoice-container').removeClass('loading');
220 }
221 });
222 }
223
224 // Listen for template changes from the template cards
225 $('.template-card').on('click', function(e) {
226 const templateId = $(this).data('template-id');
227
228 // Check if this is a premium template in free version
229 const isPremium = $(this).attr('data-is-premium') === 'true';
230 const isFreeVersion = !window.easyInvoice || !window.easyInvoice.isPro;
231 if (isPremium && isFreeVersion) {
232 // Show premium upgrade modal instead of selecting the template
233 if (typeof EasyInvoiceConfirmation !== 'undefined' && EasyInvoiceConfirmation.showFeatureUpgrade) {
234 EasyInvoiceConfirmation.showFeatureUpgrade('<?php echo esc_js(__('Premium Templates', 'easy-invoice')); ?>', '<?php echo esc_js(__('Access all premium templates including Modern, Professional, Classic, and more to give your invoices a unique and professional look.', 'easy-invoice')); ?>');
235 } else {
236 alert('<?php echo esc_js(__('This is a premium feature. Please upgrade to Pro to access all templates.', 'easy-invoice')); ?>');
237 }
238 return;
239 }
240
241 updateLivePreview(templateId);
242 });
243
244 // Listen for template changes from the gallery
245 $('.gallery-template-card').on('click', function() {
246 const templateId = $(this).data('template-id');
247 updateLivePreview(templateId);
248 });
249
250
251 // Live means live: re-render as the builder changes, not only when the
252 // template changes. Debounced so typing a description does not fire a
253 // request per keystroke.
254 (function () {
255 var $form = $('#invoice-form'); if (!$form.length) { return; }
256 var timer = null;
257 var current = function () { return $form.find('input[name="invoice_template"]').val() || $('.template-card.selected, .template-card.active').first().data('template-id') || 'standard'; };
258 var schedule = function () { clearTimeout(timer); timer = setTimeout(function () { updateLivePreview(current()); }, 700); };
259 $form.on('input change', 'input, textarea, select', schedule);
260 $(document).on('click', '.add-item-button, .remove-item, .client-option, #remove-selected-client, .template-card', schedule);
261 $(document).on('easy_invoice_items_changed easy_invoice_client_changed', schedule);
262 })();
263
264 });
265 </script>
266
267 <script>
268 // Scale the A4 design to the preview pane instead of letting it reflow: the
269 // preview then shows the sheet the client will see, only smaller.
270 (function () {
271 var box = document.getElementById('invoice-container');
272 if (!box || !window.ResizeObserver) { return; }
273 var SHEET = 794;
274 function fit() {
275 var sheet = box.querySelector('.template, [class*="template-"]');
276 if (!sheet) { box.style.height = ''; return; }
277 var avail = box.clientWidth;
278 if (avail <= 0) { return; }
279 var k = Math.min(1, avail / SHEET);
280 sheet.style.width = SHEET + 'px';
281 sheet.style.maxWidth = 'none';
282 sheet.style.transformOrigin = 'top left';
283 sheet.style.transform = k < 1 ? 'scale(' + k + ')' : '';
284 box.style.overflow = 'hidden';
285 box.style.height = k < 1 ? Math.ceil(sheet.offsetHeight * k) + 'px' : '';
286 }
287 new ResizeObserver(fit).observe(box);
288 new MutationObserver(fit).observe(box, { childList: true, subtree: true });
289 fit();
290 })();
291 </script>
292